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