DevTools: introduce Adb-based DevToolsExternalAgentProxyDelegate.
Review URL: https://chromiumcodereview.appspot.com/13119005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@190931 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/devtools/devtools_adb_bridge.cc b/chrome/browser/devtools/devtools_adb_bridge.cc
index 145e5e55..eddf865 100644
--- a/chrome/browser/devtools/devtools_adb_bridge.cc
+++ b/chrome/browser/devtools/devtools_adb_bridge.cc
@@ -4,13 +4,16 @@
#include "chrome/browser/devtools/devtools_adb_bridge.h"
+#include <map>
#include <vector>
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/json/json_reader.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/message_loop_proxy.h"
+#include "base/rand_util.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/strings/string_number_conversions.h"
@@ -21,6 +24,8 @@
#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_external_agent_proxy.h"
+#include "content/public/browser/devtools_external_agent_proxy_delegate.h"
#include "content/public/browser/devtools_manager.h"
#include "net/base/net_errors.h"
#include "net/server/web_socket.h"
@@ -180,17 +185,185 @@
scoped_ptr<DevToolsAdbBridge::RemotePages> pages_;
};
+} // namespace
+
+class AgentHostDelegate;
+
+typedef std::map<std::string, AgentHostDelegate*> AgentHostDelegates;
+
+base::LazyInstance<AgentHostDelegates>::Leaky g_host_delegates =
+ LAZY_INSTANCE_INITIALIZER;
+
+class AgentHostDelegate : public base::RefCountedThreadSafe<AgentHostDelegate>,
+ public content::DevToolsExternalAgentProxyDelegate {
+ public:
+ AgentHostDelegate(
+ const std::string& id,
+ scoped_refptr<DevToolsAdbBridge::RefCountedAdbThread> adb_thread,
+ net::TCPClientSocket* socket)
+ : id_(id),
+ adb_thread_(adb_thread),
+ socket_(socket) {
+ AddRef(); // Balanced in SelfDestruct.
+ proxy_.reset(content::DevToolsExternalAgentProxy::Create(this));
+ g_host_delegates.Get()[id] = this;
+ }
+
+ scoped_refptr<content::DevToolsAgentHost> GetAgentHost() {
+ return proxy_->GetAgentHost();
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<AgentHostDelegate>;
+
+ virtual ~AgentHostDelegate() {
+ g_host_delegates.Get().erase(id_);
+ }
+
+ virtual void Attach() OVERRIDE {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ adb_thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&AgentHostDelegate::StartListeningOnHandlerThread, this));
+ }
+
+ virtual void Detach() OVERRIDE {
+ adb_thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&AgentHostDelegate::CloseConnection, this, net::OK, false));
+ }
+
+ virtual void SendMessageToBackend(const std::string& message) OVERRIDE {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ adb_thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&AgentHostDelegate::SendFrameOnHandlerThread, this,
+ message));
+ }
+
+ void StartListeningOnHandlerThread() {
+ scoped_refptr<net::IOBuffer> response_buffer =
+ new net::IOBuffer(kBufferSize);
+ int result = socket_->Read(response_buffer, kBufferSize,
+ base::Bind(&AgentHostDelegate::OnBytesRead, this, response_buffer));
+ if (result != net::ERR_IO_PENDING)
+ OnBytesRead(response_buffer, result);
+ }
+
+ void OnBytesRead(scoped_refptr<net::IOBuffer> response_buffer, int result) {
+ if (!socket_)
+ return;
+
+ if (result <= 0) {
+ CloseIfNecessary(net::ERR_CONNECTION_CLOSED);
+ return;
+ }
+
+ std::string data = std::string(response_buffer->data(), result);
+ response_buffer_ += data;
+
+ int bytes_consumed;
+ std::string output;
+ WebSocket::ParseResult parse_result = WebSocket::DecodeFrameHybi17(
+ response_buffer_, false, &bytes_consumed, &output);
+
+ while (parse_result == WebSocket::FRAME_OK) {
+ response_buffer_ = response_buffer_.substr(bytes_consumed);
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&AgentHostDelegate::OnFrameRead, this, output));
+ parse_result = WebSocket::DecodeFrameHybi17(
+ response_buffer_, false, &bytes_consumed, &output);
+ }
+
+ if (parse_result == WebSocket::FRAME_ERROR ||
+ parse_result == WebSocket::FRAME_CLOSE) {
+ CloseIfNecessary(net::ERR_CONNECTION_CLOSED);
+ return;
+ }
+
+ result = socket_->Read(response_buffer, kBufferSize,
+ base::Bind(&AgentHostDelegate::OnBytesRead, this, response_buffer));
+ if (result != net::ERR_IO_PENDING)
+ OnBytesRead(response_buffer, result);
+ }
+
+ void SendFrameOnHandlerThread(const std::string& data) {
+ int mask = base::RandInt(0, 0x7FFFFFFF);
+ std::string encoded_frame = WebSocket::EncodeFrameHybi17(data, mask);
+ scoped_refptr<net::StringIOBuffer> request_buffer =
+ new net::StringIOBuffer(encoded_frame);
+ if (!socket_)
+ return;
+ int result = socket_->Write(request_buffer, request_buffer->size(),
+ base::Bind(&AgentHostDelegate::CloseIfNecessary, this));
+ if (result != net::ERR_IO_PENDING)
+ CloseIfNecessary(result);
+ }
+
+ void CloseConnection(int result, bool initiated_by_me) {
+ if (!socket_)
+ return;
+ socket_->Disconnect();
+ socket_.reset();
+ if (initiated_by_me) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&AgentHostDelegate::OnSocketClosed, this, result));
+ }
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&AgentHostDelegate::SelfDestruct, this));
+ }
+
+ void SelfDestruct() {
+ Release(); // Balanced in constructor.
+ }
+
+ void CloseIfNecessary(int result) {
+ if (result >= 0)
+ return;
+ CloseConnection(result, true);
+ }
+
+ void OnFrameRead(const std::string& message) {
+ scoped_ptr<base::Value> value(base::JSONReader::Read(message));
+ DictionaryValue* dvalue;
+ if (!value || !value->GetAsDictionary(&dvalue))
+ return;
+
+ content::DevToolsManager* manager = content::DevToolsManager::GetInstance();
+ content::DevToolsClientHost* client_host =
+ manager->GetDevToolsClientHostFor(proxy_->GetAgentHost());
+ if (client_host)
+ client_host->DispatchOnInspectorFrontend(message);
+ }
+
+ void OnSocketClosed(int result) {
+ proxy_->ConnectionClosed();
+ }
+
+ std::string id_;
+ scoped_refptr<DevToolsAdbBridge::RefCountedAdbThread> adb_thread_;
+ scoped_ptr<net::TCPClientSocket> socket_;
+ scoped_ptr<content::DevToolsExternalAgentProxy> proxy_;
+ std::string response_buffer_;
+ DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate);
+};
+
class AdbAttachCommand : public base::RefCounted<AdbAttachCommand> {
public:
- explicit AdbAttachCommand(scoped_refptr<DevToolsAdbBridge::RemotePage> page)
- : page_(page) {
+ explicit AdbAttachCommand(const base::WeakPtr<DevToolsAdbBridge>& bridge,
+ const std::string& serial,
+ const std::string& debug_url,
+ const std::string& frontend_url)
+ : bridge_(bridge),
+ serial_(serial),
+ debug_url_(debug_url),
+ frontend_url_(frontend_url) {
}
void Run() {
AdbClientSocket::HttpQuery(
- kAdbPort, page_->serial(), kDevToolsChannelName,
- base::StringPrintf(kWebSocketUpgradeRequest,
- page_->debug_url().c_str()),
+ kAdbPort, serial_, kDevToolsChannelName,
+ base::StringPrintf(kWebSocketUpgradeRequest, debug_url_.c_str()),
base::Bind(&AdbAttachCommand::Handle, this));
}
@@ -208,15 +381,33 @@
void OpenDevToolsWindow(net::TCPClientSocket* socket) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- scoped_ptr<net::TCPClientSocket> tcp_socket(socket);
- // TODO(pfeldman): Show DevToolsWindow here.
+
+ DevToolsAdbBridge* bridge = bridge_.get();
+ if (!bridge)
+ return;
+
+ std::string id = base::StringPrintf("%s:%s", serial_.c_str(),
+ debug_url_.c_str());
+ AgentHostDelegates::iterator it = g_host_delegates.Get().find(id);
+ AgentHostDelegate* delegate;
+ if (it != g_host_delegates.Get().end())
+ delegate = it->second;
+ else
+ delegate = new AgentHostDelegate(id, bridge->adb_thread_, socket);
+ // TODO(pfeldman): uncomment once DevToolsWindow is ready for external
+ // hosts.
+ // DevToolsWindow::OpenExternalFrontend(bridge->profile_,
+ // frontend_url_,
+ // delegate->GetAgentHost());
+ LOG(INFO) << delegate;
}
- scoped_refptr<DevToolsAdbBridge::RemotePage> page_;
+ base::WeakPtr<DevToolsAdbBridge> bridge_;
+ std::string serial_;
+ std::string debug_url_;
+ std::string frontend_url_;
};
-} // namespace
-
DevToolsAdbBridge::RemotePage::RemotePage(const std::string& serial,
const std::string& model,
const base::DictionaryValue& value)
@@ -238,6 +429,8 @@
size_t ws_param = frontend_url_.find("?ws");
if (ws_param != std::string::npos)
frontend_url_ = frontend_url_.substr(0, ws_param);
+ if (frontend_url_.find("http:") == 0)
+ frontend_url_ = "https:" + frontend_url_.substr(5);
}
DevToolsAdbBridge::RemotePage::~RemotePage() {
@@ -320,12 +513,16 @@
base::Bind(&AdbPagesCommand::Run, command));
}
-void DevToolsAdbBridge::Attach(scoped_refptr<RemotePage> page) {
+void DevToolsAdbBridge::Attach(const std::string& serial,
+ const std::string& debug_url,
+ const std::string& frontend_url) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!has_message_loop_)
return;
- scoped_refptr<AdbAttachCommand> command(new AdbAttachCommand(page));
+ scoped_refptr<AdbAttachCommand> command(
+ new AdbAttachCommand(weak_factory_.GetWeakPtr(), serial, debug_url,
+ frontend_url));
adb_thread_->message_loop()->PostTask(
FROM_HERE,
base::Bind(&AdbAttachCommand::Run, command));