Home | History | Annotate | Download | only in debugger
      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 
     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 
    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 
    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
    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 
    212 ExtensionDevToolsInfoBarDelegate::ExtensionDevToolsInfoBarDelegate(
    213     const std::string& client_name)
    214     : ConfirmInfoBarDelegate(),
    215       client_name_(client_name),
    216       client_host_(NULL) {
    217 }
    218 
    219 ExtensionDevToolsInfoBarDelegate::~ExtensionDevToolsInfoBarDelegate() {
    220 }
    221 
    222 void ExtensionDevToolsInfoBarDelegate::InfoBarDismissed() {
    223   if (client_host_)
    224     client_host_->MarkAsDismissed();
    225 }
    226 
    227 infobars::InfoBarDelegate::Type
    228 ExtensionDevToolsInfoBarDelegate::GetInfoBarType() const {
    229   return WARNING_TYPE;
    230 }
    231 
    232 bool ExtensionDevToolsInfoBarDelegate::ShouldExpireInternal(
    233     const NavigationDetails& details) const {
    234   return false;
    235 }
    236 
    237 base::string16 ExtensionDevToolsInfoBarDelegate::GetMessageText() const {
    238   return l10n_util::GetStringFUTF16(IDS_DEV_TOOLS_INFOBAR_LABEL,
    239                                     base::UTF8ToUTF16(client_name_));
    240 }
    241 
    242 int ExtensionDevToolsInfoBarDelegate::GetButtons() const {
    243   return BUTTON_CANCEL;
    244 }
    245 
    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 
    274 AttachedClientHosts::AttachedClientHosts() {
    275 }
    276 
    277 AttachedClientHosts::~AttachedClientHosts() {
    278 }
    279 
    280 // static
    281 AttachedClientHosts* AttachedClientHosts::GetInstance() {
    282   return Singleton<AttachedClientHosts>::get();
    283 }
    284 
    285 void AttachedClientHosts::Add(ExtensionDevToolsClientHost* client_host) {
    286   client_hosts_.insert(client_host);
    287 }
    288 
    289 void AttachedClientHosts::Remove(ExtensionDevToolsClientHost* client_host) {
    290   client_hosts_.erase(client_host);
    291 }
    292 
    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 
    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 
    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
    371 void ExtensionDevToolsClientHost::InspectedContentsClosing() {
    372   SendDetachedEvent();
    373   delete this;
    374 }
    375 
    376 void ExtensionDevToolsClientHost::ReplacedWithAnotherClient() {
    377   detach_reason_ = OnDetach::REASON_REPLACED_WITH_DEVTOOLS;
    378 }
    379 
    380 void ExtensionDevToolsClientHost::Close() {
    381   DevToolsManager::GetInstance()->ClientHostClosing(this);
    382   delete this;
    383 }
    384 
    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 
    404 void ExtensionDevToolsClientHost::MarkAsDismissed() {
    405   detach_reason_ = OnDetach::REASON_CANCELED_BY_USER;
    406 }
    407 
    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 
    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 
    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 
    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 
    490 DebuggerFunction::DebuggerFunction()
    491     : client_host_(NULL) {
    492 }
    493 
    494 DebuggerFunction::~DebuggerFunction() {
    495 }
    496 
    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 
    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 
    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 
    573 DebuggerAttachFunction::DebuggerAttachFunction() {
    574 }
    575 
    576 DebuggerAttachFunction::~DebuggerAttachFunction() {
    577 }
    578 
    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 
    629 DebuggerDetachFunction::DebuggerDetachFunction() {
    630 }
    631 
    632 DebuggerDetachFunction::~DebuggerDetachFunction() {
    633 }
    634 
    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 
    651 DebuggerSendCommandFunction::DebuggerSendCommandFunction() {
    652 }
    653 
    654 DebuggerSendCommandFunction::~DebuggerSendCommandFunction() {
    655 }
    656 
    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 
    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 
    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 
    734 DebuggerGetTargetsFunction::DebuggerGetTargetsFunction() {
    735 }
    736 
    737 DebuggerGetTargetsFunction::~DebuggerGetTargetsFunction() {
    738 }
    739 
    740 bool DebuggerGetTargetsFunction::RunAsync() {
    741   DevToolsTargetImpl::EnumerateAllTargets(
    742       base::Bind(&DebuggerGetTargetsFunction::SendTargetList, this));
    743   return true;
    744 }
    745 
    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