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/api/commands/command_service.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/extension_system.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "extensions/browser/event_router.h"
15 #include "extensions/common/extension_set.h"
16 #include "extensions/common/manifest_constants.h"
18 namespace extensions
{
20 ExtensionKeybindingRegistry::ExtensionKeybindingRegistry(
21 Profile
* profile
, ExtensionFilter extension_filter
, Delegate
* delegate
)
23 extension_filter_(extension_filter
),
25 registrar_
.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED
,
26 content::Source
<Profile
>(profile
->GetOriginalProfile()));
27 registrar_
.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED
,
28 content::Source
<Profile
>(profile
->GetOriginalProfile()));
29 registrar_
.Add(this, chrome::NOTIFICATION_EXTENSION_COMMAND_ADDED
,
30 content::Source
<Profile
>(profile
->GetOriginalProfile()));
31 registrar_
.Add(this, chrome::NOTIFICATION_EXTENSION_COMMAND_REMOVED
,
32 content::Source
<Profile
>(profile
->GetOriginalProfile()));
35 ExtensionKeybindingRegistry::~ExtensionKeybindingRegistry() {
38 void ExtensionKeybindingRegistry::RemoveExtensionKeybinding(
39 const Extension
* extension
,
40 const std::string
& command_name
) {
41 EventTargets::iterator it
= event_targets_
.begin();
42 while (it
!= event_targets_
.end()) {
43 TargetList
& target_list
= it
->second
;
44 TargetList::iterator target
= target_list
.begin();
45 while (target
!= target_list
.end()) {
46 if (target
->first
== extension
->id() &&
47 (command_name
.empty() || command_name
== target
->second
))
48 target
= target_list
.erase(target
);
53 EventTargets::iterator old
= it
++;
54 if (target_list
.empty()) {
55 // Let each platform-specific implementation get a chance to clean up.
56 RemoveExtensionKeybindingImpl(old
->first
, command_name
);
57 event_targets_
.erase(old
);
59 // If a specific command_name was requested, it has now been deleted so no
60 // further work is required.
61 if (!command_name
.empty())
67 void ExtensionKeybindingRegistry::Init() {
68 ExtensionService
* service
=
69 extensions::ExtensionSystem::Get(profile_
)->extension_service();
71 return; // ExtensionService can be null during testing.
73 const ExtensionSet
* extensions
= service
->extensions();
74 ExtensionSet::const_iterator iter
= extensions
->begin();
75 for (; iter
!= extensions
->end(); ++iter
)
76 if (ExtensionMatchesFilter(iter
->get()))
77 AddExtensionKeybinding(iter
->get(), std::string());
80 bool ExtensionKeybindingRegistry::ShouldIgnoreCommand(
81 const std::string
& command
) const {
82 return command
== manifest_values::kPageActionCommandEvent
||
83 command
== manifest_values::kBrowserActionCommandEvent
||
84 command
== manifest_values::kScriptBadgeCommandEvent
;
87 bool ExtensionKeybindingRegistry::NotifyEventTargets(
88 const ui::Accelerator
& accelerator
) {
89 EventTargets::iterator targets
= event_targets_
.find(accelerator
);
90 if (targets
== event_targets_
.end() || targets
->second
.empty())
93 for (TargetList::const_iterator it
= targets
->second
.begin();
94 it
!= targets
->second
.end(); it
++)
95 CommandExecuted(it
->first
, it
->second
);
100 void ExtensionKeybindingRegistry::CommandExecuted(
101 const std::string
& extension_id
, const std::string
& command
) {
102 ExtensionService
* service
=
103 ExtensionSystem::Get(profile_
)->extension_service();
105 const Extension
* extension
= service
->extensions()->GetByID(extension_id
);
109 // Grant before sending the event so that the permission is granted before
110 // the extension acts on the command. NOTE: The Global Commands handler does
111 // not set the delegate as it deals only with named commands (not page/browser
112 // actions that are associated with the current page directly).
113 ActiveTabPermissionGranter
* granter
=
114 delegate_
? delegate_
->GetActiveTabPermissionGranter() : NULL
;
116 granter
->GrantIfRequested(extension
);
118 scoped_ptr
<base::ListValue
> args(new base::ListValue());
119 args
->Append(new base::StringValue(command
));
121 scoped_ptr
<Event
> event(new Event("commands.onCommand", args
.Pass()));
122 event
->restrict_to_browser_context
= profile_
;
123 event
->user_gesture
= EventRouter::USER_GESTURE_ENABLED
;
124 ExtensionSystem::Get(profile_
)->event_router()->
125 DispatchEventToExtension(extension_id
, event
.Pass());
128 void ExtensionKeybindingRegistry::Observe(
130 const content::NotificationSource
& source
,
131 const content::NotificationDetails
& details
) {
133 case chrome::NOTIFICATION_EXTENSION_LOADED
: {
134 const extensions::Extension
* extension
=
135 content::Details
<const extensions::Extension
>(details
).ptr();
136 if (ExtensionMatchesFilter(extension
))
137 AddExtensionKeybinding(extension
, std::string());
140 case chrome::NOTIFICATION_EXTENSION_UNLOADED
: {
141 const extensions::Extension
* extension
=
142 content::Details
<UnloadedExtensionInfo
>(details
)->extension
;
143 if (ExtensionMatchesFilter(extension
))
144 RemoveExtensionKeybinding(extension
, std::string());
147 case chrome::NOTIFICATION_EXTENSION_COMMAND_ADDED
:
148 case chrome::NOTIFICATION_EXTENSION_COMMAND_REMOVED
: {
149 std::pair
<const std::string
, const std::string
>* payload
=
150 content::Details
<std::pair
<const std::string
, const std::string
> >(
153 const extensions::Extension
* extension
=
154 ExtensionSystem::Get(profile_
)->extension_service()->
155 extensions()->GetByID(payload
->first
);
156 // During install and uninstall the extension won't be found. We'll catch
157 // those events above, with the LOADED/UNLOADED, so we ignore this event.
161 if (ExtensionMatchesFilter(extension
)) {
162 if (type
== chrome::NOTIFICATION_EXTENSION_COMMAND_ADDED
)
163 AddExtensionKeybinding(extension
, payload
->second
);
165 RemoveExtensionKeybinding(extension
, payload
->second
);
175 bool ExtensionKeybindingRegistry::ExtensionMatchesFilter(
176 const extensions::Extension
* extension
)
178 switch (extension_filter_
) {
181 case PLATFORM_APPS_ONLY
:
182 return extension
->is_platform_app();
189 } // namespace extensions