Bug 1303273 part.3 Dispatch eKeyPress events without NativeKey::HandleCharMessage() when it handles WM_(SYS)KEYDOWN message and there are following WM_(SYS)CHAR messages which includes non-control character r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 06 Oct 2016 20:52:03 +0900
changeset 423627 41edfc38e7fd91262f57bc0ff89149f38ce01567
parent 423626 3d6499049912f3213863ca3abb349b8e7676ff24
child 423628 4060330fe71ba599363a1c46a603eba55e2ebd73
push id31948
push usermasayuki@d-toybox.com
push dateTue, 11 Oct 2016 12:46:29 +0000
reviewersm_kato
bugs1303273
milestone52.0a1
Bug 1303273 part.3 Dispatch eKeyPress events without NativeKey::HandleCharMessage() when it handles WM_(SYS)KEYDOWN message and there are following WM_(SYS)CHAR messages which includes non-control character r?m_kato This patch creates NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages() for dispatching eKeyPress event with mCommittedCharsAndModifiers when it stores following printable WM_(SYS)CHAR messages. Using loop for dispatching eKeyPress event for every WM_(SYS)CHAR message is wrong because WidgetKeyboardEvent::mKeyValue is initialized with mCommittedCharsAndModifiers and it causes TextEventDispatcher dispatching multiple eKeyPress events at every call of MaybeDispatchKeypressEvents(). Therefore, if mKeyValue is "^^", eKeyPress event is dispatched 4 times --for the first message, eKeyPress events are fired for each "^" and for the second message, eKeyPress events are fired again for each "^"--. Therefore, when it handles WM_(SYS)KEYDOWN and it causes inputting one or more printable characters, it's the easiest way not to use HandleCharMessage(). The new method calls TextEventDispatcher::MaybeDispatchKeypressEvents() only once and it requests to call the callback method with new argument of MaybeDispatchKeypressEvents() when it needs to dispatch 2 or more eKeyPress events. Then, NativeKey::WillDispatchKeyboardEvent() can set each eKeyPress event to raw information of the message and proper modifier state. With this change, we can dispatch multiple eKeyPress events with retrieved WM_(SYS)CHAR message information rather than retrieved information from active keyboard layout. Therefore, NeedsToHandleWithoutFollowingCharMessages() doesn't return true even when mCommittedCharsAndModifiers stores two or more characters. FYI: there is a bug in test_keycodes.xul. That is, Alt+'A' of Greek keyboard layout should cause WM_SYSCHAR with a corresponding Greek character but ASCII characters are specified. Therefore, this patch includes the fix of these bugs MozReview-Commit-ID: JVm7ZJVug0O
widget/TextEventDispatcher.cpp
widget/TextEventDispatcher.h
widget/tests/test_keycodes.xul
widget/windows/KeyboardLayout.cpp
widget/windows/KeyboardLayout.h
--- a/widget/TextEventDispatcher.cpp
+++ b/widget/TextEventDispatcher.cpp
@@ -392,17 +392,18 @@ TextEventDispatcher::DispatchKeyboardEve
 }
 
 bool
 TextEventDispatcher::DispatchKeyboardEventInternal(
                        EventMessage aMessage,
                        const WidgetKeyboardEvent& aKeyboardEvent,
                        nsEventStatus& aStatus,
                        void* aData,
-                       uint32_t aIndexOfKeypress)
+                       uint32_t aIndexOfKeypress,
+                       bool aNeedsCallback)
 {
   // Note that this method is also used for dispatching key events on a plugin
   // because key events on a plugin should be dispatched same as normal key
   // events.  Then, only some handlers which need to intercept key events
   // before the focused plugin (e.g., reserved shortcut key handlers) can
   // consume the events.
   MOZ_ASSERT(WidgetKeyboardEvent::IsKeyDownOrKeyDownOnPlugin(aMessage) ||
              WidgetKeyboardEvent::IsKeyUpOrKeyUpOnPlugin(aMessage) ||
@@ -496,17 +497,17 @@ TextEventDispatcher::DispatchKeyboardEve
 
   // Request the alternative char codes for the key event.
   // eKeyDown also needs alternative char codes because nsXBLWindowKeyHandler
   // needs to check if a following keypress event is reserved by chrome for
   // stopping propagation of its preceding keydown event.
   keyEvent.mAlternativeCharCodes.Clear();
   if ((WidgetKeyboardEvent::IsKeyDownOrKeyDownOnPlugin(aMessage) ||
        aMessage == eKeyPress) &&
-      (keyEvent.IsControl() || keyEvent.IsAlt() ||
+      (aNeedsCallback || keyEvent.IsControl() || keyEvent.IsAlt() ||
        keyEvent.IsMeta() || keyEvent.IsOS())) {
     nsCOMPtr<TextEventDispatcherListener> listener =
       do_QueryReferent(mListener);
     if (listener) {
       DebugOnly<WidgetKeyboardEvent> original(keyEvent);
       listener->WillDispatchKeyboardEvent(this, keyEvent, aIndexOfKeypress,
                                           aData);
       MOZ_ASSERT(keyEvent.mMessage ==
@@ -533,17 +534,18 @@ TextEventDispatcher::DispatchKeyboardEve
   DispatchInputEvent(mWidget, keyEvent, aStatus);
   return true;
 }
 
 bool
 TextEventDispatcher::MaybeDispatchKeypressEvents(
                        const WidgetKeyboardEvent& aKeyboardEvent,
                        nsEventStatus& aStatus,
-                       void* aData)
+                       void* aData,
+                       bool aNeedsCallback)
 {
   // If the key event was consumed, keypress event shouldn't be fired.
   if (aStatus == nsEventStatus_eConsumeNoDefault) {
     return false;
   }
 
   // If the key shouldn't cause keypress events, don't fire them.
   if (!aKeyboardEvent.ShouldCauseKeypressEvents()) {
@@ -558,17 +560,17 @@ TextEventDispatcher::MaybeDispatchKeypre
     aKeyboardEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING ?
       1 : std::max(static_cast<nsAString::size_type>(1),
                    aKeyboardEvent.mKeyValue.Length());
   bool isDispatched = false;
   bool consumed = false;
   for (size_t i = 0; i < keypressCount; i++) {
     aStatus = nsEventStatus_eIgnore;
     if (!DispatchKeyboardEventInternal(eKeyPress, aKeyboardEvent,
-                                       aStatus, aData, i)) {
+                                       aStatus, aData, i, aNeedsCallback)) {
       // The widget must have been gone.
       break;
     }
     isDispatched = true;
     if (!consumed) {
       consumed = (aStatus == nsEventStatus_eConsumeNoDefault);
     }
   }
--- a/widget/TextEventDispatcher.h
+++ b/widget/TextEventDispatcher.h
@@ -279,22 +279,26 @@ public:
    *                        nsEventStatus_eConsumeNoDefault, this does NOT
    *                        dispatch keypress events.
    *                        When this method dispatches one or more keypress
    *                        events and one of them is consumed, this returns
    *                        nsEventStatus_eConsumeNoDefault.
    * @param aData           Calling this method may cause calling
    *                        WillDispatchKeyboardEvent() of the listener.
    *                        aData will be set to its argument.
+   * @param aNeedsCallback  Set true when caller needs to initialize each
+   *                        eKeyPress event immediately before dispatch.
+   *                        Then, WillDispatchKeyboardEvent() is always called.
    * @return                true if one or more events are dispatched.
    *                        Otherwise, false.
    */
   bool MaybeDispatchKeypressEvents(const WidgetKeyboardEvent& aKeyboardEvent,
                                    nsEventStatus& aStatus,
-                                   void* aData = nullptr);
+                                   void* aData = nullptr,
+                                   bool aNeedsCallback = false);
 
 private:
   // mWidget is owner of the instance.  When this is created, this is set.
   // And when mWidget is released, this is cleared by OnDestroyWidget().
   // Note that mWidget may be destroyed already (i.e., mWidget->Destroyed() may
   // return true).
   nsIWidget* mWidget;
   // mListener is a weak reference to TextEventDispatcherListener.  That might
@@ -458,21 +462,25 @@ private:
    *                        aData will be set to its argument.
    * @param aIndexOfKeypress    This must be 0 if aMessage isn't eKeyPress or
    *                            aKeyboard.mKeyNameIndex isn't
    *                            KEY_NAME_INDEX_USE_STRING.  Otherwise, i.e.,
    *                            when an eKeyPress event causes inputting
    *                            text, this must be between 0 and
    *                            mKeyValue.Length() - 1 since keypress events
    *                            sending only one character per event.
+   * @param aNeedsCallback  Set true when caller needs to initialize each
+   *                        eKeyPress event immediately before dispatch.
+   *                        Then, WillDispatchKeyboardEvent() is always called.
    * @return                true if an event is dispatched.  Otherwise, false.
    */
   bool DispatchKeyboardEventInternal(EventMessage aMessage,
                                      const WidgetKeyboardEvent& aKeyboardEvent,
                                      nsEventStatus& aStatus,
                                      void* aData,
-                                     uint32_t aIndexOfKeypress = 0);
+                                     uint32_t aIndexOfKeypress = 0,
+                                     bool aNeedsCallback = false);
 };
 
 } // namespace widget
 } // namespace mozilla
 
 #endif // #ifndef mozilla_widget_textcompositionsynthesizer_h_
