Remove unused did_first_* fields from InternalDocumentStateData.
[chromium-blink-merge.git] / components / devtools_http_handler / devtools_http_handler.cc
blob40f7edc83b41e3a2fae59c0408d644bf7f788272
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/location.h"
13 #include "base/logging.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/threading/thread.h"
20 #include "base/values.h"
21 #include "components/devtools_discovery/devtools_discovery_manager.h"
22 #include "components/devtools_http_handler/devtools_http_handler.h"
23 #include "components/devtools_http_handler/devtools_http_handler_delegate.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/devtools_agent_host.h"
26 #include "content/public/common/url_constants.h"
27 #include "content/public/common/user_agent.h"
28 #include "net/base/escape.h"
29 #include "net/base/io_buffer.h"
30 #include "net/base/ip_endpoint.h"
31 #include "net/base/net_errors.h"
32 #include "net/server/http_server.h"
33 #include "net/server/http_server_request_info.h"
34 #include "net/server/http_server_response_info.h"
35 #include "net/socket/server_socket.h"
37 #if defined(OS_ANDROID)
38 #include "base/android/build_info.h"
39 #endif
41 using content::BrowserThread;
42 using content::DevToolsAgentHost;
43 using content::DevToolsAgentHostClient;
44 using devtools_discovery::DevToolsTargetDescriptor;
46 namespace devtools_http_handler {
48 namespace {
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
74 } // namespace
76 // ServerWrapper -------------------------------------------------------------
77 // All methods in this class are only called on handler thread.
78 class ServerWrapper : net::HttpServer::Delegate {
79 public:
80 ServerWrapper(base::WeakPtr<DevToolsHttpHandler> handler,
81 scoped_ptr<net::ServerSocket> socket,
82 const base::FilePath& frontend_dir,
83 bool bundles_resources);
85 int GetLocalAddress(net::IPEndPoint* address);
87 void AcceptWebSocket(int connection_id,
88 const net::HttpServerRequestInfo& request);
89 void SendOverWebSocket(int connection_id, const std::string& message);
90 void SendResponse(int connection_id,
91 const net::HttpServerResponseInfo& response);
92 void Send200(int connection_id,
93 const std::string& data,
94 const std::string& mime_type);
95 void Send404(int connection_id);
96 void Send500(int connection_id, const std::string& message);
97 void Close(int connection_id);
99 void WriteActivePortToUserProfile(const base::FilePath& output_directory);
101 ~ServerWrapper() override {}
103 private:
104 // net::HttpServer::Delegate implementation.
105 void OnConnect(int connection_id) override {}
106 void OnHttpRequest(int connection_id,
107 const net::HttpServerRequestInfo& info) override;
108 void OnWebSocketRequest(int connection_id,
109 const net::HttpServerRequestInfo& info) override;
110 void OnWebSocketMessage(int connection_id,
111 const std::string& data) override;
112 void OnClose(int connection_id) override;
114 base::WeakPtr<DevToolsHttpHandler> handler_;
115 scoped_ptr<net::HttpServer> server_;
116 base::FilePath frontend_dir_;
117 bool bundles_resources_;
120 ServerWrapper::ServerWrapper(base::WeakPtr<DevToolsHttpHandler> handler,
121 scoped_ptr<net::ServerSocket> socket,
122 const base::FilePath& frontend_dir,
123 bool bundles_resources)
124 : handler_(handler),
125 server_(new net::HttpServer(socket.Pass(), this)),
126 frontend_dir_(frontend_dir),
127 bundles_resources_(bundles_resources) {
130 int ServerWrapper::GetLocalAddress(net::IPEndPoint* address) {
131 return server_->GetLocalAddress(address);
134 void ServerWrapper::AcceptWebSocket(int connection_id,
135 const net::HttpServerRequestInfo& request) {
136 server_->SetSendBufferSize(connection_id, kSendBufferSizeForDevTools);
137 server_->AcceptWebSocket(connection_id, request);
140 void ServerWrapper::SendOverWebSocket(int connection_id,
141 const std::string& message) {
142 server_->SendOverWebSocket(connection_id, message);
145 void ServerWrapper::SendResponse(int connection_id,
146 const net::HttpServerResponseInfo& response) {
147 server_->SendResponse(connection_id, response);
150 void ServerWrapper::Send200(int connection_id,
151 const std::string& data,
152 const std::string& mime_type) {
153 server_->Send200(connection_id, data, mime_type);
156 void ServerWrapper::Send404(int connection_id) {
157 server_->Send404(connection_id);
160 void ServerWrapper::Send500(int connection_id,
161 const std::string& message) {
162 server_->Send500(connection_id, message);
165 void ServerWrapper::Close(int connection_id) {
166 server_->Close(connection_id);
169 // Thread and ServerWrapper lifetime management ------------------------------
171 void TerminateOnUI(base::Thread* thread,
172 ServerWrapper* server_wrapper,
173 DevToolsHttpHandler::ServerSocketFactory* socket_factory) {
174 DCHECK_CURRENTLY_ON(BrowserThread::UI);
175 if (server_wrapper) {
176 DCHECK(thread);
177 thread->message_loop()->DeleteSoon(FROM_HERE, server_wrapper);
179 if (socket_factory) {
180 DCHECK(thread);
181 thread->message_loop()->DeleteSoon(FROM_HERE, socket_factory);
183 if (thread) {
184 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, thread);
188 void ServerStartedOnUI(
189 base::WeakPtr<DevToolsHttpHandler> handler,
190 base::Thread* thread,
191 ServerWrapper* server_wrapper,
192 DevToolsHttpHandler::ServerSocketFactory* socket_factory,
193 scoped_ptr<net::IPEndPoint> ip_address) {
194 DCHECK_CURRENTLY_ON(BrowserThread::UI);
195 if (handler && thread && server_wrapper) {
196 handler->ServerStarted(thread, server_wrapper, socket_factory,
197 ip_address.Pass());
198 } else {
199 TerminateOnUI(thread, server_wrapper, socket_factory);
203 void StartServerOnHandlerThread(
204 base::WeakPtr<DevToolsHttpHandler> handler,
205 base::Thread* thread,
206 DevToolsHttpHandler::ServerSocketFactory* server_socket_factory,
207 const base::FilePath& output_directory,
208 const base::FilePath& frontend_dir,
209 bool bundles_resources) {
210 DCHECK_EQ(thread->message_loop(), base::MessageLoop::current());
211 ServerWrapper* server_wrapper = nullptr;
212 scoped_ptr<net::ServerSocket> server_socket =
213 server_socket_factory->CreateForHttpServer();
214 scoped_ptr<net::IPEndPoint> ip_address(new net::IPEndPoint);
215 if (server_socket) {
216 server_wrapper = new ServerWrapper(handler, server_socket.Pass(),
217 frontend_dir, bundles_resources);
218 if (!output_directory.empty())
219 server_wrapper->WriteActivePortToUserProfile(output_directory);
221 if (server_wrapper->GetLocalAddress(ip_address.get()) != net::OK)
222 ip_address.reset();
223 } else {
224 ip_address.reset();
225 LOG(ERROR) << "Cannot start http server for devtools. Stop devtools.";
227 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
228 base::Bind(&ServerStartedOnUI,
229 handler,
230 thread,
231 server_wrapper,
232 server_socket_factory,
233 base::Passed(&ip_address)));
236 void StartServerOnFile(
237 base::WeakPtr<DevToolsHttpHandler> handler,
238 DevToolsHttpHandler::ServerSocketFactory* server_socket_factory,
239 const base::FilePath& output_directory,
240 const base::FilePath& frontend_dir,
241 bool bundles_resources) {
242 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
243 scoped_ptr<base::Thread> thread(new base::Thread(kDevToolsHandlerThreadName));
244 base::Thread::Options options;
245 options.message_loop_type = base::MessageLoop::TYPE_IO;
246 if (thread->StartWithOptions(options)) {
247 base::MessageLoop* message_loop = thread->message_loop();
248 message_loop->task_runner()->PostTask(
249 FROM_HERE,
250 base::Bind(&StartServerOnHandlerThread, handler,
251 base::Unretained(thread.release()), server_socket_factory,
252 output_directory, frontend_dir, bundles_resources));
256 // DevToolsAgentHostClientImpl -----------------------------------------------
257 // An internal implementation of DevToolsAgentHostClient that delegates
258 // messages sent to a DebuggerShell instance.
259 class DevToolsAgentHostClientImpl : public DevToolsAgentHostClient {
260 public:
261 DevToolsAgentHostClientImpl(base::MessageLoop* message_loop,
262 ServerWrapper* server_wrapper,
263 int connection_id,
264 scoped_refptr<DevToolsAgentHost> agent_host)
265 : message_loop_(message_loop),
266 server_wrapper_(server_wrapper),
267 connection_id_(connection_id),
268 agent_host_(agent_host) {
269 DCHECK_CURRENTLY_ON(BrowserThread::UI);
270 agent_host_->AttachClient(this);
273 ~DevToolsAgentHostClientImpl() override {
274 DCHECK_CURRENTLY_ON(BrowserThread::UI);
275 if (agent_host_.get())
276 agent_host_->DetachClient();
279 void AgentHostClosed(DevToolsAgentHost* agent_host,
280 bool replaced_with_another_client) override {
281 DCHECK_CURRENTLY_ON(BrowserThread::UI);
282 DCHECK(agent_host == agent_host_.get());
284 std::string message = base::StringPrintf(
285 "{ \"method\": \"Inspector.detached\", "
286 "\"params\": { \"reason\": \"%s\"} }",
287 replaced_with_another_client ?
288 "replaced_with_devtools" : "target_closed");
289 DispatchProtocolMessage(agent_host, message);
291 agent_host_ = nullptr;
292 message_loop_->task_runner()->PostTask(
293 FROM_HERE,
294 base::Bind(&ServerWrapper::Close, base::Unretained(server_wrapper_),
295 connection_id_));
298 void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
299 const std::string& message) override {
300 DCHECK_CURRENTLY_ON(BrowserThread::UI);
301 DCHECK(agent_host == agent_host_.get());
302 message_loop_->task_runner()->PostTask(
303 FROM_HERE,
304 base::Bind(&ServerWrapper::SendOverWebSocket,
305 base::Unretained(server_wrapper_), connection_id_, message));
308 void OnMessage(const std::string& message) {
309 DCHECK_CURRENTLY_ON(BrowserThread::UI);
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 (base::EndsWith(filename, ".html", base::CompareCase::INSENSITIVE_ASCII)) {
363 return "text/html";
364 } else if (base::EndsWith(filename, ".css",
365 base::CompareCase::INSENSITIVE_ASCII)) {
366 return "text/css";
367 } else if (base::EndsWith(filename, ".js",
368 base::CompareCase::INSENSITIVE_ASCII)) {
369 return "application/javascript";
370 } else if (base::EndsWith(filename, ".png",
371 base::CompareCase::INSENSITIVE_ASCII)) {
372 return "image/png";
373 } else if (base::EndsWith(filename, ".gif",
374 base::CompareCase::INSENSITIVE_ASCII)) {
375 return "image/gif";
376 } else if (base::EndsWith(filename, ".json",
377 base::CompareCase::INSENSITIVE_ASCII)) {
378 return "application/json";
380 LOG(ERROR) << "GetMimeType doesn't know mime type for: "
381 << filename
382 << " text/plain will be returned";
383 NOTREACHED();
384 return "text/plain";
387 void ServerWrapper::OnHttpRequest(int connection_id,
388 const net::HttpServerRequestInfo& info) {
389 server_->SetSendBufferSize(connection_id, kSendBufferSizeForDevTools);
391 if (info.path.find("/json") == 0) {
392 BrowserThread::PostTask(
393 BrowserThread::UI,
394 FROM_HERE,
395 base::Bind(&DevToolsHttpHandler::OnJsonRequest,
396 handler_,
397 connection_id,
398 info));
399 return;
402 if (info.path.find(kThumbUrlPrefix) == 0) {
403 // Thumbnail request.
404 const std::string target_id = info.path.substr(strlen(kThumbUrlPrefix));
405 BrowserThread::PostTask(
406 BrowserThread::UI,
407 FROM_HERE,
408 base::Bind(&DevToolsHttpHandler::OnThumbnailRequest,
409 handler_,
410 connection_id,
411 target_id));
412 return;
415 if (info.path.empty() || info.path == "/") {
416 // Discovery page request.
417 BrowserThread::PostTask(
418 BrowserThread::UI,
419 FROM_HERE,
420 base::Bind(&DevToolsHttpHandler::OnDiscoveryPageRequest,
421 handler_,
422 connection_id));
423 return;
426 if (info.path.find("/devtools/") != 0) {
427 server_->Send404(connection_id);
428 return;
431 std::string filename = PathWithoutParams(info.path.substr(10));
432 std::string mime_type = GetMimeType(filename);
434 if (!frontend_dir_.empty()) {
435 base::FilePath path = frontend_dir_.AppendASCII(filename);
436 std::string data;
437 base::ReadFileToString(path, &data);
438 server_->Send200(connection_id, data, mime_type);
439 return;
442 if (bundles_resources_) {
443 BrowserThread::PostTask(
444 BrowserThread::UI,
445 FROM_HERE,
446 base::Bind(&DevToolsHttpHandler::OnFrontendResourceRequest,
447 handler_,
448 connection_id,
449 filename));
450 return;
452 server_->Send404(connection_id);
455 void ServerWrapper::OnWebSocketRequest(
456 int connection_id,
457 const net::HttpServerRequestInfo& request) {
458 BrowserThread::PostTask(
459 BrowserThread::UI,
460 FROM_HERE,
461 base::Bind(
462 &DevToolsHttpHandler::OnWebSocketRequest,
463 handler_,
464 connection_id,
465 request));
468 void ServerWrapper::OnWebSocketMessage(int connection_id,
469 const std::string& data) {
470 BrowserThread::PostTask(
471 BrowserThread::UI,
472 FROM_HERE,
473 base::Bind(
474 &DevToolsHttpHandler::OnWebSocketMessage,
475 handler_,
476 connection_id,
477 data));
480 void ServerWrapper::OnClose(int connection_id) {
481 BrowserThread::PostTask(
482 BrowserThread::UI,
483 FROM_HERE,
484 base::Bind(
485 &DevToolsHttpHandler::OnClose,
486 handler_,
487 connection_id));
490 std::string DevToolsHttpHandler::GetFrontendURLInternal(
491 const std::string id,
492 const std::string& host) {
493 return base::StringPrintf(
494 "%s%sws=%s%s%s",
495 frontend_url_.c_str(),
496 frontend_url_.find("?") == std::string::npos ? "?" : "&",
497 host.c_str(),
498 kPageUrlPrefix,
499 id.c_str());
502 static bool ParseJsonPath(
503 const std::string& path,
504 std::string* command,
505 std::string* target_id) {
507 // Fall back to list in case of empty query.
508 if (path.empty()) {
509 *command = "list";
510 return true;
513 if (path.find("/") != 0) {
514 // Malformed command.
515 return false;
517 *command = path.substr(1);
519 size_t separator_pos = command->find("/");
520 if (separator_pos != std::string::npos) {
521 *target_id = command->substr(separator_pos + 1);
522 *command = command->substr(0, separator_pos);
524 return true;
527 void DevToolsHttpHandler::OnJsonRequest(
528 int connection_id,
529 const net::HttpServerRequestInfo& info) {
530 // Trim /json
531 std::string path = info.path.substr(5);
533 // Trim fragment and query
534 std::string query;
535 size_t query_pos = path.find("?");
536 if (query_pos != std::string::npos) {
537 query = path.substr(query_pos + 1);
538 path = path.substr(0, query_pos);
541 size_t fragment_pos = path.find("#");
542 if (fragment_pos != std::string::npos)
543 path = path.substr(0, fragment_pos);
545 std::string command;
546 std::string target_id;
547 if (!ParseJsonPath(path, &command, &target_id)) {
548 SendJson(connection_id,
549 net::HTTP_NOT_FOUND,
550 NULL,
551 "Malformed query: " + info.path);
552 return;
555 if (command == "version") {
556 base::DictionaryValue version;
557 version.SetString("Protocol-Version",
558 DevToolsAgentHost::GetProtocolVersion().c_str());
559 version.SetString("WebKit-Version", content::GetWebKitVersion());
560 version.SetString("Browser", product_name_);
561 version.SetString("User-Agent", user_agent_);
562 #if defined(OS_ANDROID)
563 version.SetString("Android-Package",
564 base::android::BuildInfo::GetInstance()->package_name());
565 #endif
566 SendJson(connection_id, net::HTTP_OK, &version, std::string());
567 return;
570 if (command == "list") {
571 std::string host = info.headers["host"];
572 DevToolsTargetDescriptor::List descriptors =
573 devtools_discovery::DevToolsDiscoveryManager::GetInstance()->
574 GetDescriptors();
575 std::sort(descriptors.begin(), descriptors.end(), TimeComparator);
576 STLDeleteValues(&descriptor_map_);
577 base::ListValue list_value;
578 for (DevToolsTargetDescriptor* descriptor : descriptors) {
579 descriptor_map_[descriptor->GetId()] = descriptor;
580 list_value.Append(SerializeDescriptor(*descriptor, host));
582 SendJson(connection_id, net::HTTP_OK, &list_value, std::string());
583 return;
586 if (command == "new") {
587 GURL url(net::UnescapeURLComponent(
588 query, net::UnescapeRule::URL_SPECIAL_CHARS));
589 if (!url.is_valid())
590 url = GURL(url::kAboutBlankURL);
591 scoped_ptr<DevToolsTargetDescriptor> descriptor =
592 devtools_discovery::DevToolsDiscoveryManager::GetInstance()->
593 CreateNew(url);
594 if (!descriptor) {
595 SendJson(connection_id,
596 net::HTTP_INTERNAL_SERVER_ERROR,
597 NULL,
598 "Could not create new page");
599 return;
601 std::string host = info.headers["host"];
602 scoped_ptr<base::DictionaryValue> dictionary(
603 SerializeDescriptor(*descriptor.get(), host));
604 SendJson(connection_id, net::HTTP_OK, dictionary.get(), std::string());
605 const std::string target_id = descriptor->GetId();
606 descriptor_map_[target_id] = descriptor.release();
607 return;
610 if (command == "activate" || command == "close") {
611 DevToolsTargetDescriptor* descriptor = GetDescriptor(target_id);
612 if (!descriptor) {
613 SendJson(connection_id,
614 net::HTTP_NOT_FOUND,
615 NULL,
616 "No such target id: " + target_id);
617 return;
620 if (command == "activate") {
621 if (descriptor->Activate()) {
622 SendJson(connection_id, net::HTTP_OK, NULL, "Target activated");
623 } else {
624 SendJson(connection_id,
625 net::HTTP_INTERNAL_SERVER_ERROR,
626 NULL,
627 "Could not activate target id: " + target_id);
629 return;
632 if (command == "close") {
633 if (descriptor->Close()) {
634 SendJson(connection_id, net::HTTP_OK, NULL, "Target is closing");
635 } else {
636 SendJson(connection_id,
637 net::HTTP_INTERNAL_SERVER_ERROR,
638 NULL,
639 "Could not close target id: " + target_id);
641 return;
644 SendJson(connection_id,
645 net::HTTP_NOT_FOUND,
646 NULL,
647 "Unknown command: " + command);
648 return;
651 DevToolsTargetDescriptor* DevToolsHttpHandler::GetDescriptor(
652 const std::string& target_id) {
653 DescriptorMap::const_iterator it = descriptor_map_.find(target_id);
654 if (it == descriptor_map_.end())
655 return nullptr;
656 return it->second;
659 void DevToolsHttpHandler::OnThumbnailRequest(
660 int connection_id, const std::string& target_id) {
661 DevToolsTargetDescriptor* descriptor = GetDescriptor(target_id);
662 GURL page_url;
663 if (descriptor)
664 page_url = descriptor->GetURL();
665 std::string data = delegate_->GetPageThumbnailData(page_url);
666 if (!data.empty())
667 Send200(connection_id, data, "image/png");
668 else
669 Send404(connection_id);
672 void DevToolsHttpHandler::OnDiscoveryPageRequest(int connection_id) {
673 std::string response = delegate_->GetDiscoveryPageHTML();
674 Send200(connection_id, response, "text/html; charset=UTF-8");
677 void DevToolsHttpHandler::OnFrontendResourceRequest(
678 int connection_id, const std::string& path) {
679 Send200(connection_id,
680 delegate_->GetFrontendResource(path),
681 GetMimeType(path));
684 void DevToolsHttpHandler::OnWebSocketRequest(
685 int connection_id,
686 const net::HttpServerRequestInfo& request) {
687 if (!thread_)
688 return;
690 std::string browser_prefix = "/devtools/browser";
691 size_t browser_pos = request.path.find(browser_prefix);
692 if (browser_pos == 0) {
693 scoped_refptr<DevToolsAgentHost> browser_agent =
694 DevToolsAgentHost::CreateForBrowser(
695 thread_->task_runner(),
696 base::Bind(&ServerSocketFactory::CreateForTethering,
697 base::Unretained(socket_factory_)));
698 connection_to_client_[connection_id] = new DevToolsAgentHostClientImpl(
699 thread_->message_loop(), server_wrapper_, connection_id, browser_agent);
700 AcceptWebSocket(connection_id, request);
701 return;
704 size_t pos = request.path.find(kPageUrlPrefix);
705 if (pos != 0) {
706 Send404(connection_id);
707 return;
710 std::string target_id = request.path.substr(strlen(kPageUrlPrefix));
711 DevToolsTargetDescriptor* descriptor = GetDescriptor(target_id);
712 scoped_refptr<DevToolsAgentHost> agent =
713 descriptor ? descriptor->GetAgentHost() : nullptr;
714 if (!agent.get()) {
715 Send500(connection_id, "No such target id: " + target_id);
716 return;
719 if (agent->IsAttached()) {
720 Send500(connection_id,
721 "Target with given id is being inspected: " + target_id);
722 return;
725 DevToolsAgentHostClientImpl* client_host = new DevToolsAgentHostClientImpl(
726 thread_->message_loop(), server_wrapper_, connection_id, agent);
727 connection_to_client_[connection_id] = client_host;
729 AcceptWebSocket(connection_id, request);
732 void DevToolsHttpHandler::OnWebSocketMessage(
733 int connection_id,
734 const std::string& data) {
735 ConnectionToClientMap::iterator it =
736 connection_to_client_.find(connection_id);
737 if (it != connection_to_client_.end())
738 it->second->OnMessage(data);
741 void DevToolsHttpHandler::OnClose(int connection_id) {
742 ConnectionToClientMap::iterator it =
743 connection_to_client_.find(connection_id);
744 if (it != connection_to_client_.end()) {
745 delete it->second;
746 connection_to_client_.erase(connection_id);
750 DevToolsHttpHandler::DevToolsHttpHandler(
751 scoped_ptr<ServerSocketFactory> server_socket_factory,
752 const std::string& frontend_url,
753 DevToolsHttpHandlerDelegate* delegate,
754 const base::FilePath& output_directory,
755 const base::FilePath& debug_frontend_dir,
756 const std::string& product_name,
757 const std::string& user_agent)
758 : thread_(nullptr),
759 frontend_url_(frontend_url),
760 product_name_(product_name),
761 user_agent_(user_agent),
762 server_wrapper_(nullptr),
763 delegate_(delegate),
764 socket_factory_(nullptr),
765 weak_factory_(this) {
766 bool bundles_resources = frontend_url_.empty();
767 if (frontend_url_.empty())
768 frontend_url_ = "/devtools/inspector.html";
770 BrowserThread::PostTask(
771 BrowserThread::FILE, FROM_HERE,
772 base::Bind(&StartServerOnFile,
773 weak_factory_.GetWeakPtr(),
774 server_socket_factory.release(),
775 output_directory,
776 debug_frontend_dir,
777 bundles_resources));
780 void DevToolsHttpHandler::ServerStarted(
781 base::Thread* thread,
782 ServerWrapper* server_wrapper,
783 ServerSocketFactory* socket_factory,
784 scoped_ptr<net::IPEndPoint> ip_address) {
785 thread_ = thread;
786 server_wrapper_ = server_wrapper;
787 socket_factory_ = socket_factory;
788 server_ip_address_.swap(ip_address);
791 void ServerWrapper::WriteActivePortToUserProfile(
792 const base::FilePath& output_directory) {
793 DCHECK(!output_directory.empty());
794 net::IPEndPoint endpoint;
795 int err;
796 if ((err = server_->GetLocalAddress(&endpoint)) != net::OK) {
797 LOG(ERROR) << "Error " << err << " getting local address";
798 return;
801 // Write this port to a well-known file in the profile directory
802 // so Telemetry can pick it up.
803 base::FilePath path = output_directory.Append(kDevToolsActivePortFileName);
804 std::string port_string = base::IntToString(endpoint.port());
805 if (base::WriteFile(path, port_string.c_str(),
806 static_cast<int>(port_string.length())) < 0) {
807 LOG(ERROR) << "Error writing DevTools active port to file";
811 void DevToolsHttpHandler::SendJson(int connection_id,
812 net::HttpStatusCode status_code,
813 base::Value* value,
814 const std::string& message) {
815 if (!thread_)
816 return;
818 // Serialize value and message.
819 std::string json_value;
820 if (value) {
821 base::JSONWriter::WriteWithOptions(
822 *value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_value);
824 std::string json_message;
825 base::JSONWriter::Write(base::StringValue(message), &json_message);
827 net::HttpServerResponseInfo response(status_code);
828 response.SetBody(json_value + message, "application/json; charset=UTF-8");
830 thread_->task_runner()->PostTask(
831 FROM_HERE,
832 base::Bind(&ServerWrapper::SendResponse,
833 base::Unretained(server_wrapper_), connection_id, response));
836 void DevToolsHttpHandler::Send200(int connection_id,
837 const std::string& data,
838 const std::string& mime_type) {
839 if (!thread_)
840 return;
841 thread_->task_runner()->PostTask(
842 FROM_HERE,
843 base::Bind(&ServerWrapper::Send200, base::Unretained(server_wrapper_),
844 connection_id, data, mime_type));
847 void DevToolsHttpHandler::Send404(int connection_id) {
848 if (!thread_)
849 return;
850 thread_->task_runner()->PostTask(
851 FROM_HERE, base::Bind(&ServerWrapper::Send404,
852 base::Unretained(server_wrapper_), connection_id));
855 void DevToolsHttpHandler::Send500(int connection_id,
856 const std::string& message) {
857 if (!thread_)
858 return;
859 thread_->task_runner()->PostTask(
860 FROM_HERE,
861 base::Bind(&ServerWrapper::Send500, base::Unretained(server_wrapper_),
862 connection_id, message));
865 void DevToolsHttpHandler::AcceptWebSocket(
866 int connection_id,
867 const net::HttpServerRequestInfo& request) {
868 if (!thread_)
869 return;
870 thread_->task_runner()->PostTask(
871 FROM_HERE,
872 base::Bind(&ServerWrapper::AcceptWebSocket,
873 base::Unretained(server_wrapper_), connection_id, request));
876 base::DictionaryValue* DevToolsHttpHandler::SerializeDescriptor(
877 const DevToolsTargetDescriptor& descriptor,
878 const std::string& host) {
879 base::DictionaryValue* dictionary = new base::DictionaryValue;
881 std::string id = descriptor.GetId();
882 dictionary->SetString(kTargetIdField, id);
883 std::string parent_id = descriptor.GetParentId();
884 if (!parent_id.empty())
885 dictionary->SetString(kTargetParentIdField, parent_id);
886 dictionary->SetString(kTargetTypeField, descriptor.GetType());
887 dictionary->SetString(kTargetTitleField,
888 net::EscapeForHTML(descriptor.GetTitle()));
889 dictionary->SetString(kTargetDescriptionField, descriptor.GetDescription());
891 GURL url = descriptor.GetURL();
892 dictionary->SetString(kTargetUrlField, url.spec());
894 GURL favicon_url = descriptor.GetFaviconURL();
895 if (favicon_url.is_valid())
896 dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());
898 if (!delegate_->GetPageThumbnailData(url).empty()) {
899 dictionary->SetString(kTargetThumbnailUrlField,
900 std::string(kThumbUrlPrefix) + id);
903 if (!descriptor.IsAttached()) {
904 dictionary->SetString(kTargetWebSocketDebuggerUrlField,
905 base::StringPrintf("ws://%s%s%s",
906 host.c_str(),
907 kPageUrlPrefix,
908 id.c_str()));
909 std::string devtools_frontend_url = GetFrontendURLInternal(
910 id.c_str(),
911 host);
912 dictionary->SetString(
913 kTargetDevtoolsFrontendUrlField, devtools_frontend_url);
916 return dictionary;
919 } // namespace devtools_http_handler