diff options
author | junov@chromium.org <junov@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-16 22:13:39 +0000 |
---|---|---|
committer | junov@chromium.org <junov@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-16 22:13:39 +0000 |
commit | f5fae80ad48ef29224f1daf552d85830e64e8749 (patch) | |
tree | 8d7693d6fa0cfd3f34b45e595ae201527d757b1e | |
parent | bbf80170f86fdae2185dc86f0b48950880af4146 (diff) | |
download | chromium_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
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', |