Bug 855975 part.13 widget::NativeKey should manage following WM_*CHAR message if it's initialized with WM_*KEYDOWN r=jimm
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 29 May 2013 15:34:48 +0900
changeset 133240 fef076fed8ab620052e9d4474c93a7162958cdb3
parent 133239 d3aebac825b9c3af4c91c474c8aef972d896cea6
child 133241 91ba04b50639072addaaa5882251cf5ae976c650
push id28671
push usermasayuki@d-toybox.com
push dateWed, 29 May 2013 06:35:17 +0000
treeherdermozilla-inbound@57461a161f93 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs855975
milestone24.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 855975 part.13 widget::NativeKey should manage following WM_*CHAR message if it's initialized with WM_*KEYDOWN r=jimm
widget/windows/KeyboardLayout.cpp
widget/windows/KeyboardLayout.h
widget/windows/nsWindow.cpp
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -382,33 +382,49 @@ VirtualKey::FillKbdState(PBYTE aKbdState
 }
 
 /*****************************************************************************
  * mozilla::widget::NativeKey
  *****************************************************************************/
 
 NativeKey::NativeKey(nsWindowBase* aWidget,
                      const MSG& aKeyOrCharMessage,
-                     const ModifierKeyState& aModKeyState) :
+                     const ModifierKeyState& aModKeyState,
+                     const nsFakeCharMessage* aFakeCharMessage) :
   mWidget(aWidget), mMsg(aKeyOrCharMessage), mDOMKeyCode(0),
