Bug 1346499 Don't remove Ctrl nor Alt modifier state at dispatching eKeyPress event when the modifier doesn't change inputting character r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 14 Mar 2017 00:32:50 +0900
changeset 497669 f682142b5de70a0b8fc171a6e925bce5c86a8f4d
parent 497206 f9362554866b327700c7f9b18050d7b7eb3d2b23
child 548933 e64fb16bd5853ded983d1f72ada39a48f9406925
push id48948
push usermasayuki@d-toybox.com
push dateMon, 13 Mar 2017 15:49:26 +0000
reviewersm_kato
bugs1346499
milestone55.0a1
Bug 1346499 Don't remove Ctrl nor Alt modifier state at dispatching eKeyPress event when the modifier doesn't change inputting character r?m_kato Ctrl+Space causes WM_CHAR of ' '. On the other native applications, you can input ' ' with this key combination though, we shouldn't allow this because we need to remove Ctrl and Alt modifier state at dispatching keypress event for the limitation of TextEditor but this is important key combination for custom shortcut keys. So, when Ctrl or Alt key is pressed but it doesn't change the inputting character, i.e., the character can be inputted without Ctrl or Alt, we shouldn't remove those modifier state from eKeyPress event. MozReview-Commit-ID: 7omLvNdQWzW
widget/tests/test_keycodes.xul
widget/windows/KeyboardLayout.cpp
widget/windows/KeyboardLayout.h
--- a/widget/tests/test_keycodes.xul
+++ b/widget/tests/test_keycodes.xul
@@ -2934,16 +2934,20 @@ function* runKeyEventTests()
                    modifiers:{}, chars:""},
                   "Accept", "", nsIDOMKeyEvent.DOM_VK_ACCEPT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_MODECHANGE,
                    modifiers:{}, chars:""},
                   "ModeChange", "", nsIDOMKeyEvent.DOM_VK_MODECHANGE, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_SPACE,
                    modifiers:{}, chars:" "},
                   " ", "Space", nsIDOMKeyEvent.DOM_VK_SPACE, " ", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
