Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / devtools / devtools_adb_bridge.cc
blob63fd5514cb842c81636d17329b548735c80a4bb8
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"
7 #include <map>
8 #include <vector>
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 "content/public/browser/user_metrics.h"
39 #include "crypto/rsa_private_key.h"
40 #include "net/base/escape.h"
41 #include "net/base/net_errors.h"
43 using content::BrowserThread;
45 namespace {
47 const char kDeviceModelCommand[] = "shell:getprop ro.product.model";
48 const char kInstalledChromePackagesCommand[] = "shell:pm list packages";
49 const char kOpenedUnixSocketsCommand[] = "shell:cat /proc/net/unix";
50 const char kListProcessesCommand[] = "shell:ps";
51 const char kDumpsysCommand[] = "shell:dumpsys window policy";
52 const char kDumpsysScreenSizePrefix[] = "mStable=";
54 const char kUnknownModel[] = "Offline";
56 const char kPageListRequest[] = "GET /json HTTP/1.1\r\n\r\n";
57 const char kVersionRequest[] = "GET /json/version HTTP/1.1\r\n\r\n";
58 const char kClosePageRequest[] = "GET /json/close/%s HTTP/1.1\r\n\r\n";
59 const char kNewPageRequest[] = "GET /json/new HTTP/1.1\r\n\r\n";
60 const char kNewPageRequestWithURL[] = "GET /json/new?%s HTTP/1.1\r\n\r\n";
61 const char kActivatePageRequest[] =
62 "GET /json/activate/%s HTTP/1.1\r\n\r\n";
63 const int kAdbPollingIntervalMs = 1000;
65 const char kUrlParam[] = "url";
66 const char kPageReloadCommand[] = "Page.reload";
67 const char kPageNavigateCommand[] = "Page.navigate";
69 const char kChromeDefaultName[] = "Chrome";
70 const char kChromeDefaultSocket[] = "chrome_devtools_remote";
71 const int kMinVersionNewWithURL = 32;
72 const int kNewPageNavigateDelayMs = 500;
74 const char kWebViewSocketPrefix[] = "webview_devtools_remote";
75 const char kWebViewNameTemplate[] = "WebView in %s";
77 typedef DevToolsAdbBridge::Callback Callback;
78 typedef std::vector<scoped_refptr<AndroidDevice> >
79 AndroidDevices;
80 typedef base::Callback<void(const AndroidDevices&)> AndroidDevicesCallback;
83 struct BrowserDescriptor {
84 const char* package;
85 const char* socket;
86 const char* display_name;
89 const BrowserDescriptor kBrowserDescriptors[] = {
91 "com.android.chrome",
92 kChromeDefaultSocket,
93 kChromeDefaultName
96 "com.chrome.beta",
97 kChromeDefaultSocket,
98 "Chrome Beta"
101 "com.google.android.apps.chrome_dev",
102 kChromeDefaultSocket,
103 "Chrome Dev"
106 "com.google.android.apps.chrome",
107 kChromeDefaultSocket,
108 "Chromium"
111 "org.chromium.content_shell_apk",
112 "content_shell_devtools_remote",
113 "Content Shell"
116 "org.chromium.chrome.testshell",
117 "chromium_testshell_devtools_remote",
118 "Chromium Test Shell"
121 "org.chromium.android_webview.shell",
122 "webview_devtools_remote",
123 "WebView Test Shell"
127 const BrowserDescriptor* FindBrowserDescriptor(const std::string& package) {
128 int count = sizeof(kBrowserDescriptors) / sizeof(kBrowserDescriptors[0]);
129 for (int i = 0; i < count; i++)
130 if (kBrowserDescriptors[i].package == package)
131 return &kBrowserDescriptors[i];
132 return NULL;
135 typedef std::map<std::string, const BrowserDescriptor*> DescriptorMap;
137 static DescriptorMap FindInstalledBrowserPackages(
138 const std::string& response) {
139 // Parse 'pm list packages' output which on Android looks like this:
141 // package:com.android.chrome
142 // package:com.chrome.beta
143 // package:com.example.app
145 DescriptorMap package_to_descriptor;
146 const std::string package_prefix = "package:";
147 std::vector<std::string> entries;
148 Tokenize(response, "'\r\n", &entries);
149 for (size_t i = 0; i < entries.size(); ++i) {
150 if (entries[i].find(package_prefix) != 0)
151 continue;
152 std::string package = entries[i].substr(package_prefix.size());
153 const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
154 if (!descriptor)
155 continue;
156 package_to_descriptor[descriptor->package] = descriptor;
158 return package_to_descriptor;
161 typedef std::map<std::string, std::string> StringMap;
163 static void MapProcessesToPackages(const std::string& response,
164 StringMap& pid_to_package,
165 StringMap& package_to_pid) {
166 // Parse 'ps' output which on Android looks like this:
168 // USER PID PPID VSIZE RSS WCHAN PC ? NAME
170 std::vector<std::string> entries;
171 Tokenize(response, "\n", &entries);
172 for (size_t i = 1; i < entries.size(); ++i) {
173 std::vector<std::string> fields;
174 Tokenize(entries[i], " \r", &fields);
175 if (fields.size() < 9)
176 continue;
177 std::string pid = fields[1];
178 std::string package = fields[8];
179 pid_to_package[pid] = package;
180 package_to_pid[package] = pid;
184 typedef std::map<std::string,
185 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> > BrowserMap;
187 static StringMap MapSocketsToProcesses(const std::string& response,
188 const std::string& channel_pattern) {
189 // Parse 'cat /proc/net/unix' output which on Android looks like this:
191 // Num RefCount Protocol Flags Type St Inode Path
192 // 00000000: 00000002 00000000 00010000 0001 01 331813 /dev/socket/zygote
193 // 00000000: 00000002 00000000 00010000 0001 01 358606 @xxx_devtools_remote
194 // 00000000: 00000002 00000000 00010000 0001 01 347300 @yyy_devtools_remote
196 // We need to find records with paths starting from '@' (abstract socket)
197 // and containing the channel pattern ("_devtools_remote").
198 StringMap socket_to_pid;
199 std::vector<std::string> entries;
200 Tokenize(response, "\n", &entries);
201 for (size_t i = 1; i < entries.size(); ++i) {
202 std::vector<std::string> fields;
203 Tokenize(entries[i], " \r", &fields);
204 if (fields.size() < 8)
205 continue;
206 if (fields[3] != "00010000" || fields[5] != "01")
207 continue;
208 std::string path_field = fields[7];
209 if (path_field.size() < 1 || path_field[0] != '@')
210 continue;
211 size_t socket_name_pos = path_field.find(channel_pattern);
212 if (socket_name_pos == std::string::npos)
213 continue;
215 std::string socket = path_field.substr(1);
217 std::string pid;
218 size_t socket_name_end = socket_name_pos + channel_pattern.size();
219 if (socket_name_end < path_field.size() &&
220 path_field[socket_name_end] == '_') {
221 pid = path_field.substr(socket_name_end + 1);
223 socket_to_pid[socket] = pid;
225 return socket_to_pid;
228 // AdbPagesCommand ------------------------------------------------------------
230 class AdbPagesCommand : public base::RefCountedThreadSafe<
231 AdbPagesCommand,
232 BrowserThread::DeleteOnUIThread> {
233 public:
234 typedef base::Callback<void(DevToolsAdbBridge::RemoteDevices*)> Callback;
236 AdbPagesCommand(
237 scoped_refptr<RefCountedAdbThread> adb_thread,
238 const DevToolsAdbBridge::DeviceProviders& device_providers,
239 const Callback& callback);
241 private:
242 friend struct BrowserThread::DeleteOnThread<
243 BrowserThread::UI>;
244 friend class base::DeleteHelper<AdbPagesCommand>;
246 virtual ~AdbPagesCommand();
247 void ProcessDeviceProviders();
248 void ReceivedDevices(const AndroidDevices& devices);
250 void ProcessSerials();
251 void ReceivedModel(int result, const std::string& response);
252 void ReceivedDumpsys(int result, const std::string& response);
253 void ReceivedPackages(int result, const std::string& response);
254 void ReceivedProcesses(
255 const std::string& packages_response,
256 int result,
257 const std::string& processes_response);
258 void ReceivedSockets(
259 const std::string& packages_response,
260 const std::string& processes_response,
261 int result,
262 const std::string& sockets_response);
263 void ProcessSockets();
264 void ReceivedVersion(int result, const std::string& response);
265 void ReceivedPages(int result, const std::string& response);
267 scoped_refptr<AndroidDevice> current_device() const {
268 return devices_.back();
271 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> current_browser() const {
272 return browsers_.back();
275 void NextBrowser();
276 void NextDevice();
278 void Respond();
280 void CreateBrowsers(const std::string& packages_response,
281 const std::string& processes_response,
282 const std::string& sockets_response);
284 void ParseDumpsysResponse(const std::string& response);
285 void ParseScreenSize(const std::string& str);
287 scoped_refptr<RefCountedAdbThread> adb_thread_;
288 Callback callback_;
289 AndroidDevices devices_;
290 DevToolsAdbBridge::RemoteBrowsers browsers_;
291 scoped_ptr<DevToolsAdbBridge::RemoteDevices> remote_devices_;
292 DevToolsAdbBridge::DeviceProviders device_providers_;
295 AdbPagesCommand::AdbPagesCommand(
296 scoped_refptr<RefCountedAdbThread> adb_thread,
297 const DevToolsAdbBridge::DeviceProviders& device_providers,
298 const Callback& callback)
299 : adb_thread_(adb_thread),
300 callback_(callback),
301 device_providers_(device_providers){
302 remote_devices_.reset(new DevToolsAdbBridge::RemoteDevices());
304 ProcessDeviceProviders();
307 AdbPagesCommand::~AdbPagesCommand() {
308 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
311 void AdbPagesCommand::ProcessDeviceProviders() {
312 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
313 if (device_providers_.empty()) {
314 adb_thread_->message_loop()->PostTask(
315 FROM_HERE, base::Bind(&AdbPagesCommand::ProcessSerials, this));
316 return;
319 const scoped_refptr<AndroidDeviceProvider>& device_provider =
320 device_providers_.back();
322 device_provider->QueryDevices(
323 base::Bind(&AdbPagesCommand::ReceivedDevices, this));
326 void AdbPagesCommand::ReceivedDevices(const AndroidDevices& devices) {
327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
328 DCHECK(!device_providers_.empty());
329 device_providers_.pop_back();
331 devices_.insert(devices_.end(), devices.begin(), devices.end());
333 if (!device_providers_.empty()) {
334 ProcessDeviceProviders();
335 } else {
336 adb_thread_->message_loop()->PostTask(
337 FROM_HERE, base::Bind(&AdbPagesCommand::ProcessSerials, this));
341 void AdbPagesCommand::ProcessSerials() {
342 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
343 if (devices_.size() == 0) {
344 BrowserThread::PostTask(
345 BrowserThread::UI, FROM_HERE,
346 base::Bind(&AdbPagesCommand::Respond, this));
347 return;
350 scoped_refptr<AndroidDevice> device = current_device();
352 if (device->is_connected()) {
353 device->RunCommand(kDeviceModelCommand,
354 base::Bind(&AdbPagesCommand::ReceivedModel, this));
355 } else {
356 device->set_model(kUnknownModel);
357 remote_devices_->push_back(new DevToolsAdbBridge::RemoteDevice(device));
358 NextDevice();
362 void AdbPagesCommand::ReceivedModel(int result, const std::string& response) {
363 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
364 if (result < 0) {
365 NextDevice();
366 return;
368 scoped_refptr<AndroidDevice> device = current_device();
369 device->set_model(response);
370 remote_devices_->push_back(new DevToolsAdbBridge::RemoteDevice(device));
371 device->RunCommand(kDumpsysCommand,
372 base::Bind(&AdbPagesCommand::ReceivedDumpsys, this));
375 void AdbPagesCommand::ReceivedDumpsys(int result,
376 const std::string& response) {
377 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
378 if (result >= 0)
379 ParseDumpsysResponse(response);
381 current_device()->RunCommand(
382 kInstalledChromePackagesCommand,
383 base::Bind(&AdbPagesCommand::ReceivedPackages, this));
386 void AdbPagesCommand::ReceivedPackages(int result,
387 const std::string& packages_response) {
388 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
389 if (result < 0) {
390 NextDevice();
391 return;
393 current_device()->RunCommand(
394 kListProcessesCommand,
395 base::Bind(&AdbPagesCommand::ReceivedProcesses, this, packages_response));
398 void AdbPagesCommand::ReceivedProcesses(
399 const std::string& packages_response,
400 int result,
401 const std::string& processes_response) {
402 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
403 if (result < 0) {
404 NextDevice();
405 return;
407 current_device()->RunCommand(
408 kOpenedUnixSocketsCommand,
409 base::Bind(&AdbPagesCommand::ReceivedSockets,
410 this,
411 packages_response,
412 processes_response));
415 void AdbPagesCommand::ReceivedSockets(
416 const std::string& packages_response,
417 const std::string& processes_response,
418 int result,
419 const std::string& sockets_response) {
420 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
421 if (result >= 0)
422 CreateBrowsers(packages_response, processes_response, sockets_response);
423 ProcessSockets();
426 void AdbPagesCommand::ProcessSockets() {
427 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
428 if (browsers_.size() == 0) {
429 NextDevice();
430 return;
433 current_device()->HttpQuery(
434 current_browser()->socket(),
435 kVersionRequest,
436 base::Bind(&AdbPagesCommand::ReceivedVersion, this));
439 void AdbPagesCommand::ReceivedVersion(int result,
440 const std::string& response) {
441 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
442 if (result < 0) {
443 NextBrowser();
444 return;
447 // Parse version, append to package name if available,
448 scoped_ptr<base::Value> value(base::JSONReader::Read(response));
449 base::DictionaryValue* dict;
450 if (value && value->GetAsDictionary(&dict)) {
451 std::string browser;
452 if (dict->GetString("Browser", &browser)) {
453 std::vector<std::string> parts;
454 Tokenize(browser, "/", &parts);
455 if (parts.size() == 2)
456 current_browser()->set_version(parts[1]);
457 else
458 current_browser()->set_version(browser);
460 std::string package;
461 if (dict->GetString("Android-Package", &package)) {
462 const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
463 if (descriptor)
464 current_browser()->set_display_name(descriptor->display_name);
468 current_device()->HttpQuery(
469 current_browser()->socket(),
470 kPageListRequest,
471 base::Bind(&AdbPagesCommand::ReceivedPages, this));
474 void AdbPagesCommand::ReceivedPages(int result,
475 const std::string& response) {
476 DCHECK_EQ(adb_thread_->message_loop(), base::MessageLoop::current());
477 if (result >= 0) {
478 scoped_ptr<base::Value> value(base::JSONReader::Read(response));
479 base::ListValue* list_value;
480 if (value && value->GetAsList(&list_value))
481 current_browser()->SetPageDescriptors(*list_value);
483 NextBrowser();
486 void AdbPagesCommand::NextBrowser() {
487 browsers_.pop_back();
488 ProcessSockets();
491 void AdbPagesCommand::NextDevice() {
492 devices_.pop_back();
493 ProcessSerials();
496 void AdbPagesCommand::Respond() {
497 callback_.Run(remote_devices_.release());
500 void AdbPagesCommand::CreateBrowsers(
501 const std::string& packages_response,
502 const std::string& processes_response,
503 const std::string& sockets_response) {
504 DescriptorMap package_to_descriptor =
505 FindInstalledBrowserPackages(packages_response);
507 StringMap pid_to_package;
508 StringMap package_to_pid;
509 MapProcessesToPackages(processes_response, pid_to_package, package_to_pid);
511 const std::string channel_pattern =
512 base::StringPrintf(kDevToolsChannelNameFormat, "");
514 StringMap socket_to_pid = MapSocketsToProcesses(sockets_response,
515 channel_pattern);
517 scoped_refptr<DevToolsAdbBridge::RemoteDevice> remote_device =
518 remote_devices_->back();
520 // Create RemoteBrowser instances.
521 BrowserMap package_to_running_browser;
522 BrowserMap socket_to_unnamed_browser;
523 for (StringMap::iterator it = socket_to_pid.begin();
524 it != socket_to_pid.end(); ++it) {
525 std::string socket = it->first;
526 std::string pid = it->second;
528 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser =
529 new DevToolsAdbBridge::RemoteBrowser(
530 adb_thread_, remote_device->device(), socket);
532 StringMap::iterator pit = pid_to_package.find(pid);
533 if (pit != pid_to_package.end()) {
534 std::string package = pit->second;
535 package_to_running_browser[package] = browser;
536 const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
537 if (descriptor) {
538 browser->set_display_name(descriptor->display_name);
539 } else if (socket.find(kWebViewSocketPrefix) == 0) {
540 browser->set_display_name(
541 base::StringPrintf(kWebViewNameTemplate, package.c_str()));
542 } else {
543 browser->set_display_name(package);
545 } else {
546 // Set fallback display name.
547 std::string name = socket.substr(0, socket.find(channel_pattern));
548 name[0] = base::ToUpperASCII(name[0]);
549 browser->set_display_name(name);
551 socket_to_unnamed_browser[socket] = browser;
553 remote_device->AddBrowser(browser);
556 browsers_ = remote_device->browsers();
558 // Find installed packages not mapped to browsers.
559 typedef std::multimap<std::string, const BrowserDescriptor*>
560 DescriptorMultimap;
561 DescriptorMultimap socket_to_descriptor;
562 for (DescriptorMap::iterator it = package_to_descriptor.begin();
563 it != package_to_descriptor.end(); ++it) {
564 std::string package = it->first;
565 const BrowserDescriptor* descriptor = it->second;
567 if (package_to_running_browser.find(package) !=
568 package_to_running_browser.end())
569 continue; // This package is already mapped to a browser.
571 if (package_to_pid.find(package) != package_to_pid.end()) {
572 // This package is running but not mapped to a browser.
573 socket_to_descriptor.insert(
574 DescriptorMultimap::value_type(descriptor->socket, descriptor));
575 continue;
579 // Try naming remaining unnamed browsers.
580 for (DescriptorMultimap::iterator it = socket_to_descriptor.begin();
581 it != socket_to_descriptor.end(); ++it) {
582 std::string socket = it->first;
583 const BrowserDescriptor* descriptor = it->second;
585 if (socket_to_descriptor.count(socket) != 1)
586 continue; // No definitive match.
588 BrowserMap::iterator bit = socket_to_unnamed_browser.find(socket);
589 if (bit != socket_to_unnamed_browser.end())
590 bit->second->set_display_name(descriptor->display_name);
594 void AdbPagesCommand::ParseDumpsysResponse(const std::string& response) {
595 std::vector<std::string> lines;
596 Tokenize(response, "\r", &lines);
597 for (size_t i = 0; i < lines.size(); ++i) {
598 std::string line = lines[i];
599 size_t pos = line.find(kDumpsysScreenSizePrefix);
600 if (pos != std::string::npos) {
601 ParseScreenSize(
602 line.substr(pos + std::string(kDumpsysScreenSizePrefix).size()));
603 break;
608 void AdbPagesCommand::ParseScreenSize(const std::string& str) {
609 std::vector<std::string> pairs;
610 Tokenize(str, "-", &pairs);
611 if (pairs.size() != 2)
612 return;
614 int width;
615 int height;
616 std::vector<std::string> numbers;
617 Tokenize(pairs[1].substr(1, pairs[1].size() - 2), ",", &numbers);
618 if (numbers.size() != 2 ||
619 !base::StringToInt(numbers[0], &width) ||
620 !base::StringToInt(numbers[1], &height))
621 return;
623 remote_devices_->back()->set_screen_size(gfx::Size(width, height));
627 // AdbProtocolCommand ---------------------------------------------------------
629 class AdbProtocolCommand : public AdbWebSocket::Delegate {
630 public:
631 AdbProtocolCommand(
632 scoped_refptr<RefCountedAdbThread> adb_thread,
633 scoped_refptr<AndroidDevice> device,
634 const std::string& socket_name,
635 const std::string& debug_url,
636 const std::string& command);
638 private:
639 virtual void OnSocketOpened() OVERRIDE;
640 virtual void OnFrameRead(const std::string& message) OVERRIDE;
641 virtual void OnSocketClosed(bool closed_by_device) OVERRIDE;
642 virtual bool ProcessIncomingMessage(const std::string& message) OVERRIDE;
644 scoped_refptr<RefCountedAdbThread> adb_thread_;
645 const std::string command_;
646 scoped_refptr<AdbWebSocket> web_socket_;
648 DISALLOW_COPY_AND_ASSIGN(AdbProtocolCommand);
651 AdbProtocolCommand::AdbProtocolCommand(
652 scoped_refptr<RefCountedAdbThread> adb_thread,
653 scoped_refptr<AndroidDevice> device,
654 const std::string& socket_name,
655 const std::string& debug_url,
656 const std::string& command)
657 : adb_thread_(adb_thread),
658 command_(command) {
659 web_socket_ = new AdbWebSocket(
660 device, socket_name, debug_url, adb_thread_->message_loop(), this);
663 void AdbProtocolCommand::OnSocketOpened() {
664 web_socket_->SendFrame(command_);
665 web_socket_->Disconnect();
668 void AdbProtocolCommand::OnFrameRead(const std::string& message) {}
670 void AdbProtocolCommand::OnSocketClosed(bool closed_by_device) {
671 delete this;
674 bool AdbProtocolCommand::ProcessIncomingMessage(const std::string& message) {
675 return false;
678 } // namespace
680 const char kDevToolsChannelNameFormat[] = "%s_devtools_remote";
682 class AgentHostDelegate;
684 typedef std::map<std::string, AgentHostDelegate*> AgentHostDelegates;
686 base::LazyInstance<AgentHostDelegates>::Leaky g_host_delegates =
687 LAZY_INSTANCE_INITIALIZER;
689 DevToolsAdbBridge::Wrapper::Wrapper() {
690 bridge_ = new DevToolsAdbBridge();
693 DevToolsAdbBridge::Wrapper::~Wrapper() {
696 DevToolsAdbBridge* DevToolsAdbBridge::Wrapper::Get() {
697 return bridge_.get();
700 // static
701 DevToolsAdbBridge::Factory* DevToolsAdbBridge::Factory::GetInstance() {
702 return Singleton<DevToolsAdbBridge::Factory>::get();
705 // static
706 DevToolsAdbBridge* DevToolsAdbBridge::Factory::GetForProfile(
707 Profile* profile) {
708 DevToolsAdbBridge::Wrapper* wrapper =
709 static_cast<DevToolsAdbBridge::Wrapper*>(GetInstance()->
710 GetServiceForBrowserContext(profile, true));
711 return wrapper ? wrapper->Get() : NULL;
714 DevToolsAdbBridge::Factory::Factory()
715 : BrowserContextKeyedServiceFactory(
716 "DevToolsAdbBridge",
717 BrowserContextDependencyManager::GetInstance()) {}
719 DevToolsAdbBridge::Factory::~Factory() {}
721 BrowserContextKeyedService*
722 DevToolsAdbBridge::Factory::BuildServiceInstanceFor(
723 content::BrowserContext* context) const {
724 return new DevToolsAdbBridge::Wrapper();
728 // AgentHostDelegate ----------------------------------------------------------
730 class AgentHostDelegate : public content::DevToolsExternalAgentProxyDelegate,
731 public AdbWebSocket::Delegate {
732 public:
733 static void Create(const std::string& id,
734 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,
735 const std::string& debug_url,
736 const std::string& frontend_url,
737 Profile* profile) {
738 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
739 AgentHostDelegates::iterator it =
740 g_host_delegates.Get().find(id);
741 if (it != g_host_delegates.Get().end()) {
742 it->second->OpenFrontend();
743 } else if (!frontend_url.empty()) {
744 new AgentHostDelegate(
745 id, browser->device(), browser->socket(), debug_url,
746 frontend_url, browser->adb_thread()->message_loop(), profile);
750 private:
751 AgentHostDelegate(
752 const std::string& id,
753 scoped_refptr<AndroidDevice> device,
754 const std::string& socket_name,
755 const std::string& debug_url,
756 const std::string& frontend_url,
757 base::MessageLoop* adb_message_loop,
758 Profile* profile)
759 : id_(id),
760 frontend_url_(frontend_url),
761 adb_message_loop_(adb_message_loop),
762 profile_(profile) {
763 web_socket_ = new AdbWebSocket(
764 device, socket_name, debug_url, adb_message_loop, this);
765 g_host_delegates.Get()[id] = this;
767 if (socket_name.find(kWebViewSocketPrefix) == 0) {
768 content::RecordAction(
769 base::UserMetricsAction("DevTools_InspectAndroidWebView"));
770 } else {
771 content::RecordAction(
772 base::UserMetricsAction("DevTools_InspectAndroidPage"));
776 void OpenFrontend() {
777 if (!proxy_)
778 return;
779 DevToolsWindow::OpenExternalFrontend(
780 profile_, frontend_url_, proxy_->GetAgentHost().get());
783 virtual ~AgentHostDelegate() {
784 g_host_delegates.Get().erase(id_);
787 virtual void Attach() OVERRIDE {}
789 virtual void Detach() OVERRIDE {
790 web_socket_->Disconnect();
793 virtual void SendMessageToBackend(const std::string& message) OVERRIDE {
794 web_socket_->SendFrame(message);
797 virtual void OnSocketOpened() OVERRIDE {
798 proxy_.reset(content::DevToolsExternalAgentProxy::Create(this));
799 OpenFrontend();
802 virtual void OnFrameRead(const std::string& message) OVERRIDE {
803 proxy_->DispatchOnClientHost(message);
806 virtual void OnSocketClosed(bool closed_by_device) OVERRIDE {
807 if (proxy_ && closed_by_device)
808 proxy_->ConnectionClosed();
809 delete this;
812 virtual bool ProcessIncomingMessage(const std::string& message) OVERRIDE {
813 return false;
816 const std::string id_;
817 const std::string frontend_url_;
818 base::MessageLoop* adb_message_loop_;
819 Profile* profile_;
821 scoped_ptr<content::DevToolsExternalAgentProxy> proxy_;
822 scoped_refptr<AdbWebSocket> web_socket_;
823 DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate);
826 //// RemotePageTarget ----------------------------------------------
828 class RemotePageTarget : public DevToolsTargetImpl {
829 public:
830 RemotePageTarget(scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,
831 const base::DictionaryValue& value);
832 virtual ~RemotePageTarget();
834 // content::DevToolsTarget overrides:
835 virtual bool IsAttached() const OVERRIDE;
836 virtual bool Activate() const OVERRIDE;
837 virtual bool Close() const OVERRIDE;
839 // DevToolsTargetImpl overrides:
840 virtual void Inspect(Profile* profile) const OVERRIDE;
841 virtual void Reload() const OVERRIDE;
843 void Navigate(const std::string& url) const;
845 private:
846 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser_;
847 std::string debug_url_;
848 std::string frontend_url_;
849 std::string agent_id_;
850 DISALLOW_COPY_AND_ASSIGN(RemotePageTarget);
853 RemotePageTarget::RemotePageTarget(
854 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser,
855 const base::DictionaryValue& value)
856 : browser_(browser) {
857 type_ = "adb_page";
858 value.GetString("id", &id_);
859 std::string url;
860 value.GetString("url", &url);
861 url_ = GURL(url);
862 value.GetString("title", &title_);
863 title_ = base::UTF16ToUTF8(net::UnescapeForHTML(base::UTF8ToUTF16(title_)));
864 value.GetString("description", &description_);
865 std::string favicon_url;
866 value.GetString("faviconUrl", &favicon_url);
867 favicon_url_ = GURL(favicon_url);
868 value.GetString("webSocketDebuggerUrl", &debug_url_);
869 value.GetString("devtoolsFrontendUrl", &frontend_url_);
871 if (id_.empty() && !debug_url_.empty()) {
872 // Target id is not available until Chrome 26. Use page id at the end of
873 // debug_url_ instead. For attached targets the id will remain empty.
874 std::vector<std::string> parts;
875 Tokenize(debug_url_, "/", &parts);
876 id_ = parts[parts.size()-1];
879 if (debug_url_.find("ws://") == 0)
880 debug_url_ = debug_url_.substr(5);
881 else
882 debug_url_ = "";
884 size_t ws_param = frontend_url_.find("?ws");
885 if (ws_param != std::string::npos)
886 frontend_url_ = frontend_url_.substr(0, ws_param);
887 if (frontend_url_.find("http:") == 0)
888 frontend_url_ = "https:" + frontend_url_.substr(5);
890 agent_id_ = base::StringPrintf("%s:%s:%s",
891 browser_->device()->serial().c_str(),
892 browser_->socket().c_str(),
893 id_.c_str());
896 RemotePageTarget::~RemotePageTarget() {
899 bool RemotePageTarget::IsAttached() const {
900 return debug_url_.empty();
903 void RemotePageTarget::Inspect(Profile* profile) const {
904 std::string request = base::StringPrintf(kActivatePageRequest, id_.c_str());
905 base::Closure inspect_callback = base::Bind(&AgentHostDelegate::Create,
906 id_, browser_, debug_url_, frontend_url_, profile);
907 browser_->SendJsonRequest(request, inspect_callback);
910 bool RemotePageTarget::Activate() const {
911 std::string request = base::StringPrintf(kActivatePageRequest, id_.c_str());
912 browser_->SendJsonRequest(request, base::Closure());
913 return true;
916 bool RemotePageTarget::Close() const {
917 if (IsAttached())
918 return false;
919 std::string request = base::StringPrintf(kClosePageRequest, id_.c_str());
920 browser_->SendJsonRequest(request, base::Closure());
921 return true;
924 void RemotePageTarget::Reload() const {
925 browser_->SendProtocolCommand(debug_url_, kPageReloadCommand, NULL);
928 void RemotePageTarget::Navigate(const std::string& url) const {
929 base::DictionaryValue params;
930 params.SetString(kUrlParam, url);
931 browser_->SendProtocolCommand(debug_url_, kPageNavigateCommand, &params);
934 // DevToolsAdbBridge::RemoteBrowser -------------------------------------------
936 DevToolsAdbBridge::RemoteBrowser::RemoteBrowser(
937 scoped_refptr<RefCountedAdbThread> adb_thread,
938 scoped_refptr<AndroidDevice> device,
939 const std::string& socket)
940 : adb_thread_(adb_thread),
941 device_(device),
942 socket_(socket),
943 page_descriptors_(new base::ListValue()) {
946 bool DevToolsAdbBridge::RemoteBrowser::IsChrome() const {
947 return socket_.find(kChromeDefaultSocket) == 0;
950 DevToolsAdbBridge::RemoteBrowser::ParsedVersion
951 DevToolsAdbBridge::RemoteBrowser::GetParsedVersion() const {
952 ParsedVersion result;
953 std::vector<std::string> parts;
954 Tokenize(version_, ".", &parts);
955 for (size_t i = 0; i != parts.size(); ++i) {
956 int value = 0;
957 base::StringToInt(parts[i], &value);
958 result.push_back(value);
960 return result;
963 std::vector<DevToolsTargetImpl*>
964 DevToolsAdbBridge::RemoteBrowser::CreatePageTargets() {
965 std::vector<DevToolsTargetImpl*> result;
966 for (size_t i = 0; i < page_descriptors_->GetSize(); ++i) {
967 base::Value* item;
968 page_descriptors_->Get(i, &item);
969 if (!item)
970 continue;
971 base::DictionaryValue* dict;
972 if (!item->GetAsDictionary(&dict))
973 continue;
974 result.push_back(new RemotePageTarget(this, *dict));
976 return result;
979 void DevToolsAdbBridge::RemoteBrowser::SetPageDescriptors(
980 const base::ListValue& list) {
981 page_descriptors_.reset(list.DeepCopy());
984 static void RespondOnUIThread(base::Closure callback, int, const std::string&) {
985 if (!callback.is_null())
986 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
989 void DevToolsAdbBridge::RemoteBrowser::SendJsonRequest(
990 const std::string& request, base::Closure callback) {
991 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
992 adb_thread_->message_loop()->PostTask(FROM_HERE,
993 base::Bind(&AndroidDevice::HttpQuery, device_, socket_, request,
994 base::Bind(&RespondOnUIThread, callback)));
997 void DevToolsAdbBridge::RemoteBrowser::SendProtocolCommand(
998 const std::string& debug_url,
999 const std::string& method,
1000 base::DictionaryValue* params) {
1001 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1002 if (debug_url.empty())
1003 return;
1004 DevToolsProtocol::Command command(1, method, params);
1005 new AdbProtocolCommand(
1006 adb_thread_, device_, socket_, debug_url, command.Serialize());
1009 static void NoOp(int, const std::string&) {}
1011 void DevToolsAdbBridge::RemoteBrowser::Open(const std::string& input_url) {
1012 GURL gurl(input_url);
1013 if (!gurl.is_valid()) {
1014 gurl = GURL("http://" + input_url);
1015 if (!gurl.is_valid())
1016 return;
1018 std::string url = gurl.spec();
1020 ParsedVersion parsed_version = GetParsedVersion();
1021 if (IsChrome() &&
1022 !parsed_version.empty() &&
1023 parsed_version[0] >= kMinVersionNewWithURL) {
1024 std::string query = net::EscapeQueryParamValue(url, false /* use_plus */);
1025 std::string request =
1026 base::StringPrintf(kNewPageRequestWithURL, query.c_str());
1027 adb_thread_->message_loop()->PostTask(FROM_HERE,
1028 base::Bind(&AndroidDevice::HttpQuery,
1029 device_, socket_, request, base::Bind(&NoOp)));
1030 } else {
1031 adb_thread_->message_loop()->PostTask(FROM_HERE,
1032 base::Bind(&AndroidDevice::HttpQuery,
1033 device_, socket_, kNewPageRequest,
1034 base::Bind(&RemoteBrowser::PageCreatedOnHandlerThread, this, url)));
1038 void DevToolsAdbBridge::RemoteBrowser::PageCreatedOnHandlerThread(
1039 const std::string& url, int result, const std::string& response) {
1040 if (result < 0)
1041 return;
1042 // Navigating too soon after the page creation breaks navigation history
1043 // (crbug.com/311014). This can be avoided by adding a moderate delay.
1044 BrowserThread::PostDelayedTask(
1045 BrowserThread::UI, FROM_HERE,
1046 base::Bind(&RemoteBrowser::PageCreatedOnUIThread, this, response, url),
1047 base::TimeDelta::FromMilliseconds(kNewPageNavigateDelayMs));
1050 void DevToolsAdbBridge::RemoteBrowser::PageCreatedOnUIThread(
1051 const std::string& response, const std::string& url) {
1052 scoped_ptr<base::Value> value(base::JSONReader::Read(response));
1053 base::DictionaryValue* dict;
1054 if (value && value->GetAsDictionary(&dict)) {
1055 RemotePageTarget new_page(this, *dict);
1056 new_page.Navigate(url);
1060 DevToolsAdbBridge::RemoteBrowser::~RemoteBrowser() {
1064 // DevToolsAdbBridge::RemoteDevice --------------------------------------------
1066 DevToolsAdbBridge::RemoteDevice::RemoteDevice(
1067 scoped_refptr<AndroidDevice> device)
1068 : device_(device) {
1071 std::string DevToolsAdbBridge::RemoteDevice::GetSerial() {
1072 return device_->serial();
1075 std::string DevToolsAdbBridge::RemoteDevice::GetModel() {
1076 return device_->model();
1079 bool DevToolsAdbBridge::RemoteDevice::IsConnected() {
1080 return device_->is_connected();
1083 void DevToolsAdbBridge::RemoteDevice::AddBrowser(
1084 scoped_refptr<RemoteBrowser> browser) {
1085 browsers_.push_back(browser);
1088 DevToolsAdbBridge::RemoteDevice::~RemoteDevice() {
1092 // DevToolsAdbBridge ----------------------------------------------------------
1094 DevToolsAdbBridge::DevToolsAdbBridge()
1095 : adb_thread_(RefCountedAdbThread::GetInstance()),
1096 has_message_loop_(adb_thread_->message_loop() != NULL) {
1099 void DevToolsAdbBridge::AddListener(Listener* listener) {
1100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1101 if (listeners_.empty())
1102 RequestRemoteDevices();
1103 listeners_.push_back(listener);
1106 void DevToolsAdbBridge::RemoveListener(Listener* listener) {
1107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1108 Listeners::iterator it =
1109 std::find(listeners_.begin(), listeners_.end(), listener);
1110 DCHECK(it != listeners_.end());
1111 listeners_.erase(it);
1114 bool DevToolsAdbBridge::HasDevToolsWindow(const std::string& agent_id) {
1115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1116 return g_host_delegates.Get().find(agent_id) != g_host_delegates.Get().end();
1119 DevToolsAdbBridge::~DevToolsAdbBridge() {
1120 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1121 DCHECK(listeners_.empty());
1124 void DevToolsAdbBridge::RequestRemoteDevices() {
1125 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1126 if (!has_message_loop_)
1127 return;
1129 new AdbPagesCommand(
1130 adb_thread_, device_providers_,
1131 base::Bind(&DevToolsAdbBridge::ReceivedRemoteDevices, this));
1134 void DevToolsAdbBridge::ReceivedRemoteDevices(RemoteDevices* devices_ptr) {
1135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1137 scoped_ptr<RemoteDevices> devices(devices_ptr);
1139 Listeners copy(listeners_);
1140 for (Listeners::iterator it = copy.begin(); it != copy.end(); ++it)
1141 (*it)->RemoteDevicesChanged(devices.get());
1143 if (listeners_.empty())
1144 return;
1146 BrowserThread::PostDelayedTask(
1147 BrowserThread::UI,
1148 FROM_HERE,
1149 base::Bind(&DevToolsAdbBridge::RequestRemoteDevices, this),
1150 base::TimeDelta::FromMilliseconds(kAdbPollingIntervalMs));