Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / extension_keybinding_registry.cc
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.
4
5 #include "chrome/browser/extensions/extension_keybinding_registry.h"
6
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"
19
20 namespace {
21 const char kOnCommandEventName[] = "commands.onCommand";
22 }  // namespace
23
24 namespace extensions {
25
26 ExtensionKeybindingRegistry::ExtensionKeybindingRegistry(
27     content::BrowserContext* context,
28     ExtensionFilter extension_filter,
29     Delegate* delegate)
30     : browser_context_(context),
31       extension_filter_(extension_filter),
32       delegate_(delegate),
33       extension_registry_observer_(this) {
34   extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
35
36   Profile* profile = Profile::FromBrowserContext(browser_context_);
37   registrar_.Add(this,
38                  extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED,
39                  content::Source<Profile>(profile->GetOriginalProfile()));
40   registrar_.Add(this,
41                  extensions::NOTIFICATION_EXTENSION_COMMAND_REMOVED,
42                  content::Source<Profile>(profile->GetOriginalProfile()));
43 }
44
45 ExtensionKeybindingRegistry::~ExtensionKeybindingRegistry() {
46 }
47
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);
59       else
60         target++;
61     }
62
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);
68
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())
72         break;
73     }
74   }
75 }
76
77 void ExtensionKeybindingRegistry::Init() {
78   ExtensionService* service =
79       ExtensionSystem::Get(browser_context_)->extension_service();
80   if (!service)
81     return;  // ExtensionService can be null during testing.
82
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());
88 }
89
90 bool ExtensionKeybindingRegistry::ShouldIgnoreCommand(
91     const std::string& command) const {
92   return command == manifest_values::kPageActionCommandEvent ||
93          command == manifest_values::kBrowserActionCommandEvent;
94 }
95
96 bool ExtensionKeybindingRegistry::NotifyEventTargets(
97     const ui::Accelerator& accelerator) {
98   return ExecuteCommands(accelerator, std::string());
99 }
100
101 void ExtensionKeybindingRegistry::CommandExecuted(
102     const std::string& extension_id, const std::string& command) {
103   ExtensionService* service =
104       ExtensionSystem::Get(browser_context_)->extension_service();
105
106   const Extension* extension = service->extensions()->GetByID(extension_id);
107   if (!extension)
108     return;
109
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;
116   if (granter)
117     granter->GrantIfRequested(extension);
118
119   scoped_ptr<base::ListValue> args(new base::ListValue());
120   args->Append(new base::StringValue(command));
121
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());
127 }
128
129 bool ExtensionKeybindingRegistry::IsAcceleratorRegistered(
130     const ui::Accelerator& accelerator) const {
131   return event_targets_.find(accelerator) != event_targets_.end();
132 }
133
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());
144 }
145
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())
152     return false;
153
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;
158   return true;
159 }
160
161 bool ExtensionKeybindingRegistry::IsEventTargetsEmpty() const {
162   return event_targets_.empty();
163 }
164
165 void ExtensionKeybindingRegistry::ExecuteCommand(
166     const std::string& extension_id,
167     const ui::Accelerator& accelerator) {
168   ExecuteCommands(accelerator, extension_id);
169 }
170
171 void ExtensionKeybindingRegistry::OnExtensionLoaded(
172     content::BrowserContext* browser_context,
173     const Extension* extension) {
174   if (ExtensionMatchesFilter(extension))
175     AddExtensionKeybinding(extension, std::string());
176 }
177
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());
184 }
185
186 void ExtensionKeybindingRegistry::Observe(
187     int type,
188     const content::NotificationSource& source,
189     const content::NotificationDetails& details) {
190   switch (type) {
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> >(
195               details).ptr();
196
197       const Extension* extension = ExtensionSystem::Get(browser_context_)
198                                        ->extension_service()
199                                        ->extensions()
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.
203       if (!extension)
204         return;
205
206       if (ExtensionMatchesFilter(extension)) {
207         if (type == extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED)
208           AddExtensionKeybinding(extension, payload->second);
209         else
210           RemoveExtensionKeybinding(extension, payload->second);
211       }
212       break;
213     }
214     default:
215       NOTREACHED();
216       break;
217   }
218 }
219
220 bool ExtensionKeybindingRegistry::ExtensionMatchesFilter(
221     const extensions::Extension* extension)
222 {
223   switch (extension_filter_) {
224     case ALL_EXTENSIONS:
225       return true;
226     case PLATFORM_APPS_ONLY:
227       return extension->is_platform_app();
228     default:
229       NOTREACHED();
230   }
231   return false;
232 }
233
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())
239     return false;
240
241   bool executed = false;
242   for (TargetList::const_iterator it = targets->second.begin();
243        it != targets->second.end(); it++) {
244     if (!extensions::EventRouter::Get(browser_context_)
245         ->ExtensionHasEventListener(it->first, kOnCommandEventName))
246       continue;
247
248     if (extension_id.empty() || it->first == extension_id) {
249       CommandExecuted(it->first, it->second);
250       executed = true;
251     }
252   }
253
254   return executed;
255 }
256
257 }  // namespace extensions