Bug 1343955 - part 2: Implement _guessCodeFromKeyName() in EventUtils.js with C++ and make it accessible with nsITextInputProcessor for EventUtils.js r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 03 Oct 2018 09:21:47 +0000
changeset 497850 fea8e039767a88ebfb73b1f0d43d1f87ee7394e1
parent 497849 1a1ef374eba66c3af2c252711e607d095e2c2c23
child 497851 4a3ba199155a58cd282ee0383925525141593413
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1343955
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
dom/base/TextInputProcessor.cpp
dom/base/TextInputProcessor.h
dom/interfaces/base/nsITextInputProcessor.idl
testing/mochitest/tests/SimpleTest/EventUtils.js
widget/TextEvents.h
widget/WidgetEventImpl.cpp
--- a/dom/base/TextInputProcessor.cpp
+++ b/dom/base/TextInputProcessor.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #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"
 #include "mozilla/widget/IMEData.h"
 #include "mozilla/dom/KeyboardEvent.h"
 #include "nsContentUtils.h"
 #include "nsIDocShell.h"
 #include "nsIWidget.h"
@@ -1270,16 +1271,302 @@ TextInputProcessor::ShareModifierStateOf
   TextInputProcessor* other = static_cast<TextInputProcessor*>(aOther);
   if (!other->mModifierKeyDataArray) {
     other->mModifierKeyDataArray = new ModifierKeyDataArray();
   }
   mModifierKeyDataArray = other->mModifierKeyDataArray;
   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
  ******************************************************************************/
 TextInputProcessor::AutoPendingCompositionResetter::
   AutoPendingCompositionResetter(TextInputProcessor* aTIP)
   : mTIP(aTIP)
 {
   MOZ_RELEASE_ASSERT(mTIP.get(), "mTIP must not be null");
--- a/dom/base/TextInputProcessor.h
+++ b/dom/base/TextInputProcessor.h
@@ -4,16 +4,17 @@
  * 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/. */
 
 #ifndef mozilla_dom_textinputprocessor_h_
 #define mozilla_dom_textinputprocessor_h_
 
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/TextEventDispatcher.h"
 #include "mozilla/TextEventDispatcherListener.h"
 #include "nsITextInputProcessor.h"
 #include "nsITextInputProcessorCallback.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 
@@ -44,16 +45,36 @@ public:
     OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) override;
 
   NS_IMETHOD_(void) WillDispatchKeyboardEvent(
                       TextEventDispatcher* aTextEventDispatcher,
                       WidgetKeyboardEvent& aKeyboardEvent,
                       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();
 
 private:
   bool IsComposing() const;
   nsresult BeginInputTransactionInternal(
              mozIDOMWindow* aWindow,
              nsITextInputProcessorCallback* aCallback,
--- a/dom/interfaces/base/nsITextInputProcessor.idl
+++ b/dom/interfaces/base/nsITextInputProcessor.idl
@@ -600,16 +600,54 @@ interface nsITextInputProcessor : nsISup
    * state of another instance.  After that, changes to either this and the
    * other instance's modifier state is synchronized.
    *
    * @param aOther          Another instance which will be referred by the
    *                        instance.  If this is null, the instance restarts
    *                        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++
 #define TEXT_INPUT_PROCESSOR_CID \
   { 0xcaaab47f, 0x1e31, 0x478e, \
     { 0x89, 0x19, 0x97, 0x09, 0x04, 0xe9, 0xcb, 0x72 } }
 #define TEXT_INPUT_PROCESSOR_CONTRACTID \
   "@mozilla.org/text-input-processor;1"
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -929,17 +929,17 @@ function synthesizeKey(aKey, aEvent = un
   var event = aEvent === undefined || aEvent === null ? {} : aEvent;
 
   var TIP = _getTIP(aWindow, aCallback);
   if (!TIP) {
     return;
   }
   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;
   var dispatchKeyup =
     !("type" in event) || event.type === "keyup"   || !event.type;
 
   try {
     if (dispatchKeydown) {
@@ -968,17 +968,17 @@ function synthesizeKey(aKey, aEvent = un
  * This API is supposed to be used in those test cases that synthesize some
  * input events to chrome process and have some checks in content.
  */
 function synthesizeAndWaitKey(aKey, aEvent, aWindow = window,
                               checkBeforeSynthesize, checkAfterSynthesize)
 {
   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 => {
     mm.addMessageListener("Test:KeyRegistered", function processed(message) {
       mm.removeMessageListener("Test:KeyRegistered", processed);
       resolve();
     });
   });
@@ -1513,309 +1513,61 @@ function _guessKeyNameFromKeyCode(aKeyCo
       return "EraseEof";
     case KeyboardEvent.DOM_VK_PLAY:
       return "Play";
     default:
       return "Unidentified";
   }
 }
 
-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) {
       throw "Unknown key: " + aKey;
     }
     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) {
       keyCode = _computeKeyCodeFromChar(aKey.charAt(0));
     }
     if (!keyCode) {
       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;
   }
   if (aKeyEvent.doNotMarkKeydownAsProcessed) {
     result.flags |=
       _EU_Ci.nsITextInputProcessor.KEY_DONT_MARK_KEYDOWN_AS_PROCESSED;
   }
@@ -1974,24 +1726,25 @@ function synthesizeComposition(aEvent, a
   }
   var KeyboardEvent = _getKeyboardEvent(aWindow);
   var modifiers = _emulateToActivateModifiers(TIP, aEvent.key, aWindow);
   var ret = false;
   var keyEventDict = {dictionary: null, flags: 0};
   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 {
     switch (aEvent.type) {
       case "compositionstart":
         ret = TIP.startComposition(keyEvent, keyEventDict.flags);
         break;
       case "compositioncommitasis":
@@ -2110,24 +1863,26 @@ function synthesizeCompositionChange(aEv
   }
 
   var modifiers = _emulateToActivateModifiers(TIP, aEvent.key, aWindow);
   try {
     var keyEventDict = {dictionary: null, flags: 0};
     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);
   } finally {
     _emulateToInactivateModifiers(TIP, modifiers, aWindow);
   }
 }
 
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -8,16 +8,17 @@
 
 #include <stdint.h>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/BasicEvents.h"
 #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"
 #include "nsCOMPtr.h"
 #include "nsISelectionController.h"
 #include "nsISelectionListener.h"
 #include "nsITransferable.h"
 #include "nsRect.h"
@@ -588,22 +589,59 @@ public:
    * ComputeKeyCodeFromKeyNameIndex() return a .mKeyCode value which can be
    * mapped from the specified key value.  Note that this returns 0 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.
    */
   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.
    */
   static bool IsLockableModifier(KeyNameIndex aKeyNameIndex);
 
   static void GetDOMKeyName(KeyNameIndex aKeyNameIndex,
                             nsAString& aKeyName);
   static void GetDOMCodeName(CodeNameIndex aCodeNameIndex,
--- a/widget/WidgetEventImpl.cpp
+++ b/widget/WidgetEventImpl.cpp
@@ -1443,16 +1443,341 @@ WidgetKeyboardEvent::ComputeKeyCodeFromK
     case KEY_NAME_INDEX_ZoomIn:
     case KEY_NAME_INDEX_ZoomOut:
       return dom::KeyboardEvent_Binding::DOM_VK_ZOOM;
     default:
       return 0;
   }
 }
 
+/* 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)
 {
   switch (aKeyNameIndex) {
     case KEY_NAME_INDEX_Alt:
       return MODIFIER_ALT;
     case KEY_NAME_INDEX_AltGraph:
       return MODIFIER_ALTGRAPH;