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
11using content::BrowserThread;
12
13namespace extensions {
14
15GlobalShortcutListener::GlobalShortcutListener()
16    : shortcut_handling_suspended_(false) {
17  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
18}
19
20GlobalShortcutListener::~GlobalShortcutListener() {
21  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
22  DCHECK(accelerator_map_.empty());  // Make sure we've cleaned up.
23}
24
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
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
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
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
105bool GlobalShortcutListener::IsShortcutHandlingSuspended() const {
106  return shortcut_handling_suspended_;
107}
108
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