1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/extensions/global_shortcut_listener.h" 6 7 #include "base/logging.h" 8 #include "content/public/browser/browser_thread.h" 9 #include "ui/base/accelerators/accelerator.h" 10 11 using content::BrowserThread; 12 13 namespace extensions { 14 GlobalShortcutListener()15GlobalShortcutListener::GlobalShortcutListener() 16 : shortcut_handling_suspended_(false) { 17 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 18 } 19 ~GlobalShortcutListener()20GlobalShortcutListener::~GlobalShortcutListener() { 21 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 22 DCHECK(accelerator_map_.empty()); // Make sure we've cleaned up. 23 } 24 RegisterAccelerator(const ui::Accelerator & accelerator,Observer * observer)25bool GlobalShortcutListener::RegisterAccelerator( 26 const ui::Accelerator& accelerator, Observer* observer) { 27 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 28 if (IsShortcutHandlingSuspended()) 29 return false; 30 31 AcceleratorMap::const_iterator it = accelerator_map_.find(accelerator); 32 if (it != accelerator_map_.end()) { 33 // The accelerator has been registered. 34 return false; 35 } 36 37 if (!RegisterAcceleratorImpl(accelerator)) { 38 // If the platform-specific registration fails, mostly likely the shortcut 39 // has been registered by other native applications. 40 return false; 41 } 42 43 if (accelerator_map_.empty()) 44 StartListening(); 45 46 accelerator_map_[accelerator] = observer; 47 return true; 48 } 49 UnregisterAccelerator(const ui::Accelerator & accelerator,Observer * observer)50void GlobalShortcutListener::UnregisterAccelerator( 51 const ui::Accelerator& accelerator, Observer* observer) { 52 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 53 if (IsShortcutHandlingSuspended()) 54 return; 55 56 AcceleratorMap::iterator it = accelerator_map_.find(accelerator); 57 // We should never get asked to unregister something that we didn't register. 58 DCHECK(it != accelerator_map_.end()); 59 // The caller should call this function with the right observer. 60 DCHECK(it->second == observer); 61 62 UnregisterAcceleratorImpl(accelerator); 63 accelerator_map_.erase(it); 64 if (accelerator_map_.empty()) 65 StopListening(); 66 } 67 UnregisterAccelerators(Observer * observer)68void GlobalShortcutListener::UnregisterAccelerators(Observer* observer) { 69 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 70 if (IsShortcutHandlingSuspended()) 71 return; 72 73 AcceleratorMap::iterator it = accelerator_map_.begin(); 74 while (it != accelerator_map_.end()) { 75 if (it->second == observer) { 76 AcceleratorMap::iterator to_remove = it++; 77 UnregisterAccelerator(to_remove->first, observer); 78 } else { 79 ++it; 80 } 81 } 82 } 83 SetShortcutHandlingSuspended(bool suspended)84void GlobalShortcutListener::SetShortcutHandlingSuspended(bool suspended) { 85 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 86 if (shortcut_handling_suspended_ == suspended) 87 return; 88 89 shortcut_handling_suspended_ = suspended; 90 for (AcceleratorMap::iterator it = accelerator_map_.begin(); 91 it != accelerator_map_.end(); 92 ++it) { 93 // On Linux, when shortcut handling is suspended we cannot simply early 94 // return in NotifyKeyPressed (similar to what we do for non-global 95 // shortcuts) because we'd eat the keyboard event thereby preventing the 96 // user from setting the shortcut. Therefore we must unregister while 97 // handling is suspended and register when handling resumes. 98 if (shortcut_handling_suspended_) 99 UnregisterAcceleratorImpl(it->first); 100 else 101 RegisterAcceleratorImpl(it->first); 102 } 103 } 104 IsShortcutHandlingSuspended() const105bool GlobalShortcutListener::IsShortcutHandlingSuspended() const { 106 return shortcut_handling_suspended_; 107 } 108 NotifyKeyPressed(const ui::Accelerator & accelerator)109void GlobalShortcutListener::NotifyKeyPressed( 110 const ui::Accelerator& accelerator) { 111 AcceleratorMap::iterator iter = accelerator_map_.find(accelerator); 112 if (iter == accelerator_map_.end()) { 113 // This should never occur, because if it does, we have failed to unregister 114 // or failed to clean up the map after unregistering the shortcut. 115 NOTREACHED(); 116 return; // No-one is listening to this key. 117 } 118 119 iter->second->OnKeyPressed(accelerator); 120 } 121 122 } // namespace extensions 123