--- a/widget/tests/test_keycodes.xul
+++ b/widget/tests/test_keycodes.xul
@@ -3755,27 +3755,27 @@ function* runAccessKeyTests()
                    modifiers:{ctrlKey:1, shiftKey:1, altKey:1}, chars:""},
                   "a", false);
     yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_A,
                    modifiers:{ctrlKey:1, shiftKey:1, altKey:1}, chars:""},
                   "A", false);
 
     // Greek layout can activate a Latin accesskey
     yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
-                   modifiers:{shiftKey:1, altKey:1}, chars:"A"},
+                   modifiers:{shiftKey:1, altKey:1}, chars:"\u0391"},
                   "a", true);
     yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
-                   modifiers:{shiftKey:1, altKey:1}, chars:"A"},
+                   modifiers:{shiftKey:1, altKey:1}, chars:"\u0391"},
                   "A", true);
     // ... and a Greek accesskey!
     yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
-                   modifiers:{shiftKey:1, altKey:1}, chars:"A"},
+                   modifiers:{shiftKey:1, altKey:1}, chars:"\u0391"},
                   "\u03b1", true);
     yield testKey({layout:KEYBOARD_LAYOUT_GREEK, keyCode:WIN_VK_A,
-                   modifiers:{shiftKey:1, altKey:1}, chars:"A"},
+                   modifiers:{shiftKey:1, altKey:1}, chars:"\u0391"},
                   "\u0391", true);
 
     // bug 359638
     yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_OEM_PERIOD,
                    modifiers:{shiftKey:1, altKey:1}, chars:".", unmodifiedChars:"."},
                   ".", true);
   }
   
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -1514,17 +1514,17 @@ NativeKey::InitWithKeyChar()
     mKeyNameIndex == KEY_NAME_INDEX_USE_STRING ||
     KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode);
 
   if (IsKeyDownMessage()) {
     // Compute some strings which may be inputted by the key with various
     // modifier state if this key event won't cause text input actually.
     // They will be used for setting mAlternativeCharCodes in the callback
     // method which will be called by TextEventDispatcher.
-    if (NeedsToHandleWithoutFollowingCharMessages()) {
+    if (!IsFollowedByPrintableCharMessage()) {
       ComputeInputtingStringWithKeyboardLayout();
     }
     // Remove odd char messages if there are.
     RemoveFollowingOddCharMessages();
   }
 }
 
 void
