- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / devtools / devtools_http_handler_impl.cc
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.
4
5 #include "content/browser/devtools/devtools_http_handler_impl.h"
6
7 #include <algorithm>
8 #include <utility>
9
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 "grit/devtools_resources_map.h"
35 #include "net/base/escape.h"
36 #include "net/base/io_buffer.h"
37 #include "net/base/ip_endpoint.h"
38 #include "net/server/http_server_request_info.h"
39 #include "net/server/http_server_response_info.h"
40 #include "webkit/common/user_agent/user_agent.h"
41 #include "webkit/common/user_agent/user_agent_util.h"
42
43 namespace content {
44
45 namespace {
46
47 const char kProtocolVersion[] = "1.0";
48
49 const char kDevToolsHandlerThreadName[] = "Chrome_DevToolsHandlerThread";
50
51 const char kThumbUrlPrefix[] = "/thumb/";
52 const char kPageUrlPrefix[] = "/devtools/page/";
53
54 const char kTargetIdField[] = "id";
55 const char kTargetTypeField[] = "type";
56 const char kTargetTitleField[] = "title";
57 const char kTargetDescriptionField[] = "description";
58 const char kTargetUrlField[] = "url";
59 const char kTargetThumbnailUrlField[] = "thumbnailUrl";
60 const char kTargetFaviconUrlField[] = "faviconUrl";
61 const char kTargetWebSocketDebuggerUrlField[] = "webSocketDebuggerUrl";
62 const char kTargetDevtoolsFrontendUrlField[] = "devtoolsFrontendUrl";
63
64 // An internal implementation of DevToolsClientHost that delegates
65 // messages sent for DevToolsClient to a DebuggerShell instance.
66 class DevToolsClientHostImpl : public DevToolsClientHost {
67  public:
68   DevToolsClientHostImpl(base::MessageLoop* message_loop,
69                          net::HttpServer* server,
70                          int connection_id)
71       : message_loop_(message_loop),
72         server_(server),
73         connection_id_(connection_id),
74         is_closed_(false),
75         detach_reason_("target_closed") {}
76
77   virtual ~DevToolsClientHostImpl() {}
78
79   // DevToolsClientHost interface
80   virtual void InspectedContentsClosing() OVERRIDE {
81     if (is_closed_)
82       return;
83     is_closed_ = true;
84
85     base::DictionaryValue notification;
86     notification.SetString(
87         devtools::Inspector::detached::kParamReason, detach_reason_);
88     std::string response = DevToolsProtocol::CreateNotification(
89         devtools::Inspector::detached::kName,
90         notification.DeepCopy())->Serialize();
91     message_loop_->PostTask(
92         FROM_HERE,
93         base::Bind(&net::HttpServer::SendOverWebSocket,
94                    server_,
95                    connection_id_,
96                    response));
97
98     message_loop_->PostTask(
99         FROM_HERE,
100         base::Bind(&net::HttpServer::Close, server_, connection_id_));
101   }
102
103   virtual void DispatchOnInspectorFrontend(const std::string& data) OVERRIDE {
104     message_loop_->PostTask(
105         FROM_HERE,
106         base::Bind(&net::HttpServer::SendOverWebSocket,
107                    server_,
108                    connection_id_,
109                    data));
110   }
111
112   virtual void ReplacedWithAnotherClient() OVERRIDE {
113     detach_reason_ = "replaced_with_devtools";
114   }
115
116  private:
117   base::MessageLoop* message_loop_;
118   net::HttpServer* server_;
119   int connection_id_;
120   bool is_closed_;
121   std::string detach_reason_;
122 };
123
124 static bool TimeComparator(const DevToolsTarget* target1,
125                            const DevToolsTarget* target2) {
126   return target1->GetLastActivityTime() > target2->GetLastActivityTime();
127 }
128
129 }  // namespace
130
131 // static
132 bool DevToolsHttpHandler::IsSupportedProtocolVersion(
133     const std::string& version) {
134   return version == kProtocolVersion;
135 }
136
137 // static
138 int DevToolsHttpHandler::GetFrontendResourceId(const std::string& name) {
139   for (size_t i = 0; i < kDevtoolsResourcesSize; ++i) {
140     if (name == kDevtoolsResources[i].name)
141       return kDevtoolsResources[i].value;
142   }
143   return -1;
144 }
145
146 // static
147 DevToolsHttpHandler* DevToolsHttpHandler::Start(
148     const net::StreamListenSocketFactory* socket_factory,
149     const std::string& frontend_url,
150     DevToolsHttpHandlerDelegate* delegate) {
151   DevToolsHttpHandlerImpl* http_handler =
152       new DevToolsHttpHandlerImpl(socket_factory,
153                                   frontend_url,
154                                   delegate);
155   http_handler->Start();
156   return http_handler;
157 }
158
159 DevToolsHttpHandlerImpl::~DevToolsHttpHandlerImpl() {
160   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
161   // Stop() must be called prior to destruction.
162   DCHECK(server_.get() == NULL);
163   DCHECK(thread_.get() == NULL);
164   STLDeleteValues(&target_map_);
165 }
166
167 void DevToolsHttpHandlerImpl::Start() {
168   if (thread_)
169     return;
170   thread_.reset(new base::Thread(kDevToolsHandlerThreadName));
171   BrowserThread::PostTask(
172       BrowserThread::FILE, FROM_HERE,
173       base::Bind(&DevToolsHttpHandlerImpl::StartHandlerThread, this));
174 }
175
176 // Runs on FILE thread.
177 void DevToolsHttpHandlerImpl::StartHandlerThread() {
178   base::Thread::Options options;
179   options.message_loop_type = base::MessageLoop::TYPE_IO;
180   if (!thread_->StartWithOptions(options)) {
181     BrowserThread::PostTask(
182         BrowserThread::UI, FROM_HERE,
183         base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThread, this));
184     return;
185   }
186
187   thread_->message_loop()->PostTask(
188       FROM_HERE,
189       base::Bind(&DevToolsHttpHandlerImpl::Init, this));
190 }
191
192 void DevToolsHttpHandlerImpl::ResetHandlerThread() {
193   thread_.reset();
194 }
195
196 void DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease() {
197   ResetHandlerThread();
198   Release();
199 }
200
201 void DevToolsHttpHandlerImpl::Stop() {
202   if (!thread_)
203     return;
204   BrowserThread::PostTaskAndReply(
205       BrowserThread::FILE, FROM_HERE,
206       base::Bind(&DevToolsHttpHandlerImpl::StopHandlerThread, this),
207       base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease, this));
208 }
209
210 GURL DevToolsHttpHandlerImpl::GetFrontendURL() {
211   net::IPEndPoint ip_address;
212   if (server_->GetLocalAddress(&ip_address))
213     return GURL();
214   return GURL(std::string("http://") + ip_address.ToString() +
215       overridden_frontend_url_);
216 }
217
218 static std::string PathWithoutParams(const std::string& path) {
219   size_t query_position = path.find("?");
220   if (query_position != std::string::npos)
221     return path.substr(0, query_position);
222   return path;
223 }
224
225 static std::string GetMimeType(const std::string& filename) {
226   if (EndsWith(filename, ".html", false)) {
227     return "text/html";
228   } else if (EndsWith(filename, ".css", false)) {
229     return "text/css";
230   } else if (EndsWith(filename, ".js", false)) {
231     return "application/javascript";
232   } else if (EndsWith(filename, ".png", false)) {
233     return "image/png";
234   } else if (EndsWith(filename, ".gif", false)) {
235     return "image/gif";
236   }
237   NOTREACHED();
238   return "text/plain";
239 }
240
241 void DevToolsHttpHandlerImpl::OnHttpRequest(
242     int connection_id,
243     const net::HttpServerRequestInfo& info) {
244   if (info.path.find("/json") == 0) {
245     BrowserThread::PostTask(
246         BrowserThread::UI,
247         FROM_HERE,
248         base::Bind(&DevToolsHttpHandlerImpl::OnJsonRequestUI,
249                    this,
250                    connection_id,
251                    info));
252     return;
253   }
254
255   if (info.path.find(kThumbUrlPrefix) == 0) {
256     // Thumbnail request.
257     const std::string target_id = info.path.substr(strlen(kThumbUrlPrefix));
258     DevToolsTarget* target = GetTarget(target_id);
259     GURL page_url;
260     if (target)
261       page_url = target->GetUrl();
262     BrowserThread::PostTask(
263         BrowserThread::UI,
264         FROM_HERE,
265         base::Bind(&DevToolsHttpHandlerImpl::OnThumbnailRequestUI,
266                    this,
267                    connection_id,
268                    page_url));
269     return;
270   }
271
272   if (info.path == "" || info.path == "/") {
273     // Discovery page request.
274     BrowserThread::PostTask(
275         BrowserThread::UI,
276         FROM_HERE,
277         base::Bind(&DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI,
278                    this,
279                    connection_id));
280     return;
281   }
282
283   if (info.path.find("/devtools/") != 0) {
284     server_->Send404(connection_id);
285     return;
286   }
287
288   std::string filename = PathWithoutParams(info.path.substr(10));
289   std::string mime_type = GetMimeType(filename);
290
291   base::FilePath frontend_dir = delegate_->GetDebugFrontendDir();
292   if (!frontend_dir.empty()) {
293     base::FilePath path = frontend_dir.AppendASCII(filename);
294     std::string data;
295     base::ReadFileToString(path, &data);
296     server_->Send200(connection_id, data, mime_type);
297     return;
298   }
299   if (delegate_->BundlesFrontendResources()) {
300     int resource_id = DevToolsHttpHandler::GetFrontendResourceId(filename);
301     if (resource_id != -1) {
302       base::StringPiece data = GetContentClient()->GetDataResource(
303               resource_id, ui::SCALE_FACTOR_NONE);
304       server_->Send200(connection_id, data.as_string(), mime_type);
305       return;
306     }
307   }
308   server_->Send404(connection_id);
309 }
310
311 void DevToolsHttpHandlerImpl::OnWebSocketRequest(
312     int connection_id,
313     const net::HttpServerRequestInfo& request) {
314   std::string browser_prefix = "/devtools/browser";
315   size_t browser_pos = request.path.find(browser_prefix);
316   if (browser_pos == 0) {
317     if (browser_target_) {
318       server_->Send500(connection_id, "Another client already attached");
319       return;
320     }
321     browser_target_ = new DevToolsBrowserTarget(
322         thread_->message_loop_proxy().get(), 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 */);
335
336     server_->AcceptWebSocket(connection_id, request);
337     return;
338   }
339
340   BrowserThread::PostTask(
341       BrowserThread::UI,
342       FROM_HERE,
343       base::Bind(
344           &DevToolsHttpHandlerImpl::OnWebSocketRequestUI,
345           this,
346           connection_id,
347           request));
348 }
349
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;
356   }
357
358   BrowserThread::PostTask(
359       BrowserThread::UI,
360       FROM_HERE,
361       base::Bind(
362           &DevToolsHttpHandlerImpl::OnWebSocketMessageUI,
363           this,
364           connection_id,
365           data));
366 }
367
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;
373   }
374
375   BrowserThread::PostTask(
376       BrowserThread::UI,
377       FROM_HERE,
378       base::Bind(
379           &DevToolsHttpHandlerImpl::OnCloseUI,
380           this,
381           connection_id));
382 }
383
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       overridden_frontend_url_.c_str(),
390       overridden_frontend_url_.find("?") == std::string::npos ? "?" : "&",
391       host.c_str(),
392       kPageUrlPrefix,
393       id.c_str());
394 }
395
396 static bool ParseJsonPath(
397     const std::string& path,
398     std::string* command,
399     std::string* target_id) {
400
401   // Fall back to list in case of empty query.
402   if (path.empty()) {
403     *command = "list";
404     return true;
405   }
406
407   if (path.find("/") != 0) {
408     // Malformed command.
409     return false;
410   }
411   *command = path.substr(1);
412
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);
417   }
418   return true;
419 }
420
421 void DevToolsHttpHandlerImpl::OnJsonRequestUI(
422     int connection_id,
423     const net::HttpServerRequestInfo& info) {
424   // Trim /json
425   std::string path = info.path.substr(5);
426
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);
433   }
434
435   size_t fragment_pos = path.find("#");
436   if (fragment_pos != std::string::npos)
437     path = path.substr(0, fragment_pos);
438
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;
447   }
448
449   if (command == "version") {
450     base::DictionaryValue version;
451     version.SetString("Protocol-Version", kProtocolVersion);
452     version.SetString("WebKit-Version", webkit_glue::GetWebKitVersion());
453     version.SetString("Browser", content::GetContentClient()->GetProduct());
454     version.SetString("User-Agent",
455                       webkit_glue::GetUserAgent(GURL(kAboutBlankURL)));
456     SendJson(connection_id, net::HTTP_OK, &version, std::string());
457     return;
458   }
459
460   if (command == "list") {
461     std::string host = info.headers["host"];
462     AddRef();  // Balanced in OnTargetListReceived.
463     delegate_->EnumerateTargets(
464         base::Bind(&DevToolsHttpHandlerImpl::OnTargetListReceived,
465                    this, connection_id, host));
466     return;
467   }
468
469   if (command == "new") {
470     GURL url(net::UnescapeURLComponent(
471         query, net::UnescapeRule::URL_SPECIAL_CHARS));
472     if (!url.is_valid())
473       url = GURL(kAboutBlankURL);
474     scoped_ptr<DevToolsTarget> target(delegate_->CreateNewTarget(url));
475     if (!target) {
476       SendJson(connection_id,
477                net::HTTP_INTERNAL_SERVER_ERROR,
478                NULL,
479                "Could not create new page");
480       return;
481     }
482     std::string host = info.headers["host"];
483     scoped_ptr<base::DictionaryValue> dictionary(
484         SerializeTarget(*target.get(), host));
485     SendJson(connection_id, net::HTTP_OK, dictionary.get(), std::string());
486     const std::string target_id = target->GetId();
487     target_map_[target_id] = target.release();
488     return;
489   }
490
491   if (command == "activate" || command == "close") {
492     DevToolsTarget* target = GetTarget(target_id);
493     if (!target) {
494       SendJson(connection_id,
495                net::HTTP_NOT_FOUND,
496                NULL,
497                "No such target id: " + target_id);
498       return;
499     }
500
501     if (command == "activate") {
502       if (target->Activate()) {
503         SendJson(connection_id, net::HTTP_OK, NULL, "Target activated");
504       } else {
505         SendJson(connection_id,
506                  net::HTTP_INTERNAL_SERVER_ERROR,
507                  NULL,
508                  "Could not activate target id: " + target_id);
509       }
510       return;
511     }
512
513     if (command == "close") {
514       if (target->Close()) {
515         SendJson(connection_id, net::HTTP_OK, NULL, "Target is closing");
516       } else {
517         SendJson(connection_id,
518                  net::HTTP_INTERNAL_SERVER_ERROR,
519                  NULL,
520                  "Could not close target id: " + target_id);
521       }
522       return;
523     }
524   }
525   SendJson(connection_id,
526            net::HTTP_NOT_FOUND,
527            NULL,
528            "Unknown command: " + command);
529   return;
530 }
531
532 void DevToolsHttpHandlerImpl::OnTargetListReceived(
533     int connection_id,
534     const std::string& host,
535     const DevToolsHttpHandlerDelegate::TargetList& targets) {
536   DevToolsHttpHandlerDelegate::TargetList sorted_targets = targets;
537   std::sort(sorted_targets.begin(), sorted_targets.end(), TimeComparator);
538
539   STLDeleteValues(&target_map_);
540   base::ListValue list_value;
541   for (DevToolsHttpHandlerDelegate::TargetList::const_iterator it =
542       sorted_targets.begin(); it != sorted_targets.end(); ++it) {
543     DevToolsTarget* target = *it;
544     target_map_[target->GetId()] = target;
545     list_value.Append(SerializeTarget(*target, host));
546   }
547   SendJson(connection_id, net::HTTP_OK, &list_value, std::string());
548   Release();  // Balanced in OnJsonRequestUI.
549 }
550
551 DevToolsTarget* DevToolsHttpHandlerImpl::GetTarget(const std::string& id) {
552   TargetMap::const_iterator it = target_map_.find(id);
553   if (it == target_map_.end())
554     return NULL;
555   return it->second;
556 }
557
558 void DevToolsHttpHandlerImpl::OnThumbnailRequestUI(
559     int connection_id, const GURL& page_url) {
560   std::string data = delegate_->GetPageThumbnailData(page_url);
561   if (!data.empty())
562     Send200(connection_id, data, "image/png");
563   else
564     Send404(connection_id);
565 }
566
567 void DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI(int connection_id) {
568   std::string response = delegate_->GetDiscoveryPageHTML();
569   Send200(connection_id, response, "text/html; charset=UTF-8");
570 }
571
572 void DevToolsHttpHandlerImpl::OnWebSocketRequestUI(
573     int connection_id,
574     const net::HttpServerRequestInfo& request) {
575   if (!thread_)
576     return;
577
578   size_t pos = request.path.find(kPageUrlPrefix);
579   if (pos != 0) {
580     Send404(connection_id);
581     return;
582   }
583
584   std::string page_id = request.path.substr(strlen(kPageUrlPrefix));
585   DevToolsTarget* target = GetTarget(page_id);
586   scoped_refptr<DevToolsAgentHost> agent =
587       target ? target->GetAgentHost() : NULL;
588   if (!agent) {
589     Send500(connection_id, "No such target id: " + page_id);
590     return;
591   }
592
593   if (agent->IsAttached()) {
594     Send500(connection_id,
595             "Target with given id is being inspected: " + page_id);
596     return;
597   }
598
599   DevToolsClientHostImpl* client_host = new DevToolsClientHostImpl(
600       thread_->message_loop(), server_.get(), connection_id);
601   connection_to_client_host_ui_[connection_id] = client_host;
602
603   DevToolsManager::GetInstance()->
604       RegisterDevToolsClientHostFor(agent, client_host);
605
606   AcceptWebSocket(connection_id, request);
607 }
608
609 void DevToolsHttpHandlerImpl::OnWebSocketMessageUI(
610     int connection_id,
611     const std::string& data) {
612   ConnectionToClientHostMap::iterator it =
613       connection_to_client_host_ui_.find(connection_id);
614   if (it == connection_to_client_host_ui_.end())
615     return;
616
617   DevToolsManager* manager = DevToolsManager::GetInstance();
618   manager->DispatchOnInspectorBackend(it->second, data);
619 }
620
621 void DevToolsHttpHandlerImpl::OnCloseUI(int connection_id) {
622   ConnectionToClientHostMap::iterator it =
623       connection_to_client_host_ui_.find(connection_id);
624   if (it != connection_to_client_host_ui_.end()) {
625     DevToolsClientHostImpl* client_host =
626         static_cast<DevToolsClientHostImpl*>(it->second);
627     DevToolsManager::GetInstance()->ClientHostClosing(client_host);
628     delete client_host;
629     connection_to_client_host_ui_.erase(connection_id);
630   }
631 }
632
633 DevToolsHttpHandlerImpl::DevToolsHttpHandlerImpl(
634     const net::StreamListenSocketFactory* socket_factory,
635     const std::string& frontend_url,
636     DevToolsHttpHandlerDelegate* delegate)
637     : overridden_frontend_url_(frontend_url),
638       socket_factory_(socket_factory),
639       delegate_(delegate) {
640   if (overridden_frontend_url_.empty())
641       overridden_frontend_url_ = "/devtools/devtools.html";
642
643   // Balanced in ResetHandlerThreadAndRelease().
644   AddRef();
645 }
646
647 // Runs on the handler thread
648 void DevToolsHttpHandlerImpl::Init() {
649   server_ = new net::HttpServer(*socket_factory_.get(), this);
650 }
651
652 // Runs on the handler thread
653 void DevToolsHttpHandlerImpl::Teardown() {
654   server_ = NULL;
655 }
656
657 // Runs on FILE thread to make sure that it is serialized against
658 // {Start|Stop}HandlerThread and to allow calling pthread_join.
659 void DevToolsHttpHandlerImpl::StopHandlerThread() {
660   if (!thread_->message_loop())
661     return;
662   thread_->message_loop()->PostTask(
663       FROM_HERE,
664       base::Bind(&DevToolsHttpHandlerImpl::Teardown, this));
665   // Thread::Stop joins the thread.
666   thread_->Stop();
667 }
668
669 void DevToolsHttpHandlerImpl::SendJson(int connection_id,
670                                        net::HttpStatusCode status_code,
671                                        base::Value* value,
672                                        const std::string& message) {
673   if (!thread_)
674     return;
675
676   // Serialize value and message.
677   std::string json_value;
678   if (value) {
679     base::JSONWriter::WriteWithOptions(value,
680                                        base::JSONWriter::OPTIONS_PRETTY_PRINT,
681                                        &json_value);
682   }
683   std::string json_message;
684   scoped_ptr<base::Value> message_object(new base::StringValue(message));
685   base::JSONWriter::Write(message_object.get(), &json_message);
686
687   net::HttpServerResponseInfo response(status_code);
688   response.SetBody(json_value + message, "application/json; charset=UTF-8");
689
690   thread_->message_loop()->PostTask(
691       FROM_HERE,
692       base::Bind(&net::HttpServer::SendResponse,
693                  server_.get(),
694                  connection_id,
695                  response));
696 }
697
698 void DevToolsHttpHandlerImpl::Send200(int connection_id,
699                                       const std::string& data,
700                                       const std::string& mime_type) {
701   if (!thread_)
702     return;
703   thread_->message_loop()->PostTask(
704       FROM_HERE,
705       base::Bind(&net::HttpServer::Send200,
706                  server_.get(),
707                  connection_id,
708                  data,
709                  mime_type));
710 }
711
712 void DevToolsHttpHandlerImpl::Send404(int connection_id) {
713   if (!thread_)
714     return;
715   thread_->message_loop()->PostTask(
716       FROM_HERE,
717       base::Bind(&net::HttpServer::Send404, server_.get(), connection_id));
718 }
719
720 void DevToolsHttpHandlerImpl::Send500(int connection_id,
721                                       const std::string& message) {
722   if (!thread_)
723     return;
724   thread_->message_loop()->PostTask(
725       FROM_HERE,
726       base::Bind(&net::HttpServer::Send500, server_.get(), connection_id,
727                  message));
728 }
729
730 void DevToolsHttpHandlerImpl::AcceptWebSocket(
731     int connection_id,
732     const net::HttpServerRequestInfo& request) {
733   if (!thread_)
734     return;
735   thread_->message_loop()->PostTask(
736       FROM_HERE,
737       base::Bind(&net::HttpServer::AcceptWebSocket, server_.get(),
738                  connection_id, request));
739 }
740
741 base::DictionaryValue* DevToolsHttpHandlerImpl::SerializeTarget(
742     const DevToolsTarget& target,
743     const std::string& host) {
744   base::DictionaryValue* dictionary = new base::DictionaryValue;
745
746   std::string id = target.GetId();
747   dictionary->SetString(kTargetIdField, id);
748   dictionary->SetString(kTargetTypeField, target.GetType());
749   dictionary->SetString(kTargetTitleField,
750                         net::EscapeForHTML(target.GetTitle()));
751   dictionary->SetString(kTargetDescriptionField, target.GetDescription());
752
753   GURL url = target.GetUrl();
754   dictionary->SetString(kTargetUrlField, url.spec());
755
756   GURL favicon_url = target.GetFaviconUrl();
757   if (favicon_url.is_valid())
758     dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());
759
760   if (!delegate_->GetPageThumbnailData(url).empty()) {
761     dictionary->SetString(kTargetThumbnailUrlField,
762                           std::string(kThumbUrlPrefix) + id);
763   }
764
765   if (!target.IsAttached()) {
766     dictionary->SetString(kTargetWebSocketDebuggerUrlField,
767                           base::StringPrintf("ws://%s%s%s",
768                                              host.c_str(),
769                                              kPageUrlPrefix,
770                                              id.c_str()));
771     std::string devtools_frontend_url = GetFrontendURLInternal(
772         id.c_str(),
773         host);
774     dictionary->SetString(
775         kTargetDevtoolsFrontendUrlField, devtools_frontend_url);
776   }
777
778   return dictionary;
779 }
780
781 }  // namespace content