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 draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 31 May 2018 18:36:33 +0900
changeset 806857 d5673bbd1fee3b2c6eede1e96e50a057c828bf59
parent 806856 900222ca199f4fa91d2edd3eaeb84e7e13b6b7ad
child 806858 ad1b8d3c634efe2526abcca44e0c2a934895d9b3
push id112973
push usermasayuki@d-toybox.com
push dateTue, 12 Jun 2018 10:30:22 +0000
reviewersm_kato, smaug
bugs900750
milestone62.0a1
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) {