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.
9 #include "base/compiler_specific.h"
10 #include "base/files/file_util.h"
11 #include "base/json/json_writer.h"
12 #include "base/logging.h"
13 #include "base/message_loop/message_loop_proxy.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/threading/thread.h"
17 #include "base/values.h"
18 #include "content/browser/devtools/devtools_manager.h"
19 #include "content/browser/devtools/protocol/devtools_protocol_handler.h"
20 #include "content/browser/devtools/protocol/system_info_handler.h"
21 #include "content/browser/devtools/protocol/tethering_handler.h"
22 #include "content/browser/devtools/protocol/tracing_handler.h"
23 #include "content/common/devtools_messages.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/devtools_agent_host.h"
26 #include "content/public/browser/devtools_http_handler.h"
27 #include "content/public/browser/devtools_http_handler_delegate.h"
28 #include "content/public/browser/devtools_target.h"
29 #include "content/public/common/content_client.h"
30 #include "content/public/common/url_constants.h"
31 #include "content/public/common/user_agent.h"
32 #include "grit/devtools_resources_map.h"
33 #include "net/base/escape.h"
34 #include "net/base/io_buffer.h"
35 #include "net/base/ip_endpoint.h"
36 #include "net/base/net_errors.h"
37 #include "net/server/http_server.h"
38 #include "net/server/http_server_request_info.h"
39 #include "net/server/http_server_response_info.h"
40 #include "net/socket/server_socket.h"
42 #if defined(OS_ANDROID)
43 #include "base/android/build_info.h"
50 const base::FilePath::CharType kDevToolsActivePortFileName
[] =
51 FILE_PATH_LITERAL("DevToolsActivePort");
53 const char kDevToolsHandlerThreadName
[] = "Chrome_DevToolsHandlerThread";
55 const char kThumbUrlPrefix
[] = "/thumb/";
56 const char kPageUrlPrefix
[] = "/devtools/page/";
58 const char kTargetIdField
[] = "id";
59 const char kTargetParentIdField
[] = "parentId";
60 const char kTargetTypeField
[] = "type";
61 const char kTargetTitleField
[] = "title";
62 const char kTargetDescriptionField
[] = "description";
63 const char kTargetUrlField
[] = "url";
64 const char kTargetThumbnailUrlField
[] = "thumbnailUrl";
65 const char kTargetFaviconUrlField
[] = "faviconUrl";
66 const char kTargetWebSocketDebuggerUrlField
[] = "webSocketDebuggerUrl";
67 const char kTargetDevtoolsFrontendUrlField
[] = "devtoolsFrontendUrl";
69 // Maximum write buffer size of devtools http/websocket connections.
70 // TODO(rmcilroy/pfieldman): Reduce this back to 100Mb when we have
71 // added back pressure on the TraceComplete message protocol - crbug.com/456845.
72 const int32 kSendBufferSizeForDevTools
= 256 * 1024 * 1024; // 256Mb
75 class DevToolsAgentHostClientImpl
;
78 // DevToolsHttpHandlerImpl ---------------------------------------------------
80 class DevToolsHttpHandlerImpl
: public DevToolsHttpHandler
{
82 DevToolsHttpHandlerImpl(scoped_ptr
<ServerSocketFactory
> server_socket_factory
,
83 const std::string
& frontend_url
,
84 DevToolsHttpHandlerDelegate
* delegate
,
85 const base::FilePath
& output_directory
);
86 ~DevToolsHttpHandlerImpl() override
;
88 void OnJsonRequest(int connection_id
,
89 const net::HttpServerRequestInfo
& info
);
90 void OnThumbnailRequest(int connection_id
, const std::string
& target_id
);
91 void OnDiscoveryPageRequest(int connection_id
);
93 void OnWebSocketRequest(int connection_id
,
94 const net::HttpServerRequestInfo
& info
);
95 void OnWebSocketMessage(int connection_id
, const std::string
& data
);
96 void OnClose(int connection_id
);
98 void ServerStarted(base::Thread
* thread
,
99 ServerWrapper
* server_wrapper
,
100 ServerSocketFactory
* socket_factory
,
101 scoped_ptr
<net::IPEndPoint
> ip_address
);
104 // DevToolsHttpHandler implementation.
105 GURL
GetFrontendURL(const std::string
& path
) override
;
107 static void OnTargetListReceivedWeak(
108 base::WeakPtr
<DevToolsHttpHandlerImpl
> handler
,
110 const std::string
& host
,
111 const DevToolsManagerDelegate::TargetList
& targets
);
112 void OnTargetListReceived(
114 const std::string
& host
,
115 const DevToolsManagerDelegate::TargetList
& targets
);
117 DevToolsTarget
* GetTarget(const std::string
& id
);
119 void SendJson(int connection_id
,
120 net::HttpStatusCode status_code
,
122 const std::string
& message
);
123 void Send200(int connection_id
,
124 const std::string
& data
,
125 const std::string
& mime_type
);
126 void Send404(int connection_id
);
127 void Send500(int connection_id
,
128 const std::string
& message
);
129 void AcceptWebSocket(int connection_id
,
130 const net::HttpServerRequestInfo
& request
);
132 // Returns the front end url without the host at the beginning.
133 std::string
GetFrontendURLInternal(const std::string target_id
,
134 const std::string
& host
);
136 base::DictionaryValue
* SerializeTarget(const DevToolsTarget
& target
,
137 const std::string
& host
);
139 // The thread used by the devtools handler to run server socket.
140 base::Thread
* thread_
;
142 std::string frontend_url_
;
143 ServerWrapper
* server_wrapper_
;
144 scoped_ptr
<net::IPEndPoint
> server_ip_address_
;
145 typedef std::map
<int, DevToolsAgentHostClientImpl
*> ConnectionToClientMap
;
146 ConnectionToClientMap connection_to_client_
;
147 const scoped_ptr
<DevToolsHttpHandlerDelegate
> delegate_
;
148 ServerSocketFactory
* socket_factory_
;
149 typedef std::map
<std::string
, DevToolsTarget
*> TargetMap
;
150 TargetMap target_map_
;
151 typedef std::map
<int, BrowserTarget
*> BrowserTargets
;
152 BrowserTargets browser_targets_
;
153 base::WeakPtrFactory
<DevToolsHttpHandlerImpl
> weak_factory_
;
155 DISALLOW_COPY_AND_ASSIGN(DevToolsHttpHandlerImpl
);
158 // ServerWrapper -------------------------------------------------------------
159 // All methods in this class are only called on handler thread.
160 class ServerWrapper
: net::HttpServer::Delegate
{
162 ServerWrapper(base::WeakPtr
<DevToolsHttpHandlerImpl
> handler
,
163 scoped_ptr
<net::ServerSocket
> socket
,
164 const base::FilePath
& frontend_dir
,
165 bool bundles_resources
);
167 int GetLocalAddress(net::IPEndPoint
* address
);
169 void AcceptWebSocket(int connection_id
,
170 const net::HttpServerRequestInfo
& request
);
171 void SendOverWebSocket(int connection_id
, const std::string
& message
);
172 void SendResponse(int connection_id
,
173 const net::HttpServerResponseInfo
& response
);
174 void Send200(int connection_id
,
175 const std::string
& data
,
176 const std::string
& mime_type
);
177 void Send404(int connection_id
);
178 void Send500(int connection_id
, const std::string
& message
);
179 void Close(int connection_id
);
181 void WriteActivePortToUserProfile(const base::FilePath
& output_directory
);
183 virtual ~ServerWrapper() {}
186 // net::HttpServer::Delegate implementation.
187 void OnConnect(int connection_id
) override
{}
188 void OnHttpRequest(int connection_id
,
189 const net::HttpServerRequestInfo
& info
) override
;
190 void OnWebSocketRequest(int connection_id
,
191 const net::HttpServerRequestInfo
& info
) override
;
192 void OnWebSocketMessage(int connection_id
,
193 const std::string
& data
) override
;
194 void OnClose(int connection_id
) override
;
196 base::WeakPtr
<DevToolsHttpHandlerImpl
> handler_
;
197 scoped_ptr
<net::HttpServer
> server_
;
198 base::FilePath frontend_dir_
;
199 bool bundles_resources_
;
202 ServerWrapper::ServerWrapper(base::WeakPtr
<DevToolsHttpHandlerImpl
> handler
,
203 scoped_ptr
<net::ServerSocket
> socket
,
204 const base::FilePath
& frontend_dir
,
205 bool bundles_resources
)
207 server_(new net::HttpServer(socket
.Pass(), this)),
208 frontend_dir_(frontend_dir
),
209 bundles_resources_(bundles_resources
) {
212 int ServerWrapper::GetLocalAddress(net::IPEndPoint
* address
) {
213 return server_
->GetLocalAddress(address
);
216 void ServerWrapper::AcceptWebSocket(int connection_id
,
217 const net::HttpServerRequestInfo
& request
) {
218 server_
->SetSendBufferSize(connection_id
, kSendBufferSizeForDevTools
);
219 server_
->AcceptWebSocket(connection_id
, request
);
222 void ServerWrapper::SendOverWebSocket(int connection_id
,
223 const std::string
& message
) {
224 server_
->SendOverWebSocket(connection_id
, message
);
227 void ServerWrapper::SendResponse(int connection_id
,
228 const net::HttpServerResponseInfo
& response
) {
229 server_
->SendResponse(connection_id
, response
);
232 void ServerWrapper::Send200(int connection_id
,
233 const std::string
& data
,
234 const std::string
& mime_type
) {
235 server_
->Send200(connection_id
, data
, mime_type
);
238 void ServerWrapper::Send404(int connection_id
) {
239 server_
->Send404(connection_id
);
242 void ServerWrapper::Send500(int connection_id
,
243 const std::string
& message
) {
244 server_
->Send500(connection_id
, message
);
247 void ServerWrapper::Close(int connection_id
) {
248 server_
->Close(connection_id
);
251 // Thread and ServerWrapper lifetime management ------------------------------
253 void TerminateOnUI(base::Thread
* thread
,
254 ServerWrapper
* server_wrapper
,
255 DevToolsHttpHandler::ServerSocketFactory
* socket_factory
) {
256 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
257 if (server_wrapper
) {
259 thread
->message_loop()->DeleteSoon(FROM_HERE
, server_wrapper
);
261 if (socket_factory
) {
263 thread
->message_loop()->DeleteSoon(FROM_HERE
, socket_factory
);
266 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE
, thread
);
270 void ServerStartedOnUI(
271 base::WeakPtr
<DevToolsHttpHandlerImpl
> handler
,
272 base::Thread
* thread
,
273 ServerWrapper
* server_wrapper
,
274 DevToolsHttpHandler::ServerSocketFactory
* socket_factory
,
275 scoped_ptr
<net::IPEndPoint
> ip_address
) {
276 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
277 if (handler
&& thread
&& server_wrapper
) {
278 handler
->ServerStarted(thread
, server_wrapper
, socket_factory
,
281 TerminateOnUI(thread
, server_wrapper
, socket_factory
);
285 void StartServerOnHandlerThread(
286 base::WeakPtr
<DevToolsHttpHandlerImpl
> handler
,
287 base::Thread
* thread
,
288 DevToolsHttpHandler::ServerSocketFactory
* server_socket_factory
,
289 const base::FilePath
& output_directory
,
290 const base::FilePath
& frontend_dir
,
291 bool bundles_resources
) {
292 DCHECK_EQ(thread
->message_loop(), base::MessageLoop::current());
293 ServerWrapper
* server_wrapper
= nullptr;
294 scoped_ptr
<net::ServerSocket
> server_socket
=
295 server_socket_factory
->CreateForHttpServer();
296 scoped_ptr
<net::IPEndPoint
> ip_address(new net::IPEndPoint
);
298 server_wrapper
= new ServerWrapper(handler
, server_socket
.Pass(),
299 frontend_dir
, bundles_resources
);
300 if (!output_directory
.empty())
301 server_wrapper
->WriteActivePortToUserProfile(output_directory
);
303 if (server_wrapper
->GetLocalAddress(ip_address
.get()) != net::OK
)
307 LOG(ERROR
) << "Cannot start http server for devtools. Stop devtools.";
309 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
310 base::Bind(&ServerStartedOnUI
,
314 server_socket_factory
,
315 base::Passed(&ip_address
)));
318 void StartServerOnFile(
319 base::WeakPtr
<DevToolsHttpHandlerImpl
> handler
,
320 DevToolsHttpHandler::ServerSocketFactory
* server_socket_factory
,
321 const base::FilePath
& output_directory
,
322 const base::FilePath
& frontend_dir
,
323 bool bundles_resources
) {
324 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
325 scoped_ptr
<base::Thread
> thread(new base::Thread(kDevToolsHandlerThreadName
));
326 base::Thread::Options options
;
327 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
328 if (thread
->StartWithOptions(options
)) {
329 base::MessageLoop
* message_loop
= thread
->message_loop();
330 message_loop
->PostTask(FROM_HERE
,
331 base::Bind(&StartServerOnHandlerThread
,
333 base::Unretained(thread
.release()),
334 server_socket_factory
,
341 // DevToolsAgentHostClientImpl -----------------------------------------------
342 // An internal implementation of DevToolsAgentHostClient that delegates
343 // messages sent to a DebuggerShell instance.
344 class DevToolsAgentHostClientImpl
: public DevToolsAgentHostClient
{
346 DevToolsAgentHostClientImpl(base::MessageLoop
* message_loop
,
347 ServerWrapper
* server_wrapper
,
349 DevToolsAgentHost
* agent_host
)
350 : message_loop_(message_loop
),
351 server_wrapper_(server_wrapper
),
352 connection_id_(connection_id
),
353 agent_host_(agent_host
) {
354 agent_host_
->AttachClient(this);
357 ~DevToolsAgentHostClientImpl() override
{
358 if (agent_host_
.get())
359 agent_host_
->DetachClient();
362 void AgentHostClosed(DevToolsAgentHost
* agent_host
,
363 bool replaced_with_another_client
) override
{
364 DCHECK(agent_host
== agent_host_
.get());
366 base::Callback
<void(const std::string
&)> raw_message_callback(
367 base::Bind(&DevToolsAgentHostClientImpl::DispatchProtocolMessage
,
368 base::Unretained(this), base::Unretained(agent_host
)));
369 devtools::inspector::Client
inspector(raw_message_callback
);
370 inspector
.Detached(devtools::inspector::DetachedParams::Create()
371 ->set_reason(replaced_with_another_client
?
372 "replaced_with_devtools" : "target_closed"));
374 agent_host_
= nullptr;
375 message_loop_
->PostTask(
377 base::Bind(&ServerWrapper::Close
,
378 base::Unretained(server_wrapper_
),
382 void DispatchProtocolMessage(DevToolsAgentHost
* agent_host
,
383 const std::string
& message
) override
{
384 DCHECK(agent_host
== agent_host_
.get());
385 message_loop_
->PostTask(
387 base::Bind(&ServerWrapper::SendOverWebSocket
,
388 base::Unretained(server_wrapper_
),
393 void OnMessage(const std::string
& message
) {
394 if (agent_host_
.get())
395 agent_host_
->DispatchProtocolMessage(message
);
399 base::MessageLoop
* const message_loop_
;
400 ServerWrapper
* const server_wrapper_
;
401 const int connection_id_
;
402 scoped_refptr
<DevToolsAgentHost
> agent_host_
;
405 static bool TimeComparator(const DevToolsTarget
* target1
,
406 const DevToolsTarget
* target2
) {
407 return target1
->GetLastActivityTime() > target2
->GetLastActivityTime();
410 // BrowserTarget -------------------------------------------------------------
412 class BrowserTarget
{
414 BrowserTarget(base::MessageLoop
* message_loop
,
415 ServerWrapper
* server_wrapper
,
416 DevToolsHttpHandler::ServerSocketFactory
* socket_factory
,
418 : message_loop_(message_loop
),
419 server_wrapper_(server_wrapper
),
420 connection_id_(connection_id
),
421 system_info_handler_(new devtools::system_info::SystemInfoHandler()),
422 tethering_handler_(new devtools::tethering::TetheringHandler(
423 socket_factory
, message_loop
->message_loop_proxy())),
424 tracing_handler_(new devtools::tracing::TracingHandler(
425 devtools::tracing::TracingHandler::Browser
)),
426 protocol_handler_(new DevToolsProtocolHandler(
427 base::Bind(&BrowserTarget::Respond
, base::Unretained(this)))) {
428 DevToolsProtocolDispatcher
* dispatcher
= protocol_handler_
->dispatcher();
429 dispatcher
->SetSystemInfoHandler(system_info_handler_
.get());
430 dispatcher
->SetTetheringHandler(tethering_handler_
.get());
431 dispatcher
->SetTracingHandler(tracing_handler_
.get());
434 void HandleMessage(const std::string
& message
) {
435 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
436 std::string error_response
;
437 scoped_ptr
<base::DictionaryValue
> command
=
438 protocol_handler_
->ParseCommand(message
);
440 protocol_handler_
->HandleCommand(command
.Pass());
443 void Respond(const std::string
& message
) {
444 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
445 message_loop_
->PostTask(
447 base::Bind(&ServerWrapper::SendOverWebSocket
,
448 base::Unretained(server_wrapper_
),
454 base::MessageLoop
* const message_loop_
;
455 ServerWrapper
* const server_wrapper_
;
456 const int connection_id_
;
457 scoped_ptr
<devtools::system_info::SystemInfoHandler
> system_info_handler_
;
458 scoped_ptr
<devtools::tethering::TetheringHandler
> tethering_handler_
;
459 scoped_ptr
<devtools::tracing::TracingHandler
> tracing_handler_
;
460 scoped_ptr
<DevToolsProtocolHandler
> protocol_handler_
;
465 // DevToolsHttpHandler -------------------------------------------------------
468 bool DevToolsHttpHandler::IsSupportedProtocolVersion(
469 const std::string
& version
) {
470 return devtools::IsSupportedProtocolVersion(version
);
474 int DevToolsHttpHandler::GetFrontendResourceId(const std::string
& name
) {
475 for (size_t i
= 0; i
< kDevtoolsResourcesSize
; ++i
) {
476 if (name
== kDevtoolsResources
[i
].name
)
477 return kDevtoolsResources
[i
].value
;
483 DevToolsHttpHandler
* DevToolsHttpHandler::Start(
484 scoped_ptr
<ServerSocketFactory
> server_socket_factory
,
485 const std::string
& frontend_url
,
486 DevToolsHttpHandlerDelegate
* delegate
,
487 const base::FilePath
& active_port_output_directory
) {
488 DevToolsHttpHandlerImpl
* http_handler
=
489 new DevToolsHttpHandlerImpl(server_socket_factory
.Pass(),
492 active_port_output_directory
);
496 // DevToolsHttpHandler::ServerSocketFactory ----------------------------------
498 scoped_ptr
<net::ServerSocket
>
499 DevToolsHttpHandler::ServerSocketFactory::CreateForHttpServer() {
500 return scoped_ptr
<net::ServerSocket
>();
503 scoped_ptr
<net::ServerSocket
>
504 DevToolsHttpHandler::ServerSocketFactory::CreateForTethering(
506 return scoped_ptr
<net::ServerSocket
>();
509 // DevToolsHttpHandlerImpl ---------------------------------------------------
511 DevToolsHttpHandlerImpl::~DevToolsHttpHandlerImpl() {
512 TerminateOnUI(thread_
, server_wrapper_
, socket_factory_
);
513 STLDeleteValues(&target_map_
);
514 STLDeleteValues(&browser_targets_
);
515 STLDeleteValues(&connection_to_client_
);
518 GURL
DevToolsHttpHandlerImpl::GetFrontendURL(const std::string
& path
) {
519 if (!server_ip_address_
)
521 return GURL(std::string("http://") + server_ip_address_
->ToString() +
522 (path
.empty() ? frontend_url_
: path
));
525 static std::string
PathWithoutParams(const std::string
& path
) {
526 size_t query_position
= path
.find("?");
527 if (query_position
!= std::string::npos
)
528 return path
.substr(0, query_position
);
532 static std::string
GetMimeType(const std::string
& filename
) {
533 if (EndsWith(filename
, ".html", false)) {
535 } else if (EndsWith(filename
, ".css", false)) {
537 } else if (EndsWith(filename
, ".js", false)) {
538 return "application/javascript";
539 } else if (EndsWith(filename
, ".png", false)) {
541 } else if (EndsWith(filename
, ".gif", false)) {
543 } else if (EndsWith(filename
, ".json", false)) {
544 return "application/json";
546 LOG(ERROR
) << "GetMimeType doesn't know mime type for: "
548 << " text/plain will be returned";
553 void ServerWrapper::OnHttpRequest(int connection_id
,
554 const net::HttpServerRequestInfo
& info
) {
555 server_
->SetSendBufferSize(connection_id
, kSendBufferSizeForDevTools
);
557 if (info
.path
.find("/json") == 0) {
558 BrowserThread::PostTask(
561 base::Bind(&DevToolsHttpHandlerImpl::OnJsonRequest
,
568 if (info
.path
.find(kThumbUrlPrefix
) == 0) {
569 // Thumbnail request.
570 const std::string target_id
= info
.path
.substr(strlen(kThumbUrlPrefix
));
571 BrowserThread::PostTask(
574 base::Bind(&DevToolsHttpHandlerImpl::OnThumbnailRequest
,
581 if (info
.path
.empty() || info
.path
== "/") {
582 // Discovery page request.
583 BrowserThread::PostTask(
586 base::Bind(&DevToolsHttpHandlerImpl::OnDiscoveryPageRequest
,
592 if (info
.path
.find("/devtools/") != 0) {
593 server_
->Send404(connection_id
);
597 std::string filename
= PathWithoutParams(info
.path
.substr(10));
598 std::string mime_type
= GetMimeType(filename
);
600 if (!frontend_dir_
.empty()) {
601 base::FilePath path
= frontend_dir_
.AppendASCII(filename
);
603 base::ReadFileToString(path
, &data
);
604 server_
->Send200(connection_id
, data
, mime_type
);
607 if (bundles_resources_
) {
608 int resource_id
= DevToolsHttpHandler::GetFrontendResourceId(filename
);
609 if (resource_id
!= -1) {
610 base::StringPiece data
= GetContentClient()->GetDataResource(
611 resource_id
, ui::SCALE_FACTOR_NONE
);
612 server_
->Send200(connection_id
, data
.as_string(), mime_type
);
616 server_
->Send404(connection_id
);
619 void ServerWrapper::OnWebSocketRequest(
621 const net::HttpServerRequestInfo
& request
) {
622 BrowserThread::PostTask(
626 &DevToolsHttpHandlerImpl::OnWebSocketRequest
,
632 void ServerWrapper::OnWebSocketMessage(int connection_id
,
633 const std::string
& data
) {
634 BrowserThread::PostTask(
638 &DevToolsHttpHandlerImpl::OnWebSocketMessage
,
644 void ServerWrapper::OnClose(int connection_id
) {
645 BrowserThread::PostTask(
649 &DevToolsHttpHandlerImpl::OnClose
,
654 std::string
DevToolsHttpHandlerImpl::GetFrontendURLInternal(
655 const std::string id
,
656 const std::string
& host
) {
657 return base::StringPrintf(
659 frontend_url_
.c_str(),
660 frontend_url_
.find("?") == std::string::npos
? "?" : "&",
666 static bool ParseJsonPath(
667 const std::string
& path
,
668 std::string
* command
,
669 std::string
* target_id
) {
671 // Fall back to list in case of empty query.
677 if (path
.find("/") != 0) {
678 // Malformed command.
681 *command
= path
.substr(1);
683 size_t separator_pos
= command
->find("/");
684 if (separator_pos
!= std::string::npos
) {
685 *target_id
= command
->substr(separator_pos
+ 1);
686 *command
= command
->substr(0, separator_pos
);
691 void DevToolsHttpHandlerImpl::OnJsonRequest(
693 const net::HttpServerRequestInfo
& info
) {
695 std::string path
= info
.path
.substr(5);
697 // Trim fragment and query
699 size_t query_pos
= path
.find("?");
700 if (query_pos
!= std::string::npos
) {
701 query
= path
.substr(query_pos
+ 1);
702 path
= path
.substr(0, query_pos
);
705 size_t fragment_pos
= path
.find("#");
706 if (fragment_pos
!= std::string::npos
)
707 path
= path
.substr(0, fragment_pos
);
710 std::string target_id
;
711 if (!ParseJsonPath(path
, &command
, &target_id
)) {
712 SendJson(connection_id
,
715 "Malformed query: " + info
.path
);
719 if (command
== "version") {
720 base::DictionaryValue version
;
721 version
.SetString("Protocol-Version", devtools::kProtocolVersion
);
722 version
.SetString("WebKit-Version", GetWebKitVersion());
723 version
.SetString("Browser", GetContentClient()->GetProduct());
724 version
.SetString("User-Agent", GetContentClient()->GetUserAgent());
725 #if defined(OS_ANDROID)
726 version
.SetString("Android-Package",
727 base::android::BuildInfo::GetInstance()->package_name());
729 SendJson(connection_id
, net::HTTP_OK
, &version
, std::string());
733 if (command
== "list") {
734 std::string host
= info
.headers
["host"];
735 DevToolsManagerDelegate
* manager_delegate
=
736 DevToolsManager::GetInstance()->delegate();
737 if (manager_delegate
) {
738 manager_delegate
->EnumerateTargets(
739 base::Bind(&DevToolsHttpHandlerImpl::OnTargetListReceivedWeak
,
740 weak_factory_
.GetWeakPtr(), connection_id
, host
));
742 DevToolsManagerDelegate::TargetList empty_list
;
743 OnTargetListReceived(connection_id
, host
, empty_list
);
748 if (command
== "new") {
749 GURL
url(net::UnescapeURLComponent(
750 query
, net::UnescapeRule::URL_SPECIAL_CHARS
));
752 url
= GURL(url::kAboutBlankURL
);
753 scoped_ptr
<DevToolsTarget
> target
;
754 DevToolsManagerDelegate
* manager_delegate
=
755 DevToolsManager::GetInstance()->delegate();
756 if (manager_delegate
)
757 target
= manager_delegate
->CreateNewTarget(url
);
759 SendJson(connection_id
,
760 net::HTTP_INTERNAL_SERVER_ERROR
,
762 "Could not create new page");
765 std::string host
= info
.headers
["host"];
766 scoped_ptr
<base::DictionaryValue
> dictionary(
767 SerializeTarget(*target
.get(), host
));
768 SendJson(connection_id
, net::HTTP_OK
, dictionary
.get(), std::string());
769 const std::string target_id
= target
->GetId();
770 target_map_
[target_id
] = target
.release();
774 if (command
== "activate" || command
== "close") {
775 DevToolsTarget
* target
= GetTarget(target_id
);
777 SendJson(connection_id
,
780 "No such target id: " + target_id
);
784 if (command
== "activate") {
785 if (target
->Activate()) {
786 SendJson(connection_id
, net::HTTP_OK
, NULL
, "Target activated");
788 SendJson(connection_id
,
789 net::HTTP_INTERNAL_SERVER_ERROR
,
791 "Could not activate target id: " + target_id
);
796 if (command
== "close") {
797 if (target
->Close()) {
798 SendJson(connection_id
, net::HTTP_OK
, NULL
, "Target is closing");
800 SendJson(connection_id
,
801 net::HTTP_INTERNAL_SERVER_ERROR
,
803 "Could not close target id: " + target_id
);
808 SendJson(connection_id
,
811 "Unknown command: " + command
);
816 void DevToolsHttpHandlerImpl::OnTargetListReceivedWeak(
817 base::WeakPtr
<DevToolsHttpHandlerImpl
> handler
,
819 const std::string
& host
,
820 const DevToolsManagerDelegate::TargetList
& targets
) {
822 handler
->OnTargetListReceived(connection_id
, host
, targets
);
824 STLDeleteContainerPointers(targets
.begin(), targets
.end());
828 void DevToolsHttpHandlerImpl::OnTargetListReceived(
830 const std::string
& host
,
831 const DevToolsManagerDelegate::TargetList
& targets
) {
832 DevToolsManagerDelegate::TargetList sorted_targets
= targets
;
833 std::sort(sorted_targets
.begin(), sorted_targets
.end(), TimeComparator
);
835 STLDeleteValues(&target_map_
);
836 base::ListValue list_value
;
837 for (DevToolsManagerDelegate::TargetList::const_iterator it
=
838 sorted_targets
.begin(); it
!= sorted_targets
.end(); ++it
) {
839 DevToolsTarget
* target
= *it
;
840 target_map_
[target
->GetId()] = target
;
841 list_value
.Append(SerializeTarget(*target
, host
));
843 SendJson(connection_id
, net::HTTP_OK
, &list_value
, std::string());
846 DevToolsTarget
* DevToolsHttpHandlerImpl::GetTarget(const std::string
& id
) {
847 TargetMap::const_iterator it
= target_map_
.find(id
);
848 if (it
== target_map_
.end())
853 void DevToolsHttpHandlerImpl::OnThumbnailRequest(
854 int connection_id
, const std::string
& target_id
) {
855 DevToolsTarget
* target
= GetTarget(target_id
);
858 page_url
= target
->GetURL();
859 DevToolsManagerDelegate
* manager_delegate
=
860 DevToolsManager::GetInstance()->delegate();
862 manager_delegate
? manager_delegate
->GetPageThumbnailData(page_url
) : "";
864 Send200(connection_id
, data
, "image/png");
866 Send404(connection_id
);
869 void DevToolsHttpHandlerImpl::OnDiscoveryPageRequest(int connection_id
) {
870 std::string response
= delegate_
->GetDiscoveryPageHTML();
871 Send200(connection_id
, response
, "text/html; charset=UTF-8");
874 void DevToolsHttpHandlerImpl::OnWebSocketRequest(
876 const net::HttpServerRequestInfo
& request
) {
880 std::string browser_prefix
= "/devtools/browser";
881 size_t browser_pos
= request
.path
.find(browser_prefix
);
882 if (browser_pos
== 0) {
883 browser_targets_
[connection_id
] = new BrowserTarget(thread_
->message_loop(),
887 AcceptWebSocket(connection_id
, request
);
891 size_t pos
= request
.path
.find(kPageUrlPrefix
);
893 Send404(connection_id
);
897 std::string page_id
= request
.path
.substr(strlen(kPageUrlPrefix
));
898 DevToolsTarget
* target
= GetTarget(page_id
);
899 scoped_refptr
<DevToolsAgentHost
> agent
=
900 target
? target
->GetAgentHost() : NULL
;
902 Send500(connection_id
, "No such target id: " + page_id
);
906 if (agent
->IsAttached()) {
907 Send500(connection_id
,
908 "Target with given id is being inspected: " + page_id
);
912 DevToolsAgentHostClientImpl
* client_host
= new DevToolsAgentHostClientImpl(
913 thread_
->message_loop(), server_wrapper_
, connection_id
, agent
.get());
914 connection_to_client_
[connection_id
] = client_host
;
916 AcceptWebSocket(connection_id
, request
);
919 void DevToolsHttpHandlerImpl::OnWebSocketMessage(
921 const std::string
& data
) {
922 BrowserTargets::iterator browser_it
= browser_targets_
.find(connection_id
);
923 if (browser_it
!= browser_targets_
.end()) {
924 browser_it
->second
->HandleMessage(data
);
928 ConnectionToClientMap::iterator it
=
929 connection_to_client_
.find(connection_id
);
930 if (it
!= connection_to_client_
.end())
931 it
->second
->OnMessage(data
);
934 void DevToolsHttpHandlerImpl::OnClose(int connection_id
) {
935 BrowserTargets::iterator browser_it
= browser_targets_
.find(connection_id
);
936 if (browser_it
!= browser_targets_
.end()) {
937 delete browser_it
->second
;
938 browser_targets_
.erase(connection_id
);
942 ConnectionToClientMap::iterator it
=
943 connection_to_client_
.find(connection_id
);
944 if (it
!= connection_to_client_
.end()) {
946 connection_to_client_
.erase(connection_id
);
950 DevToolsHttpHandlerImpl::DevToolsHttpHandlerImpl(
951 scoped_ptr
<ServerSocketFactory
> server_socket_factory
,
952 const std::string
& frontend_url
,
953 DevToolsHttpHandlerDelegate
* delegate
,
954 const base::FilePath
& output_directory
)
956 frontend_url_(frontend_url
),
957 server_wrapper_(nullptr),
959 socket_factory_(nullptr),
960 weak_factory_(this) {
961 if (frontend_url_
.empty())
962 frontend_url_
= "/devtools/inspector.html";
964 BrowserThread::PostTask(
965 BrowserThread::FILE, FROM_HERE
,
966 base::Bind(&StartServerOnFile
,
967 weak_factory_
.GetWeakPtr(),
968 server_socket_factory
.release(),
970 delegate_
->GetDebugFrontendDir(),
971 delegate_
->BundlesFrontendResources()));
974 void DevToolsHttpHandlerImpl::ServerStarted(
975 base::Thread
* thread
,
976 ServerWrapper
* server_wrapper
,
977 ServerSocketFactory
* socket_factory
,
978 scoped_ptr
<net::IPEndPoint
> ip_address
) {
980 server_wrapper_
= server_wrapper
;
981 socket_factory_
= socket_factory
;
982 server_ip_address_
.swap(ip_address
);
985 void ServerWrapper::WriteActivePortToUserProfile(
986 const base::FilePath
& output_directory
) {
987 DCHECK(!output_directory
.empty());
988 net::IPEndPoint endpoint
;
990 if ((err
= server_
->GetLocalAddress(&endpoint
)) != net::OK
) {
991 LOG(ERROR
) << "Error " << err
<< " getting local address";
995 // Write this port to a well-known file in the profile directory
996 // so Telemetry can pick it up.
997 base::FilePath path
= output_directory
.Append(kDevToolsActivePortFileName
);
998 std::string port_string
= base::IntToString(endpoint
.port());
999 if (base::WriteFile(path
, port_string
.c_str(), port_string
.length()) < 0) {
1000 LOG(ERROR
) << "Error writing DevTools active port to file";
1004 void DevToolsHttpHandlerImpl::SendJson(int connection_id
,
1005 net::HttpStatusCode status_code
,
1007 const std::string
& message
) {
1011 // Serialize value and message.
1012 std::string json_value
;
1014 base::JSONWriter::WriteWithOptions(value
,
1015 base::JSONWriter::OPTIONS_PRETTY_PRINT
,
1018 std::string json_message
;
1019 scoped_ptr
<base::Value
> message_object(new base::StringValue(message
));
1020 base::JSONWriter::Write(message_object
.get(), &json_message
);
1022 net::HttpServerResponseInfo
response(status_code
);
1023 response
.SetBody(json_value
+ message
, "application/json; charset=UTF-8");
1025 thread_
->message_loop()->PostTask(
1027 base::Bind(&ServerWrapper::SendResponse
,
1028 base::Unretained(server_wrapper_
),
1033 void DevToolsHttpHandlerImpl::Send200(int connection_id
,
1034 const std::string
& data
,
1035 const std::string
& mime_type
) {
1038 thread_
->message_loop()->PostTask(
1040 base::Bind(&ServerWrapper::Send200
,
1041 base::Unretained(server_wrapper_
),
1047 void DevToolsHttpHandlerImpl::Send404(int connection_id
) {
1050 thread_
->message_loop()->PostTask(
1052 base::Bind(&ServerWrapper::Send404
,
1053 base::Unretained(server_wrapper_
),
1057 void DevToolsHttpHandlerImpl::Send500(int connection_id
,
1058 const std::string
& message
) {
1061 thread_
->message_loop()->PostTask(
1063 base::Bind(&ServerWrapper::Send500
,
1064 base::Unretained(server_wrapper_
),
1069 void DevToolsHttpHandlerImpl::AcceptWebSocket(
1071 const net::HttpServerRequestInfo
& request
) {
1074 thread_
->message_loop()->PostTask(
1076 base::Bind(&ServerWrapper::AcceptWebSocket
,
1077 base::Unretained(server_wrapper_
),
1082 base::DictionaryValue
* DevToolsHttpHandlerImpl::SerializeTarget(
1083 const DevToolsTarget
& target
,
1084 const std::string
& host
) {
1085 base::DictionaryValue
* dictionary
= new base::DictionaryValue
;
1087 std::string id
= target
.GetId();
1088 dictionary
->SetString(kTargetIdField
, id
);
1089 std::string parent_id
= target
.GetParentId();
1090 if (!parent_id
.empty())
1091 dictionary
->SetString(kTargetParentIdField
, parent_id
);
1092 dictionary
->SetString(kTargetTypeField
, target
.GetType());
1093 dictionary
->SetString(kTargetTitleField
,
1094 net::EscapeForHTML(target
.GetTitle()));
1095 dictionary
->SetString(kTargetDescriptionField
, target
.GetDescription());
1097 GURL url
= target
.GetURL();
1098 dictionary
->SetString(kTargetUrlField
, url
.spec());
1100 GURL favicon_url
= target
.GetFaviconURL();
1101 if (favicon_url
.is_valid())
1102 dictionary
->SetString(kTargetFaviconUrlField
, favicon_url
.spec());
1104 DevToolsManagerDelegate
* manager_delegate
=
1105 DevToolsManager::GetInstance()->delegate();
1106 if (manager_delegate
&&
1107 !manager_delegate
->GetPageThumbnailData(url
).empty()) {
1108 dictionary
->SetString(kTargetThumbnailUrlField
,
1109 std::string(kThumbUrlPrefix
) + id
);
1112 if (!target
.IsAttached()) {
1113 dictionary
->SetString(kTargetWebSocketDebuggerUrlField
,
1114 base::StringPrintf("ws://%s%s%s",
1118 std::string devtools_frontend_url
= GetFrontendURLInternal(
1121 dictionary
->SetString(
1122 kTargetDevtoolsFrontendUrlField
, devtools_frontend_url
);
1128 } // namespace content