Commit 4f1226d3 authored by yzshen's avatar yzshen Committed by Commit bot

Mandoline DevTools service: remote debugging protocol over HTTP/WebSocket.

This CL:
- adds service-side DevTools agent representation (DevToolsAgentHost) and registration for DevTools agents.
- supports requesting the list of available agents using HTTP request and communicating with them using WebSocket.

BUG=478249
TEST=None

Review URL: https://codereview.chromium.org/1164383002

Cr-Commit-Position: refs/heads/master@{#333804}
parent eb2fad48
...@@ -6,6 +6,8 @@ import("//mojo/public/mojo_application.gni") ...@@ -6,6 +6,8 @@ import("//mojo/public/mojo_application.gni")
source_set("lib") { source_set("lib") {
sources = [ sources = [
"devtools_agent_host.cc",
"devtools_agent_host.h",
"devtools_http_server.cc", "devtools_http_server.cc",
"devtools_http_server.h", "devtools_http_server.h",
"devtools_registry_impl.cc", "devtools_registry_impl.cc",
...@@ -21,6 +23,7 @@ source_set("lib") { ...@@ -21,6 +23,7 @@ source_set("lib") {
"//components/devtools_service/public/interfaces", "//components/devtools_service/public/interfaces",
"//mojo/application/public/cpp", "//mojo/application/public/cpp",
"//mojo/common", "//mojo/common",
"//mojo/services/network/public/cpp",
"//mojo/services/network/public/interfaces", "//mojo/services/network/public/interfaces",
"//third_party/mojo/src/mojo/public/cpp/bindings", "//third_party/mojo/src/mojo/public/cpp/bindings",
"//url", "//url",
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/devtools_service/devtools_agent_host.h"
#include "base/guid.h"
#include "base/logging.h"
namespace devtools_service {
DevToolsAgentHost::DevToolsAgentHost(DevToolsAgentPtr agent)
: id_(base::GenerateGUID()),
agent_(agent.Pass()),
binding_(this),
delegate_(nullptr) {
agent_.set_error_handler(this);
}
DevToolsAgentHost::~DevToolsAgentHost() {
if (delegate_)
delegate_->OnAgentHostClosed(this);
}
void DevToolsAgentHost::SetDelegate(Delegate* delegate) {
delegate_ = delegate;
if (delegate_) {
if (binding_.is_bound())
return;
DevToolsAgentClientPtr client;
binding_.Bind(&client);
agent_->SetClient(client.Pass(), id_);
} else {
if (!binding_.is_bound())
return;
binding_.Close();
}
}
void DevToolsAgentHost::SendProtocolMessageToAgent(const std::string& message) {
agent_->DispatchProtocolMessage(message);
}
void DevToolsAgentHost::DispatchProtocolMessage(const mojo::String& message) {
delegate_->DispatchProtocolMessage(this, message);
}
void DevToolsAgentHost::OnConnectionError() {
agent_connection_error_handler_.Run();
}
} // namespace devtools_service
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_DEVTOOLS_SERVICE_DEVTOOLS_AGENT_HOST_H_
#define COMPONENTS_DEVTOOLS_SERVICE_DEVTOOLS_AGENT_HOST_H_
#include <string>
#include "base/macros.h"
#include "components/devtools_service/public/interfaces/devtools_service.mojom.h"
#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h"
#include "third_party/mojo/src/mojo/public/cpp/bindings/callback.h"
#include "third_party/mojo/src/mojo/public/cpp/bindings/error_handler.h"
namespace devtools_service {
// DevToolsAgentHost represents a DevTools agent at the service side.
class DevToolsAgentHost : public DevToolsAgentClient,
public mojo::ErrorHandler {
public:
class Delegate {
public:
virtual ~Delegate() {}
virtual void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
const std::string& message) = 0;
// Notifies the delegate that |agent_host| is going away.
virtual void OnAgentHostClosed(DevToolsAgentHost* agent_host) = 0;
};
explicit DevToolsAgentHost(DevToolsAgentPtr agent);
~DevToolsAgentHost() override;
void set_agent_connection_error_handler(const mojo::Closure& handler) {
agent_connection_error_handler_ = handler;
}
std::string id() const { return id_; }
// Doesn't take ownership of |delegate|. If |delegate| dies before this
// object, a new delegate or nullptr must be set so that this object doesn't
// hold an invalid pointer.
void SetDelegate(Delegate* delegate);
bool IsAttached() const { return !!delegate_; }
void SendProtocolMessageToAgent(const std::string& message);
private:
// DevToolsAgentClient implementation.
void DispatchProtocolMessage(const mojo::String& message) override;
// mojo::ErrorHandler implementation.
void OnConnectionError() override;
const std::string id_;
DevToolsAgentPtr agent_;
mojo::Closure agent_connection_error_handler_;
mojo::Binding<DevToolsAgentClient> binding_;
Delegate* delegate_;
DISALLOW_COPY_AND_ASSIGN(DevToolsAgentHost);
};
} // namespace devtools_service
#endif // COMPONENTS_DEVTOOLS_SERVICE_DEVTOOLS_AGENT_HOST_H_
...@@ -4,17 +4,282 @@ ...@@ -4,17 +4,282 @@
#include "components/devtools_service/devtools_http_server.h" #include "components/devtools_service/devtools_http_server.h"
#include <string.h>
#include <string>
#include "base/bind.h"
#include "base/json/json_writer.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/values.h"
#include "components/devtools_service/devtools_agent_host.h"
#include "components/devtools_service/devtools_registry_impl.h"
#include "components/devtools_service/devtools_service.h" #include "components/devtools_service/devtools_service.h"
#include "mojo/application/public/cpp/application_impl.h" #include "mojo/application/public/cpp/application_impl.h"
#include "mojo/services/network/public/interfaces/http_message.mojom.h" #include "mojo/services/network/public/cpp/web_socket_read_queue.h"
#include "mojo/services/network/public/cpp/web_socket_write_queue.h"
#include "mojo/services/network/public/interfaces/net_address.mojom.h" #include "mojo/services/network/public/interfaces/net_address.mojom.h"
#include "mojo/services/network/public/interfaces/network_service.mojom.h" #include "mojo/services/network/public/interfaces/network_service.mojom.h"
#include "mojo/services/network/public/interfaces/web_socket.mojom.h"
#include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h"
namespace devtools_service { namespace devtools_service {
namespace {
const char kPageUrlPrefix[] = "/devtools/page/";
const char kBrowserUrlPrefix[] = "/devtools/browser";
const char kJsonRequestUrlPrefix[] = "/json";
const char kActivateCommand[] = "activate";
const char kCloseCommand[] = "close";
const char kListCommand[] = "list";
const char kNewCommand[] = "new";
const char kVersionCommand[] = "version";
const char kTargetIdField[] = "id";
const char kTargetTypeField[] = "type";
const char kTargetTitleField[] = "title";
const char kTargetDescriptionField[] = "description";
const char kTargetUrlField[] = "url";
const char kTargetWebSocketDebuggerUrlField[] = "webSocketDebuggerUrl";
const char kTargetDevtoolsFrontendUrlField[] = "devtoolsFrontendUrl";
bool ParseJsonPath(const std::string& path,
std::string* command,
std::string* target_id) {
// Fall back to list in case of empty query.
if (path.empty()) {
*command = kListCommand;
return true;
}
if (path.find("/") != 0) {
// Malformed command.
return false;
}
*command = path.substr(1);
size_t separator_pos = command->find("/");
if (separator_pos != std::string::npos) {
*target_id = command->substr(separator_pos + 1);
*command = command->substr(0, separator_pos);
}
return true;
}
mojo::HttpResponsePtr MakeResponse(uint32_t status_code,
const std::string& content_type,
const std::string& body) {
mojo::HttpResponsePtr response(mojo::HttpResponse::New());
response->headers.resize(2);
response->headers[0] = mojo::HttpHeader::New();
response->headers[0]->name = "Content-Length";
response->headers[0]->value =
base::StringPrintf("%lu", static_cast<unsigned long>(body.size()));
response->headers[1] = mojo::HttpHeader::New();
response->headers[1]->name = "Content-Type";
response->headers[1]->value = content_type;
if (!body.empty()) {
uint32_t num_bytes = static_cast<uint32_t>(body.size());
MojoCreateDataPipeOptions options;
options.struct_size = sizeof(MojoCreateDataPipeOptions);
options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
options.element_num_bytes = 1;
options.capacity_num_bytes = num_bytes;
mojo::DataPipe data_pipe(options);
response->body = data_pipe.consumer_handle.Pass();
MojoResult result =
WriteDataRaw(data_pipe.producer_handle.get(), body.data(), &num_bytes,
MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
CHECK_EQ(MOJO_RESULT_OK, result);
}
return response.Pass();
}
mojo::HttpResponsePtr MakeJsonResponse(uint32_t status_code,
base::Value* value,
const std::string& message) {
// Serialize value and message.
std::string json_value;
if (value) {
base::JSONWriter::WriteWithOptions(
*value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_value);
}
return MakeResponse(status_code, "application/json; charset=UTF-8",
json_value + message);
}
class WebSocketRelayer : public DevToolsAgentHost::Delegate,
public mojo::WebSocketClient,
public mojo::ErrorHandler {
public:
// Creates a WebSocketRelayer instance and sets it as the delegate of
// |agent_host|.
//
// The object destroys itself when either of the following happens:
// - |agent_host| is dead and the object finishes all pending sends (if any)
// to the Web socket; or
// - the underlying pipe of |web_socket| is closed and the object finishes all
// pending receives (if any) from the Web socket.
static mojo::WebSocketClientPtr SetUp(
DevToolsAgentHost* agent_host,
mojo::WebSocketPtr web_socket,
mojo::ScopedDataPipeProducerHandle send_stream) {
DCHECK(agent_host);
DCHECK(web_socket);
DCHECK(send_stream.is_valid());
mojo::WebSocketClientPtr web_socket_client;
new WebSocketRelayer(agent_host, web_socket.Pass(), send_stream.Pass(),
&web_socket_client);
return web_socket_client.Pass();
}
private:
WebSocketRelayer(DevToolsAgentHost* agent_host,
mojo::WebSocketPtr web_socket,
mojo::ScopedDataPipeProducerHandle send_stream,
mojo::WebSocketClientPtr* web_socket_client)
: agent_host_(agent_host),
binding_(this, web_socket_client),
web_socket_(web_socket.Pass()),
send_stream_(send_stream.Pass()),
write_send_stream_(new mojo::WebSocketWriteQueue(send_stream_.get())),
pending_send_count_(0),
pending_receive_count_(0) {
web_socket_.set_error_handler(this);
agent_host->SetDelegate(this);
}
~WebSocketRelayer() override {
if (agent_host_)
agent_host_->SetDelegate(nullptr);
}
// DevToolsAgentHost::Delegate implementation.
void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
const std::string& message) override {
if (!web_socket_)
return;
// TODO(yzshen): It shouldn't be an issue to pass an empty message. However,
// WebSocket{Read,Write}Queue doesn't handle that correctly.
if (message.empty())
return;
pending_send_count_++;
uint32_t size = static_cast<uint32_t>(message.size());
write_send_stream_->Write(
&message[0], size,
base::Bind(&WebSocketRelayer::OnFinishedWritingSendStream,
base::Unretained(this), size));
}
void OnAgentHostClosed(DevToolsAgentHost* agent_host) override {
DispatchProtocolMessage(agent_host_,
"{ \"method\": \"Inspector.detached\", "
"\"params\": { \"reason\": \"target_closed\" } }");
// No need to call SetDelegate(nullptr) on |agent_host_| because it is going
// away.
agent_host_ = nullptr;
if (ShouldSelfDestruct())
delete this;
}
// WebSocketClient implementation.
void DidConnect(const mojo::String& selected_subprotocol,
const mojo::String& extensions,
mojo::ScopedDataPipeConsumerHandle receive_stream) override {
receive_stream_ = receive_stream.Pass();
read_receive_stream_.reset(
new mojo::WebSocketReadQueue(receive_stream_.get()));
}
void DidReceiveData(bool fin,
mojo::WebSocket::MessageType type,
uint32_t num_bytes) override {
if (!agent_host_)
return;
// TODO(yzshen): It shouldn't be an issue to pass an empty message. However,
// WebSocket{Read,Write}Queue doesn't handle that correctly.
if (num_bytes == 0)
return;
pending_receive_count_++;
read_receive_stream_->Read(
num_bytes, base::Bind(&WebSocketRelayer::OnFinishedReadingReceiveStream,
base::Unretained(this), num_bytes));
}
void DidReceiveFlowControl(int64_t quota) override {}
void DidFail(const mojo::String& message) override {}
void DidClose(bool was_clean,
uint16_t code,
const mojo::String& reason) override {}
// mojo::ErrorHandler implementation.
void OnConnectionError() override {
web_socket_ = nullptr;
binding_.Close();
if (ShouldSelfDestruct())
delete this;
}
void OnFinishedWritingSendStream(uint32_t num_bytes, const char* buffer) {
DCHECK_GT(pending_send_count_, 0u);
pending_send_count_--;
if (web_socket_ && buffer)
web_socket_->Send(true, mojo::WebSocket::MESSAGE_TYPE_TEXT, num_bytes);
if (ShouldSelfDestruct())
delete this;
}
void OnFinishedReadingReceiveStream(uint32_t num_bytes, const char* data) {
DCHECK_GT(pending_receive_count_, 0u);
pending_receive_count_--;
if (agent_host_ && data)
agent_host_->SendProtocolMessageToAgent(std::string(data, num_bytes));
if (ShouldSelfDestruct())
delete this;
}
bool ShouldSelfDestruct() const {
return (!agent_host_ && pending_send_count_ == 0) ||
(!web_socket_ && pending_receive_count_ == 0);
}
DevToolsAgentHost* agent_host_;
mojo::Binding<WebSocketClient> binding_;
mojo::WebSocketPtr web_socket_;
mojo::ScopedDataPipeProducerHandle send_stream_;
scoped_ptr<mojo::WebSocketWriteQueue> write_send_stream_;
size_t pending_send_count_;
mojo::ScopedDataPipeConsumerHandle receive_stream_;
scoped_ptr<mojo::WebSocketReadQueue> read_receive_stream_;
size_t pending_receive_count_;
DISALLOW_COPY_AND_ASSIGN(WebSocketRelayer);
};
} // namespace
class DevToolsHttpServer::HttpConnectionDelegateImpl class DevToolsHttpServer::HttpConnectionDelegateImpl
: public mojo::HttpConnectionDelegate, : public mojo::HttpConnectionDelegate,
public mojo::ErrorHandler { public mojo::ErrorHandler {
...@@ -61,7 +326,7 @@ class DevToolsHttpServer::HttpConnectionDelegateImpl ...@@ -61,7 +326,7 @@ class DevToolsHttpServer::HttpConnectionDelegateImpl
DevToolsHttpServer::DevToolsHttpServer(DevToolsService* service, DevToolsHttpServer::DevToolsHttpServer(DevToolsService* service,
uint16_t remote_debugging_port) uint16_t remote_debugging_port)
: service_(service) { : service_(service), remote_debugging_port_(remote_debugging_port) {
VLOG(1) << "Remote debugging HTTP server is started on port " VLOG(1) << "Remote debugging HTTP server is started on port "
<< remote_debugging_port << "."; << remote_debugging_port << ".";
mojo::NetworkServicePtr network_service; mojo::NetworkServicePtr network_service;
...@@ -104,30 +369,13 @@ void DevToolsHttpServer::OnReceivedRequest( ...@@ -104,30 +369,13 @@ void DevToolsHttpServer::OnReceivedRequest(
const OnReceivedRequestCallback& callback) { const OnReceivedRequestCallback& callback) {
DCHECK(connections_.find(connection) != connections_.end()); DCHECK(connections_.find(connection) != connections_.end());
if (request->url.get().find(kJsonRequestUrlPrefix) == 0) {
callback.Run(ProcessJsonRequest(request.Pass()));
} else {
// TODO(yzshen): Implement it. // TODO(yzshen): Implement it.
static const char kNotImplemented[] = "Not implemented yet!"; NOTIMPLEMENTED();
mojo::HttpResponsePtr response(mojo::HttpResponse::New()); callback.Run(MakeResponse(404, "text/html", "Not implemented yet!"));
response->headers.resize(2); }
response->headers[0] = mojo::HttpHeader::New();
response->headers[0]->name = "Content-Length";
response->headers[0]->value = base::StringPrintf(
"%lu", static_cast<unsigned long>(sizeof(kNotImplemented)));
response->headers[1] = mojo::HttpHeader::New();
response->headers[1]->name = "Content-Type";
response->headers[1]->value = "text/html";
uint32_t num_bytes = sizeof(kNotImplemented);
MojoCreateDataPipeOptions options;
options.struct_size = sizeof(MojoCreateDataPipeOptions);
options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
options.element_num_bytes = 1;
options.capacity_num_bytes = num_bytes;
mojo::DataPipe data_pipe(options);
response->body = data_pipe.consumer_handle.Pass();
WriteDataRaw(data_pipe.producer_handle.get(), kNotImplemented, &num_bytes,
MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
callback.Run(response.Pass());
} }
void DevToolsHttpServer::OnReceivedWebSocketRequest( void DevToolsHttpServer::OnReceivedWebSocketRequest(
...@@ -136,8 +384,36 @@ void DevToolsHttpServer::OnReceivedWebSocketRequest( ...@@ -136,8 +384,36 @@ void DevToolsHttpServer::OnReceivedWebSocketRequest(
const OnReceivedWebSocketRequestCallback& callback) { const OnReceivedWebSocketRequestCallback& callback) {
DCHECK(connections_.find(connection) != connections_.end()); DCHECK(connections_.find(connection) != connections_.end());
std::string path = request->url;
size_t browser_pos = path.find(kBrowserUrlPrefix);
if (browser_pos == 0) {
// TODO(yzshen): Implement it. // TODO(yzshen): Implement it.
NOTIMPLEMENTED(); NOTIMPLEMENTED();
callback.Run(nullptr, mojo::ScopedDataPipeConsumerHandle(), nullptr);
return;
}
size_t pos = path.find(kPageUrlPrefix);
if (pos != 0) {
callback.Run(nullptr, mojo::ScopedDataPipeConsumerHandle(), nullptr);
return;
}
std::string target_id = path.substr(strlen(kPageUrlPrefix));
DevToolsAgentHost* agent = service_->registry()->GetAgentById(target_id);
if (!agent || agent->IsAttached()) {
callback.Run(nullptr, mojo::ScopedDataPipeConsumerHandle(), nullptr);
return;
}
mojo::WebSocketPtr web_socket;
mojo::InterfaceRequest<mojo::WebSocket> web_socket_request =
mojo::GetProxy(&web_socket);
mojo::DataPipe data_pipe;
mojo::WebSocketClientPtr web_socket_client = WebSocketRelayer::SetUp(
agent, web_socket.Pass(), data_pipe.producer_handle.Pass());
callback.Run(web_socket_request.Pass(), data_pipe.consumer_handle.Pass(),
web_socket_client.Pass());
} }
void DevToolsHttpServer::OnConnectionClosed( void DevToolsHttpServer::OnConnectionClosed(
...@@ -148,4 +424,58 @@ void DevToolsHttpServer::OnConnectionClosed( ...@@ -148,4 +424,58 @@ void DevToolsHttpServer::OnConnectionClosed(
connections_.erase(connection); connections_.erase(connection);
} }
mojo::HttpResponsePtr DevToolsHttpServer::ProcessJsonRequest(
mojo::HttpRequestPtr request) {
// Trim "/json".
std::string path = request->url.get().substr(strlen(kJsonRequestUrlPrefix));
// Trim query.
size_t query_pos = path.find("?");
if (query_pos != std::string::npos)
path = path.substr(0, query_pos);
// Trim fragment.
size_t fragment_pos = path.find("#");
if (fragment_pos != std::string::npos)
path = path.substr(0, fragment_pos);
std::string command;
std::string target_id;
if (!ParseJsonPath(path, &command, &target_id))
return MakeJsonResponse(404, nullptr,
"Malformed query: " + request->url.get());
if (command == kVersionCommand || command == kNewCommand ||
command == kActivateCommand || command == kCloseCommand) {
NOTIMPLEMENTED();
return MakeJsonResponse(404, nullptr,
"Not implemented yet: " + request->url.get());
}
if (command == kListCommand) {
base::ListValue list_value;
for (DevToolsRegistryImpl::Iterator iter(service_->registry());
!iter.IsAtEnd(); iter.Advance()) {
scoped_ptr<base::DictionaryValue> dict_value(new base::DictionaryValue());
// TODO(yzshen): Add more information.
dict_value->SetString(kTargetDescriptionField, std::string());
dict_value->SetString(kTargetDevtoolsFrontendUrlField, std::string());
dict_value->SetString(kTargetIdField, iter.value()->id());
dict_value->SetString(kTargetTitleField, std::string());
dict_value->SetString(kTargetTypeField, "page");
dict_value->SetString(kTargetUrlField, std::string());
dict_value->SetString(
kTargetWebSocketDebuggerUrlField,
base::StringPrintf("ws://127.0.0.1:%u%s%s",
static_cast<unsigned>(remote_debugging_port_),
kPageUrlPrefix, iter.value()->id().c_str()));
list_value.Append(dict_value.Pass());
}
return MakeJsonResponse(200, &list_value, std::string());
}
return MakeJsonResponse(404, nullptr, "Unknown command: " + command);
}
} // namespace devtools_service } // namespace devtools_service
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "mojo/services/network/public/interfaces/http_connection.mojom.h" #include "mojo/services/network/public/interfaces/http_connection.mojom.h"
#include "mojo/services/network/public/interfaces/http_message.mojom.h"
#include "mojo/services/network/public/interfaces/http_server.mojom.h" #include "mojo/services/network/public/interfaces/http_server.mojom.h"
namespace devtools_service { namespace devtools_service {
...@@ -30,25 +31,27 @@ class DevToolsHttpServer : public mojo::HttpServerDelegate { ...@@ -30,25 +31,27 @@ class DevToolsHttpServer : public mojo::HttpServerDelegate {
mojo::HttpConnectionPtr connection, mojo::HttpConnectionPtr connection,
mojo::InterfaceRequest<mojo::HttpConnectionDelegate> delegate) override; mojo::InterfaceRequest<mojo::HttpConnectionDelegate> delegate) override;
// The following methods are called by HttpConnectionDelegateImpl. // The following three methods are called by HttpConnectionDelegateImpl.
using OnReceivedRequestCallback = using OnReceivedRequestCallback =
mojo::HttpConnectionDelegate::OnReceivedRequestCallback; mojo::HttpConnectionDelegate::OnReceivedRequestCallback;
void OnReceivedRequest(HttpConnectionDelegateImpl* connection, void OnReceivedRequest(HttpConnectionDelegateImpl* connection,
mojo::HttpRequestPtr request, mojo::HttpRequestPtr request,
const OnReceivedRequestCallback& callback); const OnReceivedRequestCallback& callback);
using OnReceivedWebSocketRequestCallback = using OnReceivedWebSocketRequestCallback =
mojo::HttpConnectionDelegate::OnReceivedWebSocketRequestCallback; mojo::HttpConnectionDelegate::OnReceivedWebSocketRequestCallback;
void OnReceivedWebSocketRequest( void OnReceivedWebSocketRequest(
HttpConnectionDelegateImpl* connection, HttpConnectionDelegateImpl* connection,
mojo::HttpRequestPtr request, mojo::HttpRequestPtr request,
const OnReceivedWebSocketRequestCallback& callback); const OnReceivedWebSocketRequestCallback& callback);
void OnConnectionClosed(HttpConnectionDelegateImpl* connection); void OnConnectionClosed(HttpConnectionDelegateImpl* connection);
mojo::HttpResponsePtr ProcessJsonRequest(mojo::HttpRequestPtr request);
// Not owned by this object. // Not owned by this object.
DevToolsService* const service_; DevToolsService* const service_;
const uint16_t remote_debugging_port_;
scoped_ptr<mojo::Binding<mojo::HttpServerDelegate>> scoped_ptr<mojo::Binding<mojo::HttpServerDelegate>>
http_server_delegate_binding_; http_server_delegate_binding_;
......
...@@ -5,9 +5,17 @@ ...@@ -5,9 +5,17 @@
#include "components/devtools_service/devtools_registry_impl.h" #include "components/devtools_service/devtools_registry_impl.h"
#include "base/logging.h" #include "base/logging.h"
#include "components/devtools_service/devtools_agent_host.h"
namespace devtools_service { namespace devtools_service {
DevToolsRegistryImpl::Iterator::Iterator(DevToolsRegistryImpl* registry)
: registry_(registry), iter_(registry->agents_.begin()) {
}
DevToolsRegistryImpl::Iterator::~Iterator() {
}
DevToolsRegistryImpl::DevToolsRegistryImpl(DevToolsService* service) DevToolsRegistryImpl::DevToolsRegistryImpl(DevToolsService* service)
: service_(service) { : service_(service) {
} }
...@@ -20,9 +28,26 @@ void DevToolsRegistryImpl::BindToRegistryRequest( ...@@ -20,9 +28,26 @@ void DevToolsRegistryImpl::BindToRegistryRequest(
bindings_.AddBinding(this, request.Pass()); bindings_.AddBinding(this, request.Pass());
} }
DevToolsAgentHost* DevToolsRegistryImpl::GetAgentById(const std::string& id) {
auto iter = agents_.find(id);
if (iter == agents_.end())
return nullptr;
return iter->second.get();
}
void DevToolsRegistryImpl::RegisterAgent(DevToolsAgentPtr agent) { void DevToolsRegistryImpl::RegisterAgent(DevToolsAgentPtr agent) {
// TODO(yzshen): Implement it. linked_ptr<DevToolsAgentHost> agent_host(new DevToolsAgentHost(agent.Pass()));
NOTIMPLEMENTED(); std::string id = agent_host->id();
agent_host->set_agent_connection_error_handler(
[this, id]() { OnAgentConnectionError(id); });
agents_[agent_host->id()] = agent_host;
}
void DevToolsRegistryImpl::OnAgentConnectionError(const std::string& id) {
DCHECK(agents_.find(id) != agents_.end());
agents_.erase(id);
} }
} // namespace devtools_service } // namespace devtools_service
...@@ -5,30 +5,57 @@ ...@@ -5,30 +5,57 @@
#ifndef COMPONENTS_DEVTOOLS_SERVICE_DEVTOOLS_REGISTRY_IMPL_H_ #ifndef COMPONENTS_DEVTOOLS_SERVICE_DEVTOOLS_REGISTRY_IMPL_H_
#define COMPONENTS_DEVTOOLS_SERVICE_DEVTOOLS_REGISTRY_IMPL_H_ #define COMPONENTS_DEVTOOLS_SERVICE_DEVTOOLS_REGISTRY_IMPL_H_
#include <map>
#include <string>
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/linked_ptr.h"
#include "components/devtools_service/public/interfaces/devtools_service.mojom.h" #include "components/devtools_service/public/interfaces/devtools_service.mojom.h"
#include "mojo/common/weak_binding_set.h" #include "mojo/common/weak_binding_set.h"
namespace devtools_service { namespace devtools_service {
class DevToolsAgentHost;
class DevToolsService; class DevToolsService;
class DevToolsRegistryImpl : public DevToolsRegistry { class DevToolsRegistryImpl : public DevToolsRegistry {
public: public:
class Iterator {
public:
// |registry| must outlive this object.
explicit Iterator(DevToolsRegistryImpl* registry);
~Iterator();
bool IsAtEnd() const { return iter_ == registry_->agents_.end(); }
void Advance() { ++iter_; }
DevToolsAgentHost* value() { return iter_->second.get(); }
private:
DevToolsRegistryImpl* const registry_;
std::map<std::string, linked_ptr<DevToolsAgentHost>>::const_iterator iter_;
};
// |service| must outlive this object. // |service| must outlive this object.
explicit DevToolsRegistryImpl(DevToolsService* service); explicit DevToolsRegistryImpl(DevToolsService* service);
~DevToolsRegistryImpl() override; ~DevToolsRegistryImpl() override;
void BindToRegistryRequest(mojo::InterfaceRequest<DevToolsRegistry> request); void BindToRegistryRequest(mojo::InterfaceRequest<DevToolsRegistry> request);
DevToolsAgentHost* GetAgentById(const std::string& id);
private: private:
// DevToolsRegistry implementation. // DevToolsRegistry implementation.
void RegisterAgent(DevToolsAgentPtr agent) override; void RegisterAgent(DevToolsAgentPtr agent) override;
void OnAgentConnectionError(const std::string& id);
DevToolsService* const service_; DevToolsService* const service_;
mojo::WeakBindingSet<DevToolsRegistry> bindings_; mojo::WeakBindingSet<DevToolsRegistry> bindings_;
std::map<std::string, linked_ptr<DevToolsAgentHost>> agents_;
DISALLOW_COPY_AND_ASSIGN(DevToolsRegistryImpl); DISALLOW_COPY_AND_ASSIGN(DevToolsRegistryImpl);
}; };
......
...@@ -30,7 +30,7 @@ interface DevToolsAgent { ...@@ -30,7 +30,7 @@ interface DevToolsAgent {
// DispatchProtocolMessage() calls. If a client doesn't want to receive // DispatchProtocolMessage() calls. If a client doesn't want to receive
// messages anymore, it could simply close the underlying message pipe of // messages anymore, it could simply close the underlying message pipe of
// |client|. // |client|.
SetClient(DevToolsAgentClient client); SetClient(DevToolsAgentClient client, string client_id);
// Sends a command (in remote debugging protocol JSON format) to the agent. // Sends a command (in remote debugging protocol JSON format) to the agent.
DispatchProtocolMessage(string message); DispatchProtocolMessage(string message);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment