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