CustomKeysParent.sys.mjs (7396B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; 6 import { CustomKeys } from "resource:///modules/CustomKeys.sys.mjs"; 7 import { ShortcutUtils } from "resource://gre/modules/ShortcutUtils.sys.mjs"; 8 9 const KEY_NAMES_TO_CODES = { 10 ArrowDown: "VK_DOWN", 11 ArrowLeft: "VK_LEFT", 12 ArrowRight: "VK_RIGHT", 13 ArrowUp: "VK_UP", 14 }; 15 16 /** 17 * Actor implementation for about:keyboard. 18 */ 19 export class CustomKeysParent extends JSWindowActorParent { 20 getKeyData(id) { 21 const keyEl = 22 this.browsingContext.topChromeWindow.document.getElementById(id); 23 if (!keyEl) { 24 return null; 25 } 26 return { 27 shortcut: ShortcutUtils.prettifyShortcut(keyEl), 28 isCustomized: !!CustomKeys.getDefaultKey(id), 29 }; 30 } 31 32 // Get keyboard shortcuts in the form: 33 // { <categoryTitle>: { <keyId>: { title: <title>, shortcut: <String>, isCustomized: <Boolean> } } 34 // If categoryTitle or keyTitle begins with "customkeys-", it is a Fluent id. 35 getKeys() { 36 // Make Dev Tools populate the Browser Tools menu so we can gather those 37 // shortcuts here. 38 const topWin = this.browsingContext.topChromeWindow; 39 Services.obs.notifyObservers(topWin, "customkeys-ui-showing"); 40 41 const add = (category, id, title) => { 42 const data = this.getKeyData(id); 43 if (data) { 44 data.title = title; 45 category[id] = data; 46 } 47 }; 48 49 const keys = {}; 50 // Gather as many keys as we can from the menu bar menus. 51 const mainMenuBar = topWin.document.getElementById("main-menubar"); 52 for (const item of mainMenuBar.querySelectorAll("menuitem[key]")) { 53 const menu = item.closest("menu"); 54 if (menu.id == "historyUndoMenu" || menu.id == "historyUndoWindowMenu") { 55 // The reopen last tab/window commands have the label of the actual 56 // tab/window they will reopen. We handle those specially later. 57 continue; 58 } 59 if (!keys[menu.label]) { 60 keys[menu.label] = {}; 61 } 62 add(keys[menu.label], item.getAttribute("key"), item.label); 63 } 64 65 // Add some shortcuts that aren't available in menus. 66 const historyCat = topWin.document.getElementById("history-menu").label; 67 let cat = keys[historyCat]; 68 add( 69 cat, 70 "key_restoreLastClosedTabOrWindowOrSession", 71 "customkeys-history-reopen-tab" 72 ); 73 add(cat, "key_undoCloseWindow", "customkeys-history-reopen-window"); 74 const toolsCat = topWin.document.getElementById("browserToolsMenu").label; 75 cat = keys[toolsCat]; 76 add(cat, "key_toggleToolboxF12", "customkeys-dev-tools"); 77 add(cat, "key_inspector", "customkeys-dev-inspector"); 78 add(cat, "key_webconsole", "customkeys-dev-webconsole"); 79 add(cat, "key_jsdebugger", "customkeys-dev-debugger"); 80 add(cat, "key_netmonitor", "customkeys-dev-network"); 81 add(cat, "key_styleeditor", "customkeys-dev-style"); 82 add(cat, "key_performance", "customkeys-dev-performance"); 83 add(cat, "key_storage", "customkeys-dev-storage"); 84 add(cat, "key_dom", "customkeys-dev-dom"); 85 add(cat, "key_accessibility", "customkeys-dev-accessibility"); 86 add(cat, "key_profilerStartStop", "customkeys-dev-profiler-toggle"); 87 add( 88 cat, 89 "key_profilerStartStopAlternate", 90 "customkeys-dev-profiler-toggle" 91 ); 92 add(cat, "key_profilerCapture", "customkeys-dev-profiler-capture"); 93 add(cat, "key_profilerCaptureAlternate", "customkeys-dev-profiler-capture"); 94 cat = keys["customkeys-category-navigation"] = {}; 95 add(cat, "goBackKb", "customkeys-nav-back"); 96 add(cat, "goForwardKb", "customkeys-nav-forward"); 97 add(cat, "goHome", "customkeys-nav-home"); 98 add(cat, "key_reload", "customkeys-nav-reload"); 99 add(cat, "key_reload2", "customkeys-nav-reload"); 100 add(cat, "key_reload_skip_cache", "customkeys-nav-reload-skip-cache"); 101 add(cat, "key_reload_skip_cache2", "customkeys-nav-reload-skip-cache"); 102 add(cat, "key_stop", "customkeys-nav-stop"); 103 104 return keys; 105 } 106 107 prettifyShortcut({ modifiers, key, keycode }) { 108 // ShortcutUtils.prettifyShortcut needs a key element, but we don't have 109 // that here. It simply concatenates the prettified modifiers and key 110 // anyway. 111 const prettyMods = ShortcutUtils.getModifierString(modifiers); 112 const prettyKey = ShortcutUtils.getKeyString(keycode, key); 113 return prettyMods + prettyKey; 114 } 115 116 async receiveMessage(message) { 117 switch (message.name) { 118 case "CustomKeys:CaptureKey": { 119 if (message.data) { 120 this.browsingContext.embedderElement.addEventListener( 121 "keydown", 122 this 123 ); 124 } else { 125 this.browsingContext.embedderElement.removeEventListener( 126 "keydown", 127 this 128 ); 129 } 130 return null; 131 } 132 case "CustomKeys:ChangeKey": { 133 CustomKeys.changeKey(message.data.id, message.data); 134 return this.getKeyData(message.data.id); 135 } 136 case "CustomKeys:ClearKey": { 137 const id = message.data; 138 CustomKeys.clearKey(id); 139 return this.getKeyData(id); 140 } 141 case "CustomKeys:GetDefaultKey": { 142 const data = { id: message.data }; 143 Object.assign(data, CustomKeys.getDefaultKey(data.id)); 144 data.shortcut = this.prettifyShortcut(data); 145 return data; 146 } 147 case "CustomKeys:GetKeys": { 148 return this.getKeys(); 149 } 150 case "CustomKeys:ResetAll": { 151 return CustomKeys.resetAll(); 152 } 153 case "CustomKeys:ResetKey": { 154 CustomKeys.resetKey(message.data); 155 return this.getKeyData(message.data); 156 } 157 } 158 return null; 159 } 160 161 handleEvent(event) { 162 if (event.type == "keydown") { 163 event.preventDefault(); 164 event.stopPropagation(); 165 const data = {}; 166 let modifiers = []; 167 const isMac = AppConstants.platform === "macosx"; 168 if (event.altKey) { 169 modifiers.push("Alt"); 170 } 171 if (event.ctrlKey) { 172 modifiers.push(isMac ? "MacCtrl" : "Ctrl"); 173 } 174 if (isMac && event.metaKey) { 175 modifiers.push("Command"); 176 } 177 if (event.shiftKey) { 178 modifiers.push("Shift"); 179 } 180 data.modifiers = ShortcutUtils.getModifiersAttribute(modifiers); 181 if ( 182 event.key == "Alt" || 183 event.key == "Control" || 184 event.key == "Meta" || 185 event.key == "Shift" 186 ) { 187 data.isModifier = true; 188 data.modifierString = ShortcutUtils.getModifierString(data.modifiers); 189 this.sendAsyncMessage("CustomKeys:CapturedKey", data); 190 return; 191 } 192 data.isValid = true; 193 if (event.key.length == 1) { 194 data.key = event.key.toUpperCase(); 195 if (!modifiers.length || (event.shiftKey && modifiers.length == 1)) { 196 // This is a printable character; e.g. a letter, number or punctuation 197 // mark. That's not a valid shortcut key. 198 data.isValid = false; 199 } 200 } else { 201 data.keycode = 202 KEY_NAMES_TO_CODES[event.key] ?? 203 ShortcutUtils.getKeycodeAttribute(event.key); 204 } 205 data.shortcut = this.prettifyShortcut(data); 206 this.sendAsyncMessage("CustomKeys:CapturedKey", data); 207 } 208 } 209 }