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