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.
5 #include "chrome/browser/devtools/device/devtools_android_bridge.h"
10 #include "base/base64.h"
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/compiler_specific.h"
14 #include "base/json/json_reader.h"
15 #include "base/lazy_instance.h"
16 #include "base/memory/singleton.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/prefs/pref_service.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/threading/thread.h"
24 #include "base/values.h"
25 #include "chrome/browser/devtools/device/adb/adb_device_provider.h"
26 #include "chrome/browser/devtools/device/port_forwarding_controller.h"
27 #include "chrome/browser/devtools/device/self_device_provider.h"
28 #include "chrome/browser/devtools/device/usb/usb_device_provider.h"
29 #include "chrome/browser/devtools/device/webrtc/webrtc_device_provider.h"
30 #include "chrome/browser/devtools/devtools_protocol.h"
31 #include "chrome/browser/devtools/devtools_target_impl.h"
32 #include "chrome/browser/devtools/devtools_window.h"
33 #include "chrome/browser/devtools/remote_debugging_server.h"
34 #include "chrome/browser/profiles/profile.h"
35 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
36 #include "chrome/browser/signin/signin_manager_factory.h"
37 #include "chrome/common/chrome_switches.h"
38 #include "chrome/common/pref_names.h"
39 #include "components/keyed_service/content/browser_context_dependency_manager.h"
40 #include "components/signin/core/browser/profile_oauth2_token_service.h"
41 #include "components/signin/core/browser/signin_manager.h"
42 #include "content/public/browser/devtools_agent_host.h"
43 #include "content/public/browser/devtools_external_agent_proxy.h"
44 #include "content/public/browser/devtools_external_agent_proxy_delegate.h"
45 #include "content/public/browser/user_metrics.h"
46 #include "net/base/escape.h"
47 #include "net/base/net_errors.h"
49 using content::BrowserThread
;
53 const char kPageListRequest
[] = "/json";
54 const char kVersionRequest
[] = "/json/version";
55 const char kClosePageRequest
[] = "/json/close/%s";
56 const char kNewPageRequest
[] = "/json/new";
57 const char kNewPageRequestWithURL
[] = "/json/new?%s";
58 const char kActivatePageRequest
[] = "/json/activate/%s";
59 const char kBrowserTargetSocket
[] = "/devtools/browser";
60 const int kAdbPollingIntervalMs
= 1000;
62 const char kUrlParam
[] = "url";
63 const char kPageReloadCommand
[] = "Page.reload";
64 const char kPageNavigateCommand
[] = "Page.navigate";
66 const int kMinVersionNewWithURL
= 32;
67 const int kNewPageNavigateDelayMs
= 500;
69 bool IsWebRTCDeviceProviderEnabled() {
70 return base::CommandLine::ForCurrentProcess()->HasSwitch(
71 switches::kEnableDevToolsExperiments
);
76 // DiscoveryRequest -----------------------------------------------------
78 class DevToolsAndroidBridge::DiscoveryRequest
79 : public base::RefCountedThreadSafe
<DiscoveryRequest
,
80 BrowserThread::DeleteOnUIThread
> {
82 DiscoveryRequest(AndroidDeviceManager
* device_manager
,
83 const DeviceListCallback
& callback
);
85 friend struct BrowserThread::DeleteOnThread
<BrowserThread::UI
>;
86 friend class base::DeleteHelper
<DiscoveryRequest
>;
87 virtual ~DiscoveryRequest();
89 void ReceivedDevices(const AndroidDeviceManager::Devices
& devices
);
90 void ReceivedDeviceInfo(scoped_refptr
<AndroidDeviceManager::Device
> device
,
91 const AndroidDeviceManager::DeviceInfo
& device_info
);
92 void ReceivedVersion(scoped_refptr
<RemoteBrowser
>,
94 const std::string
& response
);
95 void ReceivedPages(scoped_refptr
<RemoteBrowser
>,
97 const std::string
& response
);
99 DeviceListCallback callback_
;
100 CompleteDevices complete_devices_
;
103 DevToolsAndroidBridge::DiscoveryRequest::DiscoveryRequest(
104 AndroidDeviceManager
* device_manager
,
105 const DeviceListCallback
& callback
)
106 : callback_(callback
) {
107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
108 device_manager
->QueryDevices(
109 base::Bind(&DiscoveryRequest::ReceivedDevices
, this));
112 DevToolsAndroidBridge::DiscoveryRequest::~DiscoveryRequest() {
113 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
114 callback_
.Run(complete_devices_
);
117 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedDevices(
118 const AndroidDeviceManager::Devices
& devices
) {
119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
120 for (const auto& device
: devices
) {
121 device
->QueryDeviceInfo(
122 base::Bind(&DiscoveryRequest::ReceivedDeviceInfo
, this, device
));
126 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedDeviceInfo(
127 scoped_refptr
<AndroidDeviceManager::Device
> device
,
128 const AndroidDeviceManager::DeviceInfo
& device_info
) {
129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
130 scoped_refptr
<RemoteDevice
> remote_device
=
131 new RemoteDevice(device
->serial(), device_info
);
132 complete_devices_
.push_back(std::make_pair(device
, remote_device
));
133 for (RemoteBrowsers::iterator it
= remote_device
->browsers().begin();
134 it
!= remote_device
->browsers().end(); ++it
) {
135 device
->SendJsonRequest(
138 base::Bind(&DiscoveryRequest::ReceivedVersion
, this, *it
));
139 device
->SendJsonRequest(
142 base::Bind(&DiscoveryRequest::ReceivedPages
, this, *it
));
146 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedVersion(
147 scoped_refptr
<RemoteBrowser
> browser
,
149 const std::string
& response
) {
150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
153 // Parse version, append to package name if available,
154 scoped_ptr
<base::Value
> value(base::JSONReader::Read(response
));
155 base::DictionaryValue
* dict
;
156 if (value
&& value
->GetAsDictionary(&dict
)) {
157 std::string browser_name
;
158 if (dict
->GetString("Browser", &browser_name
)) {
159 std::vector
<std::string
> parts
;
160 Tokenize(browser_name
, "/", &parts
);
161 if (parts
.size() == 2)
162 browser
->version_
= parts
[1];
164 browser
->version_
= browser_name
;
167 if (dict
->GetString("Android-Package", &package
)) {
168 browser
->display_name_
=
169 AndroidDeviceManager::GetBrowserName(browser
->socket(), package
);
174 void DevToolsAndroidBridge::DiscoveryRequest::ReceivedPages(
175 scoped_refptr
<RemoteBrowser
> browser
,
177 const std::string
& response
) {
178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
181 scoped_ptr
<base::Value
> value(base::JSONReader::Read(response
));
182 base::ListValue
* list_value
;
183 if (value
&& value
->GetAsList(&list_value
)) {
184 for (const auto& page_value
: *list_value
) {
185 base::DictionaryValue
* dict
;
186 if (page_value
->GetAsDictionary(&dict
)) {
187 browser
->pages_
.push_back(
188 new RemotePage(browser
->browser_id_
, *dict
, browser
->IsWebView()));
194 // ProtocolCommand ------------------------------------------------------------
198 class ProtocolCommand
199 : public AndroidDeviceManager::AndroidWebSocket::Delegate
{
202 scoped_refptr
<AndroidDeviceManager::Device
> device
,
203 const std::string
& socket
,
204 const std::string
& debug_url
,
205 const std::string
& command
,
206 const base::Closure callback
);
209 void OnSocketOpened() override
;
210 void OnFrameRead(const std::string
& message
) override
;
211 void OnSocketClosed() override
;
212 ~ProtocolCommand() override
;
214 const std::string command_
;
215 const base::Closure callback_
;
216 scoped_ptr
<AndroidDeviceManager::AndroidWebSocket
> web_socket_
;
218 DISALLOW_COPY_AND_ASSIGN(ProtocolCommand
);
221 ProtocolCommand::ProtocolCommand(
222 scoped_refptr
<AndroidDeviceManager::Device
> device
,
223 const std::string
& socket
,
224 const std::string
& debug_url
,
225 const std::string
& command
,
226 const base::Closure callback
)
229 web_socket_(device
->CreateWebSocket(socket
, debug_url
, this)) {
232 void ProtocolCommand::OnSocketOpened() {
233 web_socket_
->SendFrame(command_
);
236 void ProtocolCommand::OnFrameRead(const std::string
& message
) {
240 void ProtocolCommand::OnSocketClosed() {
244 ProtocolCommand::~ProtocolCommand() {
245 if (!callback_
.is_null())
252 DevToolsAndroidBridge::Factory
* DevToolsAndroidBridge::Factory::GetInstance() {
253 return Singleton
<DevToolsAndroidBridge::Factory
>::get();
257 DevToolsAndroidBridge
* DevToolsAndroidBridge::Factory::GetForProfile(
259 return static_cast<DevToolsAndroidBridge
*>(GetInstance()->
260 GetServiceForBrowserContext(profile
, true));
263 DevToolsAndroidBridge::Factory::Factory()
264 : BrowserContextKeyedServiceFactory(
265 "DevToolsAndroidBridge",
266 BrowserContextDependencyManager::GetInstance()) {
267 if (IsWebRTCDeviceProviderEnabled()) {
268 DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
269 DependsOn(SigninManagerFactory::GetInstance());
273 DevToolsAndroidBridge::Factory::~Factory() {}
275 KeyedService
* DevToolsAndroidBridge::Factory::BuildServiceInstanceFor(
276 content::BrowserContext
* context
) const {
277 Profile
* profile
= Profile::FromBrowserContext(context
);
279 ProfileOAuth2TokenService
* token_service
= nullptr;
280 SigninManagerBase
* signin_manager
= nullptr;
282 if (IsWebRTCDeviceProviderEnabled()) {
283 token_service
= ProfileOAuth2TokenServiceFactory::GetForProfile(profile
);
284 signin_manager
= SigninManagerFactory::GetForProfile(profile
);
287 return new DevToolsAndroidBridge(
288 profile
, signin_manager
, token_service
);
291 // AgentHostDelegate ----------------------------------------------------------
293 class DevToolsAndroidBridge::AgentHostDelegate
294 : public content::DevToolsExternalAgentProxyDelegate
,
295 public AndroidDeviceManager::AndroidWebSocket::Delegate
{
297 static scoped_refptr
<content::DevToolsAgentHost
> GetOrCreateAgentHost(
298 DevToolsAndroidBridge
* bridge
,
299 const std::string
& id
,
300 const BrowserId
& browser_id
,
301 const std::string
& debug_url
,
306 DevToolsAndroidBridge
* bridge
,
307 const std::string
& id
,
308 const BrowserId
& browser_id
,
309 const std::string
& debug_url
,
311 ~AgentHostDelegate() override
;
312 void Attach(content::DevToolsExternalAgentProxy
* proxy
) override
;
313 void Detach() override
;
314 void SendMessageToBackend(const std::string
& message
) override
;
315 void OnSocketOpened() override
;
316 void OnFrameRead(const std::string
& message
) override
;
317 void OnSocketClosed() override
;
320 base::WeakPtr
<DevToolsAndroidBridge
> bridge_
;
321 BrowserId browser_id_
;
322 std::string debug_url_
;
325 std::vector
<std::string
> pending_messages_
;
326 scoped_refptr
<AndroidDeviceManager::Device
> device_
;
327 scoped_ptr
<AndroidDeviceManager::AndroidWebSocket
> web_socket_
;
328 content::DevToolsAgentHost
* agent_host_
;
329 content::DevToolsExternalAgentProxy
* proxy_
;
330 DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate
);
334 scoped_refptr
<content::DevToolsAgentHost
>
335 DevToolsAndroidBridge::AgentHostDelegate::GetOrCreateAgentHost(
336 DevToolsAndroidBridge
* bridge
,
337 const std::string
& id
,
338 const BrowserId
& browser_id
,
339 const std::string
& debug_url
,
341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
342 AgentHostDelegates::iterator it
= bridge
->host_delegates_
.find(id
);
343 if (it
!= bridge
->host_delegates_
.end())
344 return it
->second
->agent_host_
;
346 AgentHostDelegate
* delegate
=
347 new AgentHostDelegate(bridge
, id
, browser_id
, debug_url
, is_web_view
);
348 scoped_refptr
<content::DevToolsAgentHost
> result
=
349 content::DevToolsAgentHost::Create(delegate
);
350 delegate
->agent_host_
= result
.get();
354 DevToolsAndroidBridge::AgentHostDelegate::AgentHostDelegate(
355 DevToolsAndroidBridge
* bridge
,
356 const std::string
& id
,
357 const BrowserId
& browser_id
,
358 const std::string
& debug_url
,
361 bridge_(bridge
->AsWeakPtr()),
362 browser_id_(browser_id
),
363 debug_url_(debug_url
),
364 socket_opened_(false),
365 is_web_view_(is_web_view
),
368 bridge_
->host_delegates_
[id
] = this;
371 DevToolsAndroidBridge::AgentHostDelegate::~AgentHostDelegate() {
373 bridge_
->host_delegates_
.erase(id_
);
376 void DevToolsAndroidBridge::AgentHostDelegate::Attach(
377 content::DevToolsExternalAgentProxy
* proxy
) {
379 content::RecordAction(is_web_view_
?
380 base::UserMetricsAction("DevTools_InspectAndroidWebView") :
381 base::UserMetricsAction("DevTools_InspectAndroidPage"));
383 // Retain the device so it's not released until AgentHost is detached.
385 device_
= bridge_
->FindDevice(browser_id_
.first
);
390 device_
->CreateWebSocket(browser_id_
.second
, debug_url_
, this));
393 void DevToolsAndroidBridge::AgentHostDelegate::Detach() {
398 void DevToolsAndroidBridge::AgentHostDelegate::SendMessageToBackend(
399 const std::string
& message
) {
401 web_socket_
->SendFrame(message
);
403 pending_messages_
.push_back(message
);
406 void DevToolsAndroidBridge::AgentHostDelegate::OnSocketOpened() {
407 socket_opened_
= true;
408 for (std::vector
<std::string
>::iterator it
= pending_messages_
.begin();
409 it
!= pending_messages_
.end(); ++it
) {
410 SendMessageToBackend(*it
);
412 pending_messages_
.clear();
415 void DevToolsAndroidBridge::AgentHostDelegate::OnFrameRead(
416 const std::string
& message
) {
418 proxy_
->DispatchOnClientHost(message
);
421 void DevToolsAndroidBridge::AgentHostDelegate::OnSocketClosed() {
423 proxy_
->ConnectionClosed();
426 //// RemotePageTarget ----------------------------------------------
428 class DevToolsAndroidBridge::RemotePageTarget
: public DevToolsTargetImpl
{
430 RemotePageTarget(DevToolsAndroidBridge
* bridge
,
431 const BrowserId
& browser_id
,
432 const base::DictionaryValue
& value
,
434 ~RemotePageTarget() override
;
436 // DevToolsTargetImpl overrides.
437 std::string
GetId() const override
;
438 bool IsAttached() const override
;
439 bool Activate() const override
;
440 bool Close() const override
;
441 void Inspect(Profile
* profile
) const override
;
442 void Reload() const override
;
444 void Navigate(const std::string
& url
, base::Closure callback
) const;
447 base::WeakPtr
<DevToolsAndroidBridge
> bridge_
;
448 BrowserId browser_id_
;
449 std::string debug_url_
;
450 std::string frontend_url_
;
451 std::string remote_id_
;
452 std::string remote_type_
;
453 std::string local_id_
;
454 DISALLOW_COPY_AND_ASSIGN(RemotePageTarget
);
457 static std::string
GetStringProperty(const base::DictionaryValue
& value
,
458 const std::string
& name
) {
460 value
.GetString(name
, &result
);
464 static std::string
BuildUniqueTargetId(
465 const DevToolsAndroidBridge::BrowserId
& browser_id
,
466 const base::DictionaryValue
& value
) {
467 return base::StringPrintf("%s:%s:%s", browser_id
.first
.c_str(),
468 browser_id
.second
.c_str(), GetStringProperty(value
, "id").c_str());
471 static std::string
GetFrontendURL(const base::DictionaryValue
& value
) {
472 std::string frontend_url
= GetStringProperty(value
, "devtoolsFrontendUrl");
473 size_t ws_param
= frontend_url
.find("?ws");
474 if (ws_param
!= std::string::npos
)
475 frontend_url
= frontend_url
.substr(0, ws_param
);
476 if (frontend_url
.find("http:") == 0)
477 frontend_url
= "https:" + frontend_url
.substr(5);
481 static std::string
GetDebugURL(const base::DictionaryValue
& value
) {
482 std::string debug_url
= GetStringProperty(value
, "webSocketDebuggerUrl");
484 if (debug_url
.find("ws://") == 0)
485 debug_url
= debug_url
.substr(5);
487 debug_url
= std::string();
491 DevToolsAndroidBridge::RemotePageTarget::RemotePageTarget(
492 DevToolsAndroidBridge
* bridge
,
493 const BrowserId
& browser_id
,
494 const base::DictionaryValue
& value
,
496 : DevToolsTargetImpl(AgentHostDelegate::GetOrCreateAgentHost(
498 BuildUniqueTargetId(browser_id
, value
),
502 bridge_(bridge
->AsWeakPtr()),
503 browser_id_(browser_id
),
504 debug_url_(GetDebugURL(value
)),
505 frontend_url_(GetFrontendURL(value
)),
506 remote_id_(GetStringProperty(value
, "id")),
507 remote_type_(GetStringProperty(value
, "type")),
508 local_id_(BuildUniqueTargetId(browser_id
, value
)) {
509 set_type("adb_page");
510 set_url(GURL(GetStringProperty(value
, "url")));
511 set_title(base::UTF16ToUTF8(net::UnescapeForHTML(base::UTF8ToUTF16(
512 GetStringProperty(value
, "title")))));
513 set_description(GetStringProperty(value
, "description"));
514 set_favicon_url(GURL(GetStringProperty(value
, "faviconUrl")));
515 debug_url_
= GetDebugURL(value
);
518 DevToolsAndroidBridge::RemotePageTarget::~RemotePageTarget() {
521 std::string
DevToolsAndroidBridge::RemotePageTarget::GetId() const {
525 bool DevToolsAndroidBridge::RemotePageTarget::IsAttached() const {
526 return debug_url_
.empty();
529 static void NoOp(int, const std::string
&) {}
531 void DevToolsAndroidBridge::RemotePageTarget::Inspect(Profile
* profile
) const {
533 bool isWorker
= remote_type_
== kTargetTypeWorker
||
534 remote_type_
== kTargetTypeServiceWorker
;
535 DevToolsWindow::OpenExternalFrontend(profile
, frontend_url_
, GetAgentHost(),
539 bool DevToolsAndroidBridge::RemotePageTarget::Activate() const {
543 std::string request
= base::StringPrintf(kActivatePageRequest
,
545 bridge_
->SendJsonRequest(browser_id_
, request
, base::Bind(&NoOp
));
549 bool DevToolsAndroidBridge::RemotePageTarget::Close() const {
553 std::string request
= base::StringPrintf(kClosePageRequest
,
555 bridge_
->SendJsonRequest(browser_id_
, request
, base::Bind(&NoOp
));
559 void DevToolsAndroidBridge::RemotePageTarget::Reload() const {
563 bridge_
->SendProtocolCommand(browser_id_
, debug_url_
, kPageReloadCommand
,
564 NULL
, base::Closure());
567 void DevToolsAndroidBridge::RemotePageTarget::Navigate(
568 const std::string
& url
,
569 base::Closure callback
) const {
573 scoped_ptr
<base::DictionaryValue
> params(new base::DictionaryValue
);
574 params
->SetString(kUrlParam
, url
);
575 bridge_
->SendProtocolCommand(browser_id_
, debug_url_
, kPageNavigateCommand
,
576 params
.Pass(), callback
);
579 // DevToolsAndroidBridge::RemotePage ------------------------------------------
581 DevToolsAndroidBridge::RemotePage::RemotePage(
582 const BrowserId
& browser_id
,
583 const base::DictionaryValue
& dict
,
585 : browser_id_(browser_id
),
586 frontend_url_(GetFrontendURL(dict
)),
587 is_web_view_(is_web_view
),
588 dict_(dict
.DeepCopy()) {
591 DevToolsAndroidBridge::RemotePage::~RemotePage() {
594 // DevToolsAndroidBridge::RemoteBrowser ---------------------------------------
596 DevToolsAndroidBridge::RemoteBrowser::RemoteBrowser(
597 const std::string
& serial
,
598 const AndroidDeviceManager::BrowserInfo
& browser_info
)
599 : browser_id_(std::make_pair(serial
, browser_info
.socket_name
)),
600 display_name_(browser_info
.display_name
),
601 user_(browser_info
.user
),
602 type_(browser_info
.type
) {
605 bool DevToolsAndroidBridge::RemoteBrowser::IsChrome() {
606 return type_
== AndroidDeviceManager::BrowserInfo::kTypeChrome
;
609 bool DevToolsAndroidBridge::RemoteBrowser::IsWebView() {
610 return type_
== AndroidDeviceManager::BrowserInfo::kTypeWebView
;
613 DevToolsAndroidBridge::RemoteBrowser::ParsedVersion
614 DevToolsAndroidBridge::RemoteBrowser::GetParsedVersion() {
615 ParsedVersion result
;
616 std::vector
<std::string
> parts
;
617 Tokenize(version_
, ".", &parts
);
618 for (size_t i
= 0; i
!= parts
.size(); ++i
) {
620 base::StringToInt(parts
[i
], &value
);
621 result
.push_back(value
);
627 DevToolsAndroidBridge::CreatePageTarget(scoped_refptr
<RemotePage
> page
) {
628 return new RemotePageTarget(this, page
->browser_id_
, *page
->dict_
,
632 void DevToolsAndroidBridge::SendJsonRequest(
633 const BrowserId
& browser_id
,
634 const std::string
& request
,
635 const JsonRequestCallback
& callback
) {
636 scoped_refptr
<AndroidDeviceManager::Device
> device(
637 FindDevice(browser_id
.first
));
639 callback
.Run(net::ERR_FAILED
, std::string());
642 device
->SendJsonRequest(browser_id
.second
, request
, callback
);
645 void DevToolsAndroidBridge::SendProtocolCommand(
646 const BrowserId
& browser_id
,
647 const std::string
& debug_url
,
648 const std::string
& method
,
649 scoped_ptr
<base::DictionaryValue
> params
,
650 const base::Closure callback
) {
651 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
652 if (debug_url
.empty())
654 scoped_refptr
<AndroidDeviceManager::Device
> device(
655 FindDevice(browser_id
.first
));
661 device
, browser_id
.second
, debug_url
,
662 DevToolsProtocol::SerializeCommand(1, method
, params
.Pass()),
666 scoped_refptr
<content::DevToolsAgentHost
>
667 DevToolsAndroidBridge::GetBrowserAgentHost(
668 scoped_refptr
<RemoteBrowser
> browser
) {
669 return AgentHostDelegate::GetOrCreateAgentHost(
671 "adb:" + browser
->serial() + ":" + browser
->socket(),
672 browser
->browser_id_
,
673 kBrowserTargetSocket
,
674 browser
->IsWebView());
677 scoped_refptr
<AndroidDeviceManager::Device
> DevToolsAndroidBridge::FindDevice(
678 const std::string
& serial
) {
679 DeviceMap::iterator it
= device_map_
.find(serial
);
680 return it
== device_map_
.end() ? nullptr : it
->second
;
683 void DevToolsAndroidBridge::RespondToOpenOnUIThread(
684 scoped_refptr
<RemoteBrowser
> browser
,
685 const RemotePageCallback
& callback
,
687 const std::string
& response
) {
688 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
693 scoped_ptr
<base::Value
> value(base::JSONReader::Read(response
));
694 base::DictionaryValue
* dict
;
695 if (value
&& value
->GetAsDictionary(&dict
)) {
696 scoped_refptr
<RemotePage
> new_page(
697 new RemotePage(browser
->browser_id_
, *dict
, browser
->IsWebView()));
698 callback
.Run(new_page
);
702 void DevToolsAndroidBridge::OpenRemotePage(
703 scoped_refptr
<RemoteBrowser
> browser
,
704 const std::string
& input_url
,
705 const DevToolsAndroidBridge::RemotePageCallback
& callback
) {
706 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
707 GURL
gurl(input_url
);
708 if (!gurl
.is_valid()) {
709 gurl
= GURL("http://" + input_url
);
710 if (!gurl
.is_valid())
713 std::string url
= gurl
.spec();
714 RemoteBrowser::ParsedVersion parsed_version
= browser
->GetParsedVersion();
716 if (browser
->IsChrome() &&
717 !parsed_version
.empty() &&
718 parsed_version
[0] >= kMinVersionNewWithURL
) {
719 std::string query
= net::EscapeQueryParamValue(url
, false /* use_plus */);
720 std::string request
=
721 base::StringPrintf(kNewPageRequestWithURL
, query
.c_str());
722 SendJsonRequest(browser
->browser_id_
, request
,
723 base::Bind(&DevToolsAndroidBridge::RespondToOpenOnUIThread
,
724 AsWeakPtr(), browser
, callback
));
726 SendJsonRequest(browser
->browser_id_
, kNewPageRequest
,
727 base::Bind(&DevToolsAndroidBridge::PageCreatedOnUIThread
,
728 AsWeakPtr(), browser
, callback
, url
));
732 void DevToolsAndroidBridge::PageCreatedOnUIThread(
733 scoped_refptr
<RemoteBrowser
> browser
,
734 const RemotePageCallback
& callback
,
735 const std::string
& url
,
737 const std::string
& response
) {
738 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
742 // Navigating too soon after the page creation breaks navigation history
743 // (crbug.com/311014). This can be avoided by adding a moderate delay.
744 BrowserThread::PostDelayedTask(
745 BrowserThread::UI
, FROM_HERE
,
746 base::Bind(&DevToolsAndroidBridge::NavigatePageOnUIThread
,
747 AsWeakPtr(), browser
, callback
, result
, response
, url
),
748 base::TimeDelta::FromMilliseconds(kNewPageNavigateDelayMs
));
751 void DevToolsAndroidBridge::NavigatePageOnUIThread(
752 scoped_refptr
<RemoteBrowser
> browser
,
753 const RemotePageCallback
& callback
,
755 const std::string
& response
,
756 const std::string
& url
) {
757 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
758 scoped_ptr
<base::Value
> value(base::JSONReader::Read(response
));
759 base::DictionaryValue
* dict
;
761 if (value
&& value
->GetAsDictionary(&dict
)) {
762 RemotePageTarget
new_page(this, browser
->browser_id_
, *dict
,
763 browser
->IsWebView());
764 new_page
.Navigate(url
,
765 base::Bind(&DevToolsAndroidBridge::RespondToOpenOnUIThread
,
766 AsWeakPtr(), browser
, callback
, result
, response
));
770 DevToolsAndroidBridge::RemoteBrowser::~RemoteBrowser() {
773 // DevToolsAndroidBridge::RemoteDevice ----------------------------------------
775 DevToolsAndroidBridge::RemoteDevice::RemoteDevice(
776 const std::string
& serial
,
777 const AndroidDeviceManager::DeviceInfo
& device_info
)
779 model_(device_info
.model
),
780 connected_(device_info
.connected
),
781 screen_size_(device_info
.screen_size
) {
782 for (std::vector
<AndroidDeviceManager::BrowserInfo
>::const_iterator it
=
783 device_info
.browser_info
.begin();
784 it
!= device_info
.browser_info
.end();
786 browsers_
.push_back(new RemoteBrowser(serial
, *it
));
790 DevToolsAndroidBridge::RemoteDevice::~RemoteDevice() {
793 // DevToolsAndroidBridge ------------------------------------------------------
795 DevToolsAndroidBridge::DevToolsAndroidBridge(
797 SigninManagerBase
* signin_manager
,
798 ProfileOAuth2TokenService
* const token_service
)
800 signin_manager_(signin_manager
),
801 token_service_(token_service
),
802 device_manager_(AndroidDeviceManager::Create()),
803 task_scheduler_(base::Bind(&DevToolsAndroidBridge::ScheduleTaskDefault
)),
804 port_forwarding_controller_(new PortForwardingController(profile
, this)),
805 weak_factory_(this) {
806 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
807 pref_change_registrar_
.Init(profile_
->GetPrefs());
808 pref_change_registrar_
.Add(prefs::kDevToolsDiscoverUsbDevicesEnabled
,
809 base::Bind(&DevToolsAndroidBridge::CreateDeviceProviders
,
810 base::Unretained(this)));
811 CreateDeviceProviders();
814 void DevToolsAndroidBridge::AddDeviceListListener(
815 DeviceListListener
* listener
) {
816 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
817 bool polling_was_off
= !NeedsDeviceListPolling();
818 device_list_listeners_
.push_back(listener
);
820 StartDeviceListPolling();
823 void DevToolsAndroidBridge::RemoveDeviceListListener(
824 DeviceListListener
* listener
) {
825 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
826 DeviceListListeners::iterator it
= std::find(
827 device_list_listeners_
.begin(), device_list_listeners_
.end(), listener
);
828 DCHECK(it
!= device_list_listeners_
.end());
829 device_list_listeners_
.erase(it
);
830 if (!NeedsDeviceListPolling())
831 StopDeviceListPolling();
834 void DevToolsAndroidBridge::AddDeviceCountListener(
835 DeviceCountListener
* listener
) {
836 device_count_listeners_
.push_back(listener
);
837 if (device_count_listeners_
.size() == 1)
838 StartDeviceCountPolling();
841 void DevToolsAndroidBridge::RemoveDeviceCountListener(
842 DeviceCountListener
* listener
) {
843 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
844 DeviceCountListeners::iterator it
= std::find(
845 device_count_listeners_
.begin(), device_count_listeners_
.end(), listener
);
846 DCHECK(it
!= device_count_listeners_
.end());
847 device_count_listeners_
.erase(it
);
848 if (device_count_listeners_
.empty())
849 StopDeviceCountPolling();
852 void DevToolsAndroidBridge::AddPortForwardingListener(
853 PortForwardingListener
* listener
) {
854 bool polling_was_off
= !NeedsDeviceListPolling();
855 port_forwarding_listeners_
.push_back(listener
);
857 StartDeviceListPolling();
860 void DevToolsAndroidBridge::RemovePortForwardingListener(
861 PortForwardingListener
* listener
) {
862 PortForwardingListeners::iterator it
= std::find(
863 port_forwarding_listeners_
.begin(),
864 port_forwarding_listeners_
.end(),
866 DCHECK(it
!= port_forwarding_listeners_
.end());
867 port_forwarding_listeners_
.erase(it
);
868 if (!NeedsDeviceListPolling())
869 StopDeviceListPolling();
872 bool DevToolsAndroidBridge::HasDevToolsWindow(const std::string
& agent_id
) {
873 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
874 return host_delegates_
.find(agent_id
) != host_delegates_
.end();
877 DevToolsAndroidBridge::~DevToolsAndroidBridge() {
878 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
879 DCHECK(device_list_listeners_
.empty());
880 DCHECK(device_count_listeners_
.empty());
881 DCHECK(port_forwarding_listeners_
.empty());
884 void DevToolsAndroidBridge::StartDeviceListPolling() {
885 device_list_callback_
.Reset(
886 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceList
, AsWeakPtr()));
887 RequestDeviceList(device_list_callback_
.callback());
890 void DevToolsAndroidBridge::StopDeviceListPolling() {
891 device_list_callback_
.Cancel();
895 bool DevToolsAndroidBridge::NeedsDeviceListPolling() {
896 return !device_list_listeners_
.empty() || !port_forwarding_listeners_
.empty();
899 void DevToolsAndroidBridge::RequestDeviceList(
900 const DeviceListCallback
& callback
) {
901 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
903 if (!NeedsDeviceListPolling() ||
904 !callback
.Equals(device_list_callback_
.callback()))
907 new DiscoveryRequest(device_manager_
.get(), callback
);
910 void DevToolsAndroidBridge::ReceivedDeviceList(
911 const CompleteDevices
& complete_devices
) {
912 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
915 RemoteDevices remote_devices
;
916 for (const auto& pair
: complete_devices
) {
917 device_map_
[pair
.first
->serial()] = pair
.first
;
918 remote_devices
.push_back(pair
.second
);
921 DeviceListListeners
copy(device_list_listeners_
);
922 for (DeviceListListeners::iterator it
= copy
.begin(); it
!= copy
.end(); ++it
)
923 (*it
)->DeviceListChanged(remote_devices
);
925 ForwardingStatus status
=
926 port_forwarding_controller_
->DeviceListChanged(remote_devices
);
927 PortForwardingListeners
forwarding_listeners(port_forwarding_listeners_
);
928 for (PortForwardingListeners::iterator it
= forwarding_listeners
.begin();
929 it
!= forwarding_listeners
.end(); ++it
) {
930 (*it
)->PortStatusChanged(status
);
933 if (!NeedsDeviceListPolling())
937 base::Bind(&DevToolsAndroidBridge::RequestDeviceList
,
938 AsWeakPtr(), device_list_callback_
.callback()));
941 void DevToolsAndroidBridge::StartDeviceCountPolling() {
942 device_count_callback_
.Reset(
943 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceCount
, AsWeakPtr()));
944 RequestDeviceCount(device_count_callback_
.callback());
947 void DevToolsAndroidBridge::StopDeviceCountPolling() {
948 device_count_callback_
.Cancel();
951 void DevToolsAndroidBridge::RequestDeviceCount(
952 const base::Callback
<void(int)>& callback
) {
953 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
955 if (device_count_listeners_
.empty() ||
956 !callback
.Equals(device_count_callback_
.callback()))
959 UsbDeviceProvider::CountDevices(callback
);
962 void DevToolsAndroidBridge::ReceivedDeviceCount(int count
) {
963 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
965 DeviceCountListeners
copy(device_count_listeners_
);
966 for (DeviceCountListeners::iterator it
= copy
.begin(); it
!= copy
.end(); ++it
)
967 (*it
)->DeviceCountChanged(count
);
969 if (device_count_listeners_
.empty())
973 base::Bind(&DevToolsAndroidBridge::RequestDeviceCount
,
974 AsWeakPtr(), device_count_callback_
.callback()));
978 void DevToolsAndroidBridge::ScheduleTaskDefault(const base::Closure
& task
) {
979 BrowserThread::PostDelayedTask(
983 base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs
));
986 void DevToolsAndroidBridge::CreateDeviceProviders() {
987 AndroidDeviceManager::DeviceProviders device_providers
;
988 #if defined(DEBUG_DEVTOOLS)
989 RemoteDebuggingServer::EnableTetheringForDebug();
990 // We cannot rely on command line switch here as we might want to connect
991 // to another instance of Chrome. Using hard-coded port number instead.
992 const int kDefaultDebuggingPort
= 9222;
993 device_providers
.push_back(new SelfAsDeviceProvider(kDefaultDebuggingPort
));
995 device_providers
.push_back(new AdbDeviceProvider());
997 PrefService
* service
= profile_
->GetPrefs();
998 const PrefService::Preference
* pref
=
999 service
->FindPreference(prefs::kDevToolsDiscoverUsbDevicesEnabled
);
1000 const base::Value
* pref_value
= pref
->GetValue();
1003 if (pref_value
->GetAsBoolean(&enabled
) && enabled
) {
1004 device_providers
.push_back(new UsbDeviceProvider(profile_
));
1007 if (IsWebRTCDeviceProviderEnabled()) {
1008 device_providers
.push_back(
1009 new WebRTCDeviceProvider(profile_
, signin_manager_
, token_service_
));
1012 device_manager_
->SetDeviceProviders(device_providers
);
1013 if (NeedsDeviceListPolling()) {
1014 StopDeviceListPolling();
1015 StartDeviceListPolling();