--- a/toolkit/modules/ShortcutUtils.jsm
+++ b/toolkit/modules/ShortcutUtils.jsm
@@ -15,29 +15,18 @@ XPCOMUtils.defineLazyGetter(this, "Platf
});
XPCOMUtils.defineLazyGetter(this, "Keys", function() {
return Services.strings.createBundle(
"chrome://global/locale/keys.properties");
});
var ShortcutUtils = {
- /**
- * Prettifies the modifier keys for an element.
- *
- * @param Node aElemKey
- * The key element to get the modifiers from.
- * @param boolean aNoCloverLeaf
- * Pass true to use a descriptive string instead of the cloverleaf symbol. (OS X only)
- * @return string
- * A prettified and properly separated modifier keys string.
- */
- prettifyShortcut(aElemKey, aNoCloverLeaf) {
+ getModifierString(elemMod, aNoCloverLeaf) {
let elemString = "";
- let elemMod = aElemKey.getAttribute("modifiers");
let haveCloverLeaf = false;
if (elemMod.match("accel")) {
if (Services.appinfo.OS == "Darwin") {
// XXX bug 779642 Use "Cmd-" literal vs. cloverleaf meta-key until
// Orion adds variable height lines.
if (aNoCloverLeaf) {
elemString += "Cmd-";
@@ -79,32 +68,56 @@ var ShortcutUtils = {
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
if (haveCloverLeaf) {
elemString += PlatformKeys.GetStringFromName("VK_META") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
+ return elemString;
+ },
+
+ getKeyString(keyCode, keyAttribute) {
let key;
- let keyCode = aElemKey.getAttribute("keycode");
if (keyCode) {
keyCode = keyCode.toUpperCase();
try {
let bundle = keyCode == "VK_RETURN" ? PlatformKeys : Keys;
// Some keys might not exist in the locale file, which will throw.
key = bundle.GetStringFromName(keyCode);
} catch (ex) {
Cu.reportError("Error finding " + keyCode + ": " + ex);
key = keyCode.replace(/^VK_/, "");
}
} else {
- key = aElemKey.getAttribute("key");
+ key = keyAttribute;
key = key.toUpperCase();
}
+
+ return key;
+ },
+
+ /**
+ * Prettifies the modifier keys for an element.
+ *
+ * @param Node aElemKey
+ * The key element to get the modifiers from.
+ * @param boolean aNoCloverLeaf
+ * Pass true to use a descriptive string instead of the cloverleaf symbol. (OS X only)
+ * @return string
+ * A prettified and properly separated modifier keys string.
+ */
+ prettifyShortcut(aElemKey, aNoCloverLeaf) {
+ let elemString = this.getModifierString(
+ aElemKey.getAttribute("modifiers"),
+ aNoCloverLeaf);
+ let key = this.getKeyString(
+ aElemKey.getAttribute("keycode"),
+ aElemKey.getAttribute("key"));
return elemString + key;
},
findShortcut(aElemCommand) {
let document = aElemCommand.ownerDocument;
return document.querySelector("key[command=\"" + aElemCommand.getAttribute("id") + "\"]");
}
};
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -59,16 +59,61 @@ const XMLURI_PARSE_ERROR = "http://www.m
var gViewDefault = "addons://discover/";
XPCOMUtils.defineLazyGetter(this, "extensionStylesheets", () => {
const {ExtensionParent} = ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm", {});
return ExtensionParent.extensionStylesheets;
});
+let validKeys = new Set([
+ "Home", "End", "PageUp", "PageDown", "Insert", "Delete",
+ "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+ "MediaNextTrack", "MediaPlayPause", "MediaPrevTrack", "MediaStop",
+]);
+let remapKeys = {
+ a: "A", b: "B", c: "C", d: "D", e: "E", f: "F", g: "G",
+ h: "H", i: "I", j: "J", k: "K", l: "L", m: "M", n: "N", o: "O", p: "P",
+ q: "Q", r: "R", s: "S",
+ t: "T", u: "U", v: "V",
+ w: "W", x: "X",
+ y: "Y",
+ z: "Z",
+ ArrowUp: "Up",
+ ArrowRight: "Right",
+ ArrowDown: "Down",
+ ArrowLeft: "Left",
+ ",": "Comma",
+ ".": "Period",
+ " ": "Space",
+};
+
+function getModifiersAttribute(chromeModifiers) {
+ let modifiersMap = {
+ Alt: "alt",
+ Command: "accel",
+ Ctrl: "accel",
+ MacCtrl: "control",
+ Shift: "shift",
+ };
+ return Array.from(chromeModifiers, modifier => {
+ return modifiersMap[modifier];
+ }).join(" ");
+}
+
+function getStringForKey(key) {
+ if (remapKeys.hasOwnProperty(key)) {
+ return remapKeys[key];
+ } else if (validKeys.has(key)) {
+ return key;
+ }
+ return "";
+}
+
var gStrings = {};
XPCOMUtils.defineLazyServiceGetter(gStrings, "bundleSvc",
"@mozilla.org/intl/stringbundle;1",
"nsIStringBundleService");
XPCOMUtils.defineLazyGetter(gStrings, "brand", function() {
return this.bundleSvc.createBundle("chrome://branding/locale/brand.properties");
});
@@ -2724,49 +2769,67 @@ var gDetailView = {
});
if (aAddon.isWebExtension) {
let extension = ExtensionParent.GlobalManager.extensionMap.get(aAddon.id);
let {shortcuts} = extension;
let commandsSection = document.getElementById("commands-grid");
let commandsRows = document.getElementById("commands-rows");
commandsRows.textContent = "";
- let setPlaceholder = (input, name, value) => {
- let placeholder;
- if (value) {
- let key = shortcuts.buildKeyFromShortcut(document, name, value);
- placeholder = ShortcutUtils.prettifyShortcut(key);
- } else {
- placeholder = "Not set";
+ let setPlaceholder = (input, name, modifiers, key) => {
+ let placeholder = "";
+ if (modifiers.length > 0) {
+ let modifiersString = getModifiersAttribute(modifiers);
+ placeholder += ShortcutUtils.getModifierString(modifiersString);
}
+ if (key) {
+ placeholder += ShortcutUtils.getKeyString(key, key);
+ }
+ input.setAttribute("value", placeholder);
input.setAttribute("placeholder", placeholder);
};
shortcuts.allCommands().then(commands => {
commandsSection.hidden = commands.length == 0;
for (let command of commands) {
let row = document.createElement("row");
// FIXME: Update the class names.
row.setAttribute("class", "detail-row-complex");
let label = document.createElement("label");
label.setAttribute("class", "detail-row-label");
label.setAttribute("value", command.description || command.name);
row.appendChild(label);
let shortcut = document.createElement("textbox");
- setPlaceholder(shortcut, command.name, command.shortcut);
- shortcut.addEventListener("keydown", async (e) => {
- if (e.key != "Enter") return;
- let value = e.target.value;
-
- shortcut.value = "";
- setPlaceholder(shortcut, command.name, value);
-
- await shortcuts.updateCommand({name: command.name, shortcut: value});
- });
+ let commandModifiers = command.shortcut.split("+");
+ let commandKey = commandModifiers.pop();
+ setPlaceholder(shortcut, command.name, commandModifiers, commandKey);
+ shortcut.setAttribute("value", "");
+ let updateShortcut = async (e) => {
+ let modifierMap = {
+ MacCtrl: e.ctrlKey,
+ Alt: e.altKey,
+ Command: e.metaKey,
+ Shift: e.shiftKey,
+ };
+ let modifierParts = Object.entries(modifierMap)
+ .filter(([key, isDown]) => isDown)
+ .map(([key]) => key);
+ let keyString = getStringForKey(e.key);
+ setPlaceholder(shortcut, command.name, modifierParts, keyString);
+
+ if (keyString && (modifierParts.length == 2 || (modifierParts.length == 1 && modifierParts[0] != "Shift"))) {
+ let value = modifierParts.concat(remapKeys[e.key] || e.key).join("+");
+ shortcut.setAttribute("value", "");
+ shortcut.blur();
+ await shortcuts.updateCommand({name: command.name, shortcut: value});
+ }
+ };
+ shortcut.addEventListener("keydown", updateShortcut);
+ shortcut.addEventListener("keyup", updateShortcut);
row.appendChild(shortcut);
commandsRows.appendChild(row);
}
});
}
},