1 // Copyright 2014 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 <gtest/gtest.h> 6 7 #import <Carbon/Carbon.h> 8 #import <Cocoa/Cocoa.h> 9 10 #include "base/files/file_path.h" 11 #include "base/mac/scoped_nsobject.h" 12 #include "base/path_service.h" 13 #import "content/browser/cocoa/system_hotkey_map.h" 14 #include "content/public/common/content_paths.h" 15 16 namespace content { 17 18 class SystemHotkeyMapTest : public ::testing::Test { 19 protected: 20 SystemHotkeyMapTest() {} 21 22 NSData* DataFromTestFile(const char* file) { 23 base::FilePath test_data_dir; 24 bool result = PathService::Get(DIR_TEST_DATA, &test_data_dir); 25 if (!result) 26 return nil; 27 28 base::FilePath test_path = test_data_dir.AppendASCII(file); 29 std::string test_path_string = test_path.AsUTF8Unsafe(); 30 NSString* file_path = 31 [NSString stringWithUTF8String:test_path_string.c_str()]; 32 return [NSData dataWithContentsOfFile:file_path]; 33 } 34 35 void AddEntryToDictionary(BOOL enabled, 36 unsigned short key_code, 37 NSUInteger modifiers) { 38 NSMutableArray* parameters = [NSMutableArray array]; 39 // The first parameter is unused. 40 [parameters addObject:[NSNumber numberWithInt:65535]]; 41 [parameters addObject:[NSNumber numberWithUnsignedShort:key_code]]; 42 [parameters addObject:[NSNumber numberWithUnsignedInteger:modifiers]]; 43 44 NSMutableDictionary* value_dictionary = [NSMutableDictionary dictionary]; 45 [value_dictionary setObject:parameters forKey:@"parameters"]; 46 [value_dictionary setObject:@"standard" forKey:@"type"]; 47 48 NSMutableDictionary* outer_dictionary = [NSMutableDictionary dictionary]; 49 [outer_dictionary setObject:value_dictionary forKey:@"value"]; 50 51 NSNumber* enabled_number = [NSNumber numberWithBool:enabled]; 52 [outer_dictionary setObject:enabled_number forKey:@"enabled"]; 53 54 NSString* key = [NSString stringWithFormat:@"%d", count_]; 55 [system_hotkey_inner_dictionary_ setObject:outer_dictionary forKey:key]; 56 ++count_; 57 } 58 59 virtual void SetUp() OVERRIDE { 60 system_hotkey_dictionary_.reset([[NSMutableDictionary alloc] init]); 61 system_hotkey_inner_dictionary_.reset([[NSMutableDictionary alloc] init]); 62 [system_hotkey_dictionary_ setObject:system_hotkey_inner_dictionary_ 63 forKey:@"AppleSymbolicHotKeys"]; 64 count_ = 100; 65 } 66 67 virtual void TearDown() OVERRIDE { 68 system_hotkey_dictionary_.reset(); 69 system_hotkey_inner_dictionary_.reset(); 70 } 71 72 // A constructed dictionary that matches the format of the one that would be 73 // parsed from the system hotkeys plist. 74 base::scoped_nsobject<NSMutableDictionary> system_hotkey_dictionary_; 75 76 private: 77 // A reference to the mutable dictionary to which new entries are added. 78 base::scoped_nsobject<NSMutableDictionary> system_hotkey_inner_dictionary_; 79 // Each entry in the system_hotkey_inner_dictionary_ needs to have a unique 80 // key. This count is used to generate those unique keys. 81 int count_; 82 }; 83 84 TEST_F(SystemHotkeyMapTest, Parse) { 85 // This plist was pulled from a real machine. It is extensively populated, 86 // and has no missing or incomplete entries. 87 NSData* data = DataFromTestFile("mac/mac_system_hotkeys.plist"); 88 ASSERT_TRUE(data); 89 90 NSDictionary* dictionary = SystemHotkeyMap::DictionaryFromData(data); 91 ASSERT_TRUE(dictionary); 92 93 SystemHotkeyMap map; 94 bool result = map.ParseDictionary(dictionary); 95 EXPECT_TRUE(result); 96 97 // Command + ` is a common key binding. It should exist. 98 unsigned short key_code = kVK_ANSI_Grave; 99 NSUInteger modifiers = NSCommandKeyMask; 100 EXPECT_TRUE(map.IsHotkeyReserved(key_code, modifiers)); 101 102 // Command + Shift + ` is a common key binding. It should exist. 103 modifiers = NSCommandKeyMask | NSShiftKeyMask; 104 EXPECT_TRUE(map.IsHotkeyReserved(key_code, modifiers)); 105 106 // Command + Shift + Ctr + ` is not a common key binding. 107 modifiers = NSCommandKeyMask | NSShiftKeyMask | NSControlKeyMask; 108 EXPECT_FALSE(map.IsHotkeyReserved(key_code, modifiers)); 109 110 // Command + L is not a common key binding. 111 key_code = kVK_ANSI_L; 112 modifiers = NSCommandKeyMask; 113 EXPECT_FALSE(map.IsHotkeyReserved(key_code, modifiers)); 114 } 115 116 TEST_F(SystemHotkeyMapTest, ParseNil) { 117 NSDictionary* dictionary = nil; 118 119 SystemHotkeyMap map; 120 bool result = map.ParseDictionary(dictionary); 121 EXPECT_FALSE(result); 122 } 123 124 TEST_F(SystemHotkeyMapTest, ParseMouse) { 125 // This plist was pulled from a real machine. It has missing entries, 126 // incomplete entries, and mouse hotkeys. 127 NSData* data = DataFromTestFile("mac/mac_system_hotkeys_sparse.plist"); 128 ASSERT_TRUE(data); 129 130 NSDictionary* dictionary = SystemHotkeyMap::DictionaryFromData(data); 131 ASSERT_TRUE(dictionary); 132 133 SystemHotkeyMap map; 134 bool result = map.ParseDictionary(dictionary); 135 EXPECT_TRUE(result); 136 137 // Command + ` is a common key binding. It is missing. 138 // TODO(erikchen): OSX uses the default value when the keybinding is missing, 139 // so the hotkey should still be reserved. 140 // http://crbug.com/383558 141 unsigned short key_code = kVK_ANSI_Grave; 142 NSUInteger modifiers = NSCommandKeyMask; 143 EXPECT_FALSE(map.IsHotkeyReserved(key_code, modifiers)); 144 145 // There is a mouse keybinding for 0x08. It should not apply to keyboard 146 // hotkeys. 147 key_code = kVK_ANSI_C; 148 modifiers = 0; 149 EXPECT_FALSE(map.IsHotkeyReserved(key_code, modifiers)); 150 151 // Command + Alt + = is an accessibility shortcut. Its entry in the plist is 152 // incomplete. 153 // TODO(erikchen): OSX uses the default bindings, so this hotkey should still 154 // be reserved. 155 // http://crbug.com/383558 156 key_code = kVK_ANSI_Equal; 157 modifiers = NSCommandKeyMask | NSAlternateKeyMask; 158 EXPECT_FALSE(map.IsHotkeyReserved(key_code, modifiers)); 159 } 160 161 TEST_F(SystemHotkeyMapTest, ParseCustomEntries) { 162 unsigned short key_code = kVK_ANSI_C; 163 164 AddEntryToDictionary(YES, key_code, 0); 165 AddEntryToDictionary(YES, key_code, NSAlphaShiftKeyMask); 166 AddEntryToDictionary(YES, key_code, NSShiftKeyMask); 167 AddEntryToDictionary(YES, key_code, NSControlKeyMask); 168 AddEntryToDictionary(YES, key_code, NSFunctionKeyMask); 169 AddEntryToDictionary(YES, key_code, NSFunctionKeyMask | NSControlKeyMask); 170 AddEntryToDictionary(NO, key_code, NSAlternateKeyMask); 171 172 SystemHotkeyMap map; 173 174 bool result = map.ParseDictionary(system_hotkey_dictionary_.get()); 175 EXPECT_TRUE(result); 176 177 // Entries without control, command, or alternate key mask should not be 178 // reserved. 179 EXPECT_FALSE(map.IsHotkeyReserved(key_code, 0)); 180 EXPECT_FALSE(map.IsHotkeyReserved(key_code, NSAlphaShiftKeyMask)); 181 EXPECT_FALSE(map.IsHotkeyReserved(key_code, NSShiftKeyMask)); 182 EXPECT_FALSE(map.IsHotkeyReserved(key_code, NSFunctionKeyMask)); 183 184 // Unlisted entries should not be reserved. 185 EXPECT_FALSE(map.IsHotkeyReserved(key_code, NSCommandKeyMask)); 186 187 // Disabled entries should not be reserved. 188 EXPECT_FALSE(map.IsHotkeyReserved(key_code, NSAlternateKeyMask)); 189 190 // Other entries should be reserved. 191 EXPECT_TRUE(map.IsHotkeyReserved(key_code, NSControlKeyMask)); 192 EXPECT_TRUE( 193 map.IsHotkeyReserved(key_code, NSFunctionKeyMask | NSControlKeyMask)); 194 } 195 196 } // namespace content 197