summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjunov@chromium.org <junov@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-16 22:13:39 +0000
committerjunov@chromium.org <junov@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-16 22:13:39 +0000
commitf5fae80ad48ef29224f1daf552d85830e64e8749 (patch)
tree8d7693d6fa0cfd3f34b45e595ae201527d757b1e
parentbbf80170f86fdae2185dc86f0b48950880af4146 (diff)
downloadchromium_src-f5fae80ad48ef29224f1daf552d85830e64e8749.zip
chromium_src-f5fae80ad48ef29224f1daf552d85830e64e8749.tar.gz
chromium_src-f5fae80ad48ef29224f1daf552d85830e64e8749.tar.bz2
Revert 240915 "Revert 240681 "Revert 240536 "Mac-specific implem..."
Reason for reverting the revert of the revert (we must go deeper): Test falkiness returned after original revert was reverted. So we have pretty strong evidence that this CL does in fact cause EventUtilsTest.TestEventFlagsFromNSEvent and EventUtilsTest.TestWindowOpenDispositionFromNSEvent to be flaky on Mac. > Revert 240681 "Revert 240536 "Mac-specific implementation of the..." > > Reverting the revert: observed failures may have been red herrings. > > > Revert 240536 "Mac-specific implementation of the GlobalShortcut..." > > > > Reason for revert: patch suspected of causing flakiness on > > tests EventUtilsTest.TestEventFlagsFromNSEvent and > > EventUtilsTest.TestWindowOpenDispositionFromNSEvent on mac10.6 and > > mac 10.8 (ui_unit_tests on chromium.webkit waterfall) > > > > > 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. > > > > > > Adds two things: > > > 1. Intercepts media keys. Uses an event tap for intercepting media keys > > > (PlayPause, NextTrack, PreviousTrack). > > > 2. Binds keyboard shortcuts (hot keys). Carbon RegisterEventHotKey API for > > > binding to non-media key global hot keys (eg. Command-Shift-1). > > > > > > Also added Mac tests. > > > > > > BUG=302437 > > > > > > Review URL: https://codereview.chromium.org/60353008 > > > > TBR=smus@chromium.org > > > > Review URL: https://codereview.chromium.org/115323003 > > TBR=smus@chromium.org > > Review URL: https://codereview.chromium.org/99133021 TBR=junov@chromium.org Review URL: https://codereview.chromium.org/111643010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@241044 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/api/commands/command_service.cc7
-rw-r--r--chrome/browser/extensions/extension_commands_global_registry_apitest.cc58
-rw-r--r--chrome/browser/extensions/global_shortcut_listener_mac.cc75
-rw-r--r--chrome/browser/extensions/global_shortcut_listener_mac.h72
-rw-r--r--chrome/browser/extensions/global_shortcut_listener_mac.mm384
-rw-r--r--chrome/chrome_browser_extensions.gypi2
6 files changed, 83 insertions, 515 deletions
diff --git a/chrome/browser/extensions/api/commands/command_service.cc b/chrome/browser/extensions/api/commands/command_service.cc
index 99993bd..37f51b3 100644
--- a/chrome/browser/extensions/api/commands/command_service.cc
+++ b/chrome/browser/extensions/api/commands/command_service.cc
@@ -79,17 +79,10 @@ bool InitialBindingsHaveBeenAssigned(
}
bool IsWhitelistedGlobalShortcut(const extensions::Command& command) {
- // Non-global shortcuts are always allowed.
if (!command.global())
return true;
- // Global shortcuts must be (Ctrl|Command)-Shift-[0-9].
-#if defined OS_MACOSX
- if (!command.accelerator().IsCmdDown())
- return false;
-#else
if (!command.accelerator().IsCtrlDown())
return false;
-#endif
if (!command.accelerator().IsShiftDown())
return false;
return (command.accelerator().key_code() >= ui::VKEY_0 &&
diff --git a/chrome/browser/extensions/extension_commands_global_registry_apitest.cc b/chrome/browser/extensions/extension_commands_global_registry_apitest.cc
index 14fdb01..5d45810 100644
--- a/chrome/browser/extensions/extension_commands_global_registry_apitest.cc
+++ b/chrome/browser/extensions/extension_commands_global_registry_apitest.cc
@@ -20,12 +20,6 @@
#include "ui/gfx/x/x11_types.h"
#endif
-#if defined(OS_MACOSX)
-#include <Carbon/Carbon.h>
-
-#include "base/mac/scoped_cftyperef.h"
-#endif
-
namespace extensions {
typedef ExtensionApiTest GlobalCommandsApiTest;
@@ -73,44 +67,7 @@ void SendNativeKeyEventToXDisplay(ui::KeyboardCode key,
}
#endif // OS_LINUX
-#if defined(OS_MACOSX)
-using base::ScopedCFTypeRef;
-
-void SendNativeCommandShift(int key_code) {
- CGEventSourceRef event_source =
- CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
- CGEventTapLocation event_tap_location = kCGHIDEventTap;
-
- // Create the keyboard press events.
- ScopedCFTypeRef<CGEventRef> command_down(CGEventCreateKeyboardEvent(
- event_source, kVK_Command, true));
- ScopedCFTypeRef<CGEventRef> shift_down(CGEventCreateKeyboardEvent(
- event_source, kVK_Shift, true));
- ScopedCFTypeRef<CGEventRef> key_down(CGEventCreateKeyboardEvent(
- event_source, key_code, true));
- CGEventSetFlags(key_down, kCGEventFlagMaskCommand | kCGEventFlagMaskShift);
-
- // Create the keyboard release events.
- ScopedCFTypeRef<CGEventRef> command_up(CGEventCreateKeyboardEvent(
- event_source, kVK_Command, false));
- ScopedCFTypeRef<CGEventRef> shift_up(CGEventCreateKeyboardEvent(
- event_source, kVK_Shift, false));
- ScopedCFTypeRef<CGEventRef> key_up(CGEventCreateKeyboardEvent(
- event_source, key_code, false));
- CGEventSetFlags(key_up, kCGEventFlagMaskCommand | kCGEventFlagMaskShift);
-
- // Post all of the events.
- CGEventPost(event_tap_location, command_down);
- CGEventPost(event_tap_location, shift_down);
- CGEventPost(event_tap_location, key_down);
- CGEventPost(event_tap_location, key_up);
- CGEventPost(event_tap_location, shift_up);
- CGEventPost(event_tap_location, command_up);
-}
-#endif
-
-#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS)) \
- || defined(OS_MACOSX)
+#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS))
// The feature is only fully implemented on Windows and Linux, other platforms
// coming.
#define MAYBE_GlobalCommand GlobalCommand
@@ -131,7 +88,7 @@ IN_PROC_BROWSER_TEST_F(GlobalCommandsApiTest, MAYBE_GlobalCommand) {
ASSERT_TRUE(RunExtensionTest("keybinding/global")) << message_;
ASSERT_TRUE(catcher.GetNextResult());
-#if defined(OS_WIN)
+#if !defined(OS_LINUX)
// Our infrastructure for sending keys expects a browser to send them to, but
// to properly test global shortcuts you need to send them to another target.
// So, create an incognito browser to use as a target to send the shortcuts
@@ -152,7 +109,7 @@ IN_PROC_BROWSER_TEST_F(GlobalCommandsApiTest, MAYBE_GlobalCommand) {
// Activate the shortcut (Ctrl+Shift+9). This should have an effect.
ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
incognito_browser, ui::VKEY_9, true, true, false, false));
-#elif defined(OS_LINUX)
+#else
// Create an incognito browser to capture the focus.
CreateIncognitoBrowser();
@@ -164,14 +121,6 @@ IN_PROC_BROWSER_TEST_F(GlobalCommandsApiTest, MAYBE_GlobalCommand) {
SendNativeKeyEventToXDisplay(ui::VKEY_1, true, true, false);
SendNativeKeyEventToXDisplay(ui::VKEY_A, true, true, false);
SendNativeKeyEventToXDisplay(ui::VKEY_9, true, true, false);
-#elif defined(OS_MACOSX)
- // Create an incognito browser to capture the focus.
- CreateIncognitoBrowser();
-
- // Send some native mac key events.
- SendNativeCommandShift(kVK_ANSI_1);
- SendNativeCommandShift(kVK_ANSI_A);
- SendNativeCommandShift(kVK_ANSI_9);
#endif
// If this fails, it might be because the global shortcut failed to work,
@@ -182,7 +131,6 @@ IN_PROC_BROWSER_TEST_F(GlobalCommandsApiTest, MAYBE_GlobalCommand) {
#if defined(OS_WIN)
// The feature is only fully implemented on Windows, other platforms coming.
-// TODO(smus): On mac, SendKeyPress must first support media keys.
#define MAYBE_GlobalDuplicatedMediaKey GlobalDuplicatedMediaKey
#else
#define MAYBE_GlobalDuplicatedMediaKey DISABLED_GlobalDuplicatedMediaKey
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
index ddc191e..8c1b5db 100644
--- a/chrome/browser/extensions/global_shortcut_listener_mac.h
+++ b/chrome/browser/extensions/global_shortcut_listener_mac.h
@@ -5,43 +5,26 @@
#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"
-#include <Carbon/Carbon.h>
-#include <CoreFoundation/CoreFoundation.h>
-
-#include <map>
-
-#include "base/mac/scoped_nsobject.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.
-//
-// This class does two things:
-// 1. Intercepts media keys. Uses an event tap for intercepting media keys
-// (PlayPause, NextTrack, PreviousTrack).
-// 2. Binds keyboard shortcuts (hot keys). Carbon RegisterEventHotKey API for
-// binding to non-media key global hot keys (eg. Command-Shift-1).
+// TODO(finnur/smus): Implement this class.
class GlobalShortcutListenerMac : public GlobalShortcutListener {
public:
- GlobalShortcutListenerMac();
virtual ~GlobalShortcutListenerMac();
virtual void StartListening() OVERRIDE;
virtual void StopListening() OVERRIDE;
private:
- typedef int KeyId;
- typedef std::map<ui::Accelerator, KeyId> AcceleratorIdMap;
- typedef std::map<KeyId, ui::Accelerator> IdAcceleratorMap;
- typedef std::map<KeyId, EventHotKeyRef> IdHotKeyRefMap;
+ friend struct base::DefaultLazyInstanceTraits<GlobalShortcutListenerMac>;
- // Keyboard event callbacks.
- void OnHotKeyEvent(EventHotKeyID hot_key_id);
- bool OnMediaKeyEvent(int key_code);
+ GlobalShortcutListenerMac();
// Register an |accelerator| with the particular |observer|.
virtual void RegisterAccelerator(
@@ -52,56 +35,9 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener {
const ui::Accelerator& accelerator,
GlobalShortcutListener::Observer* observer) OVERRIDE;
- // Mac-specific functions for registering hot keys with modifiers.
- void RegisterHotKey(const ui::Accelerator& accelerator, KeyId hot_key_id);
- void UnregisterHotKey(const ui::Accelerator& accelerator);
-
- // Enable and disable the media key event tap.
- void StartWatchingMediaKeys();
- void StopWatchingMediaKeys();
-
- // Enable and disable the hot key event handler.
- void StartWatchingHotKeys();
- void StopWatchingHotKeys();
-
- // Whether or not any media keys are currently registered.
- bool IsAnyMediaKeyRegistered();
-
- // Whether or not any hot keys are currently registered.
- bool IsAnyHotKeyRegistered();
-
- // The callback for when an event tap happens.
- static CGEventRef EventTapCallback(
- CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon);
-
- // The callback for when a hot key event happens.
- static OSStatus HotKeyHandler(
- EventHandlerCallRef next_handler, EventRef event, void* user_data);
-
// Whether this object is listening for global shortcuts.
bool is_listening_;
- // The hotkey identifier for the next global shortcut that is added.
- KeyId hot_key_id_;
-
- // A map of all hotkeys (media keys and shortcuts) mapping to their
- // corresponding hotkey IDs. For quickly finding if an accelerator is
- // registered.
- AcceleratorIdMap accelerator_ids_;
-
- // The inverse map for quickly looking up accelerators by hotkey id.
- IdAcceleratorMap id_accelerators_;
-
- // Keyboard shortcut IDs to hotkeys map for unregistration.
- IdHotKeyRefMap id_hot_key_refs_;
-
- // Event tap for intercepting mac media keys.
- CFMachPortRef event_tap_;
- CFRunLoopSourceRef event_tap_source_;
-
- // Event handler for keyboard shortcut hot keys.
- EventHandlerRef event_handler_;
-
DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerMac);
};
diff --git a/chrome/browser/extensions/global_shortcut_listener_mac.mm b/chrome/browser/extensions/global_shortcut_listener_mac.mm
deleted file mode 100644
index ea36483..0000000
--- a/chrome/browser/extensions/global_shortcut_listener_mac.mm
+++ /dev/null
@@ -1,384 +0,0 @@
-// Copyright 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 <ApplicationServices/ApplicationServices.h>
-#import <Cocoa/Cocoa.h>
-#include <IOKit/hidsystem/ev_keymap.h>
-
-#import "base/mac/foundation_util.h"
-#include "chrome/browser/extensions/api/commands/command_service.h"
-#include "content/public/browser/browser_thread.h"
-#include "ui/base/accelerators/accelerator.h"
-#include "ui/events/event.h"
-#import "ui/events/keycodes/keyboard_code_conversion_mac.h"
-
-using content::BrowserThread;
-using extensions::GlobalShortcutListenerMac;
-
-namespace {
-
-// The media keys subtype. No official docs found, but widely known.
-// http://lists.apple.com/archives/cocoa-dev/2007/Aug/msg00499.html
-const int kSystemDefinedEventMediaKeysSubtype = 8;
-
-ui::KeyboardCode MediaKeyCodeToKeyboardCode(int key_code) {
- switch (key_code) {
- case NX_KEYTYPE_PLAY:
- return ui::VKEY_MEDIA_PLAY_PAUSE;
- case NX_KEYTYPE_PREVIOUS:
- case NX_KEYTYPE_REWIND:
- return ui::VKEY_MEDIA_PREV_TRACK;
- case NX_KEYTYPE_NEXT:
- case NX_KEYTYPE_FAST:
- return ui::VKEY_MEDIA_NEXT_TRACK;
- }
- return ui::VKEY_UNKNOWN;
-}
-
-} // namespace
-
-namespace extensions {
-
-// static
-GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- static GlobalShortcutListenerMac *instance =
- new GlobalShortcutListenerMac();
- return instance;
-}
-
-GlobalShortcutListenerMac::GlobalShortcutListenerMac()
- : is_listening_(false),
- hot_key_id_(0),
- event_tap_(NULL),
- event_tap_source_(NULL),
- event_handler_(NULL) {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-}
-
-GlobalShortcutListenerMac::~GlobalShortcutListenerMac() {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- // By this point, UnregisterAccelerator should have been called for all
- // keyboard shortcuts. Still we should clean up.
- if (is_listening_)
- StopListening();
-
- // If keys are still registered, make sure we stop the tap. Again, this
- // should never happen.
- if (IsAnyMediaKeyRegistered())
- StopWatchingMediaKeys();
-
- if (IsAnyHotKeyRegistered())
- StopWatchingHotKeys();
-}
-
-void GlobalShortcutListenerMac::StartListening() {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- DCHECK(!accelerator_ids_.empty());
- DCHECK(!id_accelerators_.empty());
- DCHECK(!is_listening_);
-
- is_listening_ = true;
-}
-
-void GlobalShortcutListenerMac::StopListening() {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- DCHECK(accelerator_ids_.empty()); // Make sure the set is clean.
- DCHECK(id_accelerators_.empty());
- DCHECK(is_listening_);
-
- is_listening_ = false;
-}
-
-void GlobalShortcutListenerMac::OnHotKeyEvent(EventHotKeyID hot_key_id) {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- // This hot key should be registered.
- DCHECK(id_accelerators_.find(hot_key_id.id) != id_accelerators_.end());
- // Look up the accelerator based on this hot key ID.
- const ui::Accelerator& accelerator = id_accelerators_[hot_key_id.id];
- NotifyKeyPressed(accelerator);
-}
-
-bool GlobalShortcutListenerMac::OnMediaKeyEvent(int media_key_code) {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- ui::KeyboardCode key_code = MediaKeyCodeToKeyboardCode(media_key_code);
- // Create an accelerator corresponding to the keyCode.
- ui::Accelerator accelerator(key_code, 0);
- // Look for a match with a bound hot_key.
- if (accelerator_ids_.find(accelerator) != accelerator_ids_.end()) {
- // If matched, callback to the event handling system.
- NotifyKeyPressed(accelerator);
- return true;
- }
- return false;
-}
-
-void GlobalShortcutListenerMac::RegisterAccelerator(
- const ui::Accelerator& accelerator,
- GlobalShortcutListener::Observer* observer) {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (accelerator_ids_.find(accelerator) != accelerator_ids_.end()) {
- // The shortcut has already been registered. Some shortcuts, such as
- // MediaKeys can have multiple targets, all keyed off of the same
- // accelerator.
- return;
- }
-
- if (CommandService::IsMediaKey(accelerator)) {
- if (!IsAnyMediaKeyRegistered()) {
- // If this is the first media key registered, start the event tap.
- StartWatchingMediaKeys();
- }
- } else {
- // Register hot_key if they are non-media keyboard shortcuts.
- RegisterHotKey(accelerator, hot_key_id_);
- if (!IsAnyHotKeyRegistered()) {
- StartWatchingHotKeys();
- }
- }
-
- // Store the hotkey-ID mappings we will need for lookup later.
- id_accelerators_[hot_key_id_] = accelerator;
- accelerator_ids_[accelerator] = hot_key_id_;
- ++hot_key_id_;
- GlobalShortcutListener::RegisterAccelerator(accelerator, observer);
-}
-
-void GlobalShortcutListenerMac::UnregisterAccelerator(
- const ui::Accelerator& accelerator,
- GlobalShortcutListener::Observer* observer) {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- // 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.
- AcceleratorIdMap::iterator it = accelerator_ids_.find(accelerator);
- if (it == accelerator_ids_.end())
- return;
-
- // Unregister the hot_key if it's a keyboard shortcut.
- if (!CommandService::IsMediaKey(accelerator))
- UnregisterHotKey(accelerator);
-
- // Remove hot_key from the mappings.
- KeyId key_id = accelerator_ids_[accelerator];
- id_accelerators_.erase(key_id);
- accelerator_ids_.erase(accelerator);
- GlobalShortcutListener::UnregisterAccelerator(accelerator, observer);
-
- if (CommandService::IsMediaKey(accelerator)) {
- // If we unregistered a media key, and now no media keys are registered,
- // stop the media key tap.
- if (!IsAnyMediaKeyRegistered())
- StopWatchingMediaKeys();
- } else {
- // If we unregistered a hot key, and no more hot keys are registered, remove
- // the hot key handler.
- if (!IsAnyHotKeyRegistered()) {
- StopWatchingHotKeys();
- }
- }
-}
-
-void GlobalShortcutListenerMac::RegisterHotKey(
- const ui::Accelerator& accelerator, KeyId hot_key_id) {
- EventHotKeyID event_hot_key_id;
-
- // Signature uniquely identifies the application that owns this hot_key.
- event_hot_key_id.signature = base::mac::CreatorCodeForApplication();
- event_hot_key_id.id = hot_key_id;
-
- // Translate ui::Accelerator modifiers to cmdKey, altKey, etc.
- int modifiers = 0;
- modifiers |= (accelerator.IsShiftDown() ? shiftKey : 0);
- modifiers |= (accelerator.IsCtrlDown() ? controlKey : 0);
- modifiers |= (accelerator.IsAltDown() ? optionKey : 0);
- modifiers |= (accelerator.IsCmdDown() ? cmdKey : 0);
-
- int key_code = ui::MacKeyCodeForWindowsKeyCode(accelerator.key_code(), 0,
- NULL, NULL);
-
- // Register the event hot key.
- EventHotKeyRef hot_key_ref;
- RegisterEventHotKey(key_code, modifiers, event_hot_key_id,
- GetApplicationEventTarget(), 0, &hot_key_ref);
-
- id_hot_key_refs_[hot_key_id] = hot_key_ref;
-}
-
-void GlobalShortcutListenerMac::UnregisterHotKey(
- const ui::Accelerator& accelerator) {
- // Ensure this accelerator is already registered.
- DCHECK(accelerator_ids_.find(accelerator) != accelerator_ids_.end());
- // Get the ref corresponding to this accelerator.
- KeyId key_id = accelerator_ids_[accelerator];
- EventHotKeyRef ref = id_hot_key_refs_[key_id];
- // Unregister the event hot key.
- UnregisterEventHotKey(ref);
-
- // Remove the event from the mapping.
- id_hot_key_refs_.erase(key_id);
-}
-
-void GlobalShortcutListenerMac::StartWatchingMediaKeys() {
- // Make sure there's no existing event tap.
- DCHECK(event_tap_ == NULL);
- DCHECK(event_tap_source_ == NULL);
-
- // Add an event tap to intercept the system defined media key events.
- event_tap_ = CGEventTapCreate(kCGSessionEventTap,
- kCGHeadInsertEventTap,
- kCGEventTapOptionDefault,
- CGEventMaskBit(NX_SYSDEFINED),
- EventTapCallback,
- this);
- if (event_tap_ == NULL) {
- LOG(ERROR) << "Error: failed to create event tap.";
- return;
- }
-
- event_tap_source_ = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault,
- event_tap_, 0);
- if (event_tap_source_ == NULL) {
- LOG(ERROR) << "Error: failed to create new run loop source.";
- return;
- }
-
- CFRunLoopAddSource(CFRunLoopGetCurrent(), event_tap_source_,
- kCFRunLoopCommonModes);
-}
-
-void GlobalShortcutListenerMac::StopWatchingMediaKeys() {
- CFRunLoopRemoveSource(CFRunLoopGetCurrent(), event_tap_source_,
- kCFRunLoopCommonModes);
- // Ensure both event tap and source are initialized.
- DCHECK(event_tap_ != NULL);
- DCHECK(event_tap_source_ != NULL);
-
- // Invalidate the event tap.
- CFMachPortInvalidate(event_tap_);
- CFRelease(event_tap_);
- event_tap_ = NULL;
-
- // Release the event tap source.
- CFRelease(event_tap_source_);
- event_tap_source_ = NULL;
-}
-
-void GlobalShortcutListenerMac::StartWatchingHotKeys() {
- DCHECK(!event_handler_);
- EventHandlerUPP hot_key_function = NewEventHandlerUPP(HotKeyHandler);
- EventTypeSpec event_type;
- event_type.eventClass = kEventClassKeyboard;
- event_type.eventKind = kEventHotKeyPressed;
- InstallApplicationEventHandler(
- hot_key_function, 1, &event_type, this, &event_handler_);
-}
-
-void GlobalShortcutListenerMac::StopWatchingHotKeys() {
- DCHECK(event_handler_);
- RemoveEventHandler(event_handler_);
- event_handler_ = NULL;
-}
-
-bool GlobalShortcutListenerMac::IsAnyMediaKeyRegistered() {
- // Iterate through registered accelerators, looking for media keys.
- AcceleratorIdMap::iterator it;
- for (it = accelerator_ids_.begin(); it != accelerator_ids_.end(); ++it) {
- if (CommandService::IsMediaKey(it->first))
- return true;
- }
- return false;
-}
-
-bool GlobalShortcutListenerMac::IsAnyHotKeyRegistered() {
- AcceleratorIdMap::iterator it;
- for (it = accelerator_ids_.begin(); it != accelerator_ids_.end(); ++it) {
- if (!CommandService::IsMediaKey(it->first))
- return true;
- }
- return false;
-}
-
-// Processed events should propagate if they aren't handled by any listeners.
-// For events that don't matter, this handler should return as quickly as
-// possible.
-// Returning event causes the event to propagate to other applications.
-// Returning NULL prevents the event from propagating.
-// static
-CGEventRef GlobalShortcutListenerMac::EventTapCallback(
- CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon) {
- GlobalShortcutListenerMac* shortcut_listener =
- static_cast<GlobalShortcutListenerMac*>(refcon);
-
- // Handle the timeout case by re-enabling the tap.
- if (type == kCGEventTapDisabledByTimeout) {
- CGEventTapEnable(shortcut_listener->event_tap_, TRUE);
- return event;
- }
-
- // Convert the CGEvent to an NSEvent for access to the data1 field.
- NSEvent* ns_event = [NSEvent eventWithCGEvent:event];
- if (ns_event == nil) {
- return event;
- }
-
- // Ignore events that are not system defined media keys.
- if (type != NX_SYSDEFINED ||
- [ns_event type] != NSSystemDefined ||
- [ns_event subtype] != kSystemDefinedEventMediaKeysSubtype) {
- return event;
- }
-
- NSInteger data1 = [ns_event data1];
- // Ignore media keys that aren't previous, next and play/pause.
- // Magical constants are from http://weblog.rogueamoeba.com/2007/09/29/
- int key_code = (data1 & 0xFFFF0000) >> 16;
- if (key_code != NX_KEYTYPE_PLAY && key_code != NX_KEYTYPE_NEXT &&
- key_code != NX_KEYTYPE_PREVIOUS && key_code != NX_KEYTYPE_FAST &&
- key_code != NX_KEYTYPE_REWIND) {
- return event;
- }
-
- int key_flags = data1 & 0x0000FFFF;
- bool is_key_pressed = ((key_flags & 0xFF00) >> 8) == 0xA;
-
- // If the key wasn't pressed (eg. was released), ignore this event.
- if (!is_key_pressed)
- return event;
-
- // Now we have a media key that we care about. Send it to the caller.
- bool was_handled = shortcut_listener->OnMediaKeyEvent(key_code);
-
- // Prevent event from proagating to other apps if handled by Chrome.
- if (was_handled)
- return NULL;
-
- // By default, pass the event through.
- return event;
-}
-
-// static
-OSStatus GlobalShortcutListenerMac::HotKeyHandler(
- EventHandlerCallRef next_handler, EventRef event, void* user_data) {
- // Extract the hotkey from the event.
- EventHotKeyID hot_key_id;
- OSStatus result = GetEventParameter(event, kEventParamDirectObject,
- typeEventHotKeyID, NULL, sizeof(hot_key_id), NULL, &hot_key_id);
- if (result != noErr)
- return result;
-
- GlobalShortcutListenerMac* shortcut_listener =
- static_cast<GlobalShortcutListenerMac*>(user_data);
- shortcut_listener->OnHotKeyEvent(hot_key_id);
- return noErr;
-}
-
-} // namespace extensions
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index 597f2e9..9fab561 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -800,7 +800,7 @@
'browser/extensions/global_shortcut_listener.h',
'browser/extensions/global_shortcut_listener_chromeos.cc',
'browser/extensions/global_shortcut_listener_chromeos.h',
- 'browser/extensions/global_shortcut_listener_mac.mm',
+ 'browser/extensions/global_shortcut_listener_mac.cc',
'browser/extensions/global_shortcut_listener_mac.h',
'browser/extensions/global_shortcut_listener_ozone.cc',
'browser/extensions/global_shortcut_listener_ozone.h',