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#import <Cocoa/Cocoa.h> 6 7#include "base/logging.h" 8#import "chrome/browser/ui/cocoa/accelerators_cocoa.h" 9#include "chrome/test/base/in_process_browser_test.h" 10#include "ui/base/accelerators/platform_accelerator_cocoa.h" 11#import "ui/events/keycodes/keyboard_code_conversion_mac.h" 12#include "testing/gtest_mac.h" 13 14typedef InProcessBrowserTest AcceleratorsCocoaBrowserTest; 15 16namespace { 17 18// Adds all NSMenuItems with an accelerator to the array. 19void AddAcceleratorItemsToArray(NSMenu* menu, NSMutableArray* array) { 20 for (NSMenuItem* item in [menu itemArray]) { 21 NSMenu* submenu = item.submenu; 22 if (submenu) 23 AddAcceleratorItemsToArray(submenu, array); 24 25 if (item.keyEquivalent.length > 0) 26 [array addObject:item]; 27 } 28} 29 30// Returns the NSMenuItem that has the given keyEquivalent and modifiers, or 31// nil. 32NSMenuItem* MenuContainsAccelerator(NSMenu* menu, 33 NSString* key_equivalent, 34 NSUInteger modifiers) { 35 for (NSMenuItem* item in [menu itemArray]) { 36 NSMenu* submenu = item.submenu; 37 if (submenu) { 38 NSMenuItem* result = 39 MenuContainsAccelerator(submenu, key_equivalent, modifiers); 40 if (result) 41 return result; 42 } 43 44 if ([item.keyEquivalent isEqual:key_equivalent]) { 45 BOOL maskEqual = 46 (modifiers == item.keyEquivalentModifierMask) || 47 ((modifiers & (~NSShiftKeyMask)) == item.keyEquivalentModifierMask); 48 if (maskEqual) 49 return item; 50 } 51 } 52 return nil; 53} 54 55} // namespace 56 57// Checks that each NSMenuItem in the main menu has a corresponding accelerator, 58// and the keyEquivalent/modifiers match. 59IN_PROC_BROWSER_TEST_F(AcceleratorsCocoaBrowserTest, 60 MainMenuAcceleratorsInMapping) { 61 NSMenu* menu = [NSApp mainMenu]; 62 NSMutableArray* array = [NSMutableArray array]; 63 AddAcceleratorItemsToArray(menu, array); 64 65 for (NSMenuItem* item in array) { 66 NSInteger command_id = item.tag; 67 AcceleratorsCocoa* keymap = AcceleratorsCocoa::GetInstance(); 68 const ui::Accelerator* accelerator; 69 70 // If the tag is zero, then the NSMenuItem must use a custom selector. 71 // Check that the accelerator is present as an un-mapped accelerator. 72 if (command_id == 0) { 73 accelerator = keymap->GetAcceleratorForHotKey( 74 item.keyEquivalent, item.keyEquivalentModifierMask); 75 76 EXPECT_TRUE(accelerator); 77 return; 78 } 79 80 // If the tag isn't zero, then it must correspond to an IDC_* command. 81 accelerator = keymap->GetAcceleratorForCommand(command_id); 82 EXPECT_TRUE(accelerator); 83 if (!accelerator) 84 continue; 85 86 // Get the Cocoa key_equivalent associated with the accelerator. 87 const ui::PlatformAcceleratorCocoa* platform_accelerator = 88 static_cast<const ui::PlatformAcceleratorCocoa*>( 89 accelerator->platform_accelerator()); 90 NSString* key_equivalent = platform_accelerator->characters(); 91 92 // Check that the menu item's keyEquivalent matches the one from the 93 // Cocoa accelerator map. 94 EXPECT_NSEQ(key_equivalent, item.keyEquivalent); 95 96 // Check that the menu item's modifier mask matches the one stored in the 97 // accelerator. A mask that include NSShiftKeyMask may not include the 98 // relevant bit (the information is reflected in the keyEquivalent of the 99 // NSMenuItem). 100 NSUInteger mask = platform_accelerator->modifier_mask(); 101 BOOL maskEqual = 102 (mask == item.keyEquivalentModifierMask) || 103 ((mask & (~NSShiftKeyMask)) == item.keyEquivalentModifierMask); 104 EXPECT_TRUE(maskEqual); 105 } 106} 107 108// Check that each accelerator with a command_id has an associated NSMenuItem 109// in the main menu. If the selector is commandDispatch:, then the tag must 110// match the command_id. 111IN_PROC_BROWSER_TEST_F(AcceleratorsCocoaBrowserTest, 112 MappingAcceleratorsInMainMenu) { 113 AcceleratorsCocoa* keymap = AcceleratorsCocoa::GetInstance(); 114 for (AcceleratorsCocoa::AcceleratorMap::iterator it = 115 keymap->accelerators_.begin(); 116 it != keymap->accelerators_.end(); 117 ++it) { 118 const ui::PlatformAcceleratorCocoa* platform_accelerator = 119 static_cast<const ui::PlatformAcceleratorCocoa*>( 120 it->second.platform_accelerator()); 121 122 // Check that there exists a corresponding NSMenuItem. 123 NSMenuItem* item = 124 MenuContainsAccelerator([NSApp mainMenu], 125 platform_accelerator->characters(), 126 platform_accelerator->modifier_mask()); 127 EXPECT_TRUE(item); 128 129 // If the menu uses a commandDispatch:, the tag must match the command id! 130 if (item.action == @selector(commandDispatch:)) 131 EXPECT_EQ(item.tag, it->first); 132 } 133} 134