Bug 1343955 - part 2: Implement _guessCodeFromKeyName() in EventUtils.js with C++ and make it accessible with nsITextInputProcessor for EventUtils.js r=smaug

We need to port synthesizeKey() of EventUtils.js to FuzzingFunctions.  So,
its helper function, _guessCodeFromKeyName() in EventUtils.js needs to be
accessible from FuzzingFunctions.  Therefore, we need to reimplement it
with C++ and make it accessible via nsITextInputProcessor for EventUtils.js
for making easier to maintain.

This patch moves _guessCodeFromKeyName() into TextInputProcessor and
WidgetKeyboardEvent.  Non-printable key part of _guessCodeFromKeyName() is
moved to WidgetKeyboardEvent::ComputeCodeNameIndexFromKeyNameIndex() because
of not depending on active keyboard layout.  On the other hand, printable
key part needs to assume that active keyboard layout is en-US keyboard layout.
Therefore, it's moved to
TextInputProcessor::GuessCodeValueOfPrintableKeyInUSEnglishKeyboardLayout()
because any Core code shouldn't refer it as utility method for keeping that
we're i18n-aware.

Differential Revision: https://phabricator.services.mozilla.com/D5514

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Masayuki Nakano 2018-10-03 09:21:47 +00:00
parent d1540c3e0a
commit eb2efb612f
6 changed files with 737 additions and 273 deletions

View File

