1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/devtools/devtools_http_handler_impl.h"
10 #include "base/bind.h"
11 #include "base/compiler_specific.h"
12 #include "base/files/file_util.h"
13 #include "base/json/json_writer.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/threading/thread.h"
19 #include "base/values.h"
20 #include "content/browser/devtools/devtools_manager.h"
21 #include "content/browser/devtools/devtools_protocol.h"
22 #include "content/browser/devtools/devtools_protocol_constants.h"
23 #include "content/browser/devtools/devtools_system_info_handler.h"
24 #include "content/browser/devtools/protocol/devtools_protocol_handler_impl.h"
25 #include "content/browser/devtools/protocol/tracing_handler.h"
26 #include "content/browser/devtools/tethering_handler.h"
27 #include "content/common/devtools_messages.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/devtools_agent_host.h"
30 #include "content/public/browser/devtools_http_handler_delegate.h"
31 #include "content/public/browser/devtools_target.h"
32 #include "content/public/common/content_client.h"
33 #include "content/public/common/url_constants.h"
34 #include "content/public/common/user_agent.h"
35 #include "grit/devtools_resources_map.h"
36 #include "net/base/escape.h"
37 #include "net/base/io_buffer.h"
38 #include "net/base/ip_endpoint.h"
39 #include "net/base/net_errors.h"
40 #include "net/server/http_server_request_info.h"
41 #include "net/server/http_server_response_info.h"
42 #include "net/socket/server_socket.h"
44 #if defined(OS_ANDROID)
45 #include "base/android/build_info.h"
52 const base::FilePath::CharType kDevToolsActivePortFileName[] =
53 FILE_PATH_LITERAL("DevToolsActivePort");
55 const char kDevToolsHandlerThreadName[] = "Chrome_DevToolsHandlerThread";
57 const char kThumbUrlPrefix[] = "/thumb/";
58 const char kPageUrlPrefix[] = "/devtools/page/";
60 const char kTargetIdField[] = "id";
61 const char kTargetParentIdField[] = "parentId";
62 const char kTargetTypeField[] = "type";
63 const char kTargetTitleField[] = "title";
64 const char kTargetDescriptionField[] = "description";
65 const char kTargetUrlField[] = "url";
66 const char kTargetThumbnailUrlField[] = "thumbnailUrl";
67 const char kTargetFaviconUrlField[] = "faviconUrl";
68 const char kTargetWebSocketDebuggerUrlField[] = "webSocketDebuggerUrl";
69 const char kTargetDevtoolsFrontendUrlField[] = "devtoolsFrontendUrl";
71 // Maximum write buffer size of devtools http/websocket connectinos.
72 const int32 kSendBufferSizeForDevTools = 100 * 1024 * 1024; // 100Mb
74 // DevToolsAgentHostClientImpl -----------------------------------------------
76 // An internal implementation of DevToolsAgentHostClient that delegates
77 // messages sent to a DebuggerShell instance.
78 class DevToolsAgentHostClientImpl : public DevToolsAgentHostClient {
80 DevToolsAgentHostClientImpl(base::MessageLoop* message_loop,
81 net::HttpServer* server,
83 DevToolsAgentHost* agent_host)
84 : message_loop_(message_loop),
86 connection_id_(connection_id),
87 agent_host_(agent_host) {
88 agent_host_->AttachClient(this);
91 ~DevToolsAgentHostClientImpl() override {
92 if (agent_host_.get())
93 agent_host_->DetachClient();
96 void AgentHostClosed(DevToolsAgentHost* agent_host,
97 bool replaced_with_another_client) override {
98 DCHECK(agent_host == agent_host_.get());
101 base::DictionaryValue notification;
102 notification.SetString(
103 devtools::Inspector::detached::kParamReason,
104 replaced_with_another_client ?
105 "replaced_with_devtools" : "target_closed");
106 std::string response = DevToolsProtocol::CreateNotification(
107 devtools::Inspector::detached::kName,
108 notification.DeepCopy())->Serialize();
109 message_loop_->PostTask(
111 base::Bind(&net::HttpServer::SendOverWebSocket,
112 base::Unretained(server_),
116 message_loop_->PostTask(
118 base::Bind(&net::HttpServer::Close,
119 base::Unretained(server_),
123 void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
124 const std::string& message) override {
125 DCHECK(agent_host == agent_host_.get());
126 message_loop_->PostTask(
128 base::Bind(&net::HttpServer::SendOverWebSocket,
129 base::Unretained(server_),
134 void OnMessage(const std::string& message) {
135 if (agent_host_.get())
136 agent_host_->DispatchProtocolMessage(message);
140 base::MessageLoop* const message_loop_;
141 net::HttpServer* const server_;
142 const int connection_id_;
143 scoped_refptr<DevToolsAgentHost> agent_host_;
146 static bool TimeComparator(const DevToolsTarget* target1,
147 const DevToolsTarget* target2) {
148 return target1->GetLastActivityTime() > target2->GetLastActivityTime();
153 // DevToolsHttpHandlerImpl::BrowserTarget ------------------------------------
155 class DevToolsHttpHandlerImpl::BrowserTarget {
157 BrowserTarget(base::MessageLoop* message_loop,
158 net::HttpServer* server,
160 : message_loop_(message_loop),
162 connection_id_(connection_id),
163 tracing_handler_(new devtools::tracing::TracingHandler(
164 devtools::tracing::TracingHandler::Browser)),
165 protocol_handler_(new DevToolsProtocolHandlerImpl()) {
166 protocol_handler_->SetNotifier(
167 base::Bind(&BrowserTarget::Respond, base::Unretained(this)));
168 protocol_handler_->SetTracingHandler(tracing_handler_.get());
172 STLDeleteElements(&handlers_);
176 void RegisterHandler(DevToolsProtocol::Handler* handler) {
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
178 handler->SetNotifier(
179 base::Bind(&BrowserTarget::Respond, base::Unretained(this)));
180 handlers_.push_back(handler);
183 void HandleMessage(const std::string& message) {
184 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
185 std::string error_response;
186 scoped_refptr<DevToolsProtocol::Command> command =
187 DevToolsProtocol::ParseCommand(message, &error_response);
188 if (!command.get()) {
189 Respond(error_response);
193 scoped_refptr<DevToolsProtocol::Response> response =
194 protocol_handler_->HandleCommand(command);
195 for (const auto& handler : handlers_) {
198 response = handler->HandleCommand(command);
201 if (response.get()) {
202 if (!response->is_async_promise())
203 Respond(response->Serialize());
205 Respond(command->NoSuchMethodErrorResponse()->Serialize());
209 void Respond(const std::string& message) {
210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
211 message_loop_->PostTask(
213 base::Bind(&net::HttpServer::SendOverWebSocket,
214 base::Unretained(server_),
220 base::MessageLoop* const message_loop_;
221 net::HttpServer* const server_;
222 const int connection_id_;
223 scoped_ptr<devtools::tracing::TracingHandler> tracing_handler_;
224 scoped_ptr<DevToolsProtocolHandlerImpl> protocol_handler_;
225 std::vector<DevToolsProtocol::Handler*> handlers_;
228 // DevToolsHttpHandler -------------------------------------------------------
231 bool DevToolsHttpHandler::IsSupportedProtocolVersion(
232 const std::string& version) {
233 return devtools::IsSupportedProtocolVersion(version);
237 int DevToolsHttpHandler::GetFrontendResourceId(const std::string& name) {
238 for (size_t i = 0; i < kDevtoolsResourcesSize; ++i) {
239 if (name == kDevtoolsResources[i].name)
240 return kDevtoolsResources[i].value;
246 DevToolsHttpHandler* DevToolsHttpHandler::Start(
247 scoped_ptr<ServerSocketFactory> server_socket_factory,
248 const std::string& frontend_url,
249 DevToolsHttpHandlerDelegate* delegate,
250 const base::FilePath& active_port_output_directory) {
251 DevToolsHttpHandlerImpl* http_handler =
252 new DevToolsHttpHandlerImpl(server_socket_factory.Pass(),
255 active_port_output_directory);
256 http_handler->Start();
260 // DevToolsHttpHandler::ServerSocketFactory ----------------------------------
262 DevToolsHttpHandler::ServerSocketFactory::ServerSocketFactory(
263 const std::string& address,
271 DevToolsHttpHandler::ServerSocketFactory::~ServerSocketFactory() {
274 scoped_ptr<net::ServerSocket>
275 DevToolsHttpHandler::ServerSocketFactory::CreateAndListen() const {
276 scoped_ptr<net::ServerSocket> socket = Create();
278 socket->ListenWithAddressAndPort(address_, port_, backlog_) == net::OK) {
279 return socket.Pass();
281 return scoped_ptr<net::ServerSocket>();
284 // DevToolsHttpHandlerImpl ---------------------------------------------------
286 DevToolsHttpHandlerImpl::~DevToolsHttpHandlerImpl() {
287 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
288 // Stop() must be called prior to destruction.
289 DCHECK(server_.get() == NULL);
290 DCHECK(thread_.get() == NULL);
291 STLDeleteValues(&target_map_);
294 void DevToolsHttpHandlerImpl::Start() {
297 thread_.reset(new base::Thread(kDevToolsHandlerThreadName));
298 BrowserThread::PostTask(
299 BrowserThread::FILE, FROM_HERE,
300 base::Bind(&DevToolsHttpHandlerImpl::StartHandlerThread, this));
303 // Runs on FILE thread.
304 void DevToolsHttpHandlerImpl::StartHandlerThread() {
305 base::Thread::Options options;
306 options.message_loop_type = base::MessageLoop::TYPE_IO;
307 if (!thread_->StartWithOptions(options)) {
308 BrowserThread::PostTask(
309 BrowserThread::UI, FROM_HERE,
310 base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThread, this));
314 thread_->message_loop()->PostTask(
316 base::Bind(&DevToolsHttpHandlerImpl::Init, this));
319 void DevToolsHttpHandlerImpl::ResetHandlerThread() {
321 server_ip_address_.reset();
324 void DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease() {
325 ResetHandlerThread();
329 void DevToolsHttpHandlerImpl::Stop() {
332 BrowserThread::PostTaskAndReply(
333 BrowserThread::FILE, FROM_HERE,
334 base::Bind(&DevToolsHttpHandlerImpl::StopHandlerThread, this),
335 base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease, this));
338 void DevToolsHttpHandlerImpl::StopWithoutRelease() {
341 BrowserThread::PostTaskAndReply(
342 BrowserThread::FILE, FROM_HERE,
343 base::Bind(&DevToolsHttpHandlerImpl::StopHandlerThread, this),
344 base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThread, this));
347 GURL DevToolsHttpHandlerImpl::GetFrontendURL() {
348 if (!server_ip_address_)
350 return GURL(std::string("http://") + server_ip_address_->ToString() + frontend_url_);
353 static std::string PathWithoutParams(const std::string& path) {
354 size_t query_position = path.find("?");
355 if (query_position != std::string::npos)
356 return path.substr(0, query_position);
360 static std::string GetMimeType(const std::string& filename) {
361 if (EndsWith(filename, ".html", false)) {
363 } else if (EndsWith(filename, ".css", false)) {
365 } else if (EndsWith(filename, ".js", false)) {
366 return "application/javascript";
367 } else if (EndsWith(filename, ".png", false)) {
369 } else if (EndsWith(filename, ".gif", false)) {
371 } else if (EndsWith(filename, ".json", false)) {
372 return "application/json";
374 LOG(ERROR) << "GetMimeType doesn't know mime type for: "
376 << " text/plain will be returned";
381 void DevToolsHttpHandlerImpl::OnHttpRequest(
383 const net::HttpServerRequestInfo& info) {
384 server_->SetSendBufferSize(connection_id, kSendBufferSizeForDevTools);
386 if (info.path.find("/json") == 0) {
387 BrowserThread::PostTask(
390 base::Bind(&DevToolsHttpHandlerImpl::OnJsonRequestUI,
397 if (info.path.find(kThumbUrlPrefix) == 0) {
398 // Thumbnail request.
399 const std::string target_id = info.path.substr(strlen(kThumbUrlPrefix));
400 DevToolsTarget* target = GetTarget(target_id);
403 page_url = target->GetURL();
404 BrowserThread::PostTask(
407 base::Bind(&DevToolsHttpHandlerImpl::OnThumbnailRequestUI,
414 if (info.path == "" || info.path == "/") {
415 // Discovery page request.
416 BrowserThread::PostTask(
419 base::Bind(&DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI,
425 if (info.path.find("/devtools/") != 0) {
426 server_->Send404(connection_id);
430 std::string filename = PathWithoutParams(info.path.substr(10));
431 std::string mime_type = GetMimeType(filename);
433 base::FilePath frontend_dir = delegate_->GetDebugFrontendDir();
434 if (!frontend_dir.empty()) {
435 base::FilePath path = frontend_dir.AppendASCII(filename);
437 base::ReadFileToString(path, &data);
438 server_->Send200(connection_id, data, mime_type);
441 if (delegate_->BundlesFrontendResources()) {
442 int resource_id = DevToolsHttpHandler::GetFrontendResourceId(filename);
443 if (resource_id != -1) {
444 base::StringPiece data = GetContentClient()->GetDataResource(
445 resource_id, ui::SCALE_FACTOR_NONE);
446 server_->Send200(connection_id, data.as_string(), mime_type);
450 server_->Send404(connection_id);
453 void DevToolsHttpHandlerImpl::OnWebSocketRequest(
455 const net::HttpServerRequestInfo& request) {
456 BrowserThread::PostTask(
460 &DevToolsHttpHandlerImpl::OnWebSocketRequestUI,
466 void DevToolsHttpHandlerImpl::OnWebSocketMessage(
468 const std::string& data) {
469 BrowserThread::PostTask(
473 &DevToolsHttpHandlerImpl::OnWebSocketMessageUI,
479 void DevToolsHttpHandlerImpl::OnClose(int connection_id) {
480 BrowserThread::PostTask(
484 &DevToolsHttpHandlerImpl::OnCloseUI,
489 std::string DevToolsHttpHandlerImpl::GetFrontendURLInternal(
490 const std::string id,
491 const std::string& host) {
492 return base::StringPrintf(
494 frontend_url_.c_str(),
495 frontend_url_.find("?") == std::string::npos ? "?" : "&",
501 static bool ParseJsonPath(
502 const std::string& path,
503 std::string* command,
504 std::string* target_id) {
506 // Fall back to list in case of empty query.
512 if (path.find("/") != 0) {
513 // Malformed command.
516 *command = path.substr(1);
518 size_t separator_pos = command->find("/");
519 if (separator_pos != std::string::npos) {
520 *target_id = command->substr(separator_pos + 1);
521 *command = command->substr(0, separator_pos);
526 void DevToolsHttpHandlerImpl::OnJsonRequestUI(
528 const net::HttpServerRequestInfo& info) {
530 std::string path = info.path.substr(5);
532 // Trim fragment and query
534 size_t query_pos = path.find("?");
535 if (query_pos != std::string::npos) {
536 query = path.substr(query_pos + 1);
537 path = path.substr(0, query_pos);
540 size_t fragment_pos = path.find("#");
541 if (fragment_pos != std::string::npos)
542 path = path.substr(0, fragment_pos);
545 std::string target_id;
546 if (!ParseJsonPath(path, &command, &target_id)) {
547 SendJson(connection_id,
550 "Malformed query: " + info.path);
554 if (command == "version") {
555 base::DictionaryValue version;
556 version.SetString("Protocol-Version", devtools::kProtocolVersion);
557 version.SetString("WebKit-Version", GetWebKitVersion());
558 version.SetString("Browser", GetContentClient()->GetProduct());
559 version.SetString("User-Agent", GetContentClient()->GetUserAgent());
560 #if defined(OS_ANDROID)
561 version.SetString("Android-Package",
562 base::android::BuildInfo::GetInstance()->package_name());
564 SendJson(connection_id, net::HTTP_OK, &version, std::string());
568 if (command == "list") {
569 std::string host = info.headers["host"];
570 AddRef(); // Balanced in OnTargetListReceived.
571 DevToolsManagerDelegate* manager_delegate =
572 DevToolsManager::GetInstance()->delegate();
573 if (manager_delegate) {
574 manager_delegate->EnumerateTargets(
575 base::Bind(&DevToolsHttpHandlerImpl::OnTargetListReceived,
576 this, connection_id, host));
578 DevToolsManagerDelegate::TargetList empty_list;
579 OnTargetListReceived(connection_id, host, empty_list);
584 if (command == "new") {
585 GURL url(net::UnescapeURLComponent(
586 query, net::UnescapeRule::URL_SPECIAL_CHARS));
588 url = GURL(url::kAboutBlankURL);
589 scoped_ptr<DevToolsTarget> target;
590 DevToolsManagerDelegate* manager_delegate =
591 DevToolsManager::GetInstance()->delegate();
592 if (manager_delegate)
593 target = manager_delegate->CreateNewTarget(url);
595 SendJson(connection_id,
596 net::HTTP_INTERNAL_SERVER_ERROR,
598 "Could not create new page");
601 std::string host = info.headers["host"];
602 scoped_ptr<base::DictionaryValue> dictionary(
603 SerializeTarget(*target.get(), host));
604 SendJson(connection_id, net::HTTP_OK, dictionary.get(), std::string());
605 const std::string target_id = target->GetId();
606 target_map_[target_id] = target.release();
610 if (command == "activate" || command == "close") {
611 DevToolsTarget* target = GetTarget(target_id);
613 SendJson(connection_id,
616 "No such target id: " + target_id);
620 if (command == "activate") {
621 if (target->Activate()) {
622 SendJson(connection_id, net::HTTP_OK, NULL, "Target activated");
624 SendJson(connection_id,
625 net::HTTP_INTERNAL_SERVER_ERROR,
627 "Could not activate target id: " + target_id);
632 if (command == "close") {
633 if (target->Close()) {
634 SendJson(connection_id, net::HTTP_OK, NULL, "Target is closing");
636 SendJson(connection_id,
637 net::HTTP_INTERNAL_SERVER_ERROR,
639 "Could not close target id: " + target_id);
644 SendJson(connection_id,
647 "Unknown command: " + command);
651 void DevToolsHttpHandlerImpl::OnTargetListReceived(
653 const std::string& host,
654 const DevToolsManagerDelegate::TargetList& targets) {
655 DevToolsManagerDelegate::TargetList sorted_targets = targets;
656 std::sort(sorted_targets.begin(), sorted_targets.end(), TimeComparator);
658 STLDeleteValues(&target_map_);
659 base::ListValue list_value;
660 for (DevToolsManagerDelegate::TargetList::const_iterator it =
661 sorted_targets.begin(); it != sorted_targets.end(); ++it) {
662 DevToolsTarget* target = *it;
663 target_map_[target->GetId()] = target;
664 list_value.Append(SerializeTarget(*target, host));
666 SendJson(connection_id, net::HTTP_OK, &list_value, std::string());
667 Release(); // Balanced in OnJsonRequestUI.
670 DevToolsTarget* DevToolsHttpHandlerImpl::GetTarget(const std::string& id) {
671 TargetMap::const_iterator it = target_map_.find(id);
672 if (it == target_map_.end())
677 void DevToolsHttpHandlerImpl::OnThumbnailRequestUI(
678 int connection_id, const GURL& page_url) {
679 DevToolsManagerDelegate* manager_delegate =
680 DevToolsManager::GetInstance()->delegate();
682 manager_delegate ? manager_delegate->GetPageThumbnailData(page_url) : "";
684 Send200(connection_id, data, "image/png");
686 Send404(connection_id);
689 void DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI(int connection_id) {
690 std::string response = delegate_->GetDiscoveryPageHTML();
691 Send200(connection_id, response, "text/html; charset=UTF-8");
694 void DevToolsHttpHandlerImpl::OnWebSocketRequestUI(
696 const net::HttpServerRequestInfo& request) {
700 std::string browser_prefix = "/devtools/browser";
701 size_t browser_pos = request.path.find(browser_prefix);
702 if (browser_pos == 0) {
703 BrowserTarget* browser_target = new BrowserTarget(
704 thread_->message_loop(), server_.get(), connection_id);
705 browser_target->RegisterHandler(
706 new TetheringHandler(delegate_.get(), thread_->message_loop_proxy()));
707 browser_target->RegisterHandler(
708 new DevToolsSystemInfoHandler());
709 browser_targets_[connection_id] = browser_target;
710 AcceptWebSocket(connection_id, request);
714 size_t pos = request.path.find(kPageUrlPrefix);
716 Send404(connection_id);
720 std::string page_id = request.path.substr(strlen(kPageUrlPrefix));
721 DevToolsTarget* target = GetTarget(page_id);
722 scoped_refptr<DevToolsAgentHost> agent =
723 target ? target->GetAgentHost() : NULL;
725 Send500(connection_id, "No such target id: " + page_id);
729 if (agent->IsAttached()) {
730 Send500(connection_id,
731 "Target with given id is being inspected: " + page_id);
735 DevToolsAgentHostClientImpl* client_host = new DevToolsAgentHostClientImpl(
736 thread_->message_loop(), server_.get(), connection_id, agent.get());
737 connection_to_client_ui_[connection_id] = client_host;
739 AcceptWebSocket(connection_id, request);
742 void DevToolsHttpHandlerImpl::OnWebSocketMessageUI(
744 const std::string& data) {
745 BrowserTargets::iterator browser_it = browser_targets_.find(connection_id);
746 if (browser_it != browser_targets_.end()) {
747 browser_it->second->HandleMessage(data);
751 ConnectionToClientMap::iterator it =
752 connection_to_client_ui_.find(connection_id);
753 if (it == connection_to_client_ui_.end())
756 DevToolsAgentHostClientImpl* client =
757 static_cast<DevToolsAgentHostClientImpl*>(it->second);
758 client->OnMessage(data);
761 void DevToolsHttpHandlerImpl::OnCloseUI(int connection_id) {
762 BrowserTargets::iterator browser_it = browser_targets_.find(connection_id);
763 if (browser_it != browser_targets_.end()) {
764 delete browser_it->second;
765 browser_targets_.erase(connection_id);
769 ConnectionToClientMap::iterator it =
770 connection_to_client_ui_.find(connection_id);
771 if (it != connection_to_client_ui_.end()) {
772 DevToolsAgentHostClientImpl* client =
773 static_cast<DevToolsAgentHostClientImpl*>(it->second);
775 connection_to_client_ui_.erase(connection_id);
779 void DevToolsHttpHandlerImpl::OnHttpServerInitialized(
780 const net::IPEndPoint& ip_address) {
781 server_ip_address_.reset(new net::IPEndPoint(ip_address));
784 DevToolsHttpHandlerImpl::DevToolsHttpHandlerImpl(
785 scoped_ptr<ServerSocketFactory> server_socket_factory,
786 const std::string& frontend_url,
787 DevToolsHttpHandlerDelegate* delegate,
788 const base::FilePath& active_port_output_directory)
789 : frontend_url_(frontend_url),
790 server_socket_factory_(server_socket_factory.Pass()),
792 active_port_output_directory_(active_port_output_directory) {
793 if (frontend_url_.empty())
794 frontend_url_ = "/devtools/devtools.html";
796 // Balanced in ResetHandlerThreadAndRelease().
800 // Runs on the handler thread
801 void DevToolsHttpHandlerImpl::Init() {
802 scoped_ptr<net::ServerSocket> server_socket =
803 server_socket_factory_->CreateAndListen();
804 if (!server_socket) {
805 LOG(ERROR) << "Cannot start http server for devtools. Stop devtools.";
806 BrowserThread::PostTask(
807 BrowserThread::UI, FROM_HERE,
808 base::Bind(&DevToolsHttpHandlerImpl::StopWithoutRelease, this));
812 server_.reset(new net::HttpServer(server_socket.Pass(), this));
813 net::IPEndPoint ip_address;
814 server_->GetLocalAddress(&ip_address);
815 BrowserThread::PostTask(
816 BrowserThread::UI, FROM_HERE,
817 base::Bind(&DevToolsHttpHandlerImpl::OnHttpServerInitialized,
819 if (!active_port_output_directory_.empty())
820 WriteActivePortToUserProfile();
823 // Runs on the handler thread
824 void DevToolsHttpHandlerImpl::Teardown() {
828 // Runs on FILE thread to make sure that it is serialized against
829 // {Start|Stop}HandlerThread and to allow calling pthread_join.
830 void DevToolsHttpHandlerImpl::StopHandlerThread() {
831 if (!thread_->message_loop())
833 thread_->message_loop()->PostTask(
835 base::Bind(&DevToolsHttpHandlerImpl::Teardown, this));
836 // Thread::Stop joins the thread.
840 void DevToolsHttpHandlerImpl::WriteActivePortToUserProfile() {
841 DCHECK(!active_port_output_directory_.empty());
842 net::IPEndPoint endpoint;
844 if ((err = server_->GetLocalAddress(&endpoint)) != net::OK) {
845 LOG(ERROR) << "Error " << err << " getting local address";
849 // Write this port to a well-known file in the profile directory
850 // so Telemetry can pick it up.
851 base::FilePath path = active_port_output_directory_.Append(
852 kDevToolsActivePortFileName);
853 std::string port_string = base::IntToString(endpoint.port());
854 if (base::WriteFile(path, port_string.c_str(), port_string.length()) < 0) {
855 LOG(ERROR) << "Error writing DevTools active port to file";
859 void DevToolsHttpHandlerImpl::SendJson(int connection_id,
860 net::HttpStatusCode status_code,
862 const std::string& message) {
866 // Serialize value and message.
867 std::string json_value;
869 base::JSONWriter::WriteWithOptions(value,
870 base::JSONWriter::OPTIONS_PRETTY_PRINT,
873 std::string json_message;
874 scoped_ptr<base::Value> message_object(new base::StringValue(message));
875 base::JSONWriter::Write(message_object.get(), &json_message);
877 net::HttpServerResponseInfo response(status_code);
878 response.SetBody(json_value + message, "application/json; charset=UTF-8");
880 thread_->message_loop()->PostTask(
882 base::Bind(&net::HttpServer::SendResponse,
883 base::Unretained(server_.get()),
888 void DevToolsHttpHandlerImpl::Send200(int connection_id,
889 const std::string& data,
890 const std::string& mime_type) {
893 thread_->message_loop()->PostTask(
895 base::Bind(&net::HttpServer::Send200,
896 base::Unretained(server_.get()),
902 void DevToolsHttpHandlerImpl::Send404(int connection_id) {
905 thread_->message_loop()->PostTask(
907 base::Bind(&net::HttpServer::Send404,
908 base::Unretained(server_.get()),
912 void DevToolsHttpHandlerImpl::Send500(int connection_id,
913 const std::string& message) {
916 thread_->message_loop()->PostTask(
918 base::Bind(&net::HttpServer::Send500,
919 base::Unretained(server_.get()),
924 void DevToolsHttpHandlerImpl::AcceptWebSocket(
926 const net::HttpServerRequestInfo& request) {
929 thread_->message_loop()->PostTask(
931 base::Bind(&net::HttpServer::SetSendBufferSize,
932 base::Unretained(server_.get()),
934 kSendBufferSizeForDevTools));
935 thread_->message_loop()->PostTask(
937 base::Bind(&net::HttpServer::AcceptWebSocket,
938 base::Unretained(server_.get()),
943 base::DictionaryValue* DevToolsHttpHandlerImpl::SerializeTarget(
944 const DevToolsTarget& target,
945 const std::string& host) {
946 base::DictionaryValue* dictionary = new base::DictionaryValue;
948 std::string id = target.GetId();
949 dictionary->SetString(kTargetIdField, id);
950 std::string parent_id = target.GetParentId();
951 if (!parent_id.empty())
952 dictionary->SetString(kTargetParentIdField, parent_id);
953 dictionary->SetString(kTargetTypeField, target.GetType());
954 dictionary->SetString(kTargetTitleField,
955 net::EscapeForHTML(target.GetTitle()));
956 dictionary->SetString(kTargetDescriptionField, target.GetDescription());
958 GURL url = target.GetURL();
959 dictionary->SetString(kTargetUrlField, url.spec());
961 GURL favicon_url = target.GetFaviconURL();
962 if (favicon_url.is_valid())
963 dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());
965 DevToolsManagerDelegate* manager_delegate =
966 DevToolsManager::GetInstance()->delegate();
967 if (manager_delegate &&
968 !manager_delegate->GetPageThumbnailData(url).empty()) {
969 dictionary->SetString(kTargetThumbnailUrlField,
970 std::string(kThumbUrlPrefix) + id);
973 if (!target.IsAttached()) {
974 dictionary->SetString(kTargetWebSocketDebuggerUrlField,
975 base::StringPrintf("ws://%s%s%s",
979 std::string devtools_frontend_url = GetFrontendURLInternal(
982 dictionary->SetString(
983 kTargetDevtoolsFrontendUrlField, devtools_frontend_url);
989 } // namespace content