Add chrome://flags entry for --try-supported-channel-layouts
[chromium-blink-merge.git] / components / devtools_http_handler / devtools_http_handler.cc
blob2a72d069f3423123b9e6346517ebbc6fdea88a9f
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 <algorithm>
6 #include <utility>
8 #include "base/bind.h"
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/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/threading/thread.h"
19 #include "base/values.h"
20 #include "components/devtools_discovery/devtools_discovery_manager.h"
21 #include "components/devtools_http_handler/devtools_http_handler.h"
22 #include "components/devtools_http_handler/devtools_http_handler_delegate.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/devtools_agent_host.h"
25 #include "content/public/common/url_constants.h"
26 #include "content/public/common/user_agent.h"
27 #include "net/base/escape.h"
28 #include "net/base/io_buffer.h"
29 #include "net/base/ip_endpoint.h"
30 #include "net/base/net_errors.h"
31 #include "net/server/http_server.h"
32 #include "net/server/http_server_request_info.h"
33 #include "net/server/http_server_response_info.h"
34 #include "net/socket/server_socket.h"
36 #if defined(OS_ANDROID)
37 #include "base/android/build_info.h"
38 #endif
40 using content::BrowserThread;
41 using content::DevToolsAgentHost;
42 using content::DevToolsAgentHostClient;
43 using devtools_discovery::DevToolsTargetDescriptor;
45 namespace devtools_http_handler {
47 namespace {
49 const base::FilePath::CharType kDevToolsActivePortFileName[] =
50 FILE_PATH_LITERAL("DevToolsActivePort");
52 const char kDevToolsHandlerThreadName[] = "Chrome_DevToolsHandlerThread";
54 const char kThumbUrlPrefix[] = "/thumb/";
55 const char kPageUrlPrefix[] = "/devtools/page/";
57 const char kTargetIdField[] = "id";
58 const char kTargetParentIdField[] = "parentId";
59 const char kTargetTypeField[] = "type";
60 const char kTargetTitleField[] = "title";
61 const char kTargetDescriptionField[] = "description";
62 const char kTargetUrlField[] = "url";
63 const char kTargetThumbnailUrlField[] = "thumbnailUrl";
64 const char kTargetFaviconUrlField[] = "faviconUrl";
65 const char kTargetWebSocketDebuggerUrlField[] = "webSocketDebuggerUrl";
66 const char kTargetDevtoolsFrontendUrlField[] = "devtoolsFrontendUrl";
68 // Maximum write buffer size of devtools http/websocket connections.
69 // TODO(rmcilroy/pfieldman): Reduce this back to 100Mb when we have
70 // added back pressure on the TraceComplete message protocol - crbug.com/456845.
71 const int32 kSendBufferSizeForDevTools = 256 * 1024 * 1024; // 256Mb
73 } // namespace
75 // ServerWrapper -------------------------------------------------------------
76 // All methods in this class are only called on handler thread.
77 class ServerWrapper : net::HttpServer::Delegate {
78 public:
79 ServerWrapper(base::WeakPtr<DevToolsHttpHandler> handler,
80 scoped_ptr<net::ServerSocket> socket,
81 const base::FilePath& frontend_dir,
82 bool bundles_resources);
84 int GetLocalAddress(net::IPEndPoint* address);
86 void AcceptWebSocket(int connection_id,
87 const net::HttpServerRequestInfo& request);
88 void SendOverWebSocket(int connection_id, const std::string& message);
89 void SendResponse(int connection_id,
90 const net::HttpServerResponseInfo& response);
91 void Send200(int connection_id,
92 const std::string& data,
93 const std::string& mime_type);
94 void Send404(int connection_id);
95 void Send500(int connection_id, const std::string& message);
96 void Close(int connection_id);
98 void WriteActivePortToUserProfile(const base::FilePath& output_directory);
100 virtual ~ServerWrapper() {}
102 private:
103 // net::HttpServer::Delegate implementation.
104 void OnConnect(int connection_id) override {}
105 void OnHttpRequest(int connection_id,
106 const net::HttpServerRequestInfo& info) override;
107 void OnWebSocketRequest(int connection_id,
108 const net::HttpServerRequestInfo& info) override;
109 void OnWebSocketMessage(int connection_id,
110 const std::string& data) override;
111 void OnClose(int connection_id) override;
113 base::WeakPtr<DevToolsHttpHandler> handler_;
114 scoped_ptr<net::HttpServer> server_;
115 base::FilePath frontend_dir_;
116 bool bundles_resources_;
119 ServerWrapper::ServerWrapper(base::WeakPtr<DevToolsHttpHandler> handler,
120 scoped_ptr<net::ServerSocket> socket,
121 const base::FilePath& frontend_dir,
122 bool bundles_resources)
123 : handler_(handler),
124 server_(new net::HttpServer(socket.Pass(), this)),
125 frontend_dir_(frontend_dir),
126 bundles_resources_(bundles_resources) {
129 int ServerWrapper::GetLocalAddress(net::IPEndPoint* address) {
130 return server_->GetLocalAddress(address);
133 void ServerWrapper::AcceptWebSocket(int connection_id,
134 const net::HttpServerRequestInfo& request) {
135 server_->SetSendBufferSize(connection_id, kSendBufferSizeForDevTools);
136 server_->AcceptWebSocket(connection_id, request);
139 void ServerWrapper::SendOverWebSocket(int connection_id,
140 const std::string& message) {
141 server_->SendOverWebSocket(connection_id, message);
144 void ServerWrapper::SendResponse(int connection_id,
145 const net::HttpServerResponseInfo& response) {
146 server_->SendResponse(connection_id, response);
149 void ServerWrapper::Send200(int connection_id,
150 const std::string& data,
151 const std::string& mime_type) {
152 server_->Send200(connection_id, data, mime_type);
155 void ServerWrapper::Send404(int connection_id) {
156 server_->Send404(connection_id);
159 void ServerWrapper::Send500(int connection_id,
160 const std::string& message) {
161 server_->Send500(connection_id, message);
164 void ServerWrapper::Close(int connection_id) {
165 server_->Close(connection_id);
168 // Thread and ServerWrapper lifetime management ------------------------------
170 void TerminateOnUI(base::Thread* thread,
171 ServerWrapper* server_wrapper,
172 DevToolsHttpHandler::ServerSocketFactory* socket_factory) {
173 DCHECK_CURRENTLY_ON(BrowserThread::UI);
174 if (server_wrapper) {
175 DCHECK(thread);
176 thread->message_loop()->DeleteSoon(FROM_HERE, server_wrapper);
178 if (socket_factory) {
179 DCHECK(thread);
180 thread->message_loop()->DeleteSoon(FROM_HERE, socket_factory);
182 if (thread) {
183 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, thread);
187 void ServerStartedOnUI(
188 base::WeakPtr<DevToolsHttpHandler> handler,
189 base::Thread* thread,
190 ServerWrapper* server_wrapper,
191 DevToolsHttpHandler::ServerSocketFactory* socket_factory,
192 scoped_ptr<net::IPEndPoint> ip_address) {
193 DCHECK_CURRENTLY_ON(BrowserThread::UI);
194 if (handler && thread && server_wrapper) {
195 handler->ServerStarted(thread, server_wrapper, socket_factory,
196 ip_address.Pass());
197 } else {
198 TerminateOnUI(thread, server_wrapper, socket_factory);
202 void StartServerOnHandlerThread(
203 base::WeakPtr<DevToolsHttpHandler> handler,
204 base::Thread* thread,
205 DevToolsHttpHandler::ServerSocketFactory* server_socket_factory,
206 const base::FilePath& output_directory,
207 const base::FilePath& frontend_dir,
208 bool bundles_resources) {
209 DCHECK_EQ(thread->message_loop(), base::MessageLoop::current());
210 ServerWrapper* server_wrapper = nullptr;
211 scoped_ptr<net::ServerSocket> server_socket =
212 server_socket_factory->CreateForHttpServer();
213 scoped_ptr<net::IPEndPoint> ip_address(new net::IPEndPoint);
214 if (server_socket) {
215 server_wrapper = new ServerWrapper(handler, server_socket.Pass(),
216 frontend_dir, bundles_resources);
217 if (!output_directory.empty())
218 server_wrapper->WriteActivePortToUserProfile(output_directory);
220 if (server_wrapper->GetLocalAddress(ip_address.get()) != net::OK)
221 ip_address.reset();
222 } else {
223 ip_address.reset();
224 LOG(ERROR) << "Cannot start http server for devtools. Stop devtools.";
226 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
227 base::Bind(&ServerStartedOnUI,
228 handler,
229 thread,
230 server_wrapper,
231 server_socket_factory,
232 base::Passed(&ip_address)));
235 void StartServerOnFile(
236 base::WeakPtr<DevToolsHttpHandler> handler,
237 DevToolsHttpHandler::ServerSocketFactory* server_socket_factory,
238 const base::FilePath& output_directory,
239 const base::FilePath& frontend_dir,
240 bool bundles_resources) {
241 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
242 scoped_ptr<base::Thread> thread(new base::Thread(kDevToolsHandlerThreadName));
243 base::Thread::Options options;
244 options.message_loop_type = base::MessageLoop::TYPE_IO;
245 if (thread->StartWithOptions(options)) {
246 base::MessageLoop* message_loop = thread->message_loop();
247 message_loop->PostTask(FROM_HERE,
248 base::Bind(&StartServerOnHandlerThread,
249 handler,
250 base::Unretained(thread.release()),
251 server_socket_factory,
252 output_directory,
253 frontend_dir,
254 bundles_resources));
258 // DevToolsAgentHostClientImpl -----------------------------------------------
259 // An internal implementation of DevToolsAgentHostClient that delegates
260 // messages sent to a DebuggerShell instance.
261 class DevToolsAgentHostClientImpl : public DevToolsAgentHostClient {
262 public:
263 DevToolsAgentHostClientImpl(base::MessageLoop* message_loop,
264 ServerWrapper* server_wrapper,
265 int connection_id,
266 scoped_refptr<DevToolsAgentHost> agent_host)
267 : message_loop_(message_loop),
268 server_wrapper_(server_wrapper),
269 connection_id_(connection_id),
270 agent_host_(agent_host) {
271 agent_host_->AttachClient(this);
274 ~DevToolsAgentHostClientImpl() override {
275 if (agent_host_.get())
276 agent_host_->DetachClient();
279 void AgentHostClosed(DevToolsAgentHost* agent_host,
280 bool replaced_with_another_client) override {
281 DCHECK(agent_host == agent_host_.get());
283 std::string message = base::StringPrintf(
284 "{ \"method\": \"Inspector.detached\", "
285 "\"params\": { \"reason\": \"%s\"} }",
286 replaced_with_another_client ?
287 "replaced_with_devtools" : "target_closed");
288 DispatchProtocolMessage(agent_host, message);
290 agent_host_ = nullptr;
291 message_loop_->PostTask(
292 FROM_HERE,
293 base::Bind(&ServerWrapper::Close,
294 base::Unretained(server_wrapper_),
295 connection_id_));
298 void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
299 const std::string& message) override {
300 DCHECK(agent_host == agent_host_.get());
301 message_loop_->PostTask(
302 FROM_HERE,
303 base::Bind(&ServerWrapper::SendOverWebSocket,
304 base::Unretained(server_wrapper_),
305 connection_id_,
306 message));
309 void OnMessage(const std::string& message) {
310 if (agent_host_.get())
311 agent_host_->DispatchProtocolMessage(message);
314 private:
315 base::MessageLoop* const message_loop_;
316 ServerWrapper* const server_wrapper_;
317 const int connection_id_;
318 scoped_refptr<DevToolsAgentHost> agent_host_;
321 static bool TimeComparator(const DevToolsTargetDescriptor* desc1,
322 const DevToolsTargetDescriptor* desc2) {
323 return desc1->GetLastActivityTime() > desc2->GetLastActivityTime();
326 // DevToolsHttpHandler::ServerSocketFactory ----------------------------------
328 scoped_ptr<net::ServerSocket>
329 DevToolsHttpHandler::ServerSocketFactory::CreateForHttpServer() {
330 return scoped_ptr<net::ServerSocket>();
333 scoped_ptr<net::ServerSocket>
334 DevToolsHttpHandler::ServerSocketFactory::CreateForTethering(
335 std::string* name) {
336 return scoped_ptr<net::ServerSocket>();
339 // DevToolsHttpHandler -------------------------------------------------------
341 DevToolsHttpHandler::~DevToolsHttpHandler() {
342 TerminateOnUI(thread_, server_wrapper_, socket_factory_);
343 STLDeleteValues(&descriptor_map_);
344 STLDeleteValues(&connection_to_client_);
347 GURL DevToolsHttpHandler::GetFrontendURL(const std::string& path) {
348 if (!server_ip_address_)
349 return GURL();
350 return GURL(std::string("http://") + server_ip_address_->ToString() +
351 (path.empty() ? frontend_url_ : path));
354 static std::string PathWithoutParams(const std::string& path) {
355 size_t query_position = path.find("?");
356 if (query_position != std::string::npos)
357 return path.substr(0, query_position);
358 return path;
361 static std::string GetMimeType(const std::string& filename) {
362 if (EndsWith(filename, ".html", false)) {
363 return "text/html";
364 } else if (EndsWith(filename, ".css", false)) {
365 return "text/css";
366 } else if (EndsWith(filename, ".js", false)) {
367 return "application/javascript";
368 } else if (EndsWith(filename, ".png", false)) {
369 return "image/png";
370 } else if (EndsWith(filename, ".gif", false)) {
371 return "image/gif";
372 } else if (EndsWith(filename, ".json", false)) {
373 return "application/json";
375 LOG(ERROR) << "GetMimeType doesn't know mime type for: "
376 << filename
377 << " text/plain will be returned";
378 NOTREACHED();
379 return "text/plain";
382 void ServerWrapper::OnHttpRequest(int connection_id,
383 const net::HttpServerRequestInfo& info) {
384 server_->SetSendBufferSize(connection_id, kSendBufferSizeForDevTools);
386 if (info.path.find("/json") == 0) {
387 BrowserThread::PostTask(
388 BrowserThread::UI,
389 FROM_HERE,
390 base::Bind(&DevToolsHttpHandler::OnJsonRequest,
391 handler_,
392 connection_id,
393 info));
394 return;
397 if (info.path.find(kThumbUrlPrefix) == 0) {
398 // Thumbnail request.
399 const std::string target_id = info.path.substr(strlen(kThumbUrlPrefix));
400 BrowserThread::PostTask(
401 BrowserThread::UI,
402 FROM_HERE,
403 base::Bind(&DevToolsHttpHandler::OnThumbnailRequest,
404 handler_,
405 connection_id,
406 target_id));
407 return;
410 if (info.path.empty() || info.path == "/") {
411 // Discovery page request.
412 BrowserThread::PostTask(
413 BrowserThread::UI,
414 FROM_HERE,
415 base::Bind(&DevToolsHttpHandler::OnDiscoveryPageRequest,
416 handler_,
417 connection_id));
418 return;
421 if (info.path.find("/devtools/") != 0) {
422 server_->Send404(connection_id);
423 return;
426 std::string filename = PathWithoutParams(info.path.substr(10));
427 std::string mime_type = GetMimeType(filename);
429 if (!frontend_dir_.empty()) {
430 base::FilePath path = frontend_dir_.AppendASCII(filename);
431 std::string data;
432 base::ReadFileToString(path, &data);
433 server_->Send200(connection_id, data, mime_type);
434 return;
437 if (bundles_resources_) {
438 BrowserThread::PostTask(
439 BrowserThread::UI,
440 FROM_HERE,
441 base::Bind(&DevToolsHttpHandler::OnFrontendResourceRequest,
442 handler_,
443 connection_id,
444 filename));
445 return;
447 server_->Send404(connection_id);
450 void ServerWrapper::OnWebSocketRequest(
451 int connection_id,
452 const net::HttpServerRequestInfo& request) {
453 BrowserThread::PostTask(
454 BrowserThread::UI,
455 FROM_HERE,
456 base::Bind(
457 &DevToolsHttpHandler::OnWebSocketRequest,
458 handler_,
459 connection_id,
460 request));
463 void ServerWrapper::OnWebSocketMessage(int connection_id,
464 const std::string& data) {
465 BrowserThread::PostTask(
466 BrowserThread::UI,
467 FROM_HERE,
468 base::Bind(
469 &DevToolsHttpHandler::OnWebSocketMessage,
470 handler_,
471 connection_id,
472 data));
475 void ServerWrapper::OnClose(int connection_id) {
476 BrowserThread::PostTask(
477 BrowserThread::UI,
478 FROM_HERE,
479 base::Bind(
480 &DevToolsHttpHandler::OnClose,
481 handler_,
482 connection_id));
485 std::string DevToolsHttpHandler::GetFrontendURLInternal(
486 const std::string id,
487 const std::string& host) {
488 return base::StringPrintf(
489 "%s%sws=%s%s%s",
490 frontend_url_.c_str(),
491 frontend_url_.find("?") == std::string::npos ? "?" : "&",
492 host.c_str(),
493 kPageUrlPrefix,
494 id.c_str());
497 static bool ParseJsonPath(
498 const std::string& path,
499 std::string* command,
500 std::string* target_id) {
502 // Fall back to list in case of empty query.
503 if (path.empty()) {
504 *command = "list";
505 return true;
508 if (path.find("/") != 0) {
509 // Malformed command.
510 return false;
512 *command = path.substr(1);
514 size_t separator_pos = command->find("/");
515 if (separator_pos != std::string::npos) {
516 *target_id = command->substr(separator_pos + 1);
517 *command = command->substr(0, separator_pos);
519 return true;
522 void DevToolsHttpHandler::OnJsonRequest(
523 int connection_id,
524 const net::HttpServerRequestInfo& info) {
525 // Trim /json
526 std::string path = info.path.substr(5);
528 // Trim fragment and query
529 std::string query;
530 size_t query_pos = path.find("?");
531 if (query_pos != std::string::npos) {
532 query = path.substr(query_pos + 1);
533 path = path.substr(0, query_pos);
536 size_t fragment_pos = path.find("#");
537 if (fragment_pos != std::string::npos)
538 path = path.substr(0, fragment_pos);
540 std::string command;
541 std::string target_id;
542 if (!ParseJsonPath(path, &command, &target_id)) {
543 SendJson(connection_id,
544 net::HTTP_NOT_FOUND,
545 NULL,
546 "Malformed query: " + info.path);
547 return;
550 if (command == "version") {
551 base::DictionaryValue version;
552 version.SetString("Protocol-Version",
553 DevToolsAgentHost::GetProtocolVersion().c_str());
554 version.SetString("WebKit-Version", content::GetWebKitVersion());
555 version.SetString("Browser", product_name_);
556 version.SetString("User-Agent", user_agent_);
557 #if defined(OS_ANDROID)
558 version.SetString("Android-Package",
559 base::android::BuildInfo::GetInstance()->package_name());
560 #endif
561 SendJson(connection_id, net::HTTP_OK, &version, std::string());
562 return;
565 if (command == "list") {
566 std::string host = info.headers["host"];
567 DevToolsTargetDescriptor::List descriptors =
568 devtools_discovery::DevToolsDiscoveryManager::GetInstance()->
569 GetDescriptors();
570 std::sort(descriptors.begin(), descriptors.end(), TimeComparator);
571 STLDeleteValues(&descriptor_map_);
572 base::ListValue list_value;
573 for (DevToolsTargetDescriptor* descriptor : descriptors) {
574 descriptor_map_[descriptor->GetId()] = descriptor;
575 list_value.Append(SerializeDescriptor(*descriptor, host));
577 SendJson(connection_id, net::HTTP_OK, &list_value, std::string());
578 return;
581 if (command == "new") {
582 GURL url(net::UnescapeURLComponent(
583 query, net::UnescapeRule::URL_SPECIAL_CHARS));
584 if (!url.is_valid())
585 url = GURL(url::kAboutBlankURL);
586 scoped_ptr<DevToolsTargetDescriptor> descriptor =
587 devtools_discovery::DevToolsDiscoveryManager::GetInstance()->
588 CreateNew(url);
589 if (!descriptor) {
590 SendJson(connection_id,
591 net::HTTP_INTERNAL_SERVER_ERROR,
592 NULL,
593 "Could not create new page");
594 return;
596 std::string host = info.headers["host"];
597 scoped_ptr<base::DictionaryValue> dictionary(
598 SerializeDescriptor(*descriptor.get(), host));
599 SendJson(connection_id, net::HTTP_OK, dictionary.get(), std::string());
600 const std::string target_id = descriptor->GetId();
601 descriptor_map_[target_id] = descriptor.release();
602 return;
605 if (command == "activate" || command == "close") {
606 DevToolsTargetDescriptor* descriptor = GetDescriptor(target_id);
607 if (!descriptor) {
608 SendJson(connection_id,
609 net::HTTP_NOT_FOUND,
610 NULL,
611 "No such target id: " + target_id);
612 return;
615 if (command == "activate") {
616 if (descriptor->Activate()) {
617 SendJson(connection_id, net::HTTP_OK, NULL, "Target activated");
618 } else {
619 SendJson(connection_id,
620 net::HTTP_INTERNAL_SERVER_ERROR,
621 NULL,
622 "Could not activate target id: " + target_id);
624 return;
627 if (command == "close") {
628 if (descriptor->Close()) {
629 SendJson(connection_id, net::HTTP_OK, NULL, "Target is closing");
630 } else {
631 SendJson(connection_id,
632 net::HTTP_INTERNAL_SERVER_ERROR,
633 NULL,
634 "Could not close target id: " + target_id);
636 return;
639 SendJson(connection_id,
640 net::HTTP_NOT_FOUND,
641 NULL,
642 "Unknown command: " + command);
643 return;
646 DevToolsTargetDescriptor* DevToolsHttpHandler::GetDescriptor(
647 const std::string& target_id) {
648 DescriptorMap::const_iterator it = descriptor_map_.find(target_id);
649 if (it == descriptor_map_.end())
650 return nullptr;
651 return it->second;
654 void DevToolsHttpHandler::OnThumbnailRequest(
655 int connection_id, const std::string& target_id) {
656 DevToolsTargetDescriptor* descriptor = GetDescriptor(target_id);
657 GURL page_url;
658 if (descriptor)
659 page_url = descriptor->GetURL();
660 std::string data = delegate_->GetPageThumbnailData(page_url);
661 if (!data.empty())
662 Send200(connection_id, data, "image/png");
663 else
664 Send404(connection_id);
667 void DevToolsHttpHandler::OnDiscoveryPageRequest(int connection_id) {
668 std::string response = delegate_->GetDiscoveryPageHTML();
669 Send200(connection_id, response, "text/html; charset=UTF-8");
672 void DevToolsHttpHandler::OnFrontendResourceRequest(
673 int connection_id, const std::string& path) {
674 Send200(connection_id,
675 delegate_->GetFrontendResource(path),
676 GetMimeType(path));
679 void DevToolsHttpHandler::OnWebSocketRequest(
680 int connection_id,
681 const net::HttpServerRequestInfo& request) {
682 if (!thread_)
683 return;
685 std::string browser_prefix = "/devtools/browser";
686 size_t browser_pos = request.path.find(browser_prefix);
687 if (browser_pos == 0) {
688 scoped_refptr<DevToolsAgentHost> browser_agent =
689 DevToolsAgentHost::CreateForBrowser(
690 thread_->message_loop_proxy(),
691 base::Bind(&ServerSocketFactory::CreateForTethering,
692 base::Unretained(socket_factory_)));
693 connection_to_client_[connection_id] = new DevToolsAgentHostClientImpl(
694 thread_->message_loop(), server_wrapper_, connection_id, browser_agent);
695 AcceptWebSocket(connection_id, request);
696 return;
699 size_t pos = request.path.find(kPageUrlPrefix);
700 if (pos != 0) {
701 Send404(connection_id);
702 return;
705 std::string target_id = request.path.substr(strlen(kPageUrlPrefix));
706 DevToolsTargetDescriptor* descriptor = GetDescriptor(target_id);
707 scoped_refptr<DevToolsAgentHost> agent =
708 descriptor ? descriptor->GetAgentHost() : nullptr;
709 if (!agent.get()) {
710 Send500(connection_id, "No such target id: " + target_id);
711 return;
714 if (agent->IsAttached()) {
715 Send500(connection_id,
716 "Target with given id is being inspected: " + target_id);
717 return;
720 DevToolsAgentHostClientImpl* client_host = new DevToolsAgentHostClientImpl(
721 thread_->message_loop(), server_wrapper_, connection_id, agent);
722 connection_to_client_[connection_id] = client_host;
724 AcceptWebSocket(connection_id, request);
727 void DevToolsHttpHandler::OnWebSocketMessage(
728 int connection_id,
729 const std::string& data) {
730 ConnectionToClientMap::iterator it =
731 connection_to_client_.find(connection_id);
732 if (it != connection_to_client_.end())
733 it->second->OnMessage(data);
736 void DevToolsHttpHandler::OnClose(int connection_id) {
737 ConnectionToClientMap::iterator it =
738 connection_to_client_.find(connection_id);
739 if (it != connection_to_client_.end()) {
740 delete it->second;
741 connection_to_client_.erase(connection_id);
745 DevToolsHttpHandler::DevToolsHttpHandler(
746 scoped_ptr<ServerSocketFactory> server_socket_factory,
747 const std::string& frontend_url,
748 DevToolsHttpHandlerDelegate* delegate,
749 const base::FilePath& output_directory,
750 const base::FilePath& debug_frontend_dir,
751 const std::string& product_name,
752 const std::string& user_agent)
753 : thread_(nullptr),
754 frontend_url_(frontend_url),
755 product_name_(product_name),
756 user_agent_(user_agent),
757 server_wrapper_(nullptr),
758 delegate_(delegate),
759 socket_factory_(nullptr),
760 weak_factory_(this) {
761 bool bundles_resources = frontend_url_.empty();
762 if (frontend_url_.empty())
763 frontend_url_ = "/devtools/inspector.html";
765 BrowserThread::PostTask(
766 BrowserThread::FILE, FROM_HERE,
767 base::Bind(&StartServerOnFile,
768 weak_factory_.GetWeakPtr(),
769 server_socket_factory.release(),
770 output_directory,
771 debug_frontend_dir,
772 bundles_resources));
775 void DevToolsHttpHandler::ServerStarted(
776 base::Thread* thread,
777 ServerWrapper* server_wrapper,
778 ServerSocketFactory* socket_factory,
779 scoped_ptr<net::IPEndPoint> ip_address) {
780 thread_ = thread;
781 server_wrapper_ = server_wrapper;
782 socket_factory_ = socket_factory;
783 server_ip_address_.swap(ip_address);
786 void ServerWrapper::WriteActivePortToUserProfile(
787 const base::FilePath& output_directory) {
788 DCHECK(!output_directory.empty());
789 net::IPEndPoint endpoint;
790 int err;
791 if ((err = server_->GetLocalAddress(&endpoint)) != net::OK) {
792 LOG(ERROR) << "Error " << err << " getting local address";
793 return;
796 // Write this port to a well-known file in the profile directory
797 // so Telemetry can pick it up.
798 base::FilePath path = output_directory.Append(kDevToolsActivePortFileName);
799 std::string port_string = base::IntToString(endpoint.port());
800 if (base::WriteFile(path, port_string.c_str(),
801 static_cast<int>(port_string.length())) < 0) {
802 LOG(ERROR) << "Error writing DevTools active port to file";
806 void DevToolsHttpHandler::SendJson(int connection_id,
807 net::HttpStatusCode status_code,
808 base::Value* value,
809 const std::string& message) {
810 if (!thread_)
811 return;
813 // Serialize value and message.
814 std::string json_value;
815 if (value) {
816 base::JSONWriter::WriteWithOptions(value,
817 base::JSONWriter::OPTIONS_PRETTY_PRINT,
818 &json_value);
820 std::string json_message;
821 scoped_ptr<base::Value> message_object(new base::StringValue(message));
822 base::JSONWriter::Write(message_object.get(), &json_message);
824 net::HttpServerResponseInfo response(status_code);
825 response.SetBody(json_value + message, "application/json; charset=UTF-8");
827 thread_->message_loop()->PostTask(
828 FROM_HERE,
829 base::Bind(&ServerWrapper::SendResponse,
830 base::Unretained(server_wrapper_),
831 connection_id,
832 response));
835 void DevToolsHttpHandler::Send200(int connection_id,
836 const std::string& data,
837 const std::string& mime_type) {
838 if (!thread_)
839 return;
840 thread_->message_loop()->PostTask(
841 FROM_HERE,
842 base::Bind(&ServerWrapper::Send200,
843 base::Unretained(server_wrapper_),
844 connection_id,
845 data,
846 mime_type));
849 void DevToolsHttpHandler::Send404(int connection_id) {
850 if (!thread_)
851 return;
852 thread_->message_loop()->PostTask(
853 FROM_HERE,
854 base::Bind(&ServerWrapper::Send404,
855 base::Unretained(server_wrapper_),
856 connection_id));
859 void DevToolsHttpHandler::Send500(int connection_id,
860 const std::string& message) {
861 if (!thread_)
862 return;
863 thread_->message_loop()->PostTask(
864 FROM_HERE,
865 base::Bind(&ServerWrapper::Send500,
866 base::Unretained(server_wrapper_),
867 connection_id,
868 message));
871 void DevToolsHttpHandler::AcceptWebSocket(
872 int connection_id,
873 const net::HttpServerRequestInfo& request) {
874 if (!thread_)
875 return;
876 thread_->message_loop()->PostTask(
877 FROM_HERE,
878 base::Bind(&ServerWrapper::AcceptWebSocket,
879 base::Unretained(server_wrapper_),
880 connection_id,
881 request));
884 base::DictionaryValue* DevToolsHttpHandler::SerializeDescriptor(
885 const DevToolsTargetDescriptor& descriptor,
886 const std::string& host) {
887 base::DictionaryValue* dictionary = new base::DictionaryValue;
889 std::string id = descriptor.GetId();
890 dictionary->SetString(kTargetIdField, id);
891 std::string parent_id = descriptor.GetParentId();
892 if (!parent_id.empty())
893 dictionary->SetString(kTargetParentIdField, parent_id);
894 dictionary->SetString(kTargetTypeField, descriptor.GetType());
895 dictionary->SetString(kTargetTitleField,
896 net::EscapeForHTML(descriptor.GetTitle()));
897 dictionary->SetString(kTargetDescriptionField, descriptor.GetDescription());
899 GURL url = descriptor.GetURL();
900 dictionary->SetString(kTargetUrlField, url.spec());
902 GURL favicon_url = descriptor.GetFaviconURL();
903 if (favicon_url.is_valid())
904 dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());
906 if (!delegate_->GetPageThumbnailData(url).empty()) {
907 dictionary->SetString(kTargetThumbnailUrlField,
908 std::string(kThumbUrlPrefix) + id);
911 if (!descriptor.IsAttached()) {
912 dictionary->SetString(kTargetWebSocketDebuggerUrlField,
913 base::StringPrintf("ws://%s%s%s",
914 host.c_str(),
915 kPageUrlPrefix,
916 id.c_str()));
917 std::string devtools_frontend_url = GetFrontendURLInternal(
918 id.c_str(),
919 host);
920 dictionary->SetString(
921 kTargetDevtoolsFrontendUrlField, devtools_frontend_url);
924 return dictionary;
927 } // namespace devtools_http_handler