summaryrefslogtreecommitdiffstats
path: root/content/browser/devtools
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser/devtools')
-rw-r--r--content/browser/devtools/DEPS4
-rw-r--r--content/browser/devtools/OWNERS2
-rw-r--r--content/browser/devtools/devtools_agent_host.cc57
-rw-r--r--content/browser/devtools/devtools_agent_host.h59
-rw-r--r--content/browser/devtools/devtools_browser_target.cc118
-rw-r--r--content/browser/devtools/devtools_browser_target.h68
-rw-r--r--content/browser/devtools/devtools_frontend_host.cc125
-rw-r--r--content/browser/devtools/devtools_frontend_host.h59
-rw-r--r--content/browser/devtools/devtools_http_handler_impl.cc873
-rw-r--r--content/browser/devtools/devtools_http_handler_impl.h143
-rw-r--r--content/browser/devtools/devtools_http_handler_unittest.cc103
-rw-r--r--content/browser/devtools/devtools_manager_impl.cc302
-rw-r--r--content/browser/devtools/devtools_manager_impl.h131
-rw-r--r--content/browser/devtools/devtools_manager_unittest.cc243
-rw-r--r--content/browser/devtools/devtools_netlog_observer.cc329
-rw-r--r--content/browser/devtools/devtools_netlog_observer.h70
-rw-r--r--content/browser/devtools/devtools_resources.gyp58
-rw-r--r--content/browser/devtools/devtools_tracing_handler.cc103
-rw-r--r--content/browser/devtools/devtools_tracing_handler.h50
-rw-r--r--content/browser/devtools/render_view_devtools_agent_host.cc154
-rw-r--r--content/browser/devtools/render_view_devtools_agent_host.h51
-rw-r--r--content/browser/devtools/worker_devtools_manager.cc472
-rw-r--r--content/browser/devtools/worker_devtools_manager.h109
-rw-r--r--content/browser/devtools/worker_devtools_message_filter.cc50
-rw-r--r--content/browser/devtools/worker_devtools_message_filter.h35
25 files changed, 3768 insertions, 0 deletions
diff --git a/content/browser/devtools/DEPS b/content/browser/devtools/DEPS
new file mode 100644
index 0000000..3e98aee
--- /dev/null
+++ b/content/browser/devtools/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ # Generated by the local devtools_resources.gyp:devtools_resources
+ "+grit/devtools_resources_map.h",
+]
diff --git a/content/browser/devtools/OWNERS b/content/browser/devtools/OWNERS
new file mode 100644
index 0000000..bb6028e
--- /dev/null
+++ b/content/browser/devtools/OWNERS
@@ -0,0 +1,2 @@
+pfeldman@chromium.org
+yurys@chromium.org
diff --git a/content/browser/devtools/devtools_agent_host.cc b/content/browser/devtools/devtools_agent_host.cc
new file mode 100644
index 0000000..fa5bc0e
--- /dev/null
+++ b/content/browser/devtools/devtools_agent_host.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 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 "content/browser/devtools/devtools_agent_host.h"
+
+#include "base/basictypes.h"
+#include "content/common/devtools_messages.h"
+
+namespace content {
+
+DevToolsAgentHost::DevToolsAgentHost() : close_listener_(NULL) {
+}
+
+void DevToolsAgentHost::Attach() {
+ SendMessageToAgent(new DevToolsAgentMsg_Attach(MSG_ROUTING_NONE));
+ NotifyClientAttaching();
+}
+
+void DevToolsAgentHost::Reattach(const std::string& saved_agent_state) {
+ SendMessageToAgent(new DevToolsAgentMsg_Reattach(
+ MSG_ROUTING_NONE,
+ saved_agent_state));
+ NotifyClientAttaching();
+}
+
+void DevToolsAgentHost::Detach() {
+ SendMessageToAgent(new DevToolsAgentMsg_Detach(MSG_ROUTING_NONE));
+ NotifyClientDetaching();
+}
+
+void DevToolsAgentHost::DipatchOnInspectorBackend(const std::string& message) {
+ SendMessageToAgent(new DevToolsAgentMsg_DispatchOnInspectorBackend(
+ MSG_ROUTING_NONE, message));
+}
+
+void DevToolsAgentHost::InspectElement(int x, int y) {
+ SendMessageToAgent(new DevToolsAgentMsg_InspectElement(MSG_ROUTING_NONE,
+ x, y));
+}
+
+void DevToolsAgentHost::AddMessageToConsole(ConsoleMessageLevel level,
+ const std::string& message) {
+ SendMessageToAgent(new DevToolsAgentMsg_AddMessageToConsole(
+ MSG_ROUTING_NONE,
+ level,
+ message));
+}
+
+void DevToolsAgentHost::NotifyCloseListener() {
+ if (close_listener_) {
+ close_listener_->AgentHostClosing(this);
+ close_listener_ = NULL;
+ }
+}
+
+} // namespace content
diff --git a/content/browser/devtools/devtools_agent_host.h b/content/browser/devtools/devtools_agent_host.h
new file mode 100644
index 0000000..ae44862
--- /dev/null
+++ b/content/browser/devtools/devtools_agent_host.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_AGENT_HOST_H_
+#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_AGENT_HOST_H_
+
+#include <string>
+
+#include "content/common/content_export.h"
+#include "content/public/common/console_message_level.h"
+
+namespace IPC {
+class Message;
+}
+
+namespace content {
+
+// Describes interface for managing devtools agents from the browser process.
+class CONTENT_EXPORT DevToolsAgentHost {
+ public:
+ class CONTENT_EXPORT CloseListener {
+ public:
+ virtual void AgentHostClosing(DevToolsAgentHost*) = 0;
+ protected:
+ virtual ~CloseListener() {}
+ };
+
+ // Sends the message to the devtools agent hosted by this object.
+ void Attach();
+ void Reattach(const std::string& saved_agent_state);
+ void Detach();
+ void DipatchOnInspectorBackend(const std::string& message);
+ void InspectElement(int x, int y);
+ void AddMessageToConsole(ConsoleMessageLevel level,
+ const std::string& message);
+
+ virtual int GetRenderProcessId() = 0;
+
+ void set_close_listener(CloseListener* listener) {
+ close_listener_ = listener;
+ }
+
+ protected:
+ DevToolsAgentHost();
+ virtual ~DevToolsAgentHost() {}
+
+ virtual void SendMessageToAgent(IPC::Message* msg) = 0;
+ virtual void NotifyClientAttaching() = 0;
+ virtual void NotifyClientDetaching() = 0;
+
+ void NotifyCloseListener();
+
+ CloseListener* close_listener_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_AGENT_HOST_H_
diff --git a/content/browser/devtools/devtools_browser_target.cc b/content/browser/devtools/devtools_browser_target.cc
new file mode 100644
index 0000000..44291b4
--- /dev/null
+++ b/content/browser/devtools/devtools_browser_target.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 2012 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 "content/browser/devtools/devtools_browser_target.h"
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+
+
+namespace content {
+
+DevToolsBrowserTarget::DevToolsBrowserTarget(int connection_id)
+ : connection_id_(connection_id) {
+}
+
+DevToolsBrowserTarget::~DevToolsBrowserTarget() {
+ for (HandlersMap::iterator i = handlers_.begin(); i != handlers_.end(); ++i)
+ delete i->second;
+}
+
+void DevToolsBrowserTarget::RegisterHandler(Handler* handler) {
+ std::string domain = handler->Domain();
+ DCHECK(handlers_.find(domain) == handlers_.end());
+ handlers_[domain] = handler;
+}
+
+std::string DevToolsBrowserTarget::HandleMessage(const std::string& data) {
+ int error_code;
+ std::string error_message;
+ scoped_ptr<base::Value> command(
+ base::JSONReader::ReadAndReturnError(
+ data, 0, &error_code, &error_message));
+
+ if (!command || !command->IsType(base::Value::TYPE_DICTIONARY))
+ return SerializeErrorResponse(
+ -1, CreateErrorObject(error_code, error_message));
+
+ int request_id;
+ std::string domain;
+ std::string method;
+ base::DictionaryValue* command_dict = NULL;
+ bool ok = true;
+ ok &= command->GetAsDictionary(&command_dict);
+ ok &= command_dict->GetInteger("id", &request_id);
+ ok &= command_dict->GetString("method", &method);
+ if (!ok)
+ return SerializeErrorResponse(
+ request_id, CreateErrorObject(-1, "Malformed request"));
+
+ base::DictionaryValue* params = NULL;
+ command_dict->GetDictionary("params", &params);
+
+ size_t pos = method.find(".");
+ if (pos == std::string::npos)
+ return SerializeErrorResponse(
+ request_id, CreateErrorObject(-1, "Method unsupported"));
+
+ domain = method.substr(0, pos);
+ if (domain.empty() || handlers_.find(domain) == handlers_.end())
+ return SerializeErrorResponse(
+ request_id, CreateErrorObject(-1, "Domain unsupported"));
+
+ base::Value* error_object = NULL;
+ base::Value* domain_result = handlers_[domain]->OnProtocolCommand(
+ method, params, &error_object);
+
+ if (error_object)
+ return SerializeErrorResponse(request_id, error_object);
+
+ if (!domain_result)
+ return SerializeErrorResponse(
+ request_id, CreateErrorObject(-1, "Invalid call"));
+
+ DictionaryValue* response = new DictionaryValue();
+ response->Set("result", domain_result);
+ return SerializeResponse(request_id, response);
+}
+
+std::string DevToolsBrowserTarget::SerializeErrorResponse(
+ int request_id, base::Value* error_object) {
+ scoped_ptr<base::DictionaryValue> error_response(new base::DictionaryValue());
+ error_response->SetInteger("id", request_id);
+ error_response->Set("error", error_object);
+ // Serialize response.
+ std::string json_response;
+ base::JSONWriter::WriteWithOptions(error_response.get(),
+ base::JSONWriter::OPTIONS_PRETTY_PRINT,
+ &json_response);
+ return json_response;
+}
+
+base::Value* DevToolsBrowserTarget::CreateErrorObject(
+ int error_code, const std::string& message) {
+ base::DictionaryValue* error_object = new base::DictionaryValue();
+ error_object->SetInteger("code", error_code);
+ error_object->SetString("message", message);
+ return error_object;
+}
+
+std::string DevToolsBrowserTarget::SerializeResponse(
+ int request_id, base::Value* response) {
+ scoped_ptr<base::DictionaryValue> ret(new base::DictionaryValue());
+ ret->SetInteger("id", request_id);
+ ret->Set("response", response);
+
+ // Serialize response.
+ std::string json_response;
+ base::JSONWriter::WriteWithOptions(ret.get(),
+ base::JSONWriter::OPTIONS_PRETTY_PRINT,
+ &json_response);
+ return json_response;
+}
+
+} // namespace content
diff --git a/content/browser/devtools/devtools_browser_target.h b/content/browser/devtools/devtools_browser_target.h
new file mode 100644
index 0000000..a0a34e3
--- /dev/null
+++ b/content/browser/devtools/devtools_browser_target.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_BROWSER_TARGET_H_
+#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_BROWSER_TARGET_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace base {
+
+class DictionaryValue;
+class Value;
+
+} // namespace base
+
+namespace content {
+
+// This class handles the "Browser" target for remote debugging.
+class DevToolsBrowserTarget {
+ public:
+ class Handler {
+ public:
+ virtual ~Handler() {}
+
+ // Returns the domain name for this handler.
+ virtual std::string Domain() = 0;
+
+ // |return_value| and |error_message_out| ownership is transferred to the
+ // caller.
+ virtual base::Value* OnProtocolCommand(
+ const std::string& method,
+ const base::DictionaryValue* params,
+ base::Value** error_message_out) = 0;
+ };
+
+ explicit DevToolsBrowserTarget(int connection_id);
+ ~DevToolsBrowserTarget();
+
+ int connection_id() const { return connection_id_; }
+
+ // Takes ownership of |handler|.
+ void RegisterHandler(Handler* handler);
+
+ std::string HandleMessage(const std::string& data);
+
+ private:
+ const int connection_id_;
+
+ typedef std::map<std::string, Handler*> HandlersMap;
+ HandlersMap handlers_;
+
+ // Takes ownership of |error_object|.
+ std::string SerializeErrorResponse(int request_id, base::Value* error_object);
+
+ base::Value* CreateErrorObject(int error_code, const std::string& message);
+
+ std::string SerializeResponse(int request_id, base::Value* response);
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsBrowserTarget);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_BROWSER_TARGET_H_
diff --git a/content/browser/devtools/devtools_frontend_host.cc b/content/browser/devtools/devtools_frontend_host.cc
new file mode 100644
index 0000000..fe7e82d
--- /dev/null
+++ b/content/browser/devtools/devtools_frontend_host.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2012 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 "content/browser/devtools/devtools_frontend_host.h"
+
+#include "content/browser/devtools/devtools_manager_impl.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/devtools_messages.h"
+#include "content/public/browser/devtools_client_host.h"
+#include "content/public/browser/devtools_frontend_host_delegate.h"
+
+namespace content {
+
+// static
+DevToolsClientHost* DevToolsClientHost::CreateDevToolsFrontendHost(
+ WebContents* client_web_contents,
+ DevToolsFrontendHostDelegate* delegate) {
+ return new DevToolsFrontendHost(
+ static_cast<WebContentsImpl*>(client_web_contents), delegate);
+}
+
+// static
+void DevToolsClientHost::SetupDevToolsFrontendClient(
+ RenderViewHost* frontend_rvh) {
+ frontend_rvh->Send(new DevToolsMsg_SetupDevToolsClient(
+ frontend_rvh->GetRoutingID()));
+}
+
+DevToolsFrontendHost::DevToolsFrontendHost(
+ WebContentsImpl* web_contents,
+ DevToolsFrontendHostDelegate* delegate)
+ : RenderViewHostObserver(web_contents->GetRenderViewHost()),
+ web_contents_(web_contents),
+ delegate_(delegate) {
+}
+
+DevToolsFrontendHost::~DevToolsFrontendHost() {
+ DevToolsManager::GetInstance()->ClientHostClosing(this);
+}
+
+void DevToolsFrontendHost::DispatchOnInspectorFrontend(
+ const std::string& message) {
+ RenderViewHostImpl* target_host =
+ static_cast<RenderViewHostImpl*>(web_contents_->GetRenderViewHost());
+ target_host->Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(
+ target_host->GetRoutingID(),
+ message));
+}
+
+void DevToolsFrontendHost::InspectedContentsClosing() {
+ delegate_->InspectedContentsClosing();
+}
+
+void DevToolsFrontendHost::FrameNavigating(const std::string& url) {
+ delegate_->FrameNavigating(url);
+}
+
+void DevToolsFrontendHost::ContentsReplaced(WebContents* new_contents) {
+ delegate_->ContentsReplaced(new_contents);
+}
+
+void DevToolsFrontendHost::ReplacedWithAnotherClient() {
+}
+
+bool DevToolsFrontendHost::OnMessageReceived(
+ const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(DevToolsFrontendHost, message)
+ IPC_MESSAGE_HANDLER(DevToolsAgentMsg_DispatchOnInspectorBackend,
+ OnDispatchOnInspectorBackend)
+ IPC_MESSAGE_HANDLER(DevToolsHostMsg_ActivateWindow, OnActivateWindow)
+ IPC_MESSAGE_HANDLER(DevToolsHostMsg_CloseWindow, OnCloseWindow)
+ IPC_MESSAGE_HANDLER(DevToolsHostMsg_MoveWindow, OnMoveWindow)
+ IPC_MESSAGE_HANDLER(DevToolsHostMsg_RequestSetDockSide,
+ OnRequestSetDockSide)
+ IPC_MESSAGE_HANDLER(DevToolsHostMsg_OpenInNewTab, OnOpenInNewTab)
+ IPC_MESSAGE_HANDLER(DevToolsHostMsg_Save, OnSave)
+ IPC_MESSAGE_HANDLER(DevToolsHostMsg_Append, OnAppend)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void DevToolsFrontendHost::OnDispatchOnInspectorBackend(
+ const std::string& message) {
+ DevToolsManagerImpl::GetInstance()->DispatchOnInspectorBackend(this, message);
+// delegate_->DispatchOnInspectorBackend(message);
+}
+
+void DevToolsFrontendHost::OnActivateWindow() {
+ delegate_->ActivateWindow();
+}
+
+void DevToolsFrontendHost::OnCloseWindow() {
+ delegate_->CloseWindow();
+}
+
+void DevToolsFrontendHost::OnMoveWindow(int x, int y) {
+ delegate_->MoveWindow(x, y);
+}
+
+void DevToolsFrontendHost::OnOpenInNewTab(const std::string& url) {
+ delegate_->OpenInNewTab(url);
+}
+
+void DevToolsFrontendHost::OnSave(
+ const std::string& url,
+ const std::string& content,
+ bool save_as) {
+ delegate_->SaveToFile(url, content, save_as);
+}
+
+void DevToolsFrontendHost::OnAppend(
+ const std::string& url,
+ const std::string& content) {
+ delegate_->AppendToFile(url, content);
+}
+
+void DevToolsFrontendHost::OnRequestSetDockSide(const std::string& side) {
+ delegate_->SetDockSide(side);
+}
+
+} // namespace content
diff --git a/content/browser/devtools/devtools_frontend_host.h b/content/browser/devtools/devtools_frontend_host.h
new file mode 100644
index 0000000..6168692
--- /dev/null
+++ b/content/browser/devtools/devtools_frontend_host.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_FRONTEND_HOST_H_
+#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_FRONTEND_HOST_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "content/public/browser/devtools_client_host.h"
+#include "content/public/browser/render_view_host_observer.h"
+
+namespace content {
+
+class DevToolsFrontendHostDelegate;
+class WebContentsImpl;
+
+// This class handles messages from DevToolsClient and calls corresponding
+// methods on DevToolsFrontendHostDelegate which is implemented by the
+// embedder. This allows us to avoid exposing DevTools client messages through
+// the content public API.
+class DevToolsFrontendHost : public DevToolsClientHost,
+ public RenderViewHostObserver {
+ public:
+ DevToolsFrontendHost(WebContentsImpl* web_contents,
+ DevToolsFrontendHostDelegate* delegate);
+
+ private:
+ virtual ~DevToolsFrontendHost();
+
+ // DevToolsClientHost implementation.
+ virtual void DispatchOnInspectorFrontend(const std::string& message) OVERRIDE;
+ virtual void InspectedContentsClosing() OVERRIDE;
+ virtual void FrameNavigating(const std::string& url) OVERRIDE;
+ virtual void ContentsReplaced(WebContents* new_contents) OVERRIDE;
+ virtual void ReplacedWithAnotherClient() OVERRIDE;
+
+ // RenderViewHostObserver overrides.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ void OnDispatchOnInspectorBackend(const std::string& message);
+ void OnActivateWindow();
+ void OnCloseWindow();
+ void OnMoveWindow(int x, int y);
+ void OnRequestSetDockSide(const std::string& side);
+ void OnOpenInNewTab(const std::string& url);
+ void OnSave(const std::string& url, const std::string& content, bool save_as);
+ void OnAppend(const std::string& url, const std::string& content);
+
+ WebContentsImpl* web_contents_;
+ DevToolsFrontendHostDelegate* delegate_;
+ DISALLOW_COPY_AND_ASSIGN(DevToolsFrontendHost);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_FRONTEND_HOST_H_
diff --git a/content/browser/devtools/devtools_http_handler_impl.cc b/content/browser/devtools/devtools_http_handler_impl.cc
new file mode 100644
index 0000000..6d214ab
--- /dev/null
+++ b/content/browser/devtools/devtools_http_handler_impl.cc
@@ -0,0 +1,873 @@
+// Copyright (c) 2012 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 "content/browser/devtools/devtools_http_handler_impl.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/file_util.h"
+#include "base/json/json_writer.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/message_loop_proxy.h"
+#include "base/string_number_conversions.h"
+#include "base/stringprintf.h"
+#include "base/threading/thread.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "content/browser/devtools/devtools_browser_target.h"
+#include "content/browser/devtools/devtools_tracing_handler.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/devtools_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/devtools_agent_host_registry.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/favicon_status.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/url_constants.h"
+#include "googleurl/src/gurl.h"
+#include "grit/devtools_resources_map.h"
+#include "net/base/escape.h"
+#include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
+#include "net/server/http_server_request_info.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDevToolsAgent.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
+#include "ui/base/layout.h"
+#include "webkit/user_agent/user_agent.h"
+#include "webkit/user_agent/user_agent_util.h"
+
+namespace content {
+
+const int kBufferSize = 16 * 1024;
+
+namespace {
+
+static const char* kDevToolsHandlerThreadName = "Chrome_DevToolsHandlerThread";
+
+class DevToolsDefaultBindingHandler
+ : public DevToolsHttpHandler::RenderViewHostBinding {
+ public:
+ DevToolsDefaultBindingHandler() {
+ }
+
+ virtual std::string GetIdentifier(RenderViewHost* rvh) OVERRIDE {
+ int process_id = rvh->GetProcess()->GetID();
+ int routing_id = rvh->GetRoutingID();
+ return base::StringPrintf("%d_%d", process_id, routing_id);
+ }
+
+ virtual RenderViewHost* ForIdentifier(
+ const std::string& identifier) OVERRIDE {
+ size_t pos = identifier.find("_");
+ if (pos == std::string::npos)
+ return NULL;
+
+ int process_id;
+ if (!base::StringToInt(identifier.substr(0, pos), &process_id))
+ return NULL;
+
+ int routing_id;
+ if (!base::StringToInt(identifier.substr(pos+1), &routing_id))
+ return NULL;
+
+ return RenderViewHost::FromID(process_id, routing_id);
+ }
+};
+
+
+// An internal implementation of DevToolsClientHost that delegates
+// messages sent for DevToolsClient to a DebuggerShell instance.
+class DevToolsClientHostImpl : public DevToolsClientHost {
+ public:
+ DevToolsClientHostImpl(
+ MessageLoop* message_loop,
+ net::HttpServer* server,
+ int connection_id)
+ : message_loop_(message_loop),
+ server_(server),
+ connection_id_(connection_id),
+ is_closed_(false),
+ detach_reason_("target_closed") {
+ }
+
+ ~DevToolsClientHostImpl() {}
+
+ // DevToolsClientHost interface
+ virtual void InspectedContentsClosing() {
+ if (is_closed_)
+ return;
+ is_closed_ = true;
+
+ std::string response =
+ WebKit::WebDevToolsAgent::inspectorDetachedEvent(
+ WebKit::WebString::fromUTF8(detach_reason_)).utf8();
+ message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&net::HttpServer::SendOverWebSocket,
+ server_,
+ connection_id_,
+ response));
+
+ message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&net::HttpServer::Close, server_, connection_id_));
+ }
+
+ virtual void DispatchOnInspectorFrontend(const std::string& data) {
+ message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&net::HttpServer::SendOverWebSocket,
+ server_,
+ connection_id_,
+ data));
+ }
+
+ virtual void ContentsReplaced(WebContents* new_contents) {
+ }
+
+ virtual void ReplacedWithAnotherClient() {
+ detach_reason_ = "replaced_with_devtools";
+ }
+
+ private:
+ virtual void FrameNavigating(const std::string& url) {}
+ MessageLoop* message_loop_;
+ net::HttpServer* server_;
+ int connection_id_;
+ bool is_closed_;
+ std::string detach_reason_;
+};
+
+} // namespace
+
+// static
+int DevToolsHttpHandler::GetFrontendResourceId(const std::string& name) {
+ for (size_t i = 0; i < kDevtoolsResourcesSize; ++i) {
+ if (name == kDevtoolsResources[i].name)
+ return kDevtoolsResources[i].value;
+ }
+ return -1;
+}
+
+// static
+DevToolsHttpHandler* DevToolsHttpHandler::Start(
+ const net::StreamListenSocketFactory* socket_factory,
+ const std::string& frontend_url,
+ DevToolsHttpHandlerDelegate* delegate) {
+ DevToolsHttpHandlerImpl* http_handler =
+ new DevToolsHttpHandlerImpl(socket_factory,
+ frontend_url,
+ delegate);
+ http_handler->Start();
+ return http_handler;
+}
+
+DevToolsHttpHandlerImpl::~DevToolsHttpHandlerImpl() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // Stop() must be called prior to destruction.
+ DCHECK(server_.get() == NULL);
+ DCHECK(thread_.get() == NULL);
+}
+
+void DevToolsHttpHandlerImpl::Start() {
+ if (thread_.get())
+ return;
+ thread_.reset(new base::Thread(kDevToolsHandlerThreadName));
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&DevToolsHttpHandlerImpl::StartHandlerThread, this));
+}
+
+// Runs on FILE thread.
+void DevToolsHttpHandlerImpl::StartHandlerThread() {
+ base::Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ if (!thread_->StartWithOptions(options)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThread, this));
+ return;
+ }
+
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&DevToolsHttpHandlerImpl::Init, this));
+}
+
+void DevToolsHttpHandlerImpl::ResetHandlerThread() {
+ thread_.reset();
+}
+
+void DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease() {
+ ResetHandlerThread();
+ Release();
+}
+
+void DevToolsHttpHandlerImpl::Stop() {
+ if (!thread_.get())
+ return;
+ BrowserThread::PostTaskAndReply(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&DevToolsHttpHandlerImpl::StopHandlerThread, this),
+ base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease, this));
+}
+
+void DevToolsHttpHandlerImpl::SetRenderViewHostBinding(
+ RenderViewHostBinding* binding) {
+ if (binding)
+ binding_ = binding;
+ else
+ binding_ = default_binding_.get();
+}
+
+GURL DevToolsHttpHandlerImpl::GetFrontendURL(RenderViewHost* render_view_host) {
+ net::IPEndPoint ip_address;
+ if (server_->GetLocalAddress(&ip_address))
+ return GURL();
+ std::string host = ip_address.ToString();
+ std::string id = binding_->GetIdentifier(render_view_host);
+ return GURL(std::string("http://") +
+ ip_address.ToString() +
+ GetFrontendURLInternal(id, host));
+}
+
+static std::string PathWithoutParams(const std::string& path) {
+ size_t query_position = path.find("?");
+ if (query_position != std::string::npos)
+ return path.substr(0, query_position);
+ return path;
+}
+
+static std::string GetMimeType(const std::string& filename) {
+ if (EndsWith(filename, ".html", false)) {
+ return "text/html";
+ } else if (EndsWith(filename, ".css", false)) {
+ return "text/css";
+ } else if (EndsWith(filename, ".js", false)) {
+ return "application/javascript";
+ } else if (EndsWith(filename, ".png", false)) {
+ return "image/png";
+ } else if (EndsWith(filename, ".gif", false)) {
+ return "image/gif";
+ }
+ NOTREACHED();
+ return "text/plain";
+}
+
+void DevToolsHttpHandlerImpl::Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ RenderProcessHost* process = Source<RenderProcessHost>(source).ptr();
+ DevToolsManager* manager = DevToolsManager::GetInstance();
+ for (ConnectionToClientHostMap::iterator it =
+ connection_to_client_host_ui_.begin();
+ it != connection_to_client_host_ui_.end(); ++it) {
+ DevToolsAgentHost* agent = manager->GetDevToolsAgentHostFor(it->second);
+ if (!agent)
+ continue;
+ RenderViewHost* rvh = DevToolsAgentHostRegistry::GetRenderViewHost(agent);
+ if (rvh && rvh->GetProcess() == process)
+ it->second->InspectedContentsClosing();
+ }
+}
+
+void DevToolsHttpHandlerImpl::OnHttpRequest(
+ int connection_id,
+ const net::HttpServerRequestInfo& info) {
+ if (info.path.find("/json") == 0) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&DevToolsHttpHandlerImpl::OnJsonRequestUI,
+ this,
+ connection_id,
+ info));
+ return;
+ }
+
+ if (info.path.find("/thumb/") == 0) {
+ // Thumbnail request.
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&DevToolsHttpHandlerImpl::OnThumbnailRequestUI,
+ this,
+ connection_id,
+ info));
+ return;
+ }
+
+ if (info.path == "" || info.path == "/") {
+ // Discovery page request.
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI,
+ this,
+ connection_id));
+ return;
+ }
+
+ if (info.path.find("/devtools/") != 0) {
+ server_->Send404(connection_id);
+ return;
+ }
+
+ std::string filename = PathWithoutParams(info.path.substr(10));
+ std::string mime_type = GetMimeType(filename);
+
+ FilePath frontend_dir = delegate_->GetDebugFrontendDir();
+ if (!frontend_dir.empty()) {
+ FilePath path = frontend_dir.AppendASCII(filename);
+ std::string data;
+ file_util::ReadFileToString(path, &data);
+ server_->Send200(connection_id, data, mime_type);
+ return;
+ }
+ if (delegate_->BundlesFrontendResources()) {
+ int resource_id = DevToolsHttpHandler::GetFrontendResourceId(filename);
+ if (resource_id != -1) {
+ base::StringPiece data = GetContentClient()->GetDataResource(
+ resource_id, ui::SCALE_FACTOR_NONE);
+ server_->Send200(connection_id, data.as_string(), mime_type);
+ return;
+ }
+ }
+ server_->Send404(connection_id);
+}
+
+void DevToolsHttpHandlerImpl::OnWebSocketRequest(
+ int connection_id,
+ const net::HttpServerRequestInfo& request) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(
+ &DevToolsHttpHandlerImpl::OnWebSocketRequestUI,
+ this,
+ connection_id,
+ request));
+}
+
+void DevToolsHttpHandlerImpl::OnWebSocketMessage(
+ int connection_id,
+ const std::string& data) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(
+ &DevToolsHttpHandlerImpl::OnWebSocketMessageUI,
+ this,
+ connection_id,
+ data));
+}
+
+void DevToolsHttpHandlerImpl::OnClose(int connection_id) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(
+ &DevToolsHttpHandlerImpl::OnCloseUI,
+ this,
+ connection_id));
+}
+
+struct DevToolsHttpHandlerImpl::PageInfo {
+ PageInfo()
+ : attached(false) {
+ }
+
+ std::string id;
+ std::string url;
+ bool attached;
+ std::string title;
+ std::string thumbnail_url;
+ std::string favicon_url;
+ base::TimeTicks last_selected_time;
+};
+
+// static
+bool DevToolsHttpHandlerImpl::SortPageListByTime(const PageInfo& info1,
+ const PageInfo& info2) {
+ return info1.last_selected_time > info2.last_selected_time;
+}
+
+DevToolsHttpHandlerImpl::PageList DevToolsHttpHandlerImpl::GeneratePageList() {
+ PageList page_list;
+ for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
+ !it.IsAtEnd(); it.Advance()) {
+ RenderProcessHost* render_process_host = it.GetCurrentValue();
+ DCHECK(render_process_host);
+
+ // Ignore processes that don't have a connection, such as crashed contents.
+ if (!render_process_host->HasConnection())
+ continue;
+
+ RenderProcessHost::RenderWidgetHostsIterator rwit(
+ render_process_host->GetRenderWidgetHostsIterator());
+ for (; !rwit.IsAtEnd(); rwit.Advance()) {
+ const RenderWidgetHost* widget = rwit.GetCurrentValue();
+ DCHECK(widget);
+ if (!widget || !widget->IsRenderView())
+ continue;
+
+ RenderViewHost* host =
+ RenderViewHost::From(const_cast<RenderWidgetHost*>(widget));
+ page_list.push_back(CreatePageInfo(host));
+ }
+ }
+ std::sort(page_list.begin(), page_list.end(), SortPageListByTime);
+ return page_list;
+}
+
+std::string DevToolsHttpHandlerImpl::GetFrontendURLInternal(
+ const std::string rvh_id,
+ const std::string& host) {
+ return base::StringPrintf(
+ "%s%sws=%s/devtools/page/%s",
+ overridden_frontend_url_.c_str(),
+ overridden_frontend_url_.find("?") == std::string::npos ? "?" : "&",
+ host.c_str(),
+ rvh_id.c_str());
+}
+
+static 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 = "list";
+ 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;
+}
+
+void DevToolsHttpHandlerImpl::OnJsonRequestUI(
+ int connection_id,
+ const net::HttpServerRequestInfo& info) {
+ // Trim /json and ?jsonp=...
+ std::string path = info.path.substr(5);
+ std::string jsonp;
+ size_t jsonp_pos = path.find("?jsonp=");
+ if (jsonp_pos != std::string::npos) {
+ jsonp = path.substr(jsonp_pos + 7);
+ path = path.substr(0, jsonp_pos);
+ }
+
+ // Trim fragment and query
+ size_t query_pos = path.find("?");
+ if (query_pos != std::string::npos)
+ path = path.substr(0, query_pos);
+
+ 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)) {
+ SendJson(connection_id,
+ net::HTTP_NOT_FOUND,
+ NULL,
+ "Malformed query: " + info.path,
+ jsonp);
+ return;
+ }
+
+ if (command == "version") {
+ DictionaryValue version;
+ version.SetString("Protocol-Version",
+ WebKit::WebDevToolsAgent::inspectorProtocolVersion());
+ version.SetString("WebKit-Version",
+ webkit_glue::GetWebKitVersion());
+ version.SetString("User-Agent",
+ webkit_glue::GetUserAgent(GURL(chrome::kAboutBlankURL)));
+ SendJson(connection_id, net::HTTP_OK, &version, "", jsonp);
+ return;
+ }
+
+ if (command == "list") {
+ PageList page_list = GeneratePageList();
+ ListValue json_pages_list;
+ std::string host = info.headers["Host"];
+ for (PageList::iterator i = page_list.begin(); i != page_list.end(); ++i)
+ json_pages_list.Append(SerializePageInfo(*i, host));
+ SendJson(connection_id, net::HTTP_OK, &json_pages_list, "", jsonp);
+ return;
+ }
+
+ if (command == "new") {
+ RenderViewHost* rvh = delegate_->CreateNewTarget();
+ if (!rvh) {
+ SendJson(connection_id,
+ net::HTTP_INTERNAL_SERVER_ERROR,
+ NULL,
+ "Could not create new page",
+ jsonp);
+ return;
+ }
+ PageInfo page_info = CreatePageInfo(rvh);
+ std::string host = info.headers["Host"];
+ scoped_ptr<DictionaryValue> dictionary(SerializePageInfo(page_info, host));
+ SendJson(connection_id, net::HTTP_OK, dictionary.get(), "", jsonp);
+ return;
+ }
+
+ if (command == "activate" || command == "close") {
+ RenderViewHost* rvh = binding_->ForIdentifier(target_id);
+ if (!rvh) {
+ SendJson(connection_id,
+ net::HTTP_NOT_FOUND,
+ NULL,
+ "No such target id: " + target_id,
+ jsonp);
+ return;
+ }
+
+ if (command == "activate") {
+ rvh->GetDelegate()->Activate();
+ SendJson(connection_id, net::HTTP_OK, NULL, "Target activated", jsonp);
+ return;
+ }
+
+ if (command == "close") {
+ rvh->ClosePage();
+ SendJson(connection_id, net::HTTP_OK, NULL, "Target is closing", jsonp);
+ return;
+ }
+ }
+ SendJson(connection_id,
+ net::HTTP_NOT_FOUND,
+ NULL,
+ "Unknown command: " + command,
+ jsonp);
+ return;
+}
+
+void DevToolsHttpHandlerImpl::OnThumbnailRequestUI(
+ int connection_id,
+ const net::HttpServerRequestInfo& info) {
+ std::string prefix = "/thumb/";
+ size_t pos = info.path.find(prefix);
+ if (pos != 0) {
+ Send404(connection_id);
+ return;
+ }
+
+ std::string page_url = info.path.substr(prefix.length());
+ std::string data = delegate_->GetPageThumbnailData(GURL(page_url));
+ if (!data.empty())
+ Send200(connection_id, data, "image/png");
+ else
+ Send404(connection_id);
+}
+
+void DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI(int connection_id) {
+ std::string response = delegate_->GetDiscoveryPageHTML();
+ Send200(connection_id, response, "text/html; charset=UTF-8");
+}
+
+void DevToolsHttpHandlerImpl::OnWebSocketRequestUI(
+ int connection_id,
+ const net::HttpServerRequestInfo& request) {
+ if (!thread_.get())
+ return;
+ std::string browser_prefix = "/devtools/browser";
+ size_t browser_pos = request.path.find(browser_prefix);
+ if (browser_pos == 0) {
+ if (browser_target_) {
+ Send500(connection_id, "Another client already attached");
+ return;
+ }
+ browser_target_.reset(new DevToolsBrowserTarget(connection_id));
+ browser_target_->RegisterHandler(new DevToolsTracingHandler());
+ AcceptWebSocket(connection_id, request);
+ return;
+ }
+
+ std::string page_prefix = "/devtools/page/";
+ size_t pos = request.path.find(page_prefix);
+ if (pos != 0) {
+ Send404(connection_id);
+ return;
+ }
+
+ std::string page_id = request.path.substr(page_prefix.length());
+ RenderViewHost* rvh = binding_->ForIdentifier(page_id);
+ if (!rvh) {
+ Send500(connection_id, "No such target id: " + page_id);
+ return;
+ }
+
+ DevToolsManager* manager = DevToolsManager::GetInstance();
+ DevToolsAgentHost* agent = DevToolsAgentHostRegistry::GetDevToolsAgentHost(
+ rvh);
+ if (manager->GetDevToolsClientHostFor(agent)) {
+ Send500(connection_id,
+ "Target with given id is being inspected: " + page_id);
+ return;
+ }
+
+ DevToolsClientHostImpl* client_host =
+ new DevToolsClientHostImpl(thread_->message_loop(),
+ server_,
+ connection_id);
+ connection_to_client_host_ui_[connection_id] = client_host;
+
+ manager->RegisterDevToolsClientHostFor(agent, client_host);
+
+ AcceptWebSocket(connection_id, request);
+}
+
+void DevToolsHttpHandlerImpl::OnWebSocketMessageUI(
+ int connection_id,
+ const std::string& data) {
+ if (browser_target_ && connection_id == browser_target_->connection_id()) {
+ std::string json_response = browser_target_->HandleMessage(data);
+
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&net::HttpServer::SendOverWebSocket,
+ server_.get(),
+ connection_id,
+ json_response));
+ return;
+ }
+
+ ConnectionToClientHostMap::iterator it =
+ connection_to_client_host_ui_.find(connection_id);
+ if (it == connection_to_client_host_ui_.end())
+ return;
+
+ DevToolsManager* manager = DevToolsManager::GetInstance();
+ manager->DispatchOnInspectorBackend(it->second, 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);
+ }
+ if (browser_target_ && browser_target_->connection_id() == connection_id) {
+ browser_target_.reset();
+ return;
+ }
+}
+
+DevToolsHttpHandlerImpl::DevToolsHttpHandlerImpl(
+ const net::StreamListenSocketFactory* socket_factory,
+ const std::string& frontend_url,
+ DevToolsHttpHandlerDelegate* delegate)
+ : overridden_frontend_url_(frontend_url),
+ socket_factory_(socket_factory),
+ delegate_(delegate) {
+ if (overridden_frontend_url_.empty())
+ overridden_frontend_url_ = "/devtools/devtools.html";
+
+ default_binding_.reset(new DevToolsDefaultBindingHandler);
+ binding_ = default_binding_.get();
+
+ registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
+ NotificationService::AllBrowserContextsAndSources());
+ registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CLOSED,
+ NotificationService::AllBrowserContextsAndSources());
+
+ // Balanced in ResetHandlerThreadAndRelease().
+ AddRef();
+}
+
+// Runs on the handler thread
+void DevToolsHttpHandlerImpl::Init() {
+ server_ = new net::HttpServer(*socket_factory_.get(), this);
+}
+
+// Runs on the handler thread
+void DevToolsHttpHandlerImpl::Teardown() {
+ server_ = NULL;
+}
+
+// Runs on FILE thread to make sure that it is serialized against
+// {Start|Stop}HandlerThread and to allow calling pthread_join.
+void DevToolsHttpHandlerImpl::StopHandlerThread() {
+ if (!thread_->message_loop())
+ return;
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&DevToolsHttpHandlerImpl::Teardown, this));
+ // Thread::Stop joins the thread.
+ thread_->Stop();
+}
+
+void DevToolsHttpHandlerImpl::SendJson(int connection_id,
+ net::HttpStatusCode status_code,
+ Value* value,
+ const std::string& message,
+ const std::string& jsonp) {
+ if (!thread_.get())
+ return;
+
+ // Serialize value and message.
+ std::string json_value;
+ if (value) {
+ base::JSONWriter::WriteWithOptions(value,
+ base::JSONWriter::OPTIONS_PRETTY_PRINT,
+ &json_value);
+ }
+ std::string json_message;
+ scoped_ptr<Value> message_object(Value::CreateStringValue(message));
+ base::JSONWriter::Write(message_object.get(), &json_message);
+
+ std::string response;
+ std::string mime_type = "application/json; charset=UTF-8";
+
+ // Wrap jsonp if necessary.
+ if (!jsonp.empty()) {
+ mime_type = "text/javascript; charset=UTF-8";
+ response = StringPrintf("%s(%s, %d, %s);",
+ jsonp.c_str(),
+ json_value.empty() ? "undefined"
+ : json_value.c_str(),
+ status_code,
+ json_message.c_str());
+ // JSONP always returns 200.
+ status_code = net::HTTP_OK;
+ } else {
+ response = StringPrintf("%s%s", json_value.c_str(), message.c_str());
+ }
+
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&net::HttpServer::Send,
+ server_.get(),
+ connection_id,
+ status_code,
+ response,
+ mime_type));
+}
+
+void DevToolsHttpHandlerImpl::Send200(int connection_id,
+ const std::string& data,
+ const std::string& mime_type) {
+ if (!thread_.get())
+ return;
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&net::HttpServer::Send200,
+ server_.get(),
+ connection_id,
+ data,
+ mime_type));
+}
+
+void DevToolsHttpHandlerImpl::Send404(int connection_id) {
+ if (!thread_.get())
+ return;
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&net::HttpServer::Send404, server_.get(), connection_id));
+}
+
+void DevToolsHttpHandlerImpl::Send500(int connection_id,
+ const std::string& message) {
+ if (!thread_.get())
+ return;
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&net::HttpServer::Send500, server_.get(), connection_id,
+ message));
+}
+
+void DevToolsHttpHandlerImpl::AcceptWebSocket(
+ int connection_id,
+ const net::HttpServerRequestInfo& request) {
+ if (!thread_.get())
+ return;
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&net::HttpServer::AcceptWebSocket, server_.get(),
+ connection_id, request));
+}
+
+DevToolsHttpHandlerImpl::PageInfo
+DevToolsHttpHandlerImpl::CreatePageInfo(RenderViewHost* rvh)
+{
+ RenderViewHostDelegate* host_delegate = rvh->GetDelegate();
+ DevToolsAgentHost* agent =
+ DevToolsAgentHostRegistry::GetDevToolsAgentHost(rvh);
+ DevToolsClientHost* client_host = DevToolsManager::GetInstance()->
+ GetDevToolsClientHostFor(agent);
+ PageInfo page_info;
+ page_info.id = binding_->GetIdentifier(rvh);
+ page_info.attached = client_host != NULL;
+ page_info.url = host_delegate->GetURL().spec();
+
+ WebContents* web_contents = host_delegate->GetAsWebContents();
+ if (web_contents) {
+ page_info.title = UTF16ToUTF8(
+ net::EscapeForHTML(web_contents->GetTitle()));
+ page_info.last_selected_time = web_contents->GetLastSelectedTime();
+
+ NavigationController& controller = web_contents->GetController();
+ NavigationEntry* entry = controller.GetActiveEntry();
+ if (entry != NULL && entry->GetURL().is_valid()) {
+ page_info.thumbnail_url = "/thumb/" + entry->GetURL().spec();
+ page_info.favicon_url = entry->GetFavicon().url.spec();
+ }
+ }
+ return page_info;
+}
+
+DictionaryValue* DevToolsHttpHandlerImpl::SerializePageInfo(
+ const PageInfo& page_info,
+ const std::string& host) {
+ DictionaryValue* dictionary = new DictionaryValue;
+ dictionary->SetString("title", page_info.title);
+ dictionary->SetString("url", page_info.url);
+ dictionary->SetString("thumbnailUrl", page_info.thumbnail_url);
+ dictionary->SetString("faviconUrl", page_info.favicon_url);
+ if (!page_info.attached) {
+ dictionary->SetString("webSocketDebuggerUrl",
+ base::StringPrintf("ws://%s/devtools/page/%s",
+ host.c_str(),
+ page_info.id.c_str()));
+ std::string devtools_frontend_url = GetFrontendURLInternal(
+ page_info.id.c_str(),
+ host);
+ dictionary->SetString("devtoolsFrontendUrl", devtools_frontend_url);
+ }
+ return dictionary;
+}
+
+} // namespace content
diff --git a/content/browser/devtools/devtools_http_handler_impl.h b/content/browser/devtools/devtools_http_handler_impl.h
new file mode 100644
index 0000000..9ee6f92
--- /dev/null
+++ b/content/browser/devtools/devtools_http_handler_impl.h
@@ -0,0 +1,143 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_HTTP_HANDLER_IMPL_H_
+#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_HTTP_HANDLER_IMPL_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/devtools_http_handler.h"
+#include "content/public/browser/devtools_http_handler_delegate.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "net/server/http_server.h"
+
+namespace base {
+class DictionaryValue;
+class Thread;
+class Value;
+}
+
+namespace net {
+class StreamListenSocketFactory;
+class URLRequestContextGetter;
+}
+
+namespace content {
+
+class DevToolsBrowserTarget;
+class DevToolsClientHost;
+class RenderViewHost;
+
+class DevToolsHttpHandlerImpl
+ : public DevToolsHttpHandler,
+ public NotificationObserver,
+ public base::RefCountedThreadSafe<DevToolsHttpHandlerImpl>,
+ public net::HttpServer::Delegate {
+ private:
+ struct PageInfo;
+ typedef std::vector<PageInfo> PageList;
+ friend class base::RefCountedThreadSafe<DevToolsHttpHandlerImpl>;
+ friend class DevToolsHttpHandler;
+
+ static bool SortPageListByTime(const PageInfo& info1, const PageInfo& info2);
+
+ // Takes ownership over |socket_factory|.
+ DevToolsHttpHandlerImpl(const net::StreamListenSocketFactory* socket_factory,
+ const std::string& frontend_url,
+ DevToolsHttpHandlerDelegate* delegate);
+ virtual ~DevToolsHttpHandlerImpl();
+ void Start();
+
+ // DevToolsHttpHandler implementation.
+ virtual void Stop() OVERRIDE;
+ virtual void SetRenderViewHostBinding(
+ RenderViewHostBinding* binding) OVERRIDE;
+ virtual GURL GetFrontendURL(RenderViewHost* render_view_host) OVERRIDE;
+
+ // NotificationObserver implementation.
+ virtual void Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) OVERRIDE;
+
+ // net::HttpServer::Delegate implementation.
+ virtual void OnHttpRequest(int connection_id,
+ const net::HttpServerRequestInfo& info) OVERRIDE;
+ virtual void OnWebSocketRequest(
+ int connection_id,
+ const net::HttpServerRequestInfo& info) OVERRIDE;
+ virtual void OnWebSocketMessage(int connection_id,
+ const std::string& data) OVERRIDE;
+ virtual void OnClose(int connection_id) OVERRIDE;
+
+ void OnJsonRequestUI(int connection_id,
+ const net::HttpServerRequestInfo& info);
+ void OnThumbnailRequestUI(int connection_id,
+ const net::HttpServerRequestInfo& info);
+ void OnDiscoveryPageRequestUI(int connection_id);
+
+ void OnWebSocketRequestUI(int connection_id,
+ const net::HttpServerRequestInfo& info);
+ void OnWebSocketMessageUI(int connection_id, const std::string& data);
+ void OnCloseUI(int connection_id);
+
+ void ResetHandlerThread();
+ void ResetHandlerThreadAndRelease();
+
+ void Init();
+ void Teardown();
+
+ void StartHandlerThread();
+ void StopHandlerThread();
+
+ void SendJson(int connection_id,
+ net::HttpStatusCode status_code,
+ base::Value* value,
+ const std::string& message,
+ const std::string& jsonp);
+ void Send200(int connection_id,
+ const std::string& data,
+ const std::string& mime_type);
+ void Send404(int connection_id);
+ void Send500(int connection_id,
+ const std::string& message);
+ void AcceptWebSocket(int connection_id,
+ const net::HttpServerRequestInfo& request);
+
+ PageList GeneratePageList();
+
+ // Returns the front end url without the host at the beginning.
+ std::string GetFrontendURLInternal(const std::string rvh_id,
+ const std::string& host);
+
+ PageInfo CreatePageInfo(RenderViewHost* rvh);
+
+ base::DictionaryValue* SerializePageInfo(const PageInfo& page_info,
+ const std::string& host);
+
+ // The thread used by the devtools handler to run server socket.
+ scoped_ptr<base::Thread> thread_;
+
+ std::string overridden_frontend_url_;
+ scoped_ptr<const net::StreamListenSocketFactory> socket_factory_;
+ scoped_refptr<net::HttpServer> server_;
+ typedef std::map<int, DevToolsClientHost*> ConnectionToClientHostMap;
+ ConnectionToClientHostMap connection_to_client_host_ui_;
+ scoped_ptr<DevToolsHttpHandlerDelegate> delegate_;
+ RenderViewHostBinding* binding_;
+ scoped_ptr<RenderViewHostBinding> default_binding_;
+ NotificationRegistrar registrar_;
+ scoped_ptr<DevToolsBrowserTarget> browser_target_;
+ DISALLOW_COPY_AND_ASSIGN(DevToolsHttpHandlerImpl);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_HTTP_HANDLER_IMPL_H_
diff --git a/content/browser/devtools/devtools_http_handler_unittest.cc b/content/browser/devtools/devtools_http_handler_unittest.cc
new file mode 100644
index 0000000..b01c9d9
--- /dev/null
+++ b/content/browser/devtools/devtools_http_handler_unittest.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2012 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 "base/message_loop.h"
+#include "base/run_loop.h"
+#include "content/browser/browser_thread_impl.h"
+#include "content/public/browser/devtools_http_handler.h"
+#include "content/public/browser/devtools_http_handler_delegate.h"
+#include "net/base/stream_listen_socket.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+namespace {
+
+using net::StreamListenSocket;
+
+class DummyListenSocket : public StreamListenSocket,
+ public StreamListenSocket::Delegate {
+ public:
+ DummyListenSocket()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(StreamListenSocket(0, this)) {}
+
+ // StreamListenSocket::Delegate "implementation"
+ virtual void DidAccept(StreamListenSocket* server,
+ StreamListenSocket* connection) OVERRIDE {}
+ virtual void DidRead(StreamListenSocket* connection,
+ const char* data,
+ int len) OVERRIDE {}
+ virtual void DidClose(StreamListenSocket* sock) OVERRIDE {}
+ protected:
+ virtual ~DummyListenSocket() {}
+ virtual void Accept() OVERRIDE {}
+};
+
+class DummyListenSocketFactory : public net::StreamListenSocketFactory {
+ public:
+ DummyListenSocketFactory(
+ base::Closure quit_closure_1, base::Closure quit_closure_2)
+ : quit_closure_1_(quit_closure_1), quit_closure_2_(quit_closure_2) {}
+ virtual ~DummyListenSocketFactory() {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE, quit_closure_2_);
+ }
+
+ virtual scoped_refptr<StreamListenSocket> CreateAndListen(
+ StreamListenSocket::Delegate* delegate) const OVERRIDE {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE, quit_closure_1_);
+ return new DummyListenSocket();
+ }
+ private:
+ base::Closure quit_closure_1_;
+ base::Closure quit_closure_2_;
+};
+
+class DummyDelegate : public DevToolsHttpHandlerDelegate {
+ public:
+ virtual std::string GetDiscoveryPageHTML() OVERRIDE { return ""; }
+ virtual bool BundlesFrontendResources() OVERRIDE { return true; }
+ virtual FilePath GetDebugFrontendDir() OVERRIDE { return FilePath(); }
+ virtual std::string GetPageThumbnailData(const GURL& url) { return ""; }
+ virtual RenderViewHost* CreateNewTarget() { return NULL; }
+};
+
+}
+
+class DevToolsHttpHandlerTest : public testing::Test {
+ public:
+ DevToolsHttpHandlerTest()
+ : ui_thread_(BrowserThread::UI, &message_loop_) {
+ }
+ protected:
+ virtual void SetUp() {
+ file_thread_.reset(new BrowserThreadImpl(BrowserThread::FILE));
+ file_thread_->Start();
+ }
+ virtual void TearDown() {
+ file_thread_->Stop();
+ }
+ private:
+ MessageLoopForIO message_loop_;
+ BrowserThreadImpl ui_thread_;
+ scoped_ptr<BrowserThreadImpl> file_thread_;
+};
+
+TEST_F(DevToolsHttpHandlerTest, TestStartStop) {
+ base::RunLoop run_loop, run_loop_2;
+ content::DevToolsHttpHandler* devtools_http_handler_ =
+ content::DevToolsHttpHandler::Start(
+ new DummyListenSocketFactory(
+ run_loop.QuitClosure(), run_loop_2.QuitClosure()),
+ "",
+ new DummyDelegate());
+ // Our dummy socket factory will post a quit message once the server will
+ // become ready.
+ run_loop.Run();
+ devtools_http_handler_->Stop();
+ // Make sure the handler actually stops.
+ run_loop_2.Run();
+}
+
+} // namespace content
diff --git a/content/browser/devtools/devtools_manager_impl.cc b/content/browser/devtools/devtools_manager_impl.cc
new file mode 100644
index 0000000..b548ba2
--- /dev/null
+++ b/content/browser/devtools/devtools_manager_impl.cc
@@ -0,0 +1,302 @@
+// Copyright (c) 2012 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 "content/browser/devtools/devtools_manager_impl.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/devtools/devtools_netlog_observer.h"
+#include "content/browser/devtools/render_view_devtools_agent_host.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/devtools_agent_host_registry.h"
+#include "content/public/browser/devtools_client_host.h"
+#include "googleurl/src/gurl.h"
+
+namespace content {
+
+// static
+DevToolsManager* DevToolsManager::GetInstance() {
+ return DevToolsManagerImpl::GetInstance();
+}
+
+// static
+DevToolsManagerImpl* DevToolsManagerImpl::GetInstance() {
+ return Singleton<DevToolsManagerImpl>::get();
+}
+
+DevToolsManagerImpl::DevToolsManagerImpl()
+ : last_orphan_cookie_(0) {
+}
+
+DevToolsManagerImpl::~DevToolsManagerImpl() {
+ DCHECK(agent_to_client_host_.empty());
+ DCHECK(client_to_agent_host_.empty());
+ // By the time we destroy devtools manager, all orphan client hosts should
+ // have been deleted; no need to notify them upon contents closing.
+ DCHECK(orphan_client_hosts_.empty());
+}
+
+DevToolsClientHost* DevToolsManagerImpl::GetDevToolsClientHostFor(
+ DevToolsAgentHost* agent_host) {
+ AgentToClientHostMap::iterator it = agent_to_client_host_.find(agent_host);
+ if (it != agent_to_client_host_.end())
+ return it->second;
+ return NULL;
+}
+
+DevToolsAgentHost* DevToolsManagerImpl::GetDevToolsAgentHostFor(
+ DevToolsClientHost* client_host) {
+ ClientToAgentHostMap::iterator it = client_to_agent_host_.find(client_host);
+ if (it != client_to_agent_host_.end())
+ return it->second;
+ return NULL;
+}
+
+void DevToolsManagerImpl::RegisterDevToolsClientHostFor(
+ DevToolsAgentHost* agent_host,
+ DevToolsClientHost* client_host) {
+ BindClientHost(agent_host, client_host);
+ agent_host->Attach();
+}
+
+bool DevToolsManagerImpl::DispatchOnInspectorBackend(
+ DevToolsClientHost* from,
+ const std::string& message) {
+ DevToolsAgentHost* agent_host = GetDevToolsAgentHostFor(from);
+ if (!agent_host)
+ return false;
+
+ agent_host->DipatchOnInspectorBackend(message);
+ return true;
+}
+
+void DevToolsManagerImpl::DispatchOnInspectorFrontend(
+ DevToolsAgentHost* agent_host,
+ const std::string& message) {
+ DevToolsClientHost* client_host = GetDevToolsClientHostFor(agent_host);
+ if (!client_host) {
+ // Client window was closed while there were messages
+ // being sent to it.
+ return;
+ }
+ client_host->DispatchOnInspectorFrontend(message);
+}
+
+void DevToolsManagerImpl::SaveAgentRuntimeState(DevToolsAgentHost* agent_host,
+ const std::string& state) {
+ agent_runtime_states_[agent_host] = state;
+}
+
+void DevToolsManagerImpl::InspectElement(DevToolsAgentHost* agent_host,
+ int x, int y) {
+ agent_host->InspectElement(x, y);
+}
+
+void DevToolsManagerImpl::AddMessageToConsole(DevToolsAgentHost* agent_host,
+ ConsoleMessageLevel level,
+ const std::string& message) {
+ agent_host->AddMessageToConsole(level, message);
+}
+
+void DevToolsManagerImpl::ClientHostClosing(DevToolsClientHost* client_host) {
+ DevToolsAgentHost* agent_host = GetDevToolsAgentHostFor(client_host);
+ if (!agent_host) {
+ // It might be in the list of orphan client hosts, remove it from there.
+ for (OrphanClientHosts::iterator it = orphan_client_hosts_.begin();
+ it != orphan_client_hosts_.end(); ++it) {
+ if (it->second.first == client_host) {
+ orphan_client_hosts_.erase(it->first);
+ return;
+ }
+ }
+ return;
+ }
+
+ UnbindClientHost(agent_host, client_host);
+}
+
+void DevToolsManagerImpl::AgentHostClosing(DevToolsAgentHost* agent_host) {
+ UnregisterDevToolsClientHostFor(agent_host);
+}
+
+void DevToolsManagerImpl::UnregisterDevToolsClientHostFor(
+ DevToolsAgentHost* agent_host) {
+ DevToolsClientHost* client_host = GetDevToolsClientHostFor(agent_host);
+ if (!client_host)
+ return;
+ UnbindClientHost(agent_host, client_host);
+ client_host->InspectedContentsClosing();
+}
+
+void DevToolsManagerImpl::OnNavigatingToPendingEntry(
+ RenderViewHost* rvh,
+ RenderViewHost* dest_rvh,
+ const GURL& gurl) {
+ if (rvh == dest_rvh && static_cast<RenderViewHostImpl*>(
+ rvh)->render_view_termination_status() ==
+ base::TERMINATION_STATUS_STILL_RUNNING)
+ return;
+ int cookie = DetachClientHost(rvh);
+ if (cookie != -1) {
+ // Navigating to URL in the inspected window.
+ AttachClientHost(cookie, dest_rvh);
+
+ DevToolsAgentHost* dest_agent_host =
+ DevToolsAgentHostRegistry::GetDevToolsAgentHost(dest_rvh);
+ DevToolsClientHost* client_host = GetDevToolsClientHostFor(
+ dest_agent_host);
+ client_host->FrameNavigating(gurl.spec());
+ }
+}
+
+void DevToolsManagerImpl::OnCancelPendingNavigation(
+ RenderViewHost* pending,
+ RenderViewHost* current) {
+ int cookie = DetachClientHost(pending);
+ if (cookie != -1) {
+ // Navigating to URL in the inspected window.
+ AttachClientHost(cookie, current);
+ }
+}
+
+void DevToolsManagerImpl::ContentsReplaced(WebContents* old_contents,
+ WebContents* new_contents) {
+ RenderViewHost* old_rvh = old_contents->GetRenderViewHost();
+ if (!DevToolsAgentHostRegistry::HasDevToolsAgentHost(old_rvh))
+ return;
+
+ DevToolsAgentHost* old_agent_host =
+ DevToolsAgentHostRegistry::GetDevToolsAgentHost(old_rvh);
+ DevToolsClientHost* client_host = GetDevToolsClientHostFor(old_agent_host);
+ if (!client_host)
+ return; // Didn't know about old_contents.
+ int cookie = DetachClientHost(old_rvh);
+ if (cookie == -1)
+ return; // Didn't know about old_contents.
+
+ client_host->ContentsReplaced(new_contents);
+ AttachClientHost(cookie, new_contents->GetRenderViewHost());
+}
+
+int DevToolsManagerImpl::DetachClientHost(RenderViewHost* from_rvh) {
+ DevToolsAgentHost* agent_host =
+ DevToolsAgentHostRegistry::GetDevToolsAgentHost(from_rvh);
+ return DetachClientHost(agent_host);
+}
+
+int DevToolsManagerImpl::DetachClientHost(DevToolsAgentHost* agent_host) {
+ DevToolsClientHost* client_host = GetDevToolsClientHostFor(agent_host);
+ if (!client_host)
+ return -1;
+
+ int cookie = last_orphan_cookie_++;
+ orphan_client_hosts_[cookie] =
+ std::pair<DevToolsClientHost*, std::string>(
+ client_host, agent_runtime_states_[agent_host]);
+
+ UnbindClientHost(agent_host, client_host);
+ return cookie;
+}
+
+void DevToolsManagerImpl::AttachClientHost(int client_host_cookie,
+ RenderViewHost* to_rvh) {
+ DevToolsAgentHost* agent_host =
+ DevToolsAgentHostRegistry::GetDevToolsAgentHost(to_rvh);
+ AttachClientHost(client_host_cookie, agent_host);
+}
+
+void DevToolsManagerImpl::AttachClientHost(int client_host_cookie,
+ DevToolsAgentHost* agent_host) {
+ OrphanClientHosts::iterator it = orphan_client_hosts_.find(
+ client_host_cookie);
+ if (it == orphan_client_hosts_.end())
+ return;
+
+ DevToolsClientHost* client_host = (*it).second.first;
+ const std::string& state = (*it).second.second;
+ BindClientHost(agent_host, client_host);
+ agent_host->Reattach(state);
+ agent_runtime_states_[agent_host] = state;
+
+ orphan_client_hosts_.erase(it);
+}
+
+void DevToolsManagerImpl::BindClientHost(
+ DevToolsAgentHost* agent_host,
+ DevToolsClientHost* client_host) {
+ DCHECK(agent_to_client_host_.find(agent_host) ==
+ agent_to_client_host_.end());
+ DCHECK(client_to_agent_host_.find(client_host) ==
+ client_to_agent_host_.end());
+
+ if (client_to_agent_host_.empty()) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&DevToolsNetLogObserver::Attach));
+ }
+ agent_to_client_host_[agent_host] = client_host;
+ client_to_agent_host_[client_host] = agent_host;
+ agent_host->set_close_listener(this);
+
+ int process_id = agent_host->GetRenderProcessId();
+ if (process_id != -1)
+ ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
+ process_id);
+}
+
+void DevToolsManagerImpl::UnbindClientHost(DevToolsAgentHost* agent_host,
+ DevToolsClientHost* client_host) {
+ DCHECK(agent_host);
+ DCHECK(agent_to_client_host_.find(agent_host)->second ==
+ client_host);
+ DCHECK(client_to_agent_host_.find(client_host)->second ==
+ agent_host);
+ agent_host->set_close_listener(NULL);
+
+ agent_to_client_host_.erase(agent_host);
+ client_to_agent_host_.erase(client_host);
+ agent_runtime_states_.erase(agent_host);
+
+ if (client_to_agent_host_.empty()) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&DevToolsNetLogObserver::Detach));
+ }
+ agent_host->Detach();
+
+ int process_id = agent_host->GetRenderProcessId();
+ if (process_id == -1)
+ return;
+ for (AgentToClientHostMap::iterator it = agent_to_client_host_.begin();
+ it != agent_to_client_host_.end();
+ ++it) {
+ if (it->first->GetRenderProcessId() == process_id)
+ return;
+ }
+ // We've disconnected from the last renderer -> revoke cookie permissions.
+ ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
+ process_id);
+}
+
+void DevToolsManagerImpl::CloseAllClientHosts() {
+ std::vector<DevToolsAgentHost*> agents;
+ for (AgentToClientHostMap::iterator it =
+ agent_to_client_host_.begin();
+ it != agent_to_client_host_.end(); ++it) {
+ agents.push_back(it->first);
+ }
+ for (std::vector<DevToolsAgentHost*>::iterator it = agents.begin();
+ it != agents.end(); ++it) {
+ UnregisterDevToolsClientHostFor(*it);
+ }
+}
+
+} // namespace content
diff --git a/content/browser/devtools/devtools_manager_impl.h b/content/browser/devtools/devtools_manager_impl.h
new file mode 100644
index 0000000..ed213b7
--- /dev/null
+++ b/content/browser/devtools/devtools_manager_impl.h
@@ -0,0 +1,131 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_IMPL_H_
+#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_IMPL_H_
+
+#include <map>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/memory/singleton.h"
+#include "content/browser/devtools/devtools_agent_host.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/devtools_client_host.h"
+#include "content/public/browser/devtools_manager.h"
+
+class GURL;
+
+namespace IPC {
+class Message;
+}
+
+namespace content {
+
+class DevToolsAgentHost;
+class RenderViewHost;
+
+// This class is a singleton that manages DevToolsClientHost instances and
+// routes messages between developer tools clients and agents.
+//
+// Methods below that accept inspected RenderViewHost as a parameter are
+// just convenience methods that call corresponding methods accepting
+// DevToolAgentHost.
+class CONTENT_EXPORT DevToolsManagerImpl
+ : public DevToolsAgentHost::CloseListener,
+ public DevToolsManager {
+ public:
+ // Returns single instance of this class. The instance is destroyed on the
+ // browser main loop exit so this method MUST NOT be called after that point.
+ static DevToolsManagerImpl* GetInstance();
+
+ DevToolsManagerImpl();
+ virtual ~DevToolsManagerImpl();
+
+ void DispatchOnInspectorFrontend(DevToolsAgentHost* agent_host,
+ const std::string& message);
+
+ void SaveAgentRuntimeState(DevToolsAgentHost* agent_host,
+ const std::string& state);
+
+ // Sends 'Attach' message to the agent using |dest_rvh| in case
+ // there is a DevToolsClientHost registered for the |inspected_rvh|.
+ void OnNavigatingToPendingEntry(RenderViewHost* inspected_rvh,
+ RenderViewHost* dest_rvh,
+ const GURL& gurl);
+ void OnCancelPendingNavigation(RenderViewHost* pending,
+ RenderViewHost* current);
+
+ // DevToolsManager implementation
+ virtual bool DispatchOnInspectorBackend(DevToolsClientHost* from,
+ const std::string& message) OVERRIDE;
+ virtual void ContentsReplaced(WebContents* old_contents,
+ WebContents* new_contents) OVERRIDE;
+ virtual void CloseAllClientHosts() OVERRIDE;
+ virtual void AttachClientHost(int client_host_cookie,
+ DevToolsAgentHost* to_agent) OVERRIDE;
+ virtual DevToolsClientHost* GetDevToolsClientHostFor(
+ DevToolsAgentHost* agent_host) OVERRIDE;
+ virtual DevToolsAgentHost* GetDevToolsAgentHostFor(
+ DevToolsClientHost* client_host) OVERRIDE;
+ virtual void RegisterDevToolsClientHostFor(
+ DevToolsAgentHost* agent_host,
+ DevToolsClientHost* client_host) OVERRIDE;
+ virtual void UnregisterDevToolsClientHostFor(
+ DevToolsAgentHost* agent_host) OVERRIDE;
+ virtual int DetachClientHost(DevToolsAgentHost* from_agent) OVERRIDE;
+ virtual void ClientHostClosing(DevToolsClientHost* host) OVERRIDE;
+ virtual void InspectElement(DevToolsAgentHost* agent_host,
+ int x, int y) OVERRIDE;
+ virtual void AddMessageToConsole(DevToolsAgentHost* agent_host,
+ ConsoleMessageLevel level,
+ const std::string& message) OVERRIDE;
+
+ private:
+ friend struct DefaultSingletonTraits<DevToolsManagerImpl>;
+
+ // DevToolsAgentHost::CloseListener implementation.
+ virtual void AgentHostClosing(DevToolsAgentHost* host) OVERRIDE;
+
+ void BindClientHost(DevToolsAgentHost* agent_host,
+ DevToolsClientHost* client_host);
+ void UnbindClientHost(DevToolsAgentHost* agent_host,
+ DevToolsClientHost* client_host);
+
+ // Detaches client host and returns cookie that can be used in
+ // AttachClientHost.
+ int DetachClientHost(RenderViewHost* from_rvh);
+
+ // Attaches orphan client host to new render view host.
+ void AttachClientHost(int client_host_cookie,
+ RenderViewHost* to_rvh);
+
+ // These two maps are for tracking dependencies between inspected contents and
+ // their DevToolsClientHosts. They are useful for routing devtools messages
+ // and allow us to have at most one devtools client host per contents.
+ //
+ // DevToolsManagerImpl starts listening to DevToolsClientHosts when they are
+ // put into these maps and removes them when they are closing.
+ typedef std::map<DevToolsAgentHost*, DevToolsClientHost*>
+ AgentToClientHostMap;
+ AgentToClientHostMap agent_to_client_host_;
+
+ typedef std::map<DevToolsClientHost*, DevToolsAgentHost*>
+ ClientToAgentHostMap;
+ ClientToAgentHostMap client_to_agent_host_;
+
+ typedef std::map<DevToolsAgentHost*, std::string> AgentRuntimeStates;
+ AgentRuntimeStates agent_runtime_states_;
+
+ typedef std::map<int, std::pair<DevToolsClientHost*, std::string> >
+ OrphanClientHosts;
+ OrphanClientHosts orphan_client_hosts_;
+ int last_orphan_cookie_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsManagerImpl);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_MANAGER_IMPL_H_
diff --git a/content/browser/devtools/devtools_manager_unittest.cc b/content/browser/devtools/devtools_manager_unittest.cc
new file mode 100644
index 0000000..d2a47a5
--- /dev/null
+++ b/content/browser/devtools/devtools_manager_unittest.cc
@@ -0,0 +1,243 @@
+// Copyright (c) 2012 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 "base/basictypes.h"
+#include "base/time.h"
+#include "content/browser/devtools/devtools_manager_impl.h"
+#include "content/browser/devtools/render_view_devtools_agent_host.h"
+#include "content/browser/renderer_host/test_render_view_host.h"
+#include "content/browser/web_contents/test_web_contents.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/devtools_agent_host_registry.h"
+#include "content/public/browser/devtools_client_host.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/test/test_content_browser_client.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::TimeDelta;
+
+namespace content {
+namespace {
+
+class TestDevToolsClientHost : public DevToolsClientHost {
+ public:
+ TestDevToolsClientHost()
+ : last_sent_message(NULL),
+ closed_(false) {
+ }
+
+ virtual ~TestDevToolsClientHost() {
+ EXPECT_TRUE(closed_);
+ }
+
+ virtual void Close(DevToolsManager* manager) {
+ EXPECT_FALSE(closed_);
+ close_counter++;
+ manager->ClientHostClosing(this);
+ closed_ = true;
+ }
+ virtual void InspectedContentsClosing() {
+ FAIL();
+ }
+
+ virtual void DispatchOnInspectorFrontend(const std::string& message) {
+ last_sent_message = &message;
+ }
+
+ virtual void ContentsReplaced(WebContents* new_contents) {
+ }
+
+ virtual void ReplacedWithAnotherClient() {
+ }
+
+ static void ResetCounters() {
+ close_counter = 0;
+ }
+
+ static int close_counter;
+
+ const std::string* last_sent_message;
+
+ private:
+ bool closed_;
+
+ virtual void FrameNavigating(const std::string& url) {}
+
+ DISALLOW_COPY_AND_ASSIGN(TestDevToolsClientHost);
+};
+
+int TestDevToolsClientHost::close_counter = 0;
+
+
+class TestWebContentsDelegate : public WebContentsDelegate {
+ public:
+ TestWebContentsDelegate() : renderer_unresponsive_received_(false) {}
+
+ // Notification that the contents is hung.
+ virtual void RendererUnresponsive(WebContents* source) {
+ renderer_unresponsive_received_ = true;
+ }
+
+ bool renderer_unresponsive_received() const {
+ return renderer_unresponsive_received_;
+ }
+
+ private:
+ bool renderer_unresponsive_received_;
+};
+
+class DevToolsManagerTestBrowserClient : public TestContentBrowserClient {
+ public:
+ DevToolsManagerTestBrowserClient() {
+ }
+
+ virtual bool ShouldSwapProcessesForNavigation(
+ const GURL& current_url,
+ const GURL& new_url) OVERRIDE {
+ return true;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DevToolsManagerTestBrowserClient);
+};
+
+} // namespace
+
+class DevToolsManagerTest : public RenderViewHostImplTestHarness {
+ public:
+ DevToolsManagerTest() {
+ }
+
+ protected:
+ virtual void SetUp() OVERRIDE {
+ original_browser_client_ = GetContentClient()->browser();
+ GetContentClient()->set_browser_for_testing(&browser_client_);
+
+ RenderViewHostImplTestHarness::SetUp();
+ TestDevToolsClientHost::ResetCounters();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ RenderViewHostImplTestHarness::TearDown();
+ GetContentClient()->set_browser_for_testing(original_browser_client_);
+ }
+
+ private:
+ ContentBrowserClient* original_browser_client_;
+ DevToolsManagerTestBrowserClient browser_client_;
+};
+
+TEST_F(DevToolsManagerTest, OpenAndManuallyCloseDevToolsClientHost) {
+ DevToolsManagerImpl manager;
+
+ DevToolsAgentHost* agent =
+ DevToolsAgentHostRegistry::GetDevToolsAgentHost(rvh());
+ DevToolsClientHost* host = manager.GetDevToolsClientHostFor(agent);
+ EXPECT_TRUE(NULL == host);
+
+ TestDevToolsClientHost client_host;
+ manager.RegisterDevToolsClientHostFor(agent, &client_host);
+ // Test that just registered devtools host is returned.
+ host = manager.GetDevToolsClientHostFor(agent);
+ EXPECT_TRUE(&client_host == host);
+ EXPECT_EQ(0, TestDevToolsClientHost::close_counter);
+
+ // Test that the same devtools host is returned.
+ host = manager.GetDevToolsClientHostFor(agent);
+ EXPECT_TRUE(&client_host == host);
+ EXPECT_EQ(0, TestDevToolsClientHost::close_counter);
+
+ client_host.Close(&manager);
+ EXPECT_EQ(1, TestDevToolsClientHost::close_counter);
+ host = manager.GetDevToolsClientHostFor(agent);
+ EXPECT_TRUE(NULL == host);
+}
+
+TEST_F(DevToolsManagerTest, ForwardMessageToClient) {
+ DevToolsManagerImpl manager;
+
+ TestDevToolsClientHost client_host;
+ DevToolsAgentHost* agent_host =
+ DevToolsAgentHostRegistry::GetDevToolsAgentHost(rvh());
+ manager.RegisterDevToolsClientHostFor(agent_host, &client_host);
+ EXPECT_EQ(0, TestDevToolsClientHost::close_counter);
+
+ std::string m = "test message";
+ agent_host = DevToolsAgentHostRegistry::GetDevToolsAgentHost(rvh());
+ manager.DispatchOnInspectorFrontend(agent_host, m);
+ EXPECT_TRUE(&m == client_host.last_sent_message);
+
+ client_host.Close(&manager);
+ EXPECT_EQ(1, TestDevToolsClientHost::close_counter);
+}
+
+TEST_F(DevToolsManagerTest, NoUnresponsiveDialogInInspectedContents) {
+ TestRenderViewHost* inspected_rvh = test_rvh();
+ inspected_rvh->set_render_view_created(true);
+ EXPECT_FALSE(contents()->GetDelegate());
+ TestWebContentsDelegate delegate;
+ contents()->SetDelegate(&delegate);
+
+ TestDevToolsClientHost client_host;
+ DevToolsAgentHost* agent_host =
+ DevToolsAgentHostRegistry::GetDevToolsAgentHost(inspected_rvh);
+ DevToolsManager::GetInstance()->
+ RegisterDevToolsClientHostFor(agent_host, &client_host);
+
+ // Start with a short timeout.
+ inspected_rvh->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(10));
+ // Wait long enough for first timeout and see if it fired.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, MessageLoop::QuitClosure(), TimeDelta::FromMilliseconds(10));
+ MessageLoop::current()->Run();
+ EXPECT_FALSE(delegate.renderer_unresponsive_received());
+
+ // Now close devtools and check that the notification is delivered.
+ client_host.Close(DevToolsManager::GetInstance());
+ // Start with a short timeout.
+ inspected_rvh->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(10));
+ // Wait long enough for first timeout and see if it fired.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, MessageLoop::QuitClosure(), TimeDelta::FromMilliseconds(10));
+ MessageLoop::current()->Run();
+ EXPECT_TRUE(delegate.renderer_unresponsive_received());
+
+ contents()->SetDelegate(NULL);
+}
+
+TEST_F(DevToolsManagerTest, ReattachOnCancelPendingNavigation) {
+ contents()->transition_cross_site = true;
+ // Navigate to URL. First URL should use first RenderViewHost.
+ const GURL url("http://www.google.com");
+ controller().LoadURL(
+ url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
+ contents()->TestDidNavigate(rvh(), 1, url, PAGE_TRANSITION_TYPED);
+ EXPECT_FALSE(contents()->cross_navigation_pending());
+
+ TestDevToolsClientHost client_host;
+ DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
+ devtools_manager->RegisterDevToolsClientHostFor(
+ DevToolsAgentHostRegistry::GetDevToolsAgentHost(rvh()),
+ &client_host);
+
+ // Navigate to new site which should get a new RenderViewHost.
+ const GURL url2("http://www.yahoo.com");
+ controller().LoadURL(
+ url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
+ EXPECT_TRUE(contents()->cross_navigation_pending());
+ EXPECT_EQ(&client_host, devtools_manager->GetDevToolsClientHostFor(
+ DevToolsAgentHostRegistry::GetDevToolsAgentHost(pending_rvh())));
+
+ // Interrupt pending navigation and navigate back to the original site.
+ controller().LoadURL(
+ url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
+ contents()->TestDidNavigate(rvh(), 1, url, PAGE_TRANSITION_TYPED);
+ EXPECT_FALSE(contents()->cross_navigation_pending());
+ EXPECT_EQ(&client_host, devtools_manager->GetDevToolsClientHostFor(
+ DevToolsAgentHostRegistry::GetDevToolsAgentHost(rvh())));
+ client_host.Close(DevToolsManager::GetInstance());
+}
+
+} // namespace content
diff --git a/content/browser/devtools/devtools_netlog_observer.cc b/content/browser/devtools/devtools_netlog_observer.cc
new file mode 100644
index 0000000..f65bc42
--- /dev/null
+++ b/content/browser/devtools/devtools_netlog_observer.cc
@@ -0,0 +1,329 @@
+// Copyright (c) 2012 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 "content/browser/devtools/devtools_netlog_observer.h"
+
+#include "base/string_tokenizer.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/common/resource_response.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "net/spdy/spdy_header_block.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_netlog_params.h"
+#include "webkit/glue/resource_loader_bridge.h"
+
+namespace content {
+const size_t kMaxNumEntries = 1000;
+
+DevToolsNetLogObserver* DevToolsNetLogObserver::instance_ = NULL;
+
+DevToolsNetLogObserver::DevToolsNetLogObserver() {
+}
+
+DevToolsNetLogObserver::~DevToolsNetLogObserver() {
+}
+
+DevToolsNetLogObserver::ResourceInfo*
+DevToolsNetLogObserver::GetResourceInfo(uint32 id) {
+ RequestToInfoMap::iterator it = request_to_info_.find(id);
+ if (it != request_to_info_.end())
+ return it->second;
+ return NULL;
+}
+
+void DevToolsNetLogObserver::OnAddEntry(const net::NetLog::Entry& entry) {
+ // The events that the Observer is interested in only occur on the IO thread.
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO))
+ return;
+
+ if (entry.source().type == net::NetLog::SOURCE_URL_REQUEST)
+ OnAddURLRequestEntry(entry);
+ else if (entry.source().type == net::NetLog::SOURCE_HTTP_STREAM_JOB)
+ OnAddHTTPStreamJobEntry(entry);
+ else if (entry.source().type == net::NetLog::SOURCE_SOCKET)
+ OnAddSocketEntry(entry);
+}
+
+void DevToolsNetLogObserver::OnAddURLRequestEntry(
+ const net::NetLog::Entry& entry) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ bool is_begin = entry.phase() == net::NetLog::PHASE_BEGIN;
+ bool is_end = entry.phase() == net::NetLog::PHASE_END;
+
+ if (entry.type() == net::NetLog::TYPE_URL_REQUEST_START_JOB) {
+ if (is_begin) {
+ int load_flags;
+ scoped_ptr<Value> event_param(entry.ParametersToValue());
+ if (!net::StartEventLoadFlagsFromEventParams(event_param.get(),
+ &load_flags)) {
+ return;
+ }
+
+ if (!(load_flags & net::LOAD_REPORT_RAW_HEADERS))
+ return;
+
+ if (request_to_info_.size() > kMaxNumEntries) {
+ LOG(WARNING) << "The raw headers observer url request count has grown "
+ "larger than expected, resetting";
+ request_to_info_.clear();
+ }
+
+ request_to_info_[entry.source().id] = new ResourceInfo();
+
+ if (request_to_encoded_data_length_.size() > kMaxNumEntries) {
+ LOG(WARNING) << "The encoded data length observer url request count "
+ "has grown larger than expected, resetting";
+ request_to_encoded_data_length_.clear();
+ }
+
+ request_to_encoded_data_length_[entry.source().id] = 0;
+ }
+ return;
+ } else if (entry.type() == net::NetLog::TYPE_REQUEST_ALIVE) {
+ // Cleanup records based on the TYPE_REQUEST_ALIVE entry.
+ if (is_end) {
+ request_to_info_.erase(entry.source().id);
+ request_to_encoded_data_length_.erase(entry.source().id);
+ }
+ return;
+ }
+
+ ResourceInfo* info = GetResourceInfo(entry.source().id);
+ if (!info)
+ return;
+
+ switch (entry.type()) {
+ case net::NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS: {
+ scoped_ptr<Value> event_params(entry.ParametersToValue());
+ std::string request_line;
+ net::HttpRequestHeaders request_headers;
+
+ if (!net::HttpRequestHeaders::FromNetLogParam(event_params.get(),
+ &request_headers,
+ &request_line)) {
+ NOTREACHED();
+ }
+
+ // We need to clear headers in case the same url_request is reused for
+ // several http requests (e.g. see http://crbug.com/80157).
+ info->request_headers.clear();
+
+ for (net::HttpRequestHeaders::Iterator it(request_headers);
+ it.GetNext();) {
+ info->request_headers.push_back(std::make_pair(it.name(), it.value()));
+ }
+ info->request_headers_text = request_line + request_headers.ToString();
+ break;
+ }
+ case net::NetLog::TYPE_HTTP_TRANSACTION_SPDY_SEND_REQUEST_HEADERS: {
+ scoped_ptr<Value> event_params(entry.ParametersToValue());
+ net::SpdyHeaderBlock request_headers;
+
+ if (!net::SpdyHeaderBlockFromNetLogParam(event_params.get(),
+ &request_headers)) {
+ NOTREACHED();
+ }
+
+ // We need to clear headers in case the same url_request is reused for
+ // several http requests (e.g. see http://crbug.com/80157).
+ info->request_headers.clear();
+
+ for (net::SpdyHeaderBlock::const_iterator it = request_headers.begin();
+ it != request_headers.end(); ++it) {
+ info->request_headers.push_back(std::make_pair(it->first, it->second));
+ }
+ info->request_headers_text = "";
+ break;
+ }
+ case net::NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS: {
+ scoped_ptr<Value> event_params(entry.ParametersToValue());
+
+ scoped_refptr<net::HttpResponseHeaders> response_headers;
+
+ if (!net::HttpResponseHeaders::FromNetLogParam(event_params.get(),
+ &response_headers)) {
+ NOTREACHED();
+ }
+
+ info->http_status_code = response_headers->response_code();
+ info->http_status_text = response_headers->GetStatusText();
+ std::string name, value;
+
+ // We need to clear headers in case the same url_request is reused for
+ // several http requests (e.g. see http://crbug.com/80157).
+ info->response_headers.clear();
+
+ for (void* it = NULL;
+ response_headers->EnumerateHeaderLines(&it, &name, &value); ) {
+ info->response_headers.push_back(std::make_pair(name, value));
+ }
+ info->response_headers_text =
+ net::HttpUtil::ConvertHeadersBackToHTTPResponse(
+ response_headers->raw_headers());
+ break;
+ }
+ case net::NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB: {
+ scoped_ptr<Value> event_params(entry.ParametersToValue());
+ net::NetLog::Source http_stream_job_source;
+ if (!net::NetLog::Source::FromEventParameters(event_params.get(),
+ &http_stream_job_source)) {
+ NOTREACHED();
+ break;
+ }
+
+ uint32 http_stream_job_id = http_stream_job_source.id;
+ HTTPStreamJobToSocketMap::iterator it =
+ http_stream_job_to_socket_.find(http_stream_job_id);
+ if (it == http_stream_job_to_socket_.end())
+ return;
+ uint32 socket_id = it->second;
+
+ if (socket_to_request_.size() > kMaxNumEntries) {
+ LOG(WARNING) << "The url request observer socket count has grown "
+ "larger than expected, resetting";
+ socket_to_request_.clear();
+ }
+
+ socket_to_request_[socket_id] = entry.source().id;
+ http_stream_job_to_socket_.erase(http_stream_job_id);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void DevToolsNetLogObserver::OnAddHTTPStreamJobEntry(
+ const net::NetLog::Entry& entry) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (entry.type() == net::NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET) {
+ scoped_ptr<Value> event_params(entry.ParametersToValue());
+ net::NetLog::Source socket_source;
+ if (!net::NetLog::Source::FromEventParameters(event_params.get(),
+ &socket_source)) {
+ NOTREACHED();
+ return;
+ }
+
+ // Prevents us from passively growing the memory unbounded in
+ // case something went wrong. Should not happen.
+ if (http_stream_job_to_socket_.size() > kMaxNumEntries) {
+ LOG(WARNING) << "The load timing observer http stream job count "
+ "has grown larger than expected, resetting";
+ http_stream_job_to_socket_.clear();
+ }
+ http_stream_job_to_socket_[entry.source().id] = socket_source.id;
+ }
+}
+
+void DevToolsNetLogObserver::OnAddSocketEntry(
+ const net::NetLog::Entry& entry) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ bool is_end = entry.phase() == net::NetLog::PHASE_END;
+
+ SocketToRequestMap::iterator it =
+ socket_to_request_.find(entry.source().id);
+ if (it == socket_to_request_.end())
+ return;
+ uint32 request_id = it->second;
+
+ if (entry.type() == net::NetLog::TYPE_SOCKET_IN_USE) {
+ if (is_end)
+ socket_to_request_.erase(entry.source().id);
+ return;
+ }
+
+ RequestToEncodedDataLengthMap::iterator encoded_data_length_it =
+ request_to_encoded_data_length_.find(request_id);
+ if (encoded_data_length_it == request_to_encoded_data_length_.end())
+ return;
+
+ if (net::NetLog::TYPE_SOCKET_BYTES_RECEIVED == entry.type()) {
+ int byte_count = 0;
+ scoped_ptr<Value> value(entry.ParametersToValue());
+ if (!value->IsType(Value::TYPE_DICTIONARY))
+ return;
+
+ DictionaryValue* dValue = static_cast<DictionaryValue*>(value.get());
+ if (!dValue->GetInteger("byte_count", &byte_count))
+ return;
+
+ encoded_data_length_it->second += byte_count;
+ }
+}
+
+void DevToolsNetLogObserver::Attach() {
+ DCHECK(!instance_);
+ net::NetLog* net_log = GetContentClient()->browser()->GetNetLog();
+ if (net_log) {
+ instance_ = new DevToolsNetLogObserver();
+ net_log->AddThreadSafeObserver(instance_, net::NetLog::LOG_ALL_BUT_BYTES);
+ }
+}
+
+void DevToolsNetLogObserver::Detach() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (instance_) {
+ // Safest not to do this in the destructor to maintain thread safety across
+ // refactorings.
+ instance_->net_log()->RemoveThreadSafeObserver(instance_);
+ delete instance_;
+ instance_ = NULL;
+ }
+}
+
+DevToolsNetLogObserver* DevToolsNetLogObserver::GetInstance() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ return instance_;
+}
+
+// static
+void DevToolsNetLogObserver::PopulateResponseInfo(
+ net::URLRequest* request,
+ ResourceResponse* response) {
+ if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS))
+ return;
+
+ uint32 source_id = request->net_log().source().id;
+ DevToolsNetLogObserver* dev_tools_net_log_observer =
+ DevToolsNetLogObserver::GetInstance();
+ if (dev_tools_net_log_observer == NULL)
+ return;
+ response->head.devtools_info =
+ dev_tools_net_log_observer->GetResourceInfo(source_id);
+}
+
+// static
+int DevToolsNetLogObserver::GetAndResetEncodedDataLength(
+ net::URLRequest* request) {
+ if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS))
+ return -1;
+
+ uint32 source_id = request->net_log().source().id;
+ DevToolsNetLogObserver* dev_tools_net_log_observer =
+ DevToolsNetLogObserver::GetInstance();
+ if (dev_tools_net_log_observer == NULL)
+ return -1;
+
+ RequestToEncodedDataLengthMap::iterator it =
+ dev_tools_net_log_observer->request_to_encoded_data_length_.find(
+ source_id);
+ if (it == dev_tools_net_log_observer->request_to_encoded_data_length_.end())
+ return -1;
+ int encoded_data_length = it->second;
+ it->second = 0;
+ return encoded_data_length;
+}
+
+} // namespace content
diff --git a/content/browser/devtools/devtools_netlog_observer.h b/content/browser/devtools/devtools_netlog_observer.h
new file mode 100644
index 0000000..4e74edd
--- /dev/null
+++ b/content/browser/devtools/devtools_netlog_observer.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_NETLOG_OBSERVER_H_
+#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_NETLOG_OBSERVER_H_
+
+#include "base/hash_tables.h"
+#include "base/memory/ref_counted.h"
+#include "net/base/net_log.h"
+#include "webkit/glue/resource_loader_bridge.h"
+
+namespace net {
+class URLRequest;
+} // namespace net
+
+namespace content {
+struct ResourceResponse;
+
+// DevToolsNetLogObserver watches the NetLog event stream and collects the
+// stuff that may be of interest to DevTools. Currently, this only includes
+// actual HTTP/SPDY headers sent and received over the network.
+//
+// As DevToolsNetLogObserver shares live data with objects that live on the
+// IO Thread, it must also reside on the IO Thread. Only OnAddEntry can be
+// called from other threads.
+class DevToolsNetLogObserver : public net::NetLog::ThreadSafeObserver {
+ typedef webkit_glue::ResourceDevToolsInfo ResourceInfo;
+
+ public:
+ // net::NetLog::ThreadSafeObserver implementation:
+ virtual void OnAddEntry(const net::NetLog::Entry& entry) OVERRIDE;
+
+ void OnAddURLRequestEntry(const net::NetLog::Entry& entry);
+ void OnAddHTTPStreamJobEntry(const net::NetLog::Entry& entry);
+ void OnAddSocketEntry(const net::NetLog::Entry& entry);
+
+ static void Attach();
+ static void Detach();
+
+ // Must be called on the IO thread. May return NULL if no observers
+ // are active.
+ static DevToolsNetLogObserver* GetInstance();
+ static void PopulateResponseInfo(net::URLRequest*,
+ ResourceResponse*);
+ static int GetAndResetEncodedDataLength(net::URLRequest* request);
+
+ private:
+ static DevToolsNetLogObserver* instance_;
+
+ DevToolsNetLogObserver();
+ virtual ~DevToolsNetLogObserver();
+
+ ResourceInfo* GetResourceInfo(uint32 id);
+
+ typedef base::hash_map<uint32, scoped_refptr<ResourceInfo> > RequestToInfoMap;
+ typedef base::hash_map<uint32, int> RequestToEncodedDataLengthMap;
+ typedef base::hash_map<uint32, uint32> HTTPStreamJobToSocketMap;
+ typedef base::hash_map<uint32, uint32> SocketToRequestMap;
+ RequestToInfoMap request_to_info_;
+ RequestToEncodedDataLengthMap request_to_encoded_data_length_;
+ HTTPStreamJobToSocketMap http_stream_job_to_socket_;
+ SocketToRequestMap socket_to_request_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsNetLogObserver);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_NETLOG_OBSERVER_H_
diff --git a/content/browser/devtools/devtools_resources.gyp b/content/browser/devtools/devtools_resources.gyp
new file mode 100644
index 0000000..9ee2076
--- /dev/null
+++ b/content/browser/devtools/devtools_resources.gyp
@@ -0,0 +1,58 @@
+# Copyright (c) 2012 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.
+
+{
+ 'variables': {
+ 'conditions': [
+ ['inside_chromium_build==0', {
+ 'webkit_src_dir': '../../../../../..',
+ },{
+ 'webkit_src_dir': '../../../third_party/WebKit',
+ }],
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'devtools_resources',
+ 'type': 'none',
+ 'dependencies': [
+ '<(webkit_src_dir)/Source/WebKit/chromium/WebKit.gyp:generate_devtools_grd',
+ ],
+ 'variables': {
+ 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/webkit',
+ },
+ 'actions': [
+ {
+ 'action_name': 'devtools_resources',
+ # This can't use build/grit_action.gypi because the grd file
+ # is generated at build time, so the trick of using grit_info to get
+ # the real inputs/outputs at GYP time isn't possible.
+ 'variables': {
+ 'grit_cmd': ['python', '../../../tools/grit/grit.py'],
+ 'grit_grd_file': '<(SHARED_INTERMEDIATE_DIR)/devtools/devtools_resources.grd',
+ },
+ 'inputs': [
+ '<(grit_grd_file)',
+ '<!@pymod_do_main(grit_info --inputs)',
+ ],
+ 'outputs': [
+ '<(grit_out_dir)/grit/devtools_resources.h',
+ '<(grit_out_dir)/devtools_resources.pak',
+ '<(grit_out_dir)/grit/devtools_resources_map.cc',
+ '<(grit_out_dir)/grit/devtools_resources_map.h',
+ ],
+ 'action': ['<@(grit_cmd)',
+ '-i', '<(grit_grd_file)', 'build',
+ '-f', 'GRIT_DIR/../gritsettings/resource_ids',
+ '-o', '<(grit_out_dir)',
+ '-D', 'SHARED_INTERMEDIATE_DIR=<(SHARED_INTERMEDIATE_DIR)',
+ '<@(grit_defines)' ],
+ 'message': 'Generating resources from <(grit_grd_file)',
+ 'msvs_cygwin_shell': 1,
+ }
+ ],
+ 'includes': [ '../../../build/grit_target.gypi' ],
+ },
+ ],
+}
diff --git a/content/browser/devtools/devtools_tracing_handler.cc b/content/browser/devtools/devtools_tracing_handler.cc
new file mode 100644
index 0000000..5514452
--- /dev/null
+++ b/content/browser/devtools/devtools_tracing_handler.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2012 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 "content/browser/devtools/devtools_tracing_handler.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/values.h"
+#include "content/browser/devtools/devtools_http_handler_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/trace_controller.h"
+#include "content/public/browser/trace_subscriber.h"
+
+namespace content {
+
+DevToolsTracingHandler::DevToolsTracingHandler()
+ : has_completed_(false),
+ buffer_data_size_(0) {
+}
+
+DevToolsTracingHandler::~DevToolsTracingHandler() {
+}
+
+std::string DevToolsTracingHandler::Domain() {
+ return "Tracing";
+}
+
+void DevToolsTracingHandler::OnEndTracingComplete() {
+ has_completed_ = true;
+}
+
+void DevToolsTracingHandler::OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& trace_fragment) {
+ buffer_.push_back(trace_fragment->data());
+ buffer_data_size_ += trace_fragment->data().size();
+}
+
+base::Value* DevToolsTracingHandler::OnProtocolCommand(
+ const std::string& method,
+ const base::DictionaryValue* params,
+ base::Value** error_out) {
+ if (method == "Tracing.start")
+ return Start(params);
+ else if (method == "Tracing.end")
+ return End(params);
+ else if (method == "Tracing.hasCompleted")
+ return HasCompleted(params);
+ else if (method == "Tracing.getTraceAndReset")
+ return GetTraceAndReset(params);
+
+ base::DictionaryValue* error_object = new base::DictionaryValue();
+ error_object->SetInteger("code", -1);
+ error_object->SetString("message", "Invalid method");
+
+ *error_out = error_object;
+
+ return NULL;
+}
+
+base::Value* DevToolsTracingHandler::Start(
+ const base::DictionaryValue* params) {
+ std::string categories;
+ if (params && params->HasKey("categories"))
+ params->GetString("categories", &categories);
+ TraceController::GetInstance()->BeginTracing(this, categories);
+
+ return base::Value::CreateBooleanValue(true);
+}
+
+base::Value* DevToolsTracingHandler::End(
+ const base::DictionaryValue* /* params */) {
+ TraceController::GetInstance()->EndTracingAsync(this);
+
+ return base::Value::CreateBooleanValue(true);
+}
+
+
+base::Value* DevToolsTracingHandler::HasCompleted(
+ const base::DictionaryValue* /* params */) {
+
+ return base::Value::CreateBooleanValue(has_completed_);
+}
+
+base::Value* DevToolsTracingHandler::GetTraceAndReset(
+ const base::DictionaryValue* /* params */) {
+ std::string ret;
+ ret.reserve(buffer_data_size_);
+ for (std::vector<std::string>::const_iterator i = buffer_.begin();
+ i != buffer_.end(); ++i) {
+ if (!ret.empty())
+ ret.append(",");
+ ret.append(*i);
+ }
+ buffer_.clear();
+ has_completed_ = false;
+ buffer_data_size_ = 0;
+
+ return base::Value::CreateStringValue(ret);
+}
+
+} // namespace content
diff --git a/content/browser/devtools/devtools_tracing_handler.h b/content/browser/devtools/devtools_tracing_handler.h
new file mode 100644
index 0000000..c01896a
--- /dev/null
+++ b/content/browser/devtools/devtools_tracing_handler.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_TRACING_HANDLER_H_
+#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_TRACING_HANDLER_H_
+
+#include "content/browser/devtools/devtools_browser_target.h"
+#include "content/public/browser/trace_subscriber.h"
+
+namespace content {
+
+// This class bridges DevTools remote debugging server with the trace
+// infrastructure.
+class DevToolsTracingHandler
+ : public TraceSubscriber,
+ public DevToolsBrowserTarget::Handler {
+ public:
+ DevToolsTracingHandler();
+ virtual ~DevToolsTracingHandler();
+
+ // TraceSubscriber:
+ virtual void OnEndTracingComplete() OVERRIDE;;
+ virtual void OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& trace_fragment) OVERRIDE;
+
+ // DevToolBrowserTarget::Handler:
+ virtual std::string Domain() OVERRIDE;
+ virtual base::Value* OnProtocolCommand(
+ const std::string& method,
+ const base::DictionaryValue* params,
+ base::Value** error_out) OVERRIDE;
+
+ private:
+ base::Value* Start(const base::DictionaryValue* params);
+ base::Value* End(const base::DictionaryValue* params);
+ base::Value* HasCompleted(const base::DictionaryValue* params);
+ base::Value* GetTraceAndReset(const base::DictionaryValue* params);
+
+ bool has_completed_;
+
+ std::vector<std::string> buffer_;
+ int buffer_data_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsTracingHandler);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_TRACING_HANDLER_H_
diff --git a/content/browser/devtools/render_view_devtools_agent_host.cc b/content/browser/devtools/render_view_devtools_agent_host.cc
new file mode 100644
index 0000000..4de7545
--- /dev/null
+++ b/content/browser/devtools/render_view_devtools_agent_host.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2012 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 "content/browser/devtools/render_view_devtools_agent_host.h"
+
+#include "base/basictypes.h"
+#include "base/lazy_instance.h"
+#include "content/browser/devtools/devtools_manager_impl.h"
+#include "content/browser/devtools/render_view_devtools_agent_host.h"
+#include "content/browser/renderer_host/render_process_host_impl.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/site_instance_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/devtools_messages.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/devtools_agent_host_registry.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+
+namespace content {
+
+typedef std::map<RenderViewHost*, RenderViewDevToolsAgentHost*> Instances;
+
+namespace {
+base::LazyInstance<Instances>::Leaky g_instances = LAZY_INSTANCE_INITIALIZER;
+} // namespace
+
+// static
+DevToolsAgentHost* DevToolsAgentHostRegistry::GetDevToolsAgentHost(
+ RenderViewHost* rvh) {
+ Instances::iterator it = g_instances.Get().find(rvh);
+ if (it != g_instances.Get().end())
+ return it->second;
+ return new RenderViewDevToolsAgentHost(rvh);
+}
+
+// static
+RenderViewHost* DevToolsAgentHostRegistry::GetRenderViewHost(
+ DevToolsAgentHost* agent_host) {
+ for (Instances::iterator it = g_instances.Get().begin();
+ it != g_instances.Get().end(); ++it) {
+ if (agent_host == it->second)
+ return it->first;
+ }
+ return NULL;
+}
+
+// static
+bool DevToolsAgentHostRegistry::HasDevToolsAgentHost(RenderViewHost* rvh) {
+ if (g_instances == NULL)
+ return false;
+ Instances::iterator it = g_instances.Get().find(rvh);
+ return it != g_instances.Get().end();
+}
+
+bool DevToolsAgentHostRegistry::IsDebuggerAttached(WebContents* web_contents) {
+ if (g_instances == NULL)
+ return false;
+ DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
+ if (!devtools_manager)
+ return false;
+ RenderViewHostDelegate* delegate =
+ static_cast<WebContentsImpl*>(web_contents);
+ for (Instances::iterator it = g_instances.Get().begin();
+ it != g_instances.Get().end(); ++it) {
+ if (it->first->GetDelegate() != delegate)
+ continue;
+ if (devtools_manager->GetDevToolsClientHostFor(it->second))
+ return true;
+ }
+ return false;
+}
+
+RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(
+ RenderViewHost* rvh)
+ : RenderViewHostObserver(rvh),
+ render_view_host_(rvh) {
+ g_instances.Get()[rvh] = this;
+}
+
+void RenderViewDevToolsAgentHost::SendMessageToAgent(IPC::Message* msg) {
+ msg->set_routing_id(render_view_host_->GetRoutingID());
+ render_view_host_->Send(msg);
+}
+
+void RenderViewDevToolsAgentHost::NotifyClientAttaching() {
+ NotificationService::current()->Notify(
+ NOTIFICATION_DEVTOOLS_AGENT_ATTACHED,
+ Source<BrowserContext>(
+ render_view_host_->GetSiteInstance()->GetProcess()->
+ GetBrowserContext()),
+ Details<RenderViewHost>(render_view_host_));
+}
+
+void RenderViewDevToolsAgentHost::NotifyClientDetaching() {
+ NotificationService::current()->Notify(
+ NOTIFICATION_DEVTOOLS_AGENT_DETACHED,
+ Source<BrowserContext>(
+ render_view_host_->GetSiteInstance()->GetProcess()->
+ GetBrowserContext()),
+ Details<RenderViewHost>(render_view_host_));
+}
+
+int RenderViewDevToolsAgentHost::GetRenderProcessId() {
+ return render_view_host_->GetProcess()->GetID();
+}
+
+RenderViewDevToolsAgentHost::~RenderViewDevToolsAgentHost() {
+ g_instances.Get().erase(render_view_host_);
+}
+
+void RenderViewDevToolsAgentHost::RenderViewHostDestroyed(
+ RenderViewHost* rvh) {
+ NotifyCloseListener();
+ delete this;
+}
+
+bool RenderViewDevToolsAgentHost::OnMessageReceived(
+ const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost, message)
+ IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
+ OnDispatchOnInspectorFrontend)
+ IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState,
+ OnSaveAgentRuntimeState)
+ IPC_MESSAGE_HANDLER(DevToolsHostMsg_ClearBrowserCache, OnClearBrowserCache)
+ IPC_MESSAGE_HANDLER(DevToolsHostMsg_ClearBrowserCookies,
+ OnClearBrowserCookies)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState(
+ const std::string& state) {
+ DevToolsManagerImpl::GetInstance()->SaveAgentRuntimeState(this, state);
+}
+
+void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend(
+ const std::string& message) {
+ DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
+ this, message);
+}
+
+void RenderViewDevToolsAgentHost::OnClearBrowserCache() {
+ GetContentClient()->browser()->ClearCache(render_view_host_);
+}
+
+void RenderViewDevToolsAgentHost::OnClearBrowserCookies() {
+ GetContentClient()->browser()->ClearCookies(render_view_host_);
+}
+
+} // namespace content
diff --git a/content/browser/devtools/render_view_devtools_agent_host.h b/content/browser/devtools/render_view_devtools_agent_host.h
new file mode 100644
index 0000000..afd3173
--- /dev/null
+++ b/content/browser/devtools/render_view_devtools_agent_host.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_DEVTOOLS_RENDER_VIEW_DEVTOOLS_AGENT_HOST_H_
+#define CONTENT_BROWSER_DEVTOOLS_RENDER_VIEW_DEVTOOLS_AGENT_HOST_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "content/browser/devtools/devtools_agent_host.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/render_view_host_observer.h"
+
+namespace content {
+
+class RenderViewHost;
+
+class CONTENT_EXPORT RenderViewDevToolsAgentHost
+ : public DevToolsAgentHost,
+ private RenderViewHostObserver {
+ public:
+ RenderViewDevToolsAgentHost(RenderViewHost*);
+
+ private:
+ virtual ~RenderViewDevToolsAgentHost();
+
+ // DevToolsAgentHost implementation.
+ virtual void SendMessageToAgent(IPC::Message* msg) OVERRIDE;
+ virtual void NotifyClientAttaching() OVERRIDE;
+ virtual void NotifyClientDetaching() OVERRIDE;
+ virtual int GetRenderProcessId() OVERRIDE;
+
+ // RenderViewHostObserver overrides.
+ virtual void RenderViewHostDestroyed(RenderViewHost* rvh) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ void OnDispatchOnInspectorFrontend(const std::string& message);
+ void OnSaveAgentRuntimeState(const std::string& state);
+ void OnClearBrowserCache();
+ void OnClearBrowserCookies();
+
+ RenderViewHost* render_view_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderViewDevToolsAgentHost);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_DEVTOOLS_RENDER_VIEW_DEVTOOLS_AGENT_HOST_H_
diff --git a/content/browser/devtools/worker_devtools_manager.cc b/content/browser/devtools/worker_devtools_manager.cc
new file mode 100644
index 0000000..f341c82
--- /dev/null
+++ b/content/browser/devtools/worker_devtools_manager.cc
@@ -0,0 +1,472 @@
+// Copyright (c) 2011 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 "content/browser/devtools/worker_devtools_manager.h"
+
+#include <list>
+#include <map>
+
+#include "base/bind.h"
+#include "content/browser/devtools/devtools_agent_host.h"
+#include "content/browser/devtools/devtools_manager_impl.h"
+#include "content/browser/devtools/worker_devtools_message_filter.h"
+#include "content/browser/worker_host/worker_service_impl.h"
+#include "content/common/devtools_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/child_process_data.h"
+#include "content/public/browser/devtools_agent_host_registry.h"
+#include "content/public/common/process_type.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDevToolsAgent.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCString.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
+
+namespace content {
+
+// Called on the UI thread.
+// static
+DevToolsAgentHost* DevToolsAgentHostRegistry::GetDevToolsAgentHostForWorker(
+ int worker_process_id,
+ int worker_route_id) {
+ return WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
+ worker_process_id,
+ worker_route_id);
+}
+
+class WorkerDevToolsManager::AgentHosts {
+public:
+ static void Add(WorkerId id, WorkerDevToolsAgentHost* host) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (!instance_)
+ instance_ = new AgentHosts();
+ instance_->map_[id] = host;
+ }
+ static void Remove(WorkerId id) {
+ DCHECK(instance_);
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ Instances& map = instance_->map_;
+ map.erase(id);
+ if (map.empty()) {
+ delete instance_;
+ instance_ = NULL;
+ }
+ }
+
+ static WorkerDevToolsAgentHost* GetAgentHost(WorkerId id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (!instance_)
+ return NULL;
+ Instances& map = instance_->map_;
+ Instances::iterator it = map.find(id);
+ if (it == map.end())
+ return NULL;
+ return it->second;
+ }
+
+private:
+ AgentHosts() {
+ }
+ ~AgentHosts() {}
+
+ static AgentHosts* instance_;
+ typedef std::map<WorkerId, WorkerDevToolsAgentHost*> Instances;
+ Instances map_;
+};
+
+WorkerDevToolsManager::AgentHosts*
+ WorkerDevToolsManager::AgentHosts::instance_ = NULL;
+
+
+struct WorkerDevToolsManager::TerminatedInspectedWorker {
+ TerminatedInspectedWorker(WorkerId id, const GURL& url, const string16& name)
+ : old_worker_id(id),
+ worker_url(url),
+ worker_name(name) {}
+ WorkerId old_worker_id;
+ GURL worker_url;
+ string16 worker_name;
+};
+
+
+class WorkerDevToolsManager::WorkerDevToolsAgentHost
+ : public DevToolsAgentHost {
+ public:
+ explicit WorkerDevToolsAgentHost(WorkerId worker_id)
+ : worker_id_(worker_id) {
+ AgentHosts::Add(worker_id, this);
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(
+ &RegisterAgent,
+ worker_id.first,
+ worker_id.second));
+ }
+
+ void WorkerDestroyed() {
+ NotifyCloseListener();
+ delete this;
+ }
+
+ private:
+ virtual ~WorkerDevToolsAgentHost() {
+ AgentHosts::Remove(worker_id_);
+ }
+
+ static void RegisterAgent(
+ int worker_process_id,
+ int worker_route_id) {
+ WorkerDevToolsManager::GetInstance()->RegisterDevToolsAgentHostForWorker(
+ worker_process_id, worker_route_id);
+ }
+
+ static void ForwardToWorkerDevToolsAgent(
+ int worker_process_id,
+ int worker_route_id,
+ IPC::Message* message) {
+ WorkerDevToolsManager::GetInstance()->ForwardToWorkerDevToolsAgent(
+ worker_process_id, worker_route_id, *message);
+ }
+
+ // DevToolsAgentHost implementation.
+ virtual void SendMessageToAgent(IPC::Message* message) OVERRIDE {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(
+ &WorkerDevToolsAgentHost::ForwardToWorkerDevToolsAgent,
+ worker_id_.first,
+ worker_id_.second,
+ base::Owned(message)));
+ }
+ virtual void NotifyClientAttaching() OVERRIDE {}
+ virtual void NotifyClientDetaching() OVERRIDE {}
+ virtual int GetRenderProcessId() OVERRIDE { return -1; }
+
+ WorkerId worker_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkerDevToolsAgentHost);
+};
+
+
+class WorkerDevToolsManager::DetachedClientHosts {
+ public:
+ static void WorkerReloaded(WorkerId old_id, WorkerId new_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (instance_ && instance_->ReattachClient(old_id, new_id))
+ return;
+ RemovePendingWorkerData(old_id);
+ }
+
+ static void WorkerDestroyed(WorkerId id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ WorkerDevToolsAgentHost* agent = AgentHosts::GetAgentHost(id);
+ if (!agent) {
+ RemovePendingWorkerData(id);
+ return;
+ }
+ DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
+ agent,
+ WebKit::WebDevToolsAgent::workerDisconnectedFromWorkerEvent().utf8());
+ int cookie = DevToolsManagerImpl::GetInstance()->DetachClientHost(agent);
+ agent->WorkerDestroyed();
+ if (cookie == -1) {
+ RemovePendingWorkerData(id);
+ return;
+ }
+ if (!instance_)
+ new DetachedClientHosts();
+ instance_->worker_id_to_cookie_[id] = cookie;
+ }
+
+ private:
+ DetachedClientHosts() {
+ instance_ = this;
+ }
+ ~DetachedClientHosts() {
+ instance_ = NULL;
+ }
+
+ bool ReattachClient(WorkerId old_id, WorkerId new_id) {
+ WorkerIdToCookieMap::iterator it = worker_id_to_cookie_.find(old_id);
+ if (it == worker_id_to_cookie_.end())
+ return false;
+ DevToolsAgentHost* agent =
+ WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
+ new_id.first,
+ new_id.second);
+ DevToolsManagerImpl::GetInstance()->AttachClientHost(
+ it->second,
+ agent);
+ worker_id_to_cookie_.erase(it);
+ if (worker_id_to_cookie_.empty())
+ delete this;
+ return true;
+ }
+
+ static void RemovePendingWorkerData(WorkerId id) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&RemoveInspectedWorkerDataOnIOThread, id));
+ }
+
+ static void RemoveInspectedWorkerDataOnIOThread(WorkerId id) {
+ WorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(id);
+ }
+
+ static DetachedClientHosts* instance_;
+ typedef std::map<WorkerId, int> WorkerIdToCookieMap;
+ WorkerIdToCookieMap worker_id_to_cookie_;
+};
+
+WorkerDevToolsManager::DetachedClientHosts*
+ WorkerDevToolsManager::DetachedClientHosts::instance_ = NULL;
+
+struct WorkerDevToolsManager::InspectedWorker {
+ InspectedWorker(WorkerProcessHost* host, int route_id, const GURL& url,
+ const string16& name)
+ : host(host),
+ route_id(route_id),
+ worker_url(url),
+ worker_name(name) {}
+ WorkerProcessHost* const host;
+ int const route_id;
+ GURL worker_url;
+ string16 worker_name;
+};
+
+// static
+WorkerDevToolsManager* WorkerDevToolsManager::GetInstance() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ return Singleton<WorkerDevToolsManager>::get();
+}
+
+// static
+DevToolsAgentHost* WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
+ int worker_process_id,
+ int worker_route_id) {
+ WorkerId id(worker_process_id, worker_route_id);
+ WorkerDevToolsAgentHost* result = AgentHosts::GetAgentHost(id);
+ if (!result)
+ result = new WorkerDevToolsAgentHost(id);
+ return result;
+}
+
+WorkerDevToolsManager::WorkerDevToolsManager() {
+}
+
+WorkerDevToolsManager::~WorkerDevToolsManager() {
+}
+
+void WorkerDevToolsManager::WorkerCreated(
+ WorkerProcessHost* worker,
+ const WorkerProcessHost::WorkerInstance& instance) {
+ for (TerminatedInspectedWorkers::iterator it = terminated_workers_.begin();
+ it != terminated_workers_.end(); ++it) {
+ if (instance.Matches(it->worker_url, it->worker_name,
+ instance.partition(),
+ instance.resource_context())) {
+ worker->Send(new DevToolsAgentMsg_PauseWorkerContextOnStart(
+ instance.worker_route_id()));
+ WorkerId new_worker_id(worker->GetData().id, instance.worker_route_id());
+ paused_workers_[new_worker_id] = it->old_worker_id;
+ terminated_workers_.erase(it);
+ return;
+ }
+ }
+}
+
+void WorkerDevToolsManager::WorkerDestroyed(
+ WorkerProcessHost* worker,
+ int worker_route_id) {
+ InspectedWorkersList::iterator it = FindInspectedWorker(
+ worker->GetData().id,
+ worker_route_id);
+ if (it == inspected_workers_.end())
+ return;
+
+ WorkerId worker_id(worker->GetData().id, worker_route_id);
+ terminated_workers_.push_back(TerminatedInspectedWorker(
+ worker_id,
+ it->worker_url,
+ it->worker_name));
+ inspected_workers_.erase(it);
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&DetachedClientHosts::WorkerDestroyed, worker_id));
+}
+
+void WorkerDevToolsManager::WorkerContextStarted(WorkerProcessHost* process,
+ int worker_route_id) {
+ WorkerId new_worker_id(process->GetData().id, worker_route_id);
+ PausedWorkers::iterator it = paused_workers_.find(new_worker_id);
+ if (it == paused_workers_.end())
+ return;
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(
+ &DetachedClientHosts::WorkerReloaded,
+ it->second,
+ new_worker_id));
+ paused_workers_.erase(it);
+}
+
+void WorkerDevToolsManager::RemoveInspectedWorkerData(
+ const WorkerId& id) {
+ for (TerminatedInspectedWorkers::iterator it = terminated_workers_.begin();
+ it != terminated_workers_.end(); ++it) {
+ if (it->old_worker_id == id) {
+ terminated_workers_.erase(it);
+ return;
+ }
+ }
+
+ for (PausedWorkers::iterator it = paused_workers_.begin();
+ it != paused_workers_.end(); ++it) {
+ if (it->second == id) {
+ SendResumeToWorker(it->first);
+ paused_workers_.erase(it);
+ return;
+ }
+ }
+}
+
+WorkerDevToolsManager::InspectedWorkersList::iterator
+WorkerDevToolsManager::FindInspectedWorker(
+ int host_id, int route_id) {
+ InspectedWorkersList::iterator it = inspected_workers_.begin();
+ while (it != inspected_workers_.end()) {
+ if (it->host->GetData().id == host_id && it->route_id == route_id)
+ break;
+ ++it;
+ }
+ return it;
+}
+
+static WorkerProcessHost* FindWorkerProcess(int worker_process_id) {
+ for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
+ if (iter.GetData().id == worker_process_id)
+ return *iter;
+ }
+ return NULL;
+}
+
+void WorkerDevToolsManager::RegisterDevToolsAgentHostForWorker(
+ int worker_process_id,
+ int worker_route_id) {
+ if (WorkerProcessHost* process = FindWorkerProcess(worker_process_id)) {
+ const WorkerProcessHost::Instances& instances = process->instances();
+ for (WorkerProcessHost::Instances::const_iterator i = instances.begin();
+ i != instances.end(); ++i) {
+ if (i->worker_route_id() == worker_route_id) {
+ DCHECK(FindInspectedWorker(worker_process_id, worker_route_id) ==
+ inspected_workers_.end());
+ inspected_workers_.push_back(
+ InspectedWorker(process, worker_route_id, i->url(), i->name()));
+ return;
+ }
+ }
+ }
+ NotifyWorkerDestroyedOnIOThread(worker_process_id, worker_route_id);
+}
+
+void WorkerDevToolsManager::ForwardToDevToolsClient(
+ int worker_process_id,
+ int worker_route_id,
+ const std::string& message) {
+ if (FindInspectedWorker(worker_process_id, worker_route_id) ==
+ inspected_workers_.end()) {
+ NOTREACHED();
+ return;
+ }
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(
+ &ForwardToDevToolsClientOnUIThread,
+ worker_process_id,
+ worker_route_id,
+ message));
+}
+
+void WorkerDevToolsManager::SaveAgentRuntimeState(int worker_process_id,
+ int worker_route_id,
+ const std::string& state) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(
+ &SaveAgentRuntimeStateOnUIThread,
+ worker_process_id,
+ worker_route_id,
+ state));
+}
+
+void WorkerDevToolsManager::ForwardToWorkerDevToolsAgent(
+ int worker_process_id,
+ int worker_route_id,
+ const IPC::Message& message) {
+ InspectedWorkersList::iterator it = FindInspectedWorker(
+ worker_process_id,
+ worker_route_id);
+ if (it == inspected_workers_.end())
+ return;
+ IPC::Message* msg = new IPC::Message(message);
+ msg->set_routing_id(worker_route_id);
+ it->host->Send(msg);
+}
+
+// static
+void WorkerDevToolsManager::ForwardToDevToolsClientOnUIThread(
+ int worker_process_id,
+ int worker_route_id,
+ const std::string& message) {
+ WorkerDevToolsAgentHost* agent_host = AgentHosts::GetAgentHost(WorkerId(
+ worker_process_id,
+ worker_route_id));
+ if (!agent_host)
+ return;
+ DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(agent_host,
+ message);
+}
+
+// static
+void WorkerDevToolsManager::SaveAgentRuntimeStateOnUIThread(
+ int worker_process_id,
+ int worker_route_id,
+ const std::string& state) {
+ WorkerDevToolsAgentHost* agent_host = AgentHosts::GetAgentHost(WorkerId(
+ worker_process_id,
+ worker_route_id));
+ if (!agent_host)
+ return;
+ DevToolsManagerImpl::GetInstance()->SaveAgentRuntimeState(agent_host, state);
+}
+
+// static
+void WorkerDevToolsManager::NotifyWorkerDestroyedOnIOThread(
+ int worker_process_id,
+ int worker_route_id) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(
+ &WorkerDevToolsManager::NotifyWorkerDestroyedOnUIThread,
+ worker_process_id,
+ worker_route_id));
+}
+
+// static
+void WorkerDevToolsManager::NotifyWorkerDestroyedOnUIThread(
+ int worker_process_id,
+ int worker_route_id) {
+ WorkerDevToolsAgentHost* host =
+ AgentHosts::GetAgentHost(WorkerId(worker_process_id, worker_route_id));
+ if (host)
+ host->WorkerDestroyed();
+}
+
+// static
+void WorkerDevToolsManager::SendResumeToWorker(const WorkerId& id) {
+ if (WorkerProcessHost* process = FindWorkerProcess(id.first))
+ process->Send(new DevToolsAgentMsg_ResumeWorkerContext(id.second));
+}
+
+} // namespace content
diff --git a/content/browser/devtools/worker_devtools_manager.h b/content/browser/devtools/worker_devtools_manager.h
new file mode 100644
index 0000000..13ef33b
--- /dev/null
+++ b/content/browser/devtools/worker_devtools_manager.h
@@ -0,0 +1,109 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_DEVTOOLS_WORKER_DEVTOOLS_MANAGER_H_
+#define CONTENT_BROWSER_DEVTOOLS_WORKER_DEVTOOLS_MANAGER_H_
+
+#include <list>
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/singleton.h"
+#include "content/browser/worker_host/worker_process_host.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class DevToolsAgentHost;
+
+// All methods are supposed to be called on the IO thread.
+class WorkerDevToolsManager {
+ public:
+ // Returns the WorkerDevToolsManager singleton.
+ static WorkerDevToolsManager* GetInstance();
+
+ // Called on the UI thread.
+ static DevToolsAgentHost* GetDevToolsAgentHostForWorker(
+ int worker_process_id,
+ int worker_route_id);
+
+ void ForwardToDevToolsClient(int worker_process_id,
+ int worker_route_id,
+ const std::string& message);
+ void SaveAgentRuntimeState(int worker_process_id,
+ int worker_route_id,
+ const std::string& state);
+
+ // Called on the IO thread.
+ void WorkerCreated(
+ WorkerProcessHost* process,
+ const WorkerProcessHost::WorkerInstance& instance);
+ void WorkerDestroyed(WorkerProcessHost* process, int worker_route_id);
+ void WorkerContextStarted(WorkerProcessHost* process, int worker_route_id);
+
+ private:
+ friend struct DefaultSingletonTraits<WorkerDevToolsManager>;
+ typedef std::pair<int, int> WorkerId;
+ class AgentHosts;
+ class DetachedClientHosts;
+ class WorkerDevToolsAgentHost;
+ struct InspectedWorker;
+ typedef std::list<InspectedWorker> InspectedWorkersList;
+
+ WorkerDevToolsManager();
+ virtual ~WorkerDevToolsManager();
+
+ void RemoveInspectedWorkerData(const WorkerId& id);
+ InspectedWorkersList::iterator FindInspectedWorker(int host_id, int route_id);
+
+ void RegisterDevToolsAgentHostForWorker(int worker_process_id,
+ int worker_route_id);
+ void ForwardToWorkerDevToolsAgent(int worker_process_host_id,
+ int worker_route_id,
+ const IPC::Message& message);
+ static void ForwardToDevToolsClientOnUIThread(
+ int worker_process_id,
+ int worker_route_id,
+ const std::string& message);
+ static void SaveAgentRuntimeStateOnUIThread(
+ int worker_process_id,
+ int worker_route_id,
+ const std::string& state);
+ static void NotifyWorkerDestroyedOnIOThread(int worker_process_id,
+ int worker_route_id);
+ static void NotifyWorkerDestroyedOnUIThread(int worker_process_id,
+ int worker_route_id);
+ static void SendResumeToWorker(const WorkerId& id);
+
+ InspectedWorkersList inspected_workers_;
+
+ struct TerminatedInspectedWorker;
+ typedef std::list<TerminatedInspectedWorker> TerminatedInspectedWorkers;
+ // List of terminated workers for which there may be a devtools client on
+ // the UI thread. Worker entry is added into this list when inspected worker
+ // is terminated and will be removed in one of two cases:
+ // - shared worker with the same URL and name is started(in wich case we will
+ // try to reattach existing DevTools client to the new worker).
+ // - DevTools client which was inspecting terminated worker is closed on the
+ // UI thread and and WorkerDevToolsManager is notified about that on the IO
+ // thread.
+ TerminatedInspectedWorkers terminated_workers_;
+
+ typedef std::map<WorkerId, WorkerId> PausedWorkers;
+ // Map from old to new worker id for the inspected workers that have been
+ // terminated and started again in paused state. Worker data will be removed
+ // from this list in one of two cases:
+ // - DevTools client is closed on the UI thread, WorkerDevToolsManager was
+ // notified about that on the IO thread and sent "resume" message to the
+ // worker.
+ // - Existing DevTools client was reattached to the new worker.
+ PausedWorkers paused_workers_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkerDevToolsManager);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_DEVTOOLS_WORKER_DEVTOOLS_MANAGER_H_
diff --git a/content/browser/devtools/worker_devtools_message_filter.cc b/content/browser/devtools/worker_devtools_message_filter.cc
new file mode 100644
index 0000000..3f5553f1
--- /dev/null
+++ b/content/browser/devtools/worker_devtools_message_filter.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 2011 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 "content/browser/devtools/worker_devtools_message_filter.h"
+
+#include "content/browser/devtools/worker_devtools_manager.h"
+#include "content/common/devtools_messages.h"
+#include "content/common/worker_messages.h"
+
+namespace content {
+
+WorkerDevToolsMessageFilter::WorkerDevToolsMessageFilter(
+ int worker_process_host_id)
+ : worker_process_host_id_(worker_process_host_id),
+ current_routing_id_(0) {
+}
+
+WorkerDevToolsMessageFilter::~WorkerDevToolsMessageFilter() {
+}
+
+bool WorkerDevToolsMessageFilter::OnMessageReceived(
+ const IPC::Message& message,
+ bool* message_was_ok) {
+ bool handled = true;
+ current_routing_id_ = message.routing_id();
+ IPC_BEGIN_MESSAGE_MAP_EX(WorkerDevToolsMessageFilter, message,
+ *message_was_ok)
+ IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
+ OnDispatchOnInspectorFrontend)
+ IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState,
+ OnSaveAgentRumtimeState)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+ return handled;
+}
+
+void WorkerDevToolsMessageFilter::OnDispatchOnInspectorFrontend(
+ const std::string& message) {
+ WorkerDevToolsManager::GetInstance()->ForwardToDevToolsClient(
+ worker_process_host_id_, current_routing_id_, message);
+}
+
+void WorkerDevToolsMessageFilter::OnSaveAgentRumtimeState(
+ const std::string& state) {
+ WorkerDevToolsManager::GetInstance()->SaveAgentRuntimeState(
+ worker_process_host_id_, current_routing_id_, state);
+}
+
+} // namespace content
diff --git a/content/browser/devtools/worker_devtools_message_filter.h b/content/browser/devtools/worker_devtools_message_filter.h
new file mode 100644
index 0000000..73ba6f84
--- /dev/null
+++ b/content/browser/devtools/worker_devtools_message_filter.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_DEVTOOLS_WORKER_DEVTOOLS_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_DEVTOOLS_WORKER_DEVTOOLS_MESSAGE_FILTER_H_
+
+#include "base/callback_forward.h"
+#include "content/public/browser/browser_message_filter.h"
+
+namespace content {
+
+class WorkerDevToolsMessageFilter : public BrowserMessageFilter {
+ public:
+ explicit WorkerDevToolsMessageFilter(int worker_process_host_id);
+
+ private:
+ virtual ~WorkerDevToolsMessageFilter();
+
+ // BrowserMessageFilter implementation.
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok) OVERRIDE;
+ // Message handlers.
+ void OnDispatchOnInspectorFrontend(const std::string& message);
+ void OnSaveAgentRumtimeState(const std::string& state);
+
+ int worker_process_host_id_;
+ int current_routing_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkerDevToolsMessageFilter);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_DEVTOOLS_WORKER_DEVTOOLS_MESSAGE_FILTER_H_