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"
19 const char kOnCommandEventName
[] = "commands.onCommand";
22 namespace extensions
{
24 ExtensionKeybindingRegistry::ExtensionKeybindingRegistry(
25 content::BrowserContext
* context
,
26 ExtensionFilter extension_filter
,
28 : browser_context_(context
),
29 extension_filter_(extension_filter
),
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_
);
37 extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED
,
38 content::Source
<Profile
>(profile
->GetOriginalProfile()));
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
);
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())
81 void ExtensionKeybindingRegistry::Init() {
82 ExtensionRegistry
* registry
= ExtensionRegistry::Get(browser_context_
);
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
);
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
;
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())
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
;
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(
190 const content::NotificationSource
& source
,
191 const content::NotificationDetails
& details
) {
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.
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
211 if (extension
->location() == Manifest::COMPONENT
)
214 AddExtensionKeybindings(extension
, payload
->command_name
);
216 RemoveExtensionKeybinding(extension
, payload
->command_name
);
227 bool ExtensionKeybindingRegistry::ExtensionMatchesFilter(
228 const extensions::Extension
* extension
)
230 switch (extension_filter_
) {
233 case PLATFORM_APPS_ONLY
:
234 return extension
->is_platform_app();
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())
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
))
255 if (extension_id
.empty() || it
->first
== extension_id
) {
256 CommandExecuted(it
->first
, it
->second
);
264 } // namespace extensions