blob: aeddcfc1b76a3397abaf293a0fda4adc272f37a0 [file] [log] [blame]
[email protected]91ba3312011-03-17 20:39:221// 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
[email protected]ff31a8a2011-03-30 14:13:599#include <map>
[email protected]91ba3312011-03-17 20:39:2210#include <set>
11
[email protected]ff31a8a2011-03-30 14:13:5912#include "base/json/json_reader.h"
[email protected]91ba3312011-03-17 20:39:2213#include "base/json/json_writer.h"
[email protected]3b63f8f42011-03-28 01:54:1514#include "base/memory/singleton.h"
[email protected]91ba3312011-03-17 20:39:2215#include "base/string_number_conversions.h"
16#include "base/values.h"
[email protected]91ba3312011-03-17 20:39:2217#include "chrome/browser/extensions/extension_debugger_api_constants.h"
18#include "chrome/browser/extensions/extension_event_router.h"
[email protected]ac84431b2011-09-27 17:26:1119#include "chrome/browser/extensions/extension_tab_util.h"
[email protected]91ba3312011-03-17 20:39:2220#include "chrome/browser/profiles/profile.h"
21#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
[email protected]ac84431b2011-09-27 17:26:1122#include "chrome/common/chrome_notification_types.h"
[email protected]91ba3312011-03-17 20:39:2223#include "chrome/common/extensions/extension.h"
24#include "chrome/common/extensions/extension_error_utils.h"
[email protected]b46442d7e2011-06-29 02:16:0625#include "content/browser/debugger/devtools_client_host.h"
26#include "content/browser/debugger/devtools_manager.h"
[email protected]91ba3312011-03-17 20:39:2227#include "content/browser/tab_contents/tab_contents.h"
[email protected]bf7cc5382011-05-27 08:26:0628#include "content/common/devtools_messages.h"
[email protected]91ba3312011-03-17 20:39:2229#include "content/common/notification_service.h"
30
31namespace keys = extension_debugger_api_constants;
32
33class ExtensionDevToolsClientHost : public DevToolsClientHost,
34 public NotificationObserver {
35 public:
36 ExtensionDevToolsClientHost(TabContents* tab_contents,
37 const std::string& extension_id,
38 int tab_id);
39
40 ~ExtensionDevToolsClientHost();
41
42 bool MatchesContentsAndExtensionId(TabContents* tab_contents,
43 const std::string& extension_id);
44 void Close();
[email protected]ac310102011-04-08 14:08:3345 void SendMessageToBackend(SendRequestDebuggerFunction* function,
46 const std::string& method,
47 Value* params);
[email protected]91ba3312011-03-17 20:39:2248
49 // DevToolsClientHost interface
50 virtual void InspectedTabClosing();
51 virtual void SendMessageToClient(const IPC::Message& msg);
[email protected]dce502762011-07-20 08:53:4952 virtual void TabReplaced(TabContents* tab_contents);
[email protected]91ba3312011-03-17 20:39:2253 virtual void FrameNavigating(const std::string& url) {}
54
55 private:
56 // NotificationObserver implementation.
[email protected]432115822011-07-10 15:52:2757 virtual void Observe(int type,
[email protected]91ba3312011-03-17 20:39:2258 const NotificationSource& source,
59 const NotificationDetails& details);
60 void OnDispatchOnInspectorFrontend(const std::string& data);
61
62 TabContents* tab_contents_;
63 std::string extension_id_;
64 int tab_id_;
65 NotificationRegistrar registrar_;
[email protected]ff31a8a2011-03-30 14:13:5966 int last_request_id_;
[email protected]ac310102011-04-08 14:08:3367 typedef std::map<int, scoped_refptr<SendRequestDebuggerFunction> >
[email protected]ff31a8a2011-03-30 14:13:5968 PendingRequests;
69 PendingRequests pending_requests_;
[email protected]91ba3312011-03-17 20:39:2270
71 DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsClientHost);
72};
73
74namespace {
75
76class AttachedClientHosts {
77 public:
78 AttachedClientHosts() {}
79
80 // Returns the singleton instance of this class
81 static AttachedClientHosts* GetInstance() {
82 return Singleton<AttachedClientHosts>::get();
83 }
84
85 void Add(ExtensionDevToolsClientHost* client_host) {
86 client_hosts_.insert(client_host);
87 }
88
89 void Remove(ExtensionDevToolsClientHost* client_host) {
90 client_hosts_.erase(client_host);
91 }
92
93 ExtensionDevToolsClientHost* Lookup(RenderViewHost* rvh) {
94 DevToolsClientHost* client_host =
95 DevToolsManager::GetInstance()->GetDevToolsClientHostFor(rvh);
96 std::set<DevToolsClientHost*>::iterator it =
97 client_hosts_.find(client_host);
98 if (it == client_hosts_.end())
99 return NULL;
100 return static_cast<ExtensionDevToolsClientHost*>(client_host);
101 }
102
103 private:
104 std::set<DevToolsClientHost*> client_hosts_;
105};
106
107} // namespace
108
109ExtensionDevToolsClientHost::ExtensionDevToolsClientHost(
110 TabContents* tab_contents,
111 const std::string& extension_id,
112 int tab_id)
113 : tab_contents_(tab_contents),
114 extension_id_(extension_id),
[email protected]ff31a8a2011-03-30 14:13:59115 tab_id_(tab_id),
116 last_request_id_(0) {
[email protected]91ba3312011-03-17 20:39:22117 AttachedClientHosts::GetInstance()->Add(this);
118
119 // Detach from debugger when extension unloads.
[email protected]cafe4ad2011-07-28 18:34:56120 Profile* profile =
121 Profile::FromBrowserContext(tab_contents_->browser_context());
[email protected]432115822011-07-10 15:52:27122 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
[email protected]cafe4ad2011-07-28 18:34:56123 Source<Profile>(profile));
[email protected]91ba3312011-03-17 20:39:22124
125 // Attach to debugger and tell it we are ready.
126 DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
127 tab_contents_->render_view_host(),
128 this);
129 DevToolsManager::GetInstance()->ForwardToDevToolsAgent(
130 this,
[email protected]6f4c7e12011-07-13 07:07:43131 DevToolsAgentMsg_FrontendLoaded(MSG_ROUTING_NONE));
[email protected]91ba3312011-03-17 20:39:22132}
133
134ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() {
135 AttachedClientHosts::GetInstance()->Remove(this);
136}
137
138bool ExtensionDevToolsClientHost::MatchesContentsAndExtensionId(
139 TabContents* tab_contents,
140 const std::string& extension_id) {
141 return tab_contents == tab_contents_ && extension_id_ == extension_id;
142}
143
144// DevToolsClientHost interface
145void ExtensionDevToolsClientHost::InspectedTabClosing() {
146 // Tell extension that this client host has been detached.
[email protected]cafe4ad2011-07-28 18:34:56147 Profile* profile =
148 Profile::FromBrowserContext(tab_contents_->browser_context());
[email protected]91ba3312011-03-17 20:39:22149 if (profile != NULL && profile->GetExtensionEventRouter()) {
150 ListValue args;
151 args.Append(Value::CreateIntegerValue(tab_id_));
152
153 std::string json_args;
154 base::JSONWriter::Write(&args, false, &json_args);
155
156 profile->GetExtensionEventRouter()->DispatchEventToExtension(
157 extension_id_, keys::kOnDetach, json_args, profile, GURL());
158 }
159 delete this;
160}
161
162void ExtensionDevToolsClientHost::SendMessageToClient(
163 const IPC::Message& msg) {
164 IPC_BEGIN_MESSAGE_MAP(ExtensionDevToolsClientHost, msg)
165 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
166 OnDispatchOnInspectorFrontend);
167 IPC_MESSAGE_UNHANDLED_ERROR()
168 IPC_END_MESSAGE_MAP()
169}
170
171void ExtensionDevToolsClientHost::TabReplaced(
[email protected]dce502762011-07-20 08:53:49172 TabContents* tab_contents) {
173 tab_contents_ = tab_contents;
[email protected]91ba3312011-03-17 20:39:22174}
175
176void ExtensionDevToolsClientHost::Close() {
177 DevToolsClientHost::NotifyCloseListener();
178 delete this;
179}
180
181void ExtensionDevToolsClientHost::SendMessageToBackend(
[email protected]ac310102011-04-08 14:08:33182 SendRequestDebuggerFunction* function,
183 const std::string& method,
184 Value* params) {
185 DictionaryValue protocol_request;
[email protected]ff31a8a2011-03-30 14:13:59186 int request_id = ++last_request_id_;
187 pending_requests_[request_id] = function;
[email protected]ac310102011-04-08 14:08:33188 protocol_request.SetInteger("id", request_id);
189 protocol_request.SetString("method", method);
190 if (params)
191 protocol_request.Set("params", params->DeepCopy());
[email protected]ff31a8a2011-03-30 14:13:59192
193 std::string json_args;
[email protected]ac310102011-04-08 14:08:33194 base::JSONWriter::Write(&protocol_request, false, &json_args);
[email protected]91ba3312011-03-17 20:39:22195 DevToolsManager::GetInstance()->ForwardToDevToolsAgent(
196 this,
[email protected]6f4c7e12011-07-13 07:07:43197 DevToolsAgentMsg_DispatchOnInspectorBackend(MSG_ROUTING_NONE,
198 json_args));
[email protected]91ba3312011-03-17 20:39:22199}
200
201void ExtensionDevToolsClientHost::Observe(
[email protected]432115822011-07-10 15:52:27202 int type,
[email protected]91ba3312011-03-17 20:39:22203 const NotificationSource& source,
204 const NotificationDetails& details) {
[email protected]432115822011-07-10 15:52:27205 DCHECK(type == chrome::NOTIFICATION_EXTENSION_UNLOADED);
[email protected]91ba3312011-03-17 20:39:22206 Close();
207}
208
209void ExtensionDevToolsClientHost::OnDispatchOnInspectorFrontend(
210 const std::string& data) {
[email protected]cafe4ad2011-07-28 18:34:56211 Profile* profile =
212 Profile::FromBrowserContext(tab_contents_->browser_context());
[email protected]ff31a8a2011-03-30 14:13:59213 if (profile == NULL || !profile->GetExtensionEventRouter())
214 return;
[email protected]91ba3312011-03-17 20:39:22215
[email protected]ff31a8a2011-03-30 14:13:59216 scoped_ptr<Value> result(base::JSONReader::Read(data, false));
217 if (!result->IsType(Value::TYPE_DICTIONARY))
218 return;
219 DictionaryValue* dictionary = static_cast<DictionaryValue*>(result.get());
220
[email protected]ac310102011-04-08 14:08:33221 int id;
222 if (!dictionary->GetInteger("id", &id)) {
223 std::string method_name;
224 if (!dictionary->GetString("method", &method_name))
[email protected]7d713ec2011-04-01 14:22:02225 return;
226
227 ListValue args;
228 args.Append(Value::CreateIntegerValue(tab_id_));
[email protected]ac310102011-04-08 14:08:33229 args.Append(Value::CreateStringValue(method_name));
230 Value* params_value;
231 if (dictionary->Get("params", &params_value))
232 args.Append(params_value->DeepCopy());
[email protected]7d713ec2011-04-01 14:22:02233
234 std::string json_args;
235 base::JSONWriter::Write(&args, false, &json_args);
236
[email protected]91ba3312011-03-17 20:39:22237 profile->GetExtensionEventRouter()->DispatchEventToExtension(
[email protected]7d713ec2011-04-01 14:22:02238 extension_id_, keys::kOnEvent, json_args, profile, GURL());
[email protected]ac310102011-04-08 14:08:33239 } else {
240 SendRequestDebuggerFunction* function = pending_requests_[id];
241 if (!function)
242 return;
243
244 function->SendResponseBody(dictionary);
245 pending_requests_.erase(id);
[email protected]91ba3312011-03-17 20:39:22246 }
247}
248
249DebuggerFunction::DebuggerFunction()
250 : contents_(0),
251 client_host_(0) {
252}
253
254bool DebuggerFunction::InitTabContents(int tab_id) {
255 // Find the TabContents that contains this tab id.
256 contents_ = NULL;
257 TabContentsWrapper* wrapper = NULL;
258 bool result = ExtensionTabUtil::GetTabById(
259 tab_id, profile(), include_incognito(), NULL, NULL, &wrapper, NULL);
260 if (!result || !wrapper) {
261 error_ = error_ = ExtensionErrorUtils::FormatErrorMessage(
262 keys::kNoTabError,
263 base::IntToString(tab_id));
264 return false;
265 }
266 contents_ = wrapper->tab_contents();
267 return true;
268}
269
270bool DebuggerFunction::InitClientHost(int tab_id) {
271 if (!InitTabContents(tab_id))
272 return false;
273
274 RenderViewHost* rvh = contents_->render_view_host();
275 client_host_ = AttachedClientHosts::GetInstance()->Lookup(rvh);
276
277 if (!client_host_ ||
278 !client_host_->MatchesContentsAndExtensionId(contents_,
279 GetExtension()->id())) {
280 error_ = ExtensionErrorUtils::FormatErrorMessage(
281 keys::kNotAttachedError,
282 base::IntToString(tab_id));
283 return false;
284 }
285 return true;
286}
287
288AttachDebuggerFunction::AttachDebuggerFunction() {}
289
290AttachDebuggerFunction::~AttachDebuggerFunction() {}
291
292bool AttachDebuggerFunction::RunImpl() {
293 int tab_id;
294 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
295
296 if (!InitTabContents(tab_id))
297 return false;
298
299 DevToolsClientHost* client_host = DevToolsManager::GetInstance()->
300 GetDevToolsClientHostFor(contents_->render_view_host());
301
302 if (client_host != NULL) {
303 error_ = ExtensionErrorUtils::FormatErrorMessage(
304 keys::kAlreadyAttachedError,
305 base::IntToString(tab_id));
306 return false;
307 }
308
309 new ExtensionDevToolsClientHost(contents_, GetExtension()->id(), tab_id);
[email protected]ff31a8a2011-03-30 14:13:59310 SendResponse(true);
[email protected]91ba3312011-03-17 20:39:22311 return true;
312}
313
314DetachDebuggerFunction::DetachDebuggerFunction() {}
315
316DetachDebuggerFunction::~DetachDebuggerFunction() {}
317
318bool DetachDebuggerFunction::RunImpl() {
319 int tab_id;
320 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
321
322 if (!InitClientHost(tab_id))
323 return false;
324
325 client_host_->Close();
[email protected]ff31a8a2011-03-30 14:13:59326 SendResponse(true);
[email protected]91ba3312011-03-17 20:39:22327 return true;
328}
329
[email protected]ac310102011-04-08 14:08:33330SendRequestDebuggerFunction::SendRequestDebuggerFunction() {}
[email protected]91ba3312011-03-17 20:39:22331
[email protected]ac310102011-04-08 14:08:33332SendRequestDebuggerFunction::~SendRequestDebuggerFunction() {}
[email protected]91ba3312011-03-17 20:39:22333
[email protected]ac310102011-04-08 14:08:33334bool SendRequestDebuggerFunction::RunImpl() {
[email protected]91ba3312011-03-17 20:39:22335 int tab_id;
336 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
337
[email protected]91ba3312011-03-17 20:39:22338 if (!InitClientHost(tab_id))
339 return false;
340
[email protected]ac310102011-04-08 14:08:33341 std::string method;
342 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &method));
[email protected]ff31a8a2011-03-30 14:13:59343
[email protected]ac310102011-04-08 14:08:33344 Value *params;
[email protected]9080b802011-04-11 13:14:50345 if (!args_->Get(2, &params))
[email protected]ac310102011-04-08 14:08:33346 params = NULL;
[email protected]ff31a8a2011-03-30 14:13:59347
[email protected]ac310102011-04-08 14:08:33348 client_host_->SendMessageToBackend(this, method, params);
[email protected]91ba3312011-03-17 20:39:22349 return true;
350}
[email protected]ff31a8a2011-03-30 14:13:59351
[email protected]ac310102011-04-08 14:08:33352void SendRequestDebuggerFunction::SendResponseBody(
353 DictionaryValue* dictionary) {
354 Value* error_body;
355 if (dictionary->Get("error", &error_body)) {
356 base::JSONWriter::Write(error_body, false, &error_);
[email protected]ff31a8a2011-03-30 14:13:59357 SendResponse(false);
358 return;
359 }
360
[email protected]ac310102011-04-08 14:08:33361 Value* result_body;
362 if (dictionary->Get("result", &result_body))
363 result_.reset(result_body->DeepCopy());
[email protected]ff31a8a2011-03-30 14:13:59364 else
365 result_.reset(new DictionaryValue());
366 SendResponse(true);
367}