@@ -1973,23 +1973,18 @@ NativeKey::InitKeyEvent(WidgetKeyboardEv
   if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
     aKeyEvent.mKeyValue = mCommittedCharsAndModifiers.ToString();
   }
   aKeyEvent.mCodeNameIndex = mCodeNameIndex;
   MOZ_ASSERT(mCodeNameIndex != CODE_NAME_INDEX_USE_STRING);
   aKeyEvent.mLocation = GetKeyLocation();
   aModKeyState.InitInputEvent(aKeyEvent);
 
-  NPEvent pluginEvent;
-  if (aMsgSentToPlugin &&
-      mWidget->GetInputContext().mIMEState.mEnabled == IMEState::PLUGIN) {
-    pluginEvent.event = aMsgSentToPlugin->message;
-    pluginEvent.wParam = aMsgSentToPlugin->wParam;
-    pluginEvent.lParam = aMsgSentToPlugin->lParam;
-    aKeyEvent.mPluginEvent.Copy(pluginEvent);
+  if (aMsgSentToPlugin) {
+    MaybeInitPluginEventOfKeyEvent(aKeyEvent, *aMsgSentToPlugin);
   }
 
   KeyboardLayout::NotifyIdleServiceOfUserActivity();
 
   MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
     ("%p   NativeKey::InitKeyEvent(), initialized, aKeyEvent={ "
      "mMessage=%s, mKeyNameIndex=%s, mKeyValue=\"%s\", mCodeNameIndex=%s, "
      "mKeyCode=%s, mLocation=%s, mModifiers=%s, DefaultPrevented()=%s }",
@@ -2001,16 +1996,30 @@ NativeKey::InitKeyEvent(WidgetKeyboardEv
      GetKeyLocationName(aKeyEvent.mLocation).get(),
      GetModifiersName(aKeyEvent.mModifiers).get(),
      GetBoolName(aKeyEvent.DefaultPrevented())));
 
   return aKeyEvent.DefaultPrevented() ? nsEventStatus_eConsumeNoDefault :
                                         nsEventStatus_eIgnore;
 }
 