+    // Ctrl+Space causes WM_CHAR with ' '.  However, its keypress event's ctrlKey state shouldn't be false.
+    yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_SPACE,
+                   modifiers:{ctrlKey:1}, chars:" "},
+                  " ", "Space", nsIDOMKeyEvent.DOM_VK_SPACE, " ", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_SELECT,
                    modifiers:{}, chars:""},
                   "Select", "", nsIDOMKeyEvent.DOM_VK_SELECT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_PRINT,
                    modifiers:{}, chars:""},
                   "Unidentified", "", nsIDOMKeyEvent.DOM_VK_PRINT, "", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:WIN_VK_EXECUTE,
                    modifiers:{}, chars:""},
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -1218,16 +1218,17 @@ NativeKey::NativeKey(nsWindowBase* aWidg
   , mVirtualKeyCode(0)
   , mOriginalVirtualKeyCode(0)
   , mShiftedLatinChar(0)
   , mUnshiftedLatinChar(0)
   , mScanCode(0)
   , mIsExtended(false)
   , mIsDeadKey(false)
   , mCharMessageHasGone(false)
+  , mCanIgnoreModifierStateAtKeyPress(true)
   , mFakeCharMsgs(aFakeCharMsgs && aFakeCharMsgs->Length() ?
                     aFakeCharMsgs : nullptr)
 {
   MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
     ("%p NativeKey::NativeKey(aWidget=0x%p { GetWindowHandle()=0x%p }, "
      "aMessage=%s, aModKeyState=%s), sLatestInstance=0x%p",
      this, aWidget, aWidget->GetWindowHandle(), ToString(aMessage).get(),
      ToString(aModKeyState).get(), sLatestInstance));
@@ -3374,17 +3375,17 @@ NativeKey::DispatchKeyPressEventsWithRet
        "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 (mCanIgnoreModifierStateAtKeyPress && 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 =
@@ -3484,25 +3485,27 @@ NativeKey::WillDispatchKeyboardEvent(Wid
         // 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.ModifiersAt(aIndex));
-    modKeyState.InitInputEvent(aKeyboardEvent);
-    MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
-      ("%p   NativeKey::WillDispatchKeyboardEvent(), "
-       "setting %uth modifier state to %s",
-       this, aIndex + 1, ToString(modKeyState).get()));
+    if (mCanIgnoreModifierStateAtKeyPress) {
+      ModifierKeyState modKeyState(mModKeyState);
+      modKeyState.Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT |
+                        MODIFIER_ALTGRAPH | MODIFIER_CAPSLOCK);
+      modKeyState.Set(mCommittedCharsAndModifiers.ModifiersAt(aIndex));
+      modKeyState.InitInputEvent(aKeyboardEvent);
+      MOZ_LOG(sNativeKeyLogger, LogLevel::Info,
+        ("%p   NativeKey::WillDispatchKeyboardEvent(), "
+         "setting %uth modifier state to %s",
+         this, aIndex + 1, ToString(modKeyState).get()));
+    }
   }
   size_t longestLength =
     std::max(mInputtingStringAndModifiers.Length(),
              std::max(mShiftedString.Length(), mUnshiftedString.Length()));
   size_t skipUniChars = longestLength - mInputtingStringAndModifiers.Length();
   size_t skipShiftedChars = longestLength - mShiftedString.Length();
   size_t skipUnshiftedChars = longestLength - mUnshiftedString.Length();
   if (aIndex >= longestLength) {
@@ -3789,16 +3792,35 @@ KeyboardLayout::InitNativeKey(NativeKey&
   if (aNativeKey.IsFollowedByPrintableCharOrSysCharMessage() ||
       aNativeKey.mCharMessageHasGone) {
     MOZ_ASSERT(!aNativeKey.IsCharMessage(aNativeKey.mMsg));
     if (aNativeKey.IsFollowedByPrintableCharOrSysCharMessage()) {
       // Initialize mCommittedCharsAndModifiers with following char messages.
       aNativeKey.
         InitCommittedCharsAndModifiersWithFollowingCharMessages(aModKeyState);
       MOZ_ASSERT(!aNativeKey.mCommittedCharsAndModifiers.IsEmpty());
+
+      // Currently, we are doing a ugly hack to keypress events to cause
+      // inputting character even if Ctrl or Alt key is pressed, that is, we
+      // remove Ctrl and Alt modifier state from keypress event.  However, for
+      // example, Ctrl+Space which causes ' ' of WM_CHAR message never causes
+      // keypress event whose ctrlKey is true.  For preventing this problem,
+      // we should mark as not removable if Ctrl or Alt key does not cause
+      // changing inputting character.
+      if (IsPrintableCharKey(aNativeKey.mOriginalVirtualKeyCode) &&
+          !aModKeyState.IsAltGr() &&
+          (aModKeyState.IsControl() || aModKeyState.IsAlt())) {
+        ModifierKeyState state = aModKeyState;
+        state.Unset(MODIFIER_ALT | MODIFIER_CONTROL);
+        UniCharsAndModifiers charsWithoutModifier =
+          GetUniCharsAndModifiers(aNativeKey.mOriginalVirtualKeyCode, state);
+        aNativeKey.mCanIgnoreModifierStateAtKeyPress =
+          !charsWithoutModifier.UniCharsEqual(
+                                  aNativeKey.mCommittedCharsAndModifiers);
+      }
     } else {
       aNativeKey.mCommittedCharsAndModifiers.Clear();
     }
     aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
 
     // If it's not in dead key sequence, we don't need to do anymore here.
     if (!IsInDeadKeySequence()) {
       return;
--- a/widget/windows/KeyboardLayout.h
+++ b/widget/windows/KeyboardLayout.h
@@ -385,16 +385,19 @@ private:
   // mCharMessageHasGone is true if the message is a keydown message and
   // it's followed by at least one char message but it's gone at removing
   // from the queue.  This could occur if PeekMessage() or something is
   // hooked by odd tool.
   bool    mCharMessageHasGone;
   // mIsOverridingKeyboardLayout is true if the instance temporarily overriding
   // keyboard layout with specified by the constructor.
   bool    mIsOverridingKeyboardLayout;
+  // mCanIgnoreModifierStateAtKeyPress is true if it's allowed to remove
+  // Ctrl or Alt modifier state at dispatching eKeyPress.
+  bool    mCanIgnoreModifierStateAtKeyPress;
 
   nsTArray<FakeCharMsg>* mFakeCharMsgs;
 
   // When a keydown event is dispatched at handling WM_APPCOMMAND, the computed
   // virtual keycode is set to this.  Even if we consume WM_APPCOMMAND message,
   // Windows may send WM_KEYDOWN and WM_KEYUP message for them.
   // At that time, we should not dispatch key events for them.
   static uint8_t sDispatchedKeyOfAppCommand;