Bug 900750 - part 5: Make NativeKey set KeyboardEvent.key value of AltRight key to "AltGraph" when active keyboard layout has AltGr key r=m_kato,smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 31 May 2018 18:36:33 +0900
changeset 478700 44cb540ef2fc4589c2941ab13dc6ea6b6da59474
parent 478699 311827011b111f087b862c61c14d47191a58dedc
child 478701 f15f24bf1131f03941efa265996f84ce8b9c61a9
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato, smaug
bugs900750
milestone63.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 900750 - part 5: Make NativeKey set KeyboardEvent.key value of AltRight key to "AltGraph" when active keyboard layout has AltGr key r=m_kato,smaug When AltGr key is pressed, following messages come: 1. WM_KEYDOWN for ControlLeft 2. WM_KEYDOWN for AltLeft 3. WM_SYSKEYUP for ControlLeft 4. WM_KEYUP for AltLeft In these key sequence, KeyboardEvent.key value of keydown event at #2 and keyup event at #4 should be "AltGraph". This patch fixes the key value and adding new test into test_keycodes.xul to check the behavior with SynthesizeNativeKey(). MozReview-Commit-ID: JZ6WednB8la
widget/NativeKeyToDOMKeyName.h
widget/tests/test_keycodes.xul
widget/windows/KeyboardLayout.cpp
--- a/widget/NativeKeyToDOMKeyName.h
+++ b/widget/NativeKeyToDOMKeyName.h
@@ -79,17 +79,18 @@
 #endif
 
 /******************************************************************************
  * Modifier Keys
  ******************************************************************************/
 // Alt
 KEY_MAP_WIN     (Alt, VK_MENU)
 KEY_MAP_WIN     (Alt, VK_LMENU)
-KEY_MAP_WIN     (Alt, VK_RMENU)
+KEY_MAP_WIN     (Alt, VK_RMENU) // This is ignored if active keyboard layout
+                                // has AltGr.  In such case, AltGraph is mapped.
 KEY_MAP_COCOA   (Alt, kVK_Option)
 KEY_MAP_COCOA   (Alt, kVK_RightOption)
 KEY_MAP_GTK     (Alt, GDK_Alt_L)
 KEY_MAP_GTK     (Alt, GDK_Alt_R)
 KEY_MAP_ANDROID (Alt, AKEYCODE_ALT_LEFT)
 KEY_MAP_ANDROID (Alt, AKEYCODE_ALT_RIGHT)
 
 // AltGraph
--- a/widget/tests/test_keycodes.xul
+++ b/widget/tests/test_keycodes.xul
@@ -192,16 +192,17 @@ function* runKeyEventTests()
   var name; // Current test name. Needs to be renamed later.
   var eventList, keyDownFlags, keyUpFlags, testingEvent, expectedDOMKeyCode;
   const kShiftFlag    = 0x1;
   const kCtrlFlag     = 0x2;
   const kAltFlag      = 0x4;
   const kMetaFlag     = 0x8;
   const kNumLockFlag  = 0x10;
   const kCapsLockFlag = 0x20;
+  const kAltGraphFlag = 0x40;
 
   function onKeyEvent(e)
   {
     function removeFlag(e, aFlag)
     {
       if (e.type == "keydown") {
         var oldValue = keyDownFlags;
         keyDownFlags &= ~aFlag;
@@ -256,16 +257,33 @@ function* runKeyEventTests()
              name + ", Alt of Alt " + e.type + " event mismatch");
           is(e.shiftKey, (flags & kShiftFlag) != 0, name + ", Shift of Alt " + e.type + " event mismatch");
           is(e.getModifierState("AltGraph"),
              e.type == "keydown" && ((IS_WIN && !!testingEvent.modifiers.altGrKey) || (IS_MAC && e.altKey)),
              name + ", AltGraph of Alt " + e.type + " event mismatch");
           return (testingEvent.modifiers.altKey || testingEvent.modifiers.altRightKey ||
                   (IS_WIN && !!testingEvent.modifiers.altGrKey)) &&
                  removeFlag(e, kAltFlag) && expectedDOMKeyCode != e.keyCode;
+        case "AltGraph":
+          // On Windows, AltGraph events are fired only when AltRight key is
+          // pressed when active keyboard layout maps AltGraph to AltRight.
+          // Note that AltGraph is represented with pressing both Control key
+          // and Alt key.  Therefore, when AltGraph keyboard event is fired,
+          // both ctrlKey and altKey are always false on Windows.
+          is(e.ctrlKey, (flags & kCtrlFlag) != 0 && !IS_WIN,
+             name + ", Ctrl of AltGraph " + e.type + " event mismatch");
+          is(e.metaKey, (flags & kMetaFlag) != 0,
+             name + ", Command of AltGraph " + e.type + " event mismatch");
+          is(e.altKey, (flags & kAltFlag) != 0 && !IS_WIN,
+             name + ", Alt of AltGraph " + e.type + " event mismatch");
+          is(e.shiftKey, (flags & kShiftFlag) != 0, name + ", Shift of Ctrl " + e.type + " event mismatch");
+          is(e.getModifierState("AltGraph"), e.type === "keydown",
+             name + ", AltGraph of AltGraph " + e.type + " event mismatch");
+          return IS_WIN && testingEvent.modifiers.altGrKey &&
+                 removeFlag(e, kAltGraphFlag) && expectedDOMKeyCode != e.keyCode;
         case "Meta":
           is(e.ctrlKey, (flags & kCtrlFlag) != 0, name + ", Ctrl of Command " + e.type + " evnet mismatch");
           is(e.metaKey, e.type == "keydown", name + ", Command of Command " + e.type + " evnet mismatch");
           is(e.altKey, (flags & kAltFlag) != 0, name + ", Alt of Command " + e.type + " evnet mismatch");
           is(e.shiftKey, (flags & kShiftFlag) != 0, name + ", Shift of Command " + e.type + " evnet mismatch");
           is(e.getModifierState("AltGraph"),
              (IS_WIN && (flags & kAltGraphFlag) != 0) || (IS_MAC && e.altKey),
              name + ", AltGraph of Meta " + e.type + " event mismatch");
@@ -339,20 +357,22 @@ function* runKeyEventTests()
       // state changing for synthesizeNativeKeyEvent.
       if (aEvent.modifiers.shiftKey || aEvent.modifiers.shiftRightKey) {
         keyDownFlags |= kShiftFlag;
       }
       if (aEvent.modifiers.ctrlKey || aEvent.modifiers.ctrlRightKey ||
           (IS_WIN && aEvent.modifiers.altGrKey)) {
         keyDownFlags |= kCtrlFlag;
       }
-      if (aEvent.modifiers.altKey || aEvent.modifiers.altRightKey ||
-          (IS_WIN && aEvent.modifiers.altGrKey)) {
+      if (aEvent.modifiers.altKey || aEvent.modifiers.altRightKey) {
         keyDownFlags |= kAltFlag;
       }
+      if (aEvent.modifiers.altGrKey) {
+        keyDownFlags |= kAltGraphFlag;
+      }
       if (aEvent.modifiers.metaKey || aEvent.modifiers.metaRightKey) {
         keyDownFlags |= kMetaFlag;
       }
       if (aEvent.modifiers.numLockKey || aEvent.modifiers.numericKeyPadKey) {
         keyDownFlags |= kNumLockFlag;
       }
       if (aEvent.modifiers.capsLockKey) {
         keyDownFlags |= kCapsLockFlag;
@@ -5300,22 +5320,187 @@ function* runTextInputTests()
                   "0");
   }
 
   // XXX We need to move focus for canceling to search the autocomplete
   // result. If we don't do here, Fx will crash at end of this tests.
   document.getElementById("button").focus();
 }
 
+function* runAltRightKeyOnWindows()
+{
+  if (!IS_WIN) {
+    return;
+  }
+
+  var button = document.getElementById("button");
+  button.focus();
+
+  const kKeyboardLayouts = [
+    { layout: KEYBOARD_LAYOUT_ARABIC },
+    { layout: KEYBOARD_LAYOUT_BRAZILIAN_ABNT },
+    { layout: KEYBOARD_LAYOUT_EN_US },
+    { layout: KEYBOARD_LAYOUT_FRENCH },
+    { layout: KEYBOARD_LAYOUT_GREEK },
+    { layout: KEYBOARD_LAYOUT_GERMAN },
+    { layout: KEYBOARD_LAYOUT_HEBREW },
+    { layout: KEYBOARD_LAYOUT_JAPANESE },
+    { layout: KEYBOARD_LAYOUT_KHMER },
+    { layout: KEYBOARD_LAYOUT_LITHUANIAN },
+    { layout: KEYBOARD_LAYOUT_NORWEGIAN },
+    { layout: KEYBOARD_LAYOUT_RUSSIAN_MNEMONIC,
+      canTestIt: function() { return OS_VERSION >= WIN8; } },
+    { layout: KEYBOARD_LAYOUT_SPANISH },
+    { layout: KEYBOARD_LAYOUT_SWEDISH },
+    { layout: KEYBOARD_LAYOUT_THAI },
+  ];
+  var events = [];
+  function pushEvent(aEvent) {
+    events.push(aEvent);
+    if (aEvent.key === "Alt") {
+      // Prevent working the menubar.
+      aEvent.preventDefault();
+    }
+  }
+  button.addEventListener("keydown", pushEvent);
+  button.addEventListener("keyup", pushEvent);
+
+  function testKey(aKeyboardLayout) {
+    return synthesizeKey({layout: aKeyboardLayout.layout, keyCode: WIN_VK_RMENU,
+                          modifiers: {}, chars: ""}, "button", function() {
+      const kDescription =
+        "runAltRightKeyOnWindows(" + aKeyboardLayout.layout.name + "): ";
+      if (aKeyboardLayout.layout.hasAltGrOnWin) {
+        is(events.length, 4,
+           kDescription + "AltRight should fire 2 pairs of keydown and keyup events");
+        is(events[0].type, "keydown",
+           kDescription + "First event should be keydown of ControlLeft");
+        is(events[0].key, "Control",
+           kDescription + "First event should be keydown of ControlLeft whose key should be Control");
+        is(events[0].code, "ControlLeft",
+           kDescription + "First event should be keydown of ControlLeft");
+        is(events[0].location, KeyboardEvent.DOM_KEY_LOCATION_LEFT,
+           kDescription + "First event should be keydown of ControlLeft whose location should be DOM_KEY_LOCATION_LEFT");
+        is(events[0].keyCode, KeyboardEvent.DOM_VK_CONTROL,
+           kDescription + "First event should be keydown of ControlLeft whose keyCode should be DOM_VK_CONTROL");
+        is(events[0].ctrlKey, true,
+           kDescription + "First event should be keydown of ControlLeft whose ctrlKey should be true");
+        is(events[0].altKey, false,
+           kDescription + "First event should be keydown of ControlLeft whose altKey should be false");
+        is(events[0].getModifierState("AltGraph"), false,
+           kDescription + "First event should be keydown of ControlLeft whose getModifierState(\"AltGraph\") should be false");
+        is(events[1].type, "keydown",
+           kDescription + "Second event should be keydown of AltRight");
+        is(events[1].key, "AltGraph",
+           kDescription + "Second event should be keydown of AltRight whose key should be AltGraph");
+        is(events[1].code, "AltRight",
+           kDescription + "Second event should be keydown of AltRight");
+        is(events[1].location, KeyboardEvent.DOM_KEY_LOCATION_RIGHT,
+           kDescription + "Second event should be keydown of AltRight whose location should be DOM_KEY_LOCATION_RIGHT");
+        is(events[1].keyCode, KeyboardEvent.DOM_VK_ALT,
+           kDescription + "Second event should be keydown of AltRight whose keyCode should be DOM_VK_ALT");
+        is(events[1].ctrlKey, false,
+           kDescription + "Second event should be keydown of AltRight whose ctrlKey should be false");
+        is(events[1].altKey, false,
+           kDescription + "Second event should be keydown of AltRight whose altKey should be false");
+        is(events[1].getModifierState("AltGraph"), true,
+           kDescription + "Second event should be keydown of AltRight whose getModifierState(\"AltGraph\") should be true");
+        is(events[2].type, "keyup",
+           kDescription + "Third event should be keyup of ControlLeft");
+        is(events[2].key, "Control",
+           kDescription + "Third event should be keyup of ControlLeft whose key should be Control");
+        is(events[2].code, "ControlLeft",
+           kDescription + "Third event should be keyup of ControlLeft");
+        is(events[2].location, KeyboardEvent.DOM_KEY_LOCATION_LEFT,
+           kDescription + "Third event should be keyup of ControlLeft whose location should be DOM_KEY_LOCATION_LEFT");
+        is(events[2].keyCode, KeyboardEvent.DOM_VK_CONTROL,
+           kDescription + "Third event should be keyup of ControlLeft whose keyCode should be DOM_VK_CONTROL");
+        is(events[2].ctrlKey, false,
+           kDescription + "Third event should be keyup of ControlLeft whose ctrlKey should be false");
+        is(events[2].altKey, false,
+           kDescription + "Third event should be keyup of ControlLeft whose altKey should be false");
+        is(events[2].getModifierState("AltGraph"), true,
+           kDescription + "Third event should be keyup of ControlLeft whose getModifierState(\"AltGraph\") should be true");
+        is(events[3].type, "keyup",
+           kDescription + "Forth event should be keyup of AltRight");
+        is(events[3].key, "AltGraph",
+           kDescription + "Forth event should be keyup of AltRight whose key should be AltGraph");
+        is(events[3].code, "AltRight",
+           kDescription + "Forth event should be keyup of AltRight");
+        is(events[3].location, KeyboardEvent.DOM_KEY_LOCATION_RIGHT,
+           kDescription + "Forth event should be keyup of AltRight whose location should be DOM_KEY_LOCATION_RIGHT");
+        is(events[3].keyCode, KeyboardEvent.DOM_VK_ALT,
+           kDescription + "Forth event should be keyup of AltRight whose keyCode should be DOM_VK_ALT");
+        is(events[3].ctrlKey, false,
+           kDescription + "Third event should be keyup of AltRight whose ctrlKey should be false");
+        is(events[3].altKey, false,
+           kDescription + "Third event should be keyup of AltRight whose altKey should be false");
+        is(events[3].getModifierState("AltGraph"), false,
+           kDescription + "Third event should be keyup of AltRight whose getModifierState(\"AltGraph\") should be false");
+      } else {
+        is(events.length, 2,
+           kDescription + "AltRight should fire a pair of keydown and keyup events");
+        is(events[0].type, "keydown",
+           kDescription + "First event should be keydown of AltRight");
+        is(events[0].key, "Alt",
+           kDescription + "First event should be keydown of AltRight whose key should be Alt");
+        is(events[0].code, "AltRight",
+           kDescription + "First event should be keydown of AltRight");
+        is(events[0].location, KeyboardEvent.DOM_KEY_LOCATION_RIGHT,
+           kDescription + "First event should be keydown of AltRight whose location should be DOM_KEY_LOCATION_RIGHT");
+        is(events[0].keyCode, KeyboardEvent.DOM_VK_ALT,
+           kDescription + "First event should be keydown of AltRight whose keyCode should be DOM_VK_ALT");
+        is(events[0].ctrlKey, false,
+           kDescription + "First event should be keydown of AltRight whose ctrlKey should be false");
+        is(events[0].altKey, true,
+           kDescription + "First event should be keydown of AltRight whose altKey should be true");
+        is(events[0].getModifierState("AltGraph"), false,
+           kDescription + "First event should be keydown of AltRight whose getModifierState(\"AltGraph\") should be false");
+        is(events[1].type, "keyup",
+           kDescription + "Second event should be keyup of AltRight");
+        is(events[1].key, "Alt",
+           kDescription + "Second event should be keyup of AltRight whose key should be Alt");
+        is(events[1].code, "AltRight",
+           kDescription + "Second event should be keyup of AltRight");
+        is(events[1].location, KeyboardEvent.DOM_KEY_LOCATION_RIGHT,
+           kDescription + "Second event should be keyup of AltRight whose location should be DOM_KEY_LOCATION_RIGHT");
+        is(events[1].keyCode, KeyboardEvent.DOM_VK_ALT,
+           kDescription + "Second event should be keyup of AltRight whose keyCode should be DOM_VK_ALT");
+        is(events[1].ctrlKey, false,
+           kDescription + "Second event should be keyup of AltRight whose ctrlKey should be false");
+        is(events[1].altKey, false,
+           kDescription + "Second event should be keyup of AltRight whose altKey should be false");
+        is(events[1].getModifierState("AltGraph"), false,
+           kDescription + "Second event should be keyup of AltRight whose getModifierState(\"AltGraph\") should be false");
+      }
+
+      continueTest();
+    });
+  }
+
+  for (const kKeyboardLayout of kKeyboardLayouts) {
+    if (typeof kKeyboardLayout.canTestIt === "function" &&
+        !kKeyboardLayout.canTestIt()) {
+      continue;
+    }
+    events = [];
+    yield testKey(kKeyboardLayout);
+  }
+
+  button.addEventListener("keydown", pushEvent);
+  button.addEventListener("keyup", pushEvent);
+}
+
 function* runAllTests() {
   yield* runKeyEventTests();
   yield* runAccessKeyTests();
   yield* runXULKeyTests();
   yield* runReservedKeyTests();
   yield* runTextInputTests();
+  yield* runAltRightKeyOnWindows();
 }
 
 var gTestContinuation = null;
 
 function continueTest()
 {
   if (!gTestContinuation) {
     gTestContinuation = runAllTests();
@@ -5331,18 +5516,21 @@ function continueTest()
 function runTest()
 {
   if (!IS_MAC && !IS_WIN) {
     todo(false, "This test is supported on MacOSX and Windows only. (Bug 431503)");
     return;
   }
 
   if (IS_WIN && OS_VERSION >= WIN8) {
-    // Switching keyboard layout to Russian - Mnemonic causes 2 assertions in KeyboardLayout::LoadLayout().
-    SimpleTest.expectAssertions(2, 2);
+    // Switching keyboard layout to Russian - Mnemonic causes 2 assertions in
+    // KeyboardLayout::LoadLayout().
+    const kAssertionCountDueToRussainMnemonic = 2 * 2;
+    SimpleTest.expectAssertions(kAssertionCountDueToRussainMnemonic,
+                                kAssertionCountDueToRussainMnemonic);
   }
   SimpleTest.waitForExplicitFinish();
 
   clearInfobars();
 
   continueTest();
 }
 
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -5,16 +5,17 @@
 
 #include "mozilla/Logging.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/MiscEvents.h"
+#include "mozilla/Preferences.h"
 #include "mozilla/TextEvents.h"
 
 #include "nsAlgorithm.h"
 #include "nsExceptionHandler.h"
 #include "nsGkAtoms.h"
 #include "nsIIdleServiceInternal.h"
 #include "nsIWindowsRegKey.h"
 #include "nsMemory.h"
@@ -1479,24 +1480,24 @@ NativeKey::InitWithKeyOrChar()
   }
 
   if (!mVirtualKeyCode) {
     mVirtualKeyCode = mOriginalVirtualKeyCode;
   }
 
   KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
   mDOMKeyCode =
-    keyboardLayout->ConvertNativeKeyCodeToDOMKeyCode(mOriginalVirtualKeyCode);
+    keyboardLayout->ConvertNativeKeyCodeToDOMKeyCode(mVirtualKeyCode);
   // Be aware, keyboard utilities can change non-printable keys to printable
   // keys.  In such case, we should make the key value as a printable key.
   // FYI: IsFollowedByPrintableCharMessage() returns true only when it's
   //      handling a keydown message.
   mKeyNameIndex = IsFollowedByPrintableCharMessage() ?
     KEY_NAME_INDEX_USE_STRING :
-    keyboardLayout->ConvertNativeKeyCodeToKeyNameIndex(mOriginalVirtualKeyCode);
+    keyboardLayout->ConvertNativeKeyCodeToKeyNameIndex(mVirtualKeyCode);
   mCodeNameIndex =
     KeyboardLayout::ConvertScanCodeToCodeNameIndex(
       GetScanCodeWithExtendedFlag());
 
   // If next message of WM_(SYS)KEYDOWN is WM_*CHAR message and the key
   // combination is not reserved by the system, let's consume it now.
   // TODO: We cannot initialize mCommittedCharsAndModifiers for VK_PACKET
   //       if the message is WM_KEYUP because we don't have preceding
@@ -4777,16 +4778,30 @@ KeyboardLayout::ConvertNativeKeyCodeToDO
 
     case VK_VOLUME_MUTE:
       return NS_VK_VOLUME_MUTE;
     case VK_VOLUME_DOWN:
       return NS_VK_VOLUME_DOWN;
     case VK_VOLUME_UP:
       return NS_VK_VOLUME_UP;
 
+    case VK_LSHIFT:
+    case VK_RSHIFT:
+      return NS_VK_SHIFT;
+
+    case VK_LCONTROL:
+    case VK_RCONTROL:
+      return NS_VK_CONTROL;
+
+    // Note that even if the key is AltGr, we should return NS_VK_ALT for
+    // compatibility with both older Gecko and the other browsers.
+    case VK_LMENU:
+    case VK_RMENU:
+      return NS_VK_ALT;
+
     // Following keycodes are not defined in our DOM keycodes.
     case VK_BROWSER_BACK:
     case VK_BROWSER_FORWARD:
     case VK_BROWSER_REFRESH:
     case VK_BROWSER_STOP:
     case VK_BROWSER_SEARCH:
     case VK_BROWSER_FAVORITES:
     case VK_BROWSER_HOME:
@@ -4975,16 +4990,22 @@ KeyboardLayout::ConvertNativeKeyCodeToDO
 
 KeyNameIndex
 KeyboardLayout::ConvertNativeKeyCodeToKeyNameIndex(uint8_t aVirtualKey) const
 {
   if (IsPrintableCharKey(aVirtualKey) || aVirtualKey == VK_PACKET) {
     return KEY_NAME_INDEX_USE_STRING;
   }
 
+  // If the keyboard layout has AltGr and AltRight key is pressed,
+  // return AltGraph.
+  if (aVirtualKey == VK_RMENU && HasAltGr()) {
+    return KEY_NAME_INDEX_AltGraph;
+  }
+
   switch (aVirtualKey) {
 
 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
     case aNativeKey: return aKeyNameIndex;
 
 #include "NativeKeyToDOMKeyName.h"
 
@@ -5096,16 +5117,17 @@ KeyboardLayout::SynthesizeNativeKeyEvent
   BYTE kbdState[256];
   memset(kbdState, 0, sizeof(kbdState));
   // This changes the state of the keyboard for the current thread only,
   // and we'll restore it soon, so this should be OK.
   ::SetKeyboardState(kbdState);
 
   OverrideLayout(loadedLayout);
 
+  bool isAltGrKeyPress = false;
   if (aModifierFlags & nsIWidget::ALTGRAPH) {
     if (!HasAltGr()) {
       return NS_ERROR_INVALID_ARG;
     }
     // AltGr emulates ControlLeft key press and AltRight key press.
     // So, we should remove those flags from aModifierFlags before
     // calling WinUtils::SetupKeyModifiersSequence() to create correct
     // key sequence.
@@ -5153,31 +5175,42 @@ KeyboardLayout::SynthesizeNativeKeyEvent
       aModifierFlags &= ~nsIWidget::ALT_L;
       argumentKeySpecific = aNativeKeyCode & 0xFF;
       aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_MENU;
       break;
     case VK_RMENU:
       aModifierFlags &= ~(nsIWidget::ALT_R | nsIWidget::ALTGRAPH);
       argumentKeySpecific = aNativeKeyCode & 0xFF;
       aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_MENU;
+      // If AltRight key is AltGr in the keyboard layout, let's use
+      // SetupKeyModifiersSequence() to emulate the native behavior
+      // since the same event order between keydown and keyup makes
+      // the following code complicated.
+      if (HasAltGr()) {
+        isAltGrKeyPress = true;
+        aModifierFlags &= ~nsIWidget::CTRL_L;
+        aModifierFlags |= nsIWidget::ALTGRAPH;
+      }
       break;
     case VK_CAPITAL:
       aModifierFlags &= ~nsIWidget::CAPS_LOCK;
       argumentKeySpecific = VK_CAPITAL;
       break;
     case VK_NUMLOCK:
       aModifierFlags &= ~nsIWidget::NUM_LOCK;
       argumentKeySpecific = VK_NUMLOCK;
       break;
   }
 
   AutoTArray<KeyPair,10> keySequence;
   WinUtils::SetupKeyModifiersSequence(&keySequence, aModifierFlags,
                                       WM_KEYDOWN);
-  keySequence.AppendElement(KeyPair(aNativeKeyCode, argumentKeySpecific));
+  if (!isAltGrKeyPress) {
+    keySequence.AppendElement(KeyPair(aNativeKeyCode, argumentKeySpecific));
+  }
 
   // Simulate the pressing of each modifier key and then the real key
   // FYI: Each NativeKey instance here doesn't need to override keyboard layout
   //      since this method overrides and restores the keyboard layout.
   for (uint32_t i = 0; i < keySequence.Length(); ++i) {
     uint8_t key = keySequence[i].mGeneral;
     uint8_t keySpecific = keySequence[i].mSpecific;
     uint16_t scanCode = keySequence[i].mScanCode;
@@ -5244,17 +5277,19 @@ KeyboardLayout::SynthesizeNativeKeyEvent
       }
     } else {
       NativeKey nativeKey(aWidget, keyDownMsg, modKeyState);
       nativeKey.HandleKeyDownMessage();
     }
   }
 
   keySequence.Clear();
-  keySequence.AppendElement(KeyPair(aNativeKeyCode, argumentKeySpecific));
+  if (!isAltGrKeyPress) {
+    keySequence.AppendElement(KeyPair(aNativeKeyCode, argumentKeySpecific));
+  }
   WinUtils::SetupKeyModifiersSequence(&keySequence, aModifierFlags,
                                       WM_KEYUP);
   for (uint32_t i = 0; i < keySequence.Length(); ++i) {
     uint8_t key = keySequence[i].mGeneral;
     uint8_t keySpecific = keySequence[i].mSpecific;
     uint16_t scanCode = keySequence[i].mScanCode;
     kbdState[key] = 0; // key is up and toggled off if appropriate
     if (keySpecific) {