blob: 145e5e55d7cd50ba7604c9f18ec25b4e3c8ab7b7 [file] [log] [blame]
[email protected]50a80dd2013-03-11 17:37:401// 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.
4
5#include "chrome/browser/devtools/devtools_adb_bridge.h"
6
[email protected]29b0a202013-03-22 09:34:157#include <vector>
8
[email protected]50a80dd2013-03-11 17:37:409#include "base/bind.h"
10#include "base/compiler_specific.h"
[email protected]29b0a202013-03-22 09:34:1511#include "base/json/json_reader.h"
[email protected]50a80dd2013-03-11 17:37:4012#include "base/logging.h"
13#include "base/message_loop_proxy.h"
[email protected]29b0a202013-03-22 09:34:1514#include "base/string_util.h"
[email protected]50a80dd2013-03-11 17:37:4015#include "base/stringprintf.h"
16#include "base/strings/string_number_conversions.h"
17#include "base/threading/thread.h"
[email protected]29b0a202013-03-22 09:34:1518#include "base/values.h"
[email protected]50a80dd2013-03-11 17:37:4019#include "chrome/browser/devtools/adb_client_socket.h"
[email protected]49d892062013-03-26 06:18:4120#include "chrome/browser/profiles/profile.h"
[email protected]50a80dd2013-03-11 17:37:4021#include "content/public/browser/browser_thread.h"
[email protected]49d892062013-03-26 06:18:4122#include "content/public/browser/devtools_agent_host.h"
23#include "content/public/browser/devtools_client_host.h"
24#include "content/public/browser/devtools_manager.h"
[email protected]29b0a202013-03-22 09:34:1525#include "net/base/net_errors.h"
[email protected]49d892062013-03-26 06:18:4126#include "net/server/web_socket.h"
[email protected]50a80dd2013-03-11 17:37:4027
28using content::BrowserThread;
[email protected]49d892062013-03-26 06:18:4129using net::WebSocket;
[email protected]50a80dd2013-03-11 17:37:4030
31namespace {
32
[email protected]29b0a202013-03-22 09:34:1533static const char kDevToolsAdbBridgeThreadName[] = "Chrome_DevToolsADBThread";
34static const char kDevToolsChannelName[] = "chrome_devtools_remote";
35static const char kHostDevicesCommand[] = "host:devices";
36static const char kDeviceModelCommand[] =
37 "host:transport:%s|shell:getprop ro.product.model";
[email protected]49d892062013-03-26 06:18:4138
39static const char kPageListRequest[] = "GET /json HTTP/1.1\r\n\r\n";
40static const char kWebSocketUpgradeRequest[] = "GET %s HTTP/1.1\r\n"
41 "Upgrade: WebSocket\r\n"
42 "Connection: Upgrade\r\n"
43 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
44 "Sec-WebSocket-Version: 13\r\n"
45 "\r\n";
[email protected]50a80dd2013-03-11 17:37:4046const int kAdbPort = 5037;
[email protected]49d892062013-03-26 06:18:4147const int kBufferSize = 16 * 1024;
48
49typedef DevToolsAdbBridge::Callback Callback;
50typedef DevToolsAdbBridge::PagesCallback PagesCallback;
51
52class AdbQueryCommand : public base::RefCounted<AdbQueryCommand> {
53 public:
54 AdbQueryCommand(const std::string& query,
55 const Callback& callback)
56 : query_(query),
57 callback_(callback) {
58 }
59
60 void Run() {
61 AdbClientSocket::AdbQuery(kAdbPort, query_,
62 base::Bind(&AdbQueryCommand::Handle, this));
63 }
64
65 private:
66 friend class base::RefCounted<AdbQueryCommand>;
67 virtual ~AdbQueryCommand() {}
68
69 void Handle(int result, const std::string& response) {
70 BrowserThread::PostTask(
71 BrowserThread::UI, FROM_HERE,
72 base::Bind(&AdbQueryCommand::Respond, this, result, response));
73 }
74
75 void Respond(int result, const std::string& response) {
76 callback_.Run(result, response);
77 }
78
79 std::string query_;
80 Callback callback_;
81};
82
83class AdbPagesCommand : public base::RefCounted<AdbPagesCommand> {
84 public:
85 explicit AdbPagesCommand(const PagesCallback& callback)
86 : callback_(callback) {
87 pages_.reset(new DevToolsAdbBridge::RemotePages());
88 }
89
90 void Run() {
91 AdbClientSocket::AdbQuery(
92 kAdbPort, kHostDevicesCommand,
93 base::Bind(&AdbPagesCommand::ReceivedDevices, this));
94 }
95
96 private:
97 friend class base::RefCounted<AdbPagesCommand>;
98 virtual ~AdbPagesCommand() {}
99
100 void ReceivedDevices(int result, const std::string& response) {
101 if (result != net::OK) {
102 ProcessSerials();
103 return;
104 }
105
106 std::vector<std::string> devices;
107 Tokenize(response, "\n", &devices);
108 for (size_t i = 0; i < devices.size(); ++i) {
109 std::vector<std::string> tokens;
110 Tokenize(devices[i], "\t ", &tokens);
111 std::string serial = tokens[0];
112 serials_.push_back(serial);
113 }
114
115 ProcessSerials();
116 }
117
118 void ProcessSerials() {
119 if (serials_.size() == 0) {
120 BrowserThread::PostTask(
121 BrowserThread::UI, FROM_HERE,
122 base::Bind(&AdbPagesCommand::Respond, this));
123 return;
124 }
125
126 AdbClientSocket::AdbQuery(
127 kAdbPort,
128 base::StringPrintf(kDeviceModelCommand, serials_.back().c_str()),
129 base::Bind(&AdbPagesCommand::ReceivedModel, this));
130 }
131
132 void ReceivedModel(int result, const std::string& response) {
133 if (result != net::OK) {
134 serials_.pop_back();
135 ProcessSerials();
136 return;
137 }
138
139 AdbClientSocket::HttpQuery(
140 kAdbPort, serials_.back(), kDevToolsChannelName, kPageListRequest,
141 base::Bind(&AdbPagesCommand::ReceivedPages, this, response));
142 }
143
144 void ReceivedPages(const std::string& model,
145 int result,
146 const std::string& response) {
147 std::string serial = serials_.back();
148 serials_.pop_back();
149 if (result < 0) {
150 ProcessSerials();
151 return;
152 }
153
154 std::string body = response.substr(result);
155 scoped_ptr<base::Value> value(base::JSONReader::Read(body));
156 base::ListValue* list_value;
157 if (!value || !value->GetAsList(&list_value)) {
158 ProcessSerials();
159 return;
160 }
161
162 base::Value* item;
163 for (size_t i = 0; i < list_value->GetSize(); ++i) {
164 list_value->Get(i, &item);
165 base::DictionaryValue* dict;
166 if (!item || !item->GetAsDictionary(&dict))
167 continue;
168 pages_->push_back(
169 new DevToolsAdbBridge::RemotePage(serial, model, *dict));
170 }
171 ProcessSerials();
172 }
173
174 void Respond() {
175 callback_.Run(net::OK, pages_.release());
176 }
177
178 PagesCallback callback_;
179 std::vector<std::string> serials_;
180 scoped_ptr<DevToolsAdbBridge::RemotePages> pages_;
181};
182
183class AdbAttachCommand : public base::RefCounted<AdbAttachCommand> {
184 public:
185 explicit AdbAttachCommand(scoped_refptr<DevToolsAdbBridge::RemotePage> page)
186 : page_(page) {
187 }
188
189 void Run() {
190 AdbClientSocket::HttpQuery(
191 kAdbPort, page_->serial(), kDevToolsChannelName,
192 base::StringPrintf(kWebSocketUpgradeRequest,
193 page_->debug_url().c_str()),
194 base::Bind(&AdbAttachCommand::Handle, this));
195 }
196
197 private:
198 friend class base::RefCounted<AdbAttachCommand>;
199 virtual ~AdbAttachCommand() {}
200
201 void Handle(int result, net::TCPClientSocket* socket) {
202 if (result != net::OK || socket == NULL)
203 return;
204
205 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
206 base::Bind(&AdbAttachCommand::OpenDevToolsWindow, this, socket));
207 }
208
209 void OpenDevToolsWindow(net::TCPClientSocket* socket) {
210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
211 scoped_ptr<net::TCPClientSocket> tcp_socket(socket);
212 // TODO(pfeldman): Show DevToolsWindow here.
213 }
214
215 scoped_refptr<DevToolsAdbBridge::RemotePage> page_;
216};
[email protected]50a80dd2013-03-11 17:37:40217
218} // namespace
219
[email protected]49d892062013-03-26 06:18:41220DevToolsAdbBridge::RemotePage::RemotePage(const std::string& serial,
221 const std::string& model,
222 const base::DictionaryValue& value)
[email protected]29b0a202013-03-22 09:34:15223 : serial_(serial),
224 model_(model) {
225 value.GetString("id", &id_);
[email protected]49d892062013-03-26 06:18:41226 value.GetString("url", &url_);
[email protected]29b0a202013-03-22 09:34:15227 value.GetString("title", &title_);
228 value.GetString("descirption", &description_);
229 value.GetString("faviconUrl", &favicon_url_);
230 value.GetString("webSocketDebuggerUrl", &debug_url_);
[email protected]49d892062013-03-26 06:18:41231 value.GetString("devtoolsFrontendUrl", &frontend_url_);
232
233 if (debug_url_.find("ws://") == 0)
234 debug_url_ = debug_url_.substr(5);
235 else
236 debug_url_ = "";
237
238 size_t ws_param = frontend_url_.find("?ws");
239 if (ws_param != std::string::npos)
240 frontend_url_ = frontend_url_.substr(0, ws_param);
[email protected]29b0a202013-03-22 09:34:15241}
242
[email protected]49d892062013-03-26 06:18:41243DevToolsAdbBridge::RemotePage::~RemotePage() {
244}
245
246DevToolsAdbBridge::RefCountedAdbThread*
247DevToolsAdbBridge::RefCountedAdbThread::instance_ = NULL;
248
249// static
250scoped_refptr<DevToolsAdbBridge::RefCountedAdbThread>
251DevToolsAdbBridge::RefCountedAdbThread::GetInstance() {
252 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
253 if (!instance_)
254 new RefCountedAdbThread();
255 return instance_;
256}
257
258DevToolsAdbBridge::RefCountedAdbThread::RefCountedAdbThread() {
259 instance_ = this;
260 thread_ = new base::Thread(kDevToolsAdbBridgeThreadName);
261 base::Thread::Options options;
262 options.message_loop_type = MessageLoop::TYPE_IO;
263 if (!thread_->StartWithOptions(options)) {
264 delete thread_;
265 thread_ = NULL;
266 }
267}
268
269MessageLoop* DevToolsAdbBridge::RefCountedAdbThread::message_loop() {
270 return thread_ ? thread_->message_loop() : NULL;
[email protected]29b0a202013-03-22 09:34:15271}
272
[email protected]50a80dd2013-03-11 17:37:40273// static
[email protected]49d892062013-03-26 06:18:41274void DevToolsAdbBridge::RefCountedAdbThread::StopThread(base::Thread* thread) {
275 thread->Stop();
[email protected]50a80dd2013-03-11 17:37:40276}
277
[email protected]49d892062013-03-26 06:18:41278DevToolsAdbBridge::RefCountedAdbThread::~RefCountedAdbThread() {
[email protected]50a80dd2013-03-11 17:37:40279 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]49d892062013-03-26 06:18:41280 instance_ = NULL;
281 if (!thread_)
[email protected]50a80dd2013-03-11 17:37:40282 return;
[email protected]49d892062013-03-26 06:18:41283 // Shut down thread on FILE thread to join into IO.
284 BrowserThread::PostTask(
[email protected]50a80dd2013-03-11 17:37:40285 BrowserThread::FILE, FROM_HERE,
[email protected]49d892062013-03-26 06:18:41286 base::Bind(&RefCountedAdbThread::StopThread, thread_));
287}
288
289DevToolsAdbBridge::DevToolsAdbBridge(Profile* profile)
290 : profile_(profile),
291 adb_thread_(RefCountedAdbThread::GetInstance()),
292 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
293 has_message_loop_(adb_thread_->message_loop() != NULL) {
294}
295
296DevToolsAdbBridge::~DevToolsAdbBridge() {
297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]50a80dd2013-03-11 17:37:40298}
299
300void DevToolsAdbBridge::Query(
301 const std::string query,
302 const Callback& callback) {
303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]49d892062013-03-26 06:18:41304 if (!has_message_loop_) {
305 callback.Run(net::ERR_FAILED, "Could not start ADB thread");
[email protected]50a80dd2013-03-11 17:37:40306 return;
307 }
[email protected]49d892062013-03-26 06:18:41308 scoped_refptr<AdbQueryCommand> command(new AdbQueryCommand(query, callback));
309 adb_thread_->message_loop()->PostTask(FROM_HERE,
310 base::Bind(&AdbQueryCommand::Run, command));
[email protected]29b0a202013-03-22 09:34:15311}
312
[email protected]49d892062013-03-26 06:18:41313void DevToolsAdbBridge::Pages(const PagesCallback& callback) {
[email protected]29b0a202013-03-22 09:34:15314 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]49d892062013-03-26 06:18:41315 if (!has_message_loop_)
[email protected]29b0a202013-03-22 09:34:15316 return;
317
[email protected]49d892062013-03-26 06:18:41318 scoped_refptr<AdbPagesCommand> command(new AdbPagesCommand(callback));
319 adb_thread_->message_loop()->PostTask(FROM_HERE,
320 base::Bind(&AdbPagesCommand::Run, command));
[email protected]50a80dd2013-03-11 17:37:40321}
322
[email protected]49d892062013-03-26 06:18:41323void DevToolsAdbBridge::Attach(scoped_refptr<RemotePage> page) {
[email protected]50a80dd2013-03-11 17:37:40324 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]49d892062013-03-26 06:18:41325 if (!has_message_loop_)
[email protected]50a80dd2013-03-11 17:37:40326 return;
[email protected]50a80dd2013-03-11 17:37:40327
[email protected]49d892062013-03-26 06:18:41328 scoped_refptr<AdbAttachCommand> command(new AdbAttachCommand(page));
329 adb_thread_->message_loop()->PostTask(
330 FROM_HERE,
331 base::Bind(&AdbAttachCommand::Run, command));
[email protected]50a80dd2013-03-11 17:37:40332}