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/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_browser_target.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/devtools_tracing_handler.h"
25 #include "content/browser/devtools/tethering_handler.h"
26 #include "content/common/devtools_messages.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/devtools_agent_host.h"
29 #include "content/public/browser/devtools_http_handler_delegate.h"
30 #include "content/public/browser/devtools_target.h"
31 #include "content/public/common/content_client.h"
32 #include "content/public/common/url_constants.h"
33 #include "content/public/common/user_agent.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 // An internal implementation of DevToolsAgentHostClient that delegates
75 // messages sent to a DebuggerShell instance.
76 class DevToolsAgentHostClientImpl
: public DevToolsAgentHostClient
{
78 DevToolsAgentHostClientImpl(base::MessageLoop
* message_loop
,
79 net::HttpServer
* server
,
81 DevToolsAgentHost
* agent_host
)
82 : message_loop_(message_loop
),
84 connection_id_(connection_id
),
85 agent_host_(agent_host
) {
86 agent_host_
->AttachClient(this);
89 virtual ~DevToolsAgentHostClientImpl() {
90 if (agent_host_
.get())
91 agent_host_
->DetachClient();
94 virtual void AgentHostClosed(
95 DevToolsAgentHost
* agent_host
,
96 bool replaced_with_another_client
) OVERRIDE
{
97 DCHECK(agent_host
== agent_host_
.get());
100 base::DictionaryValue notification
;
101 notification
.SetString(
102 devtools::Inspector::detached::kParamReason
,
103 replaced_with_another_client
?
104 "replaced_with_devtools" : "target_closed");
105 std::string response
= DevToolsProtocol::CreateNotification(
106 devtools::Inspector::detached::kName
,
107 notification
.DeepCopy())->Serialize();
108 message_loop_
->PostTask(
110 base::Bind(&net::HttpServer::SendOverWebSocket
,
111 base::Unretained(server_
),
115 message_loop_
->PostTask(
117 base::Bind(&net::HttpServer::Close
,
118 base::Unretained(server_
),
122 virtual void DispatchProtocolMessage(
123 DevToolsAgentHost
* agent_host
, const std::string
& message
) OVERRIDE
{
124 DCHECK(agent_host
== agent_host_
.get());
125 message_loop_
->PostTask(
127 base::Bind(&net::HttpServer::SendOverWebSocket
,
128 base::Unretained(server_
),
133 void OnMessage(const std::string
& message
) {
134 if (agent_host_
.get())
135 agent_host_
->DispatchProtocolMessage(message
);
139 base::MessageLoop
* const message_loop_
;
140 net::HttpServer
* const server_
;
141 const int connection_id_
;
142 scoped_refptr
<DevToolsAgentHost
> agent_host_
;
145 static bool TimeComparator(const DevToolsTarget
* target1
,
146 const DevToolsTarget
* target2
) {
147 return target1
->GetLastActivityTime() > target2
->GetLastActivityTime();
153 bool DevToolsHttpHandler::IsSupportedProtocolVersion(
154 const std::string
& version
) {
155 return devtools::IsSupportedProtocolVersion(version
);
159 int DevToolsHttpHandler::GetFrontendResourceId(const std::string
& name
) {
160 for (size_t i
= 0; i
< kDevtoolsResourcesSize
; ++i
) {
161 if (name
== kDevtoolsResources
[i
].name
)
162 return kDevtoolsResources
[i
].value
;
168 DevToolsHttpHandler
* DevToolsHttpHandler::Start(
169 scoped_ptr
<ServerSocketFactory
> server_socket_factory
,
170 const std::string
& frontend_url
,
171 DevToolsHttpHandlerDelegate
* delegate
,
172 const base::FilePath
& active_port_output_directory
) {
173 DevToolsHttpHandlerImpl
* http_handler
=
174 new DevToolsHttpHandlerImpl(server_socket_factory
.Pass(),
177 active_port_output_directory
);
178 http_handler
->Start();
182 DevToolsHttpHandler::ServerSocketFactory::ServerSocketFactory(
183 const std::string
& address
,
191 DevToolsHttpHandler::ServerSocketFactory::~ServerSocketFactory() {
194 scoped_ptr
<net::ServerSocket
>
195 DevToolsHttpHandler::ServerSocketFactory::CreateAndListen() const {
196 scoped_ptr
<net::ServerSocket
> socket
= Create();
198 socket
->ListenWithAddressAndPort(address_
, port_
, backlog_
) == net::OK
) {
199 return socket
.Pass();
201 return scoped_ptr
<net::ServerSocket
>();
204 DevToolsHttpHandlerImpl::~DevToolsHttpHandlerImpl() {
205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
206 // Stop() must be called prior to destruction.
207 DCHECK(server_
.get() == NULL
);
208 DCHECK(thread_
.get() == NULL
);
209 STLDeleteValues(&target_map_
);
212 void DevToolsHttpHandlerImpl::Start() {
215 thread_
.reset(new base::Thread(kDevToolsHandlerThreadName
));
216 BrowserThread::PostTask(
217 BrowserThread::FILE, FROM_HERE
,
218 base::Bind(&DevToolsHttpHandlerImpl::StartHandlerThread
, this));
221 // Runs on FILE thread.
222 void DevToolsHttpHandlerImpl::StartHandlerThread() {
223 base::Thread::Options options
;
224 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
225 if (!thread_
->StartWithOptions(options
)) {
226 BrowserThread::PostTask(
227 BrowserThread::UI
, FROM_HERE
,
228 base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThread
, this));
232 thread_
->message_loop()->PostTask(
234 base::Bind(&DevToolsHttpHandlerImpl::Init
, this));
237 void DevToolsHttpHandlerImpl::ResetHandlerThread() {
241 void DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease() {
242 ResetHandlerThread();
246 void DevToolsHttpHandlerImpl::Stop() {
249 BrowserThread::PostTaskAndReply(
250 BrowserThread::FILE, FROM_HERE
,
251 base::Bind(&DevToolsHttpHandlerImpl::StopHandlerThread
, this),
252 base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease
, this));
255 void DevToolsHttpHandlerImpl::StopWithoutRelease() {
258 BrowserThread::PostTaskAndReply(
259 BrowserThread::FILE, FROM_HERE
,
260 base::Bind(&DevToolsHttpHandlerImpl::StopHandlerThread
, this),
261 base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThread
, this));
264 GURL
DevToolsHttpHandlerImpl::GetFrontendURL() {
265 net::IPEndPoint ip_address
;
266 if (server_
&& server_
->GetLocalAddress(&ip_address
))
268 return GURL(std::string("http://") + ip_address
.ToString() + frontend_url_
);
271 static std::string
PathWithoutParams(const std::string
& path
) {
272 size_t query_position
= path
.find("?");
273 if (query_position
!= std::string::npos
)
274 return path
.substr(0, query_position
);
278 static std::string
GetMimeType(const std::string
& filename
) {
279 if (EndsWith(filename
, ".html", false)) {
281 } else if (EndsWith(filename
, ".css", false)) {
283 } else if (EndsWith(filename
, ".js", false)) {
284 return "application/javascript";
285 } else if (EndsWith(filename
, ".png", false)) {
287 } else if (EndsWith(filename
, ".gif", false)) {
289 } else if (EndsWith(filename
, ".json", false)) {
290 return "application/json";
292 LOG(ERROR
) << "GetMimeType doesn't know mime type for: "
294 << " text/plain will be returned";
299 void DevToolsHttpHandlerImpl::OnHttpRequest(
301 const net::HttpServerRequestInfo
& info
) {
302 server_
->SetSendBufferSize(connection_id
, kSendBufferSizeForDevTools
);
304 if (info
.path
.find("/json") == 0) {
305 BrowserThread::PostTask(
308 base::Bind(&DevToolsHttpHandlerImpl::OnJsonRequestUI
,
315 if (info
.path
.find(kThumbUrlPrefix
) == 0) {
316 // Thumbnail request.
317 const std::string target_id
= info
.path
.substr(strlen(kThumbUrlPrefix
));
318 DevToolsTarget
* target
= GetTarget(target_id
);
321 page_url
= target
->GetURL();
322 BrowserThread::PostTask(
325 base::Bind(&DevToolsHttpHandlerImpl::OnThumbnailRequestUI
,
332 if (info
.path
== "" || info
.path
== "/") {
333 // Discovery page request.
334 BrowserThread::PostTask(
337 base::Bind(&DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI
,
343 if (info
.path
.find("/devtools/") != 0) {
344 server_
->Send404(connection_id
);
348 std::string filename
= PathWithoutParams(info
.path
.substr(10));
349 std::string mime_type
= GetMimeType(filename
);
351 base::FilePath frontend_dir
= delegate_
->GetDebugFrontendDir();
352 if (!frontend_dir
.empty()) {
353 base::FilePath path
= frontend_dir
.AppendASCII(filename
);
355 base::ReadFileToString(path
, &data
);
356 server_
->Send200(connection_id
, data
, mime_type
);
359 if (delegate_
->BundlesFrontendResources()) {
360 int resource_id
= DevToolsHttpHandler::GetFrontendResourceId(filename
);
361 if (resource_id
!= -1) {
362 base::StringPiece data
= GetContentClient()->GetDataResource(
363 resource_id
, ui::SCALE_FACTOR_NONE
);
364 server_
->Send200(connection_id
, data
.as_string(), mime_type
);
368 server_
->Send404(connection_id
);
371 void DevToolsHttpHandlerImpl::OnWebSocketRequest(
373 const net::HttpServerRequestInfo
& request
) {
374 std::string browser_prefix
= "/devtools/browser";
375 size_t browser_pos
= request
.path
.find(browser_prefix
);
376 if (browser_pos
== 0) {
377 scoped_refptr
<DevToolsBrowserTarget
> browser_target
=
378 new DevToolsBrowserTarget(server_
.get(), connection_id
);
379 browser_target
->RegisterDomainHandler(
380 devtools::Tracing::kName
,
381 new DevToolsTracingHandler(DevToolsTracingHandler::Browser
),
382 true /* handle on UI thread */);
383 browser_target
->RegisterDomainHandler(
384 TetheringHandler::kDomain
,
385 new TetheringHandler(delegate_
.get()),
386 false /* handle on this thread */);
387 browser_target
->RegisterDomainHandler(
388 devtools::SystemInfo::kName
,
389 new DevToolsSystemInfoHandler(),
390 true /* handle on UI thread */);
391 browser_targets_
[connection_id
] = browser_target
;
393 server_
->SetSendBufferSize(connection_id
, kSendBufferSizeForDevTools
);
394 server_
->AcceptWebSocket(connection_id
, request
);
398 BrowserThread::PostTask(
402 &DevToolsHttpHandlerImpl::OnWebSocketRequestUI
,
408 void DevToolsHttpHandlerImpl::OnWebSocketMessage(
410 const std::string
& data
) {
411 BrowserTargets::iterator it
= browser_targets_
.find(connection_id
);
412 if (it
!= browser_targets_
.end()) {
413 it
->second
->HandleMessage(data
);
417 BrowserThread::PostTask(
421 &DevToolsHttpHandlerImpl::OnWebSocketMessageUI
,
427 void DevToolsHttpHandlerImpl::OnClose(int connection_id
) {
428 BrowserTargets::iterator it
= browser_targets_
.find(connection_id
);
429 if (it
!= browser_targets_
.end()) {
430 it
->second
->Detach();
431 browser_targets_
.erase(it
);
435 BrowserThread::PostTask(
439 &DevToolsHttpHandlerImpl::OnCloseUI
,
444 std::string
DevToolsHttpHandlerImpl::GetFrontendURLInternal(
445 const std::string id
,
446 const std::string
& host
) {
447 return base::StringPrintf(
449 frontend_url_
.c_str(),
450 frontend_url_
.find("?") == std::string::npos
? "?" : "&",
456 static bool ParseJsonPath(
457 const std::string
& path
,
458 std::string
* command
,
459 std::string
* target_id
) {
461 // Fall back to list in case of empty query.
467 if (path
.find("/") != 0) {
468 // Malformed command.
471 *command
= path
.substr(1);
473 size_t separator_pos
= command
->find("/");
474 if (separator_pos
!= std::string::npos
) {
475 *target_id
= command
->substr(separator_pos
+ 1);
476 *command
= command
->substr(0, separator_pos
);
481 void DevToolsHttpHandlerImpl::OnJsonRequestUI(
483 const net::HttpServerRequestInfo
& info
) {
485 std::string path
= info
.path
.substr(5);
487 // Trim fragment and query
489 size_t query_pos
= path
.find("?");
490 if (query_pos
!= std::string::npos
) {
491 query
= path
.substr(query_pos
+ 1);
492 path
= path
.substr(0, query_pos
);
495 size_t fragment_pos
= path
.find("#");
496 if (fragment_pos
!= std::string::npos
)
497 path
= path
.substr(0, fragment_pos
);
500 std::string target_id
;
501 if (!ParseJsonPath(path
, &command
, &target_id
)) {
502 SendJson(connection_id
,
505 "Malformed query: " + info
.path
);
509 if (command
== "version") {
510 base::DictionaryValue version
;
511 version
.SetString("Protocol-Version", devtools::kProtocolVersion
);
512 version
.SetString("WebKit-Version", GetWebKitVersion());
513 version
.SetString("Browser", GetContentClient()->GetProduct());
514 version
.SetString("User-Agent", GetContentClient()->GetUserAgent());
515 #if defined(OS_ANDROID)
516 version
.SetString("Android-Package",
517 base::android::BuildInfo::GetInstance()->package_name());
519 SendJson(connection_id
, net::HTTP_OK
, &version
, std::string());
523 if (command
== "list") {
524 std::string host
= info
.headers
["host"];
525 AddRef(); // Balanced in OnTargetListReceived.
526 delegate_
->EnumerateTargets(
527 base::Bind(&DevToolsHttpHandlerImpl::OnTargetListReceived
,
528 this, connection_id
, host
));
532 if (command
== "new") {
533 GURL
url(net::UnescapeURLComponent(
534 query
, net::UnescapeRule::URL_SPECIAL_CHARS
));
536 url
= GURL(url::kAboutBlankURL
);
537 scoped_ptr
<DevToolsTarget
> target(delegate_
->CreateNewTarget(url
));
539 SendJson(connection_id
,
540 net::HTTP_INTERNAL_SERVER_ERROR
,
542 "Could not create new page");
545 std::string host
= info
.headers
["host"];
546 scoped_ptr
<base::DictionaryValue
> dictionary(
547 SerializeTarget(*target
.get(), host
));
548 SendJson(connection_id
, net::HTTP_OK
, dictionary
.get(), std::string());
549 const std::string target_id
= target
->GetId();
550 target_map_
[target_id
] = target
.release();
554 if (command
== "activate" || command
== "close") {
555 DevToolsTarget
* target
= GetTarget(target_id
);
557 SendJson(connection_id
,
560 "No such target id: " + target_id
);
564 if (command
== "activate") {
565 if (target
->Activate()) {
566 SendJson(connection_id
, net::HTTP_OK
, NULL
, "Target activated");
568 SendJson(connection_id
,
569 net::HTTP_INTERNAL_SERVER_ERROR
,
571 "Could not activate target id: " + target_id
);
576 if (command
== "close") {
577 if (target
->Close()) {
578 SendJson(connection_id
, net::HTTP_OK
, NULL
, "Target is closing");
580 SendJson(connection_id
,
581 net::HTTP_INTERNAL_SERVER_ERROR
,
583 "Could not close target id: " + target_id
);
588 SendJson(connection_id
,
591 "Unknown command: " + command
);
595 void DevToolsHttpHandlerImpl::OnTargetListReceived(
597 const std::string
& host
,
598 const DevToolsHttpHandlerDelegate::TargetList
& targets
) {
599 DevToolsHttpHandlerDelegate::TargetList sorted_targets
= targets
;
600 std::sort(sorted_targets
.begin(), sorted_targets
.end(), TimeComparator
);
602 STLDeleteValues(&target_map_
);
603 base::ListValue list_value
;
604 for (DevToolsHttpHandlerDelegate::TargetList::const_iterator it
=
605 sorted_targets
.begin(); it
!= sorted_targets
.end(); ++it
) {
606 DevToolsTarget
* target
= *it
;
607 target_map_
[target
->GetId()] = target
;
608 list_value
.Append(SerializeTarget(*target
, host
));
610 SendJson(connection_id
, net::HTTP_OK
, &list_value
, std::string());
611 Release(); // Balanced in OnJsonRequestUI.
614 DevToolsTarget
* DevToolsHttpHandlerImpl::GetTarget(const std::string
& id
) {
615 TargetMap::const_iterator it
= target_map_
.find(id
);
616 if (it
== target_map_
.end())
621 void DevToolsHttpHandlerImpl::OnThumbnailRequestUI(
622 int connection_id
, const GURL
& page_url
) {
623 std::string data
= delegate_
->GetPageThumbnailData(page_url
);
625 Send200(connection_id
, data
, "image/png");
627 Send404(connection_id
);
630 void DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI(int connection_id
) {
631 std::string response
= delegate_
->GetDiscoveryPageHTML();
632 Send200(connection_id
, response
, "text/html; charset=UTF-8");
635 void DevToolsHttpHandlerImpl::OnWebSocketRequestUI(
637 const net::HttpServerRequestInfo
& request
) {
641 size_t pos
= request
.path
.find(kPageUrlPrefix
);
643 Send404(connection_id
);
647 std::string page_id
= request
.path
.substr(strlen(kPageUrlPrefix
));
648 DevToolsTarget
* target
= GetTarget(page_id
);
649 scoped_refptr
<DevToolsAgentHost
> agent
=
650 target
? target
->GetAgentHost() : NULL
;
652 Send500(connection_id
, "No such target id: " + page_id
);
656 if (agent
->IsAttached()) {
657 Send500(connection_id
,
658 "Target with given id is being inspected: " + page_id
);
662 DevToolsAgentHostClientImpl
* client_host
= new DevToolsAgentHostClientImpl(
663 thread_
->message_loop(), server_
.get(), connection_id
, agent
.get());
664 connection_to_client_ui_
[connection_id
] = client_host
;
666 AcceptWebSocket(connection_id
, request
);
669 void DevToolsHttpHandlerImpl::OnWebSocketMessageUI(
671 const std::string
& data
) {
672 ConnectionToClientMap::iterator it
=
673 connection_to_client_ui_
.find(connection_id
);
674 if (it
== connection_to_client_ui_
.end())
677 DevToolsAgentHostClientImpl
* client
=
678 static_cast<DevToolsAgentHostClientImpl
*>(it
->second
);
679 client
->OnMessage(data
);
682 void DevToolsHttpHandlerImpl::OnCloseUI(int connection_id
) {
683 ConnectionToClientMap::iterator it
=
684 connection_to_client_ui_
.find(connection_id
);
685 if (it
!= connection_to_client_ui_
.end()) {
686 DevToolsAgentHostClientImpl
* client
=
687 static_cast<DevToolsAgentHostClientImpl
*>(it
->second
);
689 connection_to_client_ui_
.erase(connection_id
);
693 DevToolsHttpHandlerImpl::DevToolsHttpHandlerImpl(
694 scoped_ptr
<ServerSocketFactory
> server_socket_factory
,
695 const std::string
& frontend_url
,
696 DevToolsHttpHandlerDelegate
* delegate
,
697 const base::FilePath
& active_port_output_directory
)
698 : frontend_url_(frontend_url
),
699 server_socket_factory_(server_socket_factory
.Pass()),
701 active_port_output_directory_(active_port_output_directory
) {
702 if (frontend_url_
.empty())
703 frontend_url_
= "/devtools/devtools.html";
705 // Balanced in ResetHandlerThreadAndRelease().
709 // Runs on the handler thread
710 void DevToolsHttpHandlerImpl::Init() {
711 scoped_ptr
<net::ServerSocket
> server_socket
=
712 server_socket_factory_
->CreateAndListen();
713 if (!server_socket
) {
714 LOG(ERROR
) << "Cannot start http server for devtools. Stop devtools.";
715 BrowserThread::PostTask(
716 BrowserThread::UI
, FROM_HERE
,
717 base::Bind(&DevToolsHttpHandlerImpl::StopWithoutRelease
, this));
721 server_
.reset(new net::HttpServer(server_socket
.Pass(), this));
722 if (!active_port_output_directory_
.empty())
723 WriteActivePortToUserProfile();
726 // Runs on the handler thread
727 void DevToolsHttpHandlerImpl::Teardown() {
731 // Runs on FILE thread to make sure that it is serialized against
732 // {Start|Stop}HandlerThread and to allow calling pthread_join.
733 void DevToolsHttpHandlerImpl::StopHandlerThread() {
734 if (!thread_
->message_loop())
736 thread_
->message_loop()->PostTask(
738 base::Bind(&DevToolsHttpHandlerImpl::Teardown
, this));
739 // Thread::Stop joins the thread.
743 void DevToolsHttpHandlerImpl::WriteActivePortToUserProfile() {
744 DCHECK(!active_port_output_directory_
.empty());
745 net::IPEndPoint endpoint
;
747 if ((err
= server_
->GetLocalAddress(&endpoint
)) != net::OK
) {
748 LOG(ERROR
) << "Error " << err
<< " getting local address";
752 // Write this port to a well-known file in the profile directory
753 // so Telemetry can pick it up.
754 base::FilePath path
= active_port_output_directory_
.Append(
755 kDevToolsActivePortFileName
);
756 std::string port_string
= base::IntToString(endpoint
.port());
757 if (base::WriteFile(path
, port_string
.c_str(), port_string
.length()) < 0) {
758 LOG(ERROR
) << "Error writing DevTools active port to file";
762 void DevToolsHttpHandlerImpl::SendJson(int connection_id
,
763 net::HttpStatusCode status_code
,
765 const std::string
& message
) {
769 // Serialize value and message.
770 std::string json_value
;
772 base::JSONWriter::WriteWithOptions(value
,
773 base::JSONWriter::OPTIONS_PRETTY_PRINT
,
776 std::string json_message
;
777 scoped_ptr
<base::Value
> message_object(new base::StringValue(message
));
778 base::JSONWriter::Write(message_object
.get(), &json_message
);
780 net::HttpServerResponseInfo
response(status_code
);
781 response
.SetBody(json_value
+ message
, "application/json; charset=UTF-8");
783 thread_
->message_loop()->PostTask(
785 base::Bind(&net::HttpServer::SendResponse
,
786 base::Unretained(server_
.get()),
791 void DevToolsHttpHandlerImpl::Send200(int connection_id
,
792 const std::string
& data
,
793 const std::string
& mime_type
) {
796 thread_
->message_loop()->PostTask(
798 base::Bind(&net::HttpServer::Send200
,
799 base::Unretained(server_
.get()),
805 void DevToolsHttpHandlerImpl::Send404(int connection_id
) {
808 thread_
->message_loop()->PostTask(
810 base::Bind(&net::HttpServer::Send404
,
811 base::Unretained(server_
.get()),
815 void DevToolsHttpHandlerImpl::Send500(int connection_id
,
816 const std::string
& message
) {
819 thread_
->message_loop()->PostTask(
821 base::Bind(&net::HttpServer::Send500
,
822 base::Unretained(server_
.get()),
827 void DevToolsHttpHandlerImpl::AcceptWebSocket(
829 const net::HttpServerRequestInfo
& request
) {
832 thread_
->message_loop()->PostTask(
834 base::Bind(&net::HttpServer::SetSendBufferSize
,
835 base::Unretained(server_
.get()),
837 kSendBufferSizeForDevTools
));
838 thread_
->message_loop()->PostTask(
840 base::Bind(&net::HttpServer::AcceptWebSocket
,
841 base::Unretained(server_
.get()),
846 base::DictionaryValue
* DevToolsHttpHandlerImpl::SerializeTarget(
847 const DevToolsTarget
& target
,
848 const std::string
& host
) {
849 base::DictionaryValue
* dictionary
= new base::DictionaryValue
;
851 std::string id
= target
.GetId();
852 dictionary
->SetString(kTargetIdField
, id
);
853 std::string parent_id
= target
.GetParentId();
854 if (!parent_id
.empty())
855 dictionary
->SetString(kTargetParentIdField
, parent_id
);
856 dictionary
->SetString(kTargetTypeField
, target
.GetType());
857 dictionary
->SetString(kTargetTitleField
,
858 net::EscapeForHTML(target
.GetTitle()));
859 dictionary
->SetString(kTargetDescriptionField
, target
.GetDescription());
861 GURL url
= target
.GetURL();
862 dictionary
->SetString(kTargetUrlField
, url
.spec());
864 GURL favicon_url
= target
.GetFaviconURL();
865 if (favicon_url
.is_valid())
866 dictionary
->SetString(kTargetFaviconUrlField
, favicon_url
.spec());
868 if (!delegate_
->GetPageThumbnailData(url
).empty()) {
869 dictionary
->SetString(kTargetThumbnailUrlField
,
870 std::string(kThumbUrlPrefix
) + id
);
873 if (!target
.IsAttached()) {
874 dictionary
->SetString(kTargetWebSocketDebuggerUrlField
,
875 base::StringPrintf("ws://%s%s%s",
879 std::string devtools_frontend_url
= GetFrontendURLInternal(
882 dictionary
->SetString(
883 kTargetDevtoolsFrontendUrlField
, devtools_frontend_url
);
889 } // namespace content