• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // Implements the Chrome Extensions Debugger API.
6 
7 #include "chrome/browser/extensions/api/debugger/debugger_api.h"
8 
9 #include <map>
10 #include <set>
11 
12 #include "base/command_line.h"
13 #include "base/json/json_reader.h"
14 #include "base/json/json_writer.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/memory/singleton.h"
17 #include "base/scoped_observer.h"
18 #include "base/stl_util.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/values.h"
22 #include "chrome/browser/chrome_notification_types.h"
23 #include "chrome/browser/devtools/devtools_target_impl.h"
24 #include "chrome/browser/extensions/api/debugger/debugger_api_constants.h"
25 #include "chrome/browser/extensions/extension_service.h"
26 #include "chrome/browser/extensions/extension_tab_util.h"
27 #include "chrome/browser/infobars/infobar_service.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
30 #include "chrome/common/chrome_switches.h"
31 #include "components/infobars/core/confirm_infobar_delegate.h"
32 #include "components/infobars/core/infobar.h"
33 #include "content/public/browser/devtools_agent_host.h"
34 #include "content/public/browser/devtools_client_host.h"
35 #include "content/public/browser/devtools_http_handler.h"
36 #include "content/public/browser/devtools_manager.h"
37 #include "content/public/browser/notification_service.h"
38 #include "content/public/browser/notification_source.h"
39 #include "content/public/browser/render_process_host.h"
40 #include "content/public/browser/render_view_host.h"
41 #include "content/public/browser/render_widget_host.h"
42 #include "content/public/browser/web_contents.h"
43 #include "content/public/common/content_client.h"
44 #include "content/public/common/url_utils.h"
45 #include "extensions/browser/event_router.h"
46 #include "extensions/browser/extension_host.h"
47 #include "extensions/browser/extension_registry.h"
48 #include "extensions/browser/extension_registry_observer.h"
49 #include "extensions/browser/extension_system.h"
50 #include "extensions/common/constants.h"
51 #include "extensions/common/error_utils.h"
52 #include "extensions/common/extension.h"
53 #include "extensions/common/manifest_constants.h"
54 #include "extensions/common/permissions/permissions_data.h"
55 #include "extensions/common/switches.h"
56 #include "grit/generated_resources.h"
57 #include "ui/base/l10n/l10n_util.h"
58 
59 using content::DevToolsAgentHost;
60 using content::DevToolsClientHost;
61 using content::DevToolsHttpHandler;
62 using content::DevToolsManager;
63 using content::RenderProcessHost;
64 using content::RenderViewHost;
65 using content::RenderWidgetHost;
66 using content::WebContents;
67 
68 namespace keys = debugger_api_constants;
69 namespace Attach = extensions::api::debugger::Attach;
70 namespace Detach = extensions::api::debugger::Detach;
71 namespace OnDetach = extensions::api::debugger::OnDetach;
72 namespace OnEvent = extensions::api::debugger::OnEvent;
73 namespace SendCommand = extensions::api::debugger::SendCommand;
74 
75 namespace extensions {
76 class ExtensionRegistry;
77 
78 // ExtensionDevToolsClientHost ------------------------------------------------
79 
80 class ExtensionDevToolsClientHost : public DevToolsClientHost,
81                                     public content::NotificationObserver,
82                                     public ExtensionRegistryObserver {
83  public:
84   ExtensionDevToolsClientHost(Profile* profile,
85                               DevToolsAgentHost* agent_host,
86                               const std::string& extension_id,
87                               const std::string& extension_name,
88                               const Debuggee& debuggee,
89                               infobars::InfoBar* infobar);
90 
91   virtual ~ExtensionDevToolsClientHost();
92 
extension_id()93   const std::string& extension_id() { return extension_id_; }
94   void Close();
95   void SendMessageToBackend(DebuggerSendCommandFunction* function,
96                             const std::string& method,
97                             SendCommand::Params::CommandParams* command_params);
98 
99   // Marks connection as to-be-terminated by the user.
100   void MarkAsDismissed();
101 
102   // DevToolsClientHost interface
103   virtual void InspectedContentsClosing() OVERRIDE;
104   virtual void DispatchOnInspectorFrontend(const std::string& message) OVERRIDE;
105   virtual void ReplacedWithAnotherClient() OVERRIDE;
106 
107  private:
108   void SendDetachedEvent();
109 
110   // content::NotificationObserver implementation.
111   virtual void Observe(int type,
112                        const content::NotificationSource& source,
113                        const content::NotificationDetails& details) OVERRIDE;
114 
115   // ExtensionRegistryObserver implementation.
116   virtual void OnExtensionUnloaded(
117       content::BrowserContext* browser_context,
118       const Extension* extension,
119       UnloadedExtensionInfo::Reason reason) OVERRIDE;
120 
121   Profile* profile_;
122   scoped_refptr<DevToolsAgentHost> agent_host_;
123   std::string extension_id_;
124   Debuggee debuggee_;
125   content::NotificationRegistrar registrar_;
126   int last_request_id_;
127   typedef std::map<int, scoped_refptr<DebuggerSendCommandFunction> >
128       PendingRequests;
129   PendingRequests pending_requests_;
130   infobars::InfoBar* infobar_;
131   OnDetach::Reason detach_reason_;
132 
133   // Listen to extension unloaded notification.
134   ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
135       extension_registry_observer_;
136 
137   DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsClientHost);
138 };
139 
140 // The member function declarations come after the other class declarations, so
141 // they can call members on them.
142 
143 
144 namespace {
145 
146 // Helpers --------------------------------------------------------------------
147 
CopyDebuggee(Debuggee * dst,const Debuggee & src)148 void CopyDebuggee(Debuggee* dst, const Debuggee& src) {
149   if (src.tab_id)
150     dst->tab_id.reset(new int(*src.tab_id));
151   if (src.extension_id)
152     dst->extension_id.reset(new std::string(*src.extension_id));
153   if (src.target_id)
154     dst->target_id.reset(new std::string(*src.target_id));
155 }
156 
157 
158 // ExtensionDevToolsInfoBarDelegate -------------------------------------------
159 
160 class ExtensionDevToolsInfoBarDelegate : public ConfirmInfoBarDelegate {
161  public:
162   // Creates an extension dev tools infobar and delegate and adds the infobar to
163   // the InfoBarService associated with |rvh|.  Returns the infobar if it was
164   // successfully added.
165   static infobars::InfoBar* Create(RenderViewHost* rvh,
166                                    const std::string& client_name);
167 
set_client_host(ExtensionDevToolsClientHost * client_host)168   void set_client_host(ExtensionDevToolsClientHost* client_host) {
169     client_host_ = client_host;
170   }
171 
172  private:
173   explicit ExtensionDevToolsInfoBarDelegate(const std::string& client_name);
174   virtual ~ExtensionDevToolsInfoBarDelegate();
175 
176   // ConfirmInfoBarDelegate:
177   virtual void InfoBarDismissed() OVERRIDE;
178   virtual Type GetInfoBarType() const OVERRIDE;
179   virtual bool ShouldExpireInternal(
180       const NavigationDetails& details) const OVERRIDE;
181   virtual base::string16 GetMessageText() const OVERRIDE;
182   virtual int GetButtons() const OVERRIDE;
183   virtual bool Cancel() OVERRIDE;
184 
185   std::string client_name_;
186   ExtensionDevToolsClientHost* client_host_;
187 
188   DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsInfoBarDelegate);
189 };
190 
191 // static
Create(RenderViewHost * rvh,const std::string & client_name)192 infobars::InfoBar* ExtensionDevToolsInfoBarDelegate::Create(
193     RenderViewHost* rvh,
194     const std::string& client_name) {
195   if (!rvh)
196     return NULL;
197 
198   WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
199   if (!web_contents)
200     return NULL;
201 
202   InfoBarService* infobar_service =
203       InfoBarService::FromWebContents(web_contents);
204   if (!infobar_service)
205     return NULL;
206 
207   return infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar(
208       scoped_ptr<ConfirmInfoBarDelegate>(
209           new ExtensionDevToolsInfoBarDelegate(client_name))));
210 }
211 
ExtensionDevToolsInfoBarDelegate(const std::string & client_name)212 ExtensionDevToolsInfoBarDelegate::ExtensionDevToolsInfoBarDelegate(
213     const std::string& client_name)
214     : ConfirmInfoBarDelegate(),
215       client_name_(client_name),
216       client_host_(NULL) {
217 }
218 
~ExtensionDevToolsInfoBarDelegate()219 ExtensionDevToolsInfoBarDelegate::~ExtensionDevToolsInfoBarDelegate() {
220 }
221 
InfoBarDismissed()222 void ExtensionDevToolsInfoBarDelegate::InfoBarDismissed() {
223   if (client_host_)
224     client_host_->MarkAsDismissed();
225 }
226 
227 infobars::InfoBarDelegate::Type
GetInfoBarType() const228 ExtensionDevToolsInfoBarDelegate::GetInfoBarType() const {
229   return WARNING_TYPE;
230 }
231 
ShouldExpireInternal(const NavigationDetails & details) const232 bool ExtensionDevToolsInfoBarDelegate::ShouldExpireInternal(
233     const NavigationDetails& details) const {
234   return false;
235 }
236 
GetMessageText() const237 base::string16 ExtensionDevToolsInfoBarDelegate::GetMessageText() const {
238   return l10n_util::GetStringFUTF16(IDS_DEV_TOOLS_INFOBAR_LABEL,
239                                     base::UTF8ToUTF16(client_name_));
240 }
241 
GetButtons() const242 int ExtensionDevToolsInfoBarDelegate::GetButtons() const {
243   return BUTTON_CANCEL;
244 }
245 
Cancel()246 bool ExtensionDevToolsInfoBarDelegate::Cancel() {
247   InfoBarDismissed();
248   return true;
249 }
250 
251 
252 // AttachedClientHosts --------------------------------------------------------
253 
254 class AttachedClientHosts {
255  public:
256   AttachedClientHosts();
257   ~AttachedClientHosts();
258 
259   // Returns the singleton instance of this class.
260   static AttachedClientHosts* GetInstance();
261 
262   void Add(ExtensionDevToolsClientHost* client_host);
263   void Remove(ExtensionDevToolsClientHost* client_host);
264   ExtensionDevToolsClientHost* Lookup(DevToolsAgentHost* agent_host,
265                                       const std::string& extension_id);
266 
267  private:
268   typedef std::set<ExtensionDevToolsClientHost*> ClientHosts;
269   ClientHosts client_hosts_;
270 
271   DISALLOW_COPY_AND_ASSIGN(AttachedClientHosts);
272 };
273 
AttachedClientHosts()274 AttachedClientHosts::AttachedClientHosts() {
275 }
276 
~AttachedClientHosts()277 AttachedClientHosts::~AttachedClientHosts() {
278 }
279 
280 // static
GetInstance()281 AttachedClientHosts* AttachedClientHosts::GetInstance() {
282   return Singleton<AttachedClientHosts>::get();
283 }
284 
Add(ExtensionDevToolsClientHost * client_host)285 void AttachedClientHosts::Add(ExtensionDevToolsClientHost* client_host) {
286   client_hosts_.insert(client_host);
287 }
288 
Remove(ExtensionDevToolsClientHost * client_host)289 void AttachedClientHosts::Remove(ExtensionDevToolsClientHost* client_host) {
290   client_hosts_.erase(client_host);
291 }
292 
Lookup(DevToolsAgentHost * agent_host,const std::string & extension_id)293 ExtensionDevToolsClientHost* AttachedClientHosts::Lookup(
294     DevToolsAgentHost* agent_host,
295     const std::string& extension_id) {
296   DevToolsManager* manager = DevToolsManager::GetInstance();
297   for (ClientHosts::iterator it = client_hosts_.begin();
298        it != client_hosts_.end(); ++it) {
299     ExtensionDevToolsClientHost* client_host = *it;
300     if (manager->GetDevToolsAgentHostFor(client_host) == agent_host &&
301         client_host->extension_id() == extension_id)
302       return client_host;
303   }
304   return NULL;
305 }
306 
307 }  // namespace
308 
309 
310 // ExtensionDevToolsClientHost ------------------------------------------------
311 
ExtensionDevToolsClientHost(Profile * profile,DevToolsAgentHost * agent_host,const std::string & extension_id,const std::string & extension_name,const Debuggee & debuggee,infobars::InfoBar * infobar)312 ExtensionDevToolsClientHost::ExtensionDevToolsClientHost(
313     Profile* profile,
314     DevToolsAgentHost* agent_host,
315     const std::string& extension_id,
316     const std::string& extension_name,
317     const Debuggee& debuggee,
318     infobars::InfoBar* infobar)
319     : profile_(profile),
320       agent_host_(agent_host),
321       extension_id_(extension_id),
322       last_request_id_(0),
323       infobar_(infobar),
324       detach_reason_(OnDetach::REASON_TARGET_CLOSED),
325       extension_registry_observer_(this) {
326   CopyDebuggee(&debuggee_, debuggee);
327 
328   AttachedClientHosts::GetInstance()->Add(this);
329 
330   // ExtensionRegistryObserver listen extension unloaded and detach debugger
331   // from there.
332   extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
333 
334   // RVH-based agents disconnect from their clients when the app is terminating
335   // but shared worker-based agents do not.
336   // Disconnect explicitly to make sure that |this| observer is not leaked.
337   registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
338                  content::NotificationService::AllSources());
339 
340   // Attach to debugger and tell it we are ready.
341   DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
342       agent_host_.get(), this);
343 
344   if (infobar_) {
345     static_cast<ExtensionDevToolsInfoBarDelegate*>(
346         infobar_->delegate())->set_client_host(this);
347     registrar_.Add(
348         this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
349         content::Source<InfoBarService>(InfoBarService::FromWebContents(
350             WebContents::FromRenderViewHost(
351                 agent_host_->GetRenderViewHost()))));
352   }
353 }
354 
~ExtensionDevToolsClientHost()355 ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() {
356   // Ensure calling RemoveInfoBar() below won't result in Observe() trying to
357   // Close() us.
358   registrar_.RemoveAll();
359 
360   if (infobar_) {
361     static_cast<ExtensionDevToolsInfoBarDelegate*>(
362         infobar_->delegate())->set_client_host(NULL);
363     InfoBarService* infobar_service = InfoBarService::FromWebContents(
364         WebContents::FromRenderViewHost(agent_host_->GetRenderViewHost()));
365     infobar_service->RemoveInfoBar(infobar_);
366   }
367   AttachedClientHosts::GetInstance()->Remove(this);
368 }
369 
370 // DevToolsClientHost interface
InspectedContentsClosing()371 void ExtensionDevToolsClientHost::InspectedContentsClosing() {
372   SendDetachedEvent();
373   delete this;
374 }
375 
ReplacedWithAnotherClient()376 void ExtensionDevToolsClientHost::ReplacedWithAnotherClient() {
377   detach_reason_ = OnDetach::REASON_REPLACED_WITH_DEVTOOLS;
378 }
379 
Close()380 void ExtensionDevToolsClientHost::Close() {
381   DevToolsManager::GetInstance()->ClientHostClosing(this);
382   delete this;
383 }
384 
SendMessageToBackend(DebuggerSendCommandFunction * function,const std::string & method,SendCommand::Params::CommandParams * command_params)385 void ExtensionDevToolsClientHost::SendMessageToBackend(
386     DebuggerSendCommandFunction* function,
387     const std::string& method,
388     SendCommand::Params::CommandParams* command_params) {
389   base::DictionaryValue protocol_request;
390   int request_id = ++last_request_id_;
391   pending_requests_[request_id] = function;
392   protocol_request.SetInteger("id", request_id);
393   protocol_request.SetString("method", method);
394   if (command_params) {
395     protocol_request.Set("params",
396                          command_params->additional_properties.DeepCopy());
397   }
398 
399   std::string json_args;
400   base::JSONWriter::Write(&protocol_request, &json_args);
401   DevToolsManager::GetInstance()->DispatchOnInspectorBackend(this, json_args);
402 }
403 
MarkAsDismissed()404 void ExtensionDevToolsClientHost::MarkAsDismissed() {
405   detach_reason_ = OnDetach::REASON_CANCELED_BY_USER;
406 }
407 
SendDetachedEvent()408 void ExtensionDevToolsClientHost::SendDetachedEvent() {
409   if (!EventRouter::Get(profile_))
410     return;
411 
412   scoped_ptr<base::ListValue> args(OnDetach::Create(debuggee_,
413                                                     detach_reason_));
414   scoped_ptr<Event> event(new Event(OnDetach::kEventName, args.Pass()));
415   event->restrict_to_browser_context = profile_;
416   EventRouter::Get(profile_)
417       ->DispatchEventToExtension(extension_id_, event.Pass());
418 }
419 
OnExtensionUnloaded(content::BrowserContext * browser_context,const Extension * extension,UnloadedExtensionInfo::Reason reason)420 void ExtensionDevToolsClientHost::OnExtensionUnloaded(
421     content::BrowserContext* browser_context,
422     const Extension* extension,
423     UnloadedExtensionInfo::Reason reason) {
424   if (extension->id() == extension_id_)
425     Close();
426 }
427 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)428 void ExtensionDevToolsClientHost::Observe(
429     int type,
430     const content::NotificationSource& source,
431     const content::NotificationDetails& details) {
432   switch (type) {
433     case chrome::NOTIFICATION_APP_TERMINATING:
434       Close();
435       break;
436     case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED:
437       if (content::Details<infobars::InfoBar::RemovedDetails>(details)->first ==
438           infobar_) {
439         infobar_ = NULL;
440         SendDetachedEvent();
441         Close();
442       }
443       break;
444     default:
445       NOTREACHED();
446   }
447 }
448 
DispatchOnInspectorFrontend(const std::string & message)449 void ExtensionDevToolsClientHost::DispatchOnInspectorFrontend(
450     const std::string& message) {
451   if (!EventRouter::Get(profile_))
452     return;
453 
454   scoped_ptr<base::Value> result(base::JSONReader::Read(message));
455   if (!result->IsType(base::Value::TYPE_DICTIONARY))
456     return;
457   base::DictionaryValue* dictionary =
458       static_cast<base::DictionaryValue*>(result.get());
459 
460   int id;
461   if (!dictionary->GetInteger("id", &id)) {
462     std::string method_name;
463     if (!dictionary->GetString("method", &method_name))
464       return;
465 
466     OnEvent::Params params;
467     base::DictionaryValue* params_value;
468     if (dictionary->GetDictionary("params", &params_value))
469       params.additional_properties.Swap(params_value);
470 
471     scoped_ptr<base::ListValue> args(
472         OnEvent::Create(debuggee_, method_name, params));
473     scoped_ptr<Event> event(new Event(OnEvent::kEventName, args.Pass()));
474     event->restrict_to_browser_context = profile_;
475     EventRouter::Get(profile_)
476         ->DispatchEventToExtension(extension_id_, event.Pass());
477   } else {
478     DebuggerSendCommandFunction* function = pending_requests_[id].get();
479     if (!function)
480       return;
481 
482     function->SendResponseBody(dictionary);
483     pending_requests_.erase(id);
484   }
485 }
486 
487 
488 // DebuggerFunction -----------------------------------------------------------
489 
DebuggerFunction()490 DebuggerFunction::DebuggerFunction()
491     : client_host_(NULL) {
492 }
493 
~DebuggerFunction()494 DebuggerFunction::~DebuggerFunction() {
495 }
496 
FormatErrorMessage(const std::string & format)497 void DebuggerFunction::FormatErrorMessage(const std::string& format) {
498   if (debuggee_.tab_id)
499     error_ = ErrorUtils::FormatErrorMessage(
500       format, keys::kTabTargetType, base::IntToString(*debuggee_.tab_id));
501   else if (debuggee_.extension_id)
502     error_ = ErrorUtils::FormatErrorMessage(
503       format, keys::kBackgroundPageTargetType, *debuggee_.extension_id);
504   else
505     error_ = ErrorUtils::FormatErrorMessage(
506       format, keys::kOpaqueTargetType, *debuggee_.target_id);
507 }
508 
InitAgentHost()509 bool DebuggerFunction::InitAgentHost() {
510   const Extension* extension = GetExtension();
511   if (debuggee_.tab_id) {
512     WebContents* web_contents = NULL;
513     bool result = ExtensionTabUtil::GetTabById(*debuggee_.tab_id,
514                                                GetProfile(),
515                                                include_incognito(),
516                                                NULL,
517                                                NULL,
518                                                &web_contents,
519                                                NULL);
520     if (result && web_contents) {
521       // TODO(rdevlin.cronin) This should definitely be GetLastCommittedURL().
522       GURL url = web_contents->GetVisibleURL();
523       if (PermissionsData::IsRestrictedUrl(url, url, extension, &error_))
524         return false;
525       agent_host_ = DevToolsAgentHost::GetOrCreateFor(web_contents);
526     }
527   } else if (debuggee_.extension_id) {
528     ExtensionHost* extension_host =
529         ExtensionSystem::Get(GetProfile())
530             ->process_manager()
531             ->GetBackgroundHostForExtension(*debuggee_.extension_id);
532     if (extension_host) {
533       if (PermissionsData::IsRestrictedUrl(extension_host->GetURL(),
534                                            extension_host->GetURL(),
535                                            extension,
536                                            &error_)) {
537         return false;
538       }
539       agent_host_ = DevToolsAgentHost::GetOrCreateFor(
540           extension_host->render_view_host());
541     }
542   } else if (debuggee_.target_id) {
543     agent_host_ = DevToolsAgentHost::GetForId(*debuggee_.target_id);
544   } else {
545     error_ = keys::kInvalidTargetError;
546     return false;
547   }
548 
549   if (!agent_host_.get()) {
550     FormatErrorMessage(keys::kNoTargetError);
551     return false;
552   }
553   return true;
554 }
555 
InitClientHost()556 bool DebuggerFunction::InitClientHost() {
557   if (!InitAgentHost())
558     return false;
559 
560   client_host_ = AttachedClientHosts::GetInstance()->Lookup(
561       agent_host_.get(), GetExtension()->id());
562 
563   if (!client_host_) {
564     FormatErrorMessage(keys::kNotAttachedError);
565     return false;
566   }
567   return true;
568 }
569 
570 
571 // DebuggerAttachFunction -----------------------------------------------------
572 
DebuggerAttachFunction()573 DebuggerAttachFunction::DebuggerAttachFunction() {
574 }
575 
~DebuggerAttachFunction()576 DebuggerAttachFunction::~DebuggerAttachFunction() {
577 }
578 
RunAsync()579 bool DebuggerAttachFunction::RunAsync() {
580   scoped_ptr<Attach::Params> params(Attach::Params::Create(*args_));
581   EXTENSION_FUNCTION_VALIDATE(params.get());
582 
583   CopyDebuggee(&debuggee_, params->target);
584   if (!InitAgentHost())
585     return false;
586 
587   if (!DevToolsHttpHandler::IsSupportedProtocolVersion(
588           params->required_version)) {
589     error_ = ErrorUtils::FormatErrorMessage(
590         keys::kProtocolVersionNotSupportedError,
591         params->required_version);
592     return false;
593   }
594 
595   if (agent_host_->IsAttached()) {
596     FormatErrorMessage(keys::kAlreadyAttachedError);
597     return false;
598   }
599 
600   const Extension* extension = GetExtension();
601   infobars::InfoBar* infobar = NULL;
602   if (!CommandLine::ForCurrentProcess()->
603        HasSwitch(::switches::kSilentDebuggerExtensionAPI)) {
604     // Do not attach to the target if for any reason the infobar cannot be shown
605     // for this WebContents instance.
606     infobar = ExtensionDevToolsInfoBarDelegate::Create(
607         agent_host_->GetRenderViewHost(), extension->name());
608     if (!infobar) {
609       error_ = ErrorUtils::FormatErrorMessage(
610           keys::kSilentDebuggingRequired,
611           ::switches::kSilentDebuggerExtensionAPI);
612       return false;
613     }
614   }
615 
616   new ExtensionDevToolsClientHost(GetProfile(),
617                                   agent_host_.get(),
618                                   extension->id(),
619                                   extension->name(),
620                                   debuggee_,
621                                   infobar);
622   SendResponse(true);
623   return true;
624 }
625 
626 
627 // DebuggerDetachFunction -----------------------------------------------------
628 
DebuggerDetachFunction()629 DebuggerDetachFunction::DebuggerDetachFunction() {
630 }
631 
~DebuggerDetachFunction()632 DebuggerDetachFunction::~DebuggerDetachFunction() {
633 }
634 
RunAsync()635 bool DebuggerDetachFunction::RunAsync() {
636   scoped_ptr<Detach::Params> params(Detach::Params::Create(*args_));
637   EXTENSION_FUNCTION_VALIDATE(params.get());
638 
639   CopyDebuggee(&debuggee_, params->target);
640   if (!InitClientHost())
641     return false;
642 
643   client_host_->Close();
644   SendResponse(true);
645   return true;
646 }
647 
648 
649 // DebuggerSendCommandFunction ------------------------------------------------
650 
DebuggerSendCommandFunction()651 DebuggerSendCommandFunction::DebuggerSendCommandFunction() {
652 }
653 
~DebuggerSendCommandFunction()654 DebuggerSendCommandFunction::~DebuggerSendCommandFunction() {
655 }
656 
RunAsync()657 bool DebuggerSendCommandFunction::RunAsync() {
658   scoped_ptr<SendCommand::Params> params(SendCommand::Params::Create(*args_));
659   EXTENSION_FUNCTION_VALIDATE(params.get());
660 
661   CopyDebuggee(&debuggee_, params->target);
662   if (!InitClientHost())
663     return false;
664 
665   client_host_->SendMessageToBackend(this, params->method,
666       params->command_params.get());
667   return true;
668 }
669 
SendResponseBody(base::DictionaryValue * response)670 void DebuggerSendCommandFunction::SendResponseBody(
671     base::DictionaryValue* response) {
672   base::Value* error_body;
673   if (response->Get("error", &error_body)) {
674     base::JSONWriter::Write(error_body, &error_);
675     SendResponse(false);
676     return;
677   }
678 
679   base::DictionaryValue* result_body;
680   SendCommand::Results::Result result;
681   if (response->GetDictionary("result", &result_body))
682     result.additional_properties.Swap(result_body);
683 
684   results_ = SendCommand::Results::Create(result);
685   SendResponse(true);
686 }
687 
688 
689 // DebuggerGetTargetsFunction -------------------------------------------------
690 
691 namespace {
692 
693 const char kTargetIdField[] = "id";
694 const char kTargetTypeField[] = "type";
695 const char kTargetTitleField[] = "title";
696 const char kTargetAttachedField[] = "attached";
697 const char kTargetUrlField[] = "url";
698 const char kTargetFaviconUrlField[] = "faviconUrl";
699 const char kTargetTypePage[] = "page";
700 const char kTargetTypeBackgroundPage[] = "background_page";
701 const char kTargetTypeWorker[] = "worker";
702 const char kTargetTypeOther[] = "other";
703 const char kTargetTabIdField[] = "tabId";
704 const char kTargetExtensionIdField[] = "extensionId";
705 
SerializeTarget(const DevToolsTargetImpl & target)706 base::Value* SerializeTarget(const DevToolsTargetImpl& target) {
707   base::DictionaryValue* dictionary = new base::DictionaryValue();
708 
709   dictionary->SetString(kTargetIdField, target.GetId());
710   dictionary->SetString(kTargetTitleField, target.GetTitle());
711   dictionary->SetBoolean(kTargetAttachedField, target.IsAttached());
712   dictionary->SetString(kTargetUrlField, target.GetURL().spec());
713 
714   std::string type = target.GetType();
715   if (type == kTargetTypePage) {
716     dictionary->SetInteger(kTargetTabIdField, target.GetTabId());
717   } else if (type == kTargetTypeBackgroundPage) {
718     dictionary->SetString(kTargetExtensionIdField, target.GetExtensionId());
719   } else if (type != kTargetTypeWorker) {
720     // DevToolsTargetImpl may support more types than the debugger API.
721     type = kTargetTypeOther;
722   }
723   dictionary->SetString(kTargetTypeField, type);
724 
725   GURL favicon_url = target.GetFaviconURL();
726   if (favicon_url.is_valid())
727     dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());
728 
729   return dictionary;
730 }
731 
732 }  // namespace
733 
DebuggerGetTargetsFunction()734 DebuggerGetTargetsFunction::DebuggerGetTargetsFunction() {
735 }
736 
~DebuggerGetTargetsFunction()737 DebuggerGetTargetsFunction::~DebuggerGetTargetsFunction() {
738 }
739 
RunAsync()740 bool DebuggerGetTargetsFunction::RunAsync() {
741   DevToolsTargetImpl::EnumerateAllTargets(
742       base::Bind(&DebuggerGetTargetsFunction::SendTargetList, this));
743   return true;
744 }
745 
SendTargetList(const std::vector<DevToolsTargetImpl * > & target_list)746 void DebuggerGetTargetsFunction::SendTargetList(
747     const std::vector<DevToolsTargetImpl*>& target_list) {
748   scoped_ptr<base::ListValue> result(new base::ListValue());
749   for (size_t i = 0; i < target_list.size(); ++i)
750     result->Append(SerializeTarget(*target_list[i]));
751   STLDeleteContainerPointers(target_list.begin(), target_list.end());
752   SetResult(result.release());
753   SendResponse(true);
754 }
755 
756 }  // namespace extensions
757