#include "base/bind.h"
#include "base/compiler_specific.h"
-#include "base/file_util.h"
+#include "base/files/file_util.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/threading/thread.h"
#include "base/values.h"
#include "content/browser/devtools/devtools_browser_target.h"
+#include "content/browser/devtools/devtools_manager.h"
#include "content/browser/devtools/devtools_protocol.h"
#include "content/browser/devtools/devtools_protocol_constants.h"
#include "content/browser/devtools/devtools_system_info_handler.h"
#include "content/common/devtools_messages.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_http_handler_delegate.h"
-#include "content/public/browser/devtools_manager.h"
#include "content/public/browser/devtools_target.h"
#include "content/public/common/content_client.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/user_agent.h"
-#include "content/public/common/user_agent.h"
#include "grit/devtools_resources_map.h"
#include "net/base/escape.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/server/http_server_request_info.h"
#include "net/server/http_server_response_info.h"
+#include "net/socket/server_socket.h"
#if defined(OS_ANDROID)
#include "base/android/build_info.h"
const char kTargetWebSocketDebuggerUrlField[] = "webSocketDebuggerUrl";
const char kTargetDevtoolsFrontendUrlField[] = "devtoolsFrontendUrl";
-// An internal implementation of DevToolsClientHost that delegates
-// messages sent for DevToolsClient to a DebuggerShell instance.
-class DevToolsClientHostImpl : public DevToolsClientHost {
+// Maximum write buffer size of devtools http/websocket connectinos.
+const int32 kSendBufferSizeForDevTools = 100 * 1024 * 1024; // 100Mb
+
+// An internal implementation of DevToolsAgentHostClient that delegates
+// messages sent to a DebuggerShell instance.
+class DevToolsAgentHostClientImpl : public DevToolsAgentHostClient {
public:
- DevToolsClientHostImpl(base::MessageLoop* message_loop,
- net::HttpServer* server,
- int connection_id)
+ DevToolsAgentHostClientImpl(base::MessageLoop* message_loop,
+ net::HttpServer* server,
+ int connection_id,
+ DevToolsAgentHost* agent_host)
: message_loop_(message_loop),
server_(server),
connection_id_(connection_id),
- is_closed_(false),
- detach_reason_("target_closed") {}
+ agent_host_(agent_host) {
+ agent_host_->AttachClient(this);
+ }
- virtual ~DevToolsClientHostImpl() {}
+ virtual ~DevToolsAgentHostClientImpl() {
+ if (agent_host_.get())
+ agent_host_->DetachClient();
+ }
- // DevToolsClientHost interface
- virtual void InspectedContentsClosing() OVERRIDE {
- if (is_closed_)
- return;
- is_closed_ = true;
+ virtual void AgentHostClosed(
+ DevToolsAgentHost* agent_host,
+ bool replaced_with_another_client) OVERRIDE {
+ DCHECK(agent_host == agent_host_.get());
+ agent_host_ = NULL;
base::DictionaryValue notification;
notification.SetString(
- devtools::Inspector::detached::kParamReason, detach_reason_);
+ devtools::Inspector::detached::kParamReason,
+ replaced_with_another_client ?
+ "replaced_with_devtools" : "target_closed");
std::string response = DevToolsProtocol::CreateNotification(
devtools::Inspector::detached::kName,
notification.DeepCopy())->Serialize();
message_loop_->PostTask(
FROM_HERE,
base::Bind(&net::HttpServer::SendOverWebSocket,
- server_,
+ base::Unretained(server_),
connection_id_,
response));
message_loop_->PostTask(
FROM_HERE,
- base::Bind(&net::HttpServer::Close, server_, connection_id_));
+ base::Bind(&net::HttpServer::Close,
+ base::Unretained(server_),
+ connection_id_));
}
- virtual void DispatchOnInspectorFrontend(const std::string& data) OVERRIDE {
+ virtual void DispatchProtocolMessage(
+ DevToolsAgentHost* agent_host, const std::string& message) OVERRIDE {
+ DCHECK(agent_host == agent_host_.get());
message_loop_->PostTask(
FROM_HERE,
base::Bind(&net::HttpServer::SendOverWebSocket,
- server_,
+ base::Unretained(server_),
connection_id_,
- data));
+ message));
}
- virtual void ReplacedWithAnotherClient() OVERRIDE {
- detach_reason_ = "replaced_with_devtools";
+ void OnMessage(const std::string& message) {
+ if (agent_host_.get())
+ agent_host_->DispatchProtocolMessage(message);
}
private:
- base::MessageLoop* message_loop_;
- net::HttpServer* server_;
- int connection_id_;
- bool is_closed_;
- std::string detach_reason_;
+ base::MessageLoop* const message_loop_;
+ net::HttpServer* const server_;
+ const int connection_id_;
+ scoped_refptr<DevToolsAgentHost> agent_host_;
};
static bool TimeComparator(const DevToolsTarget* target1,
// static
DevToolsHttpHandler* DevToolsHttpHandler::Start(
- const net::StreamListenSocketFactory* socket_factory,
+ scoped_ptr<ServerSocketFactory> server_socket_factory,
const std::string& frontend_url,
DevToolsHttpHandlerDelegate* delegate,
const base::FilePath& active_port_output_directory) {
DevToolsHttpHandlerImpl* http_handler =
- new DevToolsHttpHandlerImpl(socket_factory,
+ new DevToolsHttpHandlerImpl(server_socket_factory.Pass(),
frontend_url,
delegate,
active_port_output_directory);
return http_handler;
}
+DevToolsHttpHandler::ServerSocketFactory::ServerSocketFactory(
+ const std::string& address,
+ int port,
+ int backlog)
+ : address_(address),
+ port_(port),
+ backlog_(backlog) {
+}
+
+DevToolsHttpHandler::ServerSocketFactory::~ServerSocketFactory() {
+}
+
+scoped_ptr<net::ServerSocket>
+DevToolsHttpHandler::ServerSocketFactory::CreateAndListen() const {
+ scoped_ptr<net::ServerSocket> socket = Create();
+ if (socket &&
+ socket->ListenWithAddressAndPort(address_, port_, backlog_) == net::OK) {
+ return socket.Pass();
+ }
+ return scoped_ptr<net::ServerSocket>();
+}
+
DevToolsHttpHandlerImpl::~DevToolsHttpHandlerImpl() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Stop() must be called prior to destruction.
base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease, this));
}
+void DevToolsHttpHandlerImpl::StopWithoutRelease() {
+ if (!thread_)
+ return;
+ BrowserThread::PostTaskAndReply(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&DevToolsHttpHandlerImpl::StopHandlerThread, this),
+ base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThread, this));
+}
+
GURL DevToolsHttpHandlerImpl::GetFrontendURL() {
net::IPEndPoint ip_address;
- if (server_->GetLocalAddress(&ip_address))
+ if (server_ && server_->GetLocalAddress(&ip_address))
return GURL();
return GURL(std::string("http://") + ip_address.ToString() + frontend_url_);
}
void DevToolsHttpHandlerImpl::OnHttpRequest(
int connection_id,
const net::HttpServerRequestInfo& info) {
+ server_->SetSendBufferSize(connection_id, kSendBufferSizeForDevTools);
+
if (info.path.find("/json") == 0) {
BrowserThread::PostTask(
BrowserThread::UI,
new DevToolsTracingHandler(DevToolsTracingHandler::Browser),
true /* handle on UI thread */);
browser_target->RegisterDomainHandler(
- TetheringHandler::kDomain,
+ devtools::Tethering::kName,
new TetheringHandler(delegate_.get()),
false /* handle on this thread */);
browser_target->RegisterDomainHandler(
true /* handle on UI thread */);
browser_targets_[connection_id] = browser_target;
+ server_->SetSendBufferSize(connection_id, kSendBufferSizeForDevTools);
server_->AcceptWebSocket(connection_id, request);
return;
}
if (command == "list") {
std::string host = info.headers["host"];
AddRef(); // Balanced in OnTargetListReceived.
- delegate_->EnumerateTargets(
- base::Bind(&DevToolsHttpHandlerImpl::OnTargetListReceived,
- this, connection_id, host));
+ DevToolsManagerDelegate* manager_delegate =
+ DevToolsManager::GetInstance()->delegate();
+ if (manager_delegate) {
+ manager_delegate->EnumerateTargets(
+ base::Bind(&DevToolsHttpHandlerImpl::OnTargetListReceived,
+ this, connection_id, host));
+ } else {
+ DevToolsManagerDelegate::TargetList empty_list;
+ OnTargetListReceived(connection_id, host, empty_list);
+ }
return;
}
query, net::UnescapeRule::URL_SPECIAL_CHARS));
if (!url.is_valid())
url = GURL(url::kAboutBlankURL);
- scoped_ptr<DevToolsTarget> target(delegate_->CreateNewTarget(url));
+ scoped_ptr<DevToolsTarget> target;
+ DevToolsManagerDelegate* manager_delegate =
+ DevToolsManager::GetInstance()->delegate();
+ if (manager_delegate)
+ target = manager_delegate->CreateNewTarget(url);
if (!target) {
SendJson(connection_id,
net::HTTP_INTERNAL_SERVER_ERROR,
void DevToolsHttpHandlerImpl::OnTargetListReceived(
int connection_id,
const std::string& host,
- const DevToolsHttpHandlerDelegate::TargetList& targets) {
- DevToolsHttpHandlerDelegate::TargetList sorted_targets = targets;
+ const DevToolsManagerDelegate::TargetList& targets) {
+ DevToolsManagerDelegate::TargetList sorted_targets = targets;
std::sort(sorted_targets.begin(), sorted_targets.end(), TimeComparator);
STLDeleteValues(&target_map_);
base::ListValue list_value;
- for (DevToolsHttpHandlerDelegate::TargetList::const_iterator it =
+ for (DevToolsManagerDelegate::TargetList::const_iterator it =
sorted_targets.begin(); it != sorted_targets.end(); ++it) {
DevToolsTarget* target = *it;
target_map_[target->GetId()] = target;
void DevToolsHttpHandlerImpl::OnThumbnailRequestUI(
int connection_id, const GURL& page_url) {
- std::string data = delegate_->GetPageThumbnailData(page_url);
+ DevToolsManagerDelegate* manager_delegate =
+ DevToolsManager::GetInstance()->delegate();
+ std::string data =
+ manager_delegate ? manager_delegate->GetPageThumbnailData(page_url) : "";
if (!data.empty())
Send200(connection_id, data, "image/png");
else
DevToolsTarget* target = GetTarget(page_id);
scoped_refptr<DevToolsAgentHost> agent =
target ? target->GetAgentHost() : NULL;
- if (!agent) {
+ if (!agent.get()) {
Send500(connection_id, "No such target id: " + page_id);
return;
}
return;
}
- DevToolsClientHostImpl* client_host = new DevToolsClientHostImpl(
- thread_->message_loop(), server_.get(), connection_id);
- connection_to_client_host_ui_[connection_id] = client_host;
-
- DevToolsManager::GetInstance()->
- RegisterDevToolsClientHostFor(agent, client_host);
+ DevToolsAgentHostClientImpl* client_host = new DevToolsAgentHostClientImpl(
+ thread_->message_loop(), server_.get(), connection_id, agent.get());
+ connection_to_client_ui_[connection_id] = client_host;
AcceptWebSocket(connection_id, request);
}
void DevToolsHttpHandlerImpl::OnWebSocketMessageUI(
int connection_id,
const std::string& data) {
- ConnectionToClientHostMap::iterator it =
- connection_to_client_host_ui_.find(connection_id);
- if (it == connection_to_client_host_ui_.end())
+ ConnectionToClientMap::iterator it =
+ connection_to_client_ui_.find(connection_id);
+ if (it == connection_to_client_ui_.end())
return;
- DevToolsManager* manager = DevToolsManager::GetInstance();
- manager->DispatchOnInspectorBackend(it->second, data);
+ DevToolsAgentHostClientImpl* client =
+ static_cast<DevToolsAgentHostClientImpl*>(it->second);
+ client->OnMessage(data);
}
void DevToolsHttpHandlerImpl::OnCloseUI(int connection_id) {
- ConnectionToClientHostMap::iterator it =
- connection_to_client_host_ui_.find(connection_id);
- if (it != connection_to_client_host_ui_.end()) {
- DevToolsClientHostImpl* client_host =
- static_cast<DevToolsClientHostImpl*>(it->second);
- DevToolsManager::GetInstance()->ClientHostClosing(client_host);
- delete client_host;
- connection_to_client_host_ui_.erase(connection_id);
+ ConnectionToClientMap::iterator it =
+ connection_to_client_ui_.find(connection_id);
+ if (it != connection_to_client_ui_.end()) {
+ DevToolsAgentHostClientImpl* client =
+ static_cast<DevToolsAgentHostClientImpl*>(it->second);
+ delete client;
+ connection_to_client_ui_.erase(connection_id);
}
}
DevToolsHttpHandlerImpl::DevToolsHttpHandlerImpl(
- const net::StreamListenSocketFactory* socket_factory,
+ scoped_ptr<ServerSocketFactory> server_socket_factory,
const std::string& frontend_url,
DevToolsHttpHandlerDelegate* delegate,
const base::FilePath& active_port_output_directory)
: frontend_url_(frontend_url),
- socket_factory_(socket_factory),
+ server_socket_factory_(server_socket_factory.Pass()),
delegate_(delegate),
active_port_output_directory_(active_port_output_directory) {
if (frontend_url_.empty())
- frontend_url_ = "/devtools/devtools.html";
+ frontend_url_ = "/devtools/devtools.html";
// Balanced in ResetHandlerThreadAndRelease().
AddRef();
// Runs on the handler thread
void DevToolsHttpHandlerImpl::Init() {
- server_ = new net::HttpServer(*socket_factory_.get(), this);
+ scoped_ptr<net::ServerSocket> server_socket =
+ server_socket_factory_->CreateAndListen();
+ if (!server_socket) {
+ LOG(ERROR) << "Cannot start http server for devtools. Stop devtools.";
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&DevToolsHttpHandlerImpl::StopWithoutRelease, this));
+ return;
+ }
+
+ server_.reset(new net::HttpServer(server_socket.Pass(), this));
if (!active_port_output_directory_.empty())
WriteActivePortToUserProfile();
}
// Runs on the handler thread
void DevToolsHttpHandlerImpl::Teardown() {
- server_ = NULL;
+ server_.reset(NULL);
}
// Runs on FILE thread to make sure that it is serialized against
thread_->message_loop()->PostTask(
FROM_HERE,
base::Bind(&net::HttpServer::SendResponse,
- server_.get(),
+ base::Unretained(server_.get()),
connection_id,
response));
}
thread_->message_loop()->PostTask(
FROM_HERE,
base::Bind(&net::HttpServer::Send200,
- server_.get(),
+ base::Unretained(server_.get()),
connection_id,
data,
mime_type));
return;
thread_->message_loop()->PostTask(
FROM_HERE,
- base::Bind(&net::HttpServer::Send404, server_.get(), connection_id));
+ base::Bind(&net::HttpServer::Send404,
+ base::Unretained(server_.get()),
+ connection_id));
}
void DevToolsHttpHandlerImpl::Send500(int connection_id,
return;
thread_->message_loop()->PostTask(
FROM_HERE,
- base::Bind(&net::HttpServer::Send500, server_.get(), connection_id,
+ base::Bind(&net::HttpServer::Send500,
+ base::Unretained(server_.get()),
+ connection_id,
message));
}
return;
thread_->message_loop()->PostTask(
FROM_HERE,
- base::Bind(&net::HttpServer::AcceptWebSocket, server_.get(),
- connection_id, request));
+ base::Bind(&net::HttpServer::SetSendBufferSize,
+ base::Unretained(server_.get()),
+ connection_id,
+ kSendBufferSizeForDevTools));
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&net::HttpServer::AcceptWebSocket,
+ base::Unretained(server_.get()),
+ connection_id,
+ request));
}
base::DictionaryValue* DevToolsHttpHandlerImpl::SerializeTarget(
if (favicon_url.is_valid())
dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());
- if (!delegate_->GetPageThumbnailData(url).empty()) {
+ DevToolsManagerDelegate* manager_delegate =
+ DevToolsManager::GetInstance()->delegate();
+ if (manager_delegate &&
+ !manager_delegate->GetPageThumbnailData(url).empty()) {
dictionary->SetString(kTargetThumbnailUrlField,
std::string(kThumbUrlPrefix) + id);
}