@ -7,6 +7,7 @@
#include "gfxPrefs.h"
#include "mozilla/dom/Event.h"
#include "mozilla/EventForwards.h"
#include "mozilla/Maybe.h"
#include "mozilla/TextEventDispatcher.h"
#include "mozilla/TextEvents.h"
#include "mozilla/TextInputProcessor.h"
@ -1275,6 +1276,292 @@ TextInputProcessor::ShareModifierStateOf(nsITextInputProcessor* aOther)
return NS_OK;
}
NS_IMETHODIMP
TextInputProcessor::ComputeCodeValueOfNonPrintableKey(
const nsAString& aKeyValue,
JS::Handle<JS::Value> aLocation,
uint8_t aOptionalArgc,
nsAString& aCodeValue)
{
aCodeValue.Truncate();
Maybe<uint32_t> location;
if (aOptionalArgc) {
if (aLocation.isNullOrUndefined()) {
// location should be nothing.
} else if (aLocation.isInt32()) {
location = mozilla::Some(static_cast<uint32_t>(aLocation.toInt32()));
} else {
NS_WARNING_ASSERTION(aLocation.isNullOrUndefined() || aLocation.isInt32(),
"aLocation must be undefined, null or int");
return NS_ERROR_INVALID_ARG;
}
}
KeyNameIndex keyNameIndex = WidgetKeyboardEvent::GetKeyNameIndex(aKeyValue);
if (keyNameIndex == KEY_NAME_INDEX_Unidentified ||
keyNameIndex == KEY_NAME_INDEX_USE_STRING) {
return NS_OK;
}
CodeNameIndex codeNameIndex =
WidgetKeyboardEvent::ComputeCodeNameIndexFromKeyNameIndex(keyNameIndex,
location);
if (codeNameIndex == CODE_NAME_INDEX_UNKNOWN) {
return NS_OK;
}
MOZ_ASSERT(codeNameIndex != CODE_NAME_INDEX_USE_STRING);
WidgetKeyboardEvent::GetDOMCodeName(codeNameIndex, aCodeValue);
return NS_OK;
}
NS_IMETHODIMP
TextInputProcessor::GuessCodeValueOfPrintableKeyInUSEnglishKeyboardLayout(
const nsAString& aKeyValue,
JS::Handle<JS::Value> aLocation,
uint8_t aOptionalArgc,
nsAString& aCodeValue)
{
aCodeValue.Truncate();
Maybe<uint32_t> location;
if (aOptionalArgc) {
if (aLocation.isNullOrUndefined()) {
// location should be nothing.
} else if (aLocation.isInt32()) {
location = mozilla::Some(static_cast<uint32_t>(aLocation.toInt32()));
} else {
NS_WARNING_ASSERTION(aLocation.isNullOrUndefined() || aLocation.isInt32(),
"aLocation must be undefined, null or int");
return NS_ERROR_INVALID_ARG;
}
}
CodeNameIndex codeNameIndex =
GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout(aKeyValue, location);
if (codeNameIndex == CODE_NAME_INDEX_UNKNOWN) {
return NS_OK;
}
MOZ_ASSERT(codeNameIndex != CODE_NAME_INDEX_USE_STRING);
WidgetKeyboardEvent::GetDOMCodeName(codeNameIndex, aCodeValue);
return NS_OK;
}
// static
CodeNameIndex
TextInputProcessor::GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout(
const nsAString& aKeyValue,
const Maybe<uint32_t>& aLocation)
{
if (aKeyValue.IsEmpty()) {
return CODE_NAME_INDEX_UNKNOWN;
}
// US keyboard layout can input only one character per key. So, we can
// assume that if the key value is 2 or more characters, it's a known
// key name or not a usual key emulation.
if (aKeyValue.Length() > 1) {
return CODE_NAME_INDEX_UNKNOWN;
}
if (aLocation.isSome() &&
aLocation.value() ==
dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD) {
switch (aKeyValue[0]) {
case '+':
return CODE_NAME_INDEX_NumpadAdd;
case '-':
return CODE_NAME_INDEX_NumpadSubtract;
case '*':
return CODE_NAME_INDEX_NumpadMultiply;
case '/':
return CODE_NAME_INDEX_NumpadDivide;
case '.':
return CODE_NAME_INDEX_NumpadDecimal;
case '0':
return CODE_NAME_INDEX_Numpad0;
case '1':
return CODE_NAME_INDEX_Numpad1;
case '2':
return CODE_NAME_INDEX_Numpad2;
case '3':
return CODE_NAME_INDEX_Numpad3;
case '4':
return CODE_NAME_INDEX_Numpad4;
case '5':
return CODE_NAME_INDEX_Numpad5;
case '6':
return CODE_NAME_INDEX_Numpad6;
case '7':
return CODE_NAME_INDEX_Numpad7;
case '8':
return CODE_NAME_INDEX_Numpad8;
case '9':
return CODE_NAME_INDEX_Numpad9;
default:
return CODE_NAME_INDEX_UNKNOWN;
}
}
if (aLocation.isSome() &&
aLocation.value() !=
dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_STANDARD) {
return CODE_NAME_INDEX_UNKNOWN;
}
// TODO: Support characters inputted with option key on macOS.
switch (aKeyValue[0]) {
case 'a':
case 'A':
return CODE_NAME_INDEX_KeyA;
case 'b':
case 'B':
return CODE_NAME_INDEX_KeyB;
case 'c':
case 'C':
return CODE_NAME_INDEX_KeyC;
case 'd':
case 'D':
return CODE_NAME_INDEX_KeyD;
case 'e':
case 'E':
return CODE_NAME_INDEX_KeyE;
case 'f':
case 'F':
return CODE_NAME_INDEX_KeyF;
case 'g':
case 'G':
return CODE_NAME_INDEX_KeyG;
case 'h':
case 'H':
return CODE_NAME_INDEX_KeyH;
case 'i':
case 'I':
return CODE_NAME_INDEX_KeyI;
case 'j':
case 'J':
return CODE_NAME_INDEX_KeyJ;
case 'k':
case 'K':
return CODE_NAME_INDEX_KeyK;
case 'l':
case 'L':
return CODE_NAME_INDEX_KeyL;
case 'm':
case 'M':
return CODE_NAME_INDEX_KeyM;
case 'n':
case 'N':
return CODE_NAME_INDEX_KeyN;
case 'o':
case 'O':
return CODE_NAME_INDEX_KeyO;
case 'p':
case 'P':
return CODE_NAME_INDEX_KeyP;
case 'q':
case 'Q':
return CODE_NAME_INDEX_KeyQ;
case 'r':
case 'R':
return CODE_NAME_INDEX_KeyR;
case 's':
case 'S':
return CODE_NAME_INDEX_KeyS;
case 't':
case 'T':
return CODE_NAME_INDEX_KeyT;
case 'u':
case 'U':
return CODE_NAME_INDEX_KeyU;
case 'v':
case 'V':
return CODE_NAME_INDEX_KeyV;
case 'w':
case 'W':
return CODE_NAME_INDEX_KeyW;
case 'x':
case 'X':
return CODE_NAME_INDEX_KeyX;
case 'y':
case 'Y':
return CODE_NAME_INDEX_KeyY;
case 'z':
case 'Z':
return CODE_NAME_INDEX_KeyZ;
case '`':
case '~':
return CODE_NAME_INDEX_Backquote;
case '1':
case '!':
return CODE_NAME_INDEX_Digit1;
case '2':
case '@':
return CODE_NAME_INDEX_Digit2;
case '3':
case '#':
return CODE_NAME_INDEX_Digit3;
case '4':
case '$':
return CODE_NAME_INDEX_Digit4;
case '5':
case '%':
return CODE_NAME_INDEX_Digit5;
case '6':
case '^':
return CODE_NAME_INDEX_Digit6;
case '7':
case '&':
return CODE_NAME_INDEX_Digit7;
case '8':
case '*':
return CODE_NAME_INDEX_Digit8;
case '9':
case '(':
return CODE_NAME_INDEX_Digit9;
case '0':
case ')':
return CODE_NAME_INDEX_Digit0;
case '-':
case '_':
return CODE_NAME_INDEX_Minus;
case '=':
case '+':
return CODE_NAME_INDEX_Equal;
case '[':
case '{':
return CODE_NAME_INDEX_BracketLeft;
case ']':
case '}':
return CODE_NAME_INDEX_BracketRight;
case '\\':
case '|':
return CODE_NAME_INDEX_Backslash;
case ';':
case ':':
return CODE_NAME_INDEX_Semicolon;
case '\'':
case '"':
return CODE_NAME_INDEX_Quote;
case ',':
case '<':
return CODE_NAME_INDEX_Comma;
case '.':
case '>':
return CODE_NAME_INDEX_Period;
case '/':
case '?':
return CODE_NAME_INDEX_Slash;
case ' ':
return CODE_NAME_INDEX_Space;
default:
return CODE_NAME_INDEX_UNKNOWN;
}
}
/******************************************************************************
* TextInputProcessor::AutoPendingCompositionResetter
******************************************************************************/

View File

