diff options
author | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-26 06:18:41 +0000 |
---|---|---|
committer | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-26 06:18:41 +0000 |
commit | 49d89206f6e43333cdf558adc261ceec1844541c (patch) | |
tree | 82d2c0d1684ad884202ce3fd49922111c863bbae | |
parent | cb6ec5471fa7015852d1df5f03cb8b9a02d3fc2e (diff) | |
download | chromium_src-49d89206f6e43333cdf558adc261ceec1844541c.zip chromium_src-49d89206f6e43333cdf558adc261ceec1844541c.tar.gz chromium_src-49d89206f6e43333cdf558adc261ceec1844541c.tar.bz2 |
DevTools: extract ADB command classes, change objects' lifetimes
TBR=jhawkins (for minor webui changes)
Review URL: https://codereview.chromium.org/12586010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@190564 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/devtools/adb_client_socket.cc | 257 | ||||
-rw-r--r-- | chrome/browser/devtools/adb_client_socket.h | 29 | ||||
-rw-r--r-- | chrome/browser/devtools/devtools_adb_bridge.cc | 439 | ||||
-rw-r--r-- | chrome/browser/devtools/devtools_adb_bridge.h | 99 | ||||
-rw-r--r-- | chrome/browser/resources/inspect/inspect.js | 21 | ||||
-rw-r--r-- | chrome/browser/ui/webui/inspect_ui.cc | 54 | ||||
-rw-r--r-- | chrome/browser/ui/webui/inspect_ui.h | 7 |
7 files changed, 503 insertions, 403 deletions
diff --git a/chrome/browser/devtools/adb_client_socket.cc b/chrome/browser/devtools/adb_client_socket.cc index 342a098..a64171d 100644 --- a/chrome/browser/devtools/adb_client_socket.cc +++ b/chrome/browser/devtools/adb_client_socket.cc @@ -38,141 +38,14 @@ std::string EncodeMessage(const std::string& message) { return result + message; } -class AdbSocket { - public: - AdbSocket(const std::string& host, int port) : host_(host), port_(port) { - } - - ~AdbSocket() { - } - - protected: - void Connect(const net::CompletionCallback& callback) { - net::IPAddressNumber ip_number; - if (!net::ParseIPLiteralToNumber(host_, &ip_number)) { - callback.Run(net::ERR_FAILED); - return; - } - - net::AddressList address_list = - net::AddressList::CreateFromIPAddress(ip_number, port_); - socket_.reset(new net::TCPClientSocket(address_list, NULL, - net::NetLog::Source())); - int result = socket_->Connect(callback); - if (result != net::ERR_IO_PENDING) - callback.Run(result); - } - - void SendCommand(const std::string& command, - bool is_void, - const CommandCallback& callback) { - scoped_refptr<net::StringIOBuffer> request_buffer = - new net::StringIOBuffer(EncodeMessage(command)); - int result = socket_->Write(request_buffer, request_buffer->size(), - base::Bind(&AdbSocket::ReadResponse, base::Unretained(this), - callback, is_void)); - if (result != net::ERR_IO_PENDING) - ReadResponse(callback, is_void, result); - } - - scoped_ptr<net::TCPClientSocket> socket_; - - private: - void ReadResponse(const CommandCallback& callback, bool is_void, int result) { - if (result < 0) { - callback.Run(result, "IO error"); - return; - } - scoped_refptr<net::IOBuffer> response_buffer = - new net::IOBuffer(kBufferSize); - result = socket_->Read(response_buffer, kBufferSize, - base::Bind(&AdbSocket::OnResponseHeader, base::Unretained(this), - callback, is_void, response_buffer)); - if (result != net::ERR_IO_PENDING) - OnResponseHeader(callback, is_void, response_buffer, result); - } - - void OnResponseHeader(const CommandCallback& callback, - bool is_void, - scoped_refptr<net::IOBuffer> response_buffer, - int result) { - if (result < 0) { - callback.Run(result, "IO error"); - return; - } - - std::string data = std::string(response_buffer->data(), result); - if (result < 4) { - callback.Run(net::ERR_FAILED, "Response is too short: " + data); - return; - } - - std::string status = data.substr(0, 4); - if (status != kOkayResponse) { - callback.Run(net::ERR_FAILED, data); - return; - } - - data = data.substr(4); - - if (!is_void) { - int payload_length = 0; - int bytes_left = -1; - if (data.length() >= 4 && - base::HexStringToInt(data.substr(0, 4), &payload_length)) { - data = data.substr(4); - bytes_left = payload_length - result + 8; - } else { - bytes_left = -1; - } - OnResponseData(callback, data, response_buffer, bytes_left, 0); - } else { - callback.Run(net::OK, data); - } - } - - void OnResponseData(const CommandCallback& callback, - const std::string& response, - scoped_refptr<net::IOBuffer> response_buffer, - int bytes_left, - int result) { - if (result < 0) { - callback.Run(result, "IO error"); - return; - } - - bytes_left -= result; - std::string new_response = - response + std::string(response_buffer->data(), result); - if (bytes_left == 0) { - callback.Run(net::OK, new_response); - return; - } - - // Read tail - result = socket_->Read(response_buffer, kBufferSize, - base::Bind(&AdbSocket::OnResponseData, base::Unretained(this), - callback, new_response, response_buffer, bytes_left)); - if (result > 0) { - OnResponseData(callback, new_response, response_buffer, bytes_left, - result); - } else if (result != net::ERR_IO_PENDING) { - callback.Run(net::OK, new_response); - } - } - - std::string host_; - int port_; -}; - -class AdbTransportSocket : public AdbSocket { +class AdbTransportSocket : public AdbClientSocket { public: AdbTransportSocket(const std::string& host, int port, const std::string& serial, const std::string& socket_name, const SocketCallback& callback) - : AdbSocket(host, port), + : AdbClientSocket(host, port), serial_(serial), socket_name_(socket_name), callback_(callback) { @@ -294,6 +167,10 @@ class HttpOverAdbSocket { int result) { if (!CheckNetResultOrDie(result)) return; + if (result == 0) { + CheckNetResultOrDie(net::ERR_CONNECTION_CLOSED); + return; + } response_ += std::string(response_buffer->data(), result); int expected_length = 0; @@ -354,13 +231,13 @@ class HttpOverAdbSocket { size_t body_pos_; }; -class AdbQuerySocket : AdbSocket { +class AdbQuerySocket : AdbClientSocket { public: AdbQuerySocket(const std::string& host, int port, const std::string& query, const CommandCallback& callback) - : AdbSocket(host, port), + : AdbClientSocket(host, port), current_query_(0), callback_(callback) { if (Tokenize(query, "|", &queries_) == 0) { @@ -373,7 +250,6 @@ class AdbQuerySocket : AdbSocket { private: ~AdbQuerySocket() { - } void SendNextQuery(int result) { @@ -440,8 +316,123 @@ void AdbClientSocket::HttpQuery(int port, callback); } -AdbClientSocket::AdbClientSocket() { +AdbClientSocket::AdbClientSocket(const std::string& host, int port) + : host_(host), port_(port) { } AdbClientSocket::~AdbClientSocket() { } + +void AdbClientSocket::Connect(const net::CompletionCallback& callback) { + net::IPAddressNumber ip_number; + if (!net::ParseIPLiteralToNumber(host_, &ip_number)) { + callback.Run(net::ERR_FAILED); + return; + } + + net::AddressList address_list = + net::AddressList::CreateFromIPAddress(ip_number, port_); + socket_.reset(new net::TCPClientSocket(address_list, NULL, + net::NetLog::Source())); + int result = socket_->Connect(callback); + if (result != net::ERR_IO_PENDING) + callback.Run(result); +} + +void AdbClientSocket::SendCommand(const std::string& command, + bool is_void, + const CommandCallback& callback) { + scoped_refptr<net::StringIOBuffer> request_buffer = + new net::StringIOBuffer(EncodeMessage(command)); + int result = socket_->Write(request_buffer, request_buffer->size(), + base::Bind(&AdbClientSocket::ReadResponse, base::Unretained(this), + callback, is_void)); + if (result != net::ERR_IO_PENDING) + ReadResponse(callback, is_void, result); +} + +void AdbClientSocket::ReadResponse(const CommandCallback& callback, + bool is_void, + int result) { + if (result < 0) { + callback.Run(result, "IO error"); + return; + } + scoped_refptr<net::IOBuffer> response_buffer = + new net::IOBuffer(kBufferSize); + result = socket_->Read(response_buffer, kBufferSize, + base::Bind(&AdbClientSocket::OnResponseHeader, base::Unretained(this), + callback, is_void, response_buffer)); + if (result != net::ERR_IO_PENDING) + OnResponseHeader(callback, is_void, response_buffer, result); +} + +void AdbClientSocket::OnResponseHeader( + const CommandCallback& callback, + bool is_void, + scoped_refptr<net::IOBuffer> response_buffer, + int result) { + if (result <= 0) { + callback.Run(result == 0 ? net::ERR_CONNECTION_CLOSED : result, + "IO error"); + return; + } + + std::string data = std::string(response_buffer->data(), result); + if (result < 4) { + callback.Run(net::ERR_FAILED, "Response is too short: " + data); + return; + } + + std::string status = data.substr(0, 4); + if (status != kOkayResponse) { + callback.Run(net::ERR_FAILED, data); + return; + } + + data = data.substr(4); + + if (!is_void) { + int payload_length = 0; + int bytes_left = -1; + if (data.length() >= 4 && + base::HexStringToInt(data.substr(0, 4), &payload_length)) { + data = data.substr(4); + bytes_left = payload_length - result + 8; + } else { + bytes_left = -1; + } + OnResponseData(callback, data, response_buffer, bytes_left, 0); + } else { + callback.Run(net::OK, data); + } +} + +void AdbClientSocket::OnResponseData( + const CommandCallback& callback, + const std::string& response, + scoped_refptr<net::IOBuffer> response_buffer, + int bytes_left, + int result) { + if (result < 0) { + callback.Run(result, "IO error"); + return; + } + + bytes_left -= result; + std::string new_response = + response + std::string(response_buffer->data(), result); + if (bytes_left == 0) { + callback.Run(net::OK, new_response); + return; + } + + // Read tail + result = socket_->Read(response_buffer, kBufferSize, + base::Bind(&AdbClientSocket::OnResponseData, base::Unretained(this), + callback, new_response, response_buffer, bytes_left)); + if (result > 0) + OnResponseData(callback, new_response, response_buffer, bytes_left, result); + else if (result != net::ERR_IO_PENDING) + callback.Run(net::OK, new_response); +} diff --git a/chrome/browser/devtools/adb_client_socket.h b/chrome/browser/devtools/adb_client_socket.h index f5e7872..f93a1af 100644 --- a/chrome/browser/devtools/adb_client_socket.h +++ b/chrome/browser/devtools/adb_client_socket.h @@ -31,10 +31,35 @@ class AdbClientSocket { const std::string& request, const SocketCallback& callback); - private: - AdbClientSocket(); + AdbClientSocket(const std::string& host, int port); ~AdbClientSocket(); + protected: + void Connect(const net::CompletionCallback& callback); + + void SendCommand(const std::string& command, + bool is_void, + const CommandCallback& callback); + + scoped_ptr<net::TCPClientSocket> socket_; + + private: + void ReadResponse(const CommandCallback& callback, bool is_void, int result); + + void OnResponseHeader(const CommandCallback& callback, + bool is_void, + scoped_refptr<net::IOBuffer> response_buffer, + int result); + + void OnResponseData(const CommandCallback& callback, + const std::string& response, + scoped_refptr<net::IOBuffer> response_buffer, + int bytes_left, + int result); + + std::string host_; + int port_; + DISALLOW_COPY_AND_ASSIGN(AdbClientSocket); }; diff --git a/chrome/browser/devtools/devtools_adb_bridge.cc b/chrome/browser/devtools/devtools_adb_bridge.cc index 0efd62b..145e5e5 100644 --- a/chrome/browser/devtools/devtools_adb_bridge.cc +++ b/chrome/browser/devtools/devtools_adb_bridge.cc @@ -17,10 +17,16 @@ #include "base/threading/thread.h" #include "base/values.h" #include "chrome/browser/devtools/adb_client_socket.h" +#include "chrome/browser/profiles/profile.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/devtools_agent_host.h" +#include "content/public/browser/devtools_client_host.h" +#include "content/public/browser/devtools_manager.h" #include "net/base/net_errors.h" +#include "net/server/web_socket.h" using content::BrowserThread; +using net::WebSocket; namespace { @@ -29,237 +35,298 @@ static const char kDevToolsChannelName[] = "chrome_devtools_remote"; static const char kHostDevicesCommand[] = "host:devices"; static const char kDeviceModelCommand[] = "host:transport:%s|shell:getprop ro.product.model"; -static const char kPageListQuery[] = "/json"; + +static const char kPageListRequest[] = "GET /json HTTP/1.1\r\n\r\n"; +static const char kWebSocketUpgradeRequest[] = "GET %s HTTP/1.1\r\n" + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"; const int kAdbPort = 5037; +const int kBufferSize = 16 * 1024; + +typedef DevToolsAdbBridge::Callback Callback; +typedef DevToolsAdbBridge::PagesCallback PagesCallback; + +class AdbQueryCommand : public base::RefCounted<AdbQueryCommand> { + public: + AdbQueryCommand(const std::string& query, + const Callback& callback) + : query_(query), + callback_(callback) { + } + + void Run() { + AdbClientSocket::AdbQuery(kAdbPort, query_, + base::Bind(&AdbQueryCommand::Handle, this)); + } + + private: + friend class base::RefCounted<AdbQueryCommand>; + virtual ~AdbQueryCommand() {} + + void Handle(int result, const std::string& response) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&AdbQueryCommand::Respond, this, result, response)); + } + + void Respond(int result, const std::string& response) { + callback_.Run(result, response); + } + + std::string query_; + Callback callback_; +}; + +class AdbPagesCommand : public base::RefCounted<AdbPagesCommand> { + public: + explicit AdbPagesCommand(const PagesCallback& callback) + : callback_(callback) { + pages_.reset(new DevToolsAdbBridge::RemotePages()); + } + + void Run() { + AdbClientSocket::AdbQuery( + kAdbPort, kHostDevicesCommand, + base::Bind(&AdbPagesCommand::ReceivedDevices, this)); + } + + private: + friend class base::RefCounted<AdbPagesCommand>; + virtual ~AdbPagesCommand() {} + + void ReceivedDevices(int result, const std::string& response) { + if (result != net::OK) { + ProcessSerials(); + return; + } + + std::vector<std::string> devices; + Tokenize(response, "\n", &devices); + for (size_t i = 0; i < devices.size(); ++i) { + std::vector<std::string> tokens; + Tokenize(devices[i], "\t ", &tokens); + std::string serial = tokens[0]; + serials_.push_back(serial); + } + + ProcessSerials(); + } + + void ProcessSerials() { + if (serials_.size() == 0) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&AdbPagesCommand::Respond, this)); + return; + } + + AdbClientSocket::AdbQuery( + kAdbPort, + base::StringPrintf(kDeviceModelCommand, serials_.back().c_str()), + base::Bind(&AdbPagesCommand::ReceivedModel, this)); + } + + void ReceivedModel(int result, const std::string& response) { + if (result != net::OK) { + serials_.pop_back(); + ProcessSerials(); + return; + } + + AdbClientSocket::HttpQuery( + kAdbPort, serials_.back(), kDevToolsChannelName, kPageListRequest, + base::Bind(&AdbPagesCommand::ReceivedPages, this, response)); + } + + void ReceivedPages(const std::string& model, + int result, + const std::string& response) { + std::string serial = serials_.back(); + serials_.pop_back(); + if (result < 0) { + ProcessSerials(); + return; + } + + std::string body = response.substr(result); + scoped_ptr<base::Value> value(base::JSONReader::Read(body)); + base::ListValue* list_value; + if (!value || !value->GetAsList(&list_value)) { + ProcessSerials(); + return; + } + + base::Value* item; + for (size_t i = 0; i < list_value->GetSize(); ++i) { + list_value->Get(i, &item); + base::DictionaryValue* dict; + if (!item || !item->GetAsDictionary(&dict)) + continue; + pages_->push_back( + new DevToolsAdbBridge::RemotePage(serial, model, *dict)); + } + ProcessSerials(); + } + + void Respond() { + callback_.Run(net::OK, pages_.release()); + } + + PagesCallback callback_; + std::vector<std::string> serials_; + scoped_ptr<DevToolsAdbBridge::RemotePages> pages_; +}; + +class AdbAttachCommand : public base::RefCounted<AdbAttachCommand> { + public: + explicit AdbAttachCommand(scoped_refptr<DevToolsAdbBridge::RemotePage> page) + : page_(page) { + } + + void Run() { + AdbClientSocket::HttpQuery( + kAdbPort, page_->serial(), kDevToolsChannelName, + base::StringPrintf(kWebSocketUpgradeRequest, + page_->debug_url().c_str()), + base::Bind(&AdbAttachCommand::Handle, this)); + } + + private: + friend class base::RefCounted<AdbAttachCommand>; + virtual ~AdbAttachCommand() {} + + void Handle(int result, net::TCPClientSocket* socket) { + if (result != net::OK || socket == NULL) + return; + + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&AdbAttachCommand::OpenDevToolsWindow, this, socket)); + } + + void OpenDevToolsWindow(net::TCPClientSocket* socket) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + scoped_ptr<net::TCPClientSocket> tcp_socket(socket); + // TODO(pfeldman): Show DevToolsWindow here. + } + + scoped_refptr<DevToolsAdbBridge::RemotePage> page_; +}; } // namespace -DevToolsAdbBridge::AgentHost::AgentHost(const std::string& serial, - const std::string& model, - const base::DictionaryValue& value) +DevToolsAdbBridge::RemotePage::RemotePage(const std::string& serial, + const std::string& model, + const base::DictionaryValue& value) : serial_(serial), model_(model) { value.GetString("id", &id_); + value.GetString("url", &url_); value.GetString("title", &title_); value.GetString("descirption", &description_); value.GetString("faviconUrl", &favicon_url_); value.GetString("webSocketDebuggerUrl", &debug_url_); + value.GetString("devtoolsFrontendUrl", &frontend_url_); + + if (debug_url_.find("ws://") == 0) + debug_url_ = debug_url_.substr(5); + else + debug_url_ = ""; + + size_t ws_param = frontend_url_.find("?ws"); + if (ws_param != std::string::npos) + frontend_url_ = frontend_url_.substr(0, ws_param); } -DevToolsAdbBridge::AgentHost::~AgentHost() { +DevToolsAdbBridge::RemotePage::~RemotePage() { } +DevToolsAdbBridge::RefCountedAdbThread* +DevToolsAdbBridge::RefCountedAdbThread::instance_ = NULL; + // static -DevToolsAdbBridge* DevToolsAdbBridge::Start() { +scoped_refptr<DevToolsAdbBridge::RefCountedAdbThread> +DevToolsAdbBridge::RefCountedAdbThread::GetInstance() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - return new DevToolsAdbBridge(); + if (!instance_) + new RefCountedAdbThread(); + return instance_; } -void DevToolsAdbBridge::Stop() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!thread_.get()) { - ResetHandlerAndReleaseOnUIThread(); - return; +DevToolsAdbBridge::RefCountedAdbThread::RefCountedAdbThread() { + instance_ = this; + thread_ = new base::Thread(kDevToolsAdbBridgeThreadName); + base::Thread::Options options; + options.message_loop_type = MessageLoop::TYPE_IO; + if (!thread_->StartWithOptions(options)) { + delete thread_; + thread_ = NULL; } - BrowserThread::PostTaskAndReply( - BrowserThread::FILE, FROM_HERE, - base::Bind(&DevToolsAdbBridge::StopHandlerOnFileThread, - base::Unretained(this)), - base::Bind(&DevToolsAdbBridge::ResetHandlerAndReleaseOnUIThread, - base::Unretained(this))); } -void DevToolsAdbBridge::Query( - const std::string query, - const Callback& callback) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +MessageLoop* DevToolsAdbBridge::RefCountedAdbThread::message_loop() { + return thread_ ? thread_->message_loop() : NULL; +} - // There is a race condition in case Query immediately follows start. We - // consider it Ok since query is polling anyways. - if (!thread_.get()) { - callback.Run(net::ERR_FAILED, "ADB is not yet connected"); - return; - } - thread_->message_loop()->PostTask( - FROM_HERE, - base::Bind(&DevToolsAdbBridge::QueryOnHandlerThread, - base::Unretained(this), query, callback)); +// static +void DevToolsAdbBridge::RefCountedAdbThread::StopThread(base::Thread* thread) { + thread->Stop(); } -void DevToolsAdbBridge::Devices() { +DevToolsAdbBridge::RefCountedAdbThread::~RefCountedAdbThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - if (!thread_.get()) + instance_ = NULL; + if (!thread_) return; - - thread_->message_loop()->PostTask( - FROM_HERE, - base::Bind(&DevToolsAdbBridge::DevicesOnHandlerThread, - base::Unretained(this), - base::Bind(&DevToolsAdbBridge::PrintHosts, - base::Unretained(this)))); + // Shut down thread on FILE thread to join into IO. + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&RefCountedAdbThread::StopThread, thread_)); } -DevToolsAdbBridge::DevToolsAdbBridge() { - thread_.reset(new base::Thread(kDevToolsAdbBridgeThreadName)); - - base::Thread::Options options; - options.message_loop_type = MessageLoop::TYPE_IO; - if (!thread_->StartWithOptions(options)) - thread_.reset(); +DevToolsAdbBridge::DevToolsAdbBridge(Profile* profile) + : profile_(profile), + adb_thread_(RefCountedAdbThread::GetInstance()), + ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), + has_message_loop_(adb_thread_->message_loop() != NULL) { } DevToolsAdbBridge::~DevToolsAdbBridge() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - // Stop() must be called prior to destruction. - DCHECK(thread_.get() == NULL); -} - -// Runs on FILE thread to make sure that it is serialized against -// {Start|Stop}HandlerThread and to allow calling pthread_join. -void DevToolsAdbBridge::StopHandlerOnFileThread() { - if (!thread_->message_loop()) - return; - // Thread::Stop joins the thread. - thread_->Stop(); -} - -void DevToolsAdbBridge::ResetHandlerAndReleaseOnUIThread() { - ResetHandlerOnUIThread(); - delete this; } -void DevToolsAdbBridge::ResetHandlerOnUIThread() { - thread_.reset(); -} - -void DevToolsAdbBridge::QueryOnHandlerThread( +void DevToolsAdbBridge::Query( const std::string query, const Callback& callback) { - AdbClientSocket::AdbQuery(kAdbPort, query, - base::Bind(&DevToolsAdbBridge::QueryResponseOnHandlerThread, - base::Unretained(this), callback)); -} - -void DevToolsAdbBridge::QueryResponseOnHandlerThread( - const Callback& callback, - int result, - const std::string& response) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&DevToolsAdbBridge::RespondOnUIThread, base::Unretained(this), - callback, result, response)); -} - -void DevToolsAdbBridge::DevicesOnHandlerThread( - const HostsCallback& callback) { - AdbClientSocket::AdbQuery( - kAdbPort, kHostDevicesCommand, - base::Bind(&DevToolsAdbBridge::ReceivedDevices, - base::Unretained(this), callback)); -} - -void DevToolsAdbBridge::ReceivedDevices( - const HostsCallback& callback, - int result, - const std::string& response) { - AgentHosts* hosts = new AgentHosts(); - if (result != net::OK) { - callback.Run(result, hosts); - return; - } - - std::vector<std::string> devices; - Tokenize(response, "\n", &devices); - std::vector<std::string>* serials = new std::vector<std::string>(); - for (size_t i = 0; i < devices.size(); ++i) { - std::vector<std::string> tokens; - Tokenize(devices[i], "\t ", &tokens); - std::string serial = tokens[0]; - serials->push_back(serial); - } - - ProcessSerials(callback, hosts, serials); -} - -void DevToolsAdbBridge::ProcessSerials( - const HostsCallback& callback, - AgentHosts* hosts, - std::vector<std::string>* serials) { - if (serials->size() == 0) { - delete serials; - callback.Run(net::OK, hosts); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!has_message_loop_) { + callback.Run(net::ERR_FAILED, "Could not start ADB thread"); return; } - - AdbClientSocket::AdbQuery( - kAdbPort, - base::StringPrintf(kDeviceModelCommand, serials->back().c_str()), - base::Bind(&DevToolsAdbBridge::ReceivedModel, base::Unretained(this), - callback, hosts, serials)); + scoped_refptr<AdbQueryCommand> command(new AdbQueryCommand(query, callback)); + adb_thread_->message_loop()->PostTask(FROM_HERE, + base::Bind(&AdbQueryCommand::Run, command)); } -void DevToolsAdbBridge::ReceivedModel(const HostsCallback& callback, - AgentHosts* hosts, - std::vector<std::string>* serials, - int result, - const std::string& response) { - if (result != net::OK) { - serials->pop_back(); - ProcessSerials(callback, hosts, serials); +void DevToolsAdbBridge::Pages(const PagesCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!has_message_loop_) return; - } - - AdbClientSocket::HttpQuery( - kAdbPort, serials->back(), kDevToolsChannelName, kPageListQuery, - base::Bind(&DevToolsAdbBridge::ReceivedPages, base::Unretained(this), - callback, hosts, serials, response)); + scoped_refptr<AdbPagesCommand> command(new AdbPagesCommand(callback)); + adb_thread_->message_loop()->PostTask(FROM_HERE, + base::Bind(&AdbPagesCommand::Run, command)); } -void DevToolsAdbBridge::ReceivedPages(const HostsCallback& callback, - AgentHosts* hosts, - std::vector<std::string>* serials, - const std::string& model, - int result, - const std::string& response) { - std::string serial = serials->back(); - serials->pop_back(); - if (result != net::OK) { - ProcessSerials(callback, hosts, serials); - return; - } - - scoped_ptr<base::Value> value(base::JSONReader::Read(response)); - base::ListValue* list_value; - if (!value || !value->GetAsList(&list_value)) { - ProcessSerials(callback, hosts, serials); +void DevToolsAdbBridge::Attach(scoped_refptr<RemotePage> page) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!has_message_loop_) return; - } - - base::Value* item; - for (size_t i = 0; i < list_value->GetSize(); ++i) { - list_value->Get(i, &item); - base::DictionaryValue* dict; - if (!item || !item->GetAsDictionary(&dict)) - continue; - scoped_refptr<AgentHost> host = new AgentHost(serial, model, *dict); - hosts->push_back(host); - } - ProcessSerials(callback, hosts, serials); -} - -void DevToolsAdbBridge::RespondOnUIThread(const Callback& callback, - int result, - const std::string& response) { - callback.Run(result, response); -} -void DevToolsAdbBridge::PrintHosts(int result, AgentHosts* hosts) { - for (AgentHosts::iterator it = hosts->begin(); it != hosts->end(); ++it) { - AgentHost* host = it->get(); - fprintf(stderr, "HOST %s %s %s %s %s %s %s\n", host->serial().c_str(), - host->model().c_str(), host->id().c_str(), host->title().c_str(), - host->description().c_str(), host->favicon_url().c_str(), - host->debug_url().c_str()); - } + scoped_refptr<AdbAttachCommand> command(new AdbAttachCommand(page)); + adb_thread_->message_loop()->PostTask( + FROM_HERE, + base::Bind(&AdbAttachCommand::Run, command)); } diff --git a/chrome/browser/devtools/devtools_adb_bridge.h b/chrome/browser/devtools/devtools_adb_bridge.h index 2043ffc..8c9141b 100644 --- a/chrome/browser/devtools/devtools_adb_bridge.h +++ b/chrome/browser/devtools/devtools_adb_bridge.h @@ -9,7 +9,9 @@ #include <vector> #include "base/callback.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "net/socket/tcp_client_socket.h" namespace base { @@ -17,92 +19,77 @@ class Thread; class DictionaryValue; } +class MessageLoop; +class Profile; + class DevToolsAdbBridge { public: typedef base::Callback<void(int result, const std::string& response)> Callback; - class AgentHost : public base::RefCounted<AgentHost> { + class RemotePage : public base::RefCounted<RemotePage> { public: - AgentHost(const std::string& serial, - const std::string& model, - const base::DictionaryValue& value); + RemotePage(const std::string& serial, + const std::string& model, + const base::DictionaryValue& value); std::string serial() { return serial_; } std::string model() { return model_; } std::string id() { return id_; } + std::string url() { return url_; } std::string title() { return title_; } std::string description() { return description_; } std::string favicon_url() { return favicon_url_; } std::string debug_url() { return debug_url_; } + std::string frontend_url() { return frontend_url_; } private: - friend class base::RefCounted<AgentHost>; - - virtual ~AgentHost(); + friend class base::RefCounted<RemotePage>; + virtual ~RemotePage(); std::string serial_; std::string model_; std::string id_; + std::string url_; std::string title_; std::string description_; std::string favicon_url_; std::string debug_url_; - DISALLOW_COPY_AND_ASSIGN(AgentHost); + std::string frontend_url_; + DISALLOW_COPY_AND_ASSIGN(RemotePage); }; - typedef std::vector<scoped_refptr<AgentHost> > AgentHosts; - typedef base::Callback<void(int, AgentHosts*)> HostsCallback; + typedef std::vector<scoped_refptr<RemotePage> > RemotePages; + typedef base::Callback<void(int, RemotePages*)> PagesCallback; + + explicit DevToolsAdbBridge(Profile* profile); + ~DevToolsAdbBridge(); - static DevToolsAdbBridge* Start(); void Query(const std::string query, const Callback& callback); - void Devices(); - void Stop(); + void Pages(const PagesCallback& callback); + void Attach(scoped_refptr<RemotePage> page); private: - friend class base::RefCountedThreadSafe<DevToolsAdbBridge>; - - - explicit DevToolsAdbBridge(); - virtual ~DevToolsAdbBridge(); - - void StopHandlerOnFileThread(); - - void ResetHandlerAndReleaseOnUIThread(); - void ResetHandlerOnUIThread(); - - void QueryOnHandlerThread(const std::string query, const Callback& callback); - void QueryResponseOnHandlerThread(const Callback& callback, - int result, - const std::string& response); - - void DevicesOnHandlerThread(const HostsCallback& callback); - void ReceivedDevices(const HostsCallback& callback, - int result, - const std::string& response); - void ProcessSerials(const HostsCallback& callback, - AgentHosts* hosts, - std::vector<std::string>* serials); - void ReceivedModel(const HostsCallback& callback, - AgentHosts* hosts, - std::vector<std::string>* serials, - int result, - const std::string& response); - void ReceivedPages(const HostsCallback& callback, - AgentHosts* hosts, - std::vector<std::string>* serials, - const std::string& model, - int result, - const std::string& response); - - void RespondOnUIThread(const Callback& callback, - int result, - const std::string& response); - - void PrintHosts(int result, AgentHosts* hosts); - - // The thread used by the devtools to run client socket. - scoped_ptr<base::Thread> thread_; + friend class AdbWebSocket; + + class RefCountedAdbThread : public base::RefCounted<RefCountedAdbThread> { + public: + static scoped_refptr<RefCountedAdbThread> GetInstance(); + RefCountedAdbThread(); + MessageLoop* message_loop(); + + private: + friend class base::RefCounted<RefCountedAdbThread>; + static DevToolsAdbBridge::RefCountedAdbThread* instance_; + static void StopThread(base::Thread* thread); + + virtual ~RefCountedAdbThread(); + base::Thread* thread_; + }; + Profile* profile_; + scoped_refptr<RefCountedAdbThread> adb_thread_; + base::WeakPtrFactory<DevToolsAdbBridge> weak_factory_; + bool has_message_loop_; DISALLOW_COPY_AND_ASSIGN(DevToolsAdbBridge); }; diff --git a/chrome/browser/resources/inspect/inspect.js b/chrome/browser/resources/inspect/inspect.js index 88094f7..bae8877 100644 --- a/chrome/browser/resources/inspect/inspect.js +++ b/chrome/browser/resources/inspect/inspect.js @@ -104,16 +104,15 @@ AdbDevice.adbQuery_ = function(query) { * @return {?Object} ADB query result. * @private */ -AdbDevice.adbDevices_ = function() { +AdbDevice.adbPages_ = function() { var xhr = new XMLHttpRequest(); - xhr.open('GET', 'adb-devices', false); + xhr.open('GET', 'adb-pages', false); xhr.send(null); if (xhr.status !== 200) return null; try { - var result = JSON.parse(xhr.responseText); - return result[0] ? null : result[1]; + return JSON.parse(xhr.responseText); } catch (e) { } return null; @@ -164,8 +163,8 @@ AdbDevice.prototype.targets = function() { target['name'] = json['title']; target['url'] = json['url']; target['attached'] = !json['webSocketDebuggerUrl']; - target['favicon_url'] = json['faviconUrl']; - target['inspect_url'] = json['devtoolsFrontendUrl']; + target['faviconUrl'] = json['faviconUrl']; + target['inspectUrl'] = json['devtoolsFrontendUrl']; targets.push(target); } return targets; @@ -205,8 +204,8 @@ function requestData() { } function inspect(data) { - if (data['inspect_url']) { - window.open(data['inspect_url'], undefined, + if (data['inspectUrl']) { + window.open(data['inspectUrl'], undefined, 'location=0,width=800,height=600'); return; } @@ -278,14 +277,14 @@ function populateDeviceLists() { for (var j = 0; j < targets.length; j++) { addTargetToList(targets[j], 'device-' + device.serial, - ['favicon_url', 'name', 'url']); + ['faviconUrl', 'name', 'url']); } } setTimeout(populateDeviceLists, 1000); } function addToPagesList(data) { - addTargetToList(data, 'pages', ['favicon_url', 'name', 'url']); + addTargetToList(data, 'pages', ['faviconUrl', 'name', 'url']); } function addToExtensionsList(data) { @@ -306,7 +305,7 @@ function addToOthersList(data) { function formatValue(data, property) { var value = data[property]; - if (property == 'favicon_url') { + if (property == 'faviconUrl') { var faviconElement = document.createElement('img'); if (value) faviconElement.src = value; diff --git a/chrome/browser/ui/webui/inspect_ui.cc b/chrome/browser/ui/webui/inspect_ui.cc index 0562d12..437f3aa 100644 --- a/chrome/browser/ui/webui/inspect_ui.cc +++ b/chrome/browser/ui/webui/inspect_ui.cc @@ -42,6 +42,7 @@ #include "grit/browser_resources.h" #include "grit/generated_resources.h" #include "net/base/escape.h" +#include "net/base/net_errors.h" #include "ui/base/resource/resource_bundle.h" using content::BrowserThread; @@ -62,12 +63,13 @@ namespace { static const char kDataFile[] = "targets-data.json"; static const char kAdbQuery[] = "adb-query/"; -static const char kAdbDevices[] = "adb-devices"; +static const char kAdbPages[] = "adb-pages"; static const char kLocalXhr[] = "local-xhr/"; static const char kExtensionTargetType[] = "extension"; static const char kPageTargetType[] = "page"; static const char kWorkerTargetType[] = "worker"; +static const char kAdbTargetType[] = "adb_page"; static const char kInspectCommand[] = "inspect"; static const char kTerminateCommand[] = "terminate"; @@ -78,8 +80,12 @@ static const char kProcessIdField[] = "processId"; static const char kRouteIdField[] = "routeId"; static const char kUrlField[] = "url"; static const char kNameField[] = "name"; -static const char kFaviconUrlField[] = "favicon_url"; +static const char kFaviconUrlField[] = "faviconUrl"; static const char kPidField[] = "pid"; +static const char kAdbSerialField[] = "adbSerial"; +static const char kAdbModelField[] = "adbModel"; +static const char kAdbPageIdField[] = "adbPageId"; +static const char kAdbDebugUrl[] = "adbDebugUrl"; DictionaryValue* BuildTargetDescriptor( const std::string& target_type, @@ -169,7 +175,6 @@ void SendDescriptors( std::string json_string; base::JSONWriter::Write(rvh_list, &json_string); - callback.Run(base::RefCountedString::TakeString(&json_string)); } @@ -338,7 +343,7 @@ InspectUI::InspectUI(content::WebUI* web_ui) web_ui->AddMessageHandler(new InspectMessageHandler()); Profile* profile = Profile::FromWebUI(web_ui); - adb_bridge_ = DevToolsAdbBridge::Start(); + adb_bridge_.reset(new DevToolsAdbBridge(profile)); content::WebUIDataSource::Add(profile, CreateInspectUIHTMLSource()); registrar_.Add(this, @@ -383,10 +388,7 @@ void InspectUI::StopListeningNotifications() { if (!observer_) return; - if (adb_bridge_) { - adb_bridge_->Stop(); - adb_bridge_ = NULL; - } + adb_bridge_.reset(); observer_->InspectUIDestroyed(); observer_ = NULL; registrar_.RemoveAll(); @@ -408,8 +410,8 @@ bool InspectUI::HandleRequestCallback( const content::WebUIDataSource::GotDataCallback& callback) { if (path == kDataFile) return HandleDataRequestCallback(path, callback); - if (path.find(kAdbDevices) == 0) - return HandleAdbDevicesCallback(path, callback); + if (path.find(kAdbPages) == 0) + return HandleAdbPagesCallback(path, callback); if (path.find(kAdbQuery) == 0) return HandleAdbQueryCallback(path, callback); if (path.find(kLocalXhr) == 0) @@ -429,13 +431,39 @@ bool InspectUI::HandleAdbQueryCallback( return true; } -bool InspectUI::HandleAdbDevicesCallback( +bool InspectUI::HandleAdbPagesCallback( const std::string& path, const content::WebUIDataSource::GotDataCallback& callback) { - adb_bridge_->Devices(); + adb_bridge_->Pages(base::Bind(&InspectUI::OnAdbPages, + weak_factory_.GetWeakPtr(), + callback)); + return true; +} + +void InspectUI::OnAdbPages( + const content::WebUIDataSource::GotDataCallback& callback, + int result, + DevToolsAdbBridge::RemotePages* pages) { + if (result != net::OK) + return; + ListValue targets; + scoped_ptr<DevToolsAdbBridge::RemotePages> my_pages(pages); + for (DevToolsAdbBridge::RemotePages::iterator it = my_pages->begin(); + it != my_pages->end(); ++it) { + DevToolsAdbBridge::RemotePage* page = it->get(); + DictionaryValue* target_data = BuildTargetDescriptor(kAdbTargetType, + false, GURL(page->url()), page->title(), GURL(page->favicon_url()), 0, + 0); + target_data->SetString(kAdbSerialField, page->serial()); + target_data->SetString(kAdbModelField, page->model()); + target_data->SetString(kAdbPageIdField, page->id()); + target_data->SetString(kAdbDebugUrl, page->debug_url()); + targets.Append(target_data); + } + std::string json_string = ""; + base::JSONWriter::Write(&targets, &json_string); callback.Run(base::RefCountedString::TakeString(&json_string)); - return true; } bool InspectUI::HandleLocalXhrCallback( diff --git a/chrome/browser/ui/webui/inspect_ui.h b/chrome/browser/ui/webui/inspect_ui.h index ec382ba..264b9fd 100644 --- a/chrome/browser/ui/webui/inspect_ui.h +++ b/chrome/browser/ui/webui/inspect_ui.h @@ -44,7 +44,7 @@ class InspectUI : public content::WebUIController, const std::string& path, const content::WebUIDataSource::GotDataCallback& callback); - bool HandleAdbDevicesCallback( + bool HandleAdbPagesCallback( const std::string& path, const content::WebUIDataSource::GotDataCallback& callback); @@ -61,13 +61,16 @@ class InspectUI : public content::WebUIController, int result, const std::string& response); + void OnAdbPages(const content::WebUIDataSource::GotDataCallback& callback, + int result, + DevToolsAdbBridge::RemotePages* pages); scoped_refptr<WorkerCreationDestructionListener> observer_; // A scoped container for notification registries. content::NotificationRegistrar registrar_; - DevToolsAdbBridge* adb_bridge_; + scoped_ptr<DevToolsAdbBridge> adb_bridge_; base::WeakPtrFactory<InspectUI> weak_factory_; DISALLOW_COPY_AND_ASSIGN(InspectUI); |