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