Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / content / browser / devtools / devtools_http_handler_impl.cc
blob2a3e32b56649e1d01d1942acd378ed103d6c97a6
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"
7 #include <algorithm>
8 #include <utility>
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"
46 #endif
48 namespace content {
50 namespace {
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 {
77 public:
78 DevToolsAgentHostClientImpl(base::MessageLoop* message_loop,
79 net::HttpServer* server,
80 int connection_id,
81 DevToolsAgentHost* agent_host)
82 : message_loop_(message_loop),
83 server_(server),
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());
98 agent_host_ = NULL;
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(
109 FROM_HERE,
110 base::Bind(&net::HttpServer::SendOverWebSocket,
111 base::Unretained(server_),
112 connection_id_,
113 response));
115 message_loop_->PostTask(
116 FROM_HERE,
117 base::Bind(&net::HttpServer::Close,
118 base::Unretained(server_),
119 connection_id_));
122 virtual void DispatchProtocolMessage(
123 DevToolsAgentHost* agent_host, const std::string& message) OVERRIDE {
124 DCHECK(agent_host == agent_host_.get());
125 message_loop_->PostTask(
126 FROM_HERE,
127 base::Bind(&net::HttpServer::SendOverWebSocket,
128 base::Unretained(server_),
129 connection_id_,
130 message));
133 void OnMessage(const std::string& message) {
134 if (agent_host_.get())
135 agent_host_->DispatchProtocolMessage(message);
138 private:
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();
150 } // namespace
152 // static
153 bool DevToolsHttpHandler::IsSupportedProtocolVersion(
154 const std::string& version) {
155 return devtools::IsSupportedProtocolVersion(version);
158 // static
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;
164 return -1;
167 // static
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(),
175 frontend_url,
176 delegate,
177 active_port_output_directory);
178 http_handler->Start();
179 return http_handler;
182 DevToolsHttpHandler::ServerSocketFactory::ServerSocketFactory(
183 const std::string& address,
184 int port,
185 int backlog)
186 : address_(address),
187 port_(port),
188 backlog_(backlog) {
191 DevToolsHttpHandler::ServerSocketFactory::~ServerSocketFactory() {
194 scoped_ptr<net::ServerSocket>
195 DevToolsHttpHandler::ServerSocketFactory::CreateAndListen() const {
196 scoped_ptr<net::ServerSocket> socket = Create();
197 if (socket &&
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() {
213 if (thread_)
214 return;
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));
229 return;
232 thread_->message_loop()->PostTask(
233 FROM_HERE,
234 base::Bind(&DevToolsHttpHandlerImpl::Init, this));
237 void DevToolsHttpHandlerImpl::ResetHandlerThread() {
238 thread_.reset();
241 void DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease() {
242 ResetHandlerThread();
243 Release();
246 void DevToolsHttpHandlerImpl::Stop() {
247 if (!thread_)
248 return;
249 BrowserThread::PostTaskAndReply(
250 BrowserThread::FILE, FROM_HERE,
251 base::Bind(&DevToolsHttpHandlerImpl::StopHandlerThread, this),
252 base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease, this));
255 void DevToolsHttpHandlerImpl::StopWithoutRelease() {
256 if (!thread_)
257 return;
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))
267 return GURL();
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);
275 return path;
278 static std::string GetMimeType(const std::string& filename) {
279 if (EndsWith(filename, ".html", false)) {
280 return "text/html";
281 } else if (EndsWith(filename, ".css", false)) {
282 return "text/css";
283 } else if (EndsWith(filename, ".js", false)) {
284 return "application/javascript";
285 } else if (EndsWith(filename, ".png", false)) {
286 return "image/png";
287 } else if (EndsWith(filename, ".gif", false)) {
288 return "image/gif";
289 } else if (EndsWith(filename, ".json", false)) {
290 return "application/json";
292 LOG(ERROR) << "GetMimeType doesn't know mime type for: "
293 << filename
294 << " text/plain will be returned";
295 NOTREACHED();
296 return "text/plain";
299 void DevToolsHttpHandlerImpl::OnHttpRequest(
300 int connection_id,
301 const net::HttpServerRequestInfo& info) {
302 server_->SetSendBufferSize(connection_id, kSendBufferSizeForDevTools);
304 if (info.path.find("/json") == 0) {
305 BrowserThread::PostTask(
306 BrowserThread::UI,
307 FROM_HERE,
308 base::Bind(&DevToolsHttpHandlerImpl::OnJsonRequestUI,
309 this,
310 connection_id,
311 info));
312 return;
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);
319 GURL page_url;
320 if (target)
321 page_url = target->GetURL();
322 BrowserThread::PostTask(
323 BrowserThread::UI,
324 FROM_HERE,
325 base::Bind(&DevToolsHttpHandlerImpl::OnThumbnailRequestUI,
326 this,
327 connection_id,
328 page_url));
329 return;
332 if (info.path == "" || info.path == "/") {
333 // Discovery page request.
334 BrowserThread::PostTask(
335 BrowserThread::UI,
336 FROM_HERE,
337 base::Bind(&DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI,
338 this,
339 connection_id));
340 return;
343 if (info.path.find("/devtools/") != 0) {
344 server_->Send404(connection_id);
345 return;
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);
354 std::string data;
355 base::ReadFileToString(path, &data);
356 server_->Send200(connection_id, data, mime_type);
357 return;
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);
365 return;
368 server_->Send404(connection_id);
371 void DevToolsHttpHandlerImpl::OnWebSocketRequest(
372 int connection_id,
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);
395 return;
398 BrowserThread::PostTask(
399 BrowserThread::UI,
400 FROM_HERE,
401 base::Bind(
402 &DevToolsHttpHandlerImpl::OnWebSocketRequestUI,
403 this,
404 connection_id,
405 request));
408 void DevToolsHttpHandlerImpl::OnWebSocketMessage(
409 int connection_id,
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);
414 return;
417 BrowserThread::PostTask(
418 BrowserThread::UI,
419 FROM_HERE,
420 base::Bind(
421 &DevToolsHttpHandlerImpl::OnWebSocketMessageUI,
422 this,
423 connection_id,
424 data));
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);
432 return;
435 BrowserThread::PostTask(
436 BrowserThread::UI,
437 FROM_HERE,
438 base::Bind(
439 &DevToolsHttpHandlerImpl::OnCloseUI,
440 this,
441 connection_id));
444 std::string DevToolsHttpHandlerImpl::GetFrontendURLInternal(
445 const std::string id,
446 const std::string& host) {
447 return base::StringPrintf(
448 "%s%sws=%s%s%s",
449 frontend_url_.c_str(),
450 frontend_url_.find("?") == std::string::npos ? "?" : "&",
451 host.c_str(),
452 kPageUrlPrefix,
453 id.c_str());
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.
462 if (path.empty()) {
463 *command = "list";
464 return true;
467 if (path.find("/") != 0) {
468 // Malformed command.
469 return false;
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);
478 return true;
481 void DevToolsHttpHandlerImpl::OnJsonRequestUI(
482 int connection_id,
483 const net::HttpServerRequestInfo& info) {
484 // Trim /json
485 std::string path = info.path.substr(5);
487 // Trim fragment and query
488 std::string 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);
499 std::string command;
500 std::string target_id;
501 if (!ParseJsonPath(path, &command, &target_id)) {
502 SendJson(connection_id,
503 net::HTTP_NOT_FOUND,
504 NULL,
505 "Malformed query: " + info.path);
506 return;
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());
518 #endif
519 SendJson(connection_id, net::HTTP_OK, &version, std::string());
520 return;
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));
529 return;
532 if (command == "new") {
533 GURL url(net::UnescapeURLComponent(
534 query, net::UnescapeRule::URL_SPECIAL_CHARS));
535 if (!url.is_valid())
536 url = GURL(url::kAboutBlankURL);
537 scoped_ptr<DevToolsTarget> target(delegate_->CreateNewTarget(url));
538 if (!target) {
539 SendJson(connection_id,
540 net::HTTP_INTERNAL_SERVER_ERROR,
541 NULL,
542 "Could not create new page");
543 return;
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();
551 return;
554 if (command == "activate" || command == "close") {
555 DevToolsTarget* target = GetTarget(target_id);
556 if (!target) {
557 SendJson(connection_id,
558 net::HTTP_NOT_FOUND,
559 NULL,
560 "No such target id: " + target_id);
561 return;
564 if (command == "activate") {
565 if (target->Activate()) {
566 SendJson(connection_id, net::HTTP_OK, NULL, "Target activated");
567 } else {
568 SendJson(connection_id,
569 net::HTTP_INTERNAL_SERVER_ERROR,
570 NULL,
571 "Could not activate target id: " + target_id);
573 return;
576 if (command == "close") {
577 if (target->Close()) {
578 SendJson(connection_id, net::HTTP_OK, NULL, "Target is closing");
579 } else {
580 SendJson(connection_id,
581 net::HTTP_INTERNAL_SERVER_ERROR,
582 NULL,
583 "Could not close target id: " + target_id);
585 return;
588 SendJson(connection_id,
589 net::HTTP_NOT_FOUND,
590 NULL,
591 "Unknown command: " + command);
592 return;
595 void DevToolsHttpHandlerImpl::OnTargetListReceived(
596 int connection_id,
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())
617 return NULL;
618 return it->second;
621 void DevToolsHttpHandlerImpl::OnThumbnailRequestUI(
622 int connection_id, const GURL& page_url) {
623 std::string data = delegate_->GetPageThumbnailData(page_url);
624 if (!data.empty())
625 Send200(connection_id, data, "image/png");
626 else
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(
636 int connection_id,
637 const net::HttpServerRequestInfo& request) {
638 if (!thread_)
639 return;
641 size_t pos = request.path.find(kPageUrlPrefix);
642 if (pos != 0) {
643 Send404(connection_id);
644 return;
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;
651 if (!agent.get()) {
652 Send500(connection_id, "No such target id: " + page_id);
653 return;
656 if (agent->IsAttached()) {
657 Send500(connection_id,
658 "Target with given id is being inspected: " + page_id);
659 return;
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(
670 int connection_id,
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())
675 return;
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);
688 delete client;
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()),
700 delegate_(delegate),
701 active_port_output_directory_(active_port_output_directory) {
702 if (frontend_url_.empty())
703 frontend_url_ = "/devtools/devtools.html";
705 // Balanced in ResetHandlerThreadAndRelease().
706 AddRef();
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));
718 return;
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() {
728 server_.reset(NULL);
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())
735 return;
736 thread_->message_loop()->PostTask(
737 FROM_HERE,
738 base::Bind(&DevToolsHttpHandlerImpl::Teardown, this));
739 // Thread::Stop joins the thread.
740 thread_->Stop();
743 void DevToolsHttpHandlerImpl::WriteActivePortToUserProfile() {
744 DCHECK(!active_port_output_directory_.empty());
745 net::IPEndPoint endpoint;
746 int err;
747 if ((err = server_->GetLocalAddress(&endpoint)) != net::OK) {
748 LOG(ERROR) << "Error " << err << " getting local address";
749 return;
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,
764 base::Value* value,
765 const std::string& message) {
766 if (!thread_)
767 return;
769 // Serialize value and message.
770 std::string json_value;
771 if (value) {
772 base::JSONWriter::WriteWithOptions(value,
773 base::JSONWriter::OPTIONS_PRETTY_PRINT,
774 &json_value);
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(
784 FROM_HERE,
785 base::Bind(&net::HttpServer::SendResponse,
786 base::Unretained(server_.get()),
787 connection_id,
788 response));
791 void DevToolsHttpHandlerImpl::Send200(int connection_id,
792 const std::string& data,
793 const std::string& mime_type) {
794 if (!thread_)
795 return;
796 thread_->message_loop()->PostTask(
797 FROM_HERE,
798 base::Bind(&net::HttpServer::Send200,
799 base::Unretained(server_.get()),
800 connection_id,
801 data,
802 mime_type));
805 void DevToolsHttpHandlerImpl::Send404(int connection_id) {
806 if (!thread_)
807 return;
808 thread_->message_loop()->PostTask(
809 FROM_HERE,
810 base::Bind(&net::HttpServer::Send404,
811 base::Unretained(server_.get()),
812 connection_id));
815 void DevToolsHttpHandlerImpl::Send500(int connection_id,
816 const std::string& message) {
817 if (!thread_)
818 return;
819 thread_->message_loop()->PostTask(
820 FROM_HERE,
821 base::Bind(&net::HttpServer::Send500,
822 base::Unretained(server_.get()),
823 connection_id,
824 message));
827 void DevToolsHttpHandlerImpl::AcceptWebSocket(
828 int connection_id,
829 const net::HttpServerRequestInfo& request) {
830 if (!thread_)
831 return;
832 thread_->message_loop()->PostTask(
833 FROM_HERE,
834 base::Bind(&net::HttpServer::SetSendBufferSize,
835 base::Unretained(server_.get()),
836 connection_id,
837 kSendBufferSizeForDevTools));
838 thread_->message_loop()->PostTask(
839 FROM_HERE,
840 base::Bind(&net::HttpServer::AcceptWebSocket,
841 base::Unretained(server_.get()),
842 connection_id,
843 request));
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",
876 host.c_str(),
877 kPageUrlPrefix,
878 id.c_str()));
879 std::string devtools_frontend_url = GetFrontendURLInternal(
880 id.c_str(),
881 host);
882 dictionary->SetString(
883 kTargetDevtoolsFrontendUrlField, devtools_frontend_url);
886 return dictionary;
889 } // namespace content