1 // Copyright (c) 2011 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 // Implements the Chrome Extensions Debugger API. 6 7 #include "chrome/browser/extensions/extension_debugger_api.h" 8 9 #include <map> 10 #include <set> 11 12 #include "base/json/json_reader.h" 13 #include "base/json/json_writer.h" 14 #include "base/memory/singleton.h" 15 #include "base/string_number_conversions.h" 16 #include "base/values.h" 17 #include "chrome/browser/debugger/devtools_client_host.h" 18 #include "chrome/browser/debugger/devtools_manager.h" 19 #include "chrome/browser/extensions/extension_debugger_api_constants.h" 20 #include "chrome/browser/extensions/extension_event_router.h" 21 #include "chrome/browser/extensions/extension_tabs_module.h" 22 #include "chrome/browser/profiles/profile.h" 23 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" 24 #include "chrome/common/devtools_messages.h" 25 #include "chrome/common/extensions/extension.h" 26 #include "chrome/common/extensions/extension_error_utils.h" 27 #include "content/browser/tab_contents/tab_contents.h" 28 #include "content/common/notification_service.h" 29 30 namespace keys = extension_debugger_api_constants; 31 32 class ExtensionDevToolsClientHost : public DevToolsClientHost, 33 public NotificationObserver { 34 public: 35 ExtensionDevToolsClientHost(TabContents* tab_contents, 36 const std::string& extension_id, 37 int tab_id); 38 39 ~ExtensionDevToolsClientHost(); 40 41 bool MatchesContentsAndExtensionId(TabContents* tab_contents, 42 const std::string& extension_id); 43 void Close(); 44 void SendMessageToBackend(SendRequestDebuggerFunction* function, 45 const std::string& method, 46 Value* params); 47 48 // DevToolsClientHost interface 49 virtual void InspectedTabClosing(); 50 virtual void SendMessageToClient(const IPC::Message& msg); 51 virtual void TabReplaced(TabContentsWrapper* tab_contents); 52 virtual void FrameNavigating(const std::string& url) {} 53 54 private: 55 // NotificationObserver implementation. 56 virtual void Observe(NotificationType type, 57 const NotificationSource& source, 58 const NotificationDetails& details); 59 void OnDispatchOnInspectorFrontend(const std::string& data); 60 61 TabContents* tab_contents_; 62 std::string extension_id_; 63 int tab_id_; 64 NotificationRegistrar registrar_; 65 int last_request_id_; 66 typedef std::map<int, scoped_refptr<SendRequestDebuggerFunction> > 67 PendingRequests; 68 PendingRequests pending_requests_; 69 70 DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsClientHost); 71 }; 72 73 namespace { 74 75 class AttachedClientHosts { 76 public: 77 AttachedClientHosts() {} 78 79 // Returns the singleton instance of this class 80 static AttachedClientHosts* GetInstance() { 81 return Singleton<AttachedClientHosts>::get(); 82 } 83 84 void Add(ExtensionDevToolsClientHost* client_host) { 85 client_hosts_.insert(client_host); 86 } 87 88 void Remove(ExtensionDevToolsClientHost* client_host) { 89 client_hosts_.erase(client_host); 90 } 91 92 ExtensionDevToolsClientHost* Lookup(RenderViewHost* rvh) { 93 DevToolsClientHost* client_host = 94 DevToolsManager::GetInstance()->GetDevToolsClientHostFor(rvh); 95 std::set<DevToolsClientHost*>::iterator it = 96 client_hosts_.find(client_host); 97 if (it == client_hosts_.end()) 98 return NULL; 99 return static_cast<ExtensionDevToolsClientHost*>(client_host); 100 } 101 102 private: 103 std::set<DevToolsClientHost*> client_hosts_; 104 }; 105 106 } // namespace 107 108 ExtensionDevToolsClientHost::ExtensionDevToolsClientHost( 109 TabContents* tab_contents, 110 const std::string& extension_id, 111 int tab_id) 112 : tab_contents_(tab_contents), 113 extension_id_(extension_id), 114 tab_id_(tab_id), 115 last_request_id_(0) { 116 AttachedClientHosts::GetInstance()->Add(this); 117 118 // Detach from debugger when extension unloads. 119 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, 120 Source<Profile>(tab_contents_->profile())); 121 122 // Attach to debugger and tell it we are ready. 123 DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor( 124 tab_contents_->render_view_host(), 125 this); 126 DevToolsManager::GetInstance()->ForwardToDevToolsAgent( 127 this, 128 DevToolsAgentMsg_FrontendLoaded()); 129 } 130 131 ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() { 132 AttachedClientHosts::GetInstance()->Remove(this); 133 } 134 135 bool ExtensionDevToolsClientHost::MatchesContentsAndExtensionId( 136 TabContents* tab_contents, 137 const std::string& extension_id) { 138 return tab_contents == tab_contents_ && extension_id_ == extension_id; 139 } 140 141 // DevToolsClientHost interface 142 void ExtensionDevToolsClientHost::InspectedTabClosing() { 143 // Tell extension that this client host has been detached. 144 Profile* profile = tab_contents_->profile(); 145 if (profile != NULL && profile->GetExtensionEventRouter()) { 146 ListValue args; 147 args.Append(Value::CreateIntegerValue(tab_id_)); 148 149 std::string json_args; 150 base::JSONWriter::Write(&args, false, &json_args); 151 152 profile->GetExtensionEventRouter()->DispatchEventToExtension( 153 extension_id_, keys::kOnDetach, json_args, profile, GURL()); 154 } 155 delete this; 156 } 157 158 void ExtensionDevToolsClientHost::SendMessageToClient( 159 const IPC::Message& msg) { 160 IPC_BEGIN_MESSAGE_MAP(ExtensionDevToolsClientHost, msg) 161 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend, 162 OnDispatchOnInspectorFrontend); 163 IPC_MESSAGE_UNHANDLED_ERROR() 164 IPC_END_MESSAGE_MAP() 165 } 166 167 void ExtensionDevToolsClientHost::TabReplaced( 168 TabContentsWrapper* tab_contents) { 169 tab_contents_ = tab_contents->tab_contents(); 170 } 171 172 void ExtensionDevToolsClientHost::Close() { 173 DevToolsClientHost::NotifyCloseListener(); 174 delete this; 175 } 176 177 void ExtensionDevToolsClientHost::SendMessageToBackend( 178 SendRequestDebuggerFunction* function, 179 const std::string& method, 180 Value* params) { 181 DictionaryValue protocol_request; 182 int request_id = ++last_request_id_; 183 pending_requests_[request_id] = function; 184 protocol_request.SetInteger("id", request_id); 185 protocol_request.SetString("method", method); 186 if (params) 187 protocol_request.Set("params", params->DeepCopy()); 188 189 std::string json_args; 190 base::JSONWriter::Write(&protocol_request, false, &json_args); 191 DevToolsManager::GetInstance()->ForwardToDevToolsAgent( 192 this, 193 DevToolsAgentMsg_DispatchOnInspectorBackend(json_args)); 194 } 195 196 void ExtensionDevToolsClientHost::Observe( 197 NotificationType type, 198 const NotificationSource& source, 199 const NotificationDetails& details) { 200 DCHECK(type == NotificationType::EXTENSION_UNLOADED); 201 Close(); 202 } 203 204 void ExtensionDevToolsClientHost::OnDispatchOnInspectorFrontend( 205 const std::string& data) { 206 Profile* profile = tab_contents_->profile(); 207 if (profile == NULL || !profile->GetExtensionEventRouter()) 208 return; 209 210 scoped_ptr<Value> result(base::JSONReader::Read(data, false)); 211 if (!result->IsType(Value::TYPE_DICTIONARY)) 212 return; 213 DictionaryValue* dictionary = static_cast<DictionaryValue*>(result.get()); 214 215 int id; 216 if (!dictionary->GetInteger("id", &id)) { 217 std::string method_name; 218 if (!dictionary->GetString("method", &method_name)) 219 return; 220 221 ListValue args; 222 args.Append(Value::CreateIntegerValue(tab_id_)); 223 args.Append(Value::CreateStringValue(method_name)); 224 Value* params_value; 225 if (dictionary->Get("params", ¶ms_value)) 226 args.Append(params_value->DeepCopy()); 227 228 std::string json_args; 229 base::JSONWriter::Write(&args, false, &json_args); 230 231 profile->GetExtensionEventRouter()->DispatchEventToExtension( 232 extension_id_, keys::kOnEvent, json_args, profile, GURL()); 233 } else { 234 SendRequestDebuggerFunction* function = pending_requests_[id]; 235 if (!function) 236 return; 237 238 function->SendResponseBody(dictionary); 239 pending_requests_.erase(id); 240 } 241 } 242 243 DebuggerFunction::DebuggerFunction() 244 : contents_(0), 245 client_host_(0) { 246 } 247 248 bool DebuggerFunction::InitTabContents(int tab_id) { 249 // Find the TabContents that contains this tab id. 250 contents_ = NULL; 251 TabContentsWrapper* wrapper = NULL; 252 bool result = ExtensionTabUtil::GetTabById( 253 tab_id, profile(), include_incognito(), NULL, NULL, &wrapper, NULL); 254 if (!result || !wrapper) { 255 error_ = error_ = ExtensionErrorUtils::FormatErrorMessage( 256 keys::kNoTabError, 257 base::IntToString(tab_id)); 258 return false; 259 } 260 contents_ = wrapper->tab_contents(); 261 return true; 262 } 263 264 bool DebuggerFunction::InitClientHost(int tab_id) { 265 if (!InitTabContents(tab_id)) 266 return false; 267 268 RenderViewHost* rvh = contents_->render_view_host(); 269 client_host_ = AttachedClientHosts::GetInstance()->Lookup(rvh); 270 271 if (!client_host_ || 272 !client_host_->MatchesContentsAndExtensionId(contents_, 273 GetExtension()->id())) { 274 error_ = ExtensionErrorUtils::FormatErrorMessage( 275 keys::kNotAttachedError, 276 base::IntToString(tab_id)); 277 return false; 278 } 279 return true; 280 } 281 282 AttachDebuggerFunction::AttachDebuggerFunction() {} 283 284 AttachDebuggerFunction::~AttachDebuggerFunction() {} 285 286 bool AttachDebuggerFunction::RunImpl() { 287 int tab_id; 288 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); 289 290 if (!InitTabContents(tab_id)) 291 return false; 292 293 DevToolsClientHost* client_host = DevToolsManager::GetInstance()-> 294 GetDevToolsClientHostFor(contents_->render_view_host()); 295 296 if (client_host != NULL) { 297 error_ = ExtensionErrorUtils::FormatErrorMessage( 298 keys::kAlreadyAttachedError, 299 base::IntToString(tab_id)); 300 return false; 301 } 302 303 new ExtensionDevToolsClientHost(contents_, GetExtension()->id(), tab_id); 304 SendResponse(true); 305 return true; 306 } 307 308 DetachDebuggerFunction::DetachDebuggerFunction() {} 309 310 DetachDebuggerFunction::~DetachDebuggerFunction() {} 311 312 bool DetachDebuggerFunction::RunImpl() { 313 int tab_id; 314 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); 315 316 if (!InitClientHost(tab_id)) 317 return false; 318 319 client_host_->Close(); 320 SendResponse(true); 321 return true; 322 } 323 324 SendRequestDebuggerFunction::SendRequestDebuggerFunction() {} 325 326 SendRequestDebuggerFunction::~SendRequestDebuggerFunction() {} 327 328 bool SendRequestDebuggerFunction::RunImpl() { 329 int tab_id; 330 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); 331 332 if (!InitClientHost(tab_id)) 333 return false; 334 335 std::string method; 336 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &method)); 337 338 Value *params; 339 if (!args_->Get(2, ¶ms)) 340 params = NULL; 341 342 client_host_->SendMessageToBackend(this, method, params); 343 return true; 344 } 345 346 void SendRequestDebuggerFunction::SendResponseBody( 347 DictionaryValue* dictionary) { 348 Value* error_body; 349 if (dictionary->Get("error", &error_body)) { 350 base::JSONWriter::Write(error_body, false, &error_); 351 SendResponse(false); 352 return; 353 } 354 355 Value* result_body; 356 if (dictionary->Get("result", &result_body)) 357 result_.reset(result_body->DeepCopy()); 358 else 359 result_.reset(new DictionaryValue()); 360 SendResponse(true); 361 } 362