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_browser_target.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/lazy_instance.h"
      9 #include "base/location.h"
     10 #include "base/logging.h"
     11 #include "base/message_loop/message_loop_proxy.h"
     12 #include "base/stl_util.h"
     13 #include "base/strings/stringprintf.h"
     14 #include "base/values.h"
     15 #include "content/public/browser/browser_thread.h"
     16 #include "net/server/http_server.h"
     17 
     18 namespace content {
     19 
     20 DevToolsBrowserTarget::DevToolsBrowserTarget(
     21     net::HttpServer* http_server,
     22     int connection_id)
     23     : message_loop_proxy_(base::MessageLoopProxy::current()),
     24       http_server_(http_server),
     25       connection_id_(connection_id),
     26       weak_factory_(this) {
     27 }
     28 
     29 void DevToolsBrowserTarget::RegisterDomainHandler(
     30     const std::string& domain,
     31     DevToolsProtocol::Handler* handler,
     32     bool handle_on_ui_thread) {
     33   DCHECK_EQ(message_loop_proxy_, base::MessageLoopProxy::current());
     34 
     35   DCHECK(handlers_.find(domain) == handlers_.end());
     36   handlers_[domain] = handler;
     37   if (handle_on_ui_thread) {
     38     handle_on_ui_thread_.insert(domain);
     39     handler->SetNotifier(base::Bind(&DevToolsBrowserTarget::RespondFromUIThread,
     40                                     weak_factory_.GetWeakPtr()));
     41   } else {
     42     handler->SetNotifier(base::Bind(&DevToolsBrowserTarget::Respond,
     43                                     base::Unretained(this)));
     44   }
     45 }
     46 
     47 typedef std::map<std::string, DevToolsBrowserTarget*> DomainMap;
     48 base::LazyInstance<DomainMap>::Leaky g_used_domains = LAZY_INSTANCE_INITIALIZER;
     49 
     50 void DevToolsBrowserTarget::HandleMessage(const std::string& data) {
     51   DCHECK_EQ(message_loop_proxy_, base::MessageLoopProxy::current());
     52   std::string error_response;
     53   scoped_refptr<DevToolsProtocol::Command> command =
     54       DevToolsProtocol::ParseCommand(data, &error_response);
     55   if (!command) {
     56     Respond(error_response);
     57     return;
     58   }
     59 
     60   DomainHandlerMap::iterator it = handlers_.find(command->domain());
     61   if (it == handlers_.end()) {
     62     Respond(command->NoSuchMethodErrorResponse()->Serialize());
     63     return;
     64   }
     65   DomainMap& used_domains(g_used_domains.Get());
     66   std::string domain = command->domain();
     67   DomainMap::iterator jt = used_domains.find(domain);
     68   if (jt == used_domains.end()) {
     69     used_domains[domain] = this;
     70   } else if (jt->second != this) {
     71     std::string message =
     72         base::StringPrintf("'%s' is held by another connection",
     73                            domain.c_str());
     74     Respond(command->ServerErrorResponse(message)->Serialize());
     75     return;
     76   }
     77 
     78   DevToolsProtocol::Handler* handler = it->second;
     79   bool handle_directly = handle_on_ui_thread_.find(domain) ==
     80       handle_on_ui_thread_.end();
     81   if (handle_directly) {
     82     scoped_refptr<DevToolsProtocol::Response> response =
     83         handler->HandleCommand(command);
     84     if (response && response->is_async_promise())
     85       return;
     86     if (response)
     87       Respond(response->Serialize());
     88     else
     89       Respond(command->NoSuchMethodErrorResponse()->Serialize());
     90     return;
     91   }
     92 
     93   BrowserThread::PostTask(
     94       BrowserThread::UI,
     95       FROM_HERE,
     96       base::Bind(&DevToolsBrowserTarget::HandleCommandOnUIThread,
     97                  this,
     98                  handler,
     99                  command));
    100 }
    101 
    102 void DevToolsBrowserTarget::Detach() {
    103   DCHECK_EQ(message_loop_proxy_, base::MessageLoopProxy::current());
    104   DCHECK(http_server_);
    105 
    106   http_server_ = NULL;
    107 
    108   DomainMap& used_domains(g_used_domains.Get());
    109   for (DomainMap::iterator it = used_domains.begin();
    110        it != used_domains.end();) {
    111     if (it->second == this) {
    112       DomainMap::iterator to_erase = it;
    113       ++it;
    114       used_domains.erase(to_erase);
    115     } else {
    116       ++it;
    117     }
    118   }
    119 
    120   std::vector<DevToolsProtocol::Handler*> ui_handlers;
    121   for (std::set<std::string>::iterator domain_it = handle_on_ui_thread_.begin();
    122        domain_it != handle_on_ui_thread_.end();
    123        ++domain_it) {
    124     DomainHandlerMap::iterator handler_it = handlers_.find(*domain_it);
    125     CHECK(handler_it != handlers_.end());
    126     ui_handlers.push_back(handler_it->second);
    127     handlers_.erase(handler_it);
    128   }
    129 
    130   STLDeleteValues(&handlers_);
    131 
    132   BrowserThread::PostTask(
    133       BrowserThread::UI,
    134       FROM_HERE,
    135       base::Bind(&DevToolsBrowserTarget::DeleteHandlersOnUIThread,
    136                  this,
    137                  ui_handlers));
    138 }
    139 
    140 DevToolsBrowserTarget::~DevToolsBrowserTarget() {
    141   // DCHECK that Detach has been called or no handler has ever been registered.
    142   DCHECK(handlers_.empty());
    143 }
    144 
    145 void DevToolsBrowserTarget::HandleCommandOnUIThread(
    146     DevToolsProtocol::Handler* handler,
    147     scoped_refptr<DevToolsProtocol::Command> command) {
    148   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    149   scoped_refptr<DevToolsProtocol::Response> response =
    150       handler->HandleCommand(command);
    151   if (response && response->is_async_promise())
    152     return;
    153 
    154   if (response)
    155     RespondFromUIThread(response->Serialize());
    156   else
    157     RespondFromUIThread(command->NoSuchMethodErrorResponse()->Serialize());
    158 }
    159 
    160 void DevToolsBrowserTarget::DeleteHandlersOnUIThread(
    161     std::vector<DevToolsProtocol::Handler*> handlers) {
    162   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    163   STLDeleteElements(&handlers);
    164 }
    165 
    166 void DevToolsBrowserTarget::Respond(const std::string& message) {
    167   DCHECK_EQ(message_loop_proxy_, base::MessageLoopProxy::current());
    168   if (!http_server_)
    169     return;
    170   http_server_->SendOverWebSocket(connection_id_, message);
    171 }
    172 
    173 void DevToolsBrowserTarget::RespondFromUIThread(const std::string& message) {
    174   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    175   message_loop_proxy_->PostTask(
    176       FROM_HERE,
    177       base::Bind(&DevToolsBrowserTarget::Respond, this, message));
    178 }
    179 
    180 }  // namespace content
    181