diff options
author | finnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-11 13:12:34 +0000 |
---|---|---|
committer | finnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-11 13:12:34 +0000 |
commit | 3658534f9be85c5e5c2ea071ec21c0b1f3021728 (patch) | |
tree | a4400b0d77044bcf628f1d3cc1d8008b43352c9a | |
parent | df8c45110a92a4383ae2b834f60dc5d9c41eeb64 (diff) | |
download | chromium_src-3658534f9be85c5e5c2ea071ec21c0b1f3021728.zip chromium_src-3658534f9be85c5e5c2ea071ec21c0b1f3021728.tar.gz chromium_src-3658534f9be85c5e5c2ea071ec21c0b1f3021728.tar.bz2 |
Implement first part of supporting global extension commands.
This first part simply reads the optional "global" flag from the manifest and registers the shortcut as global if it is set to true. The intent is to let developers register Ctrl+Shift+[0..9] (and nothing else) as global shortcuts but let users override that -- delete the shortcut, change it to whatever other combination (not restricted to Ctrl+Shift+[0..9] or even make it non-global. There's no UI at the moment to do so, but that is a separate CL that is almost ready.
This CL implements this functionality for Windows and stubs out the other platforms. The GTK stub has been fleshed out in parallel, but that's also another CL.
BUG=302437
R=erg@chromium.org, sky@chromium.org, thakis@chromium.org, yoz@chromium.org
Review URL: https://codereview.chromium.org/23812010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@228176 0039d316-1c4b-4281-b951-d872f2087c98
36 files changed, 1133 insertions, 107 deletions
diff --git a/chrome/browser/extensions/api/commands/command_service.cc b/chrome/browser/extensions/api/commands/command_service.cc index 5d38936..4b3ef78 100644 --- a/chrome/browser/extensions/api/commands/command_service.cc +++ b/chrome/browser/extensions/api/commands/command_service.cc @@ -9,6 +9,7 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/api/commands/commands.h" +#include "chrome/browser/extensions/extension_commands_global_registry.h" #include "chrome/browser/extensions/extension_function_registry.h" #include "chrome/browser/extensions/extension_keybinding_registry.h" #include "chrome/browser/extensions/extension_service.h" @@ -57,6 +58,17 @@ bool InitialBindingsHaveBeenAssigned( return assigned; } +bool IsWhitelistedGlobalShortcut(const extensions::Command& command) { + if (!command.global()) + return true; + if (!command.accelerator().IsCtrlDown()) + return false; + if (!command.accelerator().IsShiftDown()) + return false; + return (command.accelerator().key_code() >= ui::VKEY_0 && + command.accelerator().key_code() <= ui::VKEY_9); +} + } // namespace namespace extensions { @@ -125,9 +137,13 @@ bool CommandService::GetScriptBadgeCommand( bool CommandService::GetNamedCommands(const std::string& extension_id, QueryType type, + CommandScope scope, extensions::CommandMap* command_map) { - const ExtensionSet* extensions = - ExtensionSystem::Get(profile_)->extension_service()->extensions(); + ExtensionService* extension_service = + ExtensionSystem::Get(profile_)->extension_service(); + if (!extension_service) + return false; // Can occur during testing. + const ExtensionSet* extensions = extension_service->extensions(); const Extension* extension = extensions->GetByID(extension_id); CHECK(extension); @@ -146,6 +162,9 @@ bool CommandService::GetNamedCommands(const std::string& extension_id, continue; extensions::Command command = iter->second; + if (scope != ANY_SCOPE && ((scope == GLOBAL) != command.global())) + continue; + if (shortcut_assigned.key_code() != ui::VKEY_UNKNOWN) command.set_accelerator(shortcut_assigned); @@ -265,7 +284,8 @@ void CommandService::AssignInitialKeybindings(const Extension* extension) { extensions::CommandMap::const_iterator iter = commands->begin(); for (; iter != commands->end(); ++iter) { if (!chrome::IsChromeAccelerator( - iter->second.accelerator(), profile_)) { + iter->second.accelerator(), profile_) && + IsWhitelistedGlobalShortcut(iter->second)) { AddKeybindingPref(iter->second.accelerator(), extension->id(), iter->second.command_name(), @@ -404,4 +424,9 @@ bool CommandService::GetExtensionActionCommand( return true; } +template <> +void ProfileKeyedAPIFactory<CommandService>::DeclareFactoryDependencies() { + DependsOn(ExtensionCommandsGlobalRegistry::GetFactoryInstance()); +} + } // namespace extensions diff --git a/chrome/browser/extensions/api/commands/command_service.h b/chrome/browser/extensions/api/commands/command_service.h index d857eb7..ac553aa 100644 --- a/chrome/browser/extensions/api/commands/command_service.h +++ b/chrome/browser/extensions/api/commands/command_service.h @@ -45,6 +45,15 @@ class CommandService : public ProfileKeyedAPI, ACTIVE_ONLY, }; + // 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). + }; + // Register prefs for keybinding. static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); @@ -94,10 +103,11 @@ class CommandService : public ProfileKeyedAPI, // Gets the active command (if any) for the named commands of an extension // given its |extension_id|. The function consults the master list to see if // the command is active. Returns an empty map if the extension has no - // named commands or no active named commands when |type| requested is - // ACTIVE_ONLY. + // named commands of the right |scope| or no such active named commands when + // |type| requested is ACTIVE_ONLY. bool GetNamedCommands(const std::string& extension_id, QueryType type, + CommandScope scope, extensions::CommandMap* command_map); // Records a keybinding |accelerator| as active for an extension with id @@ -175,6 +185,9 @@ class CommandService : public ProfileKeyedAPI, DISALLOW_COPY_AND_ASSIGN(CommandService); }; +template <> +void ProfileKeyedAPIFactory<CommandService>::DeclareFactoryDependencies(); + } // namespace extensions #endif // CHROME_BROWSER_EXTENSIONS_API_COMMANDS_COMMAND_SERVICE_H_ diff --git a/chrome/browser/extensions/api/commands/commands.cc b/chrome/browser/extensions/api/commands/commands.cc index 1bfe054..7e2a10b 100644 --- a/chrome/browser/extensions/api/commands/commands.cc +++ b/chrome/browser/extensions/api/commands/commands.cc @@ -55,6 +55,7 @@ bool GetAllCommandsFunction::RunImpl() { extensions::CommandMap named_commands; command_service->GetNamedCommands(extension_->id(), extensions::CommandService::ALL, + extensions::CommandService::ANY_SCOPE, &named_commands); for (extensions::CommandMap::const_iterator iter = named_commands.begin(); diff --git a/chrome/browser/extensions/extension_commands_global_registry.cc b/chrome/browser/extensions/extension_commands_global_registry.cc new file mode 100644 index 0000000..a7ebd89 --- /dev/null +++ b/chrome/browser/extensions/extension_commands_global_registry.cc @@ -0,0 +1,107 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/extension_commands_global_registry.h" + +#include "base/lazy_instance.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/extensions/api/commands/command_service.h" +#include "chrome/browser/extensions/extension_keybinding_registry.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/global_shortcut_listener.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/extensions/extension.h" + +namespace extensions { + +ExtensionCommandsGlobalRegistry::ExtensionCommandsGlobalRegistry( + Profile* profile) + : ExtensionKeybindingRegistry( + profile, ExtensionKeybindingRegistry::ALL_EXTENSIONS, NULL), + profile_(profile) { + Init(); +} + +ExtensionCommandsGlobalRegistry::~ExtensionCommandsGlobalRegistry() { + EventTargets::const_iterator iter; + for (iter = event_targets_.begin(); iter != event_targets_.end(); ++iter) { + GlobalShortcutListener::GetInstance()->UnregisterAccelerator( + iter->first, this); + } +} + +static base::LazyInstance< + ProfileKeyedAPIFactory<ExtensionCommandsGlobalRegistry> > +g_factory = LAZY_INSTANCE_INITIALIZER; + +// static +ProfileKeyedAPIFactory<ExtensionCommandsGlobalRegistry>* +ExtensionCommandsGlobalRegistry::GetFactoryInstance() { + return &g_factory.Get(); +} + +// static +ExtensionCommandsGlobalRegistry* +ExtensionCommandsGlobalRegistry::Get(Profile* profile) { + return ProfileKeyedAPIFactory< + ExtensionCommandsGlobalRegistry>::GetForProfile(profile); +} + + +void ExtensionCommandsGlobalRegistry::AddExtensionKeybinding( + const extensions::Extension* extension, + const std::string& command_name) { + // This object only handles named commands, not browser/page actions. + if (ShouldIgnoreCommand(command_name)) + return; + + extensions::CommandService* command_service = + extensions::CommandService::Get(profile_); + // Add all the active global keybindings, if any. + extensions::CommandMap commands; + if (!command_service->GetNamedCommands( + extension->id(), + extensions::CommandService::ACTIVE_ONLY, + extensions::CommandService::GLOBAL, + &commands)) + return; + + extensions::CommandMap::const_iterator iter = commands.begin(); + for (; iter != commands.end(); ++iter) { + if (!command_name.empty() && (iter->second.command_name() != command_name)) + continue; + + VLOG(0) << "Adding global keybinding for " << extension->name().c_str() + << " " << command_name.c_str() + << " key: " << iter->second.accelerator().GetShortcutText(); + + event_targets_[iter->second.accelerator()] = + std::make_pair(extension->id(), iter->second.command_name()); + + GlobalShortcutListener::GetInstance()->RegisterAccelerator( + iter->second.accelerator(), this); + } +} + +void ExtensionCommandsGlobalRegistry::RemoveExtensionKeybindingImpl( + const ui::Accelerator& accelerator, + const std::string& command_name) { + VLOG(0) << "Removing keybinding for " << command_name.c_str(); + + GlobalShortcutListener::GetInstance()->UnregisterAccelerator( + accelerator, this); +} + +void ExtensionCommandsGlobalRegistry::OnKeyPressed( + const ui::Accelerator& accelerator) { + EventTargets::iterator it = event_targets_.find(accelerator); + if (it == event_targets_.end()) { + NOTREACHED(); // Shouldn't get this event for something not registered. + return; + } + + CommandExecuted(it->second.first, it->second.second); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/extension_commands_global_registry.h b/chrome/browser/extensions/extension_commands_global_registry.h new file mode 100644 index 0000000..04e8bd8 --- /dev/null +++ b/chrome/browser/extensions/extension_commands_global_registry.h @@ -0,0 +1,76 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_COMMANDS_GLOBAL_REGISTRY_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_COMMANDS_GLOBAL_REGISTRY_H_ + +#include <map> +#include <string> + +#include "base/compiler_specific.h" +#include "chrome/browser/extensions/api/profile_keyed_api_factory.h" +#include "chrome/browser/extensions/extension_keybinding_registry.h" +#include "chrome/browser/extensions/global_shortcut_listener.h" +#include "ui/base/accelerators/accelerator.h" + +class Profile; + +namespace extensions { +class Extension; +} + +namespace extensions { + +// ExtensionCommandsGlobalRegistry is a class that handles the cross-platform +// implementation of the global shortcut registration for the Extension Commands +// API). +// Note: It handles regular extension commands (not browserAction and pageAction +// popups, which are not bindable to global shortcuts). This class registers the +// accelerators on behalf of the extensions and routes the commands to them via +// the BrowserEventRouter. +class ExtensionCommandsGlobalRegistry + : public ProfileKeyedAPI, + public ExtensionKeybindingRegistry, + public GlobalShortcutListener::Observer { + public: + // ProfileKeyedAPI implementation. + static ProfileKeyedAPIFactory< + ExtensionCommandsGlobalRegistry>* GetFactoryInstance(); + + // Convenience method to get the ExtensionCommandsGlobalRegistry for a + // profile. + static ExtensionCommandsGlobalRegistry* Get(Profile* profile); + + explicit ExtensionCommandsGlobalRegistry(Profile* profile); + virtual ~ExtensionCommandsGlobalRegistry(); + + private: + friend class ProfileKeyedAPIFactory<ExtensionCommandsGlobalRegistry>; + + // ProfileKeyedAPI implementation. + static const char* service_name() { + return "ExtensionCommandsGlobalRegistry"; + } + + // Overridden from ExtensionKeybindingRegistry: + virtual void AddExtensionKeybinding( + const Extension* extension, + const std::string& command_name) OVERRIDE; + virtual void RemoveExtensionKeybindingImpl( + const ui::Accelerator& accelerator, + const std::string& command_name) OVERRIDE; + + // Called by the GlobalShortcutListener object when a shortcut this class has + // registered for has been pressed. + virtual void OnKeyPressed(const ui::Accelerator& accelerator) OVERRIDE; + + // Weak pointer to our profile. Not owned by us. + Profile* profile_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionCommandsGlobalRegistry); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_COMMANDS_GLOBAL_REGISTRY_H_ diff --git a/chrome/browser/extensions/extension_keybinding_registry.cc b/chrome/browser/extensions/extension_keybinding_registry.cc index 43ad281..a819ab4 100644 --- a/chrome/browser/extensions/extension_keybinding_registry.cc +++ b/chrome/browser/extensions/extension_keybinding_registry.cc @@ -34,6 +34,30 @@ ExtensionKeybindingRegistry::ExtensionKeybindingRegistry( ExtensionKeybindingRegistry::~ExtensionKeybindingRegistry() { } +void ExtensionKeybindingRegistry::RemoveExtensionKeybinding( + const Extension* extension, + const std::string& command_name) { + EventTargets::iterator iter = event_targets_.begin(); + while (iter != event_targets_.end()) { + if (iter->second.first != extension->id() || + (!command_name.empty() && (iter->second.second != command_name))) { + ++iter; + continue; // Not the extension or command we asked for. + } + + // Let each platform-specific implementation get a chance to clean up. + RemoveExtensionKeybindingImpl(iter->first, command_name); + + EventTargets::iterator old = iter++; + event_targets_.erase(old); + + // If a specific command_name was requested, it has now been deleted so + // no further work is required. + if (!command_name.empty()) + break; + } +} + void ExtensionKeybindingRegistry::Init() { ExtensionService* service = extensions::ExtensionSystem::Get(profile_)->extension_service(); @@ -64,9 +88,11 @@ void ExtensionKeybindingRegistry::CommandExecuted( return; // Grant before sending the event so that the permission is granted before - // the extension acts on the command. + // the extension acts on the command. NOTE: The Global Commands handler does + // not set the delegate as it deals only with named commands (not page/browser + // actions that are associated with the current page directly). ActiveTabPermissionGranter* granter = - delegate_->GetActiveTabPermissionGranter(); + delegate_ ? delegate_->GetActiveTabPermissionGranter() : NULL; if (granter) granter->GrantIfRequested(extension); diff --git a/chrome/browser/extensions/extension_keybinding_registry.h b/chrome/browser/extensions/extension_keybinding_registry.h index 7acd7f5..723a6bb 100644 --- a/chrome/browser/extensions/extension_keybinding_registry.h +++ b/chrome/browser/extensions/extension_keybinding_registry.h @@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_KEYBINDING_REGISTRY_H_ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_KEYBINDING_REGISTRY_H_ +#include <map> #include <string> #include "base/compiler_specific.h" @@ -15,6 +16,10 @@ class Profile; +namespace ui { +class Accelerator; +} + namespace extensions { class ActiveTabPermissionGranter; @@ -63,8 +68,13 @@ class ExtensionKeybindingRegistry : public content::NotificationObserver { const std::string& command_name) = 0; // Remove extension bindings for |extension|. |command_name| is optional, // but if not blank then only the command specified will be removed. - virtual void RemoveExtensionKeybinding( + void RemoveExtensionKeybinding( const Extension* extension, + const std::string& command_name); + // Overridden by platform specific implementations to provide additional + // unregistration (which varies between platforms). + virtual void RemoveExtensionKeybindingImpl( + const ui::Accelerator& accelerator, const std::string& command_name) = 0; // Make sure all extensions registered have keybindings added. @@ -78,6 +88,15 @@ class ExtensionKeybindingRegistry : public content::NotificationObserver { void CommandExecuted(const std::string& extension_id, const std::string& command); + // Maps an accelerator to a string pair (extension id, command name) for + // commands that have been registered. This keeps track of the targets for the + // keybinding event (which named command to call in which extension). On GTK, + // this map contains registration for pageAction and browserAction commands, + // whereas on other platforms it does not. + typedef std::map< ui::Accelerator, + std::pair<std::string, std::string> > EventTargets; + EventTargets event_targets_; + private: // Returns true if the |extension| matches our extension filter. bool ExtensionMatchesFilter(const extensions::Extension* extension); diff --git a/chrome/browser/extensions/global_shortcut_listener.cc b/chrome/browser/extensions/global_shortcut_listener.cc new file mode 100644 index 0000000..94547ad --- /dev/null +++ b/chrome/browser/extensions/global_shortcut_listener.cc @@ -0,0 +1,62 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/global_shortcut_listener.h"
+#include "chrome/browser/profiles/profile.h"
+#include "ui/base/accelerators/accelerator.h"
+
+namespace extensions {
+
+GlobalShortcutListener::GlobalShortcutListener() {
+}
+
+GlobalShortcutListener::~GlobalShortcutListener() {
+ DCHECK(accelerator_map_.empty()); // Make sure we've cleaned up.
+}
+
+void GlobalShortcutListener::RegisterAccelerator(
+ const ui::Accelerator& accelerator, Observer* observer) {
+ AcceleratorMap::const_iterator it = accelerator_map_.find(accelerator);
+ if (it == accelerator_map_.end()) {
+ if (accelerator_map_.empty())
+ GlobalShortcutListener::GetInstance()->StartListening();
+ Observers* observers = new Observers;
+ observers->AddObserver(observer);
+ accelerator_map_[accelerator] = observers;
+ } else {
+ // Make sure we don't register the same accelerator twice.
+ DCHECK(!accelerator_map_[accelerator]->HasObserver(observer));
+ accelerator_map_[accelerator]->AddObserver(observer);
+ }
+}
+
+void GlobalShortcutListener::UnregisterAccelerator(
+ const ui::Accelerator& accelerator, Observer* observer) {
+ AcceleratorMap::iterator it = accelerator_map_.find(accelerator);
+ DCHECK(it != accelerator_map_.end());
+ DCHECK(it->second->HasObserver(observer));
+ it->second->RemoveObserver(observer);
+ if (!it->second->might_have_observers()) {
+ accelerator_map_.erase(it);
+ if (accelerator_map_.empty())
+ GlobalShortcutListener::GetInstance()->StopListening();
+ }
+}
+
+void GlobalShortcutListener::NotifyKeyPressed(
+ const ui::Accelerator& accelerator) {
+ AcceleratorMap::iterator iter = accelerator_map_.find(accelerator);
+ if (iter == accelerator_map_.end()) {
+ // This should never occur, because if it does, we have failed to unregister
+ // or failed to clean up the map after unregistering the shortcut.
+ NOTREACHED();
+ return; // No-one is listening to this key.
+ }
+ // The observer list should not be empty.
+ DCHECK(iter->second->might_have_observers());
+
+ FOR_EACH_OBSERVER(Observer, *(iter->second), OnKeyPressed(accelerator));
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/global_shortcut_listener.h b/chrome/browser/extensions/global_shortcut_listener.h new file mode 100644 index 0000000..4ca6921 --- /dev/null +++ b/chrome/browser/extensions/global_shortcut_listener.h @@ -0,0 +1,65 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_
+#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_
+
+#include <map>
+
+#include "base/observer_list.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace ui {
+class Accelerator;
+}
+
+namespace extensions {
+
+// Platform-neutral implementation of a class that keeps track of observers and
+// monitors keystrokes. It relays messages to the appropriate observers when a
+// global shortcut has been struck by the user.
+class GlobalShortcutListener {
+ public:
+ class Observer {
+ public:
+ // Called when your global shortcut (|accelerator|) is struck.
+ virtual void OnKeyPressed(const ui::Accelerator& accelerator) = 0;
+ };
+
+ virtual ~GlobalShortcutListener();
+
+ static GlobalShortcutListener* GetInstance();
+
+ // Implemented by platform-specific implementations of this class.
+ virtual void StartListening() = 0;
+ virtual void StopListening() = 0;
+
+ // Register an observer for when a certain |accelerator| is struck.
+ virtual void RegisterAccelerator(
+ const ui::Accelerator& accelerator, Observer* observer);
+ // Stop listening for the given |accelerator|.
+ virtual void UnregisterAccelerator(
+ const ui::Accelerator& accelerator, Observer* observer);
+
+ protected:
+ GlobalShortcutListener();
+
+ // Called by platform specific implementations of this class whenever a key
+ // is struck. Only called for keys that have observers registered.
+ void NotifyKeyPressed(const ui::Accelerator& accelerator);
+
+ // The map of accelerators that have been successfully registered as global
+ // shortcuts and their observer lists.
+ typedef ObserverList<Observer> Observers;
+ typedef std::map< ui::Accelerator, Observers* > AcceleratorMap;
+ AcceleratorMap accelerator_map_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListener);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_H_
diff --git a/chrome/browser/extensions/global_shortcut_listener_aurax11.cc b/chrome/browser/extensions/global_shortcut_listener_aurax11.cc new file mode 100644 index 0000000..bfeeac9 --- /dev/null +++ b/chrome/browser/extensions/global_shortcut_listener_aurax11.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/global_shortcut_listener_aurax11.h" + +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace { + +static base::LazyInstance<extensions::GlobalShortcutListenerAuraX11> instance = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +namespace extensions { + +// static +GlobalShortcutListener* GlobalShortcutListener::GetInstance() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + return instance.Pointer(); +} + +GlobalShortcutListenerAuraX11::GlobalShortcutListenerAuraX11() + : is_listening_(false) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // TODO(implementor): Remove this. + LOG(ERROR) << "GlobalShortcutListenerAuraX11 object created"; +} + +GlobalShortcutListenerAuraX11::~GlobalShortcutListenerAuraX11() { + if (is_listening_) + StopListening(); +} + +void GlobalShortcutListenerAuraX11::StartListening() { + DCHECK(!is_listening_); // Don't start twice. + NOTIMPLEMENTED(); + is_listening_ = true; +} + +void GlobalShortcutListenerAuraX11::StopListening() { + DCHECK(is_listening_); // No point if we are not already listening. + NOTIMPLEMENTED(); + is_listening_ = false; +} + +void GlobalShortcutListenerAuraX11::RegisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) { + NOTIMPLEMENTED(); + // To implement: + // 1) Convert modifiers to platform specific modifiers. + // 2) Register for the hotkey. + // 3) If not successful, log why. + // 4) Else, call base class RegisterAccelerator. + + GlobalShortcutListener::RegisterAccelerator(accelerator, observer); +} + +void GlobalShortcutListenerAuraX11::UnregisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) { + NOTIMPLEMENTED(); + // To implement: + // 1) Unregister for the hotkey. + // 2) Call base class UnregisterAccelerator. + + GlobalShortcutListener::UnregisterAccelerator(accelerator, observer); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/global_shortcut_listener_aurax11.h b/chrome/browser/extensions/global_shortcut_listener_aurax11.h new file mode 100644 index 0000000..bc353c2 --- /dev/null +++ b/chrome/browser/extensions/global_shortcut_listener_aurax11.h @@ -0,0 +1,46 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_AURAX11_H_ +#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_AURAX11_H_ + +#include "base/lazy_instance.h" +#include "chrome/browser/extensions/global_shortcut_listener.h" + +namespace extensions { + +// Linux-specific implementation of the GlobalShortcutListener class that +// listens for global shortcuts. Handles basic keyboard intercepting and +// forwards its output to the base class for processing. +// TODO(finnur): Implement this class. +class GlobalShortcutListenerAuraX11 : public GlobalShortcutListener { + public: + virtual ~GlobalShortcutListenerAuraX11(); + + virtual void StartListening() OVERRIDE; + virtual void StopListening() OVERRIDE; + + private: + friend struct base::DefaultLazyInstanceTraits<GlobalShortcutListenerAuraX11>; + + GlobalShortcutListenerAuraX11(); + + // Register an |accelerator| with the particular |observer|. + virtual void RegisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) OVERRIDE; + // Unregister an |accelerator| with the particular |observer|. + virtual void UnregisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) OVERRIDE; + + // Whether this object is listening for global shortcuts. + bool is_listening_; + + DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerAuraX11); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_AURAX11_H_ diff --git a/chrome/browser/extensions/global_shortcut_listener_chromeos.cc b/chrome/browser/extensions/global_shortcut_listener_chromeos.cc new file mode 100644 index 0000000..af97100 --- /dev/null +++ b/chrome/browser/extensions/global_shortcut_listener_chromeos.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/global_shortcut_listener_chromeos.h" + +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace { + +static base::LazyInstance<extensions::GlobalShortcutListenerChromeOS> instance = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +namespace extensions { + +// static +GlobalShortcutListener* GlobalShortcutListener::GetInstance() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + return instance.Pointer(); +} + +GlobalShortcutListenerChromeOS::GlobalShortcutListenerChromeOS() + : is_listening_(false) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // TODO(implementor): Remove this. + LOG(ERROR) << "GlobalShortcutListenerChromeOS object created"; +} + +GlobalShortcutListenerChromeOS::~GlobalShortcutListenerChromeOS() { + if (is_listening_) + StopListening(); +} + +void GlobalShortcutListenerChromeOS::StartListening() { + DCHECK(!is_listening_); // Don't start twice. + NOTIMPLEMENTED(); + is_listening_ = true; +} + +void GlobalShortcutListenerChromeOS::StopListening() { + DCHECK(is_listening_); // No point if we are not already listening. + NOTIMPLEMENTED(); + is_listening_ = false; +} + +void GlobalShortcutListenerChromeOS::RegisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) { + NOTIMPLEMENTED(); + // To implement: + // 1) Convert modifiers to platform specific modifiers. + // 2) Register for the hotkey. + // 3) If not successful, log why. + // 4) Else, call base class RegisterAccelerator. + + GlobalShortcutListener::RegisterAccelerator(accelerator, observer); +} + +void GlobalShortcutListenerChromeOS::UnregisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) { + NOTIMPLEMENTED(); + // To implement: + // 1) Unregister for the hotkey. + // 2) Call base class UnregisterAccelerator. + + GlobalShortcutListener::UnregisterAccelerator(accelerator, observer); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/global_shortcut_listener_chromeos.h b/chrome/browser/extensions/global_shortcut_listener_chromeos.h new file mode 100644 index 0000000..f052ed65 --- /dev/null +++ b/chrome/browser/extensions/global_shortcut_listener_chromeos.h @@ -0,0 +1,48 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_CHROMEOS_H_ +#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_CHROMEOS_H_ + +#include "base/lazy_instance.h" +#include "chrome/browser/extensions/global_shortcut_listener.h" + +// TODO(finnur): Figure out what to do on ChromeOS, where the Commands API kind +// of is global already... + +namespace extensions { + +// ChromeOS-specific implementation of the GlobalShortcutListener class that +// listens for global shortcuts. Handles basic keyboard intercepting and +// forwards its output to the base class for processing. +class GlobalShortcutListenerChromeOS : public GlobalShortcutListener { + public: + virtual ~GlobalShortcutListenerChromeOS(); + + virtual void StartListening() OVERRIDE; + virtual void StopListening() OVERRIDE; + + private: + friend struct base::DefaultLazyInstanceTraits<GlobalShortcutListenerChromeOS>; + + GlobalShortcutListenerChromeOS(); + + // Register an |accelerator| with the particular |observer|. + virtual void RegisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) OVERRIDE; + // Unregister an |accelerator| with the particular |observer|. + virtual void UnregisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) OVERRIDE; + + // Whether this object is listening for global shortcuts. + bool is_listening_; + + DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerChromeOS); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_CHROMEOS_H_ diff --git a/chrome/browser/extensions/global_shortcut_listener_gtk.cc b/chrome/browser/extensions/global_shortcut_listener_gtk.cc new file mode 100644 index 0000000..bfb00ce --- /dev/null +++ b/chrome/browser/extensions/global_shortcut_listener_gtk.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/global_shortcut_listener_gtk.h" + +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace { + +static base::LazyInstance<extensions::GlobalShortcutListenerGtk> instance = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +namespace extensions { + +// static +GlobalShortcutListener* GlobalShortcutListener::GetInstance() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + return instance.Pointer(); +} + +GlobalShortcutListenerGtk::GlobalShortcutListenerGtk() + : is_listening_(false) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // TODO(implementor): Remove this. + LOG(ERROR) << "GlobalShortcutListenerGtk object created"; +} + +GlobalShortcutListenerGtk::~GlobalShortcutListenerGtk() { + if (is_listening_) + StopListening(); +} + +void GlobalShortcutListenerGtk::StartListening() { + DCHECK(!is_listening_); // Don't start twice. + NOTIMPLEMENTED(); + is_listening_ = true; +} + +void GlobalShortcutListenerGtk::StopListening() { + DCHECK(is_listening_); // No point if we are not already listening. + NOTIMPLEMENTED(); + is_listening_ = false; +} + +void GlobalShortcutListenerGtk::RegisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) { + NOTIMPLEMENTED(); + // To implement: + // 1) Convert modifiers to platform specific modifiers. + // 2) Register for the hotkey. + // 3) If not successful, log why. + // 4) Else, call base class RegisterAccelerator. + + GlobalShortcutListener::RegisterAccelerator(accelerator, observer); +} + +void GlobalShortcutListenerGtk::UnregisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) { + NOTIMPLEMENTED(); + // To implement: + // 1) Unregister for the hotkey. + // 2) Call base class UnregisterAccelerator. + + GlobalShortcutListener::UnregisterAccelerator(accelerator, observer); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/global_shortcut_listener_gtk.h b/chrome/browser/extensions/global_shortcut_listener_gtk.h new file mode 100644 index 0000000..81e97bf --- /dev/null +++ b/chrome/browser/extensions/global_shortcut_listener_gtk.h @@ -0,0 +1,46 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_GTK_H_ +#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_GTK_H_ + +#include "base/lazy_instance.h" +#include "chrome/browser/extensions/global_shortcut_listener.h" + +namespace extensions { + +// Linux-specific implementation of the GlobalShortcutListener class that +// listens for global shortcuts. Handles basic keyboard intercepting and +// forwards its output to the base class for processing. +// TODO(zhaobin): Implement this class. +class GlobalShortcutListenerGtk : public GlobalShortcutListener { + public: + virtual ~GlobalShortcutListenerGtk(); + + virtual void StartListening() OVERRIDE; + virtual void StopListening() OVERRIDE; + + private: + friend struct base::DefaultLazyInstanceTraits<GlobalShortcutListenerGtk>; + + GlobalShortcutListenerGtk(); + + // Register an |accelerator| with the particular |observer|. + virtual void RegisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) OVERRIDE; + // Unregister an |accelerator| with the particular |observer|. + virtual void UnregisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) OVERRIDE; + + // Whether this object is listening for global shortcuts. + bool is_listening_; + + DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerGtk); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_GTK_H_ diff --git a/chrome/browser/extensions/global_shortcut_listener_mac.cc b/chrome/browser/extensions/global_shortcut_listener_mac.cc new file mode 100644 index 0000000..3604c85 --- /dev/null +++ b/chrome/browser/extensions/global_shortcut_listener_mac.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/global_shortcut_listener_mac.h" + +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace { + +static base::LazyInstance<extensions::GlobalShortcutListenerMac> instance = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +namespace extensions { + +// static +GlobalShortcutListener* GlobalShortcutListener::GetInstance() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + return instance.Pointer(); +} + +GlobalShortcutListenerMac::GlobalShortcutListenerMac() + : is_listening_(false) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // TODO(implementor): Remove this. + LOG(ERROR) << "GlobalShortcutListenerMac object created"; +} + +GlobalShortcutListenerMac::~GlobalShortcutListenerMac() { + if (is_listening_) + StopListening(); +} + +void GlobalShortcutListenerMac::StartListening() { + DCHECK(!is_listening_); // Don't start twice. + NOTIMPLEMENTED(); + is_listening_ = true; +} + +void GlobalShortcutListenerMac::StopListening() { + DCHECK(is_listening_); // No point if we are not already listening. + NOTIMPLEMENTED(); + is_listening_ = false; +} + +void GlobalShortcutListenerMac::RegisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) { + NOTIMPLEMENTED(); + // To implement: + // 1) Convert modifiers to platform specific modifiers. + // 2) Register for the hotkey. + // 3) If not successful, log why. + // 4) Else, call base class RegisterAccelerator. + + GlobalShortcutListener::RegisterAccelerator(accelerator, observer); +} + +void GlobalShortcutListenerMac::UnregisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) { + NOTIMPLEMENTED(); + // To implement: + // 1) Unregister for the hotkey. + // 2) Call base class UnregisterAccelerator. + + GlobalShortcutListener::UnregisterAccelerator(accelerator, observer); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/global_shortcut_listener_mac.h b/chrome/browser/extensions/global_shortcut_listener_mac.h new file mode 100644 index 0000000..8c1b5db --- /dev/null +++ b/chrome/browser/extensions/global_shortcut_listener_mac.h @@ -0,0 +1,46 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_MAC_H_ +#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_MAC_H_ + +#include "base/lazy_instance.h" +#include "chrome/browser/extensions/global_shortcut_listener.h" + +namespace extensions { + +// Mac-specific implementation of the GlobalShortcutListener class that +// listens for global shortcuts. Handles basic keyboard intercepting and +// forwards its output to the base class for processing. +// TODO(finnur/smus): Implement this class. +class GlobalShortcutListenerMac : public GlobalShortcutListener { + public: + virtual ~GlobalShortcutListenerMac(); + + virtual void StartListening() OVERRIDE; + virtual void StopListening() OVERRIDE; + + private: + friend struct base::DefaultLazyInstanceTraits<GlobalShortcutListenerMac>; + + GlobalShortcutListenerMac(); + + // Register an |accelerator| with the particular |observer|. + virtual void RegisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) OVERRIDE; + // Unregister an |accelerator| with the particular |observer|. + virtual void UnregisterAccelerator( + const ui::Accelerator& accelerator, + GlobalShortcutListener::Observer* observer) OVERRIDE; + + // Whether this object is listening for global shortcuts. + bool is_listening_; + + DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerMac); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_MAC_H_ diff --git a/chrome/browser/extensions/global_shortcut_listener_win.cc b/chrome/browser/extensions/global_shortcut_listener_win.cc new file mode 100644 index 0000000..462ff67 --- /dev/null +++ b/chrome/browser/extensions/global_shortcut_listener_win.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/global_shortcut_listener_win.h"
+
+#include "base/win/win_util.h"
+#include "content/public/browser/browser_thread.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/keycodes/keyboard_code_conversion_win.h"
+
+using content::BrowserThread;
+
+namespace {
+
+static base::LazyInstance<extensions::GlobalShortcutListenerWin> instance =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+namespace extensions {
+
+// static
+GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return instance.Pointer();
+}
+
+GlobalShortcutListenerWin::GlobalShortcutListenerWin()
+ : is_listening_(false) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+}
+
+GlobalShortcutListenerWin::~GlobalShortcutListenerWin() {
+ if (is_listening_)
+ StopListening();
+}
+
+void GlobalShortcutListenerWin::StartListening() {
+ DCHECK(!is_listening_); // Don't start twice.
+ DCHECK(!hotkey_ids_.empty()); // Also don't start if no hotkey is registered.
+ gfx::SingletonHwnd::GetInstance()->AddObserver(this);
+ is_listening_ = true;
+}
+
+void GlobalShortcutListenerWin::StopListening() {
+ DCHECK(is_listening_); // No point if we are not already listening.
+ DCHECK(hotkey_ids_.empty()); // Make sure the map is clean before ending.
+ gfx::SingletonHwnd::GetInstance()->RemoveObserver(this);
+ is_listening_ = false;
+}
+
+void GlobalShortcutListenerWin::OnWndProc(HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam) {
+ if (message != WM_HOTKEY)
+ return;
+
+ int key_code = HIWORD(lparam);
+ int modifiers = 0;
+ modifiers |= (LOWORD(lparam) & MOD_SHIFT) ? ui::EF_SHIFT_DOWN : 0;
+ modifiers |= (LOWORD(lparam) & MOD_ALT) ? ui::EF_ALT_DOWN : 0;
+ modifiers |= (LOWORD(lparam) & MOD_CONTROL) ? ui::EF_CONTROL_DOWN : 0;
+ ui::Accelerator accelerator(
+ ui::KeyboardCodeForWindowsKeyCode(key_code), modifiers);
+
+ instance.Get().NotifyKeyPressed(accelerator);
+}
+
+void GlobalShortcutListenerWin::RegisterAccelerator(
+ const ui::Accelerator& accelerator,
+ GlobalShortcutListener::Observer* observer) {
+ int modifiers = 0;
+ modifiers |= accelerator.IsShiftDown() ? MOD_SHIFT : 0;
+ modifiers |= accelerator.IsCtrlDown() ? MOD_CONTROL : 0;
+ modifiers |= accelerator.IsAltDown() ? MOD_ALT : 0;
+ static int hotkey_id = 0;
+ bool success = !!RegisterHotKey(
+ gfx::SingletonHwnd::GetInstance()->hwnd(),
+ hotkey_id,
+ modifiers,
+ accelerator.key_code());
+
+ if (!success) {
+ // Most likely error: 1409 (Hotkey already registered).
+ LOG(ERROR) << "RegisterHotKey failed, error: " << GetLastError();
+ return;
+ }
+
+ hotkey_ids_[accelerator] = hotkey_id++;
+ GlobalShortcutListener::RegisterAccelerator(accelerator, observer);
+}
+
+void GlobalShortcutListenerWin::UnregisterAccelerator(
+ const ui::Accelerator& accelerator,
+ GlobalShortcutListener::Observer* observer) {
+ // We may get asked to unregister something that we couldn't register (for
+ // example if the shortcut was already taken by another app), so we
+ // need to handle that gracefully.
+ HotkeyIdMap::iterator it = hotkey_ids_.find(accelerator);
+ if (it == hotkey_ids_.end())
+ return;
+
+ UnregisterHotKey(gfx::SingletonHwnd::GetInstance()->hwnd(), it->second);
+ hotkey_ids_.erase(it);
+
+ GlobalShortcutListener::UnregisterAccelerator(accelerator, observer);
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/global_shortcut_listener_win.h b/chrome/browser/extensions/global_shortcut_listener_win.h new file mode 100644 index 0000000..1844ee4 --- /dev/null +++ b/chrome/browser/extensions/global_shortcut_listener_win.h @@ -0,0 +1,59 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_
+#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_
+
+#include <windows.h>
+
+#include "base/lazy_instance.h"
+#include "chrome/browser/extensions/global_shortcut_listener.h"
+#include "ui/gfx/win/singleton_hwnd.h"
+
+namespace extensions {
+
+// Windows-specific implementation of the GlobalShortcutListener class that
+// listens for global shortcuts. Handles setting up a keyboard hook and
+// forwarding its output to the base class for processing.
+class GlobalShortcutListenerWin : public GlobalShortcutListener,
+ public gfx::SingletonHwnd::Observer {
+ public:
+ virtual ~GlobalShortcutListenerWin();
+
+ virtual void StartListening() OVERRIDE;
+ virtual void StopListening() OVERRIDE;
+
+ private:
+ friend struct base::DefaultLazyInstanceTraits<GlobalShortcutListenerWin>;
+
+ GlobalShortcutListenerWin();
+
+ // The implementation of our Window Proc, called by SingletonHwnd.
+ virtual void OnWndProc(HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam) OVERRIDE;
+
+ // Register an |accelerator| with the particular |observer|.
+ virtual void RegisterAccelerator(
+ const ui::Accelerator& accelerator,
+ GlobalShortcutListener::Observer* observer) OVERRIDE;
+ // Unregister an |accelerator| with the particular |observer|.
+ virtual void UnregisterAccelerator(
+ const ui::Accelerator& accelerator,
+ GlobalShortcutListener::Observer* observer) OVERRIDE;
+
+ // Whether this object is listening for global shortcuts.
+ bool is_listening_;
+
+ // A map of registered accelerators and their registration ids.
+ typedef std::map< ui::Accelerator, int > HotkeyIdMap;
+ HotkeyIdMap hotkey_ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerWin);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_
diff --git a/chrome/browser/ui/cocoa/extensions/extension_keybinding_registry_cocoa.h b/chrome/browser/ui/cocoa/extensions/extension_keybinding_registry_cocoa.h index 41f98df..f3fdcc9 100644 --- a/chrome/browser/ui/cocoa/extensions/extension_keybinding_registry_cocoa.h +++ b/chrome/browser/ui/cocoa/extensions/extension_keybinding_registry_cocoa.h @@ -5,7 +5,6 @@ #ifndef CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_KEYBINDING_REGISTRY_COCOA_H_ #define CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_KEYBINDING_REGISTRY_COCOA_H_ -#include <map> #include <string> #include <utility> @@ -56,8 +55,8 @@ class ExtensionKeybindingRegistryCocoa virtual void AddExtensionKeybinding( const extensions::Extension* extension, const std::string& command_name) OVERRIDE; - virtual void RemoveExtensionKeybinding( - const extensions::Extension* extension, + virtual void RemoveExtensionKeybindingImpl( + const ui::Accelerator& accelerator, const std::string& command_name) OVERRIDE; private: @@ -74,14 +73,6 @@ class ExtensionKeybindingRegistryCocoa // The window we are associated with. gfx::NativeWindow window_; - // Maps an accelerator to a string pair (extension id, command name) for - // commands that have been registered. Unlike its Views counterpart, this map - // contains browserAction and pageAction commands (but does not route those - // events), so that we can query priority handlers in HasPriorityHandler(...). - typedef std::map< ui::Accelerator, - std::pair<std::string, std::string> > EventTargets; - EventTargets event_targets_; - // The content notification registrar for listening to extension events. content::NotificationRegistrar registrar_; diff --git a/chrome/browser/ui/cocoa/extensions/extension_keybinding_registry_cocoa.mm b/chrome/browser/ui/cocoa/extensions/extension_keybinding_registry_cocoa.mm index 839635a..54b0d3d 100644 --- a/chrome/browser/ui/cocoa/extensions/extension_keybinding_registry_cocoa.mm +++ b/chrome/browser/ui/cocoa/extensions/extension_keybinding_registry_cocoa.mm @@ -84,6 +84,7 @@ void ExtensionKeybindingRegistryCocoa::AddExtensionKeybinding( command_service->GetNamedCommands( extension->id(), extensions::CommandService::ACTIVE_ONLY, + extensions::CommandService::REGULAR, &commands); for (extensions::CommandMap::const_iterator iter = commands.begin(); @@ -135,14 +136,7 @@ void ExtensionKeybindingRegistryCocoa::AddExtensionKeybinding( } } -void ExtensionKeybindingRegistryCocoa::RemoveExtensionKeybinding( - const extensions::Extension* extension, +void ExtensionKeybindingRegistryCocoa::RemoveExtensionKeybindingImpl( + const ui::Accelerator& accelerator, const std::string& command_name) { - EventTargets::iterator iter = event_targets_.begin(); - while (iter != event_targets_.end()) { - EventTargets::iterator old = iter++; - if (old->second.first == extension->id() && - (command_name.empty() || (old->second.second == command_name))) - event_targets_.erase(old); - } } diff --git a/chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.cc b/chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.cc index ccb0256..4637dd0 100644 --- a/chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.cc +++ b/chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.cc @@ -64,6 +64,7 @@ void ExtensionKeybindingRegistryGtk::AddExtensionKeybinding( command_service->GetNamedCommands( extension->id(), extensions::CommandService::ACTIVE_ONLY, + extensions::CommandService::REGULAR, &commands); for (extensions::CommandMap::const_iterator iter = commands.begin(); @@ -127,28 +128,16 @@ void ExtensionKeybindingRegistryGtk::AddExtensionKeybinding( } } -void ExtensionKeybindingRegistryGtk::RemoveExtensionKeybinding( - const extensions::Extension* extension, +void ExtensionKeybindingRegistryGtk::RemoveExtensionKeybindingImpl( + const ui::Accelerator& accelerator, const std::string& command_name) { - EventTargets::iterator iter = event_targets_.begin(); - while (iter != event_targets_.end()) { - if (iter->second.first != extension->id() || - (!command_name.empty() && iter->second.second != command_name)) { - ++iter; - continue; // Not the extension or command we asked for. - } - - // On GTK, unlike Windows, the Event Targets contain all events but we must - // only unregister the ones we registered targets for. - if (!ShouldIgnoreCommand(iter->second.second)) { - gtk_accel_group_disconnect_key( - accel_group_, - ui::GetGdkKeyCodeForAccelerator(iter->first), - ui::GetGdkModifierForAccelerator(iter->first)); - } - - EventTargets::iterator old = iter++; - event_targets_.erase(old); + // On GTK, unlike Windows, the Event Targets contain all events but we must + // only unregister the ones we registered targets for. + if (!ShouldIgnoreCommand(command_name)) { + gtk_accel_group_disconnect_key( + accel_group_, + ui::GetGdkKeyCodeForAccelerator(accelerator), + ui::GetGdkModifierForAccelerator(accelerator)); } } diff --git a/chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.h b/chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.h index 8b3ac20..63c94b5 100644 --- a/chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.h +++ b/chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.h @@ -6,7 +6,6 @@ #define CHROME_BROWSER_UI_GTK_EXTENSIONS_EXTENSION_KEYBINDING_REGISTRY_GTK_H_ #include <gdk/gdk.h> -#include <map> #include <string> #include "base/compiler_specific.h" @@ -59,8 +58,8 @@ class ExtensionKeybindingRegistryGtk virtual void AddExtensionKeybinding( const extensions::Extension* extension, const std::string& command_name) OVERRIDE; - virtual void RemoveExtensionKeybinding( - const extensions::Extension* extension, + virtual void RemoveExtensionKeybindingImpl( + const ui::Accelerator& accelerator, const std::string& command_name) OVERRIDE; private: @@ -85,14 +84,6 @@ class ExtensionKeybindingRegistryGtk // The accelerator group used to handle accelerators, owned by this object. GtkAccelGroup* accel_group_; - // Maps an accelerator to a string pair (extension id, command name) for - // commands that have been registered. Unlike its Views counterpart, this map - // contains browserAction and pageAction commands (but does not route those - // events), so that we can query priority handlers in HasPriorityHandler(...). - typedef std::map< ui::Accelerator, - std::pair<std::string, std::string> > EventTargets; - EventTargets event_targets_; - // The content notification registrar for listening to extension events. content::NotificationRegistrar registrar_; diff --git a/chrome/browser/ui/views/browser_actions_container.cc b/chrome/browser/ui/views/browser_actions_container.cc index 7e011b5..8501897 100644 --- a/chrome/browser/ui/views/browser_actions_container.cc +++ b/chrome/browser/ui/views/browser_actions_container.cc @@ -84,7 +84,7 @@ BrowserActionsContainer::BrowserActionsContainer(Browser* browser, browser->profile(), owner_view->GetFocusManager(), extensions::ExtensionKeybindingRegistry::ALL_EXTENSIONS, - this)), + this)); resize_animation_.reset(new gfx::SlideAnimation(this)); resize_area_ = new views::ResizeArea(this); diff --git a/chrome/browser/ui/views/extensions/extension_keybinding_registry_views.cc b/chrome/browser/ui/views/extensions/extension_keybinding_registry_views.cc index 1531847..61f4915 100644 --- a/chrome/browser/ui/views/extensions/extension_keybinding_registry_views.cc +++ b/chrome/browser/ui/views/extensions/extension_keybinding_registry_views.cc @@ -47,7 +47,10 @@ void ExtensionKeybindingRegistryViews::AddExtensionKeybinding( // which are handled elsewhere). extensions::CommandMap commands; if (!command_service->GetNamedCommands( - extension->id(), extensions::CommandService::ACTIVE_ONLY, &commands)) + extension->id(), + extensions::CommandService::ACTIVE_ONLY, + extensions::CommandService::REGULAR, + &commands)) return; extensions::CommandMap::const_iterator iter = commands.begin(); for (; iter != commands.end(); ++iter) { @@ -62,26 +65,10 @@ void ExtensionKeybindingRegistryViews::AddExtensionKeybinding( } } -void ExtensionKeybindingRegistryViews::RemoveExtensionKeybinding( - const extensions::Extension* extension, +void ExtensionKeybindingRegistryViews::RemoveExtensionKeybindingImpl( + const ui::Accelerator& accelerator, const std::string& command_name) { - // This object only handles named commands, not browser/page actions. - if (ShouldIgnoreCommand(command_name)) - return; - - EventTargets::iterator iter = event_targets_.begin(); - while (iter != event_targets_.end()) { - if (iter->second.first != extension->id() || - (!command_name.empty() && (iter->second.second != command_name))) { - ++iter; - continue; // Not the extension or command we asked for. - } - - focus_manager_->UnregisterAccelerator(iter->first, this); - - EventTargets::iterator old = iter++; - event_targets_.erase(old); - } + focus_manager_->UnregisterAccelerator(accelerator, this); } bool ExtensionKeybindingRegistryViews::AcceleratorPressed( diff --git a/chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h b/chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h index 3615a61..4e2fc87 100644 --- a/chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h +++ b/chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h @@ -5,7 +5,6 @@ #ifndef CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSION_KEYBINDING_REGISTRY_VIEWS_H_ #define CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSION_KEYBINDING_REGISTRY_VIEWS_H_ -#include <map> #include <string> #include "base/compiler_specific.h" @@ -47,8 +46,8 @@ class ExtensionKeybindingRegistryViews virtual void AddExtensionKeybinding( const extensions::Extension* extension, const std::string& command_name) OVERRIDE; - virtual void RemoveExtensionKeybinding( - const extensions::Extension* extension, + virtual void RemoveExtensionKeybindingImpl( + const ui::Accelerator& accelerator, const std::string& command_name) OVERRIDE; // Weak pointer to the our profile. Not owned by us. @@ -58,13 +57,6 @@ class ExtensionKeybindingRegistryViews // accelerators with. Not owned by us. views::FocusManager* focus_manager_; - // Maps an accelerator to a string pair (extension id, command name) for - // commands that have been registered. Unlike its GTK counterpart, this map - // contains no registration for pageAction and browserAction commands. - typedef std::map< ui::Accelerator, - std::pair<std::string, std::string> > EventTargets; - EventTargets event_targets_; - // The content notification registrar for listening to extension events. content::NotificationRegistrar registrar_; diff --git a/chrome/browser/ui/webui/extensions/command_handler.cc b/chrome/browser/ui/webui/extensions/command_handler.cc index 15df2d8..f89634c 100644 --- a/chrome/browser/ui/webui/extensions/command_handler.cc +++ b/chrome/browser/ui/webui/extensions/command_handler.cc @@ -149,6 +149,7 @@ void CommandHandler::GetAllCommands(base::DictionaryValue* commands) { extensions::CommandMap named_commands; if (command_service->GetNamedCommands((*extension)->id(), CommandService::ALL, + extensions::CommandService::ANY_SCOPE, &named_commands)) { for (extensions::CommandMap::const_iterator iter = named_commands.begin(); iter != named_commands.end(); ++iter) { diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index aa832f1..4e4536e 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -640,6 +640,8 @@ 'browser/extensions/extension_action_icon_factory.h', 'browser/extensions/extension_action_manager.cc', 'browser/extensions/extension_action_manager.h', + 'browser/extensions/extension_commands_global_registry.cc', + 'browser/extensions/extension_commands_global_registry.h', 'browser/extensions/extension_context_menu_model.cc', 'browser/extensions/extension_context_menu_model.h', 'browser/extensions/extension_creator.cc', @@ -750,6 +752,18 @@ 'browser/extensions/external_provider_interface.h', 'browser/extensions/external_registry_loader_win.cc', 'browser/extensions/external_registry_loader_win.h', + 'browser/extensions/global_shortcut_listener.cc', + 'browser/extensions/global_shortcut_listener.h', + 'browser/extensions/global_shortcut_listener_aurax11.cc', + 'browser/extensions/global_shortcut_listener_aurax11.h', + 'browser/extensions/global_shortcut_listener_chromeos.cc', + 'browser/extensions/global_shortcut_listener_chromeos.h', + 'browser/extensions/global_shortcut_listener_gtk.cc', + 'browser/extensions/global_shortcut_listener_gtk.h', + 'browser/extensions/global_shortcut_listener_mac.cc', + 'browser/extensions/global_shortcut_listener_mac.h', + 'browser/extensions/global_shortcut_listener_win.cc', + 'browser/extensions/global_shortcut_listener_win.h', 'browser/extensions/image_loader.cc', 'browser/extensions/image_loader.h', 'browser/extensions/image_loader_factory.cc', diff --git a/chrome/common/extensions/api/_manifest_features.json b/chrome/common/extensions/api/_manifest_features.json index 3393d34..98a124d 100644 --- a/chrome/common/extensions/api/_manifest_features.json +++ b/chrome/common/extensions/api/_manifest_features.json @@ -80,6 +80,11 @@ "extension_types": ["extension"], "min_manifest_version": 2 }, + "commands.global": { + "channel": "dev", + "extension_types": ["extension"], + "min_manifest_version": 2 + }, "content_pack": { "channel": "dev", "extension_types": ["extension"] diff --git a/chrome/common/extensions/api/commands/commands_handler.cc b/chrome/common/extensions/api/commands/commands_handler.cc index 731af99..852f152 100644 --- a/chrome/common/extensions/api/commands/commands_handler.cc +++ b/chrome/common/extensions/api/commands/commands_handler.cc @@ -140,7 +140,8 @@ void CommandsHandler::MaybeSetBrowserActionDefault(const Extension* extension, info->browser_action_command.reset( new Command(manifest_values::kBrowserActionCommandEvent, string16(), - std::string())); + std::string(), + false)); } } diff --git a/chrome/common/extensions/command.cc b/chrome/common/extensions/command.cc index 038705b..6e87d7a 100644 --- a/chrome/common/extensions/command.cc +++ b/chrome/common/extensions/command.cc @@ -250,13 +250,15 @@ std::string NormalizeShortcutSuggestion(const std::string& suggestion, } // namespace -Command::Command() {} +Command::Command() : global_(false) {} Command::Command(const std::string& command_name, const string16& description, - const std::string& accelerator) + const std::string& accelerator, + bool global) : command_name_(command_name), - description_(description) { + description_(description), + global_(global) { string16 error; accelerator_ = ParseImpl(accelerator, CommandPlatform(), 0, IsNamedCommand(command_name), &error); @@ -432,6 +434,10 @@ bool Command::Parse(const base::DictionaryValue* command, } } + // Check if this is a global or a regular shortcut. + bool global = false; + command->GetBoolean(keys::kGlobal, &global); + // Normalize the suggestions. for (SuggestionMap::iterator iter = suggestions.begin(); iter != suggestions.end(); ++iter) { @@ -494,6 +500,7 @@ bool Command::Parse(const base::DictionaryValue* command, accelerator_ = accelerator; command_name_ = command_name; description_ = description; + global_ = global; } } return true; diff --git a/chrome/common/extensions/command.h b/chrome/common/extensions/command.h index 963d26b..97107d4 100644 --- a/chrome/common/extensions/command.h +++ b/chrome/common/extensions/command.h @@ -26,7 +26,8 @@ class Command { Command(); Command(const std::string& command_name, const string16& description, - const std::string& accelerator); + const std::string& accelerator, + bool global); ~Command(); // The platform value for the Command. @@ -56,6 +57,7 @@ class Command { const std::string& command_name() const { return command_name_; } const ui::Accelerator& accelerator() const { return accelerator_; } const string16& description() const { return description_; } + bool global() const { return global_; } // Setter: void set_accelerator(ui::Accelerator accelerator) { @@ -66,6 +68,7 @@ class Command { std::string command_name_; ui::Accelerator accelerator_; string16 description_; + bool global_; }; // A mapping of command name (std::string) to a command object. diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc index dccf628..c03e3f4 100644 --- a/extensions/common/manifest_constants.cc +++ b/extensions/common/manifest_constants.cc @@ -45,6 +45,7 @@ const char kFileHandlers[] = "file_handlers"; const char kFileHandlerExtensions[] = "extensions"; const char kFileHandlerTitle[] = "title"; const char kFileHandlerTypes[] = "types"; +const char kGlobal[] = "global"; const char kHomepageURL[] = "homepage_url"; const char kIcons[] = "icons"; const char kId[] = "id"; diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h index ca70780..bf78fa5 100644 --- a/extensions/common/manifest_constants.h +++ b/extensions/common/manifest_constants.h @@ -47,6 +47,7 @@ extern const char kFileHandlerTitle[]; extern const char kFileHandlerTypes[]; extern const char kFileFilters[]; extern const char kFileBrowserHandlers[]; +extern const char kGlobal[]; extern const char kMediaGalleriesHandlers[]; extern const char kHomepageURL[]; extern const char kIcons[]; diff --git a/ui/gfx/win/singleton_hwnd.cc b/ui/gfx/win/singleton_hwnd.cc index 37993e8..2de6a8b 100644 --- a/ui/gfx/win/singleton_hwnd.cc +++ b/ui/gfx/win/singleton_hwnd.cc @@ -15,16 +15,6 @@ SingletonHwnd* SingletonHwnd::GetInstance() { } void SingletonHwnd::AddObserver(Observer* observer) { - if (!hwnd()) { - if (!base::MessageLoop::current() || - base::MessageLoop::current()->type() != base::MessageLoop::TYPE_UI) { - // Creating this window in (e.g.) a renderer inhibits shutdown on - // Windows. See http://crbug.com/230122 and http://crbug.com/236039. - DLOG(ERROR) << "Cannot create windows on non-UI thread!"; - return; - } - WindowImpl::Init(NULL, Rect()); - } observer_list_.AddObserver(observer); } @@ -47,6 +37,14 @@ BOOL SingletonHwnd::ProcessWindowMessage(HWND window, } SingletonHwnd::SingletonHwnd() { + if (!base::MessageLoop::current() || + base::MessageLoop::current()->type() != base::MessageLoop::TYPE_UI) { + // Creating this window in (e.g.) a renderer inhibits shutdown on + // Windows. See http://crbug.com/230122 and http://crbug.com/236039. + DLOG(ERROR) << "Cannot create windows on non-UI thread!"; + return; + } + WindowImpl::Init(NULL, Rect()); } SingletonHwnd::~SingletonHwnd() { diff --git a/ui/gfx/win/singleton_hwnd.h b/ui/gfx/win/singleton_hwnd.h index 96af6e7..50fb9fd 100644 --- a/ui/gfx/win/singleton_hwnd.h +++ b/ui/gfx/win/singleton_hwnd.h @@ -19,7 +19,7 @@ namespace gfx { // Singleton message-only HWND that allows interested clients to receive WM_* // notifications. -class SingletonHwnd : public WindowImpl { +class GFX_EXPORT SingletonHwnd : public WindowImpl { public: static SingletonHwnd* GetInstance(); |