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/strings/string_number_conversions.h"
     18 #include "base/strings/utf_string_conversions.h"
     19 #include "base/values.h"
     20 #include "chrome/browser/chrome_notification_types.h"
     21 #include "chrome/browser/extensions/api/debugger/debugger_api_constants.h"
     22 #include "chrome/browser/extensions/event_router.h"
     23 #include "chrome/browser/extensions/extension_host.h"
     24 #include "chrome/browser/extensions/extension_service.h"
     25 #include "chrome/browser/extensions/extension_system.h"
     26 #include "chrome/browser/extensions/extension_tab_util.h"
     27 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
     28 #include "chrome/browser/infobars/infobar.h"
     29 #include "chrome/browser/infobars/infobar_service.h"
     30 #include "chrome/browser/profiles/profile.h"
     31 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
     32 #include "chrome/common/chrome_switches.h"
     33 #include "chrome/common/extensions/extension.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/favicon_status.h"
     39 #include "content/public/browser/navigation_entry.h"
     40 #include "content/public/browser/notification_service.h"
     41 #include "content/public/browser/notification_source.h"
     42 #include "content/public/browser/render_process_host.h"
     43 #include "content/public/browser/render_view_host.h"
     44 #include "content/public/browser/render_widget_host.h"
     45 #include "content/public/browser/web_contents.h"
     46 #include "content/public/browser/worker_service.h"
     47 #include "content/public/common/content_client.h"
     48 #include "content/public/common/url_utils.h"
     49 #include "extensions/common/error_utils.h"
     50 #include "grit/generated_resources.h"
     51 #include "ui/base/l10n/l10n_util.h"
     52 
     53 using content::DevToolsAgentHost;
     54 using content::DevToolsClientHost;
     55 using content::DevToolsHttpHandler;
     56 using content::DevToolsManager;
     57 using content::RenderProcessHost;
     58 using content::RenderViewHost;
     59 using content::RenderWidgetHost;
     60 using content::WebContents;
     61 using content::WorkerService;
     62 using extensions::ErrorUtils;
     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 {
     72 class ExtensionDevToolsInfoBarDelegate;
     73 }  // namespace
     74 
     75 
     76 // ExtensionDevToolsClientHost ------------------------------------------------
     77 
     78 class ExtensionDevToolsClientHost : public DevToolsClientHost,
     79                                     public content::NotificationObserver {
     80  public:
     81   ExtensionDevToolsClientHost(
     82       Profile* profile,
     83       DevToolsAgentHost* agent_host,
     84       const std::string& extension_id,
     85       const std::string& extension_name,
     86       const Debuggee& debuggee,
     87       ExtensionDevToolsInfoBarDelegate* infobar);
     88 
     89   virtual ~ExtensionDevToolsClientHost();
     90 
     91   const std::string& extension_id() { return extension_id_; }
     92   void Close();
     93   void SendMessageToBackend(DebuggerSendCommandFunction* function,
     94                             const std::string& method,
     95                             SendCommand::Params::CommandParams* command_params);
     96 
     97   // Marks connection as to-be-terminated by the user.
     98   void MarkAsDismissed();
     99 
    100   // DevToolsClientHost interface
    101   virtual void InspectedContentsClosing() OVERRIDE;
    102   virtual void DispatchOnInspectorFrontend(const std::string& message) OVERRIDE;
    103   virtual void ReplacedWithAnotherClient() OVERRIDE;
    104 
    105  private:
    106   void SendDetachedEvent();
    107 
    108   // content::NotificationObserver implementation.
    109   virtual void Observe(int type,
    110                        const content::NotificationSource& source,
    111                        const content::NotificationDetails& details) OVERRIDE;
    112 
    113   Profile* profile_;
    114   scoped_refptr<DevToolsAgentHost> agent_host_;
    115   std::string extension_id_;
    116   Debuggee debuggee_;
    117   content::NotificationRegistrar registrar_;
    118   int last_request_id_;
    119   typedef std::map<int, scoped_refptr<DebuggerSendCommandFunction> >
    120       PendingRequests;
    121   PendingRequests pending_requests_;
    122   ExtensionDevToolsInfoBarDelegate* infobar_;
    123   OnDetach::Reason detach_reason_;
    124 
    125   DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsClientHost);
    126 };
    127 
    128 // The member function declarations come after the other class declarations, so
    129 // they can call members on them.
    130 
    131 
    132 namespace {
    133 
    134 // Helpers --------------------------------------------------------------------
    135 
    136 void CopyDebuggee(Debuggee* dst, const Debuggee& src) {
    137   if (src.tab_id)
    138     dst->tab_id.reset(new int(*src.tab_id));
    139   if (src.extension_id)
    140     dst->extension_id.reset(new std::string(*src.extension_id));
    141   if (src.target_id)
    142     dst->target_id.reset(new std::string(*src.target_id));
    143 }
    144 
    145 
    146 // ExtensionDevToolsInfoBarDelegate -------------------------------------------
    147 
    148 class ExtensionDevToolsInfoBarDelegate : public ConfirmInfoBarDelegate {
    149  public:
    150   // Creates an extension dev tools infobar delegate and adds it to the
    151   // InfoBarService associated with |rvh|.  Returns the delegate if it was
    152   // successfully added.
    153   static ExtensionDevToolsInfoBarDelegate* Create(
    154       RenderViewHost* rvh,
    155       const std::string& client_name);
    156 
    157   void set_client_host(ExtensionDevToolsClientHost* client_host) {
    158     client_host_ = client_host;
    159   }
    160 
    161  private:
    162   ExtensionDevToolsInfoBarDelegate(InfoBarService* infobar_service,
    163                                    const std::string& client_name);
    164   virtual ~ExtensionDevToolsInfoBarDelegate();
    165 
    166   // ConfirmInfoBarDelegate:
    167   virtual void InfoBarDismissed() OVERRIDE;
    168   virtual Type GetInfoBarType() const OVERRIDE;
    169   virtual bool ShouldExpireInternal(
    170       const content::LoadCommittedDetails& details) const OVERRIDE;
    171   virtual string16 GetMessageText() const OVERRIDE;
    172   virtual int GetButtons() const OVERRIDE;
    173   virtual bool Cancel() OVERRIDE;
    174 
    175   std::string client_name_;
    176   ExtensionDevToolsClientHost* client_host_;
    177 
    178   DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsInfoBarDelegate);
    179 };
    180 
    181 // static
    182 ExtensionDevToolsInfoBarDelegate* ExtensionDevToolsInfoBarDelegate::Create(
    183     RenderViewHost* rvh,
    184     const std::string& client_name) {
    185   if (!rvh)
    186     return NULL;
    187 
    188   WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
    189   if (!web_contents)
    190     return NULL;
    191 
    192   InfoBarService* infobar_service =
    193       InfoBarService::FromWebContents(web_contents);
    194   if (!infobar_service)
    195     return NULL;
    196 
    197   return static_cast<ExtensionDevToolsInfoBarDelegate*>(
    198       infobar_service->AddInfoBar(scoped_ptr<InfoBarDelegate>(
    199           new ExtensionDevToolsInfoBarDelegate(infobar_service, client_name))));
    200 }
    201 
    202 ExtensionDevToolsInfoBarDelegate::ExtensionDevToolsInfoBarDelegate(
    203     InfoBarService* infobar_service,
    204     const std::string& client_name)
    205     : ConfirmInfoBarDelegate(infobar_service),
    206       client_name_(client_name),
    207       client_host_(NULL) {
    208 }
    209 
    210 ExtensionDevToolsInfoBarDelegate::~ExtensionDevToolsInfoBarDelegate() {
    211 }
    212 
    213 void ExtensionDevToolsInfoBarDelegate::InfoBarDismissed() {
    214   if (client_host_)
    215     client_host_->MarkAsDismissed();
    216 }
    217 
    218 InfoBarDelegate::Type ExtensionDevToolsInfoBarDelegate::GetInfoBarType() const {
    219   return WARNING_TYPE;
    220 }
    221 
    222 bool ExtensionDevToolsInfoBarDelegate::ShouldExpireInternal(
    223     const content::LoadCommittedDetails& details) const {
    224   return false;
    225 }
    226 
    227 string16 ExtensionDevToolsInfoBarDelegate::GetMessageText() const {
    228   return l10n_util::GetStringFUTF16(IDS_DEV_TOOLS_INFOBAR_LABEL,
    229                                     UTF8ToUTF16(client_name_));
    230 }
    231 
    232 int ExtensionDevToolsInfoBarDelegate::GetButtons() const {
    233   return BUTTON_CANCEL;
    234 }
    235 
    236 bool ExtensionDevToolsInfoBarDelegate::Cancel() {
    237   InfoBarDismissed();
    238   return true;
    239 }
    240 
    241 
    242 // AttachedClientHosts --------------------------------------------------------
    243 
    244 class AttachedClientHosts {
    245  public:
    246   AttachedClientHosts();
    247   ~AttachedClientHosts();
    248 
    249   // Returns the singleton instance of this class.
    250   static AttachedClientHosts* GetInstance();
    251 
    252   void Add(ExtensionDevToolsClientHost* client_host);
    253   void Remove(ExtensionDevToolsClientHost* client_host);
    254   ExtensionDevToolsClientHost* Lookup(DevToolsAgentHost* agent_host,
    255                                       const std::string& extension_id);
    256 
    257  private:
    258   typedef std::set<ExtensionDevToolsClientHost*> ClientHosts;
    259   ClientHosts client_hosts_;
    260 
    261   DISALLOW_COPY_AND_ASSIGN(AttachedClientHosts);
    262 };
    263 
    264 AttachedClientHosts::AttachedClientHosts() {
    265 }
    266 
    267 AttachedClientHosts::~AttachedClientHosts() {
    268 }
    269 
    270 // static
    271 AttachedClientHosts* AttachedClientHosts::GetInstance() {
    272   return Singleton<AttachedClientHosts>::get();
    273 }
    274 
    275 void AttachedClientHosts::Add(ExtensionDevToolsClientHost* client_host) {
    276   client_hosts_.insert(client_host);
    277 }
    278 
    279 void AttachedClientHosts::Remove(ExtensionDevToolsClientHost* client_host) {
    280   client_hosts_.erase(client_host);
    281 }
    282 
    283 ExtensionDevToolsClientHost* AttachedClientHosts::Lookup(
    284     DevToolsAgentHost* agent_host,
    285     const std::string& extension_id) {
    286   DevToolsManager* manager = DevToolsManager::GetInstance();
    287   for (ClientHosts::iterator it = client_hosts_.begin();
    288        it != client_hosts_.end(); ++it) {
    289     ExtensionDevToolsClientHost* client_host = *it;
    290     if (manager->GetDevToolsAgentHostFor(client_host) == agent_host &&
    291         client_host->extension_id() == extension_id)
    292       return client_host;
    293   }
    294   return NULL;
    295 }
    296 
    297 }  // namespace
    298 
    299 
    300 // ExtensionDevToolsClientHost ------------------------------------------------
    301 
    302 ExtensionDevToolsClientHost::ExtensionDevToolsClientHost(
    303     Profile* profile,
    304     DevToolsAgentHost* agent_host,
    305     const std::string& extension_id,
    306     const std::string& extension_name,
    307     const Debuggee& debuggee,
    308     ExtensionDevToolsInfoBarDelegate* infobar)
    309     : profile_(profile),
    310       agent_host_(agent_host),
    311       extension_id_(extension_id),
    312       last_request_id_(0),
    313       infobar_(infobar),
    314       detach_reason_(OnDetach::REASON_TARGET_CLOSED) {
    315   CopyDebuggee(&debuggee_, debuggee);
    316 
    317   AttachedClientHosts::GetInstance()->Add(this);
    318 
    319   // Detach from debugger when extension unloads.
    320   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
    321                  content::Source<Profile>(profile_));
    322 
    323   // RVH-based agents disconnect from their clients when the app is terminating
    324   // but shared worker-based agents do not.
    325   // Disconnect explicitly to make sure that |this| observer is not leaked.
    326   registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
    327                  content::NotificationService::AllSources());
    328 
    329   // Attach to debugger and tell it we are ready.
    330   DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
    331       agent_host_.get(), this);
    332 
    333   if (infobar_) {
    334     infobar_->set_client_host(this);
    335     registrar_.Add(
    336         this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
    337         content::Source<InfoBarService>(InfoBarService::FromWebContents(
    338             WebContents::FromRenderViewHost(
    339                 agent_host_->GetRenderViewHost()))));
    340   }
    341 }
    342 
    343 ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() {
    344   // Ensure calling RemoveInfoBar() below won't result in Observe() trying to
    345   // Close() us.
    346   registrar_.RemoveAll();
    347 
    348   if (infobar_) {
    349     infobar_->set_client_host(NULL);
    350     InfoBarService::FromWebContents(WebContents::FromRenderViewHost(
    351         agent_host_->GetRenderViewHost()))->RemoveInfoBar(infobar_);
    352   }
    353   AttachedClientHosts::GetInstance()->Remove(this);
    354 }
    355 
    356 // DevToolsClientHost interface
    357 void ExtensionDevToolsClientHost::InspectedContentsClosing() {
    358   SendDetachedEvent();
    359   delete this;
    360 }
    361 
    362 void ExtensionDevToolsClientHost::ReplacedWithAnotherClient() {
    363   detach_reason_ = OnDetach::REASON_REPLACED_WITH_DEVTOOLS;
    364 }
    365 
    366 void ExtensionDevToolsClientHost::Close() {
    367   DevToolsManager::GetInstance()->ClientHostClosing(this);
    368   delete this;
    369 }
    370 
    371 void ExtensionDevToolsClientHost::SendMessageToBackend(
    372     DebuggerSendCommandFunction* function,
    373     const std::string& method,
    374     SendCommand::Params::CommandParams* command_params) {
    375   base::DictionaryValue protocol_request;
    376   int request_id = ++last_request_id_;
    377   pending_requests_[request_id] = function;
    378   protocol_request.SetInteger("id", request_id);
    379   protocol_request.SetString("method", method);
    380   if (command_params) {
    381     protocol_request.Set("params",
    382                          command_params->additional_properties.DeepCopy());
    383   }
    384 
    385   std::string json_args;
    386   base::JSONWriter::Write(&protocol_request, &json_args);
    387   DevToolsManager::GetInstance()->DispatchOnInspectorBackend(this, json_args);
    388 }
    389 
    390 void ExtensionDevToolsClientHost::MarkAsDismissed() {
    391   detach_reason_ = OnDetach::REASON_CANCELED_BY_USER;
    392 }
    393 
    394 void ExtensionDevToolsClientHost::SendDetachedEvent() {
    395   if (!extensions::ExtensionSystem::Get(profile_)->event_router())
    396     return;
    397 
    398   scoped_ptr<base::ListValue> args(OnDetach::Create(debuggee_,
    399                                                     detach_reason_));
    400   scoped_ptr<extensions::Event> event(new extensions::Event(
    401       keys::kOnDetach, args.Pass()));
    402   event->restrict_to_profile = profile_;
    403   extensions::ExtensionSystem::Get(profile_)->event_router()->
    404       DispatchEventToExtension(extension_id_, event.Pass());
    405 }
    406 
    407 void ExtensionDevToolsClientHost::Observe(
    408     int type,
    409     const content::NotificationSource& source,
    410     const content::NotificationDetails& details) {
    411   if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
    412     if (content::Details<extensions::UnloadedExtensionInfo>(details)->
    413         extension->id() == extension_id_)
    414       Close();
    415   } else if (type == chrome::NOTIFICATION_APP_TERMINATING) {
    416     Close();
    417   } else {
    418     DCHECK_EQ(chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, type);
    419     if (content::Details<InfoBarRemovedDetails>(details)->first == infobar_) {
    420       infobar_ = NULL;
    421       SendDetachedEvent();
    422       Close();
    423     }
    424   }
    425 }
    426 
    427 void ExtensionDevToolsClientHost::DispatchOnInspectorFrontend(
    428     const std::string& message) {
    429   if (!extensions::ExtensionSystem::Get(profile_)->event_router())
    430     return;
    431 
    432   scoped_ptr<Value> result(base::JSONReader::Read(message));
    433   if (!result->IsType(Value::TYPE_DICTIONARY))
    434     return;
    435   base::DictionaryValue* dictionary =
    436       static_cast<base::DictionaryValue*>(result.get());
    437 
    438   int id;
    439   if (!dictionary->GetInteger("id", &id)) {
    440     std::string method_name;
    441     if (!dictionary->GetString("method", &method_name))
    442       return;
    443 
    444     OnEvent::Params params;
    445     base::DictionaryValue* params_value;
    446     if (dictionary->GetDictionary("params", &params_value))
    447       params.additional_properties.Swap(params_value);
    448 
    449     scoped_ptr<ListValue> args(OnEvent::Create(debuggee_, method_name, params));
    450     scoped_ptr<extensions::Event> event(new extensions::Event(
    451         keys::kOnEvent, args.Pass()));
    452     event->restrict_to_profile = profile_;
    453     extensions::ExtensionSystem::Get(profile_)->event_router()->
    454         DispatchEventToExtension(extension_id_, event.Pass());
    455   } else {
    456     DebuggerSendCommandFunction* function = pending_requests_[id].get();
    457     if (!function)
    458       return;
    459 
    460     function->SendResponseBody(dictionary);
    461     pending_requests_.erase(id);
    462   }
    463 }
    464 
    465 
    466 // DebuggerFunction -----------------------------------------------------------
    467 
    468 DebuggerFunction::DebuggerFunction()
    469     : client_host_(NULL) {
    470 }
    471 
    472 DebuggerFunction::~DebuggerFunction() {
    473 }
    474 
    475 void DebuggerFunction::FormatErrorMessage(const std::string& format) {
    476   if (debuggee_.tab_id)
    477     error_ = ErrorUtils::FormatErrorMessage(
    478       format, keys::kTabTargetType, base::IntToString(*debuggee_.tab_id));
    479   else if (debuggee_.extension_id)
    480     error_ = ErrorUtils::FormatErrorMessage(
    481       format, keys::kBackgroundPageTargetType, *debuggee_.extension_id);
    482   else
    483     error_ = ErrorUtils::FormatErrorMessage(
    484       format, keys::kOpaqueTargetType, *debuggee_.target_id);
    485 }
    486 
    487 bool DebuggerFunction::InitAgentHost() {
    488   if (debuggee_.tab_id) {
    489     WebContents* web_contents = NULL;
    490     bool result = ExtensionTabUtil::GetTabById(
    491         *debuggee_.tab_id, profile(), include_incognito(), NULL, NULL,
    492         &web_contents, NULL);
    493     if (result && web_contents) {
    494       if (content::HasWebUIScheme(web_contents->GetURL())) {
    495         error_ = ErrorUtils::FormatErrorMessage(
    496             keys::kAttachToWebUIError,
    497             web_contents->GetURL().scheme());
    498         return false;
    499       }
    500       agent_host_ = DevToolsAgentHost::GetOrCreateFor(
    501           web_contents->GetRenderViewHost());
    502     }
    503   } else if (debuggee_.extension_id) {
    504     extensions::ExtensionHost* extension_host =
    505         extensions::ExtensionSystem::Get(profile())->process_manager()->
    506             GetBackgroundHostForExtension(*debuggee_.extension_id);
    507     if (extension_host) {
    508       agent_host_ = DevToolsAgentHost::GetOrCreateFor(
    509           extension_host->render_view_host());
    510     }
    511   } else if (debuggee_.target_id) {
    512     agent_host_ = DevToolsAgentHost::GetForId(*debuggee_.target_id);
    513   } else {
    514     error_ = keys::kInvalidTargetError;
    515     return false;
    516   }
    517 
    518   if (!agent_host_.get()) {
    519     FormatErrorMessage(keys::kNoTargetError);
    520     return false;
    521   }
    522   return true;
    523 }
    524 
    525 bool DebuggerFunction::InitClientHost() {
    526   if (!InitAgentHost())
    527     return false;
    528 
    529   client_host_ = AttachedClientHosts::GetInstance()->Lookup(
    530       agent_host_.get(), GetExtension()->id());
    531 
    532   if (!client_host_) {
    533     FormatErrorMessage(keys::kNotAttachedError);
    534     return false;
    535   }
    536   return true;
    537 }
    538 
    539 
    540 // DebuggerAttachFunction -----------------------------------------------------
    541 
    542 DebuggerAttachFunction::DebuggerAttachFunction() {
    543 }
    544 
    545 DebuggerAttachFunction::~DebuggerAttachFunction() {
    546 }
    547 
    548 bool DebuggerAttachFunction::RunImpl() {
    549   scoped_ptr<Attach::Params> params(Attach::Params::Create(*args_));
    550   EXTENSION_FUNCTION_VALIDATE(params.get());
    551 
    552   CopyDebuggee(&debuggee_, params->target);
    553   if (!InitAgentHost())
    554     return false;
    555 
    556   if (!DevToolsHttpHandler::IsSupportedProtocolVersion(
    557           params->required_version)) {
    558     error_ = ErrorUtils::FormatErrorMessage(
    559         keys::kProtocolVersionNotSupportedError,
    560         params->required_version);
    561     return false;
    562   }
    563 
    564   if (agent_host_->IsAttached()) {
    565     FormatErrorMessage(keys::kAlreadyAttachedError);
    566     return false;
    567   }
    568 
    569   ExtensionDevToolsInfoBarDelegate* infobar = NULL;
    570   if (!CommandLine::ForCurrentProcess()->
    571        HasSwitch(switches::kSilentDebuggerExtensionAPI)) {
    572     // Do not attach to the target if for any reason the infobar cannot be shown
    573     // for this WebContents instance.
    574     infobar = ExtensionDevToolsInfoBarDelegate::Create(
    575           agent_host_->GetRenderViewHost(), GetExtension()->name());
    576     if (!infobar) {
    577       error_ = ErrorUtils::FormatErrorMessage(
    578           keys::kSilentDebuggingRequired,
    579           switches::kSilentDebuggerExtensionAPI);
    580       return false;
    581     }
    582   }
    583 
    584   new ExtensionDevToolsClientHost(profile(), agent_host_.get(),
    585                                   GetExtension()->id(), GetExtension()->name(),
    586                                   debuggee_, 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 extensions::ExtensionHost*
    672     GetExtensionBackgroundHost(WebContents* web_contents) {
    673   Profile* profile =
    674       Profile::FromBrowserContext(web_contents->GetBrowserContext());
    675   if (!profile)
    676     return NULL;
    677 
    678   extensions::ExtensionHost* extension_host =
    679       extensions::ExtensionSystem::Get(profile)->process_manager()->
    680           GetBackgroundHostForExtension(web_contents->GetURL().host());
    681 
    682   return (extension_host && extension_host->host_contents() == web_contents) ?
    683       extension_host : NULL;
    684 }
    685 
    686 base::Value* SerializePageInfo(RenderViewHost* rvh) {
    687   WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
    688   if (!web_contents)
    689     return NULL;
    690 
    691   DevToolsAgentHost* agent_host = DevToolsAgentHost::GetOrCreateFor(rvh);
    692 
    693   base::DictionaryValue* dictionary = new base::DictionaryValue();
    694 
    695   dictionary->SetString(kTargetIdField, agent_host->GetId());
    696   dictionary->SetBoolean(kTargetAttachedField, agent_host->IsAttached());
    697   dictionary->SetString(kTargetUrlField, web_contents->GetURL().spec());
    698 
    699   extensions::ExtensionHost* extension_host =
    700       GetExtensionBackgroundHost(web_contents);
    701   if (extension_host) {
    702     // This RenderViewHost belongs to a background page.
    703     dictionary->SetString(kTargetTypeField, kTargetTypeBackgroundPage);
    704     dictionary->SetString(kTargetExtensionIdField,
    705                           extension_host->extension()->id());
    706     dictionary->SetString(kTargetTitleField,
    707                           extension_host->extension()->name());
    708   } else {
    709     int tab_id = ExtensionTabUtil::GetTabId(web_contents);
    710     if (tab_id != -1) {
    711       // This RenderViewHost belongs to a regular page.
    712       dictionary->SetString(kTargetTypeField, kTargetTypePage);
    713       dictionary->SetInteger(kTargetTabIdField, tab_id);
    714     } else {
    715       dictionary->SetString(kTargetTypeField, kTargetTypeOther);
    716     }
    717     dictionary->SetString(kTargetTitleField, web_contents->GetTitle());
    718 
    719     content::NavigationController& controller = web_contents->GetController();
    720     content::NavigationEntry* entry = controller.GetActiveEntry();
    721     if (entry != NULL && entry->GetURL().is_valid()) {
    722       dictionary->SetString(kTargetFaviconUrlField,
    723                             entry->GetFavicon().url.spec());
    724     }
    725   }
    726 
    727   return dictionary;
    728 }
    729 
    730 base::Value* SerializeWorkerInfo(const WorkerService::WorkerInfo& worker) {
    731   base::DictionaryValue* dictionary = new base::DictionaryValue;
    732 
    733   scoped_refptr<DevToolsAgentHost> agent(DevToolsAgentHost::GetForWorker(
    734       worker.process_id, worker.route_id));
    735   dictionary->SetString(kTargetIdField, agent->GetId());
    736   dictionary->SetString(kTargetTypeField, kTargetTypeWorker);
    737   dictionary->SetString(kTargetTitleField, worker.name);
    738   dictionary->SetString(kTargetUrlField, worker.url.spec());
    739   dictionary->SetBoolean(kTargetAttachedField, agent->IsAttached());
    740 
    741   return dictionary;
    742 }
    743 
    744 } // namespace
    745 
    746 DebuggerGetTargetsFunction::DebuggerGetTargetsFunction() {
    747 }
    748 
    749 DebuggerGetTargetsFunction::~DebuggerGetTargetsFunction() {
    750 }
    751 
    752 bool DebuggerGetTargetsFunction::RunImpl() {
    753   base::ListValue* results_list = new base::ListValue();
    754 
    755   std::vector<RenderViewHost*> rvh_list =
    756       DevToolsAgentHost::GetValidRenderViewHosts();
    757   for (std::vector<RenderViewHost*>::iterator it = rvh_list.begin();
    758        it != rvh_list.end(); ++it) {
    759     base::Value* value = SerializePageInfo(*it);
    760     if (value)
    761       results_list->Append(value);
    762   }
    763 
    764   content::BrowserThread::PostTaskAndReply(
    765       content::BrowserThread::IO,
    766       FROM_HERE,
    767       base::Bind(&DebuggerGetTargetsFunction::CollectWorkerInfo, this,
    768                  results_list),
    769       base::Bind(&DebuggerGetTargetsFunction::SendTargetList, this,
    770                  results_list));
    771   return true;
    772 }
    773 
    774 void DebuggerGetTargetsFunction::CollectWorkerInfo(base::ListValue* list) {
    775   std::vector<WorkerService::WorkerInfo> worker_info =
    776       WorkerService::GetInstance()->GetWorkers();
    777   for (size_t i = 0; i < worker_info.size(); ++i)
    778     list->Append(SerializeWorkerInfo(worker_info[i]));
    779 }
    780 
    781 void DebuggerGetTargetsFunction::SendTargetList(base::ListValue* list) {
    782   SetResult(list);
    783   SendResponse(true);
    784 }
    785