blob: cf788ec10e1419c4c5f07f884086de5b4d6ca1ed [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2014 The Chromium Authors
[email protected]cc405e42014-07-31 11:35:512// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]cc405e42014-07-31 11:35:515#import <Carbon/Carbon.h>
6#import <Cocoa/Cocoa.h>
7
Avi Drissmaneac566b02023-08-18 02:56:218#import "base/apple/foundation_util.h"
[email protected]cc405e42014-07-31 11:35:519#include "base/files/file_path.h"
[email protected]cc405e42014-07-31 11:35:5110#include "base/path_service.h"
11#import "content/browser/cocoa/system_hotkey_map.h"
12#include "content/public/common/content_paths.h"
Avi Drissman7ed05532023-06-06 20:34:2913#include "testing/gtest/include/gtest/gtest.h"
14
[email protected]cc405e42014-07-31 11:35:5115namespace content {
16
17class SystemHotkeyMapTest : public ::testing::Test {
18 protected:
Avi Drissman7ed05532023-06-06 20:34:2919 SystemHotkeyMapTest() = default;
[email protected]cc405e42014-07-31 11:35:5120
Sidney San Martín68442b12019-02-06 17:26:3021 NSDictionary* DictionaryFromTestFile(const char* file) {
[email protected]cc405e42014-07-31 11:35:5122 base::FilePath test_data_dir;
Avi Drissman1cb5e9f2018-05-01 15:53:2823 bool result = base::PathService::Get(DIR_TEST_DATA, &test_data_dir);
Jayson Adams7c8897222023-08-30 18:27:4424 if (!result) {
[email protected]cc405e42014-07-31 11:35:5125 return nil;
Jayson Adams7c8897222023-08-30 18:27:4426 }
[email protected]cc405e42014-07-31 11:35:5127
28 base::FilePath test_path = test_data_dir.AppendASCII(file);
Sidney San Martín68442b12019-02-06 17:26:3029 return [NSDictionary
Avi Drissmaneac566b02023-08-18 02:56:2130 dictionaryWithContentsOfURL:base::apple::FilePathToNSURL(test_path)
Avi Drissman7ed05532023-06-06 20:34:2931 error:nil];
[email protected]cc405e42014-07-31 11:35:5132 }
33
34 void AddEntryToDictionary(BOOL enabled,
35 unsigned short key_code,
Jayson Adams7c8897222023-08-30 18:27:4436 NSUInteger modifiers,
37 int index = 0) {
38 const NSInteger kUnused = 65535;
39 NSArray* parameters = @[ @(kUnused), @(key_code), @(modifiers) ];
40 NSDictionary* values = @{@"parameters" : parameters, @"type" : @"standard"};
41 NSDictionary* outer_dictionary =
42 @{@"value" : values,
43 @"enabled" : @(enabled)};
44 if (!index) {
45 index = count_;
46 }
47 [system_hotkey_inner_dictionary_ setObject:outer_dictionary
48 forKey:@(index).stringValue];
49 count_++;
[email protected]cc405e42014-07-31 11:35:5150 }
51
dcheng60135332015-01-09 02:05:3452 void SetUp() override {
Avi Drissman7ed05532023-06-06 20:34:2953 system_hotkey_dictionary_ = [NSMutableDictionary dictionary];
54 system_hotkey_inner_dictionary_ = [NSMutableDictionary dictionary];
[email protected]cc405e42014-07-31 11:35:5155 [system_hotkey_dictionary_ setObject:system_hotkey_inner_dictionary_
56 forKey:@"AppleSymbolicHotKeys"];
57 count_ = 100;
58 }
59
dcheng60135332015-01-09 02:05:3460 void TearDown() override {
Avi Drissman7ed05532023-06-06 20:34:2961 system_hotkey_dictionary_ = nil;
62 system_hotkey_inner_dictionary_ = nil;
[email protected]cc405e42014-07-31 11:35:5163 }
64
65 // A constructed dictionary that matches the format of the one that would be
66 // parsed from the system hotkeys plist.
Avi Drissman7ed05532023-06-06 20:34:2967 NSMutableDictionary* __strong system_hotkey_dictionary_;
[email protected]cc405e42014-07-31 11:35:5168
69 private:
70 // A reference to the mutable dictionary to which new entries are added.
Avi Drissman7ed05532023-06-06 20:34:2971 NSMutableDictionary* __strong system_hotkey_inner_dictionary_;
72
[email protected]cc405e42014-07-31 11:35:5173 // Each entry in the system_hotkey_inner_dictionary_ needs to have a unique
74 // key. This count is used to generate those unique keys.
75 int count_;
76};
77
78TEST_F(SystemHotkeyMapTest, Parse) {
79 // This plist was pulled from a real machine. It is extensively populated,
80 // and has no missing or incomplete entries.
Sidney San Martín68442b12019-02-06 17:26:3081 NSDictionary* dictionary =
82 DictionaryFromTestFile("mac/mac_system_hotkeys.plist");
[email protected]cc405e42014-07-31 11:35:5183 ASSERT_TRUE(dictionary);
84
85 SystemHotkeyMap map;
Jayson Adams7c8897222023-08-30 18:27:4486 bool parse_successful = map.ParseDictionary(dictionary);
87 EXPECT_TRUE(parse_successful);
[email protected]cc405e42014-07-31 11:35:5188
89 // Command + ` is a common key binding. It should exist.
90 unsigned short key_code = kVK_ANSI_Grave;
Avi Drissman8be81112022-05-11 01:12:4291 NSUInteger modifiers = NSEventModifierFlagCommand;
[email protected]cc405e42014-07-31 11:35:5192 EXPECT_TRUE(map.IsHotkeyReserved(key_code, modifiers));
93
94 // Command + Shift + ` is a common key binding. It should exist.
Avi Drissman8be81112022-05-11 01:12:4295 modifiers = NSEventModifierFlagCommand | NSEventModifierFlagShift;
[email protected]cc405e42014-07-31 11:35:5196 EXPECT_TRUE(map.IsHotkeyReserved(key_code, modifiers));
97
98 // Command + Shift + Ctr + ` is not a common key binding.
Avi Drissman8be81112022-05-11 01:12:4299 modifiers = NSEventModifierFlagCommand | NSEventModifierFlagShift |
100 NSEventModifierFlagControl;
[email protected]cc405e42014-07-31 11:35:51101 EXPECT_FALSE(map.IsHotkeyReserved(key_code, modifiers));
102
103 // Command + L is not a common key binding.
104 key_code = kVK_ANSI_L;
Avi Drissman8be81112022-05-11 01:12:42105 modifiers = NSEventModifierFlagCommand;
[email protected]cc405e42014-07-31 11:35:51106 EXPECT_FALSE(map.IsHotkeyReserved(key_code, modifiers));
107}
108
109TEST_F(SystemHotkeyMapTest, ParseNil) {
110 NSDictionary* dictionary = nil;
111
112 SystemHotkeyMap map;
Jayson Adams7c8897222023-08-30 18:27:44113 bool parse_successful = map.ParseDictionary(dictionary);
114 EXPECT_FALSE(parse_successful);
[email protected]cc405e42014-07-31 11:35:51115}
116
117TEST_F(SystemHotkeyMapTest, ParseMouse) {
118 // This plist was pulled from a real machine. It has missing entries,
119 // incomplete entries, and mouse hotkeys.
Sidney San Martín68442b12019-02-06 17:26:30120 NSDictionary* dictionary =
121 DictionaryFromTestFile("mac/mac_system_hotkeys_sparse.plist");
[email protected]cc405e42014-07-31 11:35:51122 ASSERT_TRUE(dictionary);
123
124 SystemHotkeyMap map;
Jayson Adams7c8897222023-08-30 18:27:44125 bool parse_successful = map.ParseDictionary(dictionary);
126 EXPECT_TRUE(parse_successful);
[email protected]cc405e42014-07-31 11:35:51127
chongz2d59d8b72016-04-01 21:58:18128 // Command + ` is a common key binding. It is missing, but since OS X uses the
129 // default value the hotkey should still be reserved.
130 // https://crbug.com/383558
131 // https://crbug.com/145062
[email protected]cc405e42014-07-31 11:35:51132 unsigned short key_code = kVK_ANSI_Grave;
Avi Drissman8be81112022-05-11 01:12:42133 NSUInteger modifiers = NSEventModifierFlagCommand;
chongz2d59d8b72016-04-01 21:58:18134 EXPECT_TRUE(map.IsHotkeyReserved(key_code, modifiers));
[email protected]cc405e42014-07-31 11:35:51135
136 // There is a mouse keybinding for 0x08. It should not apply to keyboard
137 // hotkeys.
138 key_code = kVK_ANSI_C;
139 modifiers = 0;
140 EXPECT_FALSE(map.IsHotkeyReserved(key_code, modifiers));
141
142 // Command + Alt + = is an accessibility shortcut. Its entry in the plist is
143 // incomplete.
Avi Drissman7ed05532023-06-06 20:34:29144 // TODO(erikchen): macOS uses the default bindings, so this hotkey should
145 // still be reserved. http://crbug.com/383558
[email protected]cc405e42014-07-31 11:35:51146 key_code = kVK_ANSI_Equal;
Avi Drissman8be81112022-05-11 01:12:42147 modifiers = NSEventModifierFlagCommand | NSEventModifierFlagOption;
[email protected]cc405e42014-07-31 11:35:51148 EXPECT_FALSE(map.IsHotkeyReserved(key_code, modifiers));
149}
150
151TEST_F(SystemHotkeyMapTest, ParseCustomEntries) {
152 unsigned short key_code = kVK_ANSI_C;
153
154 AddEntryToDictionary(YES, key_code, 0);
Avi Drissman8be81112022-05-11 01:12:42155 AddEntryToDictionary(YES, key_code, NSEventModifierFlagCapsLock);
156 AddEntryToDictionary(YES, key_code, NSEventModifierFlagShift);
157 AddEntryToDictionary(YES, key_code, NSEventModifierFlagControl);
158 AddEntryToDictionary(YES, key_code, NSEventModifierFlagFunction);
159 AddEntryToDictionary(
160 YES, key_code, NSEventModifierFlagFunction | NSEventModifierFlagControl);
161 AddEntryToDictionary(NO, key_code, NSEventModifierFlagOption);
[email protected]cc405e42014-07-31 11:35:51162
163 SystemHotkeyMap map;
164
Jayson Adams7c8897222023-08-30 18:27:44165 bool parse_successful = map.ParseDictionary(system_hotkey_dictionary_);
166 EXPECT_TRUE(parse_successful);
[email protected]cc405e42014-07-31 11:35:51167
168 // Entries without control, command, or alternate key mask should not be
169 // reserved.
170 EXPECT_FALSE(map.IsHotkeyReserved(key_code, 0));
Avi Drissman8be81112022-05-11 01:12:42171 EXPECT_FALSE(map.IsHotkeyReserved(key_code, NSEventModifierFlagCapsLock));
172 EXPECT_FALSE(map.IsHotkeyReserved(key_code, NSEventModifierFlagShift));
173 EXPECT_FALSE(map.IsHotkeyReserved(key_code, NSEventModifierFlagFunction));
[email protected]cc405e42014-07-31 11:35:51174
175 // Unlisted entries should not be reserved.
Avi Drissman8be81112022-05-11 01:12:42176 EXPECT_FALSE(map.IsHotkeyReserved(key_code, NSEventModifierFlagCommand));
[email protected]cc405e42014-07-31 11:35:51177
178 // Disabled entries should not be reserved.
Avi Drissman8be81112022-05-11 01:12:42179 EXPECT_FALSE(map.IsHotkeyReserved(key_code, NSEventModifierFlagOption));
[email protected]cc405e42014-07-31 11:35:51180
181 // Other entries should be reserved.
Avi Drissman8be81112022-05-11 01:12:42182 EXPECT_TRUE(map.IsHotkeyReserved(key_code, NSEventModifierFlagControl));
183 EXPECT_TRUE(map.IsHotkeyReserved(
184 key_code, NSEventModifierFlagFunction | NSEventModifierFlagControl));
[email protected]cc405e42014-07-31 11:35:51185}
186
Jayson Adams7c8897222023-08-30 18:27:44187// Tests that we add a shifted (i.e. reverse) variant of the window cycling
188// hotkey.
189TEST_F(SystemHotkeyMapTest, ReverseWindowCyclingHotkeyExists) {
190 const int kCycleThroughWindowsHotkey = 27;
191 AddEntryToDictionary(YES, kVK_ANSI_Grave, NSEventModifierFlagCommand,
192 kCycleThroughWindowsHotkey);
193
194 SystemHotkeyMap map;
195
196 bool parse_successful = map.ParseDictionary(system_hotkey_dictionary_);
197 EXPECT_TRUE(parse_successful);
198
199 EXPECT_TRUE(map.IsHotkeyReserved(kVK_ANSI_Grave, NSEventModifierFlagCommand));
200 EXPECT_TRUE(map.IsHotkeyReserved(
201 kVK_ANSI_Grave, NSEventModifierFlagCommand | NSEventModifierFlagShift));
202}
203
204// Tests that we skip over certain undocumented shortcut entries that appear in
205// AppleSymbolicHotKeys. See https://crbug.com/874219 .
206TEST_F(SystemHotkeyMapTest, IgnoreUndocumentedShortcutEntries) {
207 const int kSpacesLeftHotkey = 79;
208 const int kSpacesLeftShiftedHotkey = 80;
209 const int kSpacesRightHotkey = 81;
210 const int kSpacesRightShiftedHotkey = 82;
211 AddEntryToDictionary(YES, kVK_ANSI_A, NSEventModifierFlagControl,
212 kSpacesLeftHotkey);
213 AddEntryToDictionary(YES, kVK_ANSI_B, NSEventModifierFlagControl,
214 kSpacesLeftShiftedHotkey);
215 AddEntryToDictionary(YES, kVK_ANSI_C, NSEventModifierFlagControl,
216 kSpacesRightHotkey);
217 AddEntryToDictionary(YES, kVK_ANSI_D, NSEventModifierFlagControl,
218 kSpacesRightShiftedHotkey);
219
220 SystemHotkeyMap map;
221
222 bool parse_successful = map.ParseDictionary(system_hotkey_dictionary_);
223 EXPECT_TRUE(parse_successful);
224
225 EXPECT_TRUE(map.IsHotkeyReserved(kVK_ANSI_A, NSEventModifierFlagControl));
226 EXPECT_FALSE(map.IsHotkeyReserved(kVK_ANSI_B, NSEventModifierFlagControl));
227 EXPECT_TRUE(map.IsHotkeyReserved(kVK_ANSI_C, NSEventModifierFlagControl));
228 EXPECT_FALSE(map.IsHotkeyReserved(kVK_ANSI_D, NSEventModifierFlagControl));
229}
230
[email protected]cc405e42014-07-31 11:35:51231} // namespace content