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/extensions/active_tab_permission_granter.h" 9#include "chrome/browser/extensions/extension_service.h" 10#include "chrome/browser/profiles/profile.h" 11#include "chrome/common/extensions/command.h" 12#include "content/public/browser/browser_context.h" 13#include "extensions/browser/event_router.h" 14#include "extensions/browser/extension_registry.h" 15#include "extensions/browser/extension_system.h" 16#include "extensions/browser/notification_types.h" 17#include "extensions/common/extension_set.h" 18#include "extensions/common/manifest_constants.h" 19 20namespace { 21const char kOnCommandEventName[] = "commands.onCommand"; 22} // namespace 23 24namespace extensions { 25 26ExtensionKeybindingRegistry::ExtensionKeybindingRegistry( 27 content::BrowserContext* context, 28 ExtensionFilter extension_filter, 29 Delegate* delegate) 30 : browser_context_(context), 31 extension_filter_(extension_filter), 32 delegate_(delegate), 33 extension_registry_observer_(this) { 34 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); 35 36 Profile* profile = Profile::FromBrowserContext(browser_context_); 37 registrar_.Add(this, 38 extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED, 39 content::Source<Profile>(profile->GetOriginalProfile())); 40 registrar_.Add(this, 41 extensions::NOTIFICATION_EXTENSION_COMMAND_REMOVED, 42 content::Source<Profile>(profile->GetOriginalProfile())); 43} 44 45ExtensionKeybindingRegistry::~ExtensionKeybindingRegistry() { 46} 47 48void ExtensionKeybindingRegistry::RemoveExtensionKeybinding( 49 const Extension* extension, 50 const std::string& command_name) { 51 EventTargets::iterator it = event_targets_.begin(); 52 while (it != event_targets_.end()) { 53 TargetList& target_list = it->second; 54 TargetList::iterator target = target_list.begin(); 55 while (target != target_list.end()) { 56 if (target->first == extension->id() && 57 (command_name.empty() || command_name == target->second)) 58 target = target_list.erase(target); 59 else 60 target++; 61 } 62 63 EventTargets::iterator old = it++; 64 if (target_list.empty()) { 65 // Let each platform-specific implementation get a chance to clean up. 66 RemoveExtensionKeybindingImpl(old->first, command_name); 67 event_targets_.erase(old); 68 69 // If a specific command_name was requested, it has now been deleted so no 70 // further work is required. 71 if (!command_name.empty()) 72 break; 73 } 74 } 75} 76 77void ExtensionKeybindingRegistry::Init() { 78 ExtensionService* service = 79 ExtensionSystem::Get(browser_context_)->extension_service(); 80 if (!service) 81 return; // ExtensionService can be null during testing. 82 83 const ExtensionSet* extensions = service->extensions(); 84 ExtensionSet::const_iterator iter = extensions->begin(); 85 for (; iter != extensions->end(); ++iter) 86 if (ExtensionMatchesFilter(iter->get())) 87 AddExtensionKeybinding(iter->get(), std::string()); 88} 89 90bool ExtensionKeybindingRegistry::ShouldIgnoreCommand( 91 const std::string& command) const { 92 return command == manifest_values::kPageActionCommandEvent || 93 command == manifest_values::kBrowserActionCommandEvent; 94} 95 96bool ExtensionKeybindingRegistry::NotifyEventTargets( 97 const ui::Accelerator& accelerator) { 98 return ExecuteCommands(accelerator, std::string()); 99} 100 101void ExtensionKeybindingRegistry::CommandExecuted( 102 const std::string& extension_id, const std::string& command) { 103 ExtensionService* service = 104 ExtensionSystem::Get(browser_context_)->extension_service(); 105 106 const Extension* extension = service->extensions()->GetByID(extension_id); 107 if (!extension) 108 return; 109 110 // Grant before sending the event so that the permission is granted before 111 // the extension acts on the command. NOTE: The Global Commands handler does 112 // not set the delegate as it deals only with named commands (not page/browser 113 // actions that are associated with the current page directly). 114 ActiveTabPermissionGranter* granter = 115 delegate_ ? delegate_->GetActiveTabPermissionGranter() : NULL; 116 if (granter) 117 granter->GrantIfRequested(extension); 118 119 scoped_ptr<base::ListValue> args(new base::ListValue()); 120 args->Append(new base::StringValue(command)); 121 122 scoped_ptr<Event> event(new Event(kOnCommandEventName, args.Pass())); 123 event->restrict_to_browser_context = browser_context_; 124 event->user_gesture = EventRouter::USER_GESTURE_ENABLED; 125 EventRouter::Get(browser_context_) 126 ->DispatchEventToExtension(extension_id, event.Pass()); 127} 128 129bool ExtensionKeybindingRegistry::IsAcceleratorRegistered( 130 const ui::Accelerator& accelerator) const { 131 return event_targets_.find(accelerator) != event_targets_.end(); 132} 133 134void ExtensionKeybindingRegistry::AddEventTarget( 135 const ui::Accelerator& accelerator, 136 const std::string& extension_id, 137 const std::string& command_name) { 138 event_targets_[accelerator].push_back( 139 std::make_pair(extension_id, command_name)); 140 // Shortcuts except media keys have only one target in the list. See comment 141 // about |event_targets_|. 142 if (!extensions::Command::IsMediaKey(accelerator)) 143 DCHECK_EQ(1u, event_targets_[accelerator].size()); 144} 145 146bool ExtensionKeybindingRegistry::GetFirstTarget( 147 const ui::Accelerator& accelerator, 148 std::string* extension_id, 149 std::string* command_name) const { 150 EventTargets::const_iterator targets = event_targets_.find(accelerator); 151 if (targets == event_targets_.end()) 152 return false; 153 154 DCHECK(!targets->second.empty()); 155 TargetList::const_iterator first_target = targets->second.begin(); 156 *extension_id = first_target->first; 157 *command_name = first_target->second; 158 return true; 159} 160 161bool ExtensionKeybindingRegistry::IsEventTargetsEmpty() const { 162 return event_targets_.empty(); 163} 164 165void ExtensionKeybindingRegistry::ExecuteCommand( 166 const std::string& extension_id, 167 const ui::Accelerator& accelerator) { 168 ExecuteCommands(accelerator, extension_id); 169} 170 171void ExtensionKeybindingRegistry::OnExtensionLoaded( 172 content::BrowserContext* browser_context, 173 const Extension* extension) { 174 if (ExtensionMatchesFilter(extension)) 175 AddExtensionKeybinding(extension, std::string()); 176} 177 178void ExtensionKeybindingRegistry::OnExtensionUnloaded( 179 content::BrowserContext* browser_context, 180 const Extension* extension, 181 UnloadedExtensionInfo::Reason reason) { 182 if (ExtensionMatchesFilter(extension)) 183 RemoveExtensionKeybinding(extension, std::string()); 184} 185 186void ExtensionKeybindingRegistry::Observe( 187 int type, 188 const content::NotificationSource& source, 189 const content::NotificationDetails& details) { 190 switch (type) { 191 case extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED: 192 case extensions::NOTIFICATION_EXTENSION_COMMAND_REMOVED: { 193 std::pair<const std::string, const std::string>* payload = 194 content::Details<std::pair<const std::string, const std::string> >( 195 details).ptr(); 196 197 const Extension* extension = ExtensionSystem::Get(browser_context_) 198 ->extension_service() 199 ->extensions() 200 ->GetByID(payload->first); 201 // During install and uninstall the extension won't be found. We'll catch 202 // those events above, with the LOADED/UNLOADED, so we ignore this event. 203 if (!extension) 204 return; 205 206 if (ExtensionMatchesFilter(extension)) { 207 if (type == extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED) 208 AddExtensionKeybinding(extension, payload->second); 209 else 210 RemoveExtensionKeybinding(extension, payload->second); 211 } 212 break; 213 } 214 default: 215 NOTREACHED(); 216 break; 217 } 218} 219 220bool ExtensionKeybindingRegistry::ExtensionMatchesFilter( 221 const extensions::Extension* extension) 222{ 223 switch (extension_filter_) { 224 case ALL_EXTENSIONS: 225 return true; 226 case PLATFORM_APPS_ONLY: 227 return extension->is_platform_app(); 228 default: 229 NOTREACHED(); 230 } 231 return false; 232} 233 234bool ExtensionKeybindingRegistry::ExecuteCommands( 235 const ui::Accelerator& accelerator, 236 const std::string& extension_id) { 237 EventTargets::iterator targets = event_targets_.find(accelerator); 238 if (targets == event_targets_.end() || targets->second.empty()) 239 return false; 240 241 bool executed = false; 242 for (TargetList::const_iterator it = targets->second.begin(); 243 it != targets->second.end(); it++) { 244 if (!extensions::EventRouter::Get(browser_context_) 245 ->ExtensionHasEventListener(it->first, kOnCommandEventName)) 246 continue; 247 248 if (extension_id.empty() || it->first == extension_id) { 249 CommandExecuted(it->first, it->second); 250 executed = true; 251 } 252 } 253 254 return executed; 255} 256 257} // namespace extensions 258