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