devtools_http_handler_impl.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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
43namespace content {
44
45namespace {
46
47const char kProtocolVersion[] = "1.0";
48
49const char kDevToolsHandlerThreadName[] = "Chrome_DevToolsHandlerThread";
50
51const char kThumbUrlPrefix[] = "/thumb/";
52const char kPageUrlPrefix[] = "/devtools/page/";
53
54const char kTargetIdField[] = "id";
55const char kTargetTypeField[] = "type";
56const char kTargetTitleField[] = "title";
57const char kTargetDescriptionField[] = "description";
58const char kTargetUrlField[] = "url";
59const char kTargetThumbnailUrlField[] = "thumbnailUrl";
60const char kTargetFaviconUrlField[] = "faviconUrl";
61const char kTargetWebSocketDebuggerUrlField[] = "webSocketDebuggerUrl";
62const char kTargetDevtoolsFrontendUrlField[] = "devtoolsFrontendUrl";
63
64// An internal implementation of DevToolsClientHost that delegates
65// messages sent for DevToolsClient to a DebuggerShell instance.
66class 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
124static bool TimeComparator(const DevToolsTarget* target1,
125                           const DevToolsTarget* target2) {
126  return target1->GetLastActivityTime() > target2->GetLastActivityTime();
127}
128
129}  // namespace
130
131// static
132bool DevToolsHttpHandler::IsSupportedProtocolVersion(
133    const std::string& version) {
134  return version == kProtocolVersion;
135}
136
137// static
138int 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
147DevToolsHttpHandler* 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
159DevToolsHttpHandlerImpl::~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
167void 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.
177void 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
192void DevToolsHttpHandlerImpl::ResetHandlerThread() {
193  thread_.reset();
194}
195
196void DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease() {
197  ResetHandlerThread();
198  Release();
199}
200
201void 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
210GURL 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
218static 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
225static 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
241void 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
311void 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
350void 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
368void 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
384std::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
396static 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
421void 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
532void 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
551DevToolsTarget* 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
558void 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
567void DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI(int connection_id) {
568  std::string response = delegate_->GetDiscoveryPageHTML();
569  Send200(connection_id, response, "text/html; charset=UTF-8");
570}
571
572void 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
609void 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
621void 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
633DevToolsHttpHandlerImpl::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
648void DevToolsHttpHandlerImpl::Init() {
649  server_ = new net::HttpServer(*socket_factory_.get(), this);
650}
651
652// Runs on the handler thread
653void 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.
659void 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
669void 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
698void 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
712void 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
720void 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
730void 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
741base::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
782