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#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_KEYBINDING_REGISTRY_H_
6#define CHROME_BROWSER_EXTENSIONS_EXTENSION_KEYBINDING_REGISTRY_H_
7
8#include <list>
9#include <map>
10#include <string>
11
12#include "base/compiler_specific.h"
13#include "base/scoped_observer.h"
14#include "content/public/browser/notification_details.h"
15#include "content/public/browser/notification_observer.h"
16#include "content/public/browser/notification_registrar.h"
17#include "content/public/browser/notification_source.h"
18#include "extensions/browser/extension_registry_observer.h"
19
20namespace content {
21class BrowserContext;
22}
23
24namespace ui {
25class Accelerator;
26}
27
28namespace extensions {
29
30class ActiveTabPermissionGranter;
31class Extension;
32class ExtensionRegistry;
33
34// The ExtensionKeybindingRegistry is a class that handles the cross-platform
35// logic for keyboard accelerators. See platform-specific implementations for
36// implementation details for each platform.
37class ExtensionKeybindingRegistry : public content::NotificationObserver,
38                                    public ExtensionRegistryObserver {
39 public:
40  enum ExtensionFilter {
41    ALL_EXTENSIONS,
42    PLATFORM_APPS_ONLY
43  };
44
45  class Delegate {
46   public:
47    // Gets the ActiveTabPermissionGranter for the active tab, if any.
48    // If there is no active tab then returns NULL.
49    virtual ActiveTabPermissionGranter* GetActiveTabPermissionGranter() = 0;
50  };
51
52  // If |extension_filter| is not ALL_EXTENSIONS, only keybindings by
53  // by extensions that match the filter will be registered.
54  ExtensionKeybindingRegistry(content::BrowserContext* context,
55                              ExtensionFilter extension_filter,
56                              Delegate* delegate);
57
58  virtual ~ExtensionKeybindingRegistry();
59
60  // Enables/Disables general shortcut handling in Chrome. Implemented in
61  // platform-specific ExtensionKeybindingsRegistry* files.
62  static void SetShortcutHandlingSuspended(bool suspended);
63
64  // Execute the command bound to |accelerator| and provided by the extension
65  // with |extension_id|, if it exists.
66  void ExecuteCommand(const std::string& extension_id,
67                      const ui::Accelerator& accelerator);
68
69  // Check whether the specified |accelerator| has been registered.
70  bool IsAcceleratorRegistered(const ui::Accelerator& accelerator) const;
71
72 protected:
73  // Add extension keybinding for the events defined by the |extension|.
74  // |command_name| is optional, but if not blank then only the command
75  // specified will be added.
76  virtual void AddExtensionKeybinding(
77      const Extension* extension,
78      const std::string& command_name) = 0;
79  // Remove extension bindings for |extension|. |command_name| is optional,
80  // but if not blank then only the command specified will be removed.
81  void RemoveExtensionKeybinding(
82      const Extension* extension,
83      const std::string& command_name);
84  // Overridden by platform specific implementations to provide additional
85  // unregistration (which varies between platforms).
86  virtual void RemoveExtensionKeybindingImpl(
87      const ui::Accelerator& accelerator,
88      const std::string& command_name) = 0;
89
90  // Make sure all extensions registered have keybindings added.
91  void Init();
92
93  // Whether to ignore this command. Only browserAction commands and pageAction
94  // commands are currently ignored, since they are handled elsewhere.
95  bool ShouldIgnoreCommand(const std::string& command) const;
96
97  // Fire event targets which the specified |accelerator| is binding with.
98  // Returns true if we can find the appropriate event targets.
99  bool NotifyEventTargets(const ui::Accelerator& accelerator);
100
101  // Notifies appropriate parties that a command has been executed.
102  void CommandExecuted(const std::string& extension_id,
103                       const std::string& command);
104
105  // Add event target (extension_id, command name) to the target list of
106  // |accelerator|. Note that only media keys can have more than one event
107  // target.
108  void AddEventTarget(const ui::Accelerator& accelerator,
109                      const std::string& extension_id,
110                      const std::string& command_name);
111
112  // Get the first event target by the given |accelerator|. For a valid
113  // accelerator it should have only one event target, except for media keys.
114  // Returns true if we can find it, |extension_id| and |command_name| will be
115  // set to the right target; otherwise, false is returned and |extension_id|,
116  // |command_name| are unchanged.
117  bool GetFirstTarget(const ui::Accelerator& accelerator,
118                      std::string* extension_id,
119                      std::string* command_name) const;
120
121  // Returns true if the |event_targets_| is empty; otherwise returns false.
122  bool IsEventTargetsEmpty() const;
123
124  // Returns the BrowserContext for this registry.
125  content::BrowserContext* browser_context() const { return browser_context_; }
126
127 private:
128  // Overridden from content::NotificationObserver:
129  virtual void Observe(int type,
130                       const content::NotificationSource& source,
131                       const content::NotificationDetails& details) OVERRIDE;
132
133  // ExtensionRegistryObserver implementation.
134  virtual void OnExtensionLoaded(content::BrowserContext* browser_context,
135                                 const Extension* extension) OVERRIDE;
136  virtual void OnExtensionUnloaded(
137      content::BrowserContext* browser_context,
138      const Extension* extension,
139      UnloadedExtensionInfo::Reason reason) OVERRIDE;
140
141  // Returns true if the |extension| matches our extension filter.
142  bool ExtensionMatchesFilter(const extensions::Extension* extension);
143
144  // Execute commands for |accelerator|. If |extension_id| is empty, execute all
145  // commands bound to |accelerator|, otherwise execute only commands bound by
146  // the corresponding extension. Returns true if at least one command was
147  // executed.
148  bool ExecuteCommands(const ui::Accelerator& accelerator,
149                       const std::string& extension_id);
150
151  // The content notification registrar for listening to extension events.
152  content::NotificationRegistrar registrar_;
153
154  content::BrowserContext* browser_context_;
155
156  // What extensions to register keybindings for.
157  ExtensionFilter extension_filter_;
158
159  // Weak pointer to our delegate. Not owned by us. Must outlive this class.
160  Delegate* delegate_;
161
162  // Maps an accelerator to a list of string pairs (extension id, command name)
163  // for commands that have been registered. This keeps track of the targets for
164  // the keybinding event (which named command to call in which extension). On
165  // GTK this map contains registration for pageAction and browserAction
166  // commands, whereas on other platforms it does not. Note that normal
167  // accelerator (which isn't media keys) has only one target, while the media
168  // keys can have more than one.
169  typedef std::list<std::pair<std::string, std::string> > TargetList;
170  typedef std::map<ui::Accelerator, TargetList> EventTargets;
171  EventTargets event_targets_;
172
173  // Listen to extension load, unloaded notifications.
174  ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
175      extension_registry_observer_;
176
177  DISALLOW_COPY_AND_ASSIGN(ExtensionKeybindingRegistry);
178};
179
180}  // namespace extensions
181
182#endif  // CHROME_BROWSER_EXTENSIONS_EXTENSION_KEYBINDING_REGISTRY_H_
183