/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; import { CustomKeys } from "resource:///modules/CustomKeys.sys.mjs"; import { ShortcutUtils } from "resource://gre/modules/ShortcutUtils.sys.mjs"; const KEY_NAMES_TO_CODES = { ArrowDown: "VK_DOWN", ArrowLeft: "VK_LEFT", ArrowRight: "VK_RIGHT", ArrowUp: "VK_UP", }; /** * Actor implementation for about:keyboard. */ export class CustomKeysParent extends JSWindowActorParent { getKeyData(id) { const keyEl = this.browsingContext.topChromeWindow.document.getElementById(id); if (!keyEl) { return null; } return { shortcut: ShortcutUtils.prettifyShortcut(keyEl), isCustomized: !!CustomKeys.getDefaultKey(id), }; } // Get keyboard shortcuts in the form: // { : { : { title: , shortcut: <String>, isCustomized: <Boolean> } } // If categoryTitle or keyTitle begins with "customkeys-", it is a Fluent id. getKeys() { // Make Dev Tools populate the Browser Tools menu so we can gather those // shortcuts here. const topWin = this.browsingContext.topChromeWindow; Services.obs.notifyObservers(topWin, "customkeys-ui-showing"); const add = (category, id, title) => { const data = this.getKeyData(id); if (data) { data.title = title; category[id] = data; } }; const keys = {}; // Gather as many keys as we can from the menu bar menus. const mainMenuBar = topWin.document.getElementById("main-menubar"); for (const item of mainMenuBar.querySelectorAll("menuitem[key]")) { const menu = item.closest("menu"); if (menu.id == "historyUndoMenu" || menu.id == "historyUndoWindowMenu") { // The reopen last tab/window commands have the label of the actual // tab/window they will reopen. We handle those specially later. continue; } if (!keys[menu.label]) { keys[menu.label] = {}; } add(keys[menu.label], item.getAttribute("key"), item.label); } // Add some shortcuts that aren't available in menus. const historyCat = topWin.document.getElementById("history-menu").label; let cat = keys[historyCat]; add( cat, "key_restoreLastClosedTabOrWindowOrSession", "customkeys-history-reopen-tab" ); add(cat, "key_undoCloseWindow", "customkeys-history-reopen-window"); const toolsCat = topWin.document.getElementById("browserToolsMenu").label; cat = keys[toolsCat]; add(cat, "key_toggleToolboxF12", "customkeys-dev-tools"); add(cat, "key_inspector", "customkeys-dev-inspector"); add(cat, "key_webconsole", "customkeys-dev-webconsole"); add(cat, "key_jsdebugger", "customkeys-dev-debugger"); add(cat, "key_netmonitor", "customkeys-dev-network"); add(cat, "key_styleeditor", "customkeys-dev-style"); add(cat, "key_performance", "customkeys-dev-performance"); add(cat, "key_storage", "customkeys-dev-storage"); add(cat, "key_dom", "customkeys-dev-dom"); add(cat, "key_accessibility", "customkeys-dev-accessibility"); add(cat, "key_profilerStartStop", "customkeys-dev-profiler-toggle"); add( cat, "key_profilerStartStopAlternate", "customkeys-dev-profiler-toggle" ); add(cat, "key_profilerCapture", "customkeys-dev-profiler-capture"); add(cat, "key_profilerCaptureAlternate", "customkeys-dev-profiler-capture"); cat = keys["customkeys-category-navigation"] = {}; add(cat, "goBackKb", "customkeys-nav-back"); add(cat, "goForwardKb", "customkeys-nav-forward"); add(cat, "goHome", "customkeys-nav-home"); add(cat, "key_reload", "customkeys-nav-reload"); add(cat, "key_reload2", "customkeys-nav-reload"); add(cat, "key_reload_skip_cache", "customkeys-nav-reload-skip-cache"); add(cat, "key_reload_skip_cache2", "customkeys-nav-reload-skip-cache"); add(cat, "key_stop", "customkeys-nav-stop"); return keys; } prettifyShortcut({ modifiers, key, keycode }) { // ShortcutUtils.prettifyShortcut needs a key element, but we don't have // that here. It simply concatenates the prettified modifiers and key // anyway. const prettyMods = ShortcutUtils.getModifierString(modifiers); const prettyKey = ShortcutUtils.getKeyString(keycode, key); return prettyMods + prettyKey; } async receiveMessage(message) { switch (message.name) { case "CustomKeys:CaptureKey": { if (message.data) { this.browsingContext.embedderElement.addEventListener( "keydown", this ); } else { this.browsingContext.embedderElement.removeEventListener( "keydown", this ); } return null; } case "CustomKeys:ChangeKey": { CustomKeys.changeKey(message.data.id, message.data); return this.getKeyData(message.data.id); } case "CustomKeys:ClearKey": { const id = message.data; CustomKeys.clearKey(id); return this.getKeyData(id); } case "CustomKeys:GetDefaultKey": { const data = { id: message.data }; Object.assign(data, CustomKeys.getDefaultKey(data.id)); data.shortcut = this.prettifyShortcut(data); return data; } case "CustomKeys:GetKeys": { return this.getKeys(); } case "CustomKeys:ResetAll": { return CustomKeys.resetAll(); } case "CustomKeys:ResetKey": { CustomKeys.resetKey(message.data); return this.getKeyData(message.data); } } return null; } handleEvent(event) { if (event.type == "keydown") { event.preventDefault(); event.stopPropagation(); const data = {}; let modifiers = []; const isMac = AppConstants.platform === "macosx"; if (event.altKey) { modifiers.push("Alt"); } if (event.ctrlKey) { modifiers.push(isMac ? "MacCtrl" : "Ctrl"); } if (isMac && event.metaKey) { modifiers.push("Command"); } if (event.shiftKey) { modifiers.push("Shift"); } data.modifiers = ShortcutUtils.getModifiersAttribute(modifiers); if ( event.key == "Alt" || event.key == "Control" || event.key == "Meta" || event.key == "Shift" ) { data.isModifier = true; data.modifierString = ShortcutUtils.getModifierString(data.modifiers); this.sendAsyncMessage("CustomKeys:CapturedKey", data); return; } data.isValid = true; if (event.key.length == 1) { data.key = event.key.toUpperCase(); if (!modifiers.length || (event.shiftKey && modifiers.length == 1)) { // This is a printable character; e.g. a letter, number or punctuation // mark. That's not a valid shortcut key. data.isValid = false; } } else { data.keycode = KEY_NAMES_TO_CODES[event.key] ?? ShortcutUtils.getKeycodeAttribute(event.key); } data.shortcut = this.prettifyShortcut(data); this.sendAsyncMessage("CustomKeys:CapturedKey", data); } } }