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);
FrameNavigating(const std::string & url)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:
AttachedClientHosts()77 AttachedClientHosts() {}
78
79 // Returns the singleton instance of this class
GetInstance()80 static AttachedClientHosts* GetInstance() {
81 return Singleton<AttachedClientHosts>::get();
82 }
83
Add(ExtensionDevToolsClientHost * client_host)84 void Add(ExtensionDevToolsClientHost* client_host) {
85 client_hosts_.insert(client_host);
86 }
87
Remove(ExtensionDevToolsClientHost * client_host)88 void Remove(ExtensionDevToolsClientHost* client_host) {
89 client_hosts_.erase(client_host);
90 }
91
Lookup(RenderViewHost * rvh)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
ExtensionDevToolsClientHost(TabContents * tab_contents,const std::string & extension_id,int tab_id)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
~ExtensionDevToolsClientHost()131 ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() {
132 AttachedClientHosts::GetInstance()->Remove(this);
133 }
134
MatchesContentsAndExtensionId(TabContents * tab_contents,const std::string & extension_id)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
InspectedTabClosing()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
SendMessageToClient(const IPC::Message & msg)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
TabReplaced(TabContentsWrapper * tab_contents)167 void ExtensionDevToolsClientHost::TabReplaced(
168 TabContentsWrapper* tab_contents) {
169 tab_contents_ = tab_contents->tab_contents();
170 }
171
Close()172 void ExtensionDevToolsClientHost::Close() {
173 DevToolsClientHost::NotifyCloseListener();
174 delete this;
175 }
176
SendMessageToBackend(SendRequestDebuggerFunction * function,const std::string & method,Value * params)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
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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
OnDispatchOnInspectorFrontend(const std::string & data)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
DebuggerFunction()243 DebuggerFunction::DebuggerFunction()
244 : contents_(0),
245 client_host_(0) {
246 }
247
InitTabContents(int tab_id)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
InitClientHost(int tab_id)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
AttachDebuggerFunction()282 AttachDebuggerFunction::AttachDebuggerFunction() {}
283
~AttachDebuggerFunction()284 AttachDebuggerFunction::~AttachDebuggerFunction() {}
285
RunImpl()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
DetachDebuggerFunction()308 DetachDebuggerFunction::DetachDebuggerFunction() {}
309
~DetachDebuggerFunction()310 DetachDebuggerFunction::~DetachDebuggerFunction() {}
311
RunImpl()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
SendRequestDebuggerFunction()324 SendRequestDebuggerFunction::SendRequestDebuggerFunction() {}
325
~SendRequestDebuggerFunction()326 SendRequestDebuggerFunction::~SendRequestDebuggerFunction() {}
327
RunImpl()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
SendResponseBody(DictionaryValue * dictionary)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