+void
+NativeKey::MaybeInitPluginEventOfKeyEvent(WidgetKeyboardEvent& aKeyEvent,
+                                          const MSG& aMsgSentToPlugin) const
+{
+  if (mWidget->GetInputContext().mIMEState.mEnabled != IMEState::PLUGIN) {
+    return;
+  }
+  NPEvent pluginEvent;
+  pluginEvent.event = aMsgSentToPlugin.message;
+  pluginEvent.wParam = aMsgSentToPlugin.wParam;
+  pluginEvent.lParam = aMsgSentToPlugin.lParam;
+  aKeyEvent.mPluginEvent.Copy(pluginEvent);
+}
+
 bool
 NativeKey::DispatchCommandEvent(uint32_t aEventCommand) const
 {
   nsCOMPtr<nsIAtom> command;
   switch (aEventCommand) {
     case APPCOMMAND_BROWSER_BACKWARD:
       command = nsGkAtoms::Back;
       break;
@@ -2439,54 +2448,35 @@ NativeKey::HandleKeyDownMessage(bool* aE
     MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
       ("%p   NativeKey::HandleKeyDownMessage(), not dispatching keypress "
        "event because preceding keydown event was consumed",
        this));
     MaybeDispatchPluginEventsForRemovedCharMessages();
     return true;
   }
 
+  // If mCommittedCharsAndModifiers was initialized with following char
+  // messages, we should dispatch keypress events with its information.
+  if (IsFollowedByPrintableCharOrSysCharMessage()) {
+    MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
+      ("%p   NativeKey::HandleKeyDownMessage(), tries to be dispatching "
+       "keypress events with retrieved char messages...", this));
+    return DispatchKeyPressEventsWithRetrievedCharMessages();
+  }
+
   // If we won't be getting a WM_CHAR, WM_SYSCHAR or WM_DEADCHAR, synthesize a
   // keypress for almost all keys
   if (NeedsToHandleWithoutFollowingCharMessages()) {
     MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
       ("%p   NativeKey::HandleKeyDownMessage(), tries to be dispatching "
        "keypress events...", this));
     return (MaybeDispatchPluginEventsForRemovedCharMessages() ||
             DispatchKeyPressEventsWithoutCharMessage());
   }
 
