|  | // Copyright 2012 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #ifndef CHROME_BROWSER_EXTENSIONS_COMMANDS_COMMAND_SERVICE_H_ | 
|  | #define CHROME_BROWSER_EXTENSIONS_COMMANDS_COMMAND_SERVICE_H_ | 
|  |  | 
|  | #include <string> | 
|  |  | 
|  | #include "base/memory/raw_ptr.h" | 
|  | #include "base/observer_list.h" | 
|  | #include "base/scoped_observation.h" | 
|  | #include "extensions/browser/browser_context_keyed_api_factory.h" | 
|  | #include "extensions/browser/extension_registry.h" | 
|  | #include "extensions/browser/extension_registry_observer.h" | 
|  | #include "extensions/buildflags/buildflags.h" | 
|  | #include "extensions/common/api/extension_action/action_info.h" | 
|  | #include "extensions/common/command.h" | 
|  | #include "extensions/common/extension.h" | 
|  | #include "extensions/common/extension_id.h" | 
|  | #include "ui/base/accelerators/command.h" | 
|  |  | 
|  | static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE)); | 
|  |  | 
|  | class Profile; | 
|  |  | 
|  | namespace content { | 
|  | class BrowserContext; | 
|  | } | 
|  |  | 
|  | namespace ui { | 
|  | class Accelerator; | 
|  | } | 
|  |  | 
|  | namespace user_prefs { | 
|  | class PrefRegistrySyncable; | 
|  | } | 
|  |  | 
|  | namespace extensions { | 
|  |  | 
|  | // This service keeps track of preferences related to extension commands | 
|  | // (assigning initial keybindings on install and removing them on deletion | 
|  | // and answers questions related to which commands are active. | 
|  | class CommandService : public BrowserContextKeyedAPI, | 
|  | public ExtensionRegistryObserver { | 
|  | public: | 
|  | // An enum specifying which extension commands to fetch. There are effectively | 
|  | // four options: all, active, suggested, and inactive. Only the first two | 
|  | // appear in the enum since there isn't currently a need for 'inactive' yet or | 
|  | // 'suggested'. | 
|  | // | 
|  | // 'inactive' means no key is bound. It might be because 1) a key wasn't | 
|  | // specified (suggested) or 2) it was not granted (key already taken). | 
|  | // | 
|  | // 'suggested' covers developer-assigned keys that may or may not have been | 
|  | // granted. Reasons for not granting include permission denied/key already | 
|  | // taken. | 
|  | // | 
|  | // ACTIVE means developer-assigned keys that were granted or user-assigned | 
|  | // keys. | 
|  | // | 
|  | // ACTIVE_OR_USER_MODIFIED means ACTIVE plus keys explicitly cleared by the | 
|  | // user. | 
|  | // | 
|  | // ALL is all of the above. | 
|  | enum QueryType { | 
|  | ALL, | 
|  | ACTIVE, | 
|  | ACTIVE_OR_USER_MODIFIED, | 
|  | }; | 
|  |  | 
|  | // An enum specifying whether the command is global in scope or not. Global | 
|  | // commands -- unlike regular commands -- have a global keyboard hook | 
|  | // associated with them (and therefore work when Chrome doesn't have focus). | 
|  | enum CommandScope { | 
|  | REGULAR,    // Regular (non-globally scoped) command. | 
|  | GLOBAL,     // Global command (works when Chrome doesn't have focus) | 
|  | ANY_SCOPE,  // All commands, regardless of scope (used when querying). | 
|  | }; | 
|  |  | 
|  | class Observer { | 
|  | public: | 
|  | // Called when an extension command is added. | 
|  | virtual void OnExtensionCommandAdded(const ExtensionId& extension_id, | 
|  | const Command& command) {} | 
|  |  | 
|  | // Called when an extension command is removed. | 
|  | virtual void OnExtensionCommandRemoved(const ExtensionId& extension_id, | 
|  | const Command& command) {} | 
|  |  | 
|  | // Called when the CommandService is being destroyed. | 
|  | virtual void OnCommandServiceDestroying() {} | 
|  |  | 
|  | protected: | 
|  | virtual ~Observer() = default; | 
|  | }; | 
|  |  | 
|  | // Register prefs for keybinding. | 
|  | static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); | 
|  |  | 
|  | // Constructs a CommandService object for the given profile. | 
|  | explicit CommandService(content::BrowserContext* context); | 
|  |  | 
|  | CommandService(const CommandService&) = delete; | 
|  | CommandService& operator=(const CommandService&) = delete; | 
|  |  | 
|  | ~CommandService() override; | 
|  |  | 
|  | // BrowserContextKeyedAPI implementation. | 
|  | static BrowserContextKeyedAPIFactory<CommandService>* GetFactoryInstance(); | 
|  |  | 
|  | // Convenience method to get the CommandService for a profile. | 
|  | static CommandService* Get(content::BrowserContext* context); | 
|  |  | 
|  | // Gets the command (if any) for the specified `action_type` of an extension | 
|  | // given its `extension_id`. The function consults the master list to see if | 
|  | // the command is active. Returns false if the command is not active and | 
|  | // `type` requested is ACTIVE. `command` contains the command found and | 
|  | // `active` (if not null) contains whether `command` is active. | 
|  | bool GetExtensionActionCommand(const ExtensionId& extension_id, | 
|  | ActionInfo::Type action_type, | 
|  | QueryType type, | 
|  | Command* command, | 
|  | bool* active) const; | 
|  |  | 
|  | // Gets the active named commands (if any) for the extension with | 
|  | // `extension_id`. The function consults the master list to see if the | 
|  | // commands are active. Returns an empty map if the extension has no named | 
|  | // commands of the right `scope` or no such active named commands when `type` | 
|  | // requested is ACTIVE. | 
|  | bool GetNamedCommands(const ExtensionId& extension_id, | 
|  | QueryType type, | 
|  | CommandScope scope, | 
|  | ui::CommandMap* command_map) const; | 
|  |  | 
|  | // Records a keybinding `accelerator` as active for an extension with id | 
|  | // `extension_id` and command with the name `command_name`. If | 
|  | // `allow_overrides` is false, the keybinding must be free for the change to | 
|  | // be recorded (as determined by the master list in `user_prefs`). If | 
|  | // `allow_overwrites` is true, any previously recorded keybinding for this | 
|  | // `accelerator` will be overwritten. If `global` is true, the command will | 
|  | // be registered as a global command (be active even when Chrome does not have | 
|  | // focus. Returns true if the change was successfully recorded. | 
|  | bool AddKeybindingPref(const ui::Accelerator& accelerator, | 
|  | const ExtensionId& extension_id, | 
|  | const std::string& command_name, | 
|  | bool allow_overrides, | 
|  | bool global); | 
|  |  | 
|  | // Removes all keybindings for a given extension by its `extension_id`. | 
|  | // `command_name` is optional and if specified, causes only the command with | 
|  | // the name `command_name` to be removed. | 
|  | void RemoveKeybindingPrefs(const ExtensionId& extension_id, | 
|  | const std::string& command_name); | 
|  |  | 
|  | // Update the keybinding prefs (for a command with a matching `extension_id` | 
|  | // and `command_name`) to `keystroke`. If the command had another key assigned | 
|  | // that key assignment will be removed. | 
|  | void UpdateKeybindingPrefs(const ExtensionId& extension_id, | 
|  | const std::string& command_name, | 
|  | const std::string& keystroke); | 
|  |  | 
|  | // Set the scope of the keybinding. If `global` is true, the keybinding works | 
|  | // even when Chrome does not have focus. If the scope requested is already | 
|  | // set, the function returns false, otherwise true. | 
|  | bool SetScope(const ExtensionId& extension_id, | 
|  | const std::string& command_name, | 
|  | bool global); | 
|  |  | 
|  | // Finds the command with the name `command_name` within an extension with id | 
|  | // `extension_id` . Returns an empty Command object (with keycode | 
|  | // VKEY_UNKNOWN) if the command is not found. | 
|  | Command FindCommandByName(const ExtensionId& extension_id, | 
|  | const std::string& command) const; | 
|  |  | 
|  | void AddObserver(Observer* observer); | 
|  | void RemoveObserver(Observer* observer); | 
|  |  | 
|  | // Retrieves an extension with `extension_id` if it is enabled or disabled. | 
|  | const Extension* GetExtensionInEnabledOrDisabledExtensions( | 
|  | const ExtensionId& extension_id) const; | 
|  |  | 
|  | // True if we are upgrading the extension from MV2->MV3. | 
|  | bool IsUpgradeFromMV2ToMV3(const Extension* extension, | 
|  | const std::string& existing_command_name) const; | 
|  |  | 
|  | void UpdateKeybindingsForTest(const Extension* extension) { | 
|  | UpdateKeybindings(extension); | 
|  | } | 
|  |  | 
|  | private: | 
|  | friend class BrowserContextKeyedAPIFactory<CommandService>; | 
|  |  | 
|  | // BrowserContextKeyedAPI implementation. | 
|  | static const char* service_name() { | 
|  | return "CommandService"; | 
|  | } | 
|  | static const bool kServiceRedirectedInIncognito = true; | 
|  |  | 
|  | // ExtensionRegistryObserver. | 
|  | void OnExtensionWillBeInstalled(content::BrowserContext* browser_context, | 
|  | const Extension* extension, | 
|  | bool is_update, | 
|  | const std::string& old_name) override; | 
|  | void OnExtensionUninstalled(content::BrowserContext* browser_context, | 
|  | const Extension* extension, | 
|  | extensions::UninstallReason reason) override; | 
|  |  | 
|  | // Updates keybindings for a given `extension`'s page action, browser action | 
|  | // and named commands. Assigns new keybindings and removes relinquished | 
|  | // keybindings if not changed by the user. In the case of adding keybindings, | 
|  | // if the suggested keybinding is free, it will be taken by this extension. If | 
|  | // not, the keybinding request is ignored. | 
|  | void UpdateKeybindings(const Extension* extension); | 
|  |  | 
|  | // On update, removes keybindings that the extension previously suggested but | 
|  | // now no longer does, as long as the user has not modified them. If a user | 
|  | // has modified an action command, transfer it to the new action command if | 
|  | // we're upgrading (MV2->MV3) before relinquishing. | 
|  | void RemoveRelinquishedKeybindings(const Extension* extension); | 
|  |  | 
|  | // Assigns keybindings that the extension suggests, as long as they are not | 
|  | // already assigned. | 
|  | void AssignKeybindings(const Extension* extension); | 
|  |  | 
|  | // Checks if `extension` is permitted to automatically assign the | 
|  | // `accelerator` key. | 
|  | bool CanAutoAssign(const ui::Command& command, const Extension* extension); | 
|  |  | 
|  | // Updates the record of `extension`'s most recent suggested command shortcut | 
|  | // keys in the preferences. | 
|  | void UpdateExtensionSuggestedCommandPrefs(const Extension* extension); | 
|  |  | 
|  | // Remove suggested key command prefs that apply to commands that have been | 
|  | // removed. | 
|  | void RemoveDefunctExtensionSuggestedCommandPrefs(const Extension* extension); | 
|  |  | 
|  | // Returns true if the user modified a command's shortcut key from the | 
|  | // `extension`-suggested value. | 
|  | bool IsCommandShortcutUserModified(const Extension* extension, | 
|  | const std::string& command_name) const; | 
|  |  | 
|  | // A weak pointer to the profile we are associated with. Not owned by us. | 
|  | raw_ptr<Profile> profile_; | 
|  |  | 
|  | base::ScopedObservation<ExtensionRegistry, ExtensionRegistryObserver> | 
|  | extension_registry_observation_{this}; | 
|  |  | 
|  | base::ObserverList<Observer>::Unchecked observers_; | 
|  | }; | 
|  |  | 
|  | template <> | 
|  | void | 
|  | BrowserContextKeyedAPIFactory<CommandService>::DeclareFactoryDependencies(); | 
|  |  | 
|  | }  //  namespace extensions | 
|  |  | 
|  | #endif  // CHROME_BROWSER_EXTENSIONS_COMMANDS_COMMAND_SERVICE_H_ |