Home | History | Annotate | Download | only in extensions
      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 
     15 GlobalShortcutListener::GlobalShortcutListener()
     16     : shortcut_handling_suspended_(false) {
     17   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     18 }
     19 
     20 GlobalShortcutListener::~GlobalShortcutListener() {
     21   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     22   DCHECK(accelerator_map_.empty());  // Make sure we've cleaned up.
     23 }
     24 
     25 bool 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 
     50 void 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 
     68 void 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 
     84 void 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 
    105 bool GlobalShortcutListener::IsShortcutHandlingSuspended() const {
    106   return shortcut_handling_suspended_;
    107 }
    108 
    109 void 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