summaryrefslogtreecommitdiffstats
path: root/content/browser/devtools/devtools_http_handler_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser/devtools/devtools_http_handler_impl.cc')
-rw-r--r--content/browser/devtools/devtools_http_handler_impl.cc873
1 files changed, 873 insertions, 0 deletions
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