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/chrome_notification_types.h"
9 #include "chrome/browser/extensions/active_tab_permission_granter.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/common/extensions/command.h"
13 #include "content/public/browser/browser_context.h"
14 #include "extensions/browser/event_router.h"
15 #include "extensions/browser/extension_registry.h"
16 #include "extensions/browser/extension_system.h"
17 #include "extensions/common/extension_set.h"
18 #include "extensions/common/manifest_constants.h"
20 namespace extensions
{
22 ExtensionKeybindingRegistry::ExtensionKeybindingRegistry(
23 content::BrowserContext
* context
,
24 ExtensionFilter extension_filter
,
26 : browser_context_(context
),
27 extension_filter_(extension_filter
),
29 extension_registry_observer_(this) {
30 extension_registry_observer_
.Add(ExtensionRegistry::Get(browser_context_
));
32 Profile
* profile
= Profile::FromBrowserContext(browser_context_
);
34 chrome::NOTIFICATION_EXTENSION_COMMAND_ADDED
,
35 content::Source
<Profile
>(profile
->GetOriginalProfile()));
37 chrome::NOTIFICATION_EXTENSION_COMMAND_REMOVED
,
38 content::Source
<Profile
>(profile
->GetOriginalProfile()));
41 ExtensionKeybindingRegistry::~ExtensionKeybindingRegistry() {
44 void ExtensionKeybindingRegistry::RemoveExtensionKeybinding(
45 const Extension
* extension
,
46 const std::string
& command_name
) {
47 EventTargets::iterator it
= event_targets_
.begin();
48 while (it
!= event_targets_
.end()) {
49 TargetList
& target_list
= it
->second
;
50 TargetList::iterator target
= target_list
.begin();
51 while (target
!= target_list
.end()) {
52 if (target
->first
== extension
->id() &&
53 (command_name
.empty() || command_name
== target
->second
))
54 target
= target_list
.erase(target
);
59 EventTargets::iterator old
= it
++;
60 if (target_list
.empty()) {
61 // Let each platform-specific implementation get a chance to clean up.
62 RemoveExtensionKeybindingImpl(old
->first
, command_name
);
63 event_targets_
.erase(old
);
65 // If a specific command_name was requested, it has now been deleted so no
66 // further work is required.
67 if (!command_name
.empty())
73 void ExtensionKeybindingRegistry::Init() {
74 ExtensionService
* service
=
75 ExtensionSystem::Get(browser_context_
)->extension_service();
77 return; // ExtensionService can be null during testing.
79 const ExtensionSet
* extensions
= service
->extensions();
80 ExtensionSet::const_iterator iter
= extensions
->begin();
81 for (; iter
!= extensions
->end(); ++iter
)
82 if (ExtensionMatchesFilter(iter
->get()))
83 AddExtensionKeybinding(iter
->get(), std::string());
86 bool ExtensionKeybindingRegistry::ShouldIgnoreCommand(
87 const std::string
& command
) const {
88 return command
== manifest_values::kPageActionCommandEvent
||
89 command
== manifest_values::kBrowserActionCommandEvent
;
92 bool ExtensionKeybindingRegistry::NotifyEventTargets(
93 const ui::Accelerator
& accelerator
) {
94 return ExecuteCommands(accelerator
, std::string());
97 void ExtensionKeybindingRegistry::CommandExecuted(
98 const std::string
& extension_id
, const std::string
& command
) {
99 ExtensionService
* service
=
100 ExtensionSystem::Get(browser_context_
)->extension_service();
102 const Extension
* extension
= service
->extensions()->GetByID(extension_id
);
106 // Grant before sending the event so that the permission is granted before
107 // the extension acts on the command. NOTE: The Global Commands handler does
108 // not set the delegate as it deals only with named commands (not page/browser
109 // actions that are associated with the current page directly).
110 ActiveTabPermissionGranter
* granter
=
111 delegate_
? delegate_
->GetActiveTabPermissionGranter() : NULL
;
113 granter
->GrantIfRequested(extension
);
115 scoped_ptr
<base::ListValue
> args(new base::ListValue());
116 args
->Append(new base::StringValue(command
));
118 scoped_ptr
<Event
> event(new Event("commands.onCommand", args
.Pass()));
119 event
->restrict_to_browser_context
= browser_context_
;
120 event
->user_gesture
= EventRouter::USER_GESTURE_ENABLED
;
121 EventRouter::Get(browser_context_
)
122 ->DispatchEventToExtension(extension_id
, event
.Pass());
125 bool ExtensionKeybindingRegistry::IsAcceleratorRegistered(
126 const ui::Accelerator
& accelerator
) const {
127 return event_targets_
.find(accelerator
) != event_targets_
.end();
130 void ExtensionKeybindingRegistry::AddEventTarget(
131 const ui::Accelerator
& accelerator
,
132 const std::string
& extension_id
,
133 const std::string
& command_name
) {
134 event_targets_
[accelerator
].push_back(
135 std::make_pair(extension_id
, command_name
));
136 // Shortcuts except media keys have only one target in the list. See comment
137 // about |event_targets_|.
138 if (!extensions::Command::IsMediaKey(accelerator
))
139 DCHECK_EQ(1u, event_targets_
[accelerator
].size());
142 bool ExtensionKeybindingRegistry::GetFirstTarget(
143 const ui::Accelerator
& accelerator
,
144 std::string
* extension_id
,
145 std::string
* command_name
) const {
146 EventTargets::const_iterator targets
= event_targets_
.find(accelerator
);
147 if (targets
== event_targets_
.end())
150 DCHECK(!targets
->second
.empty());
151 TargetList::const_iterator first_target
= targets
->second
.begin();
152 *extension_id
= first_target
->first
;
153 *command_name
= first_target
->second
;
157 bool ExtensionKeybindingRegistry::IsEventTargetsEmpty() const {
158 return event_targets_
.empty();
161 void ExtensionKeybindingRegistry::ExecuteCommand(
162 const std::string
& extension_id
,
163 const ui::Accelerator
& accelerator
) {
164 ExecuteCommands(accelerator
, extension_id
);
167 void ExtensionKeybindingRegistry::OnExtensionLoaded(
168 content::BrowserContext
* browser_context
,
169 const Extension
* extension
) {
170 if (ExtensionMatchesFilter(extension
))
171 AddExtensionKeybinding(extension
, std::string());
174 void ExtensionKeybindingRegistry::OnExtensionUnloaded(
175 content::BrowserContext
* browser_context
,
176 const Extension
* extension
,
177 UnloadedExtensionInfo::Reason reason
) {
178 if (ExtensionMatchesFilter(extension
))
179 RemoveExtensionKeybinding(extension
, std::string());
182 void ExtensionKeybindingRegistry::Observe(
184 const content::NotificationSource
& source
,
185 const content::NotificationDetails
& details
) {
187 case chrome::NOTIFICATION_EXTENSION_COMMAND_ADDED
:
188 case chrome::NOTIFICATION_EXTENSION_COMMAND_REMOVED
: {
189 std::pair
<const std::string
, const std::string
>* payload
=
190 content::Details
<std::pair
<const std::string
, const std::string
> >(
193 const Extension
* extension
= ExtensionSystem::Get(browser_context_
)
194 ->extension_service()
196 ->GetByID(payload
->first
);
197 // During install and uninstall the extension won't be found. We'll catch
198 // those events above, with the LOADED/UNLOADED, so we ignore this event.
202 if (ExtensionMatchesFilter(extension
)) {
203 if (type
== chrome::NOTIFICATION_EXTENSION_COMMAND_ADDED
)
204 AddExtensionKeybinding(extension
, payload
->second
);
206 RemoveExtensionKeybinding(extension
, payload
->second
);
216 bool ExtensionKeybindingRegistry::ExtensionMatchesFilter(
217 const extensions::Extension
* extension
)
219 switch (extension_filter_
) {
222 case PLATFORM_APPS_ONLY
:
223 return extension
->is_platform_app();
230 bool ExtensionKeybindingRegistry::ExecuteCommands(
231 const ui::Accelerator
& accelerator
,
232 const std::string
& extension_id
) {
233 EventTargets::iterator targets
= event_targets_
.find(accelerator
);
234 if (targets
== event_targets_
.end() || targets
->second
.empty())
237 bool executed
= false;
238 for (TargetList::const_iterator it
= targets
->second
.begin();
239 it
!= targets
->second
.end(); it
++) {
240 if (extension_id
.empty() || it
->first
== extension_id
) {
241 CommandExecuted(it
->first
, it
->second
);
249 } // namespace extensions