Home | History | Annotate | Download | only in extensions
      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 #include "chrome/browser/extensions/extension_keybinding_registry.h"
      6 
      7 #include "base/values.h"
      8 #include "chrome/browser/chrome_notification_types.h"
      9 #include "chrome/browser/extensions/active_tab_permission_granter.h"
     10 #include "chrome/browser/extensions/api/commands/command_service.h"
     11 #include "chrome/browser/extensions/extension_service.h"
     12 #include "chrome/browser/extensions/extension_system.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "chrome/common/extensions/extension_set.h"
     15 #include "extensions/browser/event_router.h"
     16 #include "extensions/common/manifest_constants.h"
     17 
     18 namespace extensions {
     19 
     20 ExtensionKeybindingRegistry::ExtensionKeybindingRegistry(
     21     Profile* profile, ExtensionFilter extension_filter, Delegate* delegate)
     22     : profile_(profile),
     23       extension_filter_(extension_filter),
     24       delegate_(delegate) {
     25   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
     26                  content::Source<Profile>(profile->GetOriginalProfile()));
     27   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
     28                  content::Source<Profile>(profile->GetOriginalProfile()));
     29   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_COMMAND_ADDED,
     30                  content::Source<Profile>(profile->GetOriginalProfile()));
     31   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_COMMAND_REMOVED,
     32                  content::Source<Profile>(profile->GetOriginalProfile()));
     33 }
     34 
     35 ExtensionKeybindingRegistry::~ExtensionKeybindingRegistry() {
     36 }
     37 
     38 void ExtensionKeybindingRegistry::RemoveExtensionKeybinding(
     39     const Extension* extension,
     40     const std::string& command_name) {
     41   EventTargets::iterator it = event_targets_.begin();
     42   while (it != event_targets_.end()) {
     43     TargetList& target_list = it->second;
     44     TargetList::iterator target = target_list.begin();
     45     while (target != target_list.end()) {
     46       if (target->first == extension->id() &&
     47           (command_name.empty() || command_name == target->second))
     48         target = target_list.erase(target);
     49       else
     50         target++;
     51     }
     52 
     53     EventTargets::iterator old = it++;
     54     if (target_list.empty()) {
     55       // Let each platform-specific implementation get a chance to clean up.
     56       RemoveExtensionKeybindingImpl(old->first, command_name);
     57       event_targets_.erase(old);
     58 
     59       // If a specific command_name was requested, it has now been deleted so no
     60       // further work is required.
     61       if (!command_name.empty())
     62         break;
     63     }
     64   }
     65 }
     66 
     67 void ExtensionKeybindingRegistry::Init() {
     68   ExtensionService* service =
     69       extensions::ExtensionSystem::Get(profile_)->extension_service();
     70   if (!service)
     71     return;  // ExtensionService can be null during testing.
     72 
     73   const ExtensionSet* extensions = service->extensions();
     74   ExtensionSet::const_iterator iter = extensions->begin();
     75   for (; iter != extensions->end(); ++iter)
     76     if (ExtensionMatchesFilter(iter->get()))
     77       AddExtensionKeybinding(iter->get(), std::string());
     78 }
     79 
     80 bool ExtensionKeybindingRegistry::ShouldIgnoreCommand(
     81     const std::string& command) const {
     82   return command == manifest_values::kPageActionCommandEvent ||
     83          command == manifest_values::kBrowserActionCommandEvent ||
     84          command == manifest_values::kScriptBadgeCommandEvent;
     85 }
     86 
     87 bool ExtensionKeybindingRegistry::NotifyEventTargets(
     88     const ui::Accelerator& accelerator) {
     89   EventTargets::iterator targets = event_targets_.find(accelerator);
     90   if (targets == event_targets_.end() || targets->second.empty())
     91     return false;
     92 
     93   for (TargetList::const_iterator it = targets->second.begin();
     94        it != targets->second.end(); it++)
     95     CommandExecuted(it->first, it->second);
     96 
     97   return true;
     98 }
     99 
    100 void ExtensionKeybindingRegistry::CommandExecuted(
    101     const std::string& extension_id, const std::string& command) {
    102   ExtensionService* service =
    103       ExtensionSystem::Get(profile_)->extension_service();
    104 
    105   const Extension* extension = service->extensions()->GetByID(extension_id);
    106   if (!extension)
    107     return;
    108 
    109   // Grant before sending the event so that the permission is granted before
    110   // the extension acts on the command. NOTE: The Global Commands handler does
    111   // not set the delegate as it deals only with named commands (not page/browser
    112   // actions that are associated with the current page directly).
    113   ActiveTabPermissionGranter* granter =
    114       delegate_ ? delegate_->GetActiveTabPermissionGranter() : NULL;
    115   if (granter)
    116     granter->GrantIfRequested(extension);
    117 
    118   scoped_ptr<base::ListValue> args(new base::ListValue());
    119   args->Append(new base::StringValue(command));
    120 
    121   scoped_ptr<Event> event(new Event("commands.onCommand", args.Pass()));
    122   event->restrict_to_browser_context = profile_;
    123   event->user_gesture = EventRouter::USER_GESTURE_ENABLED;
    124   ExtensionSystem::Get(profile_)->event_router()->
    125       DispatchEventToExtension(extension_id, event.Pass());
    126 }
    127 
    128 void ExtensionKeybindingRegistry::Observe(
    129     int type,
    130     const content::NotificationSource& source,
    131     const content::NotificationDetails& details) {
    132   switch (type) {
    133     case chrome::NOTIFICATION_EXTENSION_LOADED: {
    134       const extensions::Extension* extension =
    135           content::Details<const extensions::Extension>(details).ptr();
    136       if (ExtensionMatchesFilter(extension))
    137         AddExtensionKeybinding(extension, std::string());
    138       break;
    139     }
    140     case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
    141       const extensions::Extension* extension =
    142           content::Details<UnloadedExtensionInfo>(details)->extension;
    143       if (ExtensionMatchesFilter(extension))
    144         RemoveExtensionKeybinding(extension, std::string());
    145       break;
    146     }
    147     case chrome::NOTIFICATION_EXTENSION_COMMAND_ADDED:
    148     case chrome::NOTIFICATION_EXTENSION_COMMAND_REMOVED: {
    149       std::pair<const std::string, const std::string>* payload =
    150           content::Details<std::pair<const std::string, const std::string> >(
    151               details).ptr();
    152 
    153       const extensions::Extension* extension =
    154           ExtensionSystem::Get(profile_)->extension_service()->
    155               extensions()->GetByID(payload->first);
    156       // During install and uninstall the extension won't be found. We'll catch
    157       // those events above, with the LOADED/UNLOADED, so we ignore this event.
    158       if (!extension)
    159         return;
    160 
    161       if (ExtensionMatchesFilter(extension)) {
    162         if (type == chrome::NOTIFICATION_EXTENSION_COMMAND_ADDED)
    163           AddExtensionKeybinding(extension, payload->second);
    164         else
    165           RemoveExtensionKeybinding(extension, payload->second);
    166       }
    167       break;
    168     }
    169     default:
    170       NOTREACHED();
    171       break;
    172   }
    173 }
    174 
    175 bool ExtensionKeybindingRegistry::ExtensionMatchesFilter(
    176     const extensions::Extension* extension)
    177 {
    178   switch (extension_filter_) {
    179     case ALL_EXTENSIONS:
    180       return true;
    181     case PLATFORM_APPS_ONLY:
    182       return extension->is_platform_app();
    183     default:
    184       NOTREACHED();
    185   }
    186   return false;
    187 }
    188 
    189 }  // namespace extensions
    190