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
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 06 Oct 2016 20:52:03 +0900
changeset 362544 4e3211aa00ab01a1d81e6af54939d6adbaee5d5f
parent 362543 259115245f8516c724fe2db5b7fb2a4e5c144572
child 362545 55817ab6a405b5c2e5999331797956ee2d9c6a0d
push id1369
push userjlorenzo@mozilla.com
push dateMon, 27 Feb 2017 14:59:41 +0000
treeherdermozilla-release@d75a1dba431f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1303273
milestone52.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 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;