1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/devtools/device/devtools_android_bridge.h" 6 7 #include <map> 8 #include <vector> 9 10 #include "base/base64.h" 11 #include "base/bind.h" 12 #include "base/compiler_specific.h" 13 #include "base/json/json_reader.h" 14 #include "base/lazy_instance.h" 15 #include "base/message_loop/message_loop.h" 16 #include "base/prefs/pref_service.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/string_util.h" 19 #include "base/strings/stringprintf.h" 20 #include "base/strings/utf_string_conversions.h" 21 #include "base/threading/thread.h" 22 #include "base/values.h" 23 #include "chrome/browser/devtools/browser_list_tabcontents_provider.h" 24 #include "chrome/browser/devtools/device/adb/adb_device_info_query.h" 25 #include "chrome/browser/devtools/device/adb/adb_device_provider.h" 26 #include "chrome/browser/devtools/device/self_device_provider.h" 27 #include "chrome/browser/devtools/device/usb/usb_device_provider.h" 28 #include "chrome/browser/devtools/devtools_protocol.h" 29 #include "chrome/browser/devtools/devtools_target_impl.h" 30 #include "chrome/browser/devtools/devtools_window.h" 31 #include "chrome/browser/profiles/profile.h" 32 #include "chrome/common/pref_names.h" 33 #include "components/keyed_service/content/browser_context_dependency_manager.h" 34 #include "content/public/browser/devtools_agent_host.h" 35 #include "content/public/browser/devtools_external_agent_proxy.h" 36 #include "content/public/browser/devtools_external_agent_proxy_delegate.h" 37 #include "content/public/browser/user_metrics.h" 38 #include "net/base/escape.h" 39 40 using content::BrowserThread; 41 42 namespace { 43 44 const char kPageListRequest[] = "/json"; 45 const char kVersionRequest[] = "/json/version"; 46 const char kClosePageRequest[] = "/json/close/%s"; 47 const char kNewPageRequest[] = "/json/new"; 48 const char kNewPageRequestWithURL[] = "/json/new?%s"; 49 const char kActivatePageRequest[] = "/json/activate/%s"; 50 const char kBrowserTargetSocket[] = "/devtools/browser"; 51 const int kAdbPollingIntervalMs = 1000; 52 53 const char kUrlParam[] = "url"; 54 const char kPageReloadCommand[] = "Page.reload"; 55 const char kPageNavigateCommand[] = "Page.navigate"; 56 57 const int kMinVersionNewWithURL = 32; 58 const int kNewPageNavigateDelayMs = 500; 59 60 // DiscoveryRequest ----------------------------------------------------- 61 62 class DiscoveryRequest : public base::RefCountedThreadSafe< 63 DiscoveryRequest, 64 BrowserThread::DeleteOnUIThread> { 65 public: 66 typedef AndroidDeviceManager::Device Device; 67 typedef AndroidDeviceManager::Devices Devices; 68 typedef AndroidDeviceManager::DeviceInfo DeviceInfo; 69 typedef DevToolsAndroidBridge::RemoteDevice RemoteDevice; 70 typedef DevToolsAndroidBridge::RemoteDevices RemoteDevices; 71 typedef DevToolsAndroidBridge::RemoteBrowser RemoteBrowser; 72 typedef DevToolsAndroidBridge::RemoteBrowsers RemoteBrowsers; 73 typedef base::Callback<void(const RemoteDevices&)> DiscoveryCallback; 74 75 DiscoveryRequest(AndroidDeviceManager* device_manager, 76 const DiscoveryCallback& callback); 77 private: 78 friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>; 79 friend class base::DeleteHelper<DiscoveryRequest>; 80 virtual ~DiscoveryRequest(); 81 82 void ReceivedDevices(const Devices& devices); 83 void ReceivedDeviceInfo(scoped_refptr<Device> device, 84 const DeviceInfo& device_info); 85 void ReceivedVersion(scoped_refptr<RemoteBrowser>, 86 int result, 87 const std::string& response); 88 void ReceivedPages(scoped_refptr<RemoteBrowser>, 89 int result, 90 const std::string& response); 91 92 DiscoveryCallback callback_; 93 RemoteDevices remote_devices_; 94 }; 95 96 DiscoveryRequest::DiscoveryRequest( 97 AndroidDeviceManager* device_manager, 98 const DiscoveryCallback& callback) 99 : callback_(callback) { 100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 101 device_manager->QueryDevices( 102 base::Bind(&DiscoveryRequest::ReceivedDevices, this)); 103 } 104 105 DiscoveryRequest::~DiscoveryRequest() { 106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 107 callback_.Run(remote_devices_); 108 } 109 110 void DiscoveryRequest::ReceivedDevices(const Devices& devices) { 111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 112 for (Devices::const_iterator it = devices.begin(); 113 it != devices.end(); ++it) { 114 (*it)->QueryDeviceInfo( 115 base::Bind(&DiscoveryRequest::ReceivedDeviceInfo, this, *it)); 116 } 117 } 118 119 void DiscoveryRequest::ReceivedDeviceInfo(scoped_refptr<Device> device, 120 const DeviceInfo& device_info) { 121 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 122 scoped_refptr<RemoteDevice> remote_device = 123 new RemoteDevice(device, device_info); 124 remote_devices_.push_back(remote_device); 125 for (RemoteBrowsers::iterator it = remote_device->browsers().begin(); 126 it != remote_device->browsers().end(); ++it) { 127 (*it)->SendJsonRequest( 128 kVersionRequest, 129 base::Bind(&DiscoveryRequest::ReceivedVersion, this, *it)); 130 (*it)->SendJsonRequest( 131 kPageListRequest, 132 base::Bind(&DiscoveryRequest::ReceivedPages, this, *it)); 133 } 134 } 135 136 void DiscoveryRequest::ReceivedVersion(scoped_refptr<RemoteBrowser> browser, 137 int result, 138 const std::string& response) { 139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 140 if (result < 0) 141 return; 142 // Parse version, append to package name if available, 143 scoped_ptr<base::Value> value(base::JSONReader::Read(response)); 144 base::DictionaryValue* dict; 145 if (value && value->GetAsDictionary(&dict)) { 146 std::string browser_name; 147 if (dict->GetString("Browser", &browser_name)) { 148 std::vector<std::string> parts; 149 Tokenize(browser_name, "/", &parts); 150 if (parts.size() == 2) 151 browser->set_version(parts[1]); 152 else 153 browser->set_version(browser_name); 154 } 155 std::string package; 156 if (dict->GetString("Android-Package", &package)) { 157 browser->set_display_name( 158 AdbDeviceInfoQuery::GetDisplayName(browser->socket(), package)); 159 } 160 } 161 } 162 163 void DiscoveryRequest::ReceivedPages(scoped_refptr<RemoteBrowser> browser, 164 int result, 165 const std::string& response) { 166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 167 if (result < 0) 168 return; 169 scoped_ptr<base::Value> value(base::JSONReader::Read(response)); 170 base::ListValue* list_value; 171 if (value && value->GetAsList(&list_value)) 172 browser->SetPageDescriptors(*list_value); 173 } 174 175 // ProtocolCommand ------------------------------------------------------------ 176 177 class ProtocolCommand 178 : public DevToolsAndroidBridge::AndroidWebSocket::Delegate { 179 public: 180 ProtocolCommand( 181 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser, 182 const std::string& debug_url, 183 const std::string& command, 184 const base::Closure callback); 185 186 private: 187 virtual void OnSocketOpened() OVERRIDE; 188 virtual void OnFrameRead(const std::string& message) OVERRIDE; 189 virtual void OnSocketClosed(bool closed_by_device) OVERRIDE; 190 191 const std::string command_; 192 const base::Closure callback_; 193 scoped_refptr<DevToolsAndroidBridge::AndroidWebSocket> web_socket_; 194 195 DISALLOW_COPY_AND_ASSIGN(ProtocolCommand); 196 }; 197 198 ProtocolCommand::ProtocolCommand( 199 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser, 200 const std::string& debug_url, 201 const std::string& command, 202 const base::Closure callback) 203 : command_(command), 204 callback_(callback){ 205 web_socket_ = browser->CreateWebSocket(debug_url, this); 206 web_socket_->Connect(); 207 } 208 209 void ProtocolCommand::OnSocketOpened() { 210 web_socket_->SendFrame(command_); 211 } 212 213 void ProtocolCommand::OnFrameRead(const std::string& message) { 214 web_socket_->Disconnect(); 215 } 216 217 void ProtocolCommand::OnSocketClosed(bool closed_by_device) { 218 if (!callback_.is_null()) { 219 callback_.Run(); 220 } 221 delete this; 222 } 223 224 } // namespace 225 226 class AgentHostDelegate; 227 228 typedef std::map<std::string, AgentHostDelegate*> AgentHostDelegates; 229 230 base::LazyInstance<AgentHostDelegates>::Leaky g_host_delegates = 231 LAZY_INSTANCE_INITIALIZER; 232 233 DevToolsAndroidBridge::Wrapper::Wrapper(content::BrowserContext* context) { 234 bridge_ = new DevToolsAndroidBridge(Profile::FromBrowserContext(context)); 235 } 236 237 DevToolsAndroidBridge::Wrapper::~Wrapper() { 238 } 239 240 DevToolsAndroidBridge* DevToolsAndroidBridge::Wrapper::Get() { 241 return bridge_.get(); 242 } 243 244 // static 245 DevToolsAndroidBridge::Factory* DevToolsAndroidBridge::Factory::GetInstance() { 246 return Singleton<DevToolsAndroidBridge::Factory>::get(); 247 } 248 249 // static 250 DevToolsAndroidBridge* DevToolsAndroidBridge::Factory::GetForProfile( 251 Profile* profile) { 252 DevToolsAndroidBridge::Wrapper* wrapper = 253 static_cast<DevToolsAndroidBridge::Wrapper*>(GetInstance()-> 254 GetServiceForBrowserContext(profile, true)); 255 return wrapper ? wrapper->Get() : NULL; 256 } 257 258 DevToolsAndroidBridge::Factory::Factory() 259 : BrowserContextKeyedServiceFactory( 260 "DevToolsAndroidBridge", 261 BrowserContextDependencyManager::GetInstance()) {} 262 263 DevToolsAndroidBridge::Factory::~Factory() {} 264 265 KeyedService* DevToolsAndroidBridge::Factory::BuildServiceInstanceFor( 266 content::BrowserContext* context) const { 267 return new DevToolsAndroidBridge::Wrapper(context); 268 } 269 270 271 // AgentHostDelegate ---------------------------------------------------------- 272 273 class AgentHostDelegate 274 : public content::DevToolsExternalAgentProxyDelegate, 275 public DevToolsAndroidBridge::AndroidWebSocket::Delegate { 276 public: 277 static scoped_refptr<content::DevToolsAgentHost> GetOrCreateAgentHost( 278 const std::string& id, 279 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser, 280 const std::string& debug_url); 281 282 private: 283 AgentHostDelegate( 284 const std::string& id, 285 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser, 286 const std::string& debug_url); 287 virtual ~AgentHostDelegate(); 288 virtual void Attach(content::DevToolsExternalAgentProxy* proxy) OVERRIDE; 289 virtual void Detach() OVERRIDE; 290 virtual void SendMessageToBackend( 291 const std::string& message) OVERRIDE; 292 virtual void OnSocketOpened() OVERRIDE; 293 virtual void OnFrameRead(const std::string& message) OVERRIDE; 294 virtual void OnSocketClosed(bool closed_by_device) OVERRIDE; 295 296 const std::string id_; 297 bool socket_opened_; 298 bool detached_; 299 bool is_web_view_; 300 std::vector<std::string> pending_messages_; 301 scoped_refptr<DevToolsAndroidBridge::AndroidWebSocket> web_socket_; 302 content::DevToolsAgentHost* agent_host_; 303 content::DevToolsExternalAgentProxy* proxy_; 304 DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate); 305 }; 306 307 // static 308 scoped_refptr<content::DevToolsAgentHost> 309 AgentHostDelegate::GetOrCreateAgentHost( 310 const std::string& id, 311 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser, 312 const std::string& debug_url) { 313 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 314 AgentHostDelegates::iterator it = g_host_delegates.Get().find(id); 315 if (it != g_host_delegates.Get().end()) 316 return it->second->agent_host_; 317 318 AgentHostDelegate* delegate = new AgentHostDelegate(id, browser, debug_url); 319 scoped_refptr<content::DevToolsAgentHost> result = 320 content::DevToolsAgentHost::Create(delegate); 321 delegate->agent_host_ = result.get(); 322 return result; 323 } 324 325 AgentHostDelegate::AgentHostDelegate( 326 const std::string& id, 327 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser, 328 const std::string& debug_url) 329 : id_(id), 330 socket_opened_(false), 331 detached_(false), 332 is_web_view_(browser->IsWebView()), 333 web_socket_(browser->CreateWebSocket(debug_url, this)), 334 agent_host_(NULL), 335 proxy_(NULL) { 336 g_host_delegates.Get()[id] = this; 337 } 338 339 AgentHostDelegate::~AgentHostDelegate() { 340 g_host_delegates.Get().erase(id_); 341 web_socket_->ClearDelegate(); 342 } 343 344 void AgentHostDelegate::Attach(content::DevToolsExternalAgentProxy* proxy) { 345 proxy_ = proxy; 346 content::RecordAction(base::UserMetricsAction(is_web_view_ ? 347 "DevTools_InspectAndroidWebView" : "DevTools_InspectAndroidPage")); 348 web_socket_->Connect(); 349 } 350 351 void AgentHostDelegate::Detach() { 352 detached_ = true; 353 if (socket_opened_) 354 web_socket_->Disconnect(); 355 } 356 357 void AgentHostDelegate::SendMessageToBackend(const std::string& message) { 358 if (socket_opened_) 359 web_socket_->SendFrame(message); 360 else 361 pending_messages_.push_back(message); 362 } 363 364 void AgentHostDelegate::OnSocketOpened() { 365 if (detached_) { 366 web_socket_->Disconnect(); 367 return; 368 } 369 370 socket_opened_ = true; 371 for (std::vector<std::string>::iterator it = pending_messages_.begin(); 372 it != pending_messages_.end(); ++it) { 373 SendMessageToBackend(*it); 374 } 375 pending_messages_.clear(); 376 } 377 378 void AgentHostDelegate::OnFrameRead(const std::string& message) { 379 if (proxy_) 380 proxy_->DispatchOnClientHost(message); 381 } 382 383 void AgentHostDelegate::OnSocketClosed(bool closed_by_device) { 384 if (proxy_ && closed_by_device) 385 proxy_->ConnectionClosed(); 386 } 387 388 //// RemotePageTarget ---------------------------------------------- 389 390 class RemotePageTarget : public DevToolsTargetImpl, 391 public DevToolsAndroidBridge::RemotePage { 392 public: 393 RemotePageTarget(scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser, 394 const base::DictionaryValue& value); 395 virtual ~RemotePageTarget(); 396 397 // DevToolsAndroidBridge::RemotePage implementation. 398 virtual DevToolsTargetImpl* GetTarget() OVERRIDE; 399 virtual std::string GetFrontendURL() OVERRIDE; 400 401 // DevToolsTargetImpl overrides. 402 virtual std::string GetId() const OVERRIDE; 403 virtual bool IsAttached() const OVERRIDE; 404 virtual bool Activate() const OVERRIDE; 405 virtual bool Close() const OVERRIDE; 406 virtual void Inspect(Profile* profile) const OVERRIDE; 407 virtual void Reload() const OVERRIDE; 408 409 void Navigate(const std::string& url, base::Closure callback) const; 410 411 private: 412 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser_; 413 std::string debug_url_; 414 std::string frontend_url_; 415 std::string remote_id_; 416 DISALLOW_COPY_AND_ASSIGN(RemotePageTarget); 417 }; 418 419 static std::string GetStringProperty(const base::DictionaryValue& value, 420 const std::string& name) { 421 std::string result; 422 value.GetString(name, &result); 423 return result; 424 } 425 426 static std::string BuildUniqueTargetId( 427 DevToolsAndroidBridge::RemoteBrowser* browser, 428 const base::DictionaryValue& value) { 429 return base::StringPrintf("%s:%s:%s", browser->serial().c_str(), 430 browser->socket().c_str(), GetStringProperty(value, "id").c_str()); 431 } 432 433 static std::string GetDebugURL(const base::DictionaryValue& value) { 434 std::string debug_url = GetStringProperty(value, "webSocketDebuggerUrl"); 435 436 if (debug_url.find("ws://") == 0) 437 debug_url = debug_url.substr(5); 438 else 439 debug_url = ""; 440 return debug_url; 441 } 442 443 RemotePageTarget::RemotePageTarget( 444 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser, 445 const base::DictionaryValue& value) 446 : DevToolsTargetImpl(AgentHostDelegate::GetOrCreateAgentHost( 447 BuildUniqueTargetId(browser.get(), value), 448 browser, GetDebugURL(value))), 449 browser_(browser), 450 debug_url_(GetDebugURL(value)), 451 remote_id_(GetStringProperty(value, "id")) { 452 set_type("adb_page"); 453 set_url(GURL(GetStringProperty(value, "url"))); 454 set_title(base::UTF16ToUTF8(net::UnescapeForHTML(base::UTF8ToUTF16( 455 GetStringProperty(value, "title"))))); 456 set_description(GetStringProperty(value, "description")); 457 set_favicon_url(GURL(GetStringProperty(value, "faviconUrl"))); 458 debug_url_ = GetDebugURL(value); 459 frontend_url_ = GetStringProperty(value, "devtoolsFrontendUrl"); 460 461 size_t ws_param = frontend_url_.find("?ws"); 462 if (ws_param != std::string::npos) 463 frontend_url_ = frontend_url_.substr(0, ws_param); 464 if (frontend_url_.find("http:") == 0) 465 frontend_url_ = "https:" + frontend_url_.substr(5); 466 } 467 468 RemotePageTarget::~RemotePageTarget() { 469 } 470 471 DevToolsTargetImpl* RemotePageTarget::GetTarget() { 472 return this; 473 } 474 475 std::string RemotePageTarget::GetFrontendURL() { 476 return frontend_url_; 477 } 478 479 std::string RemotePageTarget::GetId() const { 480 return remote_id_; 481 } 482 483 bool RemotePageTarget::IsAttached() const { 484 return debug_url_.empty(); 485 } 486 487 static void NoOp(int, const std::string&) {} 488 489 void RemotePageTarget::Inspect(Profile* profile) const { 490 Activate(); 491 DevToolsWindow::OpenExternalFrontend(profile, frontend_url_, 492 GetAgentHost()); 493 } 494 495 bool RemotePageTarget::Activate() const { 496 std::string request = base::StringPrintf(kActivatePageRequest, 497 remote_id_.c_str()); 498 browser_->SendJsonRequest(request, base::Bind(&NoOp)); 499 return true; 500 } 501 502 bool RemotePageTarget::Close() const { 503 std::string request = base::StringPrintf(kClosePageRequest, 504 remote_id_.c_str()); 505 browser_->SendJsonRequest(request, base::Bind(&NoOp)); 506 return true; 507 } 508 509 void RemotePageTarget::Reload() const { 510 browser_->SendProtocolCommand(debug_url_, kPageReloadCommand, NULL, 511 base::Closure()); 512 } 513 514 void RemotePageTarget::Navigate(const std::string& url, 515 base::Closure callback) const { 516 base::DictionaryValue params; 517 params.SetString(kUrlParam, url); 518 browser_->SendProtocolCommand(debug_url_, kPageNavigateCommand, ¶ms, 519 callback); 520 } 521 522 // DevToolsAndroidBridge::RemoteBrowser --------------------------------------- 523 524 DevToolsAndroidBridge::RemoteBrowser::RemoteBrowser( 525 scoped_refptr<Device> device, 526 const AndroidDeviceManager::BrowserInfo& browser_info) 527 : device_(device), 528 socket_(browser_info.socket_name), 529 display_name_(browser_info.display_name), 530 type_(browser_info.type), 531 page_descriptors_(new base::ListValue()) { 532 } 533 534 bool DevToolsAndroidBridge::RemoteBrowser::IsChrome() const { 535 return type_ == AndroidDeviceManager::BrowserInfo::kTypeChrome; 536 } 537 538 bool DevToolsAndroidBridge::RemoteBrowser::IsWebView() const { 539 return type_ == AndroidDeviceManager::BrowserInfo::kTypeWebView; 540 } 541 542 DevToolsAndroidBridge::RemoteBrowser::ParsedVersion 543 DevToolsAndroidBridge::RemoteBrowser::GetParsedVersion() const { 544 ParsedVersion result; 545 std::vector<std::string> parts; 546 Tokenize(version_, ".", &parts); 547 for (size_t i = 0; i != parts.size(); ++i) { 548 int value = 0; 549 base::StringToInt(parts[i], &value); 550 result.push_back(value); 551 } 552 return result; 553 } 554 555 std::vector<DevToolsAndroidBridge::RemotePage*> 556 DevToolsAndroidBridge::RemoteBrowser::CreatePages() { 557 std::vector<DevToolsAndroidBridge::RemotePage*> result; 558 for (size_t i = 0; i < page_descriptors_->GetSize(); ++i) { 559 base::Value* item; 560 page_descriptors_->Get(i, &item); 561 if (!item) 562 continue; 563 base::DictionaryValue* dict; 564 if (!item->GetAsDictionary(&dict)) 565 continue; 566 result.push_back(new RemotePageTarget(this, *dict)); 567 } 568 return result; 569 } 570 571 void DevToolsAndroidBridge::RemoteBrowser::SetPageDescriptors( 572 const base::ListValue& list) { 573 page_descriptors_.reset(list.DeepCopy()); 574 } 575 576 static void RespondOnUIThread( 577 const DevToolsAndroidBridge::JsonRequestCallback& callback, 578 int result, 579 const std::string& response) { 580 if (callback.is_null()) 581 return; 582 BrowserThread::PostTask( 583 BrowserThread::UI, FROM_HERE, base::Bind(callback, result, response)); 584 } 585 586 void DevToolsAndroidBridge::RemoteBrowser::SendJsonRequest( 587 const std::string& request, const JsonRequestCallback& callback) { 588 device_->SendJsonRequest(socket_, request, callback); 589 } 590 591 void DevToolsAndroidBridge::RemoteBrowser::SendProtocolCommand( 592 const std::string& debug_url, 593 const std::string& method, 594 base::DictionaryValue* params, 595 const base::Closure callback) { 596 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 597 if (debug_url.empty()) 598 return; 599 DevToolsProtocol::Command command(1, method, params); 600 new ProtocolCommand(this, debug_url, command.Serialize(), callback); 601 } 602 603 void DevToolsAndroidBridge::RemoteBrowser::Open( 604 const std::string& url, 605 const DevToolsAndroidBridge::RemotePageCallback& callback) { 606 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 607 InnerOpen(url, base::Bind(&RemoteBrowser::RespondToOpenOnUIThread, 608 this, callback)); 609 } 610 611 scoped_refptr<content::DevToolsAgentHost> 612 DevToolsAndroidBridge::RemoteBrowser::GetAgentHost() { 613 return AgentHostDelegate::GetOrCreateAgentHost( 614 "adb:" + device_->serial() + ":" + socket_, this, kBrowserTargetSocket); 615 } 616 617 scoped_refptr<DevToolsAndroidBridge::AndroidWebSocket> 618 DevToolsAndroidBridge::RemoteBrowser::CreateWebSocket( 619 const std::string& url, 620 DevToolsAndroidBridge::AndroidWebSocket::Delegate* delegate) { 621 return device_->CreateWebSocket(socket_, url, delegate); 622 } 623 624 void DevToolsAndroidBridge::RemoteBrowser::RespondToOpenOnUIThread( 625 const DevToolsAndroidBridge::RemotePageCallback& callback, 626 int result, 627 const std::string& response) { 628 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 629 if (result < 0) { 630 callback.Run(NULL); 631 return; 632 } 633 scoped_ptr<base::Value> value(base::JSONReader::Read(response)); 634 base::DictionaryValue* dict; 635 if (value && value->GetAsDictionary(&dict)) { 636 RemotePageTarget* new_page = new RemotePageTarget(this, *dict); 637 callback.Run(new_page); 638 } 639 } 640 641 void DevToolsAndroidBridge::RemoteBrowser::InnerOpen( 642 const std::string& input_url, 643 const JsonRequestCallback& callback) { 644 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 645 GURL gurl(input_url); 646 if (!gurl.is_valid()) { 647 gurl = GURL("http://" + input_url); 648 if (!gurl.is_valid()) 649 return; 650 } 651 std::string url = gurl.spec(); 652 653 ParsedVersion parsed_version = GetParsedVersion(); 654 if (IsChrome() && 655 !parsed_version.empty() && 656 parsed_version[0] >= kMinVersionNewWithURL) { 657 std::string query = net::EscapeQueryParamValue(url, false /* use_plus */); 658 std::string request = 659 base::StringPrintf(kNewPageRequestWithURL, query.c_str()); 660 SendJsonRequest(request, callback); 661 } else { 662 SendJsonRequest(kNewPageRequest, 663 base::Bind(&RemoteBrowser::PageCreatedOnUIThread, this, 664 callback, url)); 665 } 666 } 667 668 void DevToolsAndroidBridge::RemoteBrowser::PageCreatedOnUIThread( 669 const JsonRequestCallback& callback, 670 const std::string& url, int result, const std::string& response) { 671 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 672 673 if (result < 0) 674 return; 675 // Navigating too soon after the page creation breaks navigation history 676 // (crbug.com/311014). This can be avoided by adding a moderate delay. 677 BrowserThread::PostDelayedTask( 678 BrowserThread::UI, FROM_HERE, 679 base::Bind(&RemoteBrowser::NavigatePageOnUIThread, 680 this, callback, result, response, url), 681 base::TimeDelta::FromMilliseconds(kNewPageNavigateDelayMs)); 682 } 683 684 void DevToolsAndroidBridge::RemoteBrowser::NavigatePageOnUIThread( 685 const JsonRequestCallback& callback, 686 int result, const std::string& response, const std::string& url) { 687 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 688 scoped_ptr<base::Value> value(base::JSONReader::Read(response)); 689 base::DictionaryValue* dict; 690 691 if (value && value->GetAsDictionary(&dict)) { 692 RemotePageTarget new_page(this, *dict); 693 new_page.Navigate(url, 694 base::Bind(&RespondOnUIThread, callback, result, response)); 695 } 696 } 697 698 DevToolsAndroidBridge::RemoteBrowser::~RemoteBrowser() { 699 } 700 701 // DevToolsAndroidBridge::RemoteDevice ---------------------------------------- 702 703 DevToolsAndroidBridge::RemoteDevice::RemoteDevice( 704 scoped_refptr<AndroidDeviceManager::Device> device, 705 const AndroidDeviceManager::DeviceInfo& device_info) 706 : device_(device), 707 model_(device_info.model), 708 connected_(device_info.connected), 709 screen_size_(device_info.screen_size) { 710 for (std::vector<AndroidDeviceManager::BrowserInfo>::const_iterator it = 711 device_info.browser_info.begin(); 712 it != device_info.browser_info.end(); 713 ++it) { 714 browsers_.push_back(new DevToolsAndroidBridge::RemoteBrowser(device, *it)); 715 } 716 } 717 718 void DevToolsAndroidBridge::RemoteDevice::OpenSocket( 719 const std::string& socket_name, 720 const AndroidDeviceManager::SocketCallback& callback) { 721 device_->OpenSocket(socket_name, callback); 722 } 723 724 DevToolsAndroidBridge::RemoteDevice::~RemoteDevice() { 725 } 726 727 // DevToolsAndroidBridge ------------------------------------------------------ 728 729 DevToolsAndroidBridge::DevToolsAndroidBridge(Profile* profile) 730 : profile_(profile), 731 device_manager_(AndroidDeviceManager::Create()), 732 task_scheduler_(base::Bind(&DevToolsAndroidBridge::ScheduleTaskDefault)) { 733 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 734 pref_change_registrar_.Init(profile_->GetPrefs()); 735 pref_change_registrar_.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled, 736 base::Bind(&DevToolsAndroidBridge::CreateDeviceProviders, 737 base::Unretained(this))); 738 CreateDeviceProviders(); 739 } 740 741 void DevToolsAndroidBridge::AddDeviceListListener( 742 DeviceListListener* listener) { 743 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 744 device_list_listeners_.push_back(listener); 745 if (device_list_listeners_.size() == 1) 746 StartDeviceListPolling(); 747 } 748 749 void DevToolsAndroidBridge::RemoveDeviceListListener( 750 DeviceListListener* listener) { 751 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 752 DeviceListListeners::iterator it = std::find( 753 device_list_listeners_.begin(), device_list_listeners_.end(), listener); 754 DCHECK(it != device_list_listeners_.end()); 755 device_list_listeners_.erase(it); 756 if (device_list_listeners_.empty()) 757 StopDeviceListPolling(); 758 } 759 760 void DevToolsAndroidBridge::AddDeviceCountListener( 761 DeviceCountListener* listener) { 762 device_count_listeners_.push_back(listener); 763 if (device_count_listeners_.size() == 1) 764 StartDeviceCountPolling(); 765 } 766 767 void DevToolsAndroidBridge::RemoveDeviceCountListener( 768 DeviceCountListener* listener) { 769 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 770 DeviceCountListeners::iterator it = std::find( 771 device_count_listeners_.begin(), device_count_listeners_.end(), listener); 772 DCHECK(it != device_count_listeners_.end()); 773 device_count_listeners_.erase(it); 774 if (device_count_listeners_.empty()) 775 StopDeviceCountPolling(); 776 } 777 778 // static 779 bool DevToolsAndroidBridge::HasDevToolsWindow(const std::string& agent_id) { 780 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 781 return g_host_delegates.Get().find(agent_id) != g_host_delegates.Get().end(); 782 } 783 784 DevToolsAndroidBridge::~DevToolsAndroidBridge() { 785 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 786 DCHECK(device_list_listeners_.empty()); 787 DCHECK(device_count_listeners_.empty()); 788 } 789 790 void DevToolsAndroidBridge::StartDeviceListPolling() { 791 device_list_callback_.Reset( 792 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceList, this)); 793 RequestDeviceList(device_list_callback_.callback()); 794 } 795 796 void DevToolsAndroidBridge::StopDeviceListPolling() { 797 device_list_callback_.Cancel(); 798 devices_.clear(); 799 } 800 801 void DevToolsAndroidBridge::RequestDeviceList( 802 const base::Callback<void(const RemoteDevices&)>& callback) { 803 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 804 805 if (device_list_listeners_.empty() || 806 !callback.Equals(device_list_callback_.callback())) 807 return; 808 809 new DiscoveryRequest(device_manager_.get(), callback); 810 } 811 812 void DevToolsAndroidBridge::ReceivedDeviceList(const RemoteDevices& devices) { 813 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 814 815 DeviceListListeners copy(device_list_listeners_); 816 for (DeviceListListeners::iterator it = copy.begin(); it != copy.end(); ++it) 817 (*it)->DeviceListChanged(devices); 818 819 if (device_list_listeners_.empty()) 820 return; 821 822 devices_ = devices; 823 824 task_scheduler_.Run( 825 base::Bind(&DevToolsAndroidBridge::RequestDeviceList, 826 this, device_list_callback_.callback())); 827 } 828 829 void DevToolsAndroidBridge::StartDeviceCountPolling() { 830 device_count_callback_.Reset( 831 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceCount, this)); 832 RequestDeviceCount(device_count_callback_.callback()); 833 } 834 835 void DevToolsAndroidBridge::StopDeviceCountPolling() { 836 device_count_callback_.Cancel(); 837 } 838 839 void DevToolsAndroidBridge::RequestDeviceCount( 840 const base::Callback<void(int)>& callback) { 841 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 842 843 if (device_count_listeners_.empty() || 844 !callback.Equals(device_count_callback_.callback())) 845 return; 846 847 UsbDeviceProvider::CountDevices(callback); 848 } 849 850 void DevToolsAndroidBridge::ReceivedDeviceCount(int count) { 851 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 852 853 DeviceCountListeners copy(device_count_listeners_); 854 for (DeviceCountListeners::iterator it = copy.begin(); it != copy.end(); ++it) 855 (*it)->DeviceCountChanged(count); 856 857 if (device_count_listeners_.empty()) 858 return; 859 860 task_scheduler_.Run( 861 base::Bind(&DevToolsAndroidBridge::RequestDeviceCount, 862 this, device_count_callback_.callback())); 863 } 864 865 // static 866 void DevToolsAndroidBridge::ScheduleTaskDefault(const base::Closure& task) { 867 BrowserThread::PostDelayedTask( 868 BrowserThread::UI, 869 FROM_HERE, 870 task, 871 base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs)); 872 } 873 874 void DevToolsAndroidBridge::CreateDeviceProviders() { 875 AndroidDeviceManager::DeviceProviders device_providers; 876 #if defined(DEBUG_DEVTOOLS) 877 BrowserListTabContentsProvider::EnableTethering(); 878 // We cannot rely on command line switch here as we might want to connect 879 // to another instance of Chrome. Using hard-coded port number instead. 880 const int kDefaultDebuggingPort = 9222; 881 device_providers.push_back(new SelfAsDeviceProvider(kDefaultDebuggingPort)); 882 #endif 883 device_providers.push_back(new AdbDeviceProvider()); 884 885 PrefService* service = profile_->GetPrefs(); 886 const PrefService::Preference* pref = 887 service->FindPreference(prefs::kDevToolsDiscoverUsbDevicesEnabled); 888 const base::Value* pref_value = pref->GetValue(); 889 890 bool enabled; 891 if (pref_value->GetAsBoolean(&enabled) && enabled) { 892 device_providers.push_back(new UsbDeviceProvider(profile_)); 893 } 894 device_manager_->SetDeviceProviders(device_providers); 895 if (!device_list_listeners_.empty()) { 896 StopDeviceListPolling(); 897 StartDeviceListPolling(); 898 } 899 } 900