extension_commands_global_registry_apitest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/extension_apitest.h"
6#include "chrome/browser/extensions/window_controller.h"
7#include "chrome/browser/ui/browser_window.h"
8#include "chrome/browser/ui/tabs/tab_strip_model.h"
9#include "chrome/test/base/interactive_test_utils.h"
10#include "content/public/test/browser_test_utils.h"
11#include "ui/base/base_window.h"
12#include "ui/base/test/ui_controls.h"
13
14#if defined(OS_LINUX)
15#include <X11/Xlib.h>
16#include <X11/extensions/XTest.h>
17#include <X11/keysym.h>
18
19#include "ui/events/keycodes/keyboard_code_conversion_x.h"
20#include "ui/gfx/x/x11_types.h"
21#endif
22
23#if defined(OS_MACOSX)
24#include <Carbon/Carbon.h>
25
26#include "base/mac/scoped_cftyperef.h"
27#endif
28
29namespace extensions {
30
31typedef ExtensionApiTest GlobalCommandsApiTest;
32
33#if defined(OS_LINUX)
34// Send a simulated key press and release event, where |control|, |shift| or
35// |alt| indicates whether the key is struck with corresponding modifier.
36void SendNativeKeyEventToXDisplay(ui::KeyboardCode key,
37                                  bool control,
38                                  bool shift,
39                                  bool alt) {
40  Display* display = gfx::GetXDisplay();
41  KeyCode ctrl_key_code = XKeysymToKeycode(display, XK_Control_L);
42  KeyCode shift_key_code = XKeysymToKeycode(display, XK_Shift_L);
43  KeyCode alt_key_code = XKeysymToKeycode(display, XK_Alt_L);
44
45  // Release modifiers first of all to make sure this function can work as
46  // expected. For example, when |control| is false, but the status of Ctrl key
47  // is down, we will generate a keyboard event with unwanted Ctrl key.
48  XTestFakeKeyEvent(display, ctrl_key_code, False, CurrentTime);
49  XTestFakeKeyEvent(display, shift_key_code, False, CurrentTime);
50  XTestFakeKeyEvent(display, alt_key_code, False, CurrentTime);
51
52  typedef std::vector<KeyCode> KeyCodes;
53  KeyCodes key_codes;
54  if (control)
55    key_codes.push_back(ctrl_key_code);
56  if (shift)
57    key_codes.push_back(shift_key_code);
58  if (alt)
59    key_codes.push_back(alt_key_code);
60
61  key_codes.push_back(XKeysymToKeycode(display,
62                                       XKeysymForWindowsKeyCode(key, false)));
63
64  // Simulate the keys being pressed.
65  for (KeyCodes::iterator it = key_codes.begin(); it != key_codes.end(); it++)
66    XTestFakeKeyEvent(display, *it, True, CurrentTime);
67
68  // Simulate the keys being released.
69  for (KeyCodes::iterator it = key_codes.begin(); it != key_codes.end(); it++)
70    XTestFakeKeyEvent(display, *it, False, CurrentTime);
71
72  XFlush(display);
73}
74#endif  // OS_LINUX
75
76#if defined(OS_MACOSX)
77using base::ScopedCFTypeRef;
78
79void SendNativeCommandShift(int key_code) {
80  CGEventSourceRef event_source =
81      CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
82  CGEventTapLocation event_tap_location = kCGHIDEventTap;
83
84  // Create the keyboard press events.
85  ScopedCFTypeRef<CGEventRef> command_down(CGEventCreateKeyboardEvent(
86      event_source, kVK_Command, true));
87  ScopedCFTypeRef<CGEventRef> shift_down(CGEventCreateKeyboardEvent(
88      event_source, kVK_Shift, true));
89  ScopedCFTypeRef<CGEventRef> key_down(CGEventCreateKeyboardEvent(
90      event_source, key_code, true));
91  CGEventSetFlags(key_down, kCGEventFlagMaskCommand | kCGEventFlagMaskShift);
92
93  // Create the keyboard release events.
94  ScopedCFTypeRef<CGEventRef> command_up(CGEventCreateKeyboardEvent(
95      event_source, kVK_Command, false));
96  ScopedCFTypeRef<CGEventRef> shift_up(CGEventCreateKeyboardEvent(
97      event_source, kVK_Shift, false));
98  ScopedCFTypeRef<CGEventRef> key_up(CGEventCreateKeyboardEvent(
99      event_source, key_code, false));
100  CGEventSetFlags(key_up, kCGEventFlagMaskCommand | kCGEventFlagMaskShift);
101
102  // Post all of the events.
103  CGEventPost(event_tap_location, command_down);
104  CGEventPost(event_tap_location, shift_down);
105  CGEventPost(event_tap_location, key_down);
106  CGEventPost(event_tap_location, key_up);
107  CGEventPost(event_tap_location, shift_up);
108  CGEventPost(event_tap_location, command_up);
109
110  CFRelease(event_source);
111}
112#endif
113
114#if defined(OS_CHROMEOS)
115// Fully implemented everywhere except Chrome OS.
116#define MAYBE_GlobalCommand DISABLED_GlobalCommand
117#else
118#define MAYBE_GlobalCommand GlobalCommand
119#endif
120
121// Test the basics of global commands and make sure they work when Chrome
122// doesn't have focus. Also test that non-global commands are not treated as
123// global and that keys beyond Ctrl+Shift+[0..9] cannot be auto-assigned by an
124// extension.
125IN_PROC_BROWSER_TEST_F(GlobalCommandsApiTest, MAYBE_GlobalCommand) {
126  FeatureSwitch::ScopedOverride enable_global_commands(
127      FeatureSwitch::global_commands(), true);
128
129  // Load the extension in the non-incognito browser.
130  ResultCatcher catcher;
131  ASSERT_TRUE(RunExtensionTest("keybinding/global")) << message_;
132  ASSERT_TRUE(catcher.GetNextResult());
133
134#if defined(OS_WIN)
135  // Our infrastructure for sending keys expects a browser to send them to, but
136  // to properly test global shortcuts you need to send them to another target.
137  // So, create an incognito browser to use as a target to send the shortcuts
138  // to. It will ignore all of them and allow us test whether the global
139  // shortcut really is global in nature and also that the non-global shortcut
140  // is non-global.
141  Browser* incognito_browser = CreateIncognitoBrowser();
142
143  // Try to activate the non-global shortcut (Ctrl+Shift+1) and the
144  // non-assignable shortcut (Ctrl+Shift+A) by sending the keystrokes to the
145  // incognito browser. Both shortcuts should have no effect (extension is not
146  // loaded there).
147  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
148      incognito_browser, ui::VKEY_1, true, true, false, false));
149  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
150      incognito_browser, ui::VKEY_A, true, true, false, false));
151
152  // Activate the shortcut (Ctrl+Shift+9). This should have an effect.
153  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
154      incognito_browser, ui::VKEY_9, true, true, false, false));
155#elif defined(OS_LINUX)
156  // Create an incognito browser to capture the focus.
157  CreateIncognitoBrowser();
158
159  // On Linux, our infrastructure for sending keys just synthesize keyboard
160  // event and send them directly to the specified window, without notifying the
161  // X root window. It didn't work while testing global shortcut because the
162  // stuff of global shortcut on Linux need to be notified when KeyPress event
163  // is happening on X root window. So we simulate the keyboard input here.
164  SendNativeKeyEventToXDisplay(ui::VKEY_1, true, true, false);
165  SendNativeKeyEventToXDisplay(ui::VKEY_A, true, true, false);
166  SendNativeKeyEventToXDisplay(ui::VKEY_9, true, true, false);
167#elif defined(OS_MACOSX)
168  // Create an incognito browser to capture the focus.
169  CreateIncognitoBrowser();
170
171  // Send some native mac key events.
172  SendNativeCommandShift(kVK_ANSI_1);
173  SendNativeCommandShift(kVK_ANSI_A);
174  SendNativeCommandShift(kVK_ANSI_9);
175#endif
176
177  // If this fails, it might be because the global shortcut failed to work,
178  // but it might also be because the non-global shortcuts unexpectedly
179  // worked.
180  ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
181}
182
183#if defined(OS_WIN)
184// The feature is only fully implemented on Windows, other platforms coming.
185// TODO(smus): On mac, SendKeyPress must first support media keys.
186#define MAYBE_GlobalDuplicatedMediaKey GlobalDuplicatedMediaKey
187#else
188#define MAYBE_GlobalDuplicatedMediaKey DISABLED_GlobalDuplicatedMediaKey
189#endif
190
191IN_PROC_BROWSER_TEST_F(GlobalCommandsApiTest, MAYBE_GlobalDuplicatedMediaKey) {
192  FeatureSwitch::ScopedOverride enable_global_commands(
193      FeatureSwitch::global_commands(), true);
194
195  ResultCatcher catcher;
196  ASSERT_TRUE(RunExtensionTest("keybinding/global_media_keys_0")) << message_;
197  ASSERT_TRUE(catcher.GetNextResult());
198  ASSERT_TRUE(RunExtensionTest("keybinding/global_media_keys_1")) << message_;
199  ASSERT_TRUE(catcher.GetNextResult());
200
201  Browser* incognito_browser = CreateIncognitoBrowser();  // Ditto.
202  WindowController* controller =
203      incognito_browser->extension_window_controller();
204
205  ui_controls::SendKeyPress(controller->window()->GetNativeWindow(),
206                            ui::VKEY_MEDIA_NEXT_TRACK,
207                            false,
208                            false,
209                            false,
210                            false);
211
212  // We should get two success result.
213  ASSERT_TRUE(catcher.GetNextResult());
214  ASSERT_TRUE(catcher.GetNextResult());
215}
216
217}  // namespace extensions
218