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