Bug 1293505 - Part 2: KeyboardLayout::SynthesizeNativeKeyEvent() should emulate WM_SYEKEYDOWN, WM_SYSCHAR, WM_SYSDEADCHAR and WM_SYSKEYUP correctly. r=m_kato, a=ritu
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 31 Aug 2016 16:42:27 +0900
changeset 333031 87c57eedbd7f0f4b45c5b421b4a9d434025c0570
parent 333030 9731047ebbfa20df44816f7874f57bc8eef9f318
child 333032 5968286c776b09bd2d008a9acf80ec0e7d667907
push id9975
push userryanvm@gmail.com
push dateFri, 02 Sep 2016 01:47:46 +0000
treeherdermozilla-aurora@d76058fbf56f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato, ritu
bugs1293505
milestone50.0a2
Bug 1293505 - Part 2: KeyboardLayout::SynthesizeNativeKeyEvent() should emulate WM_SYEKEYDOWN, WM_SYSCHAR, WM_SYSDEADCHAR and WM_SYSKEYUP correctly. r=m_kato, a=ritu The new test failure is caused by a bug of the test API, KeyboardLayout::SysnthesizeNativeKeyEvent(). It doesn't generate WM_SYSKEY* messages nor WM_SYS*CHAR messages when Alt key is pressed. Therefore, the new path in the previous code works unexpectedly with automated tests. This patch makes KeyboardLayout::SysnthesizeNativeKeyEvent() WM_SYS* message aware. When Alt key is pressed (but Ctrl key is not pressed) and the Alt key + something doesn't cause text input, the API generates WM_SYS* messages as expected. MozReview-Commit-ID: FLbe4SYEZLf
widget/windows/KeyboardLayout.cpp
widget/windows/KeyboardLayout.h
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -2550,16 +2550,43 @@ KeyboardLayout::IsDeadKey(uint8_t aVirtu
   if (virtualKeyIndex < 0) {
     return false;
   }
 
   return mVirtualKeys[virtualKeyIndex].IsDeadKey(
            VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()));
 }
 
+bool
+KeyboardLayout::IsSysKey(uint8_t aVirtualKey,
+                         const ModifierKeyState& aModKeyState) const
+{
+  // If Alt key is not pressed, it's never a system key combination.
+  // Additionally, if Ctrl key is pressed, it's never a system key combination
+  // too.
+  if (!aModKeyState.IsAlt() || aModKeyState.IsControl()) {
+    return false;
+  }
+
+  int32_t virtualKeyIndex = GetKeyIndex(aVirtualKey);
+  if (virtualKeyIndex < 0) {
+    return true;
+  }
+
+  UniCharsAndModifiers inputCharsAndModifiers =
+    GetUniCharsAndModifiers(aVirtualKey, aModKeyState);
+  if (inputCharsAndModifiers.IsEmpty()) {
+    return true;
+  }
+
+  // If the Alt key state isn't consumed, that means that the key with Alt
+  // doesn't cause text input.  So, the combination is a system key.
+  return inputCharsAndModifiers.mModifiers[0] != MODIFIER_ALT;
+}
+
 void
 KeyboardLayout::InitNativeKey(NativeKey& aNativeKey,
                               const ModifierKeyState& aModKeyState)
 {
   if (mIsPendingToRestoreKeyboardLayout) {
     LoadLayout(::GetKeyboardLayout(0));
   }
 
@@ -3458,18 +3485,20 @@ KeyboardLayout::SynthesizeNativeKeyEvent
     UINT scanCode =
       ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key);
     LPARAM lParam = static_cast<LPARAM>(scanCode << 16);
     // Add extended key flag to the lParam for right control key and right alt
     // key.
     if (keySpecific == VK_RCONTROL || keySpecific == VK_RMENU) {
       lParam |= 0x1000000;
     }
