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 487773 fea8e039767a88ebfb73b1f0d43d1f87ee7394e1
parent 487772 1a1ef374eba66c3af2c252711e607d095e2c2c23
child 487774 4a3ba199155a58cd282ee0383925525141593413
push id246
push userfmarier@mozilla.com
push dateSat, 13 Oct 2018 00:15:40 +0000
reviewerssmaug
bugs1343955
milestone64.0a1
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;