Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2011 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/extension_debugger_api.h"
      8 
      9 #include <map>
     10 #include <set>
     11 
     12 #include "base/json/json_reader.h"
     13 #include "base/json/json_writer.h"
     14 #include "base/memory/singleton.h"
     15 #include "base/string_number_conversions.h"
     16 #include "base/values.h"
     17 #include "chrome/browser/debugger/devtools_client_host.h"
     18 #include "chrome/browser/debugger/devtools_manager.h"
     19 #include "chrome/browser/extensions/extension_debugger_api_constants.h"
     20 #include "chrome/browser/extensions/extension_event_router.h"
     21 #include "chrome/browser/extensions/extension_tabs_module.h"
     22 #include "chrome/browser/profiles/profile.h"
     23 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
     24 #include "chrome/common/devtools_messages.h"
     25 #include "chrome/common/extensions/extension.h"
     26 #include "chrome/common/extensions/extension_error_utils.h"
     27 #include "content/browser/tab_contents/tab_contents.h"
     28 #include "content/common/notification_service.h"
     29 
     30 namespace keys = extension_debugger_api_constants;
     31 
     32 class ExtensionDevToolsClientHost : public DevToolsClientHost,
     33                                     public NotificationObserver {
     34  public:
     35   ExtensionDevToolsClientHost(TabContents* tab_contents,
     36                               const std::string& extension_id,
     37                               int tab_id);
     38 
     39   ~ExtensionDevToolsClientHost();
     40 
     41   bool MatchesContentsAndExtensionId(TabContents* tab_contents,
     42                                      const std::string& extension_id);
     43   void Close();
     44   void SendMessageToBackend(SendRequestDebuggerFunction* function,
     45                             const std::string& method,
     46                             Value* params);
     47 
     48   // DevToolsClientHost interface
     49   virtual void InspectedTabClosing();
     50   virtual void SendMessageToClient(const IPC::Message& msg);
     51   virtual void TabReplaced(TabContentsWrapper* tab_contents);
     52   virtual void FrameNavigating(const std::string& url) {}
     53 
     54  private:
     55   // NotificationObserver implementation.
     56   virtual void Observe(NotificationType type,
     57                        const NotificationSource& source,
     58                        const NotificationDetails& details);
     59   void OnDispatchOnInspectorFrontend(const std::string& data);
     60 
     61   TabContents* tab_contents_;
     62   std::string extension_id_;
     63   int tab_id_;
     64   NotificationRegistrar registrar_;
     65   int last_request_id_;
     66   typedef std::map<int, scoped_refptr<SendRequestDebuggerFunction> >
     67       PendingRequests;
     68   PendingRequests pending_requests_;
     69 
     70   DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsClientHost);
     71 };
     72 
     73 namespace {
     74 
     75 class AttachedClientHosts {
     76  public:
     77   AttachedClientHosts() {}
     78 
     79   // Returns the singleton instance of this class
     80   static AttachedClientHosts* GetInstance() {
     81     return Singleton<AttachedClientHosts>::get();
     82   }
     83 
     84   void Add(ExtensionDevToolsClientHost* client_host) {
     85     client_hosts_.insert(client_host);
     86   }
     87 
     88   void Remove(ExtensionDevToolsClientHost* client_host) {
     89     client_hosts_.erase(client_host);
     90   }
     91 
     92   ExtensionDevToolsClientHost* Lookup(RenderViewHost* rvh) {
     93     DevToolsClientHost* client_host =
     94         DevToolsManager::GetInstance()->GetDevToolsClientHostFor(rvh);
     95     std::set<DevToolsClientHost*>::iterator it =
     96         client_hosts_.find(client_host);
     97     if (it == client_hosts_.end())
     98       return NULL;
     99     return static_cast<ExtensionDevToolsClientHost*>(client_host);
    100   }
    101 
    102  private:
    103   std::set<DevToolsClientHost*> client_hosts_;
    104 };
    105 
    106 }  // namespace
    107 
    108 ExtensionDevToolsClientHost::ExtensionDevToolsClientHost(
    109     TabContents* tab_contents,
    110     const std::string& extension_id,
    111     int tab_id)
    112     : tab_contents_(tab_contents),
    113       extension_id_(extension_id),
    114       tab_id_(tab_id),
    115       last_request_id_(0) {
    116   AttachedClientHosts::GetInstance()->Add(this);
    117 
    118   // Detach from debugger when extension unloads.
    119   registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
    120                  Source<Profile>(tab_contents_->profile()));
    121 
    122   // Attach to debugger and tell it we are ready.
    123   DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
    124       tab_contents_->render_view_host(),
    125       this);
    126   DevToolsManager::GetInstance()->ForwardToDevToolsAgent(
    127       this,
    128       DevToolsAgentMsg_FrontendLoaded());
    129 }
    130 
    131 ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() {
    132   AttachedClientHosts::GetInstance()->Remove(this);
    133 }
    134 
    135 bool ExtensionDevToolsClientHost::MatchesContentsAndExtensionId(
    136     TabContents* tab_contents,
    137     const std::string& extension_id) {
    138   return tab_contents == tab_contents_ && extension_id_ == extension_id;
    139 }
    140 
    141 // DevToolsClientHost interface
    142 void ExtensionDevToolsClientHost::InspectedTabClosing() {
    143   // Tell extension that this client host has been detached.
    144   Profile* profile = tab_contents_->profile();
    145   if (profile != NULL && profile->GetExtensionEventRouter()) {
    146     ListValue args;
    147     args.Append(Value::CreateIntegerValue(tab_id_));
    148 
    149     std::string json_args;
    150     base::JSONWriter::Write(&args, false, &json_args);
    151 
    152     profile->GetExtensionEventRouter()->DispatchEventToExtension(
    153         extension_id_, keys::kOnDetach, json_args, profile, GURL());
    154   }
    155   delete this;
    156 }
    157 
    158 void ExtensionDevToolsClientHost::SendMessageToClient(
    159     const IPC::Message& msg) {
    160   IPC_BEGIN_MESSAGE_MAP(ExtensionDevToolsClientHost, msg)
    161     IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
    162                         OnDispatchOnInspectorFrontend);
    163     IPC_MESSAGE_UNHANDLED_ERROR()
    164   IPC_END_MESSAGE_MAP()
    165 }
    166 
    167 void ExtensionDevToolsClientHost::TabReplaced(
    168     TabContentsWrapper* tab_contents) {
    169   tab_contents_ = tab_contents->tab_contents();
    170 }
    171 
    172 void ExtensionDevToolsClientHost::Close() {
    173   DevToolsClientHost::NotifyCloseListener();
    174   delete this;
    175 }
    176 
    177 void ExtensionDevToolsClientHost::SendMessageToBackend(
    178     SendRequestDebuggerFunction* function,
    179     const std::string& method,
    180     Value* params) {
    181   DictionaryValue protocol_request;
    182   int request_id = ++last_request_id_;
    183   pending_requests_[request_id] = function;
    184   protocol_request.SetInteger("id", request_id);
    185   protocol_request.SetString("method", method);
    186   if (params)
    187     protocol_request.Set("params", params->DeepCopy());
    188 
    189   std::string json_args;
    190   base::JSONWriter::Write(&protocol_request, false, &json_args);
    191   DevToolsManager::GetInstance()->ForwardToDevToolsAgent(
    192       this,
    193       DevToolsAgentMsg_DispatchOnInspectorBackend(json_args));
    194 }
    195 
    196 void ExtensionDevToolsClientHost::Observe(
    197     NotificationType type,
    198     const NotificationSource& source,
    199     const NotificationDetails& details) {
    200   DCHECK(type == NotificationType::EXTENSION_UNLOADED);
    201   Close();
    202 }
    203 
    204 void ExtensionDevToolsClientHost::OnDispatchOnInspectorFrontend(
    205     const std::string& data) {
    206   Profile* profile = tab_contents_->profile();
    207   if (profile == NULL || !profile->GetExtensionEventRouter())
    208     return;
    209 
    210   scoped_ptr<Value> result(base::JSONReader::Read(data, false));
    211   if (!result->IsType(Value::TYPE_DICTIONARY))
    212     return;
    213   DictionaryValue* dictionary = static_cast<DictionaryValue*>(result.get());
    214 
    215   int id;
    216   if (!dictionary->GetInteger("id", &id)) {
    217     std::string method_name;
    218     if (!dictionary->GetString("method", &method_name))
    219       return;
    220 
    221     ListValue args;
    222     args.Append(Value::CreateIntegerValue(tab_id_));
    223     args.Append(Value::CreateStringValue(method_name));
    224     Value* params_value;
    225     if (dictionary->Get("params", &params_value))
    226       args.Append(params_value->DeepCopy());
    227 
    228     std::string json_args;
    229     base::JSONWriter::Write(&args, false, &json_args);
    230 
    231     profile->GetExtensionEventRouter()->DispatchEventToExtension(
    232         extension_id_, keys::kOnEvent, json_args, profile, GURL());
    233   } else {
    234     SendRequestDebuggerFunction* function = pending_requests_[id];
    235     if (!function)
    236       return;
    237 
    238     function->SendResponseBody(dictionary);
    239     pending_requests_.erase(id);
    240   }
    241 }
    242 
    243 DebuggerFunction::DebuggerFunction()
    244     : contents_(0),
    245       client_host_(0) {
    246 }
    247 
    248 bool DebuggerFunction::InitTabContents(int tab_id) {
    249   // Find the TabContents that contains this tab id.
    250   contents_ = NULL;
    251   TabContentsWrapper* wrapper = NULL;
    252   bool result = ExtensionTabUtil::GetTabById(
    253       tab_id, profile(), include_incognito(), NULL, NULL, &wrapper, NULL);
    254   if (!result || !wrapper) {
    255     error_ = error_ = ExtensionErrorUtils::FormatErrorMessage(
    256         keys::kNoTabError,
    257         base::IntToString(tab_id));
    258     return false;
    259   }
    260   contents_ = wrapper->tab_contents();
    261   return true;
    262 }
    263 
    264 bool DebuggerFunction::InitClientHost(int tab_id) {
    265   if (!InitTabContents(tab_id))
    266     return false;
    267 
    268   RenderViewHost* rvh = contents_->render_view_host();
    269   client_host_ = AttachedClientHosts::GetInstance()->Lookup(rvh);
    270 
    271   if (!client_host_ ||
    272       !client_host_->MatchesContentsAndExtensionId(contents_,
    273                                                    GetExtension()->id())) {
    274     error_ = ExtensionErrorUtils::FormatErrorMessage(
    275         keys::kNotAttachedError,
    276         base::IntToString(tab_id));
    277     return false;
    278   }
    279   return true;
    280 }
    281 
    282 AttachDebuggerFunction::AttachDebuggerFunction() {}
    283 
    284 AttachDebuggerFunction::~AttachDebuggerFunction() {}
    285 
    286 bool AttachDebuggerFunction::RunImpl() {
    287   int tab_id;
    288   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
    289 
    290   if (!InitTabContents(tab_id))
    291     return false;
    292 
    293   DevToolsClientHost* client_host = DevToolsManager::GetInstance()->
    294       GetDevToolsClientHostFor(contents_->render_view_host());
    295 
    296   if (client_host != NULL) {
    297     error_ = ExtensionErrorUtils::FormatErrorMessage(
    298         keys::kAlreadyAttachedError,
    299         base::IntToString(tab_id));
    300     return false;
    301   }
    302 
    303   new ExtensionDevToolsClientHost(contents_, GetExtension()->id(), tab_id);
    304   SendResponse(true);
    305   return true;
    306 }
    307 
    308 DetachDebuggerFunction::DetachDebuggerFunction() {}
    309 
    310 DetachDebuggerFunction::~DetachDebuggerFunction() {}
    311 
    312 bool DetachDebuggerFunction::RunImpl() {
    313   int tab_id;
    314   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
    315 
    316   if (!InitClientHost(tab_id))
    317     return false;
    318 
    319   client_host_->Close();
    320   SendResponse(true);
    321   return true;
    322 }
    323 
    324 SendRequestDebuggerFunction::SendRequestDebuggerFunction() {}
    325 
    326 SendRequestDebuggerFunction::~SendRequestDebuggerFunction() {}
    327 
    328 bool SendRequestDebuggerFunction::RunImpl() {
    329   int tab_id;
    330   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id));
    331 
    332   if (!InitClientHost(tab_id))
    333     return false;
    334 
    335   std::string method;
    336   EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &method));
    337 
    338   Value *params;
    339   if (!args_->Get(2, &params))
    340     params = NULL;
    341 
    342   client_host_->SendMessageToBackend(this, method, params);
    343   return true;
    344 }
    345 
    346 void SendRequestDebuggerFunction::SendResponseBody(
    347     DictionaryValue* dictionary) {
    348   Value* error_body;
    349   if (dictionary->Get("error", &error_body)) {
    350     base::JSONWriter::Write(error_body, false, &error_);
    351     SendResponse(false);
    352     return;
    353   }
    354 
    355   Value* result_body;
    356   if (dictionary->Get("result", &result_body))
    357     result_.reset(result_body->DeepCopy());
    358   else
    359     result_.reset(new DictionaryValue());
    360   SendResponse(true);
    361 }
    362