1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/devtools/devtools_adb_bridge.h"
10 #include "base/base64.h"
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/compiler_specific.h"
14 #include "base/json/json_reader.h"
15 #include "base/lazy_instance.h"
16 #include "base/logging.h"
17 #include "base/memory/singleton.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/threading/thread.h"
24 #include "base/values.h"
25 #include "chrome/browser/devtools/adb/android_rsa.h"
26 #include "chrome/browser/devtools/adb_client_socket.h"
27 #include "chrome/browser/devtools/adb_web_socket.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 "components/browser_context_keyed_service/browser_context_dependency_manager.h"
33 #include "content/public/browser/devtools_agent_host.h"
34 #include "content/public/browser/devtools_client_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/devtools_manager.h"
38 #include "crypto/rsa_private_key.h"
39 #include "net/base/escape.h"
40 #include "net/base/net_errors.h"
42 using content::BrowserThread;
46 const char kDeviceModelCommand[] = "shell:getprop ro.product.model";
47 const char kOpenedUnixSocketsCommand[] = "shell:cat /proc/net/unix";
48 const char kListProcessesCommand[] = "shell:ps";
49 const char kDumpsysCommand[] = "shell:dumpsys window policy";
50 const char kDumpsysScreenSizePrefix[] = "mStable=";
52 const char kUnknownModel[] = "Offline";
54 const char kPageListRequest[] = "GET /json HTTP/1.1\r\n\r\n";
55 const char kVersionRequest[] = "GET /json/version HTTP/1.1\r\n\r\n";
56 const char kClosePageRequest[] = "GET /json/close/%s HTTP/1.1\r\n\r\n";
57 const char kNewPageRequest[] = "GET /json/new HTTP/1.1\r\n\r\n";
58 const char kNewPageRequestWithURL[] = "GET /json/new?%s HTTP/1.1\r\n\r\n";
59 const char kActivatePageRequest[] =
60 "GET /json/activate/%s HTTP/1.1\r\n\r\n";
61 const int kAdbPollingIntervalMs = 1000;
63 const char kUrlParam[] = "url";
64 const char kPageReloadCommand[] = "Page.reload";
65 const char kPageNavigateCommand[] = "Page.navigate";
67 const char kChromeProductName[] = "Chrome";
68 const int kMinVersionNewWithURL = 32;
69 const int kNewPageNavigateDelayMs = 500;
71 #if defined(DEBUG_DEVTOOLS)
72 const char kLocalChrome[] = "Local Chrome";
73 #endif // defined(DEBUG_DEVTOOLS)
75 typedef DevToolsAdbBridge::Callback Callback;
76 typedef std::vector<scoped_refptr<AndroidDevice> >
78 typedef base::Callback<void(const AndroidDevices&)> AndroidDevicesCallback;
80 // AdbPagesCommand ------------------------------------------------------------
82 class AdbPagesCommand : public base::RefCountedThreadSafe<
84 BrowserThread::DeleteOnUIThread> {
86 typedef base::Callback<void(DevToolsAdbBridge::RemoteDevices*)> Callback;
89 scoped_refptr<RefCountedAdbThread> adb_thread,
90 const DevToolsAdbBridge::DeviceProviders& device_providers,
91 const Callback& callback);
94 friend struct BrowserThread::DeleteOnThread<
96 friend class base::DeleteHelper<AdbPagesCommand>;
98 virtual ~AdbPagesCommand();
99 void ProcessDeviceProviders();
100 void ReceivedDevices(const AndroidDevices& devices);
102 void ProcessSerials();
103 void ReceivedModel(int result, const std::string& response);
104 void ReceivedSockets(int result, const std::string& response);
105 void ReceivedDumpsys(int result, const std::string& response);
106 void ReceivedProcesses(int result, const std::string& response);
107 void ProcessSockets();
108 void ReceivedVersion(int result, const std::string& response);
109 void ReceivedPages(int result, const std::string& response);
111 void ParseSocketsList(const std::string& response);
112 void ParseProcessList(const std::string& response);
113 void ParseDumpsysResponse(const std::string& response);
114 void ParseScreenSize(const std::string& str);
116 scoped_refptr<RefCountedAdbThread> adb_thread_;
118 AndroidDevices devices_;
119 DevToolsAdbBridge::RemoteBrowsers browsers_;
120 scoped_ptr<DevToolsAdbBridge::RemoteDevices> remote_devices_;
121 DevToolsAdbBridge::DeviceProviders device_providers_;
124 AdbPagesCommand::AdbPagesCommand(
125 scoped_refptr<RefCountedAdbThread> adb_thread,
126 const DevToolsAdbBridge::DeviceProviders& device_providers,
127 const Callback& callback)
128 : adb_thread_(adb_thread),
130 device_providers_(device_providers){
131 remote_devices_.reset(new DevToolsAdbBridge::RemoteDevices());
133 ProcessDeviceProviders();
136 AdbPagesCommand::~AdbPagesCommand() {
137 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
140 void AdbPagesCommand::ProcessDeviceProviders() {
141 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
142 if (device_providers_.empty()) {
143 adb_thread_->message_loop()->PostTask(
144 FROM_HERE, base::Bind(&AdbPagesCommand::ProcessSerials, this));
148 const scoped_refptr<AndroidDeviceProvider>& device_provider =
149 device_providers_.back();
151 device_provider->QueryDevices(
152 base::Bind(&AdbPagesCommand::ReceivedDevices, this));
155 void AdbPagesCommand::ReceivedDevices(const AndroidDevices& devices) {
156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
157 DCHECK(!device_providers_.empty());
158 device_providers_.pop_back();
160 devices_.insert(devices_.end(), devices.begin(), devices.end());
162 if (!device_providers_.empty()) {
163 ProcessDeviceProviders();
165 adb_thread_->message_loop()->PostTask(
166 FROM_HERE, base::Bind(&AdbPagesCommand::ProcessSerials, this));
170 void AdbPagesCommand::ProcessSerials() {
171 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
172 if (devices_.size() == 0) {
173 BrowserThread::PostTask(
174 BrowserThread::UI, FROM_HERE,
175 base::Bind(&AdbPagesCommand::Respond, this));
179 #if defined(DEBUG_DEVTOOLS)
180 // For desktop remote debugging.
181 if (devices_.back()->serial().empty()) {
182 scoped_refptr<AndroidDevice> device =
184 device->set_model(kLocalChrome);
185 remote_devices_->push_back(
186 new DevToolsAdbBridge::RemoteDevice(device));
187 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> remote_browser =
188 new DevToolsAdbBridge::RemoteBrowser(
189 adb_thread_, device, std::string());
190 remote_browser->set_product(kChromeProductName);
191 remote_devices_->back()->AddBrowser(remote_browser);
192 browsers_.push_back(remote_browser);
194 std::string(), kVersionRequest,
195 base::Bind(&AdbPagesCommand::ReceivedVersion, this));
198 #endif // defined(DEBUG_DEVTOOLS)
200 scoped_refptr<AndroidDevice> device = devices_.back();
201 if (device->is_connected()) {
202 device->RunCommand(kDeviceModelCommand,
203 base::Bind(&AdbPagesCommand::ReceivedModel, this));
205 device->set_model(kUnknownModel);
206 remote_devices_->push_back(new DevToolsAdbBridge::RemoteDevice(device));
212 void AdbPagesCommand::ReceivedModel(int result, const std::string& response) {
213 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
219 scoped_refptr<AndroidDevice> device = devices_.back();
220 device->set_model(response);
221 remote_devices_->push_back(
222 new DevToolsAdbBridge::RemoteDevice(device));
223 device->RunCommand(kOpenedUnixSocketsCommand,
224 base::Bind(&AdbPagesCommand::ReceivedSockets, this));
227 void AdbPagesCommand::ReceivedSockets(int result,
228 const std::string& response) {
229 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
236 ParseSocketsList(response);
237 scoped_refptr<AndroidDevice> device = devices_.back();
238 device->RunCommand(kDumpsysCommand,
239 base::Bind(&AdbPagesCommand::ReceivedDumpsys, this));
242 void AdbPagesCommand::ReceivedDumpsys(int result,
243 const std::string& response) {
244 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
246 ParseDumpsysResponse(response);
248 scoped_refptr<AndroidDevice> device = devices_.back();
249 device->RunCommand(kListProcessesCommand,
250 base::Bind(&AdbPagesCommand::ReceivedProcesses, this));
253 void AdbPagesCommand::ReceivedProcesses(int result,
254 const std::string& response) {
256 ParseProcessList(response);
258 if (browsers_.size() == 0) {
266 void AdbPagesCommand::ProcessSockets() {
267 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
268 if (browsers_.size() == 0) {
272 scoped_refptr<AndroidDevice> device = devices_.back();
273 device->HttpQuery(browsers_.back()->socket(), kVersionRequest,
274 base::Bind(&AdbPagesCommand::ReceivedVersion, this));
278 void AdbPagesCommand::ReceivedVersion(int result,
279 const std::string& response) {
280 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
282 browsers_.pop_back();
287 // Parse version, append to package name if available,
288 scoped_ptr<base::Value> value(base::JSONReader::Read(response));
289 base::DictionaryValue* dict;
290 if (value && value->GetAsDictionary(&dict)) {
292 if (dict->GetString("Browser", &browser)) {
293 std::vector<std::string> parts;
294 Tokenize(browser, "/", &parts);
295 if (parts.size() == 2) {
296 if (parts[0] != "Version") // WebView has this for legacy reasons.
297 browsers_.back()->set_product(parts[0]);
298 browsers_.back()->set_version(parts[1]);
300 browsers_.back()->set_version(browser);
305 scoped_refptr<AndroidDevice> device = devices_.back();
306 device->HttpQuery(browsers_.back()->socket(), kPageListRequest,
307 base::Bind(&AdbPagesCommand::ReceivedPages, this));
310 void AdbPagesCommand::ReceivedPages(int result,
311 const std::string& response) {
312 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
313 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser = browsers_.back();
314 browsers_.pop_back();
320 scoped_ptr<base::Value> value(base::JSONReader::Read(response));
321 base::ListValue* list_value;
322 if (value && value->GetAsList(&list_value)) {
323 browser->SetPageDescriptors(*list_value);
328 void AdbPagesCommand::Respond() {
329 callback_.Run(remote_devices_.release());
332 void AdbPagesCommand::ParseSocketsList(const std::string& response) {
333 // On Android, '/proc/net/unix' looks like this:
335 // Num RefCount Protocol Flags Type St Inode Path
336 // 00000000: 00000002 00000000 00010000 0001 01 331813 /dev/socket/zygote
337 // 00000000: 00000002 00000000 00010000 0001 01 358606 @xxx_devtools_remote
338 // 00000000: 00000002 00000000 00010000 0001 01 347300 @yyy_devtools_remote
340 // We need to find records with paths starting from '@' (abstract socket)
341 // and containing "devtools_remote". We have to extract the inode number
342 // in order to find the owning process name.
344 scoped_refptr<DevToolsAdbBridge::RemoteDevice> remote_device =
345 remote_devices_->back();
347 std::vector<std::string> entries;
348 Tokenize(response, "\n", &entries);
349 const std::string channel_pattern =
350 base::StringPrintf(kDevToolsChannelNameFormat, "");
351 for (size_t i = 1; i < entries.size(); ++i) {
352 std::vector<std::string> fields;
353 Tokenize(entries[i], " \r", &fields);
354 if (fields.size() < 8)
356 if (fields[3] != "00010000" || fields[5] != "01")
358 std::string path_field = fields[7];
359 if (path_field.size() < 1 || path_field[0] != '@')
361 size_t socket_name_pos = path_field.find(channel_pattern);
362 if (socket_name_pos == std::string::npos)
365 std::string socket = path_field.substr(1);
366 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> remote_browser =
367 new DevToolsAdbBridge::RemoteBrowser(
368 adb_thread_, remote_device->device(), socket);
370 std::string product = path_field.substr(1, socket_name_pos - 1);
371 product[0] = base::ToUpperASCII(product[0]);
372 remote_browser->set_product(product);
374 size_t socket_name_end = socket_name_pos + channel_pattern.size();
375 if (socket_name_end < path_field.size() &&
376 path_field[socket_name_end] == '_') {
377 remote_browser->set_pid(path_field.substr(socket_name_end + 1));
379 remote_device->AddBrowser(remote_browser);
381 browsers_ = remote_device->browsers();
384 void AdbPagesCommand::ParseProcessList(const std::string& response) {
385 // On Android, 'ps' output looks like this:
386 // USER PID PPID VSIZE RSS WCHAN PC ? NAME
387 typedef std::map<std::string, std::string> StringMap;
388 StringMap pid_to_package;
389 std::vector<std::string> entries;
390 Tokenize(response, "\n", &entries);
391 for (size_t i = 1; i < entries.size(); ++i) {
392 std::vector<std::string> fields;
393 Tokenize(entries[i], " \r", &fields);
394 if (fields.size() < 9)
396 pid_to_package[fields[1]] = fields[8];
398 DevToolsAdbBridge::RemoteBrowsers browsers =
399 remote_devices_->back()->browsers();
400 for (DevToolsAdbBridge::RemoteBrowsers::iterator it = browsers.begin();
401 it != browsers.end(); ++it) {
402 StringMap::iterator pit = pid_to_package.find((*it)->pid());
403 if (pit != pid_to_package.end())
404 (*it)->set_package(pit->second);
408 void AdbPagesCommand::ParseDumpsysResponse(const std::string& response) {
409 std::vector<std::string> lines;
410 Tokenize(response, "\r", &lines);
411 for (size_t i = 0; i < lines.size(); ++i) {
412 std::string line = lines[i];
413 size_t pos = line.find(kDumpsysScreenSizePrefix);
414 if (pos != std::string::npos) {
416 line.substr(pos + std::string(kDumpsysScreenSizePrefix).size()));
422 void AdbPagesCommand::ParseScreenSize(const std::string& str) {
423 std::vector<std::string> pairs;
424 Tokenize(str, "-", &pairs);
425 if (pairs.size() != 2)
430 std::vector<std::string> numbers;
431 Tokenize(pairs[1].substr(1, pairs[1].size() - 2), ",", &numbers);
432 if (numbers.size() != 2 ||
433 !base::StringToInt(numbers[0], &width) ||
434 !base::StringToInt(numbers[1], &height))
437 remote_devices_->back()->set_screen_size(gfx::Size(width, height));
441 // AdbProtocolCommand ---------------------------------------------------------
443 class AdbProtocolCommand : public AdbWebSocket::Delegate {
446 scoped_refptr<RefCountedAdbThread> adb_thread,
447 scoped_refptr<AndroidDevice> device,
448 const std::string& socket_name,
449 const std::string& debug_url,
450 const std::string& command);
453 virtual void OnSocketOpened() OVERRIDE;
454 virtual void OnFrameRead(const std::string& message) OVERRIDE;
455 virtual void OnSocketClosed(bool closed_by_device) OVERRIDE;
456 virtual bool ProcessIncomingMessage(const std::string& message) OVERRIDE;
458 scoped_refptr<RefCountedAdbThread> adb_thread_;
459 const std::string command_;
460 scoped_refptr<AdbWebSocket> web_socket_;
462 DISALLOW_COPY_AND_ASSIGN(AdbProtocolCommand);
465 AdbProtocolCommand::AdbProtocolCommand(
466 scoped_refptr<RefCountedAdbThread> adb_thread,
467 scoped_refptr<AndroidDevice> device,
468 const std::string& socket_name,
469 const std::string& debug_url,
470 const std::string& command)
471 : adb_thread_(adb_thread),
473 web_socket_ = new AdbWebSocket(
474 device, socket_name, debug_url, adb_thread_->message_loop(), this);
477 void AdbProtocolCommand::OnSocketOpened() {
478 web_socket_->SendFrame(command_);
479 web_socket_->Disconnect();
482 void AdbProtocolCommand::OnFrameRead(const std::string& message) {}
484 void AdbProtocolCommand::OnSocketClosed(bool closed_by_device) {
488 bool AdbProtocolCommand::ProcessIncomingMessage(const std::string& message) {
494 const char kDevToolsChannelNameFormat[] = "%s_devtools_remote";
496 class AgentHostDelegate;
498 typedef std::map<std::string, AgentHostDelegate*> AgentHostDelegates;
500 base::LazyInstance<AgentHostDelegates>::Leaky g_host_delegates =
501 LAZY_INSTANCE_INITIALIZER;
503 DevToolsAdbBridge::Wrapper::Wrapper() {
504 bridge_ = new DevToolsAdbBridge();
507 DevToolsAdbBridge::Wrapper::~Wrapper() {
510 DevToolsAdbBridge* DevToolsAdbBridge::Wrapper::Get() {
511 return bridge_.get();
515 DevToolsAdbBridge::Factory* DevToolsAdbBridge::Factory::GetInstance() {
516 return Singleton<DevToolsAdbBridge::Factory>::get();
520 DevToolsAdbBridge* DevToolsAdbBridge::Factory::GetForProfile(
522 DevToolsAdbBridge::Wrapper* wrapper =
523 static_cast<DevToolsAdbBridge::Wrapper*>(GetInstance()->
524 GetServiceForBrowserContext(profile, true));
525 return wrapper ? wrapper->Get() : NULL;
528 DevToolsAdbBridge::Factory::Factory()
529 : BrowserContextKeyedServiceFactory(
531 BrowserContextDependencyManager::GetInstance()) {}
533 DevToolsAdbBridge::Factory::~Factory() {}
535 BrowserContextKeyedService*
536 DevToolsAdbBridge::Factory::BuildServiceInstanceFor(
537 content::BrowserContext* context) const {
538 return new DevToolsAdbBridge::Wrapper();
542 // AgentHostDelegate ----------------------------------------------------------
544 class AgentHostDelegate : public content::DevToolsExternalAgentProxyDelegate,
545 public AdbWebSocket::Delegate {
547 static void Create(const std::string& id,
548 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,
549 const std::string& debug_url,
550 const std::string& frontend_url,
552 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
553 AgentHostDelegates::iterator it =
554 g_host_delegates.Get().find(id);
555 if (it != g_host_delegates.Get().end()) {
556 it->second->OpenFrontend();
557 } else if (!frontend_url.empty()) {
558 new AgentHostDelegate(
559 id, browser->device(), browser->socket(), debug_url,
560 frontend_url, browser->adb_thread()->message_loop(), profile);
566 const std::string& id,
567 scoped_refptr<AndroidDevice> device,
568 const std::string& socket_name,
569 const std::string& debug_url,
570 const std::string& frontend_url,
571 base::MessageLoop* adb_message_loop,
574 frontend_url_(frontend_url),
575 adb_message_loop_(adb_message_loop),
577 web_socket_ = new AdbWebSocket(
578 device, socket_name, debug_url, adb_message_loop, this);
579 g_host_delegates.Get()[id] = this;
582 void OpenFrontend() {
585 DevToolsWindow::OpenExternalFrontend(
586 profile_, frontend_url_, proxy_->GetAgentHost().get());
589 virtual ~AgentHostDelegate() {
590 g_host_delegates.Get().erase(id_);
593 virtual void Attach() OVERRIDE {}
595 virtual void Detach() OVERRIDE {
596 web_socket_->Disconnect();
599 virtual void SendMessageToBackend(const std::string& message) OVERRIDE {
600 web_socket_->SendFrame(message);
603 virtual void OnSocketOpened() OVERRIDE {
604 proxy_.reset(content::DevToolsExternalAgentProxy::Create(this));
608 virtual void OnFrameRead(const std::string& message) OVERRIDE {
609 proxy_->DispatchOnClientHost(message);
612 virtual void OnSocketClosed(bool closed_by_device) OVERRIDE {
613 if (proxy_ && closed_by_device)
614 proxy_->ConnectionClosed();
618 virtual bool ProcessIncomingMessage(const std::string& message) OVERRIDE {
622 const std::string id_;
623 const std::string frontend_url_;
624 base::MessageLoop* adb_message_loop_;
627 scoped_ptr<content::DevToolsExternalAgentProxy> proxy_;
628 scoped_refptr<AdbWebSocket> web_socket_;
629 DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate);
632 //// RemotePageTarget ----------------------------------------------
634 class RemotePageTarget : public DevToolsTargetImpl {
636 RemotePageTarget(scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,
637 const base::DictionaryValue& value);
638 virtual ~RemotePageTarget();
640 // content::DevToolsTarget overrides:
641 virtual bool IsAttached() const OVERRIDE;
642 virtual bool Activate() const OVERRIDE;
643 virtual bool Close() const OVERRIDE;
645 // DevToolsTargetImpl overrides:
646 virtual void Inspect(Profile* profile) const OVERRIDE;
647 virtual void Reload() const OVERRIDE;
649 void Navigate(const std::string& url) const;
652 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser_;
653 std::string debug_url_;
654 std::string frontend_url_;
655 std::string agent_id_;
656 DISALLOW_COPY_AND_ASSIGN(RemotePageTarget);
659 RemotePageTarget::RemotePageTarget(
660 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,
661 const base::DictionaryValue& value)
662 : browser_(browser) {
664 value.GetString("id", &id_);
666 value.GetString("url", &url);
668 value.GetString("title", &title_);
669 title_ = UTF16ToUTF8(net::UnescapeForHTML(UTF8ToUTF16(title_)));
670 value.GetString("description", &description_);
671 std::string favicon_url;
672 value.GetString("faviconUrl", &favicon_url);
673 favicon_url_ = GURL(favicon_url);
674 value.GetString("webSocketDebuggerUrl", &debug_url_);
675 value.GetString("devtoolsFrontendUrl", &frontend_url_);
677 if (id_.empty() && !debug_url_.empty()) {
678 // Target id is not available until Chrome 26. Use page id at the end of
679 // debug_url_ instead. For attached targets the id will remain empty.
680 std::vector<std::string> parts;
681 Tokenize(debug_url_, "/", &parts);
682 id_ = parts[parts.size()-1];
685 if (debug_url_.find("ws://") == 0)
686 debug_url_ = debug_url_.substr(5);
690 size_t ws_param = frontend_url_.find("?ws");
691 if (ws_param != std::string::npos)
692 frontend_url_ = frontend_url_.substr(0, ws_param);
693 if (frontend_url_.find("http:") == 0)
694 frontend_url_ = "https:" + frontend_url_.substr(5);
696 agent_id_ = base::StringPrintf("%s:%s:%s",
697 browser_->device()->serial().c_str(),
698 browser_->socket().c_str(),
702 RemotePageTarget::~RemotePageTarget() {
705 bool RemotePageTarget::IsAttached() const {
706 return debug_url_.empty();
709 void RemotePageTarget::Inspect(Profile* profile) const {
710 std::string request = base::StringPrintf(kActivatePageRequest, id_.c_str());
711 base::Closure inspect_callback = base::Bind(&AgentHostDelegate::Create,
712 id_, browser_, debug_url_, frontend_url_, profile);
713 browser_->SendJsonRequest(request, inspect_callback);
716 bool RemotePageTarget::Activate() const {
717 std::string request = base::StringPrintf(kActivatePageRequest, id_.c_str());
718 browser_->SendJsonRequest(request, base::Closure());
722 bool RemotePageTarget::Close() const {
725 std::string request = base::StringPrintf(kClosePageRequest, id_.c_str());
726 browser_->SendJsonRequest(request, base::Closure());
730 void RemotePageTarget::Reload() const {
731 browser_->SendProtocolCommand(debug_url_, kPageReloadCommand, NULL);
734 void RemotePageTarget::Navigate(const std::string& url) const {
735 base::DictionaryValue params;
736 params.SetString(kUrlParam, url);
737 browser_->SendProtocolCommand(debug_url_, kPageNavigateCommand, ¶ms);
740 // DevToolsAdbBridge::RemoteBrowser -------------------------------------------
742 DevToolsAdbBridge::RemoteBrowser::RemoteBrowser(
743 scoped_refptr<RefCountedAdbThread> adb_thread,
744 scoped_refptr<AndroidDevice> device,
745 const std::string& socket)
746 : adb_thread_(adb_thread),
749 page_descriptors_(new base::ListValue()) {
752 bool DevToolsAdbBridge::RemoteBrowser::IsChrome() const {
753 return product_.find(kChromeProductName) == 0;
756 DevToolsAdbBridge::RemoteBrowser::ParsedVersion
757 DevToolsAdbBridge::RemoteBrowser::GetParsedVersion() const {
758 ParsedVersion result;
759 std::vector<std::string> parts;
760 Tokenize(version_, ".", &parts);
761 for (size_t i = 0; i != parts.size(); ++i) {
763 base::StringToInt(parts[i], &value);
764 result.push_back(value);
769 std::vector<DevToolsTargetImpl*>
770 DevToolsAdbBridge::RemoteBrowser::CreatePageTargets() {
771 std::vector<DevToolsTargetImpl*> result;
772 for (size_t i = 0; i < page_descriptors_->GetSize(); ++i) {
774 page_descriptors_->Get(i, &item);
777 base::DictionaryValue* dict;
778 if (!item->GetAsDictionary(&dict))
780 result.push_back(new RemotePageTarget(this, *dict));
785 void DevToolsAdbBridge::RemoteBrowser::SetPageDescriptors(
786 const base::ListValue& list) {
787 page_descriptors_.reset(list.DeepCopy());
790 static void RespondOnUIThread(base::Closure callback, int, const std::string&) {
791 if (!callback.is_null())
792 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
795 void DevToolsAdbBridge::RemoteBrowser::SendJsonRequest(
796 const std::string& request, base::Closure callback) {
797 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
798 adb_thread_->message_loop()->PostTask(FROM_HERE,
799 base::Bind(&AndroidDevice::HttpQuery, device_, socket_, request,
800 base::Bind(&RespondOnUIThread, callback)));
803 void DevToolsAdbBridge::RemoteBrowser::SendProtocolCommand(
804 const std::string& debug_url,
805 const std::string& method,
806 base::DictionaryValue* params) {
807 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
808 if (debug_url.empty())
810 DevToolsProtocol::Command command(1, method, params);
811 new AdbProtocolCommand(
812 adb_thread_, device_, socket_, debug_url, command.Serialize());
815 static void NoOp(int, const std::string&) {}
817 void DevToolsAdbBridge::RemoteBrowser::Open(const std::string& url) {
818 ParsedVersion parsed_version = GetParsedVersion();
820 !parsed_version.empty() &&
821 parsed_version[0] >= kMinVersionNewWithURL) {
822 std::string query = net::EscapeQueryParamValue(url, false /* use_plus */);
823 std::string request =
824 base::StringPrintf(kNewPageRequestWithURL, query.c_str());
825 adb_thread_->message_loop()->PostTask(FROM_HERE,
826 base::Bind(&AndroidDevice::HttpQuery,
827 device_, socket_, request, base::Bind(&NoOp)));
829 adb_thread_->message_loop()->PostTask(FROM_HERE,
830 base::Bind(&AndroidDevice::HttpQuery,
831 device_, socket_, kNewPageRequest,
832 base::Bind(&RemoteBrowser::PageCreatedOnHandlerThread, this, url)));
836 void DevToolsAdbBridge::RemoteBrowser::PageCreatedOnHandlerThread(
837 const std::string& url, int result, const std::string& response) {
840 // Navigating too soon after the page creation breaks navigation history
841 // (crbug.com/311014). This can be avoided by adding a moderate delay.
842 BrowserThread::PostDelayedTask(
843 BrowserThread::UI, FROM_HERE,
844 base::Bind(&RemoteBrowser::PageCreatedOnUIThread, this, response, url),
845 base::TimeDelta::FromMilliseconds(kNewPageNavigateDelayMs));
848 void DevToolsAdbBridge::RemoteBrowser::PageCreatedOnUIThread(
849 const std::string& response, const std::string& url) {
850 scoped_ptr<base::Value> value(base::JSONReader::Read(response));
851 base::DictionaryValue* dict;
852 if (value && value->GetAsDictionary(&dict)) {
853 RemotePageTarget new_page(this, *dict);
854 new_page.Navigate(url);
858 DevToolsAdbBridge::RemoteBrowser::~RemoteBrowser() {
862 // DevToolsAdbBridge::RemoteDevice --------------------------------------------
864 DevToolsAdbBridge::RemoteDevice::RemoteDevice(
865 scoped_refptr<AndroidDevice> device)
869 std::string DevToolsAdbBridge::RemoteDevice::GetSerial() {
870 return device_->serial();
873 std::string DevToolsAdbBridge::RemoteDevice::GetModel() {
874 return device_->model();
877 bool DevToolsAdbBridge::RemoteDevice::IsConnected() {
878 return device_->is_connected();
881 void DevToolsAdbBridge::RemoteDevice::AddBrowser(
882 scoped_refptr<RemoteBrowser> browser) {
883 browsers_.push_back(browser);
886 DevToolsAdbBridge::RemoteDevice::~RemoteDevice() {
890 // DevToolsAdbBridge ----------------------------------------------------------
892 DevToolsAdbBridge::DevToolsAdbBridge()
893 : adb_thread_(RefCountedAdbThread::GetInstance()),
894 has_message_loop_(adb_thread_->message_loop() != NULL) {
897 void DevToolsAdbBridge::AddListener(Listener* listener) {
898 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
899 if (listeners_.empty())
900 RequestRemoteDevices();
901 listeners_.push_back(listener);
904 void DevToolsAdbBridge::RemoveListener(Listener* listener) {
905 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
906 Listeners::iterator it =
907 std::find(listeners_.begin(), listeners_.end(), listener);
908 DCHECK(it != listeners_.end());
909 listeners_.erase(it);
912 bool DevToolsAdbBridge::HasDevToolsWindow(const std::string& agent_id) {
913 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
914 return g_host_delegates.Get().find(agent_id) != g_host_delegates.Get().end();
917 DevToolsAdbBridge::~DevToolsAdbBridge() {
918 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
919 DCHECK(listeners_.empty());
922 void DevToolsAdbBridge::RequestRemoteDevices() {
923 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
924 if (!has_message_loop_)
928 adb_thread_, device_providers_,
929 base::Bind(&DevToolsAdbBridge::ReceivedRemoteDevices, this));
932 void DevToolsAdbBridge::ReceivedRemoteDevices(RemoteDevices* devices_ptr) {
933 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
935 scoped_ptr<RemoteDevices> devices(devices_ptr);
937 Listeners copy(listeners_);
938 for (Listeners::iterator it = copy.begin(); it != copy.end(); ++it)
939 (*it)->RemoteDevicesChanged(devices.get());
941 if (listeners_.empty())
944 BrowserThread::PostDelayedTask(
947 base::Bind(&DevToolsAdbBridge::RequestRemoteDevices, this),
948 base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs));