Bug 1036008 - Use alternative ASCII capable keyboard layout information to decide keyCode even if the key produces an ASCII punctuation character r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 16 Feb 2018 15:54:07 +0900
changeset 459597 097cade856eceed3a6537707e8a1d911ce9dfccf
parent 459596 b95d386bf5fcc709306142bceffd198fd0956166
child 459598 bb0271610fd8705197ce50d3286db0e5a07fee72
child 459621 f0e351b326220c91e8da3dc33398a9f895b43944
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1036008
milestone60.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 1036008 - Use alternative ASCII capable keyboard layout information to decide keyCode even if the key produces an ASCII punctuation character r=smaug Gecko decides keyCode from an ASCII character which is produced by the key by itself or with Shift on active keyboard layout or alternative ASCII capable keyboard layout if active keyboard layout isn't ASCII capable. However, we've ignored alternative ASCII capable keyboard layout's character if both the key itself and with Shift don't produce ASCII alphabet nor ASCII numeral, i.e., ASCII punctuation characters are not used in alternative ASCII capable keyboard layout because of avoiding mapping a keyCode value to 2 or more keys. However, setting 0 to keyCode value makes Firefox unusable with some web applications which are aware of neither KeyboardEvent.key nor KeyboardEvent.code. So, even if we map same keyCode value to a key, we should avoid setting keyCode value to 0 as far as possible. This patch's approach is, we behave same keyCode value as the alternative ASCII capable keyCode is selected when computed keyCode value of active keyboard layout is 0. This means that we will make some language users whose keyboard layout for their language is not ASCII capable can use global web services which support US keyboard layout of Firefox since the new keyCode values are mostly computed with US layout on Windows or actual alternative ASCII capable keyboard layout on macOS and Linux. In other words, we cannot improve compatibility with web applications which don't support Firefox by this patch since our keyCode values are really different from Chrome's. So, unfortunately, if we'd use exactly same keyCode computation as Chromium, we'd break compatibility with existing web applications which are aware of Firefox since it's necessary to check UA name or something before using keyCode values. Note that the most important difference between Windows and the others is, such keyCode value is computed with alternative ASCII capable keyboard layout on macOS and Linux but only on Windows, it's computed with OEM virtual keycode. This means that only on Windows, the keyCode value may be different from actual alternative ASCII capable keyboard layout's keyCode. MozReview-Commit-ID: As289r9wp6i
widget/TextEvents.h
widget/WidgetEventImpl.cpp
widget/cocoa/TextInputHandler.mm
widget/gtk/nsGtkKeyUtils.cpp
widget/gtk/nsGtkKeyUtils.h
widget/tests/test_keycodes.xul
widget/windows/KeyboardLayout.cpp
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -454,16 +454,27 @@ public:
   {
     if (mCodeNameIndex == CODE_NAME_INDEX_USE_STRING) {
       aCodeName = mCodeValue;
       return;
     }
     GetDOMCodeName(mCodeNameIndex, aCodeName);
   }
 
