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
DiscoveryRequest(AndroidDeviceManager * device_manager,const DiscoveryCallback & callback)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
~DiscoveryRequest()105 DiscoveryRequest::~DiscoveryRequest() {
106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
107 callback_.Run(remote_devices_);
108 }
109
ReceivedDevices(const Devices & devices)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
ReceivedDeviceInfo(scoped_refptr<Device> device,const DeviceInfo & device_info)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
ReceivedVersion(scoped_refptr<RemoteBrowser> browser,int result,const std::string & response)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
ReceivedPages(scoped_refptr<RemoteBrowser> browser,int result,const std::string & response)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
ProtocolCommand(scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,const std::string & debug_url,const std::string & command,const base::Closure callback)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
OnSocketOpened()209 void ProtocolCommand::OnSocketOpened() {
210 web_socket_->SendFrame(command_);
211 }
212
OnFrameRead(const std::string & message)213 void ProtocolCommand::OnFrameRead(const std::string& message) {
214 web_socket_->Disconnect();
215 }
216
OnSocketClosed(bool closed_by_device)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
Wrapper(content::BrowserContext * context)233 DevToolsAndroidBridge::Wrapper::Wrapper(content::BrowserContext* context) {
234 bridge_ = new DevToolsAndroidBridge(Profile::FromBrowserContext(context));
235 }
236
~Wrapper()237 DevToolsAndroidBridge::Wrapper::~Wrapper() {
238 }
239
Get()240 DevToolsAndroidBridge* DevToolsAndroidBridge::Wrapper::Get() {
241 return bridge_.get();
242 }
243
244 // static
GetInstance()245 DevToolsAndroidBridge::Factory* DevToolsAndroidBridge::Factory::GetInstance() {
246 return Singleton<DevToolsAndroidBridge::Factory>::get();
247 }
248
249 // static
GetForProfile(Profile * profile)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
Factory()258 DevToolsAndroidBridge::Factory::Factory()
259 : BrowserContextKeyedServiceFactory(
260 "DevToolsAndroidBridge",
261 BrowserContextDependencyManager::GetInstance()) {}
262
~Factory()263 DevToolsAndroidBridge::Factory::~Factory() {}
264
BuildServiceInstanceFor(content::BrowserContext * context) const265 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>
GetOrCreateAgentHost(const std::string & id,scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,const std::string & debug_url)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
AgentHostDelegate(const std::string & id,scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,const std::string & debug_url)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
~AgentHostDelegate()339 AgentHostDelegate::~AgentHostDelegate() {
340 g_host_delegates.Get().erase(id_);
341 web_socket_->ClearDelegate();
342 }
343
Attach(content::DevToolsExternalAgentProxy * proxy)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
Detach()351 void AgentHostDelegate::Detach() {
352 detached_ = true;
353 if (socket_opened_)
354 web_socket_->Disconnect();
355 }
356
SendMessageToBackend(const std::string & message)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
OnSocketOpened()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
OnFrameRead(const std::string & message)378 void AgentHostDelegate::OnFrameRead(const std::string& message) {
379 if (proxy_)
380 proxy_->DispatchOnClientHost(message);
381 }
382
OnSocketClosed(bool closed_by_device)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
GetStringProperty(const base::DictionaryValue & value,const std::string & name)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
BuildUniqueTargetId(DevToolsAndroidBridge::RemoteBrowser * browser,const base::DictionaryValue & value)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
GetDebugURL(const base::DictionaryValue & value)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
RemotePageTarget(scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,const base::DictionaryValue & value)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
~RemotePageTarget()468 RemotePageTarget::~RemotePageTarget() {
469 }
470
GetTarget()471 DevToolsTargetImpl* RemotePageTarget::GetTarget() {
472 return this;
473 }
474
GetFrontendURL()475 std::string RemotePageTarget::GetFrontendURL() {
476 return frontend_url_;
477 }
478
GetId() const479 std::string RemotePageTarget::GetId() const {
480 return remote_id_;
481 }
482
IsAttached() const483 bool RemotePageTarget::IsAttached() const {
484 return debug_url_.empty();
485 }
486
NoOp(int,const std::string &)487 static void NoOp(int, const std::string&) {}
488
Inspect(Profile * profile) const489 void RemotePageTarget::Inspect(Profile* profile) const {
490 Activate();
491 DevToolsWindow::OpenExternalFrontend(profile, frontend_url_,
492 GetAgentHost());
493 }
494
Activate() const495 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
Close() const502 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
Reload() const509 void RemotePageTarget::Reload() const {
510 browser_->SendProtocolCommand(debug_url_, kPageReloadCommand, NULL,
511 base::Closure());
512 }
513
Navigate(const std::string & url,base::Closure callback) const514 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
RemoteBrowser(scoped_refptr<Device> device,const AndroidDeviceManager::BrowserInfo & browser_info)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
IsChrome() const534 bool DevToolsAndroidBridge::RemoteBrowser::IsChrome() const {
535 return type_ == AndroidDeviceManager::BrowserInfo::kTypeChrome;
536 }
537
IsWebView() const538 bool DevToolsAndroidBridge::RemoteBrowser::IsWebView() const {
539 return type_ == AndroidDeviceManager::BrowserInfo::kTypeWebView;
540 }
541
542 DevToolsAndroidBridge::RemoteBrowser::ParsedVersion
GetParsedVersion() const543 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*>
CreatePages()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
SetPageDescriptors(const base::ListValue & list)571 void DevToolsAndroidBridge::RemoteBrowser::SetPageDescriptors(
572 const base::ListValue& list) {
573 page_descriptors_.reset(list.DeepCopy());
574 }
575
RespondOnUIThread(const DevToolsAndroidBridge::JsonRequestCallback & callback,int result,const std::string & response)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
SendJsonRequest(const std::string & request,const JsonRequestCallback & callback)586 void DevToolsAndroidBridge::RemoteBrowser::SendJsonRequest(
587 const std::string& request, const JsonRequestCallback& callback) {
588 device_->SendJsonRequest(socket_, request, callback);
589 }
590
SendProtocolCommand(const std::string & debug_url,const std::string & method,base::DictionaryValue * params,const base::Closure callback)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
Open(const std::string & url,const DevToolsAndroidBridge::RemotePageCallback & callback)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>
GetAgentHost()612 DevToolsAndroidBridge::RemoteBrowser::GetAgentHost() {
613 return AgentHostDelegate::GetOrCreateAgentHost(
614 "adb:" + device_->serial() + ":" + socket_, this, kBrowserTargetSocket);
615 }
616
617 scoped_refptr<DevToolsAndroidBridge::AndroidWebSocket>
CreateWebSocket(const std::string & url,DevToolsAndroidBridge::AndroidWebSocket::Delegate * delegate)618 DevToolsAndroidBridge::RemoteBrowser::CreateWebSocket(
619 const std::string& url,
620 DevToolsAndroidBridge::AndroidWebSocket::Delegate* delegate) {
621 return device_->CreateWebSocket(socket_, url, delegate);
622 }
623
RespondToOpenOnUIThread(const DevToolsAndroidBridge::RemotePageCallback & callback,int result,const std::string & response)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
InnerOpen(const std::string & input_url,const JsonRequestCallback & callback)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
PageCreatedOnUIThread(const JsonRequestCallback & callback,const std::string & url,int result,const std::string & response)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
NavigatePageOnUIThread(const JsonRequestCallback & callback,int result,const std::string & response,const std::string & url)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
~RemoteBrowser()698 DevToolsAndroidBridge::RemoteBrowser::~RemoteBrowser() {
699 }
700
701 // DevToolsAndroidBridge::RemoteDevice ----------------------------------------
702
RemoteDevice(scoped_refptr<AndroidDeviceManager::Device> device,const AndroidDeviceManager::DeviceInfo & device_info)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
OpenSocket(const std::string & socket_name,const AndroidDeviceManager::SocketCallback & callback)718 void DevToolsAndroidBridge::RemoteDevice::OpenSocket(
719 const std::string& socket_name,
720 const AndroidDeviceManager::SocketCallback& callback) {
721 device_->OpenSocket(socket_name, callback);
722 }
723
~RemoteDevice()724 DevToolsAndroidBridge::RemoteDevice::~RemoteDevice() {
725 }
726
727 // DevToolsAndroidBridge ------------------------------------------------------
728
DevToolsAndroidBridge(Profile * profile)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
AddDeviceListListener(DeviceListListener * listener)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
RemoveDeviceListListener(DeviceListListener * listener)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
AddDeviceCountListener(DeviceCountListener * listener)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
RemoveDeviceCountListener(DeviceCountListener * listener)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
HasDevToolsWindow(const std::string & agent_id)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
~DevToolsAndroidBridge()784 DevToolsAndroidBridge::~DevToolsAndroidBridge() {
785 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
786 DCHECK(device_list_listeners_.empty());
787 DCHECK(device_count_listeners_.empty());
788 }
789
StartDeviceListPolling()790 void DevToolsAndroidBridge::StartDeviceListPolling() {
791 device_list_callback_.Reset(
792 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceList, this));
793 RequestDeviceList(device_list_callback_.callback());
794 }
795
StopDeviceListPolling()796 void DevToolsAndroidBridge::StopDeviceListPolling() {
797 device_list_callback_.Cancel();
798 devices_.clear();
799 }
800
RequestDeviceList(const base::Callback<void (const RemoteDevices &)> & callback)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
ReceivedDeviceList(const RemoteDevices & devices)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
StartDeviceCountPolling()829 void DevToolsAndroidBridge::StartDeviceCountPolling() {
830 device_count_callback_.Reset(
831 base::Bind(&DevToolsAndroidBridge::ReceivedDeviceCount, this));
832 RequestDeviceCount(device_count_callback_.callback());
833 }
834
StopDeviceCountPolling()835 void DevToolsAndroidBridge::StopDeviceCountPolling() {
836 device_count_callback_.Cancel();
837 }
838
RequestDeviceCount(const base::Callback<void (int)> & callback)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
ReceivedDeviceCount(int count)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
ScheduleTaskDefault(const base::Closure & task)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
CreateDeviceProviders()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