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