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