Home | History | Annotate | Download | only in cocoa
      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