+  /**
+   * GetFallbackKeyCodeOfPunctuationKey() returns a DOM keyCode value for
+   * aCodeNameIndex.  This is keyCode value of the key when active keyboard
+   * layout is ANSI (US), JIS or ABNT keyboard layout (the latter 2 layouts
+   * are used only when ANSI doesn't have the key).  The result is useful
+   * if the key doesn't produce ASCII character with active keyboard layout
+   * nor with alternative ASCII capable keyboard layout.
+   */
+  static uint32_t
+  GetFallbackKeyCodeOfPunctuationKey(CodeNameIndex aCodeNameIndex);
+
   bool IsModifierKeyEvent() const
   {
     return GetModifierForKeyName(mKeyNameIndex) != MODIFIER_NONE;
   }
 
   /**
    * Get the candidates for shortcut key.
    *
--- a/widget/WidgetEventImpl.cpp
+++ b/widget/WidgetEventImpl.cpp
@@ -1178,16 +1178,52 @@ WidgetKeyboardEvent::GetCodeNameIndex(co
                                    static_cast<CodeNameIndex>(i));
     }
   }
   CodeNameIndex result = CODE_NAME_INDEX_USE_STRING;
   sCodeNameIndexHashtable->Get(aCodeValue, &result);
   return result;
 }
 
+/* static */ uint32_t
+WidgetKeyboardEvent::GetFallbackKeyCodeOfPunctuationKey(
+                       CodeNameIndex aCodeNameIndex)
+{
+  switch (aCodeNameIndex) {
+    case CODE_NAME_INDEX_Semicolon:     // VK_OEM_1 on Windows
+      return dom::KeyboardEventBinding::DOM_VK_SEMICOLON;
+    case CODE_NAME_INDEX_Equal:         // VK_OEM_PLUS on Windows
+      return dom::KeyboardEventBinding::DOM_VK_EQUALS;
+    case CODE_NAME_INDEX_Comma:         // VK_OEM_COMMA on Windows
+      return dom::KeyboardEventBinding::DOM_VK_COMMA;
+    case CODE_NAME_INDEX_Minus:         // VK_OEM_MINUS on Windows
+      return dom::KeyboardEventBinding::DOM_VK_HYPHEN_MINUS;
+    case CODE_NAME_INDEX_Period:        // VK_OEM_PERIOD on Windows
+      return dom::KeyboardEventBinding::DOM_VK_PERIOD;
+    case CODE_NAME_INDEX_Slash:         // VK_OEM_2 on Windows
+      return dom::KeyboardEventBinding::DOM_VK_SLASH;
+    case CODE_NAME_INDEX_Backquote:     // VK_OEM_3 on Windows
+      return dom::KeyboardEventBinding::DOM_VK_BACK_QUOTE;
+    case CODE_NAME_INDEX_BracketLeft:   // VK_OEM_4 on Windows
+      return dom::KeyboardEventBinding::DOM_VK_OPEN_BRACKET;
+    case CODE_NAME_INDEX_Backslash:     // VK_OEM_5 on Windows
+      return dom::KeyboardEventBinding::DOM_VK_BACK_SLASH;
+    case CODE_NAME_INDEX_BracketRight:  // VK_OEM_6 on Windows
+      return dom::KeyboardEventBinding::DOM_VK_CLOSE_BRACKET;
+    case CODE_NAME_INDEX_Quote:         // VK_OEM_7 on Windows
+      return dom::KeyboardEventBinding::DOM_VK_QUOTE;
+    case CODE_NAME_INDEX_IntlBackslash: // VK_OEM_5 on Windows (ABNT, etc)
+    case CODE_NAME_INDEX_IntlYen:       // VK_OEM_5 on Windows (JIS)
+    case CODE_NAME_INDEX_IntlRo:        // VK_OEM_102 on Windows
+      return dom::KeyboardEventBinding::DOM_VK_BACK_SLASH;
+    default:
+      return 0;
+  }
+}
+
 /* static */ const char*
 WidgetKeyboardEvent::GetCommandStr(Command aCommand)
 {
 #define NS_DEFINE_COMMAND(aName, aCommandStr) , #aCommandStr
 #define NS_DEFINE_COMMAND_NO_EXEC_COMMAND(aName)
   static const char* const kCommands[] = {
     "" // CommandDoNothing
 #include "mozilla/CommandList.h"
--- a/widget/cocoa/TextInputHandler.mm
+++ b/widget/cocoa/TextInputHandler.mm
@@ -1405,35 +1405,44 @@ TISInputSourceWrapper::ComputeGeckoKeyCo
 
   // If the unshifed char isn't an ASCII character, use shifted char.
   charCode = TranslateToChar(aNativeKeyCode, modifiers | shiftKey, aKbType);
   keyCode = WidgetUtils::ComputeKeyCodeFromChar(charCode);
   if (keyCode) {
     return keyCode;
   }
 
-  // If this is ASCII capable, give up to compute it.
-  if (IsASCIICapable()) {
-    return 0;
+  if (!IsASCIICapable()) {
+    // Retry with ASCII capable keyboard layout.
+    TISInputSourceWrapper currentKeyboardLayout;
+    currentKeyboardLayout.InitByCurrentASCIICapableKeyboardLayout();
+    NS_ENSURE_TRUE(mInputSource != currentKeyboardLayout.mInputSource, 0);
+    keyCode = currentKeyboardLayout.ComputeGeckoKeyCode(aNativeKeyCode, aKbType,
+                                                        aCmdIsPressed);
+    // We've returned 0 for long time if keyCode isn't for an alphabet keys or
+    // a numeric key even in alternative ASCII capable keyboard layout because
+    // we decided that we should avoid setting same keyCode value to 2 or
+    // more keys since active keyboard layout may have a key to input the
+    // punctuation with different key.  However, setting keyCode to 0 makes
+    // some web applications which are aware of neither KeyboardEvent.key nor
+    // KeyboardEvent.code not work with Firefox when user selects non-ASCII
+    // capable keyboard layout such as Russian and Thai.  So, if alternative
+    // ASCII capable keyboard layout has keyCode value for the key, we should
+    // use it.  In other words, this behavior does that non-ASCII capable
+    // keyboard layout overrides some keys' keyCode value only if the key
+    // produces ASCII character by itself or with Shift key.
+    if (keyCode) {
+      return keyCode;
+    }
   }
 
-  // Retry with ASCII capable keyboard layout.
-  TISInputSourceWrapper currentKeyboardLayout;
-  currentKeyboardLayout.InitByCurrentASCIICapableKeyboardLayout();
-  NS_ENSURE_TRUE(mInputSource != currentKeyboardLayout.mInputSource, 0);
-  keyCode = currentKeyboardLayout.ComputeGeckoKeyCode(aNativeKeyCode, aKbType,
-                                                      aCmdIsPressed);
-
-  // However, if keyCode isn't for an alphabet keys or a numeric key, we should
-  // ignore it.  For example, comma key of Thai layout is same as close-square-
-  // bracket key of US layout and an unicode character key of Thai layout is
-  // same as comma key of US layout.  If we return NS_VK_COMMA for latter key,
-  // web application developers cannot distinguish with the former key.
-  return ((keyCode >= NS_VK_A && keyCode <= NS_VK_Z) ||
-          (keyCode >= NS_VK_0 && keyCode <= NS_VK_9)) ? keyCode : 0;
+  // Otherwise, let's decide keyCode value from the native virtual keycode
+  // value on major keyboard layout.
+  CodeNameIndex code = ComputeGeckoCodeNameIndex(aNativeKeyCode, aKbType);
+  return WidgetKeyboardEvent::GetFallbackKeyCodeOfPunctuationKey(code);
 }
 
 // static
 KeyNameIndex
 TISInputSourceWrapper::ComputeGeckoKeyNameIndex(UInt32 aNativeKeyCode)
 {
   // NOTE:
   //   When unsupported keys like Convert, Nonconvert of Japanese keyboard is
--- a/widget/gtk/nsGtkKeyUtils.cpp
+++ b/widget/gtk/nsGtkKeyUtils.cpp
@@ -790,17 +790,17 @@ KeymapWrapper::ComputeDOMKeyCode(const G
     if (IsBasicLatinLetterOrNumeral(unmodifiedChar)) {
         // If the unmodified character is an ASCII alphabet or an ASCII
         // numeric, it's the best hint for deciding our keyCode.
         return WidgetUtils::ComputeKeyCodeFromChar(unmodifiedChar);
     }
 
     // If the unmodified character is not an ASCII character, that means we
     // couldn't find the hint. We should reset it.
-    if (unmodifiedChar > 0x7F) {
+    if (!IsPrintableASCIICharacter(unmodifiedChar)) {
         unmodifiedChar = 0;
     }
 
     // Retry with shifted keycode.
     guint shiftState = (baseState | keymapWrapper->GetModifierMask(SHIFT));
     uint32_t shiftedChar =
         keymapWrapper->GetCharCodeFor(aGdkKeyEvent, shiftState,
                                       aGdkKeyEvent->group);
@@ -809,57 +809,91 @@ KeymapWrapper::ComputeDOMKeyCode(const G
         // layout. And also shifted character can be an ASCII numeric on
         // AZERTY keyboad layout.  Then, it's a good hint for deciding our
         // keyCode.
         return WidgetUtils::ComputeKeyCodeFromChar(shiftedChar);
     }
 
     // If the shifted unmodified character isn't an ASCII character, we should
     // discard it too.
-    if (shiftedChar > 0x7F) {
+    if (!IsPrintableASCIICharacter(shiftedChar)) {
         shiftedChar = 0;
     }
 
     // If current keyboard layout isn't ASCII alphabet inputtable layout,
     // look for ASCII alphabet inputtable keyboard layout.  If the key
     // inputs an ASCII alphabet or an ASCII numeric, we should use it
     // for deciding our keyCode.
-    // Note that it's important not to use alternative keyboard layout for ASCII
-    // alphabet inputabble keyboard layout because the keycode for the key with
-    // alternative keyboard layout may conflict with another key on current
-    // keyboard layout.
+    uint32_t unmodCharLatin = 0;
+    uint32_t shiftedCharLatin = 0;
     if (!keymapWrapper->IsLatinGroup(aGdkKeyEvent->group)) {
         gint minGroup = keymapWrapper->GetFirstLatinGroup();
         if (minGroup >= 0) {
-            uint32_t unmodCharLatin =
+            unmodCharLatin =
                 keymapWrapper->GetCharCodeFor(aGdkKeyEvent, baseState,
                                               minGroup);
             if (IsBasicLatinLetterOrNumeral(unmodCharLatin)) {
                 // If the unmodified character is an ASCII alphabet or
                 // an ASCII numeric, we should use it for the keyCode.
                 return WidgetUtils::ComputeKeyCodeFromChar(unmodCharLatin);
             }
-            uint32_t shiftedCharLatin =
+            // If the unmodified character in the alternative ASCII capable
+            // keyboard layout isn't an ASCII character, that means we couldn't
+            // find the hint. We should reset it.
+            if (!IsPrintableASCIICharacter(unmodCharLatin)) {
+                unmodCharLatin = 0;
+            }
+            shiftedCharLatin =
                 keymapWrapper->GetCharCodeFor(aGdkKeyEvent, shiftState,
                                               minGroup);
             if (IsBasicLatinLetterOrNumeral(shiftedCharLatin)) {
                 // If the shifted character is an ASCII alphabet or an ASCII
                 // numeric, we should use it for the keyCode.
                 return WidgetUtils::ComputeKeyCodeFromChar(shiftedCharLatin);
             }
+            // If the shifted unmodified character in the alternative ASCII
+            // capable keyboard layout isn't an ASCII character, we should
+            // discard it too.
+            if (!IsPrintableASCIICharacter(shiftedCharLatin)) {
+                shiftedCharLatin = 0;
+            }
         }
     }
 
-    // If unmodified character is in ASCII range, use it.  Otherwise, use
-    // shifted character.
-    if (!unmodifiedChar && !shiftedChar) {
-        return 0;
+    // If the key itself or with Shift state on active keyboard layout produces
+    // an ASCII punctuation character, we should decide keyCode value with it.
+    if (unmodifiedChar || shiftedChar) {
+        return WidgetUtils::ComputeKeyCodeFromChar(
+                   unmodifiedChar ? unmodifiedChar : shiftedChar);
     }
-    return WidgetUtils::ComputeKeyCodeFromChar(
-                unmodifiedChar ? unmodifiedChar : shiftedChar);
+
+    // If the key itself or with Shift state on alternative ASCII capable
+    // keyboard layout produces an ASCII punctuation character, we should
+    // decide keyCode value with it.  Note that We've returned 0 for long
+    // time if keyCode isn't for an alphabet keys or a numeric key even in
+    // alternative ASCII capable keyboard layout because we decided that we
+    // should avoid setting same keyCode value to 2 or more keys since active
+    // keyboard layout may have a key to input the punctuation with different
+    // key.  However, setting keyCode to 0 makes some web applications which
+    // are aware of neither KeyboardEvent.key nor KeyboardEvent.code not work
+    // with Firefox when user selects non-ASCII capable keyboard layout such
+    // as Russian and Thai.  So, if alternative ASCII capable keyboard layout
+    // has keyCode value for the key, we should use it.  In other words, this
+    // behavior means that non-ASCII capable keyboard layout overrides some
+    // keys' keyCode value only if the key produces ASCII character by itself
+    // or with Shift key.
+    if (unmodCharLatin || shiftedCharLatin) {
+        return WidgetUtils::ComputeKeyCodeFromChar(
+                   unmodCharLatin ? unmodCharLatin : shiftedCharLatin);
+    }
+
+    // Otherwise, let's decide keyCode value from the hardware_keycode
+    // value on major keyboard layout.
+    CodeNameIndex code = ComputeDOMCodeNameIndex(aGdkKeyEvent);
+    return WidgetKeyboardEvent::GetFallbackKeyCodeOfPunctuationKey(code);
 }
 
 KeyNameIndex
 KeymapWrapper::ComputeDOMKeyNameIndex(const GdkEventKey* aGdkKeyEvent)
 {
     switch (aGdkKeyEvent->keyval) {
 
 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
--- a/widget/gtk/nsGtkKeyUtils.h
+++ b/widget/gtk/nsGtkKeyUtils.h
@@ -333,16 +333,26 @@ protected:
      *
      * @param aCharCode         Charcode which you want to test.
      * @return                  TRUE if aCharCode is an alphabet or a numeric
      *                          in ASCII range.  Otherwise, FALSE.
      */
     static bool IsBasicLatinLetterOrNumeral(uint32_t aCharCode);
 
     /**
+     * IsPrintableASCIICharacter() checks whether the aCharCode is a printable
+     * ASCII character.  I.e., returns false if aCharCode is a control
+     * character even in an ASCII character.
+     */
+    static bool IsPrintableASCIICharacter(uint32_t aCharCode)
+    {
+      return aCharCode >= 0x20 && aCharCode <= 0x7E;
+    }
+
+    /**
      * GetGDKKeyvalWithoutModifier() returns the keyval for aGdkKeyEvent when
      * ignoring the modifier state except NumLock. (NumLock is a key to change
      * some key's meaning.)
      */
     static guint GetGDKKeyvalWithoutModifier(const GdkEventKey *aGdkKeyEvent);
 
     /**
      * GetDOMKeyCodeFromKeyPairs() returns DOM keycode for aGdkKeyval if
--- a/widget/tests/test_keycodes.xul
+++ b/widget/tests/test_keycodes.xul
@@ -486,17 +486,17 @@ function* runKeyEventTests()
                   "\u00b9", "KeyA", KeyboardEvent.DOM_VK_A, "\u00b9", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
 
     // German
     yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:MAC_VK_ANSI_A,
                    modifiers: {}, chars:"a", unmodifiedChars:"a"},
                   "a", "KeyA", KeyboardEvent.DOM_VK_A, "a", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:MAC_VK_ANSI_LeftBracket,
                    modifiers: {}, chars:"\u00fc", unmodifiedChars:"\u00fc"},
-                  "\u00fc", "BracketLeft", 0, "\u00fc", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  "\u00fc", "BracketLeft", KeyboardEvent.DOM_VK_OPEN_BRACKET, "\u00fc", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:MAC_VK_ANSI_Minus,
                    modifiers: {}, chars:"\u00df", unmodifiedChars:"\u00df"},
                   "\u00df", "Minus", KeyboardEvent.DOM_VK_QUESTION_MARK, "\u00df", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_GERMAN, keyCode:MAC_VK_ANSI_Minus,
                    modifiers:{shiftKey:1}, chars:"?", unmodifiedChars:"?"},
                   "?", "Minus", KeyboardEvent.DOM_VK_QUESTION_MARK, "?", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     // Note that Shift+SS is '?' but Cmd+Shift+SS is '/' on German layout.
     // Therefore, when Cmd key is pressed, the SS key's keycode is changed.
@@ -2729,20 +2729,21 @@ function* runKeyEventTests()
     // keycode should be DOM_VK_[A-Z] of the key on the latest ASCII capable keyboard layout is for alphabet
     yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_A,
                    modifiers:{}, chars:"\u0E1F", unmodifiedChars:"\u0E1F"},
                   "\u0E1F", "KeyA", KeyboardEvent.DOM_VK_A, "\u0E1F", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     // keycode should be shifted character if unshifted character isn't an ASCII character
     yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_Quote,
                    modifiers:{}, chars:"\u0E07", unmodifiedChars:"\u0E07"},
                   "\u0E07", "Quote", KeyboardEvent.DOM_VK_PERIOD, "\u0E07", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
-    // keycode should be zero if the character of the key on the latest ASCII capable keyboard layout isn't for alphabet
+    // keycode should be same as ANSI keyboard layout's key which causes the native virtual keycode
+    // if the character of the key on the latest ASCII capable keyboard layout isn't for alphabet
     yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_Period,
                    modifiers:{}, chars:"\u0E43", unmodifiedChars:"\u0E43"},
-                  "\u0E43", "Period", 0, "\u0E43", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  "\u0E43", "Period", KeyboardEvent.DOM_VK_PERIOD, "\u0E43", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     // keycode should be DOM_VK_[0-9] if the key on the latest ASCII capable keyboard layout is for numeric
     yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_1,
                    modifiers:{}, chars:"\u0E45", unmodifiedChars:"\u0E45"},
                   "\u0E45", "Digit1", KeyboardEvent.DOM_VK_1, "\u0E45", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_2,
                    modifiers:{}, chars:"/", unmodifiedChars:"/"},
                   "/", "Digit2", KeyboardEvent.DOM_VK_2, "/", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_THAI, keyCode:MAC_VK_ANSI_3,
@@ -4121,22 +4122,24 @@ function* runKeyEventTests()
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_9,
                    modifiers:{capsLockKey:1}, chars:"9"},
                   "9", "Digit9", KeyboardEvent.DOM_VK_9, "9", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_9,
                    modifiers:{capsLockKey:1, shiftKey:1}, chars:"\u00E7"},
                   "\u00E7", "Digit9", KeyboardEvent.DOM_VK_9, "\u00E7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
 
     // OEM keys
+    // If the key doesn't cause ASCII character even with or without Shift key, keyCode value should be same as
+    // the key which causes the virtual keycode on ANSI keyboard layout.
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_7,
                    modifiers:{}, chars:"\u00B2"},
-                  "\u00B2", "Backquote", 0, "\u00B2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  "\u00B2", "Backquote", KeyboardEvent.DOM_VK_QUOTE, "\u00B2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_7,
                    modifiers:{shiftKey:1}, chars:""},
-                  "", "Backquote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  "", "Backquote", KeyboardEvent.DOM_VK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_4,
                    modifiers:{}, chars:")"},
                   ")", "Minus", KeyboardEvent.DOM_VK_CLOSE_PAREN, ")", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_4,
                    modifiers:{shiftKey:1}, chars:"\u00B0"},
                   "\u00B0", "Minus", KeyboardEvent.DOM_VK_CLOSE_PAREN, "\u00B0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_PLUS,
                    modifiers:{}, chars:"="},
@@ -4197,38 +4200,38 @@ function* runKeyEventTests()
                   "!", "Slash", KeyboardEvent.DOM_VK_EXCLAMATION, "!", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_8,
                    modifiers:{shiftKey:1}, chars:"\u00A7"},
                   "\u00A7", "Slash", KeyboardEvent.DOM_VK_EXCLAMATION, "\u00A7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
 
     // OEM keys with ShiftLock
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_7,
                    modifiers:{capsLockKey:1}, chars:"\u00B2"},
-                  "\u00B2", "Backquote", 0, "\u00B2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  "\u00B2", "Backquote", KeyboardEvent.DOM_VK_QUOTE, "\u00B2", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_7,
                    modifiers:{capsLockKey:1, shiftKey:1}, chars:""},
-                  "", "Backquote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  "", "Backquote", KeyboardEvent.DOM_VK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_4,
                    modifiers:{capsLockKey:1}, chars:"\u00B0"},
                   "\u00B0", "Minus", KeyboardEvent.DOM_VK_CLOSE_PAREN, "\u00B0", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_4,
                    modifiers:{capsLockKey:1, shiftKey:1}, chars:")"},
                   ")", "Minus", KeyboardEvent.DOM_VK_CLOSE_PAREN, ")", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_PLUS,
                    modifiers:{capsLockKey:1}, chars:"+"},
                   "+", "Equal", KeyboardEvent.DOM_VK_EQUALS, "+", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_PLUS,
                    modifiers:{capsLockKey:1, shiftKey:1}, chars:"="},
                   "=", "Equal", KeyboardEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     //yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
     //               modifiers:{capsLockKey:1}, chars:""},
-    //              "Dead", "BracketLeft", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // Dead-key
+    //              "Dead", "BracketLeft", KeyboardLayout.DOM_VK_CLOSE_BRACKET, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // Dead-key
     //yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_6,
     //               modifiers:{capsLockKey:1, shiftKey:1}, chars:""},
-    //              ["\u00A8\u00A8", "\u00A8", "\u00A8", "\u00A8"], "BracketLeft", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // Dead-key
+    //              ["\u00A8\u00A8", "\u00A8", "\u00A8", "\u00A8"], "BracketLeft", KeyboardLayout.DOM_VK_CLOSE_BRACKET, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD); // Dead-key
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_1,
                    modifiers:{capsLockKey:1}, chars:"\u00A3"},
                   "\u00A3", "BracketRight", KeyboardEvent.DOM_VK_DOLLAR, "\u00A3", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_1,
                    modifiers:{capsLockKey:1, shiftKey:1}, chars:"$"},
                   "$", "BracketRight", KeyboardEvent.DOM_VK_DOLLAR, "$", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_FRENCH, keyCode:WIN_VK_OEM_3,
                    modifiers:{capsLockKey:1}, chars:"%"},
@@ -4488,66 +4491,66 @@ function* runKeyEventTests()
                    modifiers:{shiftKey:1}, chars:""},
                   "Dead", "BracketLeft", KeyboardEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_Q,
                    modifiers:{shiftKey:1}, chars:"^Q"},
                   ["^Q", "^", "Q", "Q"], "KeyQ", KeyboardEvent.DOM_VK_Q, "^Q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
 
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
                    modifiers:{}, chars:""},
-                  "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  "Dead", "Quote", KeyboardEvent.DOM_VK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
                    modifiers:{}, chars:"\u00B4\u00B4"},
-                  ["\u00B4\u00B4", "\u00B4", "\u00B4", "\u00B4"], "Quote", 0, "\u00B4\u00B4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  ["\u00B4\u00B4", "\u00B4", "\u00B4", "\u00B4"], "Quote", KeyboardEvent.DOM_VK_QUOTE, "\u00B4\u00B4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
 
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
                    modifiers:{}, chars:""},
-                  "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  "Dead", "Quote", KeyboardEvent.DOM_VK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
                    modifiers:{}, chars:"\u00E1"},
                   ["\u00E1", "\u00E1", "a"], "KeyA", KeyboardEvent.DOM_VK_A, "\u00E1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
 
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
                    modifiers:{}, chars:""},
-                  "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  "Dead", "Quote", KeyboardEvent.DOM_VK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
                    modifiers:{shiftKey:1}, chars:"\u00C1"},
                   ["\u00C1", "\u00C1", "A"], "KeyA", KeyboardEvent.DOM_VK_A, "\u00C1", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
 
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
                    modifiers:{}, chars:""},
-                  "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  "Dead", "Quote", KeyboardEvent.DOM_VK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_Q,
                    modifiers:{}, chars:"\u00B4q"},
                   ["\u00B4q", "\u00B4", "q", "q"], "KeyQ", KeyboardEvent.DOM_VK_Q, "\u00B4q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
 
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
                    modifiers:{shiftKey:1}, chars:""},
-                  "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  "Dead", "Quote", KeyboardEvent.DOM_VK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
                    modifiers:{shiftKey:1}, chars:"\u00A8\u00A8"},
-                  ["\u00A8\u00A8", "\u00A8", "\u00A8", "\u00A8"], "Quote", 0, "\u00A8\u00A8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  ["\u00A8\u00A8", "\u00A8", "\u00A8", "\u00A8"], "Quote", KeyboardEvent.DOM_VK_QUOTE, "\u00A8\u00A8", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
 
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
                    modifiers:{shiftKey:1}, chars:""},
-                  "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  "Dead", "Quote", KeyboardEvent.DOM_VK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
                    modifiers:{shiftKey:1}, chars:"\u00C4"},
                   ["\u00C4", "\u00C4", "A"], "KeyA", KeyboardEvent.DOM_VK_A, "\u00C4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
 
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
                    modifiers:{shiftKey:1}, chars:""},
-                  "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  "Dead", "Quote", KeyboardEvent.DOM_VK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_A,
                    modifiers:{}, chars:"\u00E4"},
                   ["\u00E4", "\u00E4", "a"], "KeyA", KeyboardEvent.DOM_VK_A, "\u00E4", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
 
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_OEM_7,
                    modifiers:{shiftKey:1}, chars:""},
-                  "Dead", "Quote", 0, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+                  "Dead", "Quote", KeyboardEvent.DOM_VK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_SPANISH, keyCode:WIN_VK_Q,
                    modifiers:{shiftKey:1}, chars:"\u00A8Q"},
                   ["\u00A8Q", "\u00A8", "Q", "Q"], "KeyQ", KeyboardEvent.DOM_VK_Q, "\u00A8Q", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
 
     if (OS_VERSION >= WIN8) {
       // On Russian Mnemonic layout, both 'KeyS' and 'KeyC' are dead key.  However, the sequence 'KeyS' -> 'KeyC' causes a composite character.
       yield testKey({layout:KEYBOARD_LAYOUT_RUSSIAN_MNEMONIC, keyCode:WIN_VK_S,
                      modifiers:{}, chars:""},
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -4716,17 +4716,85 @@ KeyboardLayout::ConvertNativeKeyCodeToDO
       UniCharsAndModifiers uniChars =
         GetUniCharsAndModifiers(aNativeKeyCode, modKeyState);
       if (uniChars.Length() != 1 ||
           uniChars.CharAt(0) < ' ' || uniChars.CharAt(0) > 0x7F) {
         modKeyState.Set(MODIFIER_SHIFT);
         uniChars = GetUniCharsAndModifiers(aNativeKeyCode, modKeyState);
         if (uniChars.Length() != 1 ||
             uniChars.CharAt(0) < ' ' || uniChars.CharAt(0) > 0x7F) {
-          return 0;
+          // In this case, we've returned 0 in this case for long time because
+          // we decided that we should avoid setting same keyCode value to 2 or
+          // more keys since active keyboard layout may have a key to input the
+          // punctuation with different key.  However, setting keyCode to 0
+          // makes some web applications which are aware of neither
+          // KeyboardEvent.key nor KeyboardEvent.code not work with Firefox
+          // when user selects non-ASCII capable keyboard layout such as
+          // Russian and Thai layout.  So, let's decide keyCode value with
+          // major keyboard layout's key which causes the OEM keycode.
+          // Actually, this maps same keyCode value to 2 keys on Russian
+          // keyboard layout.  "Period" key causes VK_OEM_PERIOD but inputs
+          // Yu of Cyrillic and "Slash" key causes VK_OEM_2 (same as US
+          // keyboard layout) but inputs "." (period of ASCII).  Therefore,
+          // we return DOM_VK_PERIOD which is same as VK_OEM_PERIOD for
+          // "Period" key.  On the other hand, we use same keyCode value for
+          // "Slash" key too because it inputs ".".
+          CodeNameIndex code;
+          switch (aNativeKeyCode) {
+            case VK_OEM_1:
+              code = CODE_NAME_INDEX_Semicolon;
+              break;
+            case VK_OEM_PLUS:
+              code = CODE_NAME_INDEX_Equal;
+              break;
+            case VK_OEM_COMMA:
+              code = CODE_NAME_INDEX_Comma;
+              break;
+            case VK_OEM_MINUS:
+              code = CODE_NAME_INDEX_Minus;
+              break;
+            case VK_OEM_PERIOD:
+              code = CODE_NAME_INDEX_Period;
+              break;
+            case VK_OEM_2:
+              code = CODE_NAME_INDEX_Slash;
+              break;
+            case VK_OEM_3:
+              code = CODE_NAME_INDEX_Backquote;
+              break;
+            case VK_OEM_4:
+              code = CODE_NAME_INDEX_BracketLeft;
+              break;
+            case VK_OEM_5:
+              code = CODE_NAME_INDEX_Backslash;
+              break;
+            case VK_OEM_6:
+              code = CODE_NAME_INDEX_BracketRight;
+              break;
+            case VK_OEM_7:
+              code = CODE_NAME_INDEX_Quote;
+              break;
+            case VK_OEM_8:
+              // Use keyCode value for "Backquote" key on UK keyboard layout.
+              code = CODE_NAME_INDEX_Backquote;
+              break;
+            case VK_OEM_102:
+              // Use keyCode value for "IntlBackslash" key.
+              code = CODE_NAME_INDEX_IntlBackslash;
+              break;
+            case VK_ABNT_C1: // "/" of ABNT.
+              // Use keyCode value for "IntlBackslash" key on ABNT keyboard
+              // layout.
+              code = CODE_NAME_INDEX_IntlBackslash;
+              break;
+            default:
+              MOZ_ASSERT_UNREACHABLE("Handle all OEM keycode values");
+              return 0;
+          }
+          return WidgetKeyboardEvent::GetFallbackKeyCodeOfPunctuationKey(code);
         }
       }
       return WidgetUtils::ComputeKeyCodeFromChar(uniChars.CharAt(0));
     }
 
     // IE sets 0xC2 to the DOM keyCode for VK_ABNT_C2.  However, we're already
     // using NS_VK_SEPARATOR for the separator key on Mac and Linux.  Therefore,
     // We should keep consistency between Gecko on all platforms rather than