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