-  if (!mFollowingCharMsgs.IsEmpty()) {
-    bool consumed = false;
-    for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
-        MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
-          ("%p   NativeKey::HandleKeyDownMessage(), stopped dispatching "
-           "keypress events for remaining char messages, consumed=%s, "
-           "mFollowingCharMsgs[%u]=%s, mMsg=%s, "
-           "mFocusedWndBeforeDispatch=0x%p, ::GetFocus()=0x%p",
-           this, GetBoolName(consumed), i,
-           ToString(mFollowingCharMsgs[i]).get(),
-           ToString(mMsg).get(), mFocusedWndBeforeDispatch, ::GetFocus()));
-      consumed =
-        DispatchKeyPressEventForFollowingCharMessage(mFollowingCharMsgs[i]) ||
-        consumed;
-      if (mWidget->Destroyed() || IsFocusedWindowChanged()) {
-        MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
-          ("%p   NativeKey::HandleKeyDownMessage(), %s event caused "
-           "destroying the widget", this));
-        return true;
-      }
-    }
-    MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
-      ("%p   NativeKey::HandleKeyDownMessage(), handled all following char "
-       "messages, consumed=%s",
-       this, GetBoolName(consumed)));
-    return consumed;
-  }
-
   // If WM_KEYDOWN of VK_PACKET isn't followed by WM_CHAR, we don't need to
   // dispatch keypress events.
   if (mVirtualKeyCode == VK_PACKET) {
     MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
       ("%p   NativeKey::HandleKeyDownMessage(), not dispatching keypress event "
        "because the key is VK_PACKET and there are no char messages",
        this));
     return false;
@@ -2710,26 +2700,20 @@ NativeKey::NeedsToHandleWithoutFollowing
 
   // If following char message is for a control character, it should be handled
   // without WM_CHAR message.  This is typically Ctrl + [a-z].
   if (mFollowingCharMsgs.Length() == 1 &&
       IsControlCharMessage(mFollowingCharMsgs[0])) {
     return true;
   }
 
-  // If inputting two or more characters, should be dispatched after removing
-  // whole following char messages.
-  if (mCommittedCharsAndModifiers.mLength > 1) {
-    return true;
-  }
-
-  // If keydown message is followed by WM_CHAR whose wParam isn't a control
-  // character, we should dispatch keypress event with the char message
-  // even with any modifier state.
-  if (IsFollowedByPrintableCharMessage()) {
+  // If keydown message is followed by WM_CHAR or WM_SYSCHAR whose wParam isn't
+  // a control character, we should dispatch keypress event with the char
+  // message even with any modifier state.
+  if (IsFollowedByPrintableCharOrSysCharMessage()) {
     return false;
   }
 
   // If any modifier keys which may cause printable keys becoming non-printable
   // are not pressed, we don't need special handling for the key.
   if (!mModKeyState.IsControl() && !mModKeyState.IsAlt() &&
       !mModKeyState.IsWin()) {
     return false;
@@ -3199,16 +3183,66 @@ NativeKey::ComputeInputtingStringWithKey
       mInputtingStringAndModifiers.UniCharsCaseInsensitiveEqual(
         mModKeyState.IsShift() ? mShiftedString : mUnshiftedString)) {
     mInputtingStringAndModifiers.Clear();
     mInputtingStringAndModifiers.Append(ch, mModKeyState.GetModifiers());
   }
 }
 
 bool
+NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages() const
+{
+  MOZ_ASSERT(IsKeyDownMessage());
+  MOZ_ASSERT(IsFollowedByPrintableCharOrSysCharMessage());
+
+  nsresult rv = mDispatcher->BeginNativeInputTransaction();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    MOZ_LOG(sNativeKeyLogger, LogLevel::Error,
+      ("%p   NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
+       "FAILED due to BeginNativeInputTransaction() failure", this));
+    return true;
+  }
+  WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget);
+  MOZ_LOG(sNativeKeyLogger, LogLevel::Debug,
+    ("%p   NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
+     "initializing keypress event...", this));
+  ModifierKeyState modKeyState(mModKeyState);
+  if (IsFollowedByPrintableCharMessage()) {
+    // If eKeyPress event should cause inputting text in focused editor,
+    // we need to remove Alt and Ctrl state.
+    modKeyState.Unset(MODIFIER_ALT | MODIFIER_CONTROL);
+  }
+  // We don't need to send char message here if there are two or more retrieved
+  // messages because we need to set each message to each eKeyPress event.
+  bool needsCallback = mFollowingCharMsgs.Length() > 1;
+  nsEventStatus status =
+    InitKeyEvent(keypressEvent, modKeyState,
+                 !needsCallback ? &mFollowingCharMsgs[0] : nullptr);
+  MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
+    ("%p   NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
+     "dispatching keypress event(s)...", this));
+  bool dispatched =
+    mDispatcher->MaybeDispatchKeypressEvents(keypressEvent, status,
+                                             const_cast<NativeKey*>(this),
+                                             needsCallback);
+  if (mWidget->Destroyed()) {
+    MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
+      ("%p   NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
+       "keypress event(s) caused destroying the widget", this));
+    return true;
+  }
+  bool consumed = status == nsEventStatus_eConsumeNoDefault;
+  MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
+    ("%p   NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
+     "dispatched keypress event(s), dispatched=%s, consumed=%s",
+     this, GetBoolName(dispatched), GetBoolName(consumed)));
+  return consumed;
+}
+
+bool
 NativeKey::DispatchKeyPressEventsWithoutCharMessage() const
 {
   MOZ_ASSERT(IsKeyDownMessage());
   MOZ_ASSERT(!mIsDeadKey || !mCommittedCharsAndModifiers.IsEmpty());
 
   nsresult rv = mDispatcher->BeginNativeInputTransaction();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     MOZ_LOG(sNativeKeyLogger, LogLevel::Error,
@@ -3245,16 +3279,56 @@ NativeKey::DispatchKeyPressEventsWithout
      this, GetBoolName(dispatched), GetBoolName(consumed)));
   return consumed;
 }
 
 void
 NativeKey::WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyboardEvent,
                                      uint32_t aIndex)
 {
+  // If it's an eKeyPress event and it's generated from retrieved char message,
+  // we need to set raw message information for plugins.
+  if (aKeyboardEvent.mMessage == eKeyPress &&
+      IsFollowedByPrintableCharOrSysCharMessage()) {
+    MOZ_RELEASE_ASSERT(aIndex < mCommittedCharsAndModifiers.mLength);
+    uint32_t foundPrintableCharMessages = 0;
+    for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
+      if (!IsPrintableCharOrSysCharMessage(mFollowingCharMsgs[i])) {
+        // XXX Should we dispatch a plugin event for WM_*DEADCHAR messages and
+        //     WM_CHAR with a control character here?  But we're not sure
+        //     how can we create such message queue (i.e., WM_CHAR or
+        //     WM_SYSCHAR with a printable character and such message are
+        //     generated by a keydown).  So, let's ignore such case until
+        //     we'd get some bug reports.
+        MOZ_LOG(sNativeKeyLogger, LogLevel::Warning,
+          ("%p   NativeKey::WillDispatchKeyboardEvent(), WARNING, "
+           "ignoring %uth message due to non-printable char message, %s",
+           this, i + 1, ToString(mFollowingCharMsgs[i]).get()));
+        continue;
+      }
+      if (foundPrintableCharMessages++ == aIndex) {
+        // Found message which caused the eKeyPress event.  Let's set the
+        // message for plugin if it's necessary.
+        MaybeInitPluginEventOfKeyEvent(aKeyboardEvent, mFollowingCharMsgs[i]);
+        break;
+      }
+    }
+    // Set modifier state from mCommittedCharsAndModifiers because some of them
+    // might be different.  For example, Shift key was pressed at inputting
+    // dead char but Shift key was released before inputting next character.
+    ModifierKeyState modKeyState(mModKeyState);
+    modKeyState.Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT |
+                      MODIFIER_ALTGRAPH | MODIFIER_CAPSLOCK);
+    modKeyState.Set(mCommittedCharsAndModifiers.mModifiers[aIndex]);
+    modKeyState.InitInputEvent(aKeyboardEvent);
+    MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
+      ("%p   NativeKey::WillDispatchKeyboardEvent(), "
+       "setting %uth modifier state to %s",
+       this, aIndex + 1, ToString(modKeyState).get()));
+  }
   uint32_t longestLength =
     std::max(mInputtingStringAndModifiers.mLength,
              std::max(mShiftedString.mLength, mUnshiftedString.mLength));
   uint32_t skipUniChars = longestLength - mInputtingStringAndModifiers.mLength;
   uint32_t skipShiftedChars = longestLength - mShiftedString.mLength;
   uint32_t skipUnshiftedChars = longestLength - mUnshiftedString.mLength;
   if (aIndex >= longestLength) {
     MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
@@ -3379,87 +3453,16 @@ NativeKey::WillDispatchKeyboardEvent(Wid
         charForOEMKeyCode != mUnshiftedLatinChar &&
         charForOEMKeyCode != mShiftedLatinChar) {
       AlternativeCharCode OEMChars(charForOEMKeyCode, charForOEMKeyCode);
       altArray.AppendElement(OEMChars);
     }
   }
 }
 
