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