• 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/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, &params,
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