Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / extension_keybinding_registry.cc
blob690543568dba4886cdbf9fb45780d8d21a1e8fd7
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.
5 #include "chrome/browser/extensions/extension_keybinding_registry.h"
7 #include "base/values.h"
8 #include "chrome/browser/extensions/active_tab_permission_granter.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "chrome/common/extensions/command.h"
11 #include "content/public/browser/browser_context.h"
12 #include "extensions/browser/event_router.h"
13 #include "extensions/browser/extension_registry.h"
14 #include "extensions/browser/notification_types.h"
15 #include "extensions/common/extension_set.h"
16 #include "extensions/common/manifest_constants.h"
18 namespace {
19 const char kOnCommandEventName[] = "commands.onCommand";
20 } // namespace
22 namespace extensions {
24 ExtensionKeybindingRegistry::ExtensionKeybindingRegistry(
25 content::BrowserContext* context,
26 ExtensionFilter extension_filter,
27 Delegate* delegate)
28 : browser_context_(context),
29 extension_filter_(extension_filter),
30 delegate_(delegate),
31 extension_registry_observer_(this),
32 shortcut_handling_suspended_(false) {
33 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
35 Profile* profile = Profile::FromBrowserContext(browser_context_);
36 registrar_.Add(this,
37 extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED,
38 content::Source<Profile>(profile->GetOriginalProfile()));
39 registrar_.Add(this,
40 extensions::NOTIFICATION_EXTENSION_COMMAND_REMOVED,
41 content::Source<Profile>(profile->GetOriginalProfile()));
44 ExtensionKeybindingRegistry::~ExtensionKeybindingRegistry() {
47 void ExtensionKeybindingRegistry::SetShortcutHandlingSuspended(bool suspended) {
48 shortcut_handling_suspended_ = suspended;
49 OnShortcutHandlingSuspended(suspended);
52 void ExtensionKeybindingRegistry::RemoveExtensionKeybinding(
53 const Extension* extension,
54 const std::string& command_name) {
55 EventTargets::iterator it = event_targets_.begin();
56 while (it != event_targets_.end()) {
57 TargetList& target_list = it->second;
58 TargetList::iterator target = target_list.begin();
59 while (target != target_list.end()) {
60 if (target->first == extension->id() &&
61 (command_name.empty() || command_name == target->second))
62 target = target_list.erase(target);
63 else
64 target++;
67 EventTargets::iterator old = it++;
68 if (target_list.empty()) {
69 // Let each platform-specific implementation get a chance to clean up.
70 RemoveExtensionKeybindingImpl(old->first, command_name);
71 event_targets_.erase(old);
73 // If a specific command_name was requested, it has now been deleted so no
74 // further work is required.
75 if (!command_name.empty())
76 break;
81 void ExtensionKeybindingRegistry::Init() {
82 ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
83 if (!registry)
84 return; // ExtensionRegistry can be null during testing.
86 for (const scoped_refptr<const extensions::Extension>& extension :
87 registry->enabled_extensions())
88 if (ExtensionMatchesFilter(extension.get()))
89 AddExtensionKeybindings(extension.get(), std::string());
92 bool ExtensionKeybindingRegistry::ShouldIgnoreCommand(
93 const std::string& command) const {
94 return command == manifest_values::kPageActionCommandEvent ||
95 command == manifest_values::kBrowserActionCommandEvent;
98 bool ExtensionKeybindingRegistry::NotifyEventTargets(
99 const ui::Accelerator& accelerator) {
100 return ExecuteCommands(accelerator, std::string());
103 void ExtensionKeybindingRegistry::CommandExecuted(
104 const std::string& extension_id, const std::string& command) {
105 const Extension* extension = ExtensionRegistry::Get(browser_context_)
106 ->enabled_extensions()
107 .GetByID(extension_id);
108 if (!extension)
109 return;
111 // Grant before sending the event so that the permission is granted before
112 // the extension acts on the command. NOTE: The Global Commands handler does
113 // not set the delegate as it deals only with named commands (not page/browser
114 // actions that are associated with the current page directly).
115 ActiveTabPermissionGranter* granter =
116 delegate_ ? delegate_->GetActiveTabPermissionGranter() : NULL;
117 if (granter)
118 granter->GrantIfRequested(extension);
120 scoped_ptr<base::ListValue> args(new base::ListValue());
121 args->Append(new base::StringValue(command));
123 scoped_ptr<Event> event(
124 new Event(events::COMMANDS_ON_COMMAND, kOnCommandEventName, args.Pass()));
125 event->restrict_to_browser_context = browser_context_;
126 event->user_gesture = EventRouter::USER_GESTURE_ENABLED;
127 EventRouter::Get(browser_context_)
128 ->DispatchEventToExtension(extension_id, event.Pass());
131 bool ExtensionKeybindingRegistry::IsAcceleratorRegistered(
132 const ui::Accelerator& accelerator) const {
133 return event_targets_.find(accelerator) != event_targets_.end();
136 void ExtensionKeybindingRegistry::AddEventTarget(
137 const ui::Accelerator& accelerator,
138 const std::string& extension_id,
139 const std::string& command_name) {
140 event_targets_[accelerator].push_back(
141 std::make_pair(extension_id, command_name));
142 // Shortcuts except media keys have only one target in the list. See comment
143 // about |event_targets_|.
144 if (!extensions::Command::IsMediaKey(accelerator))
145 DCHECK_EQ(1u, event_targets_[accelerator].size());
148 bool ExtensionKeybindingRegistry::GetFirstTarget(
149 const ui::Accelerator& accelerator,
150 std::string* extension_id,
151 std::string* command_name) const {
152 EventTargets::const_iterator targets = event_targets_.find(accelerator);
153 if (targets == event_targets_.end())
154 return false;
156 DCHECK(!targets->second.empty());
157 TargetList::const_iterator first_target = targets->second.begin();
158 *extension_id = first_target->first;
159 *command_name = first_target->second;
160 return true;
163 bool ExtensionKeybindingRegistry::IsEventTargetsEmpty() const {
164 return event_targets_.empty();
167 void ExtensionKeybindingRegistry::ExecuteCommand(
168 const std::string& extension_id,
169 const ui::Accelerator& accelerator) {
170 ExecuteCommands(accelerator, extension_id);
173 void ExtensionKeybindingRegistry::OnExtensionLoaded(
174 content::BrowserContext* browser_context,
175 const Extension* extension) {
176 if (ExtensionMatchesFilter(extension))
177 AddExtensionKeybindings(extension, std::string());
180 void ExtensionKeybindingRegistry::OnExtensionUnloaded(
181 content::BrowserContext* browser_context,
182 const Extension* extension,
183 UnloadedExtensionInfo::Reason reason) {
184 if (ExtensionMatchesFilter(extension))
185 RemoveExtensionKeybinding(extension, std::string());
188 void ExtensionKeybindingRegistry::Observe(
189 int type,
190 const content::NotificationSource& source,
191 const content::NotificationDetails& details) {
192 switch (type) {
193 case extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED:
194 case extensions::NOTIFICATION_EXTENSION_COMMAND_REMOVED: {
195 ExtensionCommandRemovedDetails* payload =
196 content::Details<ExtensionCommandRemovedDetails>(details).ptr();
198 const Extension* extension = ExtensionRegistry::Get(browser_context_)
199 ->enabled_extensions()
200 .GetByID(payload->extension_id);
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;
206 if (ExtensionMatchesFilter(extension)) {
207 if (type == extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED) {
208 // Component extensions triggers OnExtensionLoaded for extension
209 // installs as well as loads. This can cause adding of multiple key
210 // targets.
211 if (extension->location() == Manifest::COMPONENT)
212 return;
214 AddExtensionKeybindings(extension, payload->command_name);
215 } else {
216 RemoveExtensionKeybinding(extension, payload->command_name);
219 break;
221 default:
222 NOTREACHED();
223 break;
227 bool ExtensionKeybindingRegistry::ExtensionMatchesFilter(
228 const extensions::Extension* extension)
230 switch (extension_filter_) {
231 case ALL_EXTENSIONS:
232 return true;
233 case PLATFORM_APPS_ONLY:
234 return extension->is_platform_app();
235 default:
236 NOTREACHED();
238 return false;
241 bool ExtensionKeybindingRegistry::ExecuteCommands(
242 const ui::Accelerator& accelerator,
243 const std::string& extension_id) {
244 EventTargets::iterator targets = event_targets_.find(accelerator);
245 if (targets == event_targets_.end() || targets->second.empty())
246 return false;
248 bool executed = false;
249 for (TargetList::const_iterator it = targets->second.begin();
250 it != targets->second.end(); it++) {
251 if (!extensions::EventRouter::Get(browser_context_)
252 ->ExtensionHasEventListener(it->first, kOnCommandEventName))
253 continue;
255 if (extension_id.empty() || it->first == extension_id) {
256 CommandExecuted(it->first, it->second);
257 executed = true;
261 return executed;
264 } // namespace extensions