• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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", &params_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, &params))
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