Revert 268405 "Make sure that ScratchBuffer::Allocate() always r..."
[chromium-blink-merge.git] / content / browser / devtools / devtools_http_handler_impl.cc
blob35620d4dadeae019b34e63822002fb944684b4f2
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/threading/thread.h"
18 #include "base/values.h"
19 #include "content/browser/devtools/devtools_browser_target.h"
20 #include "content/browser/devtools/devtools_protocol.h"
21 #include "content/browser/devtools/devtools_protocol_constants.h"
22 #include "content/browser/devtools/devtools_system_info_handler.h"
23 #include "content/browser/devtools/devtools_tracing_handler.h"
24 #include "content/browser/devtools/tethering_handler.h"
25 #include "content/common/devtools_messages.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/devtools_agent_host.h"
28 #include "content/public/browser/devtools_client_host.h"
29 #include "content/public/browser/devtools_http_handler_delegate.h"
30 #include "content/public/browser/devtools_manager.h"
31 #include "content/public/browser/devtools_target.h"
32 #include "content/public/common/content_client.h"
33 #include "content/public/common/url_constants.h"
34 #include "content/public/common/user_agent.h"
35 #include "content/public/common/user_agent.h"
36 #include "grit/devtools_resources_map.h"
37 #include "net/base/escape.h"
38 #include "net/base/io_buffer.h"
39 #include "net/base/ip_endpoint.h"
40 #include "net/server/http_server_request_info.h"
41 #include "net/server/http_server_response_info.h"
43 #if defined(OS_ANDROID)
44 #include "base/android/build_info.h"
45 #endif
47 namespace content {
49 namespace {
51 const char kDevToolsHandlerThreadName[] = "Chrome_DevToolsHandlerThread";
53 const char kThumbUrlPrefix[] = "/thumb/";
54 const char kPageUrlPrefix[] = "/devtools/page/";
56 const char kTargetIdField[] = "id";
57 const char kTargetTypeField[] = "type";
58 const char kTargetTitleField[] = "title";
59 const char kTargetDescriptionField[] = "description";
60 const char kTargetUrlField[] = "url";
61 const char kTargetThumbnailUrlField[] = "thumbnailUrl";
62 const char kTargetFaviconUrlField[] = "faviconUrl";
63 const char kTargetWebSocketDebuggerUrlField[] = "webSocketDebuggerUrl";
64 const char kTargetDevtoolsFrontendUrlField[] = "devtoolsFrontendUrl";
66 // An internal implementation of DevToolsClientHost that delegates
67 // messages sent for DevToolsClient to a DebuggerShell instance.
68 class DevToolsClientHostImpl : public DevToolsClientHost {
69 public:
70 DevToolsClientHostImpl(base::MessageLoop* message_loop,
71 net::HttpServer* server,
72 int connection_id)
73 : message_loop_(message_loop),
74 server_(server),
75 connection_id_(connection_id),
76 is_closed_(false),
77 detach_reason_("target_closed") {}
79 virtual ~DevToolsClientHostImpl() {}
81 // DevToolsClientHost interface
82 virtual void InspectedContentsClosing() OVERRIDE {
83 if (is_closed_)
84 return;
85 is_closed_ = true;
87 base::DictionaryValue notification;
88 notification.SetString(
89 devtools::Inspector::detached::kParamReason, detach_reason_);
90 std::string response = DevToolsProtocol::CreateNotification(
91 devtools::Inspector::detached::kName,
92 notification.DeepCopy())->Serialize();
93 message_loop_->PostTask(
94 FROM_HERE,
95 base::Bind(&net::HttpServer::SendOverWebSocket,
96 server_,
97 connection_id_,
98 response));
100 message_loop_->PostTask(
101 FROM_HERE,
102 base::Bind(&net::HttpServer::Close, server_, connection_id_));
105 virtual void DispatchOnInspectorFrontend(const std::string& data) OVERRIDE {
106 message_loop_->PostTask(
107 FROM_HERE,
108 base::Bind(&net::HttpServer::SendOverWebSocket,
109 server_,
110 connection_id_,
111 data));
114 virtual void ReplacedWithAnotherClient() OVERRIDE {
115 detach_reason_ = "replaced_with_devtools";
118 private:
119 base::MessageLoop* message_loop_;
120 net::HttpServer* server_;
121 int connection_id_;
122 bool is_closed_;
123 std::string detach_reason_;
126 static bool TimeComparator(const DevToolsTarget* target1,
127 const DevToolsTarget* target2) {
128 return target1->GetLastActivityTime() > target2->GetLastActivityTime();
131 } // namespace
133 // static
134 bool DevToolsHttpHandler::IsSupportedProtocolVersion(
135 const std::string& version) {
136 return devtools::IsSupportedProtocolVersion(version);
139 // static
140 int DevToolsHttpHandler::GetFrontendResourceId(const std::string& name) {
141 for (size_t i = 0; i < kDevtoolsResourcesSize; ++i) {
142 if (name == kDevtoolsResources[i].name)
143 return kDevtoolsResources[i].value;
145 return -1;
148 // static
149 DevToolsHttpHandler* DevToolsHttpHandler::Start(
150 const net::StreamListenSocketFactory* socket_factory,
151 const std::string& frontend_url,
152 DevToolsHttpHandlerDelegate* delegate) {
153 DevToolsHttpHandlerImpl* http_handler =
154 new DevToolsHttpHandlerImpl(socket_factory,
155 frontend_url,
156 delegate);
157 http_handler->Start();
158 return http_handler;
161 DevToolsHttpHandlerImpl::~DevToolsHttpHandlerImpl() {
162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
163 // Stop() must be called prior to destruction.
164 DCHECK(server_.get() == NULL);
165 DCHECK(thread_.get() == NULL);
166 STLDeleteValues(&target_map_);
169 void DevToolsHttpHandlerImpl::Start() {
170 if (thread_)
171 return;
172 thread_.reset(new base::Thread(kDevToolsHandlerThreadName));
173 BrowserThread::PostTask(
174 BrowserThread::FILE, FROM_HERE,
175 base::Bind(&DevToolsHttpHandlerImpl::StartHandlerThread, this));
178 // Runs on FILE thread.
179 void DevToolsHttpHandlerImpl::StartHandlerThread() {
180 base::Thread::Options options;
181 options.message_loop_type = base::MessageLoop::TYPE_IO;
182 if (!thread_->StartWithOptions(options)) {
183 BrowserThread::PostTask(
184 BrowserThread::UI, FROM_HERE,
185 base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThread, this));
186 return;
189 thread_->message_loop()->PostTask(
190 FROM_HERE,
191 base::Bind(&DevToolsHttpHandlerImpl::Init, this));
194 void DevToolsHttpHandlerImpl::ResetHandlerThread() {
195 thread_.reset();
198 void DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease() {
199 ResetHandlerThread();
200 Release();
203 void DevToolsHttpHandlerImpl::Stop() {
204 if (!thread_)
205 return;
206 BrowserThread::PostTaskAndReply(
207 BrowserThread::FILE, FROM_HERE,
208 base::Bind(&DevToolsHttpHandlerImpl::StopHandlerThread, this),
209 base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease, this));
212 GURL DevToolsHttpHandlerImpl::GetFrontendURL() {
213 net::IPEndPoint ip_address;
214 if (server_->GetLocalAddress(&ip_address))
215 return GURL();
216 return GURL(std::string("http://") + ip_address.ToString() + frontend_url_);
219 static std::string PathWithoutParams(const std::string& path) {
220 size_t query_position = path.find("?");
221 if (query_position != std::string::npos)
222 return path.substr(0, query_position);
223 return path;
226 static std::string GetMimeType(const std::string& filename) {
227 if (EndsWith(filename, ".html", false)) {
228 return "text/html";
229 } else if (EndsWith(filename, ".css", false)) {
230 return "text/css";
231 } else if (EndsWith(filename, ".js", false)) {
232 return "application/javascript";
233 } else if (EndsWith(filename, ".png", false)) {
234 return "image/png";
235 } else if (EndsWith(filename, ".gif", false)) {
236 return "image/gif";
238 NOTREACHED();
239 return "text/plain";
242 void DevToolsHttpHandlerImpl::OnHttpRequest(
243 int connection_id,
244 const net::HttpServerRequestInfo& info) {
245 if (info.path.find("/json") == 0) {
246 BrowserThread::PostTask(
247 BrowserThread::UI,
248 FROM_HERE,
249 base::Bind(&DevToolsHttpHandlerImpl::OnJsonRequestUI,
250 this,
251 connection_id,
252 info));
253 return;
256 if (info.path.find(kThumbUrlPrefix) == 0) {
257 // Thumbnail request.
258 const std::string target_id = info.path.substr(strlen(kThumbUrlPrefix));
259 DevToolsTarget* target = GetTarget(target_id);
260 GURL page_url;
261 if (target)
262 page_url = target->GetUrl();
263 BrowserThread::PostTask(
264 BrowserThread::UI,
265 FROM_HERE,
266 base::Bind(&DevToolsHttpHandlerImpl::OnThumbnailRequestUI,
267 this,
268 connection_id,
269 page_url));
270 return;
273 if (info.path == "" || info.path == "/") {
274 // Discovery page request.
275 BrowserThread::PostTask(
276 BrowserThread::UI,
277 FROM_HERE,
278 base::Bind(&DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI,
279 this,
280 connection_id));
281 return;
284 if (info.path.find("/devtools/") != 0) {
285 server_->Send404(connection_id);
286 return;
289 std::string filename = PathWithoutParams(info.path.substr(10));
290 std::string mime_type = GetMimeType(filename);
292 base::FilePath frontend_dir = delegate_->GetDebugFrontendDir();
293 if (!frontend_dir.empty()) {
294 base::FilePath path = frontend_dir.AppendASCII(filename);
295 std::string data;
296 base::ReadFileToString(path, &data);
297 server_->Send200(connection_id, data, mime_type);
298 return;
300 if (delegate_->BundlesFrontendResources()) {
301 int resource_id = DevToolsHttpHandler::GetFrontendResourceId(filename);
302 if (resource_id != -1) {
303 base::StringPiece data = GetContentClient()->GetDataResource(
304 resource_id, ui::SCALE_FACTOR_NONE);
305 server_->Send200(connection_id, data.as_string(), mime_type);
306 return;
309 server_->Send404(connection_id);
312 void DevToolsHttpHandlerImpl::OnWebSocketRequest(
313 int connection_id,
314 const net::HttpServerRequestInfo& request) {
315 std::string browser_prefix = "/devtools/browser";
316 size_t browser_pos = request.path.find(browser_prefix);
317 if (browser_pos == 0) {
318 if (browser_target_) {
319 server_->Send500(connection_id, "Another client already attached");
320 return;
322 browser_target_ = new DevToolsBrowserTarget(server_.get(), connection_id);
323 browser_target_->RegisterDomainHandler(
324 devtools::Tracing::kName,
325 new DevToolsTracingHandler(),
326 true /* handle on UI thread */);
327 browser_target_->RegisterDomainHandler(
328 TetheringHandler::kDomain,
329 new TetheringHandler(delegate_.get()),
330 false /* handle on this thread */);
331 browser_target_->RegisterDomainHandler(
332 devtools::SystemInfo::kName,
333 new DevToolsSystemInfoHandler(),
334 true /* handle on UI thread */);
336 server_->AcceptWebSocket(connection_id, request);
337 return;
340 BrowserThread::PostTask(
341 BrowserThread::UI,
342 FROM_HERE,
343 base::Bind(
344 &DevToolsHttpHandlerImpl::OnWebSocketRequestUI,
345 this,
346 connection_id,
347 request));
350 void DevToolsHttpHandlerImpl::OnWebSocketMessage(
351 int connection_id,
352 const std::string& data) {
353 if (browser_target_ && connection_id == browser_target_->connection_id()) {
354 browser_target_->HandleMessage(data);
355 return;
358 BrowserThread::PostTask(
359 BrowserThread::UI,
360 FROM_HERE,
361 base::Bind(
362 &DevToolsHttpHandlerImpl::OnWebSocketMessageUI,
363 this,
364 connection_id,
365 data));
368 void DevToolsHttpHandlerImpl::OnClose(int connection_id) {
369 if (browser_target_ && browser_target_->connection_id() == connection_id) {
370 browser_target_->Detach();
371 browser_target_ = NULL;
372 return;
375 BrowserThread::PostTask(
376 BrowserThread::UI,
377 FROM_HERE,
378 base::Bind(
379 &DevToolsHttpHandlerImpl::OnCloseUI,
380 this,
381 connection_id));
384 std::string DevToolsHttpHandlerImpl::GetFrontendURLInternal(
385 const std::string id,
386 const std::string& host) {
387 return base::StringPrintf(
388 "%s%sws=%s%s%s",
389 frontend_url_.c_str(),
390 frontend_url_.find("?") == std::string::npos ? "?" : "&",
391 host.c_str(),
392 kPageUrlPrefix,
393 id.c_str());
396 static bool ParseJsonPath(
397 const std::string& path,
398 std::string* command,
399 std::string* target_id) {
401 // Fall back to list in case of empty query.
402 if (path.empty()) {
403 *command = "list";
404 return true;
407 if (path.find("/") != 0) {
408 // Malformed command.
409 return false;
411 *command = path.substr(1);
413 size_t separator_pos = command->find("/");
414 if (separator_pos != std::string::npos) {
415 *target_id = command->substr(separator_pos + 1);
416 *command = command->substr(0, separator_pos);
418 return true;
421 void DevToolsHttpHandlerImpl::OnJsonRequestUI(
422 int connection_id,
423 const net::HttpServerRequestInfo& info) {
424 // Trim /json
425 std::string path = info.path.substr(5);
427 // Trim fragment and query
428 std::string query;
429 size_t query_pos = path.find("?");
430 if (query_pos != std::string::npos) {
431 query = path.substr(query_pos + 1);
432 path = path.substr(0, query_pos);
435 size_t fragment_pos = path.find("#");
436 if (fragment_pos != std::string::npos)
437 path = path.substr(0, fragment_pos);
439 std::string command;
440 std::string target_id;
441 if (!ParseJsonPath(path, &command, &target_id)) {
442 SendJson(connection_id,
443 net::HTTP_NOT_FOUND,
444 NULL,
445 "Malformed query: " + info.path);
446 return;
449 if (command == "version") {
450 base::DictionaryValue version;
451 version.SetString("Protocol-Version", devtools::kProtocolVersion);
452 version.SetString("WebKit-Version", GetWebKitVersion());
453 version.SetString("Browser", GetContentClient()->GetProduct());
454 version.SetString("User-Agent", GetContentClient()->GetUserAgent());
455 #if defined(OS_ANDROID)
456 version.SetString("Android-Package",
457 base::android::BuildInfo::GetInstance()->package_name());
458 #endif
459 SendJson(connection_id, net::HTTP_OK, &version, std::string());
460 return;
463 if (command == "list") {
464 std::string host = info.headers["host"];
465 AddRef(); // Balanced in OnTargetListReceived.
466 delegate_->EnumerateTargets(
467 base::Bind(&DevToolsHttpHandlerImpl::OnTargetListReceived,
468 this, connection_id, host));
469 return;
472 if (command == "new") {
473 GURL url(net::UnescapeURLComponent(
474 query, net::UnescapeRule::URL_SPECIAL_CHARS));
475 if (!url.is_valid())
476 url = GURL(kAboutBlankURL);
477 scoped_ptr<DevToolsTarget> target(delegate_->CreateNewTarget(url));
478 if (!target) {
479 SendJson(connection_id,
480 net::HTTP_INTERNAL_SERVER_ERROR,
481 NULL,
482 "Could not create new page");
483 return;
485 std::string host = info.headers["host"];
486 scoped_ptr<base::DictionaryValue> dictionary(
487 SerializeTarget(*target.get(), host));
488 SendJson(connection_id, net::HTTP_OK, dictionary.get(), std::string());
489 const std::string target_id = target->GetId();
490 target_map_[target_id] = target.release();
491 return;
494 if (command == "activate" || command == "close") {
495 DevToolsTarget* target = GetTarget(target_id);
496 if (!target) {
497 SendJson(connection_id,
498 net::HTTP_NOT_FOUND,
499 NULL,
500 "No such target id: " + target_id);
501 return;
504 if (command == "activate") {
505 if (target->Activate()) {
506 SendJson(connection_id, net::HTTP_OK, NULL, "Target activated");
507 } else {
508 SendJson(connection_id,
509 net::HTTP_INTERNAL_SERVER_ERROR,
510 NULL,
511 "Could not activate target id: " + target_id);
513 return;
516 if (command == "close") {
517 if (target->Close()) {
518 SendJson(connection_id, net::HTTP_OK, NULL, "Target is closing");
519 } else {
520 SendJson(connection_id,
521 net::HTTP_INTERNAL_SERVER_ERROR,
522 NULL,
523 "Could not close target id: " + target_id);
525 return;
528 SendJson(connection_id,
529 net::HTTP_NOT_FOUND,
530 NULL,
531 "Unknown command: " + command);
532 return;
535 void DevToolsHttpHandlerImpl::OnTargetListReceived(
536 int connection_id,
537 const std::string& host,
538 const DevToolsHttpHandlerDelegate::TargetList& targets) {
539 DevToolsHttpHandlerDelegate::TargetList sorted_targets = targets;
540 std::sort(sorted_targets.begin(), sorted_targets.end(), TimeComparator);
542 STLDeleteValues(&target_map_);
543 base::ListValue list_value;
544 for (DevToolsHttpHandlerDelegate::TargetList::const_iterator it =
545 sorted_targets.begin(); it != sorted_targets.end(); ++it) {
546 DevToolsTarget* target = *it;
547 target_map_[target->GetId()] = target;
548 list_value.Append(SerializeTarget(*target, host));
550 SendJson(connection_id, net::HTTP_OK, &list_value, std::string());
551 Release(); // Balanced in OnJsonRequestUI.
554 DevToolsTarget* DevToolsHttpHandlerImpl::GetTarget(const std::string& id) {
555 TargetMap::const_iterator it = target_map_.find(id);
556 if (it == target_map_.end())
557 return NULL;
558 return it->second;
561 void DevToolsHttpHandlerImpl::OnThumbnailRequestUI(
562 int connection_id, const GURL& page_url) {
563 std::string data = delegate_->GetPageThumbnailData(page_url);
564 if (!data.empty())
565 Send200(connection_id, data, "image/png");
566 else
567 Send404(connection_id);
570 void DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI(int connection_id) {
571 std::string response = delegate_->GetDiscoveryPageHTML();
572 Send200(connection_id, response, "text/html; charset=UTF-8");
575 void DevToolsHttpHandlerImpl::OnWebSocketRequestUI(
576 int connection_id,
577 const net::HttpServerRequestInfo& request) {
578 if (!thread_)
579 return;
581 size_t pos = request.path.find(kPageUrlPrefix);
582 if (pos != 0) {
583 Send404(connection_id);
584 return;
587 std::string page_id = request.path.substr(strlen(kPageUrlPrefix));
588 DevToolsTarget* target = GetTarget(page_id);
589 scoped_refptr<DevToolsAgentHost> agent =
590 target ? target->GetAgentHost() : NULL;
591 if (!agent) {
592 Send500(connection_id, "No such target id: " + page_id);
593 return;
596 if (agent->IsAttached()) {
597 Send500(connection_id,
598 "Target with given id is being inspected: " + page_id);
599 return;
602 DevToolsClientHostImpl* client_host = new DevToolsClientHostImpl(
603 thread_->message_loop(), server_.get(), connection_id);
604 connection_to_client_host_ui_[connection_id] = client_host;
606 DevToolsManager::GetInstance()->
607 RegisterDevToolsClientHostFor(agent, client_host);
609 AcceptWebSocket(connection_id, request);
612 void DevToolsHttpHandlerImpl::OnWebSocketMessageUI(
613 int connection_id,
614 const std::string& data) {
615 ConnectionToClientHostMap::iterator it =
616 connection_to_client_host_ui_.find(connection_id);
617 if (it == connection_to_client_host_ui_.end())
618 return;
620 DevToolsManager* manager = DevToolsManager::GetInstance();
621 manager->DispatchOnInspectorBackend(it->second, data);
624 void DevToolsHttpHandlerImpl::OnCloseUI(int connection_id) {
625 ConnectionToClientHostMap::iterator it =
626 connection_to_client_host_ui_.find(connection_id);
627 if (it != connection_to_client_host_ui_.end()) {
628 DevToolsClientHostImpl* client_host =
629 static_cast<DevToolsClientHostImpl*>(it->second);
630 DevToolsManager::GetInstance()->ClientHostClosing(client_host);
631 delete client_host;
632 connection_to_client_host_ui_.erase(connection_id);
636 DevToolsHttpHandlerImpl::DevToolsHttpHandlerImpl(
637 const net::StreamListenSocketFactory* socket_factory,
638 const std::string& frontend_url,
639 DevToolsHttpHandlerDelegate* delegate)
640 : frontend_url_(frontend_url),
641 socket_factory_(socket_factory),
642 delegate_(delegate) {
643 if (frontend_url_.empty())
644 frontend_url_ = "/devtools/devtools.html";
646 // Balanced in ResetHandlerThreadAndRelease().
647 AddRef();
650 // Runs on the handler thread
651 void DevToolsHttpHandlerImpl::Init() {
652 server_ = new net::HttpServer(*socket_factory_.get(), this);
655 // Runs on the handler thread
656 void DevToolsHttpHandlerImpl::Teardown() {
657 server_ = NULL;
660 // Runs on FILE thread to make sure that it is serialized against
661 // {Start|Stop}HandlerThread and to allow calling pthread_join.
662 void DevToolsHttpHandlerImpl::StopHandlerThread() {
663 if (!thread_->message_loop())
664 return;
665 thread_->message_loop()->PostTask(
666 FROM_HERE,
667 base::Bind(&DevToolsHttpHandlerImpl::Teardown, this));
668 // Thread::Stop joins the thread.
669 thread_->Stop();
672 void DevToolsHttpHandlerImpl::SendJson(int connection_id,
673 net::HttpStatusCode status_code,
674 base::Value* value,
675 const std::string& message) {
676 if (!thread_)
677 return;
679 // Serialize value and message.
680 std::string json_value;
681 if (value) {
682 base::JSONWriter::WriteWithOptions(value,
683 base::JSONWriter::OPTIONS_PRETTY_PRINT,
684 &json_value);
686 std::string json_message;
687 scoped_ptr<base::Value> message_object(new base::StringValue(message));
688 base::JSONWriter::Write(message_object.get(), &json_message);
690 net::HttpServerResponseInfo response(status_code);
691 response.SetBody(json_value + message, "application/json; charset=UTF-8");
693 thread_->message_loop()->PostTask(
694 FROM_HERE,
695 base::Bind(&net::HttpServer::SendResponse,
696 server_.get(),
697 connection_id,
698 response));
701 void DevToolsHttpHandlerImpl::Send200(int connection_id,
702 const std::string& data,
703 const std::string& mime_type) {
704 if (!thread_)
705 return;
706 thread_->message_loop()->PostTask(
707 FROM_HERE,
708 base::Bind(&net::HttpServer::Send200,
709 server_.get(),
710 connection_id,
711 data,
712 mime_type));
715 void DevToolsHttpHandlerImpl::Send404(int connection_id) {
716 if (!thread_)
717 return;
718 thread_->message_loop()->PostTask(
719 FROM_HERE,
720 base::Bind(&net::HttpServer::Send404, server_.get(), connection_id));
723 void DevToolsHttpHandlerImpl::Send500(int connection_id,
724 const std::string& message) {
725 if (!thread_)
726 return;
727 thread_->message_loop()->PostTask(
728 FROM_HERE,
729 base::Bind(&net::HttpServer::Send500, server_.get(), connection_id,
730 message));
733 void DevToolsHttpHandlerImpl::AcceptWebSocket(
734 int connection_id,
735 const net::HttpServerRequestInfo& request) {
736 if (!thread_)
737 return;
738 thread_->message_loop()->PostTask(
739 FROM_HERE,
740 base::Bind(&net::HttpServer::AcceptWebSocket, server_.get(),
741 connection_id, request));
744 base::DictionaryValue* DevToolsHttpHandlerImpl::SerializeTarget(
745 const DevToolsTarget& target,
746 const std::string& host) {
747 base::DictionaryValue* dictionary = new base::DictionaryValue;
749 std::string id = target.GetId();
750 dictionary->SetString(kTargetIdField, id);
751 dictionary->SetString(kTargetTypeField, target.GetType());
752 dictionary->SetString(kTargetTitleField,
753 net::EscapeForHTML(target.GetTitle()));
754 dictionary->SetString(kTargetDescriptionField, target.GetDescription());
756 GURL url = target.GetUrl();
757 dictionary->SetString(kTargetUrlField, url.spec());
759 GURL favicon_url = target.GetFaviconUrl();
760 if (favicon_url.is_valid())
761 dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());
763 if (!delegate_->GetPageThumbnailData(url).empty()) {
764 dictionary->SetString(kTargetThumbnailUrlField,
765 std::string(kThumbUrlPrefix) + id);
768 if (!target.IsAttached()) {
769 dictionary->SetString(kTargetWebSocketDebuggerUrlField,
770 base::StringPrintf("ws://%s%s%s",
771 host.c_str(),
772 kPageUrlPrefix,
773 id.c_str()));
774 std::string devtools_frontend_url = GetFrontendURLInternal(
775 id.c_str(),
776 host);
777 dictionary->SetString(
778 kTargetDevtoolsFrontendUrlField, devtools_frontend_url);
781 return dictionary;
784 } // namespace content