18bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
28bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
38bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// found in the LICENSE file.
48bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
58bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "chrome/browser/extensions/global_shortcut_listener.h"
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/logging.h"
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
98bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "ui/base/accelerators/accelerator.h"
108bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using content::BrowserThread;
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
138bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)namespace extensions {
148bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
15effb81e5f8246d0db0270817048dc992db66e9fbBen MurdochGlobalShortcutListener::GlobalShortcutListener()
16effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    : shortcut_handling_suspended_(false) {
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
188bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
198bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
208bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)GlobalShortcutListener::~GlobalShortcutListener() {
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
228bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  DCHECK(accelerator_map_.empty());  // Make sure we've cleaned up.
238bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
248bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool GlobalShortcutListener::RegisterAccelerator(
268bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    const ui::Accelerator& accelerator, Observer* observer) {
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
28effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (IsShortcutHandlingSuspended())
29effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return false;
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
318bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  AcceleratorMap::const_iterator it = accelerator_map_.find(accelerator);
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (it != accelerator_map_.end()) {
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // The accelerator has been registered.
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!RegisterAcceleratorImpl(accelerator)) {
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // If the platform-specific registration fails, mostly likely the shortcut
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // has been registered by other native applications.
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
418bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (accelerator_map_.empty())
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    StartListening();
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  accelerator_map_[accelerator] = observer;
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return true;
488bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
498bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
508bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void GlobalShortcutListener::UnregisterAccelerator(
518bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    const ui::Accelerator& accelerator, Observer* observer) {
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
53effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (IsShortcutHandlingSuspended())
54effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return;
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
568bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  AcceleratorMap::iterator it = accelerator_map_.find(accelerator);
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // We should never get asked to unregister something that we didn't register.
588bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  DCHECK(it != accelerator_map_.end());
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // The caller should call this function with the right observer.
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(it->second == observer);
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UnregisterAcceleratorImpl(accelerator);
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  accelerator_map_.erase(it);
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (accelerator_map_.empty())
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    StopListening();
668bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
678bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void GlobalShortcutListener::UnregisterAccelerators(Observer* observer) {
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
70effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (IsShortcutHandlingSuspended())
71effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return;
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  AcceleratorMap::iterator it = accelerator_map_.begin();
74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  while (it != accelerator_map_.end()) {
75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (it->second == observer) {
76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      AcceleratorMap::iterator to_remove = it++;
77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      UnregisterAccelerator(to_remove->first, observer);
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    } else {
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      ++it;
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
84effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid GlobalShortcutListener::SetShortcutHandlingSuspended(bool suspended) {
85effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
86effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (shortcut_handling_suspended_ == suspended)
87effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return;
88effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
89effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  shortcut_handling_suspended_ = suspended;
90effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  for (AcceleratorMap::iterator it = accelerator_map_.begin();
91effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch       it != accelerator_map_.end();
92effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch       ++it) {
93effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    // On Linux, when shortcut handling is suspended we cannot simply early
94effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    // return in NotifyKeyPressed (similar to what we do for non-global
95effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    // shortcuts) because we'd eat the keyboard event thereby preventing the
96effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    // user from setting the shortcut. Therefore we must unregister while
97effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    // handling is suspended and register when handling resumes.
98effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (shortcut_handling_suspended_)
99effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      UnregisterAcceleratorImpl(it->first);
100effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    else
101effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      RegisterAcceleratorImpl(it->first);
102effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
103effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
104effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
105effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochbool GlobalShortcutListener::IsShortcutHandlingSuspended() const {
106effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return shortcut_handling_suspended_;
107effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
108effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
1098bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void GlobalShortcutListener::NotifyKeyPressed(
1108bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    const ui::Accelerator& accelerator) {
1118bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  AcceleratorMap::iterator iter = accelerator_map_.find(accelerator);
1128bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (iter == accelerator_map_.end()) {
1138bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    // This should never occur, because if it does, we have failed to unregister
1148bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    // or failed to clean up the map after unregistering the shortcut.
1158bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    NOTREACHED();
1168bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    return;  // No-one is listening to this key.
1178bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
1188bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  iter->second->OnKeyPressed(accelerator);
1208bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
1218bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1228bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}  // namespace extensions
123