-  mModKeyState(aModKeyState), mVirtualKeyCode(0), mOriginalVirtualKeyCode(0)
+  mModKeyState(aModKeyState), mVirtualKeyCode(0), mOriginalVirtualKeyCode(0),
+  mIsFakeCharMsg(false)
 {
   MOZ_ASSERT(aWidget);
   KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
   mKeyboardLayout = keyboardLayout->GetLayout();
   mScanCode = WinUtils::GetScanCode(mMsg.lParam);
   mIsExtended = WinUtils::IsExtendedScanCode(mMsg.lParam);
+  memset(&mCharMsg, 0, sizeof(MSG));
   // On WinXP and WinServer2003, we cannot compute the virtual keycode for
   // extended keys due to the API limitation.
   bool canComputeVirtualKeyCodeFromScanCode =
     (!mIsExtended || WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION);
   switch (mMsg.message) {
     case WM_KEYDOWN:
+    case WM_SYSKEYDOWN:
+      // Store following WM_*CHAR message into mCharMsg.
+      if (aFakeCharMessage) {
+        mCharMsg = aFakeCharMessage->GetCharMessage(mMsg.hwnd);
+        mIsFakeCharMsg = true;
+      } else {
+        MSG msg;
+        if (WinUtils::PeekMessage(&msg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST,
+                                  PM_NOREMOVE | PM_NOYIELD) &&
+            (msg.message == WM_CHAR || msg.message == WM_SYSCHAR ||
+             msg.message == WM_DEADCHAR)) {
+          mCharMsg = msg;
+        }
+      }
     case WM_KEYUP:
-    case WM_SYSKEYDOWN:
     case WM_SYSKEYUP: {
       // First, resolve the IME converted virtual keycode to its original
       // keycode.
       if (mMsg.wParam == VK_PROCESSKEY) {
         mOriginalVirtualKeyCode =
           static_cast<uint8_t>(::ImmGetVirtualKey(mMsg.hwnd));
       } else {
         mOriginalVirtualKeyCode = static_cast<uint8_t>(mMsg.wParam);
@@ -544,17 +560,19 @@ NativeKey::NativeKey(nsWindowBase* aWidg
 
   mDOMKeyCode =
     keyboardLayout->ConvertNativeKeyCodeToDOMKeyCode(mOriginalVirtualKeyCode);
   mKeyNameIndex =
     keyboardLayout->ConvertNativeKeyCodeToKeyNameIndex(mOriginalVirtualKeyCode);
 
   keyboardLayout->InitNativeKey(*this, mModKeyState);
 
-  mIsDeadKey = keyboardLayout->IsDeadKey(mOriginalVirtualKeyCode, mModKeyState);
+  mIsDeadKey =
+    (mCharMsg.message == WM_DEADCHAR ||
+     keyboardLayout->IsDeadKey(mOriginalVirtualKeyCode, mModKeyState));
   mIsPrintableKey = KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode);
 }
 
 bool
 NativeKey::IsIMEDoingKakuteiUndo() const
 {
   // Following message pattern is caused by "Kakutei-Undo" of ATOK or WXG:
   // ---------------------------------------------------------------------------
@@ -935,66 +953,87 @@ NativeKey::NeedsToHandleWithoutFollowing
     return false;
   }
 
   // Even if the key is a printable key, it might cause non-printable character
   // input with modifier key(s).
   return IsPrintableKey();
 }
 
+const MSG&
+NativeKey::RemoveFollowingCharMessage() const
+{
+  MOZ_ASSERT(IsFollowedByCharMessage());
+
+  if (mIsFakeCharMsg) {
+    return mCharMsg;
+  }
+
+  MSG msg;
+  if (!WinUtils::GetMessage(&msg, mMsg.hwnd,
+                            mCharMsg.message, mCharMsg.message)) {
+    MOZ_NOT_REACHED("We lost the following char message");
+    return mCharMsg;
+  }
+
+  MOZ_ASSERT(mCharMsg.message == msg.message &&
+             mCharMsg.wParam == msg.wParam &&
+             mCharMsg.lParam == msg.lParam);
+
+  return mCharMsg;
+}
+
 void
-NativeKey::RemoveMessageAndDispatchPluginEvent(UINT aFirstMsg, UINT aLastMsg,
-                        const nsFakeCharMessage* aFakeCharMessage) const
+NativeKey::RemoveMessageAndDispatchPluginEvent(UINT aFirstMsg,
+                                               UINT aLastMsg) const
 {
   MSG msg;
-  if (aFakeCharMessage) {
+  if (mIsFakeCharMsg) {
     if (aFirstMsg > WM_CHAR || aLastMsg < WM_CHAR) {
       return;
     }
-    msg = aFakeCharMessage->GetCharMessage(mMsg.hwnd);
+    msg = mCharMsg;
   } else {
     WinUtils::GetMessage(&msg, mMsg.hwnd, aFirstMsg, aLastMsg);
   }
   mWidget->DispatchPluginEvent(msg);
 }
 
 bool
 NativeKey::DispatchKeyPressEventsAndDiscardsCharMessages(
                         const UniCharsAndModifiers& aInputtingChars,
-                        const EventFlags& aExtraFlags,
-                        const nsFakeCharMessage* aFakeCharMessage) const
+                        const EventFlags& aExtraFlags) const
 {
   MOZ_ASSERT(mMsg.message == WM_KEYDOWN || mMsg.message == WM_SYSKEYDOWN);
 
   // Remove a possible WM_CHAR or WM_SYSCHAR messages from the message queue.
   // They can be more than one because of:
   //  * Dead-keys not pairing with base character
   //  * Some keyboard layouts may map up to 4 characters to the single key
   bool anyCharMessagesRemoved = false;
 
-  if (aFakeCharMessage) {
-    RemoveMessageAndDispatchPluginEvent(WM_KEYFIRST, WM_KEYLAST,
-                                        aFakeCharMessage);
+  if (mIsFakeCharMsg) {
+    RemoveMessageAndDispatchPluginEvent(WM_KEYFIRST, WM_KEYLAST);
     anyCharMessagesRemoved = true;
   } else {
     MSG msg;
     bool gotMsg =
       WinUtils::PeekMessage(&msg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST,
                             PM_NOREMOVE | PM_NOYIELD);
     while (gotMsg && (msg.message == WM_CHAR || msg.message == WM_SYSCHAR)) {
       RemoveMessageAndDispatchPluginEvent(WM_KEYFIRST, WM_KEYLAST);
       anyCharMessagesRemoved = true;
       gotMsg = WinUtils::PeekMessage(&msg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST,
                                      PM_NOREMOVE | PM_NOYIELD);
     }
   }
 
   if (!anyCharMessagesRemoved &&
       mDOMKeyCode == NS_VK_BACK && IsIMEDoingKakuteiUndo()) {
-    MOZ_ASSERT(!aFakeCharMessage);
+    MOZ_ASSERT(!mIsFakeCharMsg);
     RemoveMessageAndDispatchPluginEvent(WM_CHAR, WM_CHAR);
   }
 
   return DispatchKeyPressEventsWithKeyboardLayout(aInputtingChars, aExtraFlags);
 }
 
 bool
 NativeKey::DispatchKeyPressEventsWithKeyboardLayout(
--- a/widget/windows/KeyboardLayout.h
+++ b/widget/windows/KeyboardLayout.h
@@ -278,30 +278,37 @@ public:
 
 class MOZ_STACK_CLASS NativeKey
 {
   friend class KeyboardLayout;
 
 public:
   NativeKey(nsWindowBase* aWidget,
             const MSG& aKeyOrCharMessage,
-            const ModifierKeyState& aModKeyState);
+            const ModifierKeyState& aModKeyState,
+            const nsFakeCharMessage* aFakeCharMessage = nullptr);
 
   uint32_t GetDOMKeyCode() const { return mDOMKeyCode; }
   KeyNameIndex GetKeyNameIndex() const { return mKeyNameIndex; }
   const UniCharsAndModifiers& GetCommittedCharsAndModifiers() const
   {
     return mCommittedCharsAndModifiers;
   }
 
   UINT GetMessage() const { return mMsg.message; }
   bool IsKeyDownMessage() const
   {
     return (mMsg.message == WM_KEYDOWN || mMsg.message == WM_SYSKEYDOWN);
   }
+  bool IsFollowedByCharMessage() const
+  {
+    MOZ_ASSERT(mMsg.message == WM_KEYDOWN || mMsg.message == WM_SYSKEYDOWN);
+    return (mCharMsg.message != 0);
+  }
+  const MSG& RemoveFollowingCharMessage() const;
   bool IsDeadKey() const { return mIsDeadKey; }
   /**
    * IsPrintableKey() returns true if the key may be a printable key without
    * any modifier keys.  Otherwise, false.
    * Please note that the event may not cause any text input even if this
    * returns true.  E.g., it might be dead key state or Ctrl key may be pressed.
    */
   inline bool IsPrintableKey() const { return mIsPrintableKey; }
@@ -352,18 +359,17 @@ public:
   /**
    * Dispatches keypress events after removing WM_*CHAR messages for the
    * WM_*KEYDOWN message.
    * Returns true if the dispatched keypress event is consumed.  Otherwise,
    * false.
    */
   bool DispatchKeyPressEventsAndDiscardsCharMessages(
                         const UniCharsAndModifiers& aInputtingChars,
-                        const EventFlags& aExtraFlags,
-                        const nsFakeCharMessage* aFakeCharMessage) const;
+                        const EventFlags& aExtraFlags) const;
 
   /**
    * Checkes whether the key event down message is handled without following
    * WM_CHAR messages.  For example, if following WM_CHAR message indicates
    * control character input, the WM_CHAR message is unclear whether it's
    * caused by a printable key with Ctrl or just a function key such as Enter
    * or Backspace.
    */
@@ -389,16 +395,20 @@ public:
    * Otherwise, false.
    */
   bool HandleKeyUpMessage(bool* aEventDispatched = nullptr) const;
 
 private:
   nsRefPtr<nsWindowBase> mWidget;
   HKL mKeyboardLayout;
   MSG mMsg;
+  // mCharMsg stores WM_*CHAR message following WM_*KEYDOWN message.
+  // If mMsg isn't WM_*KEYDOWN message or WM_*KEYDOWN but there is no following
+  // WM_*CHAR message, the message member is 0.
+  MSG mCharMsg;
 
   uint32_t mDOMKeyCode;
   KeyNameIndex mKeyNameIndex;
 
   ModifierKeyState mModKeyState;
 
   // mVirtualKeyCode distinguishes left key or right key of modifier key.
   uint8_t mVirtualKeyCode;
@@ -411,16 +421,17 @@ private:
   // the key.  If dead key fail to composite a character, mCommittedChars
   // indicates both the dead characters and the base characters.
   UniCharsAndModifiers mCommittedCharsAndModifiers;
 
   WORD    mScanCode;
   bool    mIsExtended;
   bool    mIsDeadKey;
   bool    mIsPrintableKey;
+  bool    mIsFakeCharMsg;
 
   NativeKey()
   {
     MOZ_NOT_REACHED("The default constructor of NativeKey isn't available");
   }
 
   UINT GetScanCodeWithExtendedFlag() const;
 
@@ -432,18 +443,17 @@ private:
    * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern.  So, when this
    * returns true, the caller needs to be careful for processing the messages.
    */
   bool IsIMEDoingKakuteiUndo() const;
 
   /*
    * Dispatches a plugin event after the specified message is removed.
    */
-  void RemoveMessageAndDispatchPluginEvent(UINT aFirstMsg, UINT aLastMsg,
-                const nsFakeCharMessage* aFakeCharMessage = nullptr) const;
+  void RemoveMessageAndDispatchPluginEvent(UINT aFirstMsg, UINT aLastMsg) const;
 };
 
 class KeyboardLayout
 {
   friend class NativeKey;
 
 private:
   KeyboardLayout();
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -6353,17 +6353,17 @@ bool nsWindow::IsRedirectedKeyDownMessag
  * looking at or touching the message queue.
  */
 LRESULT nsWindow::OnKeyDown(const MSG &aMsg,
                             const ModifierKeyState &aModKeyState,
                             bool *aEventDispatched,
                             nsFakeCharMessage* aFakeCharMessage)
 {
   KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
-  NativeKey nativeKey(this, aMsg, aModKeyState);
+  NativeKey nativeKey(this, aMsg, aModKeyState, aFakeCharMessage);
   UniCharsAndModifiers inputtingChars =
     nativeKey.GetCommittedCharsAndModifiers();
   uint32_t DOMKeyCode = nativeKey.GetDOMKeyCode();
 
   static bool sRedirectedKeyDownEventPreventedDefault = false;
   bool noDefault;
   if (aFakeCharMessage || !IsRedirectedKeyDownMessage(aMsg)) {
     bool isIMEEnabled = IMEHandler::IsIMEEnabled(mInputContext);
@@ -6448,28 +6448,22 @@ LRESULT nsWindow::OnKeyDown(const MSG &a
       return noDefault;
   }
 
   EventFlags extraFlags;
   extraFlags.mDefaultPrevented = noDefault;
 
   if (nativeKey.NeedsToHandleWithoutFollowingCharMessages()) {
     return nativeKey.DispatchKeyPressEventsAndDiscardsCharMessages(
-                       inputtingChars, extraFlags, aFakeCharMessage);
-  }
-
-  MSG msg;
-  BOOL gotMsg = aFakeCharMessage ||
-    WinUtils::PeekMessage(&msg, mWnd, WM_KEYFIRST, WM_KEYLAST,
-                          PM_NOREMOVE | PM_NOYIELD);
-  if (gotMsg &&
-      (aFakeCharMessage || msg.message == WM_CHAR ||
-       msg.message == WM_SYSCHAR || msg.message == WM_DEADCHAR)) {
+                       inputtingChars, extraFlags);
+  }
+
+  if (nativeKey.IsFollowedByCharMessage()) {
+    const MSG& msg = nativeKey.RemoveFollowingCharMessage();
     if (aFakeCharMessage) {
-      MSG msg = aFakeCharMessage->GetCharMessage(mWnd);
       if (msg.message == WM_DEADCHAR) {
         return false;
       }
 #ifdef DEBUG
       if (nativeKey.IsPrintableKey()) {
         nsPrintfCString log(
           "OriginalVirtualKeyCode=0x%02X, inputtingChar={ mChars=[ 0x%04X, "
           "0x%04X, 0x%04X, 0x%04X, 0x%04X ], mLength=%d }, wParam=0x%04X",
@@ -6488,18 +6482,16 @@ LRESULT nsWindow::OnKeyDown(const MSG &a
         }
       }
 #endif // #ifdef DEBUG
       return static_cast<LRESULT>(
         nativeKey.HandleCharMessage(msg, nullptr, &extraFlags));
     }
 
     // If prevent default set for keydown, do same for keypress
-    WinUtils::GetMessage(&msg, mWnd, msg.message, msg.message);
-
     if (msg.message == WM_DEADCHAR) {
       if (!PluginHasFocus())
         return false;
 
       // We need to send the removed message to focused plug-in.
       nsWindowBase::DispatchPluginEvent(msg);
       return noDefault;
     }