Home | History | Annotate | Download | only in devtools
      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 
     52 namespace content {
     53 
     54 const int kBufferSize = 16 * 1024;
     55 
     56 namespace {
     57 
     58 static const char* kProtocolVersion = "1.0";
     59 
     60 static const char* kDevToolsHandlerThreadName = "Chrome_DevToolsHandlerThread";
     61 
     62 static const char* kThumbUrlPrefix = "/thumb/";
     63 static const char* kPageUrlPrefix = "/devtools/page/";
     64 
     65 static const char* kTargetIdField = "id";
     66 static const char* kTargetTypeField = "type";
     67 static const char* kTargetTitleField = "title";
     68 static const char* kTargetDescriptionField = "description";
     69 static const char* kTargetUrlField = "url";
     70 static const char* kTargetThumbnailUrlField = "thumbnailUrl";
     71 static const char* kTargetFaviconUrlField = "faviconUrl";
     72 static const char* kTargetWebSocketDebuggerUrlField = "webSocketDebuggerUrl";
     73 static const char* kTargetDevtoolsFrontendUrlField = "devtoolsFrontendUrl";
     74 
     75 static const char* kTargetTypePage = "page";
     76 static const char* kTargetTypeOther = "other";
     77 
     78 class 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.
     95 class 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 
    153 static 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 
    161 typedef std::pair<RenderViewHost*, base::TimeTicks> PageInfo;
    162 
    163 static bool TimeComparator(const PageInfo& info1, const PageInfo& info2) {
    164   return info1.second > info2.second;
    165 }
    166 
    167 }  // namespace
    168 
    169 // static
    170 bool DevToolsHttpHandler::IsSupportedProtocolVersion(
    171     const std::string& version) {
    172   return version == kProtocolVersion;
    173 }
    174 
    175 // static
    176 int 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
    185 DevToolsHttpHandler* 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 
    197 DevToolsHttpHandlerImpl::~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 
    204 void 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.
    214 void 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 
    229 void DevToolsHttpHandlerImpl::ResetHandlerThread() {
    230   thread_.reset();
    231 }
    232 
    233 void DevToolsHttpHandlerImpl::ResetHandlerThreadAndRelease() {
    234   ResetHandlerThread();
    235   Release();
    236 }
    237 
    238 void 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 
    247 void DevToolsHttpHandlerImpl::SetDevToolsAgentHostBinding(
    248     DevToolsAgentHostBinding* binding) {
    249   if (binding)
    250     binding_ = binding;
    251   else
    252     binding_ = default_binding_.get();
    253 }
    254 
    255 GURL 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 
    270 static 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 
    277 static 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 
    293 void 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 
    366 void 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 
    401 void 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 
    419 void 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 
    435 std::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 
    447 static 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 
    472 void 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 
    585 void 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 
    595 void 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 
    602 void 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 
    611 void DevToolsHttpHandlerImpl::OnDiscoveryPageRequestUI(int connection_id) {
    612   std::string response = delegate_->GetDiscoveryPageHTML();
    613   Send200(connection_id, response, "text/html; charset=UTF-8");
    614 }
    615 
    616 void 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 
    651 void 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 
    663 void 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 
    675 DevToolsHttpHandlerImpl::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
    693 void DevToolsHttpHandlerImpl::Init() {
    694   server_ = new net::HttpServer(*socket_factory_.get(), this);
    695 }
    696 
    697 // Runs on the handler thread
    698 void 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.
    704 void 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 
    714 void 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 
    743 void 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 
    757 void 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 
    765 void 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 
    775 void 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 
    786 base::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 
    828 base::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 
    851 void 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