-bool
-NativeKey::DispatchKeyPressEventForFollowingCharMessage(
-             const MSG& aCharMsg) const
-{
-  MOZ_ASSERT(IsKeyDownMessage());
-
-  if (mFakeCharMsgs) {
-    if (IsDeadCharMessage(aCharMsg)) {
-      return false;
-    }
-#ifdef DEBUG
-    if (mIsPrintableKey) {
-      nsPrintfCString log(
-        "mOriginalVirtualKeyCode=0x%02X, mCommittedCharsAndModifiers={ "
-        "mChars=[ 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X ], mLength=%d }, "
-        "wParam=0x%04X",
-        mOriginalVirtualKeyCode, mCommittedCharsAndModifiers.mChars[0],
-        mCommittedCharsAndModifiers.mChars[1],
-        mCommittedCharsAndModifiers.mChars[2],
-        mCommittedCharsAndModifiers.mChars[3],
-        mCommittedCharsAndModifiers.mChars[4],
-        mCommittedCharsAndModifiers.mLength, aCharMsg.wParam);
-      if (mCommittedCharsAndModifiers.IsEmpty()) {
-        log.Insert("length is zero: ", 0);
-        NS_ERROR(log.get());
-        NS_ABORT();
-      } else if (mCommittedCharsAndModifiers.mChars[0] != aCharMsg.wParam) {
-        log.Insert("character mismatch: ", 0);
-        NS_ERROR(log.get());
-        NS_ABORT();
-      }
-    }
-#endif // #ifdef DEBUG
-    return HandleCharMessage(aCharMsg);
-  }
-
-  if (IsDeadCharMessage(aCharMsg)) {
-    if (!mWidget->PluginHasFocus()) {
-      MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
-        ("%p   NativeKey::DispatchKeyPressEventForFollowingCharMessage(), "
-         "plugin doesn't have focus", this));
-      return false;
-    }
-    MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
-      ("%p   NativeKey::DispatchKeyPressEventForFollowingCharMessage(), "
-       "dispatching plugin event...", this));
-    bool ok = mWidget->DispatchPluginEvent(aCharMsg) || mWidget->Destroyed();
-    MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
-      ("%p   NativeKey::DispatchKeyPressEventForFollowingCharMessage(), "
-       "dispatched plugin event, result=%s, mWidget->Destroyed()=%s",
-       this, GetBoolName(ok), GetBoolName(mWidget->Destroyed())));
-  }
-
-  bool defaultPrevented = HandleCharMessage(aCharMsg);
-  // If a syschar keypress wasn't processed, Windows may want to
-  // handle it to activate a native menu.
-  if (!defaultPrevented && IsSysCharMessage(aCharMsg)) {
-    MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
-      ("%p   NativeKey::DispatchKeyPressEventForFollowingCharMessage(), "
-       "calling DefWindowProcW(aCharMsg=%s)...",
-       this, ToString(aCharMsg).get()));
-    ::DefWindowProcW(aCharMsg.hwnd, aCharMsg.message,
-                     aCharMsg.wParam, aCharMsg.lParam);
-    MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
-      ("%p   NativeKey::DispatchKeyPressEventForFollowingCharMessage(), "
-       "called DefWindowProcW(aCharMsg=%s)",
-       this, ToString(aCharMsg).get()));
-  }
-  return defaultPrevented;
-}
-
 /*****************************************************************************
  * mozilla::widget::KeyboardLayout
  *****************************************************************************/
 
 KeyboardLayout* KeyboardLayout::sInstance = nullptr;
 nsIIdleServiceInternal* KeyboardLayout::sIdleService = nullptr;
 
 // This log is very noisy if you don't want to retrieve the mapping table
