blob: f8c8b532d45ec8a4f07e65ff017a81e7be6bcf4a [file] [log] [blame]
Avi Drissman4a8573c2022-09-09 19:35:541// Copyright 2009 The Chromium Authors
[email protected]3641da6c2009-07-08 14:59:062// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Daniel Cheng7d9e3d52022-02-26 09:03:245#include "chrome/browser/global_keyboard_shortcuts_mac.h"
6
[email protected]92f5adfa2010-01-05 09:49:127#include <AppKit/NSEvent.h>
[email protected]1d313b832009-10-09 01:26:208#include <Carbon/Carbon.h>
avi6846aef2015-12-26 01:09:389#include <stddef.h>
Jayson Adams6bc96ee2022-01-15 00:03:2910
Jayson Adamsa76e9ec2022-01-14 19:12:3311#include <initializer_list>
[email protected]1d313b832009-10-09 01:26:2012
Hans Wennborgf6ad69c2020-06-18 18:02:3213#include "base/check_op.h"
[email protected]1a3aba82010-11-08 23:52:5414#include "chrome/app/chrome_command_ids.h"
[email protected]3641da6c2009-07-08 14:59:0615#include "testing/gtest/include/gtest/gtest.h"
Scott Violetb72577d2019-01-09 22:18:1816#include "ui/base/buildflags.h"
erikchen008ab23a2018-08-09 02:06:3917#include "ui/events/keycodes/keyboard_code_conversion_mac.h"
[email protected]3641da6c2009-07-08 14:59:0618
Erik Chen8f2f95682018-06-08 00:44:5419namespace {
20
Jayson Adamsa76e9ec2022-01-14 19:12:3321enum class CommandKeyState : bool {
22 kUp,
23 kDown,
24};
25enum class ShiftKeyState : bool {
26 kUp,
27 kDown,
28};
29enum class OptionKeyState : bool {
30 kUp,
31 kDown,
32};
33enum class ControlKeyState : bool {
34 kUp,
35 kDown,
36};
37
38int CommandForKeys(int vkey_code,
39 CommandKeyState command,
40 ShiftKeyState shift = ShiftKeyState::kUp,
41 OptionKeyState option = OptionKeyState::kUp,
42 ControlKeyState control = ControlKeyState::kUp) {
Avi Drissman329a222f2022-04-06 15:42:1643 NSUInteger modifier_flags = 0;
Jayson Adamsa76e9ec2022-01-14 19:12:3344 if (command == CommandKeyState::kDown)
Avi Drissman8be81112022-05-11 01:12:4245 modifier_flags |= NSEventModifierFlagCommand;
Jayson Adamsa76e9ec2022-01-14 19:12:3346 if (shift == ShiftKeyState::kDown)
Avi Drissman8be81112022-05-11 01:12:4247 modifier_flags |= NSEventModifierFlagShift;
Jayson Adamsa76e9ec2022-01-14 19:12:3348 if (option == OptionKeyState::kDown)
Avi Drissman8be81112022-05-11 01:12:4249 modifier_flags |= NSEventModifierFlagOption;
Jayson Adamsa76e9ec2022-01-14 19:12:3350 if (control == ControlKeyState::kDown)
Avi Drissman8be81112022-05-11 01:12:4251 modifier_flags |= NSEventModifierFlagControl;
Erik Chen8f2f95682018-06-08 00:44:5452
Elly Fong-Jones03312b62019-10-11 21:35:4953 switch (vkey_code) {
54 case kVK_UpArrow:
55 case kVK_DownArrow:
56 case kVK_LeftArrow:
57 case kVK_RightArrow:
Avi Drissman329a222f2022-04-06 15:42:1658 // Docs say that this is set for numpad *and* arrow keys.
59 modifier_flags |= NSEventModifierFlagNumericPad;
60 [[fallthrough]];
61 case kVK_Help:
62 case kVK_ForwardDelete:
63 case kVK_Home:
64 case kVK_End:
65 case kVK_PageUp:
66 case kVK_PageDown:
67 // Docs say that this is set for function keys *and* the cluster of six
68 // navigation keys in the center of the keyboard *and* arrow keys.
69 modifier_flags |= NSEventModifierFlagFunction;
Elly Fong-Jones03312b62019-10-11 21:35:4970 break;
71 default:
72 break;
73 }
74
erikchen008ab23a2018-08-09 02:06:3975 unichar shifted_character;
76 unichar character;
77 int result = ui::MacKeyCodeForWindowsKeyCode(
Avi Drissman329a222f2022-04-06 15:42:1678 ui::KeyboardCodeFromKeyCode(vkey_code), modifier_flags,
79 &shifted_character, &character);
erikchen008ab23a2018-08-09 02:06:3980 DCHECK_NE(result, -1);
Erik Chen8f2f95682018-06-08 00:44:5481
erikchen008ab23a2018-08-09 02:06:3982 NSEvent* event = [NSEvent
Avi Drissman8be81112022-05-11 01:12:4283 keyEventWithType:NSEventTypeKeyDown
erikchen008ab23a2018-08-09 02:06:3984 location:NSZeroPoint
Avi Drissman329a222f2022-04-06 15:42:1685 modifierFlags:modifier_flags
erikchen008ab23a2018-08-09 02:06:3986 timestamp:0.0
87 windowNumber:0
88 context:nil
89 characters:[NSString stringWithFormat:@"%C", character]
90 charactersIgnoringModifiers:[NSString
91 stringWithFormat:@"%C", shifted_character]
92 isARepeat:NO
93 keyCode:vkey_code];
Erik Chen8f2f95682018-06-08 00:44:5494
erikchen41281cd2018-06-20 18:15:0595 return CommandForKeyEvent(event).chrome_command;
Erik Chen8f2f95682018-06-08 00:44:5496}
97
98} // namespace
99
100TEST(GlobalKeyboardShortcuts, BasicFunctionality) {
[email protected]3641da6c2009-07-08 14:59:06101 // Test that an invalid shortcut translates into an invalid command id.
Jayson Adamsa76e9ec2022-01-14 19:12:33102 const int kInvalidCommandId = -1;
103 const int no_key_code = 0;
104 EXPECT_EQ(
105 kInvalidCommandId,
106 CommandForKeys(no_key_code, CommandKeyState::kUp, ShiftKeyState::kUp,
107 OptionKeyState::kUp, ControlKeyState::kUp));
[email protected]70be00a2009-07-08 23:40:08108
[email protected]3641da6c2009-07-08 14:59:06109 // Check that all known keyboard shortcuts return valid results.
Erik Chen8f2f95682018-06-08 00:44:54110 for (const auto& shortcut : GetShortcutsNotPresentInMainMenu()) {
Jayson Adamsa76e9ec2022-01-14 19:12:33111 CommandKeyState command =
112 shortcut.command_key ? CommandKeyState::kDown : CommandKeyState::kUp;
113 ShiftKeyState shift =
114 shortcut.shift_key ? ShiftKeyState::kDown : ShiftKeyState::kUp;
115 OptionKeyState option =
116 shortcut.opt_key ? OptionKeyState::kDown : OptionKeyState::kUp;
117 ControlKeyState control =
118 shortcut.cntrl_key ? ControlKeyState::kDown : ControlKeyState::kUp;
119
120 int cmd_num =
121 CommandForKeys(shortcut.vkey_code, command, shift, option, control);
mblshacb9c6b9d2016-11-21 17:11:18122 EXPECT_EQ(cmd_num, shortcut.chrome_command);
[email protected]1d313b832009-10-09 01:26:20123 }
[email protected]b7b0bcb2010-11-17 17:12:24124 // Test that switching tabs triggers off keycodes and not characters (visible
125 // with the Italian keyboard layout).
Jayson Adamsa76e9ec2022-01-14 19:12:33126 EXPECT_EQ(
127 IDC_SELECT_TAB_0,
128 CommandForKeys(kVK_ANSI_1, CommandKeyState::kDown, ShiftKeyState::kUp,
129 OptionKeyState::kUp, ControlKeyState::kUp));
[email protected]b7b0bcb2010-11-17 17:12:24130}
131
132TEST(GlobalKeyboardShortcuts, KeypadNumberKeysMatch) {
133 // Test that the shortcuts that are generated by keypad number keys match the
134 // equivalent keys.
135 static const struct {
136 int keycode;
137 int keypad_keycode;
138 } equivalents[] = {
139 {kVK_ANSI_0, kVK_ANSI_Keypad0},
140 {kVK_ANSI_1, kVK_ANSI_Keypad1},
141 {kVK_ANSI_2, kVK_ANSI_Keypad2},
142 {kVK_ANSI_3, kVK_ANSI_Keypad3},
143 {kVK_ANSI_4, kVK_ANSI_Keypad4},
144 {kVK_ANSI_5, kVK_ANSI_Keypad5},
145 {kVK_ANSI_6, kVK_ANSI_Keypad6},
146 {kVK_ANSI_7, kVK_ANSI_Keypad7},
147 {kVK_ANSI_8, kVK_ANSI_Keypad8},
148 {kVK_ANSI_9, kVK_ANSI_Keypad9},
149 };
150
erikchen008ab23a2018-08-09 02:06:39151 // We only consider unshifted keys. A shifted numpad key gives a different
152 // keyEquivalent than a shifted number key.
Jayson Adamsa76e9ec2022-01-14 19:12:33153 const ShiftKeyState shift = ShiftKeyState::kUp;
Avi Drissman329a222f2022-04-06 15:42:16154 for (auto equivalent : equivalents) {
Jayson Adamsa76e9ec2022-01-14 19:12:33155 for (CommandKeyState command :
156 {CommandKeyState::kUp, CommandKeyState::kDown}) {
157 for (OptionKeyState option :
158 {OptionKeyState::kUp, OptionKeyState::kDown}) {
159 for (ControlKeyState control :
160 {ControlKeyState::kUp, ControlKeyState::kDown}) {
Avi Drissman329a222f2022-04-06 15:42:16161 EXPECT_EQ(CommandForKeys(equivalent.keycode, command, shift, option,
162 control),
163 CommandForKeys(equivalent.keypad_keycode, command, shift,
164 option, control));
[email protected]b7b0bcb2010-11-17 17:12:24165 }
166 }
167 }
168 }
[email protected]1d313b832009-10-09 01:26:20169}