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