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/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"
21 const char kOnCommandEventName
[] = "commands.onCommand";
24 namespace extensions
{
26 ExtensionKeybindingRegistry::ExtensionKeybindingRegistry(
27 content::BrowserContext
* context
,
28 ExtensionFilter extension_filter
,
30 : browser_context_(context
),
31 extension_filter_(extension_filter
),
33 extension_registry_observer_(this) {
34 extension_registry_observer_
.Add(ExtensionRegistry::Get(browser_context_
));
36 Profile
* profile
= Profile::FromBrowserContext(browser_context_
);
38 extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED
,
39 content::Source
<Profile
>(profile
->GetOriginalProfile()));
41 extensions::NOTIFICATION_EXTENSION_COMMAND_REMOVED
,
42 content::Source
<Profile
>(profile
->GetOriginalProfile()));
45 ExtensionKeybindingRegistry::~ExtensionKeybindingRegistry() {
48 void 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
);
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
);
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())
77 void ExtensionKeybindingRegistry::Init() {
78 ExtensionService
* service
=
79 ExtensionSystem::Get(browser_context_
)->extension_service();
81 return; // ExtensionService can be null during testing.
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());
90 bool ExtensionKeybindingRegistry::ShouldIgnoreCommand(
91 const std::string
& command
) const {
92 return command
== manifest_values::kPageActionCommandEvent
||
93 command
== manifest_values::kBrowserActionCommandEvent
;
96 bool ExtensionKeybindingRegistry::NotifyEventTargets(
97 const ui::Accelerator
& accelerator
) {
98 return ExecuteCommands(accelerator
, std::string());
101 void ExtensionKeybindingRegistry::CommandExecuted(
102 const std::string
& extension_id
, const std::string
& command
) {
103 ExtensionService
* service
=
104 ExtensionSystem::Get(browser_context_
)->extension_service();
106 const Extension
* extension
= service
->extensions()->GetByID(extension_id
);
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
;
117 granter
->GrantIfRequested(extension
);
119 scoped_ptr
<base::ListValue
> args(new base::ListValue());
120 args
->Append(new base::StringValue(command
));
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());
129 bool ExtensionKeybindingRegistry::IsAcceleratorRegistered(
130 const ui::Accelerator
& accelerator
) const {
131 return event_targets_
.find(accelerator
) != event_targets_
.end();
134 void 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());
146 bool 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())
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
;
161 bool ExtensionKeybindingRegistry::IsEventTargetsEmpty() const {
162 return event_targets_
.empty();
165 void ExtensionKeybindingRegistry::ExecuteCommand(
166 const std::string
& extension_id
,
167 const ui::Accelerator
& accelerator
) {
168 ExecuteCommands(accelerator
, extension_id
);
171 void ExtensionKeybindingRegistry::OnExtensionLoaded(
172 content::BrowserContext
* browser_context
,
173 const Extension
* extension
) {
174 if (ExtensionMatchesFilter(extension
))
175 AddExtensionKeybinding(extension
, std::string());
178 void ExtensionKeybindingRegistry::OnExtensionUnloaded(
179 content::BrowserContext
* browser_context
,
180 const Extension
* extension
,
181 UnloadedExtensionInfo::Reason reason
) {
182 if (ExtensionMatchesFilter(extension
))
183 RemoveExtensionKeybinding(extension
, std::string());
186 void ExtensionKeybindingRegistry::Observe(
188 const content::NotificationSource
& source
,
189 const content::NotificationDetails
& details
) {
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
> >(
197 const Extension
* extension
= ExtensionSystem::Get(browser_context_
)
198 ->extension_service()
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.
206 if (ExtensionMatchesFilter(extension
)) {
207 if (type
== extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED
)
208 AddExtensionKeybinding(extension
, payload
->second
);
210 RemoveExtensionKeybinding(extension
, payload
->second
);
220 bool ExtensionKeybindingRegistry::ExtensionMatchesFilter(
221 const extensions::Extension
* extension
)
223 switch (extension_filter_
) {
226 case PLATFORM_APPS_ONLY
:
227 return extension
->is_platform_app();
234 bool 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())
241 if (!extension_id
.empty() &&
242 !extensions::EventRouter::Get(browser_context_
)
243 ->ExtensionHasEventListener(extension_id
, kOnCommandEventName
))
246 bool executed
= false;
247 for (TargetList::const_iterator it
= targets
->second
.begin();
248 it
!= targets
->second
.end(); it
++) {
249 if (extension_id
.empty() || it
->first
== extension_id
) {
250 CommandExecuted(it
->first
, it
->second
);
258 } // namespace extensions