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/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 
     29 namespace extensions {
     30 
     31 typedef 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.
     36 void 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)
     77 using base::ScopedCFTypeRef;
     78 
     79 void 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.
    125 IN_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 
    191 IN_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