@ -9,6 +9,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/EventForwards.h"
#include "mozilla/Maybe.h"
#include "mozilla/TextEventDispatcher.h"
#include "mozilla/TextEventDispatcherListener.h"
#include "nsITextInputProcessor.h"
@ -49,6 +50,26 @@ public:
uint32_t aIndexOfKeypress,
void* aData) override;
/**
* GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout() returns CodeNameIndex
* of a printable key which is in usual keyboard of the platform and when
* active keyboard layout is US-English.
* Note that this does not aware of option key mapping on macOS.
*
* @param aKeyValue The key value. Must be a character which can
* be inputted with US-English keyboard layout.
* @param aLocation The location of the key. This is important
* to distinguish whether the key is in Standard
* or Numpad. If this is not some, treated as
* Standard.
* @return Returns CODE_NAME_INDEX_UNKNOWN if there is
* no proper key.
*/
static CodeNameIndex
GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout(
const nsAString& aKeyValue,
const Maybe<uint32_t>& aLocation);
protected:
virtual ~TextInputProcessor();

View File

@ -605,6 +605,44 @@ interface nsITextInputProcessor : nsISupports
* to manage modifier state independently.
*/
void shareModifierStateOf(in nsITextInputProcessor aOther);
/**
* Helper method to get usual |.code| value of non-printable keys.
*
* @param aKeyValue A predefined key value such as "Enter".
* If this is not a proper non-printable key value
* or a proper key value but not in usual keyboard of
* the platform, this returns empty string.
* @param aLocation The |.location| value. This is important if
* the key may be in different location.
* E.g., Left vs. Right or Standard vs. Numpad.
* If this is undefined or null, it'll be treated
* as Standard or Left.
* @return One of a code value of well-known key on usual
* keyboard on the platform, or empty string.
*/
[optional_argc]
AString computeCodeValueOfNonPrintableKey(
in AString aKeyValue,
[optional] in jsval aLocation);
/**
* Helper method to guess |.code| value of a printable key which is in usual
* keyboard of the platform and when active keyboard layout is US-English.
* Note that this is not aware of option key mapping on macOS.
*
* @param aKeyValue The key value. Must be a character which can
* be inputted with US-English keyboard layout.
* @param aLocation The location of the key. This is important
* to distinguish whether the key is in Standard
* or Numpad. If this is undefined or null, will
* be treated as Standard.
* @return Returns empty string if there is no proper key.
*/
[optional_argc]
AString guessCodeValueOfPrintableKeyInUSEnglishKeyboardLayout(
in AString aKeyValue,
[optional] in jsval aLocation);
};
%{C++

View File

@ -934,7 +934,7 @@ function synthesizeKey(aKey, aEvent = undefined, aWindow = window, aCallback)
}
var KeyboardEvent = _getKeyboardEvent(aWindow);
var modifiers = _emulateToActivateModifiers(TIP, event, aWindow);
var keyEventDict = _createKeyboardEventDictionary(aKey, event, aWindow);
var keyEventDict = _createKeyboardEventDictionary(aKey, event, TIP, aWindow);
var keyEvent = new KeyboardEvent("", keyEventDict.dictionary);
var dispatchKeydown =
!("type" in event) || event.type === "keydown" || !event.type;
@ -973,7 +973,7 @@ function synthesizeAndWaitKey(aKey, aEvent, aWindow = window,
{
let browser = gBrowser.selectedTab.linkedBrowser;
let mm = browser.messageManager;
let keyCode = _createKeyboardEventDictionary(aKey, aEvent, aWindow).dictionary.keyCode;
let keyCode = _createKeyboardEventDictionary(aKey, aEvent, null, aWindow).dictionary.keyCode;
let ContentTask = _EU_Cu.import("resource://testing-common/ContentTask.jsm", null).ContentTask;
let keyRegisteredPromise = new Promise(resolve => {
@ -1518,279 +1518,26 @@ function _guessKeyNameFromKeyCode(aKeyCode, aWindow = window)
}
}
function _guessCodeFromKeyName(aKeyName, aLocation, aWindow = window)
{
var KeyboardEvent = _getKeyboardEvent(aWindow);
if (aLocation === KeyboardEvent.DOM_KEY_LOCATION_NUMPAD) {
switch (aKeyName) {
case "Insert":
return _EU_isMac(aWindow) ? "" : "Numpad0";
case "End":
return _EU_isMac(aWindow) ? "" : "Numpad1";
case "ArrowDown":
return _EU_isMac(aWindow) ? "" : "Numpad2";
case "PageDown":
return _EU_isMac(aWindow) ? "" : "Numpad3";
case "ArrowLeft":
return _EU_isMac(aWindow) ? "" : "Numpad4";
case "Clear":
return !_EU_isWin(aWindow) ? "" : "Numpad5";
case "ArrowRight":
return _EU_isMac(aWindow) ? "" : "Numpad6";
case "Home":
return _EU_isMac(aWindow) ? "" : "Numpad7";
case "ArrowUp":
return _EU_isMac(aWindow) ? "" : "Numpad8";
case "PageUp":
return _EU_isMac(aWindow) ? "" : "Numpad9";
case "Delete":
return _EU_isMac(aWindow) ? "" : "NumpadDecimal";
case "Enter":
return "NumpadEnter";
case "=":
return "NumpadEqual";
case "+":
return "NumpadAdd";
case "-":
return "NumpadSubtract";
case "*":
return "NumpadMultiply";
case "/":
return "NumpadDivide";
case "0":
case "1":
case "2":
case "3":
case "4":
case "5":
case "6":
case "7":
case "8":
case "9":
return "Numpad" + aKeyName;
default:
// FYI: NumLock (Clear on macOS) should be DOM_KEY_LOCATION_STANDARD.
return "";
}
}
if (aLocation === undefined ||
aLocation === KeyboardEvent.DOM_KEY_LOCATION_LEFT ||
aLocation === KeyboardEvent.DOM_KEY_LOCATION_RIGHT) {
function getLeftOrRightCode(aKey)
{
if (aLocation === undefined) {
return aKey + "Left";
}
if (aLocation === KeyboardEvent.DOM_KEY_LOCATION_LEFT) {
return aKey + "Left";
}
if (aLocation === KeyboardEvent.DOM_KEY_LOCATION_RIGHT) {
return aKey + "Right";
}
// If location value is illegal for left or right key, perhaps,
// it tries to emulate a virtual keyboard's event or something odd.
return "";
}
switch (aKeyName) {
case "Alt":
case "Control":
case "Shift":
return getLeftOrRightCode(aKeyName);
case "Meta":
if (_EU_isWin(aWindow)) {
return "";
}
if (_EU_isAndroid(aWindow) || _EU_isMac(aWindow)) {
return getLeftOrRightCode("OS");
}
// On Linux, Alt + Shift is "Meta".
return getLeftOrRightCode("Alt");
case "OS": // bug 1232918
if (_EU_isAndroid(aWindow) || _EU_isMac(aWindow)) {
return "";
}
return getLeftOrRightCode("OS");
}
}
if (aLocation === undefined || aLocation === 0) {
switch (aKeyName) {
// Same as key name.
case "ArrowDown":
case "ArrowLeft":
case "ArrowRight":
case "ArrowUp":
case "Backspace":
case "CapsLock":
case "ContextMenu":
case "Delete":
case "End":
case "Enter":
case "Escape":
case "F1":
case "F2":
case "F3":
case "F4":
case "F5":
case "F6":
case "F7":
case "F8":
case "F9":
case "F10":
case "F11":
case "F12":
case "F13":
case "F14":
case "F15":
case "F16":
case "F17":
case "F18":
case "F19":
case "F20":
case "Home":
case "PageDown":
case "PageUp":
case "Tab":
return aKeyName;
// Same as key name but not available only on macOS.
case "BrowserBack":
case "BrowserFavorites":
case "BrowserForward":
case "BrowserRefresh":
case "BrowserSearch":
case "BrowserStop":
case "F21":
case "F22":
case "F23":
case "F24":
case "Insert":
case "MediaPlayPause":
case "MediaStop":
case "MediaTrackNext":
case "MediaTrackPrevious":
case "Pause":
case "PrintScreen":
case "ScrollLock":
return _EU_isMac(aWindow) ? "" : aKeyName;
// Same as key name but available only on macOS.
case "Clear":
case "Fn":
return _EU_isMac(aWindow) ? aKeyName : "";
// Same as key name but not available only on Windows.
case "Help":
return _EU_isMac(aWindow) ? "" : aKeyName;
// Same as key name but available only on Windows and Linux.
case "BrowserHome":
return _EU_isWin(aWindow) || _EU_isLinux(aWindow) ? aKeyName : "";
// Same as key name but available only on Linux and Android.
case "Eject":
case "WakeUp":
return _EU_isLinux(aWindow) || _EU_isAndroid(aWindow) ? aKeyName : "";
// Special cases.
case "Break":
return !_EU_isMac(aWindow) ? "Pause" : "";
case "AudioVolumeDown":
case "AudioVolumeMute":
case "AudioVolumeUp":
return aKeyName.substr("Audio".length); // bug 1272579
case "LaunchApplication1":
return !_EU_isMac(aWindow) ? "LaunchApp1" : "";
case "LaunchApplication2":
return _EU_isWin(aWindow) || _EU_isLinux(aWindow) ? "LaunchApp2" : "";
// TODO: this function and synthesizeKey() should be able to take
// keyboard layout name optionally.
default:
if (aKeyName.length != 1) {
return "";
}
if (aKeyName.charCodeAt(0) >= "A".charCodeAt(0) &&
aKeyName.charCodeAt(0) <= "Z".charCodeAt(0)) {
return "Key" + aKeyName;
}
if (aKeyName.charCodeAt(0) >= "a".charCodeAt(0) &&
aKeyName.charCodeAt(0) <= "z".charCodeAt(0)) {
return "Key" + aKeyName.toUpperCase();
}
if (aKeyName.charCodeAt(0) >= "0".charCodeAt(0) &&
aKeyName.charCodeAt(0) <= "9".charCodeAt(0)) {
return "Digit" + aKeyName;
}
switch (aKeyName) {
case " ":
return "Space";
case "`":
case "~":
return "Backquote";
case "\\":
case "|":
return "Backslash";
case "[":
case "{":
return "BracketLeft";
case "]":
case "}":
return "BracketRight";
case ",":
case "<":
return "Comma";
case ")":
return "Digit0";
case "!":
return "Digit1";
case "@":
return "Digit2";
case "#":
return "Digit3";
case "$":
return "Digit4";
case "%":
return "Digit5";
case "^":
return "Digit6";
case "&":
return "Digit7";
case "*":
return "Digit8";
case "(":
return "Digit9";
case "=":
case "+":
return "Equal";
case "-":
case "_":
return "Minus";
case ".":
case ">":
return "Period";
case "'":
case "\"":
return "Quote";
case ";":
case ":":
return "Semicolon";
case "/":
case "?":
return "Slash";
default:
return "";
}
}
}
return "";
}
function _createKeyboardEventDictionary(aKey, aKeyEvent, aWindow = window) {
function _createKeyboardEventDictionary(aKey, aKeyEvent,
aTIP = null,
aWindow = window) {
var result = { dictionary: null, flags: 0 };
var keyCodeIsDefined = "keyCode" in aKeyEvent;
var keyCode =
(keyCodeIsDefined && aKeyEvent.keyCode >= 0 && aKeyEvent.keyCode <= 255) ?
aKeyEvent.keyCode : 0;
var keyName = "Unidentified";
var code = aKeyEvent.code;
if (!aTIP) {
aTIP = _getTIP(aWindow);
}
if (aKey.indexOf("KEY_") == 0) {
keyName = aKey.substr("KEY_".length);
result.flags |= _EU_Ci.nsITextInputProcessor.KEY_NON_PRINTABLE_KEY;
if (code === undefined) {
code =
aTIP.computeCodeValueOfNonPrintableKey(keyName, aKeyEvent.location);
}
} else if (aKey.indexOf("VK_") == 0) {
keyCode = _getKeyboardEvent(aWindow)["DOM_" + aKey];
if (!keyCode) {
@ -1798,6 +1545,10 @@ function _createKeyboardEventDictionary(aKey, aKeyEvent, aWindow = window) {
}
keyName = _guessKeyNameFromKeyCode(keyCode, aWindow);
result.flags |= _EU_Ci.nsITextInputProcessor.KEY_NON_PRINTABLE_KEY;
if (code === undefined) {
code =
aTIP.computeCodeValueOfNonPrintableKey(keyName, aKeyEvent.location);
}
} else if (aKey != "") {
keyName = aKey;
if (!keyCodeIsDefined) {
@ -1807,10 +1558,11 @@ function _createKeyboardEventDictionary(aKey, aKeyEvent, aWindow = window) {
result.flags |= _EU_Ci.nsITextInputProcessor.KEY_KEEP_KEYCODE_ZERO;
}
result.flags |= _EU_Ci.nsITextInputProcessor.KEY_FORCE_PRINTABLE_KEY;
if (code === undefined) {
code = aTIP.guessCodeValueOfPrintableKeyInUSEnglishKeyboardLayout(
keyName, aKeyEvent.location);
}
}
var code = "code" in aKeyEvent ?
aKeyEvent.code :
_guessCodeFromKeyName(keyName, aKeyEvent.location, aWindow);
var locationIsDefined = "location" in aKeyEvent;
if (locationIsDefined && aKeyEvent.location === 0) {
result.flags |= _EU_Ci.nsITextInputProcessor.KEY_KEEP_KEY_LOCATION_STANDARD;
@ -1979,14 +1731,15 @@ function synthesizeComposition(aEvent, aWindow = window, aCallback)
var keyEvent = null;
if (aEvent.key && typeof aEvent.key.key === "string") {
keyEventDict =
_createKeyboardEventDictionary(aEvent.key.key, aEvent.key, aWindow);
_createKeyboardEventDictionary(aEvent.key.key, aEvent.key, TIP, aWindow);
keyEvent = new KeyboardEvent(aEvent.key.type === "keydown" ?
"keydown" :
aEvent.key.type === "keyup" ?
"keyup" : "",
keyEventDict.dictionary)
} else if (aEvent.key === undefined) {
keyEventDict = _createKeyboardEventDictionary("KEY_Process", {}, aWindow);
keyEventDict =
_createKeyboardEventDictionary("KEY_Process", {}, TIP, aWindow);
keyEvent = new KeyboardEvent("", keyEventDict.dictionary)
}
try {
@ -2115,14 +1868,16 @@ function synthesizeCompositionChange(aEvent, aWindow = window, aCallback)
var keyEvent = null;
if (aEvent.key && typeof aEvent.key.key === "string") {
keyEventDict =
_createKeyboardEventDictionary(aEvent.key.key, aEvent.key, aWindow);
_createKeyboardEventDictionary(aEvent.key.key, aEvent.key,
TIP, aWindow);
keyEvent = new KeyboardEvent(aEvent.key.type === "keydown" ?
"keydown" :
aEvent.key.type === "keyup" ?
"keyup" : "",
keyEventDict.dictionary)
} else if (aEvent.key === undefined) {
keyEventDict = _createKeyboardEventDictionary("KEY_Process", {}, aWindow);
keyEventDict =
_createKeyboardEventDictionary("KEY_Process", {}, TIP, aWindow);
keyEvent = new KeyboardEvent("", keyEventDict.dictionary)
}
TIP.flushPendingComposition(keyEvent, keyEventDict.flags);

View File

@ -13,6 +13,7 @@
#include "mozilla/CheckedInt.h"
#include "mozilla/EventForwards.h" // for KeyNameIndex, temporarily
#include "mozilla/FontRange.h"
#include "mozilla/Maybe.h"
#include "mozilla/TextRange.h"
#include "mozilla/WritingModes.h"
#include "mozilla/dom/KeyboardEventBinding.h"
@ -592,12 +593,49 @@ public:
*/
static uint32_t ComputeKeyCodeFromKeyNameIndex(KeyNameIndex aKeyNameIndex);
/**
* ComputeCodeNameIndexFromKeyNameIndex() returns a code name index which
* is typically mapped to given key name index on the platform.
* Note that this returns CODE_NAME_INDEX_UNKNOWN if the key name index is
* KEY_NAME_INDEX_Unidentified or KEY_NAME_INDEX_USE_STRING.
* This means that this method is useful only for non-printable keys.
*
* @param aKeyNameIndex A non-printable key name index.
* @param aLocation Should be one of location value. This is
* important when aKeyNameIndex may exist in
* both Numpad or Standard, or in both Left or
* Right. If this is nothing, this method
* returns Left or Standard position's code
* value.
*/
static CodeNameIndex
ComputeCodeNameIndexFromKeyNameIndex(KeyNameIndex aKeyNameIndex,
const Maybe<uint32_t>& aLocation);
/**
* GetModifierForKeyName() returns a value of Modifier which is activated
* by the aKeyNameIndex.
*/
static Modifier GetModifierForKeyName(KeyNameIndex aKeyNameIndex);
/**
* IsLeftOrRightModiferKeyNameIndex() returns true if aKeyNameIndex is a
* modifier key which may be in Left and Right location.
*/
static bool IsLeftOrRightModiferKeyNameIndex(KeyNameIndex aKeyNameIndex)
{
switch (aKeyNameIndex) {
case KEY_NAME_INDEX_Alt:
case KEY_NAME_INDEX_Control:
case KEY_NAME_INDEX_Meta:
case KEY_NAME_INDEX_OS:
case KEY_NAME_INDEX_Shift:
return true;
default:
return false;
}
}
/**
* IsLockableModifier() returns true if aKeyNameIndex is a lockable modifier
* key such as CapsLock and NumLock.

View File

@ -1448,6 +1448,331 @@ WidgetKeyboardEvent::ComputeKeyCodeFromKeyNameIndex(KeyNameIndex aKeyNameIndex)
}
}
/* static */ CodeNameIndex
WidgetKeyboardEvent::ComputeCodeNameIndexFromKeyNameIndex(
KeyNameIndex aKeyNameIndex,
const Maybe<uint32_t>& aLocation)
{
if (aLocation.isSome() &&
aLocation.value() ==
dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD) {
// On macOS, NumLock is not supported. Therefore, this handles
// control key values except "Enter" only on non-macOS platforms.
switch (aKeyNameIndex) {
#ifndef XP_MACOSX
case KEY_NAME_INDEX_Insert:
return CODE_NAME_INDEX_Numpad0;
case KEY_NAME_INDEX_End:
return CODE_NAME_INDEX_Numpad1;
case KEY_NAME_INDEX_ArrowDown:
return CODE_NAME_INDEX_Numpad2;
case KEY_NAME_INDEX_PageDown:
return CODE_NAME_INDEX_Numpad3;
case KEY_NAME_INDEX_ArrowLeft:
return CODE_NAME_INDEX_Numpad4;
case KEY_NAME_INDEX_Clear:
// FYI: "Clear" on macOS should be DOM_KEY_LOCATION_STANDARD.
return CODE_NAME_INDEX_Numpad5;
case KEY_NAME_INDEX_ArrowRight:
return CODE_NAME_INDEX_Numpad6;
case KEY_NAME_INDEX_Home:
return CODE_NAME_INDEX_Numpad7;
case KEY_NAME_INDEX_ArrowUp:
return CODE_NAME_INDEX_Numpad8;
case KEY_NAME_INDEX_PageUp:
return CODE_NAME_INDEX_Numpad9;
case KEY_NAME_INDEX_Delete:
return CODE_NAME_INDEX_NumpadDecimal;
#endif // #ifndef XP_MACOSX
case KEY_NAME_INDEX_Enter:
return CODE_NAME_INDEX_NumpadEnter;
default:
return CODE_NAME_INDEX_UNKNOWN;
}
}
if (WidgetKeyboardEvent::IsLeftOrRightModiferKeyNameIndex(aKeyNameIndex)) {
if (aLocation.isSome() &&
(aLocation.value() !=
dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_LEFT &&
aLocation.value() !=
dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_RIGHT)) {
return CODE_NAME_INDEX_UNKNOWN;
}
bool isRight =
aLocation.isSome() &&
aLocation.value() == dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_RIGHT;
switch (aKeyNameIndex) {
case KEY_NAME_INDEX_Alt:
return isRight ? CODE_NAME_INDEX_AltRight : CODE_NAME_INDEX_AltLeft;
case KEY_NAME_INDEX_Control:
return isRight ? CODE_NAME_INDEX_ControlRight :
CODE_NAME_INDEX_ControlLeft;
case KEY_NAME_INDEX_Shift:
return isRight ? CODE_NAME_INDEX_ShiftRight :
CODE_NAME_INDEX_ShiftLeft;
#if defined(XP_WIN)
case KEY_NAME_INDEX_Meta:
return CODE_NAME_INDEX_UNKNOWN;
case KEY_NAME_INDEX_OS: // win key.
return isRight ? CODE_NAME_INDEX_OSRight : CODE_NAME_INDEX_OSLeft;
#elif defined(XP_MACOSX) || defined(ANDROID)
case KEY_NAME_INDEX_Meta: // command key.
return isRight ? CODE_NAME_INDEX_OSRight : CODE_NAME_INDEX_OSLeft;
case KEY_NAME_INDEX_OS:
return CODE_NAME_INDEX_UNKNOWN;
#else
case KEY_NAME_INDEX_Meta: // Alt + Shift.
return isRight ? CODE_NAME_INDEX_AltRight : CODE_NAME_INDEX_AltLeft;
case KEY_NAME_INDEX_OS: // Super/Hyper key.
return isRight ? CODE_NAME_INDEX_OSRight : CODE_NAME_INDEX_OSLeft;
#endif
default:
return CODE_NAME_INDEX_UNKNOWN;
}
}
if (aLocation.isSome() &&
aLocation.value() !=
dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_STANDARD) {
return CODE_NAME_INDEX_UNKNOWN;
}
switch (aKeyNameIndex) {
// Standard section:
case KEY_NAME_INDEX_Escape:
return CODE_NAME_INDEX_Escape;
case KEY_NAME_INDEX_Tab:
return CODE_NAME_INDEX_Tab;
case KEY_NAME_INDEX_CapsLock:
return CODE_NAME_INDEX_CapsLock;
case KEY_NAME_INDEX_ContextMenu:
return CODE_NAME_INDEX_ContextMenu;
case KEY_NAME_INDEX_Backspace:
return CODE_NAME_INDEX_Backspace;
case KEY_NAME_INDEX_Enter:
return CODE_NAME_INDEX_Enter;
#ifdef XP_MACOSX
// Although, macOS does not fire native key event of "Fn" key, we support
// Fn key event if it's sent by other apps directly.
case KEY_NAME_INDEX_Fn:
return CODE_NAME_INDEX_Fn;
#endif // #ifdef
// Arrow Pad section:
case KEY_NAME_INDEX_ArrowLeft:
return CODE_NAME_INDEX_ArrowLeft;
case KEY_NAME_INDEX_ArrowUp:
return CODE_NAME_INDEX_ArrowUp;
case KEY_NAME_INDEX_ArrowDown:
return CODE_NAME_INDEX_ArrowDown;
case KEY_NAME_INDEX_ArrowRight:
return CODE_NAME_INDEX_ArrowRight;
// Control Pad section:
#ifndef XP_MACOSX
case KEY_NAME_INDEX_Insert:
return CODE_NAME_INDEX_Insert;
#else
case KEY_NAME_INDEX_Help:
return CODE_NAME_INDEX_Help;
#endif // #ifndef XP_MACOSX #else
case KEY_NAME_INDEX_Delete:
return CODE_NAME_INDEX_Delete;
case KEY_NAME_INDEX_Home:
return CODE_NAME_INDEX_Home;
case KEY_NAME_INDEX_End:
return CODE_NAME_INDEX_End;
case KEY_NAME_INDEX_PageUp:
return CODE_NAME_INDEX_PageUp;
case KEY_NAME_INDEX_PageDown:
return CODE_NAME_INDEX_PageDown;
// Function keys:
case KEY_NAME_INDEX_F1:
return CODE_NAME_INDEX_F1;
case KEY_NAME_INDEX_F2:
return CODE_NAME_INDEX_F2;
case KEY_NAME_INDEX_F3:
return CODE_NAME_INDEX_F3;
case KEY_NAME_INDEX_F4:
return CODE_NAME_INDEX_F4;
case KEY_NAME_INDEX_F5:
return CODE_NAME_INDEX_F5;
case KEY_NAME_INDEX_F6:
return CODE_NAME_INDEX_F6;
case KEY_NAME_INDEX_F7:
return CODE_NAME_INDEX_F7;
case KEY_NAME_INDEX_F8:
return CODE_NAME_INDEX_F8;
case KEY_NAME_INDEX_F9:
return CODE_NAME_INDEX_F9;
case KEY_NAME_INDEX_F10:
return CODE_NAME_INDEX_F10;
case KEY_NAME_INDEX_F11:
return CODE_NAME_INDEX_F11;
case KEY_NAME_INDEX_F12:
return CODE_NAME_INDEX_F12;
case KEY_NAME_INDEX_F13:
return CODE_NAME_INDEX_F13;
case KEY_NAME_INDEX_F14:
return CODE_NAME_INDEX_F14;
case KEY_NAME_INDEX_F15:
return CODE_NAME_INDEX_F15;
case KEY_NAME_INDEX_F16:
return CODE_NAME_INDEX_F16;
case KEY_NAME_INDEX_F17:
return CODE_NAME_INDEX_F17;
case KEY_NAME_INDEX_F18:
return CODE_NAME_INDEX_F18;
case KEY_NAME_INDEX_F19:
return CODE_NAME_INDEX_F19;
case KEY_NAME_INDEX_F20:
return CODE_NAME_INDEX_F20;
#ifndef XP_MACOSX
case KEY_NAME_INDEX_F21:
return CODE_NAME_INDEX_F21;
case KEY_NAME_INDEX_F22:
return CODE_NAME_INDEX_F22;
case KEY_NAME_INDEX_F23:
return CODE_NAME_INDEX_F23;
case KEY_NAME_INDEX_F24:
return CODE_NAME_INDEX_F24;
case KEY_NAME_INDEX_Pause:
return CODE_NAME_INDEX_Pause;
case KEY_NAME_INDEX_PrintScreen:
return CODE_NAME_INDEX_PrintScreen;
case KEY_NAME_INDEX_ScrollLock:
return CODE_NAME_INDEX_ScrollLock;
#endif // #ifndef XP_MACOSX
// NumLock key:
#ifndef XP_MACOSX
case KEY_NAME_INDEX_NumLock:
return CODE_NAME_INDEX_NumLock;
#else
case KEY_NAME_INDEX_Clear:
return CODE_NAME_INDEX_NumLock;
#endif // #ifndef XP_MACOSX #else
// Media keys:
case KEY_NAME_INDEX_AudioVolumeDown:
return CODE_NAME_INDEX_VolumeDown;
case KEY_NAME_INDEX_AudioVolumeMute:
return CODE_NAME_INDEX_VolumeMute;
case KEY_NAME_INDEX_AudioVolumeUp:
return CODE_NAME_INDEX_VolumeUp;
#ifndef XP_MACOSX
case KEY_NAME_INDEX_BrowserBack:
return CODE_NAME_INDEX_BrowserBack;
case KEY_NAME_INDEX_BrowserFavorites:
return CODE_NAME_INDEX_BrowserFavorites;
case KEY_NAME_INDEX_BrowserForward:
return CODE_NAME_INDEX_BrowserForward;
case KEY_NAME_INDEX_BrowserRefresh:
return CODE_NAME_INDEX_BrowserRefresh;
case KEY_NAME_INDEX_BrowserSearch:
return CODE_NAME_INDEX_BrowserSearch;
case KEY_NAME_INDEX_BrowserStop:
return CODE_NAME_INDEX_BrowserStop;
case KEY_NAME_INDEX_MediaPlayPause:
return CODE_NAME_INDEX_MediaPlayPause;
case KEY_NAME_INDEX_MediaStop:
return CODE_NAME_INDEX_MediaStop;
case KEY_NAME_INDEX_MediaTrackNext:
return CODE_NAME_INDEX_MediaTrackNext;
case KEY_NAME_INDEX_MediaTrackPrevious:
return CODE_NAME_INDEX_MediaTrackPrevious;
case KEY_NAME_INDEX_LaunchApplication1:
return CODE_NAME_INDEX_LaunchApp1;
#endif // #ifndef XP_MACOSX
// Only Windows and GTK supports the following multimedia keys.
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
case KEY_NAME_INDEX_BrowserHome:
return CODE_NAME_INDEX_BrowserHome;
case KEY_NAME_INDEX_LaunchApplication2:
return CODE_NAME_INDEX_LaunchApp2;
#endif // #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
// Only GTK and Android supports the following multimedia keys.
#if defined(MOZ_WIDGET_GTK) || defined(ANDROID)
case KEY_NAME_INDEX_Eject:
return CODE_NAME_INDEX_Eject;
case KEY_NAME_INDEX_WakeUp:
return CODE_NAME_INDEX_WakeUp;
#endif // #if defined(MOZ_WIDGET_GTK) || defined(ANDROID)
// Only Windows does not support Help key (and macOS handled above).
#if !defined(XP_WIN) && !defined(XP_MACOSX)
case KEY_NAME_INDEX_Help:
return CODE_NAME_INDEX_Help;
#endif // #if !defined(XP_WIN) && !defined(XP_MACOSX)
// IME specific keys:
#ifdef XP_WIN
case KEY_NAME_INDEX_Convert:
return CODE_NAME_INDEX_Convert;
case KEY_NAME_INDEX_NonConvert:
return CODE_NAME_INDEX_NonConvert;
case KEY_NAME_INDEX_Alphanumeric:
return CODE_NAME_INDEX_CapsLock;
case KEY_NAME_INDEX_KanaMode:
case KEY_NAME_INDEX_Romaji:
case KEY_NAME_INDEX_Katakana:
case KEY_NAME_INDEX_Hiragana:
return CODE_NAME_INDEX_KanaMode;
case KEY_NAME_INDEX_Hankaku:
case KEY_NAME_INDEX_Zenkaku:
case KEY_NAME_INDEX_KanjiMode:
return CODE_NAME_INDEX_Backquote;
case KEY_NAME_INDEX_HanjaMode:
return CODE_NAME_INDEX_Lang2;
case KEY_NAME_INDEX_HangulMode:
return CODE_NAME_INDEX_Lang1;
#endif // #ifdef XP_WIN
#ifdef MOZ_WIDGET_GTK
case KEY_NAME_INDEX_Convert:
return CODE_NAME_INDEX_Convert;
case KEY_NAME_INDEX_NonConvert:
return CODE_NAME_INDEX_NonConvert;
case KEY_NAME_INDEX_Alphanumeric:
return CODE_NAME_INDEX_CapsLock;
case KEY_NAME_INDEX_HiraganaKatakana:
return CODE_NAME_INDEX_KanaMode;
case KEY_NAME_INDEX_ZenkakuHankaku:
return CODE_NAME_INDEX_Backquote;
#endif // #ifdef MOZ_WIDGET_GTK
#ifdef ANDROID
case KEY_NAME_INDEX_Convert:
return CODE_NAME_INDEX_Convert;
case KEY_NAME_INDEX_NonConvert:
return CODE_NAME_INDEX_NonConvert;
case KEY_NAME_INDEX_HiraganaKatakana:
return CODE_NAME_INDEX_KanaMode;
case KEY_NAME_INDEX_ZenkakuHankaku:
return CODE_NAME_INDEX_Backquote;
case KEY_NAME_INDEX_Eisu:
return CODE_NAME_INDEX_Lang2;
case KEY_NAME_INDEX_KanjiMode:
return CODE_NAME_INDEX_Lang1;
#endif // #ifdef ANDROID
#ifdef XP_MACOSX
case KEY_NAME_INDEX_Eisu:
return CODE_NAME_INDEX_Lang2;
case KEY_NAME_INDEX_KanjiMode:
return CODE_NAME_INDEX_Lang1;
#endif // #ifdef XP_MACOSX
default:
return CODE_NAME_INDEX_UNKNOWN;
}
}
/* static */ Modifier
WidgetKeyboardEvent::GetModifierForKeyName(KeyNameIndex aKeyNameIndex)
{