--- a/widget/windows/KeyboardLayout.h
+++ b/widget/windows/KeyboardLayout.h
@@ -546,44 +546,50 @@ private:
    */
   nsEventStatus InitKeyEvent(WidgetKeyboardEvent& aKeyEvent,
                              const ModifierKeyState& aModKeyState,
                              const MSG* aMsgSentToPlugin = nullptr) const;
   nsEventStatus InitKeyEvent(WidgetKeyboardEvent& aKeyEvent,
                              const MSG* aMsgSentToPlugin = nullptr) const;
 
   /**
+   * MaybeInitPluginEventOfKeyEvent() may initialize aKeyEvent::mPluginEvent
+   * with aMsgSentToPlugin if it's necessary.
+   */
+  void MaybeInitPluginEventOfKeyEvent(WidgetKeyboardEvent& aKeyEvent,
+                                      const MSG& aMsgSentToPlugin) const;
+
+  /**
    * Dispatches a command event for aEventCommand.
    * Returns true if the event is consumed.  Otherwise, false.
    */
   bool DispatchCommandEvent(uint32_t aEventCommand) const;
 
   /**
+   * DispatchKeyPressEventsWithRetrievedCharMessages() dispatches keypress
+   * event(s) with retrieved char messages.
+   */
+  bool DispatchKeyPressEventsWithRetrievedCharMessages() const;
+
+  /**
    * DispatchKeyPressEventsWithoutCharMessage() dispatches keypress event(s)
    * without char messages.  So, this should be used only when there are no
    * following char messages.
    */
   bool DispatchKeyPressEventsWithoutCharMessage() const;
 
   /**
    * MaybeDispatchPluginEventsForRemovedCharMessages() dispatches plugin events
    * for removed char messages when a windowless plugin has focus.
    * Returns true if the widget is destroyed or blurred during dispatching a
    * plugin event.
    */
   bool MaybeDispatchPluginEventsForRemovedCharMessages() const;
 
   /**
-   * DispatchKeyPressEventForFollowingCharMessage() dispatches keypress event
-   * for following WM_*CHAR message which is removed and set to aCharMsg.
-   * Returns true if the event is consumed.  Otherwise, false.
-   */
-  bool DispatchKeyPressEventForFollowingCharMessage(const MSG& aCharMsg) 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.
    */
   bool NeedsToHandleWithoutFollowingCharMessages() const;