diff --git a/bootstrap.js b/bootstrap.js index 6d99e05..4848e32 100644 --- a/bootstrap.js +++ b/bootstrap.js @@ -81,6 +81,7 @@ var windowsObserver = { case "popupshowing": this.popupShowingHandler(e); break; case "command": this.commandHandler(e); break; case "click": this.clickHandler(e); break; + case "keydown": case "keypress": this.keypressHandler(e); break; case "PrivateTab:PrivateChanged": this.privateChangedHandler(e); break; case "SSWindowStateBusy": this.setWindowBusy(e, true); break; @@ -143,7 +144,7 @@ var windowsObserver = { window.addEventListener("SSWindowStateBusy", this, true); window.addEventListener("SSWindowStateReady", this, true); if(this.hotkeys) - window.addEventListener("keypress", this, true); + window.addEventListener(this.keyEvent, this, true); window.setTimeout(function() { this.initControls(document); window.setTimeout(function() { @@ -186,7 +187,7 @@ var windowsObserver = { window.removeEventListener("dragstart", this, true); window.removeEventListener("dragend", this, true); window.removeEventListener("drop", this, true); - window.removeEventListener("keypress", this, true); + window.removeEventListener(this.keyEvent, this, true); window.removeEventListener("PrivateTab:PrivateChanged", this, false); window.removeEventListener("SSWindowStateBusy", this, true); window.removeEventListener("SSWindowStateReady", this, true); @@ -243,6 +244,8 @@ var windowsObserver = { prefChanged: function(pName, pVal) { if(pName.startsWith("key.")) + this.updateHotkeys(true); + else if(pName == "keysUseKeydownEvent") this.updateHotkeys(); else if(pName == "fixAppButtonWidth") { this.appButtonDontChange = !pVal; @@ -750,7 +753,7 @@ var windowsObserver = { && e.metaKey == k.metaKey && e.getModifierState("OS") == k.osKey && ( - k.char && String.fromCharCode(e.charCode).toUpperCase() == k.char + k.char && String.fromCharCode(e.charCode || e.keyCode).toUpperCase() == k.char || k.code && e.keyCode == k.code ) ) { @@ -1322,6 +1325,11 @@ var windowsObserver = { force && node.parentNode.removeChild(node); }, + get keyEvent() { + return prefs.get("keysUseKeydownEvent") + ? "keydown" + : "keypress"; + }, hotkeys: null, _hotkeysHasText: false, get accelKey() { @@ -1339,6 +1347,12 @@ var windowsObserver = { this._hotkeysHasText = false; var hasKeys = false; var keys = { __proto__: null }; + function getVKChar(vk) { + var tmp = {}; + Services.scriptloader.loadSubScript("chrome://privatetab/content/virtualKeyCodes.js", tmp); + getVKChar = tmp.getVKChar; + return getVKChar(vk); + } function initHotkey(kId) { var keyStr = prefs.get("key." + kId); _log("initHotkey: " + kId + " = " + keyStr); @@ -1367,7 +1381,11 @@ var windowsObserver = { } else { // VK_* k.code = Components.interfaces.nsIDOMKeyEvent["DOM_" + key]; - k._keyCode = key; + var char = getVKChar(key); + if(char) + k._key = char; + else + k._keyCode = key; } k._modifiers = tokens.join(","); tokens.forEach(function(token) { @@ -1479,13 +1497,17 @@ var windowsObserver = { }); } }, - updateHotkeys: function() { - _log("updateHotkeys()"); - this.initHotkeys(); + updateHotkeys: function(updateAll) { + _log("updateHotkeys(" + (updateAll || "") + ")"); + updateAll && this.initHotkeys(); var hasHotkeys = !!this.hotkeys; + var keyEvent = this.keyEvent; this.windows.forEach(function(window) { + window.removeEventListener("keydown", this, true); window.removeEventListener("keypress", this, true); - hasHotkeys && window.addEventListener("keypress", this, true); + hasHotkeys && window.addEventListener(keyEvent, this, true); + if(!updateAll) + return; var document = window.document; this.getHotkeysNodes(document, "*").forEach(function(node) { var cl = node.classList; diff --git a/defaults/preferences/prefs.js b/defaults/preferences/prefs.js index 97a0871..47ed63f 100644 --- a/defaults/preferences/prefs.js +++ b/defaults/preferences/prefs.js @@ -3,14 +3,16 @@ pref("extensions.privateTab.loadInBackground", -1); // 0 - load in foreground // 1 - load in background -pref("extensions.privateTab.keysIgnoreDefaultPrevented", false); // Ignore, if someone stops "keypress" event +pref("extensions.privateTab.keysUseKeydownEvent", true); // Use "keydown" event instead of "keypress" +pref("extensions.privateTab.keysIgnoreDefaultPrevented", false); // Ignore, if someone stops key* event // See https://developer.mozilla.org/en-US/docs/XUL/Tutorial/Keyboard_Shortcuts // and https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent#Virtual_key_codes // Syntax: [ ] +// It's better to use VK_* codes for keysUseKeydownEvent = true. // You can also create alias for hotkey using extensions.privateTab.key.%command%#%alias_name%, example: // pref("extensions.privateTab.key.openNewPrivateTab#2", "VK_F8") -pref("extensions.privateTab.key.openNewPrivateTab", "control alt p"); -pref("extensions.privateTab.key.toggleTabPrivate", "control alt v"); +pref("extensions.privateTab.key.openNewPrivateTab", "control alt VK_P"); +pref("extensions.privateTab.key.toggleTabPrivate", "control alt VK_V"); pref("extensions.privateTab.dragAndDropBehavior", 0); // 0 - make new (or target) tab private, if source or target are private diff --git a/virtualKeyCodes.js b/virtualKeyCodes.js new file mode 100644 index 0000000..9230634 --- /dev/null +++ b/virtualKeyCodes.js @@ -0,0 +1,47 @@ +function getVKChar(vk) { + // Firefox doesn't have string representation for some codes... + // https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent#Virtual_key_codes + if(/^VK_(?:NUMPAD)?([\d+A-Z])$/.test(vk)) + return RegExp.$1; + switch(vk) { + case "VK_SPACE": return " "; //32 + case "VK_COLON": return ":"; //58 + case "VK_SEMICOLON": return ";"; //59 + case "VK_LESS_THAN": return "<"; //60 + case "VK_EQUALS": return "="; //61 + case "VK_GREATER_THAN": return ">"; //62 + case "VK_QUESTION_MARK": return "?"; //63 + case "VK_AT": return "@"; //64 + case "VK_MULTIPLY": return "*"; //106 + case "VK_ADD": return "+"; //107 + case "VK_SUBTRACT": return "-"; //109 + case "VK_DECIMAL": return "."; //110 + case "VK_DIVIDE": return "/"; //111 + case "VK_CIRCUMFLEX": return "^"; //160 + case "VK_EXCLAMATION": return "!"; //161 + case "VK_DOUBLE_QUOTE": return '"'; //162 + case "VK_HASH": return "#"; //163 + case "VK_DOLLAR": return "$"; //164 + case "VK_PERCENT": return "%"; //165 + case "VK_AMPERSAND": return "&"; //166 + case "VK_UNDERSCORE": return "_"; //167 + case "OPEN_PAREN": return "("; //168 + case "CLOSE_PAREN": return ")"; //169 + case "VK_ASTERISK": return "*"; //170 + case "VK_PLUS": return "+"; //171 + case "VK_PIPE": return "|"; //172 + case "VK_HYPHEN_MINUS": return "-"; //173 + case "VK_OPEN_CURLY_BRACKET": return "{"; //174 + case "VK_CLOSE_CURLY_BRACKET": return "}"; //175 + case "VK_TILDE": return "~"; //176 + case "VK_COMMA": return ","; //188 + case "VK_PERIOD": return "."; //190 + case "VK_SLASH": return "/"; //191 + case "VK_BACK_QUOTE": return "`"; //192 + case "VK_OPEN_BRACKET": return "["; //219 + case "VK_BACK_SLASH": return "\\"; //220 + case "VK_CLOSE_BRACKET": return "]"; //221 + case "VK_QUOTE": return "'"; //222 + } + return undefined; +} \ No newline at end of file