blob: 49f42bf250d807cc475bd3187ac68a0b3af99cd6 [file] [log] [blame]
[email protected]3641da6c2009-07-08 14:59:061// 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
[email protected]92f5adfa2010-01-05 09:49:125#include <AppKit/NSEvent.h>
[email protected]1d313b832009-10-09 01:26:206#include <Carbon/Carbon.h>
avi6846aef2015-12-26 01:09:387#include <stddef.h>
Jayson Adams6bc96ee2022-01-15 00:03:298
Jayson Adamsa76e9ec2022-01-14 19:12:339#include <initializer_list>
[email protected]1d313b832009-10-09 01:26:2010
[email protected]3641da6c2009-07-08 14:59:0611#include "chrome/browser/global_keyboard_shortcuts_mac.h"
12
Hans Wennborgf6ad69c2020-06-18 18:02:3213#include "base/check_op.h"
Lei Zhangd4a2f8e2021-07-08 03:43:2814#include "base/cxx17_backports.h"
[email protected]1a3aba82010-11-08 23:52:5415#include "chrome/app/chrome_command_ids.h"
[email protected]3641da6c2009-07-08 14:59:0616#include "testing/gtest/include/gtest/gtest.h"
Scott Violetb72577d2019-01-09 22:18:1817#include "ui/base/buildflags.h"
erikchen008ab23a2018-08-09 02:06:3918#include "ui/events/keycodes/keyboard_code_conversion_mac.h"
[email protected]3641da6c2009-07-08 14:59:0619
Erik Chen8f2f95682018-06-08 00:44:5420namespace {
21
Jayson Adamsa76e9ec2022-01-14 19:12:3322enum class CommandKeyState : bool {
23 kUp,
24 kDown,
25};
26enum class ShiftKeyState : bool {
27 kUp,
28 kDown,
29};
30enum class OptionKeyState : bool {
31 kUp,
32 kDown,
33};
34enum class ControlKeyState : bool {
35 kUp,
36 kDown,
37};
38
39int CommandForKeys(int vkey_code,
40 CommandKeyState command,
41 ShiftKeyState shift = ShiftKeyState::kUp,
42 OptionKeyState option = OptionKeyState::kUp,
43 ControlKeyState control = ControlKeyState::kUp) {
Erik Chen8f2f95682018-06-08 00:44:5444 NSUInteger modifierFlags = 0;
Jayson Adamsa76e9ec2022-01-14 19:12:3345 if (command == CommandKeyState::kDown)
Erik Chen8f2f95682018-06-08 00:44:5446 modifierFlags |= NSCommandKeyMask;
Jayson Adamsa76e9ec2022-01-14 19:12:3347 if (shift == ShiftKeyState::kDown)
Erik Chen8f2f95682018-06-08 00:44:5448 modifierFlags |= NSShiftKeyMask;
Jayson Adamsa76e9ec2022-01-14 19:12:3349 if (option == OptionKeyState::kDown)
Erik Chen8f2f95682018-06-08 00:44:5450 modifierFlags |= NSAlternateKeyMask;
Jayson Adamsa76e9ec2022-01-14 19:12:3351 if (control == ControlKeyState::kDown)
52 modifierFlags |= NSControlKeyMask;
Erik Chen8f2f95682018-06-08 00:44:5453
Elly Fong-Jones03312b62019-10-11 21:35:4954 switch (vkey_code) {
55 case kVK_UpArrow:
56 case kVK_DownArrow:
57 case kVK_LeftArrow:
58 case kVK_RightArrow:
59 // Docs say this is set whenever a key came from the numpad *or* the arrow
60 // keys.
61 modifierFlags |= NSEventModifierFlagNumericPad;
62 break;
63 default:
64 break;
65 }
66
erikchen008ab23a2018-08-09 02:06:3967 unichar shifted_character;
68 unichar character;
69 int result = ui::MacKeyCodeForWindowsKeyCode(
70 ui::KeyboardCodeFromKeyCode(vkey_code), modifierFlags, &shifted_character,
71 &character);
72 DCHECK_NE(result, -1);
Erik Chen8f2f95682018-06-08 00:44:5473
erikchen008ab23a2018-08-09 02:06:3974 NSEvent* event = [NSEvent
75 keyEventWithType:NSKeyDown
76 location:NSZeroPoint
77 modifierFlags:modifierFlags
78 timestamp:0.0
79 windowNumber:0
80 context:nil
81 characters:[NSString stringWithFormat:@"%C", character]
82 charactersIgnoringModifiers:[NSString
83 stringWithFormat:@"%C", shifted_character]
84 isARepeat:NO
85 keyCode:vkey_code];
Erik Chen8f2f95682018-06-08 00:44:5486
erikchen41281cd2018-06-20 18:15:0587 return CommandForKeyEvent(event).chrome_command;
Erik Chen8f2f95682018-06-08 00:44:5488}
89
90} // namespace
91
92TEST(GlobalKeyboardShortcuts, BasicFunctionality) {
[email protected]3641da6c2009-07-08 14:59:0693 // Test that an invalid shortcut translates into an invalid command id.
Jayson Adamsa76e9ec2022-01-14 19:12:3394 const int kInvalidCommandId = -1;
95 const int no_key_code = 0;
96 EXPECT_EQ(
97 kInvalidCommandId,
98 CommandForKeys(no_key_code, CommandKeyState::kUp, ShiftKeyState::kUp,
99 OptionKeyState::kUp, ControlKeyState::kUp));
[email protected]70be00a2009-07-08 23:40:08100
[email protected]3641da6c2009-07-08 14:59:06101 // Check that all known keyboard shortcuts return valid results.
Erik Chen8f2f95682018-06-08 00:44:54102 for (const auto& shortcut : GetShortcutsNotPresentInMainMenu()) {
Jayson Adamsa76e9ec2022-01-14 19:12:33103 CommandKeyState command =
104 shortcut.command_key ? CommandKeyState::kDown : CommandKeyState::kUp;
105 ShiftKeyState shift =
106 shortcut.shift_key ? ShiftKeyState::kDown : ShiftKeyState::kUp;
107 OptionKeyState option =
108 shortcut.opt_key ? OptionKeyState::kDown : OptionKeyState::kUp;
109 ControlKeyState control =
110 shortcut.cntrl_key ? ControlKeyState::kDown : ControlKeyState::kUp;
111
112 int cmd_num =
113 CommandForKeys(shortcut.vkey_code, command, shift, option, control);
mblshacb9c6b9d2016-11-21 17:11:18114 EXPECT_EQ(cmd_num, shortcut.chrome_command);
[email protected]1d313b832009-10-09 01:26:20115 }
[email protected]b7b0bcb2010-11-17 17:12:24116 // Test that switching tabs triggers off keycodes and not characters (visible
117 // with the Italian keyboard layout).
Jayson Adamsa76e9ec2022-01-14 19:12:33118 EXPECT_EQ(
119 IDC_SELECT_TAB_0,
120 CommandForKeys(kVK_ANSI_1, CommandKeyState::kDown, ShiftKeyState::kUp,
121 OptionKeyState::kUp, ControlKeyState::kUp));
[email protected]b7b0bcb2010-11-17 17:12:24122}
123
124TEST(GlobalKeyboardShortcuts, KeypadNumberKeysMatch) {
125 // Test that the shortcuts that are generated by keypad number keys match the
126 // equivalent keys.
127 static const struct {
128 int keycode;
129 int keypad_keycode;
130 } equivalents[] = {
131 {kVK_ANSI_0, kVK_ANSI_Keypad0},
132 {kVK_ANSI_1, kVK_ANSI_Keypad1},
133 {kVK_ANSI_2, kVK_ANSI_Keypad2},
134 {kVK_ANSI_3, kVK_ANSI_Keypad3},
135 {kVK_ANSI_4, kVK_ANSI_Keypad4},
136 {kVK_ANSI_5, kVK_ANSI_Keypad5},
137 {kVK_ANSI_6, kVK_ANSI_Keypad6},
138 {kVK_ANSI_7, kVK_ANSI_Keypad7},
139 {kVK_ANSI_8, kVK_ANSI_Keypad8},
140 {kVK_ANSI_9, kVK_ANSI_Keypad9},
141 };
142
erikchen008ab23a2018-08-09 02:06:39143 // We only consider unshifted keys. A shifted numpad key gives a different
144 // keyEquivalent than a shifted number key.
Jayson Adamsa76e9ec2022-01-14 19:12:33145 const ShiftKeyState shift = ShiftKeyState::kUp;
Erik Chen8f2f95682018-06-08 00:44:54146 for (unsigned int i = 0; i < base::size(equivalents); ++i) {
Jayson Adamsa76e9ec2022-01-14 19:12:33147 for (CommandKeyState command :
148 {CommandKeyState::kUp, CommandKeyState::kDown}) {
149 for (OptionKeyState option :
150 {OptionKeyState::kUp, OptionKeyState::kDown}) {
151 for (ControlKeyState control :
152 {ControlKeyState::kUp, ControlKeyState::kDown}) {
153 EXPECT_EQ(CommandForKeys(equivalents[i].keycode, command, shift,
154 option, control),
155 CommandForKeys(equivalents[i].keypad_keycode, command,
156 shift, option, control));
[email protected]b7b0bcb2010-11-17 17:12:24157 }
158 }
159 }
160 }
[email protected]1d313b832009-10-09 01:26:20161}
Jayson Adamsa76e9ec2022-01-14 19:12:33162
163// Test that the extra View -> Zoom shortcuts exist.
164TEST(GlobalKeyboardShortcuts, ExtraZoomInOutShortcutsExist) {
165 const int zoomIn = CommandForKeys(kVK_ANSI_Equal, CommandKeyState::kDown,
166 ShiftKeyState::kDown);
167 ASSERT_EQ(IDC_ZOOM_PLUS, zoomIn);
168 const int zoomOut = CommandForKeys(kVK_ANSI_Minus, CommandKeyState::kDown);
169 ASSERT_EQ(IDC_ZOOM_MINUS, zoomOut);
170
171 const int cmdPlusFromKeypad =
172 CommandForKeys(kVK_ANSI_KeypadPlus, CommandKeyState::kDown);
173 EXPECT_EQ(cmdPlusFromKeypad, zoomIn);
174 const int cmdMinusFromKeypad =
175 CommandForKeys(kVK_ANSI_KeypadMinus, CommandKeyState::kDown);
176 EXPECT_EQ(cmdMinusFromKeypad, zoomOut);
177
178 const int cmdEquals = CommandForKeys(kVK_ANSI_Equal, CommandKeyState::kDown);
179 EXPECT_EQ(cmdEquals, zoomIn);
180}