summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/extensions/api/commands/command_service.cc31
-rw-r--r--chrome/browser/extensions/api/commands/command_service.h17
-rw-r--r--chrome/browser/extensions/api/commands/commands.cc1
-rw-r--r--chrome/browser/extensions/extension_commands_global_registry.cc107
-rw-r--r--chrome/browser/extensions/extension_commands_global_registry.h76
-rw-r--r--chrome/browser/extensions/extension_keybinding_registry.cc30
-rw-r--r--chrome/browser/extensions/extension_keybinding_registry.h21
-rw-r--r--chrome/browser/extensions/global_shortcut_listener.cc62
-rw-r--r--chrome/browser/extensions/global_shortcut_listener.h65
-rw-r--r--chrome/browser/extensions/global_shortcut_listener_aurax11.cc75
-rw-r--r--chrome/browser/extensions/global_shortcut_listener_aurax11.h46
-rw-r--r--chrome/browser/extensions/global_shortcut_listener_chromeos.cc75
-rw-r--r--chrome/browser/extensions/global_shortcut_listener_chromeos.h48
-rw-r--r--chrome/browser/extensions/global_shortcut_listener_gtk.cc75
-rw-r--r--chrome/browser/extensions/global_shortcut_listener_gtk.h46
-rw-r--r--chrome/browser/extensions/global_shortcut_listener_mac.cc75
-rw-r--r--chrome/browser/extensions/global_shortcut_listener_mac.h46
-rw-r--r--chrome/browser/extensions/global_shortcut_listener_win.cc112
-rw-r--r--chrome/browser/extensions/global_shortcut_listener_win.h59
-rw-r--r--chrome/browser/ui/cocoa/extensions/extension_keybinding_registry_cocoa.h13
-rw-r--r--chrome/browser/ui/cocoa/extensions/extension_keybinding_registry_cocoa.mm12
-rw-r--r--chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.cc31
-rw-r--r--chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.h13
-rw-r--r--chrome/browser/ui/views/browser_actions_container.cc2
-rw-r--r--chrome/browser/ui/views/extensions/extension_keybinding_registry_views.cc27
-rw-r--r--chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h12
-rw-r--r--chrome/browser/ui/webui/extensions/command_handler.cc1
-rw-r--r--chrome/chrome_browser_extensions.gypi14
-rw-r--r--chrome/common/extensions/api/_manifest_features.json5
-rw-r--r--chrome/common/extensions/api/commands/commands_handler.cc3
-rw-r--r--chrome/common/extensions/command.cc13
-rw-r--r--chrome/common/extensions/command.h5
-rw-r--r--extensions/common/manifest_constants.cc1
-rw-r--r--extensions/common/manifest_constants.h1
-rw-r--r--ui/gfx/win/singleton_hwnd.cc18
-rw-r--r--ui/gfx/win/singleton_hwnd.h2
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();