tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }