[M94 Dev] Remove *.pyc files from git repositories
[platform/framework/web/chromium-efl.git] / chrome / browser / global_keyboard_shortcuts_mac.mm
1 // Copyright (c) 2011 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 "chrome/browser/global_keyboard_shortcuts_mac.h"
6
7 #import <AppKit/AppKit.h>
8 #include <Carbon/Carbon.h>
9
10 #include "base/check.h"
11 #include "base/feature_list.h"
12 #include "base/mac/foundation_util.h"
13 #include "base/no_destructor.h"
14 #include "build/buildflag.h"
15 #include "chrome/app/chrome_command_ids.h"
16 #import "chrome/browser/app_controller_mac.h"
17 #include "chrome/browser/ui/cocoa/accelerators_cocoa.h"
18 #include "chrome/browser/ui/ui_features.h"
19 #include "ui/base/accelerators/accelerator.h"
20 #include "ui/base/accelerators/platform_accelerator_cocoa.h"
21 #import "ui/base/cocoa/nsmenuitem_additions.h"
22 #include "ui/base/ui_base_features.h"
23 #include "ui/events/event_constants.h"
24 #include "ui/events/keycodes/keyboard_code_conversion_mac.h"
25
26 namespace {
27
28 // Returns a ui::Accelerator given a KeyboardShortcutData.
29 ui::Accelerator AcceleratorFromShortcut(const KeyboardShortcutData& shortcut) {
30   int modifiers = 0;
31   if (shortcut.command_key)
32     modifiers |= ui::EF_COMMAND_DOWN;
33   if (shortcut.shift_key)
34     modifiers |= ui::EF_SHIFT_DOWN;
35   if (shortcut.cntrl_key)
36     modifiers |= ui::EF_CONTROL_DOWN;
37   if (shortcut.opt_key)
38     modifiers |= ui::EF_ALT_DOWN;
39
40   return ui::Accelerator(ui::KeyboardCodeFromKeyCode(shortcut.vkey_code),
41                          modifiers);
42 }
43
44 // Returns the menu item associated with |key| in |menu|, or nil if not found.
45 NSMenuItem* FindMenuItem(NSEvent* key, NSMenu* menu) {
46   NSMenuItem* result = nil;
47
48   for (NSMenuItem* item in [menu itemArray]) {
49     NSMenu* submenu = [item submenu];
50     if (submenu) {
51       if (submenu != [NSApp servicesMenu])
52         result = FindMenuItem(key, submenu);
53     } else if ([item cr_firesForKeyEvent:key]) {
54       result = item;
55     }
56
57     if (result)
58       break;
59   }
60
61   return result;
62 }
63
64 int MenuCommandForKeyEvent(NSEvent* event) {
65   if ([event type] != NSKeyDown)
66     return -1;
67
68   // We avoid calling -[NSMenuDelegate menuNeedsUpdate:] on each submenu's
69   // delegate as that can be slow. Instead, we update the relevant NSMenuItems
70   // if [NSApp delegate] is an instance of AppController. See
71   // https://crbug.com/851260#c4.
72   [base::mac::ObjCCast<AppController>([NSApp delegate])
73       updateMenuItemKeyEquivalents];
74
75   // Then call -[NSMenu update], which will validate every user interface item.
76   [[NSApp mainMenu] update];
77
78   NSMenuItem* item = FindMenuItem(event, [NSApp mainMenu]);
79
80   if (!item)
81     return -1;
82
83   if ([item action] == @selector(commandDispatch:) && [item tag] > 0)
84     return [item tag];
85
86   // "Close window" doesn't use the |commandDispatch:| mechanism. Menu items
87   // that do not correspond to IDC_ constants need no special treatment however,
88   // as they can't be blacklisted in
89   // |BrowserCommandController::IsReservedCommandOrKey()| anyhow.
90   if ([item action] == @selector(performClose:))
91     return IDC_CLOSE_WINDOW;
92
93   // "Exit" doesn't use the |commandDispatch:| mechanism either.
94   if ([item action] == @selector(terminate:))
95     return IDC_EXIT;
96
97   return -1;
98 }
99
100 bool MatchesEventForKeyboardShortcut(const KeyboardShortcutData& shortcut,
101                                      bool command_key,
102                                      bool shift_key,
103                                      bool cntrl_key,
104                                      bool opt_key,
105                                      int vkey_code) {
106   return shortcut.command_key == command_key &&
107          shortcut.shift_key == shift_key && shortcut.cntrl_key == cntrl_key &&
108          shortcut.opt_key == opt_key && shortcut.vkey_code == vkey_code;
109 }
110
111 const std::vector<KeyboardShortcutData>&
112 GetDelayedShortcutsNotPresentInMainMenu() {
113   static base::NoDestructor<std::vector<KeyboardShortcutData>> keys({
114       // cmd   shift  cntrl  option vkeycode               command
115       //---   -----  -----  ------ --------               -------
116       {true, false, false, false, kVK_LeftArrow, IDC_BACK},
117       {true, false, false, false, kVK_RightArrow, IDC_FORWARD},
118   });
119   return *keys;
120 }
121
122 CommandForKeyEventResult NoCommand() {
123   return {-1, /*from_main_menu=*/false};
124 }
125
126 CommandForKeyEventResult MainMenuCommand(int cmd) {
127   return {cmd, /*from_main_menu=*/true};
128 }
129
130 CommandForKeyEventResult ShortcutCommand(int cmd) {
131   return {cmd, /*from_main_menu=*/false};
132 }
133
134 }  // namespace
135
136 const std::vector<KeyboardShortcutData>& GetShortcutsNotPresentInMainMenu() {
137   // clang-format off
138   static base::NoDestructor<std::vector<KeyboardShortcutData>> keys({
139   // cmd    shift  cntrl  option vkeycode               command
140   // ---    -----  -----  ------ --------               -------
141     {true,  true,  false, false, kVK_ANSI_RightBracket, IDC_SELECT_NEXT_TAB},
142     {true,  true,  false, false, kVK_ANSI_LeftBracket,  IDC_SELECT_PREVIOUS_TAB},
143     {false, false, true,  false, kVK_PageDown,          IDC_SELECT_NEXT_TAB},
144     {false, false, true,  false, kVK_PageUp,            IDC_SELECT_PREVIOUS_TAB},
145     {true,  false, false, true,  kVK_RightArrow,        IDC_SELECT_NEXT_TAB},
146     {true,  false, false, true,  kVK_LeftArrow,         IDC_SELECT_PREVIOUS_TAB},
147
148     // Cmd-0..8 select the nth tab, with cmd-9 being "last tab".
149     {true,  false, false, false, kVK_ANSI_1,            IDC_SELECT_TAB_0},
150     {true,  false, false, false, kVK_ANSI_Keypad1,      IDC_SELECT_TAB_0},
151     {true,  false, false, false, kVK_ANSI_2,            IDC_SELECT_TAB_1},
152     {true,  false, false, false, kVK_ANSI_Keypad2,      IDC_SELECT_TAB_1},
153     {true,  false, false, false, kVK_ANSI_3,            IDC_SELECT_TAB_2},
154     {true,  false, false, false, kVK_ANSI_Keypad3,      IDC_SELECT_TAB_2},
155     {true,  false, false, false, kVK_ANSI_4,            IDC_SELECT_TAB_3},
156     {true,  false, false, false, kVK_ANSI_Keypad4,      IDC_SELECT_TAB_3},
157     {true,  false, false, false, kVK_ANSI_5,            IDC_SELECT_TAB_4},
158     {true,  false, false, false, kVK_ANSI_Keypad5,      IDC_SELECT_TAB_4},
159     {true,  false, false, false, kVK_ANSI_6,            IDC_SELECT_TAB_5},
160     {true,  false, false, false, kVK_ANSI_Keypad6,      IDC_SELECT_TAB_5},
161     {true,  false, false, false, kVK_ANSI_7,            IDC_SELECT_TAB_6},
162     {true,  false, false, false, kVK_ANSI_Keypad7,      IDC_SELECT_TAB_6},
163     {true,  false, false, false, kVK_ANSI_8,            IDC_SELECT_TAB_7},
164     {true,  false, false, false, kVK_ANSI_Keypad8,      IDC_SELECT_TAB_7},
165     {true,  false, false, false, kVK_ANSI_9,            IDC_SELECT_LAST_TAB},
166     {true,  false, false, false, kVK_ANSI_Keypad9,      IDC_SELECT_LAST_TAB},
167     {true,  true,  false, false, kVK_ANSI_M,            IDC_SHOW_AVATAR_MENU},
168     {true,  false, false, true,  kVK_ANSI_L,            IDC_SHOW_DOWNLOADS},
169     {true,  true,  false, false, kVK_ANSI_C,            IDC_DEV_TOOLS_INSPECT},
170     {true,  false, false, true,  kVK_ANSI_C,            IDC_DEV_TOOLS_INSPECT},
171
172     {true,  false, false, true,  kVK_DownArrow,         IDC_FOCUS_NEXT_PANE},
173     {true,  false, false, true,  kVK_UpArrow,           IDC_FOCUS_PREVIOUS_PANE},
174   });
175
176   keys->push_back({true,  true,  false, false, kVK_ANSI_A, IDC_TAB_SEARCH});
177
178   if (base::FeatureList::IsEnabled(features::kUIDebugTools)) {
179     keys->push_back({false, true, true, true, kVK_ANSI_T,
180                      IDC_DEBUG_TOGGLE_TABLET_MODE});
181     keys->push_back({false, true, true, true, kVK_ANSI_V,
182                      IDC_DEBUG_PRINT_VIEW_TREE});
183     keys->push_back({false, true, true, true, kVK_ANSI_M,
184                      IDC_DEBUG_PRINT_VIEW_TREE_DETAILS});
185   }
186   // clang-format on
187   return *keys;
188 }
189
190 const std::vector<NSMenuItem*>& GetMenuItemsNotPresentInMainMenu() {
191   static base::NoDestructor<std::vector<NSMenuItem*>> menu_items;
192   if (menu_items->empty()) {
193     for (const auto& shortcut : GetShortcutsNotPresentInMainMenu()) {
194       ui::Accelerator accelerator = AcceleratorFromShortcut(shortcut);
195       NSString* key_equivalent = nil;
196       NSUInteger modifier_mask = 0;
197       ui::GetKeyEquivalentAndModifierMaskFromAccelerator(
198           accelerator, &key_equivalent, &modifier_mask);
199
200       // Intentionally leaked!
201       NSMenuItem* item = [[NSMenuItem alloc] initWithTitle:@""
202                                                     action:NULL
203                                              keyEquivalent:key_equivalent];
204       item.keyEquivalentModifierMask = modifier_mask;
205
206       // We store the command in the tag.
207       item.tag = shortcut.chrome_command;
208       menu_items->push_back(item);
209     }
210   }
211   return *menu_items;
212 }
213
214 CommandForKeyEventResult CommandForKeyEvent(NSEvent* event) {
215   DCHECK(event);
216   if ([event type] != NSKeyDown)
217     return NoCommand();
218
219   int cmdNum = MenuCommandForKeyEvent(event);
220   if (cmdNum != -1)
221     return MainMenuCommand(cmdNum);
222
223   // Scan through keycodes and see if it corresponds to one of the non-menu
224   // shortcuts.
225   for (NSMenuItem* menu_item : GetMenuItemsNotPresentInMainMenu()) {
226     if ([menu_item cr_firesForKeyEvent:event])
227       return ShortcutCommand(menu_item.tag);
228   }
229
230   return NoCommand();
231 }
232
233 int DelayedWebContentsCommandForKeyEvent(NSEvent* event) {
234   DCHECK(event);
235   if ([event type] != NSKeyDown)
236     return -1;
237
238   // Look in secondary keyboard shortcuts.
239   NSUInteger modifiers = [event modifierFlags];
240   const bool cmdKey = (modifiers & NSCommandKeyMask) != 0;
241   const bool shiftKey = (modifiers & NSShiftKeyMask) != 0;
242   const bool cntrlKey = (modifiers & NSControlKeyMask) != 0;
243   const bool optKey = (modifiers & NSAlternateKeyMask) != 0;
244   const int keyCode = [event keyCode];
245
246   // Scan through keycodes and see if it corresponds to one of the non-menu
247   // shortcuts.
248   for (const auto& shortcut : GetDelayedShortcutsNotPresentInMainMenu()) {
249     if (MatchesEventForKeyboardShortcut(shortcut, cmdKey, shiftKey, cntrlKey,
250                                         optKey, keyCode)) {
251       return shortcut.chrome_command;
252     }
253   }
254
255   return -1;
256 }
257
258 // AppKit sends an event via performKeyEquivalent: if it has at least one of the
259 // command or control modifiers, and is an NSKeyDown event. CommandDispatcher
260 // supplements this by also sending event with the option modifier to
261 // performKeyEquivalent:.
262 bool EventUsesPerformKeyEquivalent(NSEvent* event) {
263   NSUInteger modifiers = [event modifierFlags];
264   if ((modifiers & (NSEventModifierFlagCommand | NSEventModifierFlagControl |
265                     NSEventModifierFlagOption)) == 0) {
266     return false;
267   }
268   return [event type] == NSKeyDown;
269 }
270
271 bool GetDefaultMacAcceleratorForCommandId(int command_id,
272                                           ui::Accelerator* accelerator) {
273   // See if it corresponds to one of the non-menu shortcuts.
274   for (const auto& shortcut : GetShortcutsNotPresentInMainMenu()) {
275     if (shortcut.chrome_command == command_id) {
276       *accelerator = AcceleratorFromShortcut(shortcut);
277       return true;
278     }
279   }
280
281   // See if it corresponds to one of the default NSMenu keyEquivalents.
282   const ui::Accelerator* default_nsmenu_equivalent =
283       AcceleratorsCocoa::GetInstance()->GetAcceleratorForCommand(command_id);
284   if (default_nsmenu_equivalent)
285     *accelerator = *default_nsmenu_equivalent;
286   return default_nsmenu_equivalent != nullptr;
287 }