1 // Copyright (c) 2009 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 <AppKit/NSEvent.h> 6 #include <Carbon/Carbon.h> 7 8 #include "chrome/browser/global_keyboard_shortcuts_mac.h" 9 10 #include "chrome/app/chrome_command_ids.h" 11 #include "testing/gtest/include/gtest/gtest.h" 12 13 TEST(GlobalKeyboardShortcuts, ShortcutsToWindowCommand) { 14 // Test that an invalid shortcut translates into an invalid command id. 15 EXPECT_EQ( 16 -1, CommandForWindowKeyboardShortcut(false, false, false, false, 0, 0)); 17 18 // Check that all known keyboard shortcuts return valid results. 19 size_t num_shortcuts = 0; 20 const KeyboardShortcutData *it = 21 GetWindowKeyboardShortcutTable(&num_shortcuts); 22 ASSERT_GT(num_shortcuts, 0U); 23 for (size_t i = 0; i < num_shortcuts; ++i, ++it) { 24 int cmd_num = CommandForWindowKeyboardShortcut( 25 it->command_key, it->shift_key, it->cntrl_key, it->opt_key, 26 it->vkey_code, it->key_char); 27 EXPECT_EQ(cmd_num, it->chrome_command); 28 } 29 30 // Test that cmd-left and backspace are not window-level commands (else they 31 // would be invoked even if e.g. the omnibox had focus, where they really 32 // should have text editing functionality). 33 EXPECT_EQ(-1, CommandForWindowKeyboardShortcut( 34 true, false, false, false, kVK_LeftArrow, 0)); 35 EXPECT_EQ(-1, CommandForWindowKeyboardShortcut( 36 false, false, false, false, kVK_Delete, 0)); 37 38 // Test that Cmd-'{' and Cmd-'}' are interpreted as IDC_SELECT_NEXT_TAB 39 // and IDC_SELECT_PREVIOUS_TAB regardless of the virtual key code values. 40 EXPECT_EQ(IDC_SELECT_NEXT_TAB, CommandForWindowKeyboardShortcut( 41 true, false, false, false, kVK_ANSI_Period, '}')); 42 EXPECT_EQ(IDC_SELECT_PREVIOUS_TAB, CommandForWindowKeyboardShortcut( 43 true, true, false, false, kVK_ANSI_Slash, '{')); 44 45 // One more test for Cmd-'{' / Alt-8 (on German keyboard layout). 46 EXPECT_EQ(IDC_SELECT_PREVIOUS_TAB, CommandForWindowKeyboardShortcut( 47 true, false, false, true, kVK_ANSI_8, '{')); 48 49 // Test that switching tabs triggers off keycodes and not characters (visible 50 // with the Italian keyboard layout). 51 EXPECT_EQ(IDC_SELECT_TAB_0, CommandForWindowKeyboardShortcut( 52 true, false, false, false, kVK_ANSI_1, '&')); 53 } 54 55 TEST(GlobalKeyboardShortcuts, KeypadNumberKeysMatch) { 56 // Test that the shortcuts that are generated by keypad number keys match the 57 // equivalent keys. 58 static const struct { 59 int keycode; 60 int keypad_keycode; 61 } equivalents[] = { 62 {kVK_ANSI_0, kVK_ANSI_Keypad0}, 63 {kVK_ANSI_1, kVK_ANSI_Keypad1}, 64 {kVK_ANSI_2, kVK_ANSI_Keypad2}, 65 {kVK_ANSI_3, kVK_ANSI_Keypad3}, 66 {kVK_ANSI_4, kVK_ANSI_Keypad4}, 67 {kVK_ANSI_5, kVK_ANSI_Keypad5}, 68 {kVK_ANSI_6, kVK_ANSI_Keypad6}, 69 {kVK_ANSI_7, kVK_ANSI_Keypad7}, 70 {kVK_ANSI_8, kVK_ANSI_Keypad8}, 71 {kVK_ANSI_9, kVK_ANSI_Keypad9}, 72 }; 73 74 for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(equivalents); ++i) { 75 for (int command = 0; command <= 1; ++command) { 76 for (int shift = 0; shift <= 1; ++shift) { 77 for (int control = 0; control <= 1; ++control) { 78 for (int option = 0; option <= 1; ++option) { 79 EXPECT_EQ( 80 CommandForWindowKeyboardShortcut( 81 command, shift, control, option, equivalents[i].keycode, 0), 82 CommandForWindowKeyboardShortcut( 83 command, shift, control, option, 84 equivalents[i].keypad_keycode, 0)); 85 EXPECT_EQ( 86 CommandForDelayedWindowKeyboardShortcut( 87 command, shift, control, option, equivalents[i].keycode, 0), 88 CommandForDelayedWindowKeyboardShortcut( 89 command, shift, control, option, 90 equivalents[i].keypad_keycode, 0)); 91 EXPECT_EQ( 92 CommandForBrowserKeyboardShortcut( 93 command, shift, control, option, equivalents[i].keycode, 0), 94 CommandForBrowserKeyboardShortcut( 95 command, shift, control, option, 96 equivalents[i].keypad_keycode, 0)); 97 } 98 } 99 } 100 } 101 } 102 } 103 104 TEST(GlobalKeyboardShortcuts, ShortcutsToDelayedWindowCommand) { 105 // Test that an invalid shortcut translates into an invalid command id. 106 EXPECT_EQ(-1, 107 CommandForDelayedWindowKeyboardShortcut(false, false, false, false, 108 0, 0)); 109 110 // Check that all known keyboard shortcuts return valid results. 111 size_t num_shortcuts = 0; 112 const KeyboardShortcutData *it = 113 GetDelayedWindowKeyboardShortcutTable(&num_shortcuts); 114 ASSERT_GT(num_shortcuts, 0U); 115 for (size_t i = 0; i < num_shortcuts; ++i, ++it) { 116 int cmd_num = CommandForDelayedWindowKeyboardShortcut( 117 it->command_key, it->shift_key, it->cntrl_key, it->opt_key, 118 it->vkey_code, it->key_char); 119 EXPECT_EQ(cmd_num, it->chrome_command); 120 } 121 } 122 123 TEST(GlobalKeyboardShortcuts, ShortcutsToBrowserCommand) { 124 // Test that an invalid shortcut translates into an invalid command id. 125 EXPECT_EQ( 126 -1, CommandForBrowserKeyboardShortcut(false, false, false, false, 127 0, 0)); 128 129 // Check that all known keyboard shortcuts return valid results. 130 size_t num_shortcuts = 0; 131 const KeyboardShortcutData *it = 132 GetBrowserKeyboardShortcutTable(&num_shortcuts); 133 ASSERT_GT(num_shortcuts, 0U); 134 for (size_t i = 0; i < num_shortcuts; ++i, ++it) { 135 int cmd_num = CommandForBrowserKeyboardShortcut( 136 it->command_key, it->shift_key, it->cntrl_key, it->opt_key, 137 it->vkey_code, it->key_char); 138 EXPECT_EQ(cmd_num, it->chrome_command); 139 } 140 } 141 142 NSEvent* KeyEvent(bool command_key, bool shift_key, 143 bool cntrl_key, bool opt_key, 144 NSString* chars, NSString* charsNoMods) { 145 NSUInteger modifierFlags = 0; 146 if (command_key) 147 modifierFlags |= NSCommandKeyMask; 148 if (shift_key) 149 modifierFlags |= NSShiftKeyMask; 150 if (cntrl_key) 151 modifierFlags |= NSControlKeyMask; 152 if (opt_key) 153 modifierFlags |= NSAlternateKeyMask; 154 return [NSEvent keyEventWithType:NSKeyDown 155 location:NSZeroPoint 156 modifierFlags:modifierFlags 157 timestamp:0.0 158 windowNumber:0 159 context:nil 160 characters:chars 161 charactersIgnoringModifiers:charsNoMods 162 isARepeat:NO 163 keyCode:0]; 164 } 165 166 TEST(GlobalKeyboardShortcuts, KeyCharacterForEvent) { 167 // 'a' 168 EXPECT_EQ('a', KeyCharacterForEvent( 169 KeyEvent(false, false, false, false, @"a", @"a"))); 170 // cmd-'a' / cmd-shift-'a' 171 EXPECT_EQ('a', KeyCharacterForEvent( 172 KeyEvent(true, true, false, false, @"a", @"A"))); 173 // '8' 174 EXPECT_EQ('8', KeyCharacterForEvent( 175 KeyEvent(false, false, false, false, @"8", @"8"))); 176 // '{' / alt-'8' on german 177 EXPECT_EQ('{', KeyCharacterForEvent( 178 KeyEvent(false, false, false, true, @"{", @"8"))); 179 // cmd-'{' / cmd-shift-'[' on ansi 180 EXPECT_EQ('{', KeyCharacterForEvent( 181 KeyEvent(true, true, false, false, @"[", @"{"))); 182 // cmd-'z' / cmd-shift-';' on dvorak-qwerty 183 EXPECT_EQ('z', KeyCharacterForEvent( 184 KeyEvent(true, true, false, false, @"z", @":"))); 185 // cmd-shift-'[' in an RTL context. 186 EXPECT_EQ('{', KeyCharacterForEvent( 187 KeyEvent(true, true, false, false, @"{", @"}"))); 188 // Test if getting dead-key events return 0 and do not hang. 189 EXPECT_EQ(0, KeyCharacterForEvent( 190 KeyEvent(false, false, false, false, @"", @""))); 191 } 192