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_API_COMMANDS_COMMAND_SERVICE_H_
6#define CHROME_BROWSER_EXTENSIONS_API_COMMANDS_COMMAND_SERVICE_H_
7
8#include <string>
9
10#include "base/basictypes.h"
11#include "base/scoped_observer.h"
12#include "chrome/common/extensions/command.h"
13#include "extensions/browser/browser_context_keyed_api_factory.h"
14#include "extensions/browser/extension_registry_observer.h"
15#include "extensions/common/extension.h"
16
17class Profile;
18
19namespace base {
20class DictionaryValue;
21}
22
23namespace content {
24class BrowserContext;
25}
26
27namespace ui {
28class Accelerator;
29}
30
31namespace user_prefs {
32class PrefRegistrySyncable;
33}
34
35namespace extensions {
36class ExtensionRegistry;
37
38// This service keeps track of preferences related to extension commands
39// (assigning initial keybindings on install and removing them on deletion
40// and answers questions related to which commands are active.
41class CommandService : public BrowserContextKeyedAPI,
42                       public ExtensionRegistryObserver {
43 public:
44  // An enum specifying whether to fetch all extension commands or only active
45  // ones.
46  enum QueryType {
47    ALL,
48    ACTIVE_ONLY,
49  };
50
51  // An enum specifying whether the command is global in scope or not. Global
52  // commands -- unlike regular commands -- have a global keyboard hook
53  // associated with them (and therefore work when Chrome doesn't have focus).
54  enum CommandScope {
55    REGULAR,    // Regular (non-globally scoped) command.
56    GLOBAL,     // Global command (works when Chrome doesn't have focus)
57    ANY_SCOPE,  // All commands, regardless of scope (used when querying).
58  };
59
60  // An enum specifying the types of commands that can be used by an extension.
61  enum ExtensionCommandType {
62    NAMED,
63    BROWSER_ACTION,
64    PAGE_ACTION
65  };
66
67  // Register prefs for keybinding.
68  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
69
70  // Constructs a CommandService object for the given profile.
71  explicit CommandService(content::BrowserContext* context);
72  virtual ~CommandService();
73
74  // BrowserContextKeyedAPI implementation.
75  static BrowserContextKeyedAPIFactory<CommandService>* GetFactoryInstance();
76
77  // Convenience method to get the CommandService for a profile.
78  static CommandService* Get(content::BrowserContext* context);
79
80  // Returns true if |extension| is permitted to and does remove the bookmark
81  // shortcut key.
82  static bool RemovesBookmarkShortcut(const Extension* extension);
83
84  // Returns true if |extension| is permitted to and does remove the bookmark
85  // open pages shortcut key.
86  static bool RemovesBookmarkOpenPagesShortcut(const Extension* extension);
87
88  // Gets the command (if any) for the browser action of an extension given
89  // its |extension_id|. The function consults the master list to see if
90  // the command is active. Returns false if the extension has no browser
91  // action. Returns false if the command is not active and |type| requested
92  // is ACTIVE_ONLY. |command| contains the command found and |active| (if not
93  // NULL) contains whether |command| is active.
94  bool GetBrowserActionCommand(const std::string& extension_id,
95                               QueryType type,
96                               Command* command,
97                               bool* active) const;
98
99  // Gets the command (if any) for the page action of an extension given
100  // its |extension_id|. The function consults the master list to see if
101  // the command is active. Returns false if the extension has no page
102  // action. Returns false if the command is not active and |type| requested
103  // is ACTIVE_ONLY. |command| contains the command found and |active| (if not
104  // NULL) contains whether |command| is active.
105  bool GetPageActionCommand(const std::string& extension_id,
106                            QueryType type,
107                            Command* command,
108                            bool* active) const;
109
110  // Gets the active named commands (if any) for the extension with
111  // |extension_id|. The function consults the master list to see if the
112  // commands are active. Returns an empty map if the extension has no named
113  // commands of the right |scope| or no such active named commands when |type|
114  // requested is ACTIVE_ONLY.
115  bool GetNamedCommands(const std::string& extension_id,
116                        QueryType type,
117                        CommandScope scope,
118                        CommandMap* command_map) const;
119
120  // Records a keybinding |accelerator| as active for an extension with id
121  // |extension_id| and command with the name |command_name|. If
122  // |allow_overrides| is false, the keybinding must be free for the change to
123  // be recorded (as determined by the master list in |user_prefs|). If
124  // |allow_overwrites| is true, any previously recorded keybinding for this
125  // |accelerator| will be overwritten. If |global| is true, the command will
126  // be registered as a global command (be active even when Chrome does not have
127  // focus. Returns true if the change was successfully recorded.
128  bool AddKeybindingPref(const ui::Accelerator& accelerator,
129                         std::string extension_id,
130                         std::string command_name,
131                         bool allow_overrides,
132                         bool global);
133
134  // Removes all keybindings for a given extension by its |extension_id|.
135  // |command_name| is optional and if specified, causes only the command with
136  // the name |command_name| to be removed.
137  void RemoveKeybindingPrefs(const std::string& extension_id,
138                             const std::string& command_name);
139
140  // Update the keybinding prefs (for a command with a matching |extension_id|
141  // and |command_name|) to |keystroke|. If the command had another key assigned
142  // that key assignment will be removed.
143  void UpdateKeybindingPrefs(const std::string& extension_id,
144                             const std::string& command_name,
145                             const std::string& keystroke);
146
147  // Set the scope of the keybinding. If |global| is true, the keybinding works
148  // even when Chrome does not have focus. If the scope requested is already
149  // set, the function returns false, otherwise true.
150  bool SetScope(const std::string& extension_id,
151                const std::string& command_name,
152                bool global);
153
154  // Finds the command with the name |command_name| within an extension with id
155  // |extension_id| . Returns an empty Command object (with keycode
156  // VKEY_UNKNOWN) if the command is not found.
157  Command FindCommandByName(const std::string& extension_id,
158                            const std::string& command) const;
159
160  // If the extension with |extension_id| binds a command to |accelerator|,
161  // returns true and assigns *|command| and *|command_type| to the command and
162  // its type if non-NULL.
163  bool GetBoundExtensionCommand(const std::string& extension_id,
164                                const ui::Accelerator& accelerator,
165                                Command* command,
166                                ExtensionCommandType* command_type) const;
167
168  // Returns true if |extension| is permitted to and does override the bookmark
169  // shortcut key.
170  bool OverridesBookmarkShortcut(const Extension* extension) const;
171
172 private:
173  friend class BrowserContextKeyedAPIFactory<CommandService>;
174
175  // BrowserContextKeyedAPI implementation.
176  static const char* service_name() {
177    return "CommandService";
178  }
179  static const bool kServiceRedirectedInIncognito = true;
180
181  // ExtensionRegistryObserver.
182  virtual void OnExtensionWillBeInstalled(
183      content::BrowserContext* browser_context,
184      const Extension* extension,
185      bool is_update,
186      bool from_ephemeral,
187      const std::string& old_name) OVERRIDE;
188  virtual void OnExtensionUninstalled(
189      content::BrowserContext* browser_context,
190      const Extension* extension,
191      extensions::UninstallReason reason) OVERRIDE;
192
193  // Updates keybindings for a given |extension|'s page action, browser action
194  // and named commands. Assigns new keybindings and removes relinquished
195  // keybindings if not changed by the user. In the case of adding keybindings,
196  // if the suggested keybinding is free, it will be taken by this extension. If
197  // not, the keybinding request is ignored.
198  void UpdateKeybindings(const Extension* extension);
199
200  // On update, removes keybindings that the extension previously suggested but
201  // now no longer does, as long as the user has not modified them.
202  void RemoveRelinquishedKeybindings(const Extension* extension);
203
204  // Assigns keybindings that the extension suggests, as long as they are not
205  // already assigned.
206  void AssignKeybindings(const Extension* extension);
207
208  // Checks if |extension| is permitted to automatically assign the
209  // |accelerator| key.
210  bool CanAutoAssign(const Command &command,
211                     const Extension* extension);
212
213  // Updates the record of |extension|'s most recent suggested command shortcut
214  // keys in the preferences.
215  void UpdateExtensionSuggestedCommandPrefs(const Extension* extension);
216
217  // Remove suggested key command prefs that apply to commands that have been
218  // removed.
219  void RemoveDefunctExtensionSuggestedCommandPrefs(const Extension* extension);
220
221  // Returns true if the user modified a command's shortcut key from the
222  // |extension|-suggested value.
223  bool IsCommandShortcutUserModified(const Extension* extension,
224                                     const std::string& command_name);
225
226  // Returns true if the extension is changing the binding of |command_name| on
227  // install.
228  bool IsKeybindingChanging(const Extension* extension,
229                            const std::string& command_name);
230
231  // Returns |extension|'s previous suggested key for |command_name| in the
232  // preferences, or the empty string if none.
233  std::string GetSuggestedKeyPref(const Extension* extension,
234                                  const std::string& command_name);
235
236  bool GetExtensionActionCommand(const std::string& extension_id,
237                                 QueryType query_type,
238                                 Command* command,
239                                 bool* active,
240                                 ExtensionCommandType action_type) const;
241
242  // A weak pointer to the profile we are associated with. Not owned by us.
243  Profile* profile_;
244
245  ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
246      extension_registry_observer_;
247
248  DISALLOW_COPY_AND_ASSIGN(CommandService);
249};
250
251template <>
252void
253    BrowserContextKeyedAPIFactory<CommandService>::DeclareFactoryDependencies();
254
255}  //  namespace extensions
256
257#endif  // CHROME_BROWSER_EXTENSIONS_API_COMMANDS_COMMAND_SERVICE_H_
258