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/lazy_instance.h"
15#include "base/logging.h"
16#include "base/message_loop/message_loop_proxy.h"
17#include "base/stl_util.h"
18#include "base/strings/string_number_conversions.h"
19#include "base/strings/utf_string_conversions.h"
20#include "base/threading/thread.h"
21#include "base/values.h"
22#include "content/browser/devtools/devtools_browser_target.h"
23#include "content/browser/devtools/devtools_protocol.h"
24#include "content/browser/devtools/devtools_protocol_constants.h"
25#include "content/browser/devtools/devtools_tracing_handler.h"
26#include "content/browser/devtools/tethering_handler.h"
27#include "content/browser/web_contents/web_contents_impl.h"
28#include "content/common/devtools_messages.h"
29#include "content/public/browser/browser_thread.h"
30#include "content/public/browser/devtools_agent_host.h"
31#include "content/public/browser/devtools_client_host.h"
32#include "content/public/browser/devtools_http_handler_delegate.h"
33#include "content/public/browser/devtools_manager.h"
34#include "content/public/browser/favicon_status.h"
35#include "content/public/browser/navigation_entry.h"
36#include "content/public/browser/notification_service.h"
37#include "content/public/browser/notification_types.h"
38#include "content/public/browser/render_view_host.h"
39#include "content/public/common/content_client.h"
40#include "content/public/common/url_constants.h"
41#include "grit/devtools_resources_map.h"
42#include "net/base/escape.h"
43#include "net/base/io_buffer.h"
44#include "net/base/ip_endpoint.h"
45#include "net/server/http_server_request_info.h"
46#include "net/server/http_server_response_info.h"
47#include "ui/base/layout.h"
48#include "url/gurl.h"
49#include "webkit/common/user_agent/user_agent.h"
50#include "webkit/common/user_agent/user_agent_util.h"
51
52namespace content {
53
54const int kBufferSize = 16 * 1024;
55
56namespace {
57
58static const char* kProtocolVersion = "1.0";
59
60static const char* kDevToolsHandlerThreadName = "Chrome_DevToolsHandlerThread";
61
62static const char* kThumbUrlPrefix = "/thumb/";
63static const char* kPageUrlPrefix = "/devtools/page/";
64
65static const char* kTargetIdField = "id";
66static const char* kTargetTypeField = "type";
67static const char* kTargetTitleField = "title";
68static const char* kTargetDescriptionField = "description";
69static const char* kTargetUrlField = "url";
70static const char* kTargetThumbnailUrlField = "thumbnailUrl";
71static const char* kTargetFaviconUrlField = "faviconUrl";
72static const char* kTargetWebSocketDebuggerUrlField = "webSocketDebuggerUrl";
73static const char* kTargetDevtoolsFrontendUrlField = "devtoolsFrontendUrl";
74
75static const char* kTargetTypePage = "page";
76static const char* kTargetTypeOther = "other";
77
78class DevToolsDefaultBindingHandler
79    : public DevToolsHttpHandler::DevToolsAgentHostBinding {
80 public:
81  DevToolsDefaultBindingHandler() {
82  }
83
84  virtual std::string GetIdentifier(DevToolsAgentHost* agent_host) OVERRIDE {
85    return agent_host->GetId();
86  }
87
88  virtual DevToolsAgentHost* ForIdentifier(const std::string& id) OVERRIDE {
89    return DevToolsAgentHost::GetForId(id).get();
90  }
91};
92
93// An internal implementation of DevToolsClientHost that delegates
94// messages sent for DevToolsClient to a DebuggerShell instance.
95class DevToolsClientHostImpl : public DevToolsClientHost {
96 public:
97  DevToolsClientHostImpl(base::MessageLoop* message_loop,
98                         net::HttpServer* server,
99                         int connection_id)
100      : message_loop_(message_loop),
101        server_(server),
102        connection_id_(connection_id),
103        is_closed_(false),
104        detach_reason_("target_closed") {}
105
106  virtual ~DevToolsClientHostImpl() {}
107
108  // DevToolsClientHost interface
109  virtual void InspectedContentsClosing() OVERRIDE {
110    if (is_closed_)
111      return;
112    is_closed_ = true;
113
114    base::DictionaryValue notification;
115    notification.SetString(
116        devtools::Inspector::detached::kParamReason, detach_reason_);
117    std::string response = DevToolsProtocol::CreateNotification(
118        devtools::Inspector::detached::kName,
119        notification.DeepCopy())->Serialize();
120    message_loop_->PostTask(
121        FROM_HERE,
122        base::Bind(&net::HttpServer::SendOverWebSocket,
123                   server_,
124                   connection_id_,
125                   response));
126
127    message_loop_->PostTask(
128        FROM_HERE,
129        base::Bind(&net::HttpServer::Close, server_, connection_id_));
130  }
131
132  virtual void DispatchOnInspectorFrontend(const std::string& data) OVERRIDE {
133    message_loop_->PostTask(
134        FROM_HERE,
135        base::Bind(&net::HttpServer::SendOverWebSocket,
136                   server_,
137                   connection_id_,
138                   data));
139  }
140
141  virtual void ReplacedWithAnotherClient() OVERRIDE {
142    detach_reason_ = "replaced_with_devtools";
143  }
144
145 private:
146  base::MessageLoop* message_loop_;
147  net::HttpServer* server_;
148  int connection_id_;
149  bool is_closed_;
150  std::string detach_reason_;
151};
152
153static base::TimeTicks GetLastSelectedTime(RenderViewHost* rvh) {
154  WebContents* web_contents = rvh->GetDelegate()->GetAsWebContents();
155  if (!web_contents)
156    return base::TimeTicks();
157
158  return web_contents->GetLastSelectedTime();
159}
160
161typedef std::pair<RenderViewHost*, base::TimeTicks> PageInfo;
162
163static bool TimeComparator(const PageInfo& info1, const PageInfo& info2) {
164  return info1.second > info2.second;
165}
166
167}  // namespace
168
169// static
170bool DevToolsHttpHandler::IsSupportedProtocolVersion(
171    const std::string& version) {
172  return version == kProtocolVersion;
173}
174
175// static
176int DevToolsHttpHandler::GetFrontendResourceId(const std::string& name) {
177  for (size_t i = 0; i < kDevtoolsResourcesSize; ++i) {
178    if (name == kDevtoolsResources[i].name)
179      return kDevtoolsResources[i].value;
180  }
181  return -1;
182}
183
184// static
185DevToolsHttpHandler* DevToolsHttpHandler::Start(
186    const net::StreamListenSocketFactory* socket_factory,
187    const std::string& frontend_url,
188    DevToolsHttpHandlerDelegate* delegate) {
189  DevToolsHttpHandlerImpl* http_handler =
190      new DevToolsHttpHandlerImpl(socket_factory,
191                                  frontend_url,
192                                  delegate);
193  http_handler->Start();
194  return http_handler;
195}
196
197DevToolsHttpHandlerImpl::~DevToolsHttpHandlerImpl() {
198  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
199  // Stop() must be called prior to destruction.
200  DCHECK(server_.get() == NULL);
201  DCHECK(thread_.get() == NULL);
202}
203
204void DevToolsHttpHandlerImpl::Start() {
205  if (thread_)
206    return;
207  thread_.reset(new base::Thread(kDevToolsHandlerThreadName));
208  BrowserThread::PostTask(
209      BrowserThread::FILE, FROM_HERE,
210      base::Bind(&DevToolsHttpHandlerImpl::StartHandlerThread, this));
211}
212
213// Runs on FILE thread.
214void DevToolsHttpHandlerImpl::StartHandlerThread() {
215  base::Thread::Options options;
216  options.message_loop_type = base::MessageLoop::TYPE_IO;
217  if (!thread_->StartWithOptions(options)) {
218    BrowserThread::PostTask(
219        BrowserThread::UI, FROM_HERE,
220        base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThread, this));
221    return;
222  }
223
224  thread_->message_loop()->PostTask(
225      FROM_HERE,
226      base::Bind(&DevToolsHttpHandlerImpl::Init, this));
227}
228
229void DevToolsHttpHandlerImpl::ResetHandlerThread() {
230  thread_.reset();
231}
232
233void DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease() {
234  ResetHandlerThread();
235  Release();
236}
237
238void DevToolsHttpHandlerImpl::Stop() {
239  if (!thread_)
240    return;
241  BrowserThread::PostTaskAndReply(
242      BrowserThread::FILE, FROM_HERE,
243      base::Bind(&DevToolsHttpHandlerImpl::StopHandlerThread, this),
244      base::Bind(&DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease, this));
245}
246
247void DevToolsHttpHandlerImpl::SetDevToolsAgentHostBinding(
248    DevToolsAgentHostBinding* binding) {
249  if (binding)
250    binding_ = binding;
251  else
252    binding_ = default_binding_.get();
253}
254
255GURL DevToolsHttpHandlerImpl::GetFrontendURL(DevToolsAgentHost* agent_host) {
256  net::IPEndPoint ip_address;
257  if (server_->GetLocalAddress(&ip_address))
258    return GURL();
259  if (!agent_host) {
260    return GURL(std::string("http://") + ip_address.ToString() +
261        overridden_frontend_url_);
262  }
263  std::string host = ip_address.ToString();
264  std::string id = binding_->GetIdentifier(agent_host);
265  return GURL(std::string("http://") +
266              ip_address.ToString() +
267              GetFrontendURLInternal(id, host));
268}
269
270static std::string PathWithoutParams(const std::string& path) {
271  size_t query_position = path.find("?");
272  if (query_position != std::string::npos)
273    return path.substr(0, query_position);
274  return path;
275}
276
277static std::string GetMimeType(const std::string& filename) {
278  if (EndsWith(filename, ".html", false)) {
279    return "text/html";
280  } else if (EndsWith(filename, ".css", false)) {
281    return "text/css";
282  } else if (EndsWith(filename, ".js", false)) {
283    return "application/javascript";
284  } else if (EndsWith(filename, ".png", false)) {
285    return "image/png";
286  } else if (EndsWith(filename, ".gif", false)) {
287    return "image/gif";
288  }
289  NOTREACHED();
290  return "text/plain";
291}
292
293void DevToolsHttpHandlerImpl::OnHttpRequest(
294    int connection_id,
295    const net::HttpServerRequestInfo& info) {
296  if (info.path.find("/json") == 0) {
297    BrowserThread::PostTask(
298        BrowserThread::UI,
299        FROM_HERE,
300        base::Bind(&DevToolsHttpHandlerImpl::OnJsonRequestUI,
301                   this,
302                   connection_id,
303                   info));
304    return;
305  }
306
307  if (info.path.find(kThumbUrlPrefix) == 0) {
308    // Thumbnail request.
309    const std::string target_id = info.path.substr(strlen(kThumbUrlPrefix));
310    DevToolsAgentHost* agent_host = binding_->ForIdentifier(target_id);
311    GURL page_url;
312    if (agent_host) {
313      RenderViewHost* rvh = agent_host->GetRenderViewHost();
314      if (rvh)
315        page_url = rvh->GetDelegate()->GetURL();
316    }
317    BrowserThread::PostTask(
318        BrowserThread::UI,
319        FROM_HERE,
320        base::Bind(&DevToolsHttpHandlerImpl::OnThumbnailRequestUI,
321                   this,
322                   connection_id,
323                   page_url));
324    return;
325  }
326
327  if (info.path == "" || info.path == "/") {
328    // Discovery page request.
329    BrowserThread::PostTask(
330        BrowserThread::UI,
331        FROM_HERE,
332        base::Bind(&DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI,
333                   this,
334                   connection_id));
335    return;
336  }
337
338  if (info.path.find("/devtools/") != 0) {
339    server_->Send404(connection_id);
340    return;
341  }
342
343  std::string filename = PathWithoutParams(info.path.substr(10));
344  std::string mime_type = GetMimeType(filename);
345
346  base::FilePath frontend_dir = delegate_->GetDebugFrontendDir();
347  if (!frontend_dir.empty()) {
348    base::FilePath path = frontend_dir.AppendASCII(filename);
349    std::string data;
350    file_util::ReadFileToString(path, &data);
351    server_->Send200(connection_id, data, mime_type);
352    return;
353  }
354  if (delegate_->BundlesFrontendResources()) {
355    int resource_id = DevToolsHttpHandler::GetFrontendResourceId(filename);
356    if (resource_id != -1) {
357      base::StringPiece data = GetContentClient()->GetDataResource(
358              resource_id, ui::SCALE_FACTOR_NONE);
359      server_->Send200(connection_id, data.as_string(), mime_type);
360      return;
361    }
362  }
363  server_->Send404(connection_id);
364}
365
366void DevToolsHttpHandlerImpl::OnWebSocketRequest(
367    int connection_id,
368    const net::HttpServerRequestInfo& request) {
369  std::string browser_prefix = "/devtools/browser";
370  size_t browser_pos = request.path.find(browser_prefix);
371  if (browser_pos == 0) {
372    if (browser_target_) {
373      server_->Send500(connection_id, "Another client already attached");
374      return;
375    }
376    browser_target_ = new DevToolsBrowserTarget(
377        thread_->message_loop_proxy().get(), server_.get(), connection_id);
378    browser_target_->RegisterDomainHandler(
379        devtools::Tracing::kName,
380        new DevToolsTracingHandler(),
381        true /* handle on UI thread */);
382    browser_target_->RegisterDomainHandler(
383        TetheringHandler::kDomain,
384        new TetheringHandler(delegate_.get()),
385        false /* handle on this thread */);
386
387    server_->AcceptWebSocket(connection_id, request);
388    return;
389  }
390
391  BrowserThread::PostTask(
392      BrowserThread::UI,
393      FROM_HERE,
394      base::Bind(
395          &DevToolsHttpHandlerImpl::OnWebSocketRequestUI,
396          this,
397          connection_id,
398          request));
399}
400
401void DevToolsHttpHandlerImpl::OnWebSocketMessage(
402    int connection_id,
403    const std::string& data) {
404  if (browser_target_ && connection_id == browser_target_->connection_id()) {
405    browser_target_->HandleMessage(data);
406    return;
407  }
408
409  BrowserThread::PostTask(
410      BrowserThread::UI,
411      FROM_HERE,
412      base::Bind(
413          &DevToolsHttpHandlerImpl::OnWebSocketMessageUI,
414          this,
415          connection_id,
416          data));
417}
418
419void DevToolsHttpHandlerImpl::OnClose(int connection_id) {
420  if (browser_target_ && browser_target_->connection_id() == connection_id) {
421    browser_target_->Detach();
422    browser_target_ = NULL;
423    return;
424  }
425
426  BrowserThread::PostTask(
427      BrowserThread::UI,
428      FROM_HERE,
429      base::Bind(
430          &DevToolsHttpHandlerImpl::OnCloseUI,
431          this,
432          connection_id));
433}
434
435std::string DevToolsHttpHandlerImpl::GetFrontendURLInternal(
436    const std::string id,
437    const std::string& host) {
438  return base::StringPrintf(
439      "%s%sws=%s%s%s",
440      overridden_frontend_url_.c_str(),
441      overridden_frontend_url_.find("?") == std::string::npos ? "?" : "&",
442      host.c_str(),
443      kPageUrlPrefix,
444      id.c_str());
445}
446
447static bool ParseJsonPath(
448    const std::string& path,
449    std::string* command,
450    std::string* target_id) {
451
452  // Fall back to list in case of empty query.
453  if (path.empty()) {
454    *command = "list";
455    return true;
456  }
457
458  if (path.find("/") != 0) {
459    // Malformed command.
460    return false;
461  }
462  *command = path.substr(1);
463
464  size_t separator_pos = command->find("/");
465  if (separator_pos != std::string::npos) {
466    *target_id = command->substr(separator_pos + 1);
467    *command = command->substr(0, separator_pos);
468  }
469  return true;
470}
471
472void DevToolsHttpHandlerImpl::OnJsonRequestUI(
473    int connection_id,
474    const net::HttpServerRequestInfo& info) {
475  // Trim /json
476  std::string path = info.path.substr(5);
477
478  // Trim fragment and query
479  size_t query_pos = path.find("?");
480  if (query_pos != std::string::npos)
481    path = path.substr(0, query_pos);
482
483  size_t fragment_pos = path.find("#");
484  if (fragment_pos != std::string::npos)
485    path = path.substr(0, fragment_pos);
486
487  std::string command;
488  std::string target_id;
489  if (!ParseJsonPath(path, &command, &target_id)) {
490    SendJson(connection_id,
491             net::HTTP_NOT_FOUND,
492             NULL,
493             "Malformed query: " + info.path);
494    return;
495  }
496
497  if (command == "version") {
498    base::DictionaryValue version;
499    version.SetString("Protocol-Version", kProtocolVersion);
500    version.SetString("WebKit-Version", webkit_glue::GetWebKitVersion());
501    version.SetString("Browser", content::GetContentClient()->GetProduct());
502    version.SetString("User-Agent",
503                      webkit_glue::GetUserAgent(GURL(kAboutBlankURL)));
504    SendJson(connection_id, net::HTTP_OK, &version, std::string());
505    return;
506  }
507
508  if (command == "list") {
509    typedef std::vector<PageInfo> PageList;
510    PageList page_list;
511
512    std::vector<RenderViewHost*> rvh_list =
513        DevToolsAgentHost::GetValidRenderViewHosts();
514    for (std::vector<RenderViewHost*>::iterator it = rvh_list.begin();
515         it != rvh_list.end(); ++it)
516      page_list.push_back(PageInfo(*it, GetLastSelectedTime(*it)));
517
518    std::sort(page_list.begin(), page_list.end(), TimeComparator);
519
520    base::ListValue* target_list = new base::ListValue();
521    std::string host = info.headers["host"];
522    for (PageList::iterator i = page_list.begin(); i != page_list.end(); ++i)
523      target_list->Append(SerializePageInfo(i->first, host));
524
525    AddRef();  // Balanced in SendTargetList.
526    BrowserThread::PostTaskAndReply(
527        BrowserThread::IO,
528        FROM_HERE,
529        base::Bind(&DevToolsHttpHandlerImpl::CollectWorkerInfo,
530                   base::Unretained(this),
531                   target_list,
532                   host),
533        base::Bind(&DevToolsHttpHandlerImpl::SendTargetList,
534                   base::Unretained(this),
535                   connection_id,
536                   target_list));
537    return;
538  }
539
540  if (command == "new") {
541    RenderViewHost* rvh = delegate_->CreateNewTarget();
542    if (!rvh) {
543      SendJson(connection_id,
544               net::HTTP_INTERNAL_SERVER_ERROR,
545               NULL,
546               "Could not create new page");
547      return;
548    }
549    std::string host = info.headers["host"];
550    scoped_ptr<base::DictionaryValue> dictionary(SerializePageInfo(rvh, host));
551    SendJson(connection_id, net::HTTP_OK, dictionary.get(), std::string());
552    return;
553  }
554
555  if (command == "activate" || command == "close") {
556    DevToolsAgentHost* agent_host = binding_->ForIdentifier(target_id);
557    RenderViewHost* rvh = agent_host ? agent_host->GetRenderViewHost() : NULL;
558    if (!rvh) {
559      SendJson(connection_id,
560               net::HTTP_NOT_FOUND,
561               NULL,
562               "No such target id: " + target_id);
563      return;
564    }
565
566    if (command == "activate") {
567      rvh->GetDelegate()->Activate();
568      SendJson(connection_id, net::HTTP_OK, NULL, "Target activated");
569      return;
570    }
571
572    if (command == "close") {
573      rvh->ClosePage();
574      SendJson(connection_id, net::HTTP_OK, NULL, "Target is closing");
575      return;
576    }
577  }
578  SendJson(connection_id,
579           net::HTTP_NOT_FOUND,
580           NULL,
581           "Unknown command: " + command);
582  return;
583}
584
585void DevToolsHttpHandlerImpl::CollectWorkerInfo(base::ListValue* target_list,
586                                                std::string host) {
587
588  std::vector<WorkerService::WorkerInfo> worker_info =
589      WorkerService::GetInstance()->GetWorkers();
590
591  for (size_t i = 0; i < worker_info.size(); ++i)
592    target_list->Append(SerializeWorkerInfo(worker_info[i], host));
593}
594
595void DevToolsHttpHandlerImpl::SendTargetList(int connection_id,
596                                             base::ListValue* target_list) {
597  SendJson(connection_id, net::HTTP_OK, target_list, std::string());
598  delete target_list;
599  Release();  // Balanced OnJsonRequestUI.
600}
601
602void DevToolsHttpHandlerImpl::OnThumbnailRequestUI(
603    int connection_id, const GURL& page_url) {
604  std::string data = delegate_->GetPageThumbnailData(page_url);
605  if (!data.empty())
606    Send200(connection_id, data, "image/png");
607  else
608    Send404(connection_id);
609}
610
611void DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI(int connection_id) {
612  std::string response = delegate_->GetDiscoveryPageHTML();
613  Send200(connection_id, response, "text/html; charset=UTF-8");
614}
615
616void DevToolsHttpHandlerImpl::OnWebSocketRequestUI(
617    int connection_id,
618    const net::HttpServerRequestInfo& request) {
619  if (!thread_)
620    return;
621
622  size_t pos = request.path.find(kPageUrlPrefix);
623  if (pos != 0) {
624    Send404(connection_id);
625    return;
626  }
627
628  std::string page_id = request.path.substr(strlen(kPageUrlPrefix));
629  DevToolsAgentHost* agent = binding_->ForIdentifier(page_id);
630  if (!agent) {
631    Send500(connection_id, "No such target id: " + page_id);
632    return;
633  }
634
635  if (agent->IsAttached()) {
636    Send500(connection_id,
637            "Target with given id is being inspected: " + page_id);
638    return;
639  }
640
641  DevToolsClientHostImpl* client_host = new DevToolsClientHostImpl(
642      thread_->message_loop(), server_.get(), connection_id);
643  connection_to_client_host_ui_[connection_id] = client_host;
644
645  DevToolsManager::GetInstance()->
646      RegisterDevToolsClientHostFor(agent, client_host);
647
648  AcceptWebSocket(connection_id, request);
649}
650
651void DevToolsHttpHandlerImpl::OnWebSocketMessageUI(
652    int connection_id,
653    const std::string& data) {
654  ConnectionToClientHostMap::iterator it =
655      connection_to_client_host_ui_.find(connection_id);
656  if (it == connection_to_client_host_ui_.end())
657    return;
658
659  DevToolsManager* manager = DevToolsManager::GetInstance();
660  manager->DispatchOnInspectorBackend(it->second, data);
661}
662
663void DevToolsHttpHandlerImpl::OnCloseUI(int connection_id) {
664  ConnectionToClientHostMap::iterator it =
665      connection_to_client_host_ui_.find(connection_id);
666  if (it != connection_to_client_host_ui_.end()) {
667    DevToolsClientHostImpl* client_host =
668        static_cast<DevToolsClientHostImpl*>(it->second);
669    DevToolsManager::GetInstance()->ClientHostClosing(client_host);
670    delete client_host;
671    connection_to_client_host_ui_.erase(connection_id);
672  }
673}
674
675DevToolsHttpHandlerImpl::DevToolsHttpHandlerImpl(
676    const net::StreamListenSocketFactory* socket_factory,
677    const std::string& frontend_url,
678    DevToolsHttpHandlerDelegate* delegate)
679    : overridden_frontend_url_(frontend_url),
680      socket_factory_(socket_factory),
681      delegate_(delegate) {
682  if (overridden_frontend_url_.empty())
683      overridden_frontend_url_ = "/devtools/devtools.html";
684
685  default_binding_.reset(new DevToolsDefaultBindingHandler);
686  binding_ = default_binding_.get();
687
688  // Balanced in ResetHandlerThreadAndRelease().
689  AddRef();
690}
691
692// Runs on the handler thread
693void DevToolsHttpHandlerImpl::Init() {
694  server_ = new net::HttpServer(*socket_factory_.get(), this);
695}
696
697// Runs on the handler thread
698void DevToolsHttpHandlerImpl::Teardown() {
699  server_ = NULL;
700}
701
702// Runs on FILE thread to make sure that it is serialized against
703// {Start|Stop}HandlerThread and to allow calling pthread_join.
704void DevToolsHttpHandlerImpl::StopHandlerThread() {
705  if (!thread_->message_loop())
706    return;
707  thread_->message_loop()->PostTask(
708      FROM_HERE,
709      base::Bind(&DevToolsHttpHandlerImpl::Teardown, this));
710  // Thread::Stop joins the thread.
711  thread_->Stop();
712}
713
714void DevToolsHttpHandlerImpl::SendJson(int connection_id,
715                                       net::HttpStatusCode status_code,
716                                       base::Value* value,
717                                       const std::string& message) {
718  if (!thread_)
719    return;
720
721  // Serialize value and message.
722  std::string json_value;
723  if (value) {
724    base::JSONWriter::WriteWithOptions(value,
725                                       base::JSONWriter::OPTIONS_PRETTY_PRINT,
726                                       &json_value);
727  }
728  std::string json_message;
729  scoped_ptr<base::Value> message_object(new base::StringValue(message));
730  base::JSONWriter::Write(message_object.get(), &json_message);
731
732  net::HttpServerResponseInfo response(status_code);
733  response.SetBody(json_value + message, "application/json; charset=UTF-8");
734
735  thread_->message_loop()->PostTask(
736      FROM_HERE,
737      base::Bind(&net::HttpServer::SendResponse,
738                 server_.get(),
739                 connection_id,
740                 response));
741}
742
743void DevToolsHttpHandlerImpl::Send200(int connection_id,
744                                      const std::string& data,
745                                      const std::string& mime_type) {
746  if (!thread_)
747    return;
748  thread_->message_loop()->PostTask(
749      FROM_HERE,
750      base::Bind(&net::HttpServer::Send200,
751                 server_.get(),
752                 connection_id,
753                 data,
754                 mime_type));
755}
756
757void DevToolsHttpHandlerImpl::Send404(int connection_id) {
758  if (!thread_)
759    return;
760  thread_->message_loop()->PostTask(
761      FROM_HERE,
762      base::Bind(&net::HttpServer::Send404, server_.get(), connection_id));
763}
764
765void DevToolsHttpHandlerImpl::Send500(int connection_id,
766                                      const std::string& message) {
767  if (!thread_)
768    return;
769  thread_->message_loop()->PostTask(
770      FROM_HERE,
771      base::Bind(&net::HttpServer::Send500, server_.get(), connection_id,
772                 message));
773}
774
775void DevToolsHttpHandlerImpl::AcceptWebSocket(
776    int connection_id,
777    const net::HttpServerRequestInfo& request) {
778  if (!thread_)
779    return;
780  thread_->message_loop()->PostTask(
781      FROM_HERE,
782      base::Bind(&net::HttpServer::AcceptWebSocket, server_.get(),
783                 connection_id, request));
784}
785
786base::DictionaryValue* DevToolsHttpHandlerImpl::SerializePageInfo(
787    RenderViewHost* rvh,
788    const std::string& host) {
789  base::DictionaryValue* dictionary = new base::DictionaryValue;
790
791  scoped_refptr<DevToolsAgentHost> agent(
792      DevToolsAgentHost::GetOrCreateFor(rvh));
793
794  std::string id = binding_->GetIdentifier(agent.get());
795  dictionary->SetString(kTargetIdField, id);
796
797  switch (delegate_->GetTargetType(rvh)) {
798    case DevToolsHttpHandlerDelegate::kTargetTypeTab:
799      dictionary->SetString(kTargetTypeField, kTargetTypePage);
800      break;
801    default:
802      dictionary->SetString(kTargetTypeField, kTargetTypeOther);
803  }
804
805  WebContents* web_contents = rvh->GetDelegate()->GetAsWebContents();
806  if (web_contents) {
807    dictionary->SetString(kTargetTitleField, UTF16ToUTF8(
808        net::EscapeForHTML(web_contents->GetTitle())));
809    dictionary->SetString(kTargetUrlField, web_contents->GetURL().spec());
810    dictionary->SetString(kTargetThumbnailUrlField,
811        std::string(kThumbUrlPrefix) + id);
812
813    NavigationController& controller = web_contents->GetController();
814    NavigationEntry* entry = controller.GetActiveEntry();
815    if (entry != NULL && entry->GetURL().is_valid()) {
816      dictionary->SetString(kTargetFaviconUrlField,
817          entry->GetFavicon().url.spec());
818    }
819  }
820  dictionary->SetString(kTargetDescriptionField,
821      delegate_->GetViewDescription(rvh));
822
823  if (!agent->IsAttached())
824    SerializeDebuggerURLs(dictionary, id, host);
825  return dictionary;
826}
827
828base::DictionaryValue* DevToolsHttpHandlerImpl::SerializeWorkerInfo(
829    const WorkerService::WorkerInfo& worker,
830    const std::string& host) {
831  base::DictionaryValue* dictionary = new base::DictionaryValue;
832
833  scoped_refptr<DevToolsAgentHost> agent(DevToolsAgentHost::GetForWorker(
834      worker.process_id, worker.route_id));
835
836  std::string id = binding_->GetIdentifier(agent.get());
837
838  dictionary->SetString(kTargetIdField, id);
839  dictionary->SetString(kTargetTypeField, kTargetTypeOther);
840  dictionary->SetString(kTargetTitleField,
841      UTF16ToUTF8(net::EscapeForHTML(worker.name)));
842  dictionary->SetString(kTargetUrlField, worker.url.spec());
843  dictionary->SetString(kTargetDescriptionField,
844      base::StringPrintf("Worker pid:%d", base::GetProcId(worker.handle)));
845
846  if (!agent->IsAttached())
847    SerializeDebuggerURLs(dictionary, id, host);
848  return dictionary;
849}
850
851void DevToolsHttpHandlerImpl::SerializeDebuggerURLs(
852    base::DictionaryValue* dictionary,
853    const std::string& id,
854    const std::string& host) {
855  dictionary->SetString(kTargetWebSocketDebuggerUrlField,
856                        base::StringPrintf("ws://%s%s%s",
857                                           host.c_str(),
858                                           kPageUrlPrefix,
859                                           id.c_str()));
860  std::string devtools_frontend_url = GetFrontendURLInternal(
861      id.c_str(),
862      host);
863  dictionary->SetString(kTargetDevtoolsFrontendUrlField, devtools_frontend_url);
864}
865
866}  // namespace content
867