-    MSG keyDownMsg = WinUtils::InitMSG(WM_KEYDOWN, key, lParam,
-                                       aWidget->GetWindowHandle());
+    bool makeSysKeyMsg = IsSysKey(key, modKeyState);
+    MSG keyDownMsg =
+      WinUtils::InitMSG(makeSysKeyMsg ? WM_SYSKEYDOWN : WM_KEYDOWN,
+                        key, lParam, aWidget->GetWindowHandle());
     if (i == keySequence.Length() - 1) {
       bool makeDeadCharMsg =
         (IsDeadKey(key, modKeyState) && aCharacters.IsEmpty());
       nsAutoString chars(aCharacters);
       if (makeDeadCharMsg) {
         UniCharsAndModifiers deadChars =
           GetUniCharsAndModifiers(key, modKeyState);
         chars = deadChars.ToString();
@@ -3480,16 +3509,17 @@ KeyboardLayout::SynthesizeNativeKeyEvent
         NativeKey nativeKey(aWidget, keyDownMsg, modKeyState);
         nativeKey.HandleKeyDownMessage();
       } else {
         AutoTArray<NativeKey::FakeCharMsg, 10> fakeCharMsgs;
         for (uint32_t j = 0; j < chars.Length(); j++) {
           NativeKey::FakeCharMsg* fakeCharMsg = fakeCharMsgs.AppendElement();
           fakeCharMsg->mCharCode = chars.CharAt(j);
           fakeCharMsg->mScanCode = scanCode;
+          fakeCharMsg->mIsSysKey = makeSysKeyMsg;
           fakeCharMsg->mIsDeadKey = makeDeadCharMsg;
         }
         NativeKey nativeKey(aWidget, keyDownMsg, modKeyState, 0, &fakeCharMsgs);
         bool dispatched;
         nativeKey.HandleKeyDownMessage(&dispatched);
         // If some char messages are not consumed, let's emulate the widget
         // receiving the message directly.
         for (uint32_t j = 1; j < fakeCharMsgs.Length(); j++) {
@@ -3518,17 +3548,20 @@ KeyboardLayout::SynthesizeNativeKeyEvent
     UINT scanCode =
       ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key);
     LPARAM lParam = static_cast<LPARAM>(scanCode << 16);
     // Add extended key flag to the lParam for right control key and right alt
     // key.
     if (keySpecific == VK_RCONTROL || keySpecific == VK_RMENU) {
       lParam |= 0x1000000;
     }
-    MSG keyUpMsg = WinUtils::InitMSG(WM_KEYUP, key, lParam,
+    // Don't use WM_SYSKEYUP for Alt keyup.
+    bool makeSysKeyMsg = IsSysKey(key, modKeyState) && key != VK_MENU;
+    MSG keyUpMsg = WinUtils::InitMSG(makeSysKeyMsg ? WM_SYSKEYUP : WM_KEYUP,
+                                     key, lParam,
                                      aWidget->GetWindowHandle());
     NativeKey nativeKey(aWidget, keyUpMsg, modKeyState);
     nativeKey.HandleKeyUpMessage();
   }
 
   // Restore old key state and layout
   ::SetKeyboardState(originalKbdState);
   RestoreLayout();
--- a/widget/windows/KeyboardLayout.h
+++ b/widget/windows/KeyboardLayout.h
@@ -175,29 +175,37 @@ class MOZ_STACK_CLASS NativeKey final
 {
   friend class KeyboardLayout;
 
 public:
   struct FakeCharMsg
   {
     UINT mCharCode;
     UINT mScanCode;
+    bool mIsSysKey;
     bool mIsDeadKey;
     bool mConsumed;
 
-    FakeCharMsg() :
-      mCharCode(0), mScanCode(0), mIsDeadKey(false), mConsumed(false)
+    FakeCharMsg()
+      : mCharCode(0)
+      , mScanCode(0)
+      , mIsSysKey(false)
+      , mIsDeadKey(false)
+      , mConsumed(false)
     {
     }
 
     MSG GetCharMsg(HWND aWnd) const
     {
       MSG msg;
       msg.hwnd = aWnd;
-      msg.message = mIsDeadKey ? WM_DEADCHAR : WM_CHAR;
+      msg.message = mIsDeadKey && mIsSysKey ? WM_SYSDEADCHAR :
+                                 mIsDeadKey ? WM_DEADCHAR :
+                                  mIsSysKey ? WM_SYSCHAR :
+                                              WM_CHAR;
       msg.wParam = static_cast<WPARAM>(mCharCode);
       msg.lParam = static_cast<LPARAM>(mScanCode << 16);
       msg.time = 0;
       msg.pt.x = msg.pt.y = 0;
       return msg;
     }
   };
 
@@ -585,16 +593,23 @@ public:
   /**
    * IsDeadKey() returns true if aVirtualKey is a dead key with aModKeyState.
    * This method isn't stateful.
    */
   bool IsDeadKey(uint8_t aVirtualKey,
                  const ModifierKeyState& aModKeyState) const;
 
   /**
+   * IsSysKey() returns true if aVirtualKey with aModKeyState causes WM_SYSKEY*
+   * or WM_SYS*CHAR messages.
+   */
+  bool IsSysKey(uint8_t aVirtualKey,
+                const ModifierKeyState& aModKeyState) const;
+
+  /**
    * GetUniCharsAndModifiers() returns characters which is inputted by the
    * aVirtualKey with aModKeyState.  This method isn't stateful.
    */
   UniCharsAndModifiers GetUniCharsAndModifiers(
                          uint8_t aVirtualKey,
                          const ModifierKeyState& aModKeyState) const;
 
   /**