1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #define FORCE_PR_LOG /* Allow logging in the release build */
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/MouseEvents.h"
14 #include "mozilla/TextEvents.h"
15 #include "mozilla/WindowsVersion.h"
17 #include "KeyboardLayout.h"
18 #include "nsIMM32Handler.h"
21 #include "nsToolkit.h"
22 #include "nsQuickSort.h"
23 #include "nsAlgorithm.h"
24 #include "nsUnicharUtils.h"
25 #include "WidgetUtils.h"
27 #include "nsWindowDbg.h"
28 #include "nsServiceManagerUtils.h"
29 #include "nsPrintfCString.h"
31 #include "nsIDOMKeyEvent.h"
32 #include "nsIIdleServiceInternal.h"
34 #ifdef MOZ_CRASHREPORTER
35 #include "nsExceptionHandler.h"
48 // In WinUser.h, MAPVK_VK_TO_VSC_EX is defined only when WINVER >= 0x0600
49 #ifndef MAPVK_VK_TO_VSC_EX
50 #define MAPVK_VK_TO_VSC_EX (4)
57 static const char* kVirtualKeyName
[] = {
58 "NULL", "VK_LBUTTON", "VK_RBUTTON", "VK_CANCEL",
59 "VK_MBUTTON", "VK_XBUTTON1", "VK_XBUTTON2", "0x07",
60 "VK_BACK", "VK_TAB", "0x0A", "0x0B",
61 "VK_CLEAR", "VK_RETURN", "0x0E", "0x0F",
63 "VK_SHIFT", "VK_CONTROL", "VK_MENU", "VK_PAUSE",
64 "VK_CAPITAL", "VK_KANA, VK_HANGUL", "0x16", "VK_JUNJA",
65 "VK_FINAL", "VK_HANJA, VK_KANJI", "0x1A", "VK_ESCAPE",
66 "VK_CONVERT", "VK_NONCONVERT", "VK_ACCEPT", "VK_MODECHANGE",
68 "VK_SPACE", "VK_PRIOR", "VK_NEXT", "VK_END",
69 "VK_HOME", "VK_LEFT", "VK_UP", "VK_RIGHT",
70 "VK_DOWN", "VK_SELECT", "VK_PRINT", "VK_EXECUTE",
71 "VK_SNAPSHOT", "VK_INSERT", "VK_DELETE", "VK_HELP",
73 "VK_0", "VK_1", "VK_2", "VK_3",
74 "VK_4", "VK_5", "VK_6", "VK_7",
75 "VK_8", "VK_9", "0x3A", "0x3B",
76 "0x3C", "0x3D", "0x3E", "0x3F",
78 "0x40", "VK_A", "VK_B", "VK_C",
79 "VK_D", "VK_E", "VK_F", "VK_G",
80 "VK_H", "VK_I", "VK_J", "VK_K",
81 "VK_L", "VK_M", "VK_N", "VK_O",
83 "VK_P", "VK_Q", "VK_R", "VK_S",
84 "VK_T", "VK_U", "VK_V", "VK_W",
85 "VK_X", "VK_Y", "VK_Z", "VK_LWIN",
86 "VK_RWIN", "VK_APPS", "0x5E", "VK_SLEEP",
88 "VK_NUMPAD0", "VK_NUMPAD1", "VK_NUMPAD2", "VK_NUMPAD3",
89 "VK_NUMPAD4", "VK_NUMPAD5", "VK_NUMPAD6", "VK_NUMPAD7",
90 "VK_NUMPAD8", "VK_NUMPAD9", "VK_MULTIPLY", "VK_ADD",
91 "VK_SEPARATOR", "VK_SUBTRACT", "VK_DECIMAL", "VK_DIVIDE",
93 "VK_F1", "VK_F2", "VK_F3", "VK_F4",
94 "VK_F5", "VK_F6", "VK_F7", "VK_F8",
95 "VK_F9", "VK_F10", "VK_F11", "VK_F12",
96 "VK_F13", "VK_F14", "VK_F15", "VK_F16",
98 "VK_F17", "VK_F18", "VK_F19", "VK_F20",
99 "VK_F21", "VK_F22", "VK_F23", "VK_F24",
100 "0x88", "0x89", "0x8A", "0x8B",
101 "0x8C", "0x8D", "0x8E", "0x8F",
103 "VK_NUMLOCK", "VK_SCROLL", "VK_OEM_NEC_EQUAL, VK_OEM_FJ_JISHO",
105 "VK_OEM_FJ_TOUROKU", "VK_OEM_FJ_LOYA", "VK_OEM_FJ_ROYA", "0x97",
106 "0x98", "0x99", "0x9A", "0x9B",
107 "0x9C", "0x9D", "0x9E", "0x9F",
109 "VK_LSHIFT", "VK_RSHIFT", "VK_LCONTROL", "VK_RCONTROL",
110 "VK_LMENU", "VK_RMENU", "VK_BROWSER_BACK", "VK_BROWSER_FORWARD",
111 "VK_BROWSER_REFRESH", "VK_BROWSER_STOP", "VK_BROWSER_SEARCH",
112 "VK_BROWSER_FAVORITES",
113 "VK_BROWSER_HOME", "VK_VOLUME_MUTE", "VK_VOLUME_DOWN", "VK_VOLUME_UP",
115 "VK_MEDIA_NEXT_TRACK", "VK_MEDIA_PREV_TRACK", "VK_MEDIA_STOP",
116 "VK_MEDIA_PLAY_PAUSE",
117 "VK_LAUNCH_MAIL", "VK_LAUNCH_MEDIA_SELECT", "VK_LAUNCH_APP1",
119 "0xB8", "0xB9", "VK_OEM_1", "VK_OEM_PLUS",
120 "VK_OEM_COMMA", "VK_OEM_MINUS", "VK_OEM_PERIOD", "VK_OEM_2",
122 "VK_OEM_3", "VK_ABNT_C1", "VK_ABNT_C2", "0xC3",
123 "0xC4", "0xC5", "0xC6", "0xC7",
124 "0xC8", "0xC9", "0xCA", "0xCB",
125 "0xCC", "0xCD", "0xCE", "0xCF",
127 "0xD0", "0xD1", "0xD2", "0xD3",
128 "0xD4", "0xD5", "0xD6", "0xD7",
129 "0xD8", "0xD9", "0xDA", "VK_OEM_4",
130 "VK_OEM_5", "VK_OEM_6", "VK_OEM_7", "VK_OEM_8",
132 "0xE0", "VK_OEM_AX", "VK_OEM_102", "VK_ICO_HELP",
133 "VK_ICO_00", "VK_PROCESSKEY", "VK_ICO_CLEAR", "VK_PACKET",
134 "0xE8", "VK_OEM_RESET", "VK_OEM_JUMP", "VK_OEM_PA1",
135 "VK_OEM_PA2", "VK_OEM_PA3", "VK_OEM_WSCTRL", "VK_OEM_CUSEL",
137 "VK_OEM_ATTN", "VK_OEM_FINISH", "VK_OEM_COPY", "VK_OEM_AUTO",
138 "VK_OEM_ENLW", "VK_OEM_BACKTAB", "VK_ATTN", "VK_CRSEL",
139 "VK_EXSEL", "VK_EREOF", "VK_PLAY", "VK_ZOOM",
140 "VK_NONAME", "VK_PA1", "VK_OEM_CLEAR", "0xFF"
143 static_assert(sizeof(kVirtualKeyName
) / sizeof(const char*) == 0x100,
144 "The virtual key name must be defined just 256 keys");
146 #endif // #ifdef PR_LOGGING
148 // Unique id counter associated with a keydown / keypress events. Used in
149 // identifing keypress events for removal from async event dispatch queue
150 // in metrofx after preventDefault is called on keydown events.
151 static uint32_t sUniqueKeyEventId
= 0;
156 char16_t CompositeChar
;
162 friend class KeyboardLayout
;
165 // KeyboardLayout::AddDeadKeyTable() will allocate as many entries as
166 // required. It is the only way to create new DeadKeyTable instances.
167 DeadKeyEntry mTable
[1];
169 void Init(const DeadKeyEntry
* aDeadKeyArray
, uint32_t aEntries
)
172 memcpy(mTable
, aDeadKeyArray
, aEntries
* sizeof(DeadKeyEntry
));
175 static uint32_t SizeInBytes(uint32_t aEntries
)
177 return offsetof(DeadKeyTable
, mTable
) + aEntries
* sizeof(DeadKeyEntry
);
181 uint32_t Entries() const
186 bool IsEqual(const DeadKeyEntry
* aDeadKeyArray
, uint32_t aEntries
) const
188 return (mEntries
== aEntries
&&
189 !memcmp(mTable
, aDeadKeyArray
,
190 aEntries
* sizeof(DeadKeyEntry
)));
193 char16_t
GetCompositeChar(char16_t aBaseChar
) const;
197 /*****************************************************************************
198 * mozilla::widget::ModifierKeyState
199 *****************************************************************************/
201 ModifierKeyState::ModifierKeyState()
206 ModifierKeyState::ModifierKeyState(bool aIsShiftDown
,
211 Unset(MODIFIER_SHIFT
| MODIFIER_CONTROL
| MODIFIER_ALT
| MODIFIER_ALTGRAPH
);
212 Modifiers modifiers
= 0;
214 modifiers
|= MODIFIER_SHIFT
;
216 if (aIsControlDown
) {
217 modifiers
|= MODIFIER_CONTROL
;
220 modifiers
|= MODIFIER_ALT
;
227 ModifierKeyState::ModifierKeyState(Modifiers aModifiers
) :
228 mModifiers(aModifiers
)
234 ModifierKeyState::Update()
237 if (IS_VK_DOWN(VK_SHIFT
)) {
238 mModifiers
|= MODIFIER_SHIFT
;
240 if (IS_VK_DOWN(VK_CONTROL
)) {
241 mModifiers
|= MODIFIER_CONTROL
;
243 if (IS_VK_DOWN(VK_MENU
)) {
244 mModifiers
|= MODIFIER_ALT
;
246 if (IS_VK_DOWN(VK_LWIN
) || IS_VK_DOWN(VK_RWIN
)) {
247 mModifiers
|= MODIFIER_OS
;
249 if (::GetKeyState(VK_CAPITAL
) & 1) {
250 mModifiers
|= MODIFIER_CAPSLOCK
;
252 if (::GetKeyState(VK_NUMLOCK
) & 1) {
253 mModifiers
|= MODIFIER_NUMLOCK
;
255 if (::GetKeyState(VK_SCROLL
) & 1) {
256 mModifiers
|= MODIFIER_SCROLLLOCK
;
263 ModifierKeyState::Unset(Modifiers aRemovingModifiers
)
265 mModifiers
&= ~aRemovingModifiers
;
266 // Note that we don't need to unset AltGr flag here automatically.
267 // For nsEditor, we need to remove Alt and Control flags but AltGr isn't
268 // checked in nsEditor, so, it can be kept.
272 ModifierKeyState::Set(Modifiers aAddingModifiers
)
274 mModifiers
|= aAddingModifiers
;
279 ModifierKeyState::InitInputEvent(WidgetInputEvent
& aInputEvent
) const
281 aInputEvent
.modifiers
= mModifiers
;
283 switch(aInputEvent
.mClass
) {
284 case eMouseEventClass
:
285 case eMouseScrollEventClass
:
286 case eWheelEventClass
:
287 case eDragEventClass
:
288 case eSimpleGestureEventClass
:
289 InitMouseEvent(aInputEvent
);
297 ModifierKeyState::InitMouseEvent(WidgetInputEvent
& aMouseEvent
) const
299 NS_ASSERTION(aMouseEvent
.mClass
== eMouseEventClass
||
300 aMouseEvent
.mClass
== eWheelEventClass
||
301 aMouseEvent
.mClass
== eDragEventClass
||
302 aMouseEvent
.mClass
== eSimpleGestureEventClass
,
303 "called with non-mouse event");
305 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro
) {
306 // Buttons for immersive mode are handled in MetroInput.
310 WidgetMouseEventBase
& mouseEvent
= *aMouseEvent
.AsMouseEventBase();
311 mouseEvent
.buttons
= 0;
312 if (::GetKeyState(VK_LBUTTON
) < 0) {
313 mouseEvent
.buttons
|= WidgetMouseEvent::eLeftButtonFlag
;
315 if (::GetKeyState(VK_RBUTTON
) < 0) {
316 mouseEvent
.buttons
|= WidgetMouseEvent::eRightButtonFlag
;
318 if (::GetKeyState(VK_MBUTTON
) < 0) {
319 mouseEvent
.buttons
|= WidgetMouseEvent::eMiddleButtonFlag
;
321 if (::GetKeyState(VK_XBUTTON1
) < 0) {
322 mouseEvent
.buttons
|= WidgetMouseEvent::e4thButtonFlag
;
324 if (::GetKeyState(VK_XBUTTON2
) < 0) {
325 mouseEvent
.buttons
|= WidgetMouseEvent::e5thButtonFlag
;
330 ModifierKeyState::IsShift() const
332 return (mModifiers
& MODIFIER_SHIFT
) != 0;
336 ModifierKeyState::IsControl() const
338 return (mModifiers
& MODIFIER_CONTROL
) != 0;
342 ModifierKeyState::IsAlt() const
344 return (mModifiers
& MODIFIER_ALT
) != 0;
348 ModifierKeyState::IsAltGr() const
350 return IsControl() && IsAlt();
354 ModifierKeyState::IsWin() const
356 return (mModifiers
& MODIFIER_OS
) != 0;
360 ModifierKeyState::IsCapsLocked() const
362 return (mModifiers
& MODIFIER_CAPSLOCK
) != 0;
366 ModifierKeyState::IsNumLocked() const
368 return (mModifiers
& MODIFIER_NUMLOCK
) != 0;
372 ModifierKeyState::IsScrollLocked() const
374 return (mModifiers
& MODIFIER_SCROLLLOCK
) != 0;
378 ModifierKeyState::GetModifiers() const
384 ModifierKeyState::EnsureAltGr()
386 // If both Control key and Alt key are pressed, it means AltGr is pressed.
387 // Ideally, we should check whether the current keyboard layout has AltGr
388 // or not. However, setting AltGr flags for keyboard which doesn't have
389 // AltGr must not be serious bug. So, it should be OK for now.
391 mModifiers
|= MODIFIER_ALTGRAPH
;
395 /*****************************************************************************
396 * mozilla::widget::UniCharsAndModifiers
397 *****************************************************************************/
400 UniCharsAndModifiers::Append(char16_t aUniChar
, Modifiers aModifiers
)
402 MOZ_ASSERT(mLength
< 5);
403 mChars
[mLength
] = aUniChar
;
404 mModifiers
[mLength
] = aModifiers
;
409 UniCharsAndModifiers::FillModifiers(Modifiers aModifiers
)
411 for (uint32_t i
= 0; i
< mLength
; i
++) {
412 mModifiers
[i
] = aModifiers
;
417 UniCharsAndModifiers::UniCharsEqual(const UniCharsAndModifiers
& aOther
) const
419 if (mLength
!= aOther
.mLength
) {
422 return !memcmp(mChars
, aOther
.mChars
, mLength
* sizeof(char16_t
));
426 UniCharsAndModifiers::UniCharsCaseInsensitiveEqual(
427 const UniCharsAndModifiers
& aOther
) const
429 if (mLength
!= aOther
.mLength
) {
433 nsCaseInsensitiveStringComparator comp
;
434 return !comp(mChars
, aOther
.mChars
, mLength
, aOther
.mLength
);
437 UniCharsAndModifiers
&
438 UniCharsAndModifiers::operator+=(const UniCharsAndModifiers
& aOther
)
440 uint32_t copyCount
= std::min(aOther
.mLength
, 5 - mLength
);
441 NS_ENSURE_TRUE(copyCount
> 0, *this);
442 memcpy(&mChars
[mLength
], aOther
.mChars
, copyCount
* sizeof(char16_t
));
443 memcpy(&mModifiers
[mLength
], aOther
.mModifiers
,
444 copyCount
* sizeof(Modifiers
));
445 mLength
+= copyCount
;
450 UniCharsAndModifiers::operator+(const UniCharsAndModifiers
& aOther
) const
452 UniCharsAndModifiers
result(*this);
457 /*****************************************************************************
458 * mozilla::widget::VirtualKey
459 *****************************************************************************/
462 VirtualKey::ShiftState
463 VirtualKey::ModifiersToShiftState(Modifiers aModifiers
)
465 ShiftState state
= 0;
466 if (aModifiers
& MODIFIER_SHIFT
) {
467 state
|= STATE_SHIFT
;
469 if (aModifiers
& MODIFIER_CONTROL
) {
470 state
|= STATE_CONTROL
;
472 if (aModifiers
& MODIFIER_ALT
) {
475 if (aModifiers
& MODIFIER_CAPSLOCK
) {
476 state
|= STATE_CAPSLOCK
;
483 VirtualKey::ShiftStateToModifiers(ShiftState aShiftState
)
485 Modifiers modifiers
= 0;
486 if (aShiftState
& STATE_SHIFT
) {
487 modifiers
|= MODIFIER_SHIFT
;
489 if (aShiftState
& STATE_CONTROL
) {
490 modifiers
|= MODIFIER_CONTROL
;
492 if (aShiftState
& STATE_ALT
) {
493 modifiers
|= MODIFIER_ALT
;
495 if (aShiftState
& STATE_CAPSLOCK
) {
496 modifiers
|= MODIFIER_CAPSLOCK
;
498 if ((modifiers
& (MODIFIER_ALT
| MODIFIER_CONTROL
)) ==
499 (MODIFIER_ALT
| MODIFIER_CONTROL
)) {
500 modifiers
|= MODIFIER_ALTGRAPH
;
506 VirtualKey::GetCompositeChar(ShiftState aShiftState
, char16_t aBaseChar
) const
508 return mShiftStates
[aShiftState
].DeadKey
.Table
->GetCompositeChar(aBaseChar
);
512 VirtualKey::MatchingDeadKeyTable(const DeadKeyEntry
* aDeadKeyArray
,
513 uint32_t aEntries
) const
519 for (ShiftState shiftState
= 0; shiftState
< 16; shiftState
++) {
520 if (!IsDeadKey(shiftState
)) {
523 const DeadKeyTable
* dkt
= mShiftStates
[shiftState
].DeadKey
.Table
;
524 if (dkt
&& dkt
->IsEqual(aDeadKeyArray
, aEntries
)) {
533 VirtualKey::SetNormalChars(ShiftState aShiftState
,
534 const char16_t
* aChars
,
535 uint32_t aNumOfChars
)
537 NS_ASSERTION(aShiftState
< ArrayLength(mShiftStates
), "invalid index");
539 SetDeadKey(aShiftState
, false);
541 for (uint32_t index
= 0; index
< aNumOfChars
; index
++) {
542 // Ignore legacy non-printable control characters
543 mShiftStates
[aShiftState
].Normal
.Chars
[index
] =
544 (aChars
[index
] >= 0x20) ? aChars
[index
] : 0;
547 uint32_t len
= ArrayLength(mShiftStates
[aShiftState
].Normal
.Chars
);
548 for (uint32_t index
= aNumOfChars
; index
< len
; index
++) {
549 mShiftStates
[aShiftState
].Normal
.Chars
[index
] = 0;
554 VirtualKey::SetDeadChar(ShiftState aShiftState
, char16_t aDeadChar
)
556 NS_ASSERTION(aShiftState
< ArrayLength(mShiftStates
), "invalid index");
558 SetDeadKey(aShiftState
, true);
560 mShiftStates
[aShiftState
].DeadKey
.DeadChar
= aDeadChar
;
561 mShiftStates
[aShiftState
].DeadKey
.Table
= nullptr;
565 VirtualKey::GetUniChars(ShiftState aShiftState
) const
567 UniCharsAndModifiers result
= GetNativeUniChars(aShiftState
);
569 const ShiftState STATE_ALT_CONTROL
= (STATE_ALT
| STATE_CONTROL
);
570 if (!(aShiftState
& STATE_ALT_CONTROL
)) {
574 if (!result
.mLength
) {
575 result
= GetNativeUniChars(aShiftState
& ~STATE_ALT_CONTROL
);
576 result
.FillModifiers(ShiftStateToModifiers(aShiftState
));
580 if ((aShiftState
& STATE_ALT_CONTROL
) == STATE_ALT_CONTROL
) {
581 // Even if the shifted chars and the unshifted chars are same, we
582 // should consume the Alt key state and the Ctrl key state when
583 // AltGr key is pressed. Because if we don't consume them, the input
584 // events are ignored on nsEditor. (I.e., Users cannot input the
585 // characters with this key combination.)
586 Modifiers finalModifiers
= ShiftStateToModifiers(aShiftState
);
587 finalModifiers
&= ~(MODIFIER_ALT
| MODIFIER_CONTROL
);
588 result
.FillModifiers(finalModifiers
);
592 UniCharsAndModifiers unmodifiedReslt
=
593 GetNativeUniChars(aShiftState
& ~STATE_ALT_CONTROL
);
594 if (!result
.UniCharsEqual(unmodifiedReslt
)) {
595 // Otherwise, we should consume the Alt key state and the Ctrl key state
596 // only when the shifted chars and unshifted chars are different.
597 Modifiers finalModifiers
= ShiftStateToModifiers(aShiftState
);
598 finalModifiers
&= ~(MODIFIER_ALT
| MODIFIER_CONTROL
);
599 result
.FillModifiers(finalModifiers
);
606 VirtualKey::GetNativeUniChars(ShiftState aShiftState
) const
609 if (aShiftState
< 0 || aShiftState
>= ArrayLength(mShiftStates
)) {
610 nsPrintfCString
warning("Shift state is out of range: "
611 "aShiftState=%d, ArrayLength(mShiftState)=%d",
612 aShiftState
, ArrayLength(mShiftStates
));
613 NS_WARNING(warning
.get());
617 UniCharsAndModifiers result
;
618 Modifiers modifiers
= ShiftStateToModifiers(aShiftState
);
619 if (IsDeadKey(aShiftState
)) {
620 result
.Append(mShiftStates
[aShiftState
].DeadKey
.DeadChar
, modifiers
);
625 uint32_t len
= ArrayLength(mShiftStates
[aShiftState
].Normal
.Chars
);
627 index
< len
&& mShiftStates
[aShiftState
].Normal
.Chars
[index
]; index
++) {
628 result
.Append(mShiftStates
[aShiftState
].Normal
.Chars
[index
], modifiers
);
635 VirtualKey::FillKbdState(PBYTE aKbdState
,
636 const ShiftState aShiftState
)
638 NS_ASSERTION(aShiftState
< 16, "aShiftState out of range");
640 if (aShiftState
& STATE_SHIFT
) {
641 aKbdState
[VK_SHIFT
] |= 0x80;
643 aKbdState
[VK_SHIFT
] &= ~0x80;
644 aKbdState
[VK_LSHIFT
] &= ~0x80;
645 aKbdState
[VK_RSHIFT
] &= ~0x80;
648 if (aShiftState
& STATE_CONTROL
) {
649 aKbdState
[VK_CONTROL
] |= 0x80;
651 aKbdState
[VK_CONTROL
] &= ~0x80;
652 aKbdState
[VK_LCONTROL
] &= ~0x80;
653 aKbdState
[VK_RCONTROL
] &= ~0x80;
656 if (aShiftState
& STATE_ALT
) {
657 aKbdState
[VK_MENU
] |= 0x80;
659 aKbdState
[VK_MENU
] &= ~0x80;
660 aKbdState
[VK_LMENU
] &= ~0x80;
661 aKbdState
[VK_RMENU
] &= ~0x80;
664 if (aShiftState
& STATE_CAPSLOCK
) {
665 aKbdState
[VK_CAPITAL
] |= 0x01;
667 aKbdState
[VK_CAPITAL
] &= ~0x01;
671 /*****************************************************************************
672 * mozilla::widget::NativeKey
673 *****************************************************************************/
675 NativeKey::NativeKey(nsWindowBase
* aWidget
,
676 const MSG
& aKeyOrCharMessage
,
677 const ModifierKeyState
& aModKeyState
,
678 nsTArray
<FakeCharMsg
>* aFakeCharMsgs
) :
679 mWidget(aWidget
), mMsg(aKeyOrCharMessage
), mDOMKeyCode(0),
680 mModKeyState(aModKeyState
), mVirtualKeyCode(0), mOriginalVirtualKeyCode(0),
681 mFakeCharMsgs(aFakeCharMsgs
&& aFakeCharMsgs
->Length() ?
682 aFakeCharMsgs
: nullptr)
685 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
686 mKeyboardLayout
= keyboardLayout
->GetLayout();
687 mScanCode
= WinUtils::GetScanCode(mMsg
.lParam
);
688 mIsExtended
= WinUtils::IsExtendedScanCode(mMsg
.lParam
);
689 switch (mMsg
.message
) {
694 // If the key message is sent from other application like a11y tools, the
695 // scancode value might not be set proper value. Then, probably the value
697 // NOTE: If the virtual keycode can be caused by both non-extended key
698 // and extended key, the API returns the non-extended key's
699 // scancode. E.g., VK_LEFT causes "4" key on numpad.
701 uint16_t scanCodeEx
= ComputeScanCodeExFromVirtualKeyCode(mMsg
.wParam
);
703 mScanCode
= static_cast<uint8_t>(scanCodeEx
& 0xFF);
704 uint8_t extended
= static_cast<uint8_t>((scanCodeEx
& 0xFF00) >> 8);
705 mIsExtended
= (extended
== 0xE0) || (extended
== 0xE1);
708 // First, resolve the IME converted virtual keycode to its original
710 if (mMsg
.wParam
== VK_PROCESSKEY
) {
711 mOriginalVirtualKeyCode
=
712 static_cast<uint8_t>(::ImmGetVirtualKey(mMsg
.hwnd
));
714 mOriginalVirtualKeyCode
= static_cast<uint8_t>(mMsg
.wParam
);
717 // Most keys are not distinguished as left or right keys.
718 bool isLeftRightDistinguishedKey
= false;
720 // mOriginalVirtualKeyCode must not distinguish left or right of
721 // Shift, Control or Alt.
722 switch (mOriginalVirtualKeyCode
) {
726 isLeftRightDistinguishedKey
= true;
730 mVirtualKeyCode
= mOriginalVirtualKeyCode
;
731 mOriginalVirtualKeyCode
= VK_SHIFT
;
732 isLeftRightDistinguishedKey
= true;
736 mVirtualKeyCode
= mOriginalVirtualKeyCode
;
737 mOriginalVirtualKeyCode
= VK_CONTROL
;
738 isLeftRightDistinguishedKey
= true;
742 mVirtualKeyCode
= mOriginalVirtualKeyCode
;
743 mOriginalVirtualKeyCode
= VK_MENU
;
744 isLeftRightDistinguishedKey
= true;
748 // If virtual keycode (left-right distinguished keycode) is already
749 // computed, we don't need to do anymore.
750 if (mVirtualKeyCode
) {
754 // If the keycode doesn't have LR distinguished keycode, we just set
755 // mOriginalVirtualKeyCode to mVirtualKeyCode. Note that don't compute
756 // it from MapVirtualKeyEx() because the scan code might be wrong if
757 // the message is sent/posted by other application. Then, we will compute
758 // unexpected keycode from the scan code.
759 if (!isLeftRightDistinguishedKey
) {
763 if (!CanComputeVirtualKeyCodeFromScanCode()) {
764 // The right control key and the right alt key are extended keys.
765 // Therefore, we never get VK_RCONTRL and VK_RMENU for the result of
766 // MapVirtualKeyEx() on WinXP or WinServer2003.
768 // If VK_CONTROL or VK_MENU key message is caused by an extended key,
769 // we should assume that the right key of them is pressed.
770 switch (mOriginalVirtualKeyCode
) {
772 mVirtualKeyCode
= VK_RCONTROL
;
775 mVirtualKeyCode
= VK_RMENU
;
778 // Neither left shift nor right shift is not an extended key,
779 // let's use VK_LSHIFT for invalid scan code.
780 mVirtualKeyCode
= VK_LSHIFT
;
783 MOZ_CRASH("Unsupported mOriginalVirtualKeyCode");
788 NS_ASSERTION(!mVirtualKeyCode
,
789 "mVirtualKeyCode has been computed already");
791 // Otherwise, compute the virtual keycode with MapVirtualKeyEx().
792 mVirtualKeyCode
= ComputeVirtualKeyCodeFromScanCodeEx();
794 // Following code shouldn't be used now because we compute scancode value
795 // if we detect that the sender doesn't set proper scancode.
796 // However, the detection might fail. Therefore, let's keep using this.
797 switch (mOriginalVirtualKeyCode
) {
799 if (mVirtualKeyCode
!= VK_LCONTROL
&&
800 mVirtualKeyCode
!= VK_RCONTROL
) {
801 mVirtualKeyCode
= mIsExtended
? VK_RCONTROL
: VK_LCONTROL
;
805 if (mVirtualKeyCode
!= VK_LMENU
&& mVirtualKeyCode
!= VK_RMENU
) {
806 mVirtualKeyCode
= mIsExtended
? VK_RMENU
: VK_LMENU
;
810 if (mVirtualKeyCode
!= VK_LSHIFT
&& mVirtualKeyCode
!= VK_RSHIFT
) {
811 // Neither left shift nor right shift is not an extended key,
812 // let's use VK_LSHIFT for invalid scan code.
813 mVirtualKeyCode
= VK_LSHIFT
;
817 MOZ_CRASH("Unsupported mOriginalVirtualKeyCode");
824 // NOTE: If other applications like a11y tools sends WM_*CHAR without
825 // scancode, we cannot compute virtual keycode. I.e., with such
826 // applications, we cannot generate proper KeyboardEvent.code value.
828 // We cannot compute the virtual key code from WM_CHAR message on WinXP
829 // if it's caused by an extended key.
830 if (!CanComputeVirtualKeyCodeFromScanCode()) {
833 mVirtualKeyCode
= mOriginalVirtualKeyCode
=
834 ComputeVirtualKeyCodeFromScanCodeEx();
835 NS_ASSERTION(mVirtualKeyCode
, "Failed to compute virtual keycode");
838 MOZ_CRASH("Unsupported message");
841 if (!mVirtualKeyCode
) {
842 mVirtualKeyCode
= mOriginalVirtualKeyCode
;
846 keyboardLayout
->ConvertNativeKeyCodeToDOMKeyCode(mOriginalVirtualKeyCode
);
848 keyboardLayout
->ConvertNativeKeyCodeToKeyNameIndex(mOriginalVirtualKeyCode
);
850 KeyboardLayout::ConvertScanCodeToCodeNameIndex(
851 GetScanCodeWithExtendedFlag());
853 keyboardLayout
->InitNativeKey(*this, mModKeyState
);
856 (IsFollowedByDeadCharMessage() ||
857 keyboardLayout
->IsDeadKey(mOriginalVirtualKeyCode
, mModKeyState
));
858 mIsPrintableKey
= KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode
);
862 NativeKey::IsFollowedByDeadCharMessage() const
866 nextMsg
= mFakeCharMsgs
->ElementAt(0).GetCharMsg(mMsg
.hwnd
);
868 if (!WinUtils::PeekMessage(&nextMsg
, mMsg
.hwnd
, WM_KEYFIRST
, WM_KEYLAST
,
869 PM_NOREMOVE
| PM_NOYIELD
)) {
873 return IsDeadCharMessage(nextMsg
);
877 NativeKey::IsIMEDoingKakuteiUndo() const
879 // Following message pattern is caused by "Kakutei-Undo" of ATOK or WXG:
880 // ---------------------------------------------------------------------------
881 // WM_KEYDOWN * n (wParam = VK_BACK, lParam = 0x1)
882 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC0000001) # ATOK
883 // WM_IME_STARTCOMPOSITION * 1 (wParam = 0x0, lParam = 0x0)
884 // WM_IME_COMPOSITION * 1 (wParam = 0x0, lParam = 0x1BF)
885 // WM_CHAR * n (wParam = VK_BACK, lParam = 0x1)
886 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC00E0001)
887 // ---------------------------------------------------------------------------
888 // This doesn't match usual key message pattern such as:
889 // WM_KEYDOWN -> WM_CHAR -> WM_KEYDOWN -> WM_CHAR -> ... -> WM_KEYUP
890 // See following bugs for the detail.
891 // https://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese)
892 // https://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English)
893 MSG startCompositionMsg
, compositionMsg
, charMsg
;
894 return WinUtils::PeekMessage(&startCompositionMsg
, mMsg
.hwnd
,
895 WM_IME_STARTCOMPOSITION
, WM_IME_STARTCOMPOSITION
,
896 PM_NOREMOVE
| PM_NOYIELD
) &&
897 WinUtils::PeekMessage(&compositionMsg
, mMsg
.hwnd
, WM_IME_COMPOSITION
,
898 WM_IME_COMPOSITION
, PM_NOREMOVE
| PM_NOYIELD
) &&
899 WinUtils::PeekMessage(&charMsg
, mMsg
.hwnd
, WM_CHAR
, WM_CHAR
,
900 PM_NOREMOVE
| PM_NOYIELD
) &&
901 startCompositionMsg
.wParam
== 0x0 &&
902 startCompositionMsg
.lParam
== 0x0 &&
903 compositionMsg
.wParam
== 0x0 &&
904 compositionMsg
.lParam
== 0x1BF &&
905 charMsg
.wParam
== VK_BACK
&& charMsg
.lParam
== 0x1 &&
906 startCompositionMsg
.time
<= compositionMsg
.time
&&
907 compositionMsg
.time
<= charMsg
.time
;
911 NativeKey::GetScanCodeWithExtendedFlag() const
913 // MapVirtualKeyEx() has been improved for supporting extended keys since
914 // Vista. When we call it for mapping a scancode of an extended key and
915 // a virtual keycode, we need to add 0xE000 to the scancode.
916 // On Win XP and Win Server 2003, this doesn't support. On them, we have
917 // no way to get virtual keycodes from scancode of extended keys.
918 if (!mIsExtended
|| !IsVistaOrLater()) {
921 return (0xE000 | mScanCode
);
925 NativeKey::GetKeyLocation() const
927 switch (mVirtualKeyCode
) {
932 return nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT
;
938 return nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT
;
941 // XXX This code assumes that all keyboard drivers use same mapping.
942 return !mIsExtended
? nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD
:
943 nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD
;
956 // XXX This code assumes that all keyboard drivers use same mapping.
957 return mIsExtended
? nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD
:
958 nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD
;
960 // NumLock key isn't included due to IE9's behavior.
976 // Separator key of Brazilian keyboard or JIS keyboard for Mac
978 return nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD
;
983 NS_WARNING("Failed to decide the key location?");
986 return nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD
;
991 NativeKey::CanComputeVirtualKeyCodeFromScanCode() const
993 // Vista or later supports ScanCodeEx.
994 if (IsVistaOrLater()) {
997 // Otherwise, MapVirtualKeyEx() can compute virtual keycode only with
1003 NativeKey::ComputeVirtualKeyCodeFromScanCode() const
1005 return static_cast<uint8_t>(
1006 ::MapVirtualKeyEx(mScanCode
, MAPVK_VSC_TO_VK
, mKeyboardLayout
));
1010 NativeKey::ComputeVirtualKeyCodeFromScanCodeEx() const
1012 if (NS_WARN_IF(!CanComputeVirtualKeyCodeFromScanCode())) {
1015 return static_cast<uint8_t>(
1016 ::MapVirtualKeyEx(GetScanCodeWithExtendedFlag(), MAPVK_VSC_TO_VK_EX
,
1021 NativeKey::ComputeScanCodeExFromVirtualKeyCode(UINT aVirtualKeyCode
) const
1023 return static_cast<uint16_t>(
1024 ::MapVirtualKeyEx(aVirtualKeyCode
,
1025 IsVistaOrLater() ? MAPVK_VK_TO_VSC_EX
:
1031 NativeKey::ComputeUnicharFromScanCode() const
1033 return static_cast<char16_t
>(
1034 ::MapVirtualKeyEx(ComputeVirtualKeyCodeFromScanCode(),
1035 MAPVK_VK_TO_CHAR
, mKeyboardLayout
));
1039 NativeKey::InitKeyEvent(WidgetKeyboardEvent
& aKeyEvent
) const
1041 InitKeyEvent(aKeyEvent
, mModKeyState
);
1045 NativeKey::InitKeyEvent(WidgetKeyboardEvent
& aKeyEvent
,
1046 const ModifierKeyState
& aModKeyState
) const
1048 nsIntPoint
point(0, 0);
1049 mWidget
->InitEvent(aKeyEvent
, &point
);
1051 switch (aKeyEvent
.message
) {
1053 aKeyEvent
.keyCode
= mDOMKeyCode
;
1054 // Unique id for this keydown event and its associated keypress.
1055 sUniqueKeyEventId
++;
1056 aKeyEvent
.mUniqueId
= sUniqueKeyEventId
;
1059 aKeyEvent
.keyCode
= mDOMKeyCode
;
1060 // Set defaultPrevented of the key event if the VK_MENU is not a system
1061 // key release, so that the menu bar does not trigger. This helps avoid
1062 // triggering the menu bar for ALT key accelerators used in assistive
1063 // technologies such as Window-Eyes and ZoomText or for switching open
1065 aKeyEvent
.mFlags
.mDefaultPrevented
=
1066 (mOriginalVirtualKeyCode
== VK_MENU
&& mMsg
.message
!= WM_SYSKEYUP
);
1069 aKeyEvent
.mUniqueId
= sUniqueKeyEventId
;
1072 MOZ_CRASH("Invalid event message");
1075 aKeyEvent
.mIsRepeat
= IsRepeat();
1076 aKeyEvent
.mKeyNameIndex
= mKeyNameIndex
;
1077 if (mKeyNameIndex
== KEY_NAME_INDEX_USE_STRING
) {
1078 aKeyEvent
.mKeyValue
= mCommittedCharsAndModifiers
.ToString();
1080 aKeyEvent
.mCodeNameIndex
= mCodeNameIndex
;
1081 MOZ_ASSERT(mCodeNameIndex
!= CODE_NAME_INDEX_USE_STRING
);
1082 aKeyEvent
.location
= GetKeyLocation();
1083 aModKeyState
.InitInputEvent(aKeyEvent
);
1087 NativeKey::DispatchKeyEvent(WidgetKeyboardEvent
& aKeyEvent
,
1088 const MSG
* aMsgSentToPlugin
) const
1090 if (mWidget
->Destroyed()) {
1091 MOZ_CRASH("NativeKey tries to dispatch a key event on destroyed widget");
1094 KeyboardLayout::NotifyIdleServiceOfUserActivity();
1096 NPEvent pluginEvent
;
1097 if (aMsgSentToPlugin
&&
1098 mWidget
->GetInputContext().mIMEState
.mEnabled
== IMEState::PLUGIN
) {
1099 pluginEvent
.event
= aMsgSentToPlugin
->message
;
1100 pluginEvent
.wParam
= aMsgSentToPlugin
->wParam
;
1101 pluginEvent
.lParam
= aMsgSentToPlugin
->lParam
;
1102 aKeyEvent
.mPluginEvent
.Copy(pluginEvent
);
1105 return (mWidget
->DispatchKeyboardEvent(&aKeyEvent
) || mWidget
->Destroyed());
1109 NativeKey::HandleKeyDownMessage(bool* aEventDispatched
) const
1111 MOZ_ASSERT(IsKeyDownMessage());
1113 if (aEventDispatched
) {
1114 *aEventDispatched
= false;
1117 bool defaultPrevented
= false;
1118 if (mFakeCharMsgs
||
1119 !RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg
)) {
1120 // Ignore [shift+]alt+space so the OS can handle it.
1121 if (mModKeyState
.IsAlt() && !mModKeyState
.IsControl() &&
1122 mVirtualKeyCode
== VK_SPACE
) {
1126 bool isIMEEnabled
= WinUtils::IsIMEEnabled(mWidget
->GetInputContext());
1127 WidgetKeyboardEvent
keydownEvent(true, NS_KEY_DOWN
, mWidget
);
1128 InitKeyEvent(keydownEvent
, mModKeyState
);
1129 if (aEventDispatched
) {
1130 *aEventDispatched
= true;
1132 defaultPrevented
= DispatchKeyEvent(keydownEvent
, &mMsg
);
1134 if (mWidget
->Destroyed()) {
1138 // If IMC wasn't associated to the window but is associated it now (i.e.,
1139 // focus is moved from a non-editable editor to an editor by keydown
1140 // event handler), WM_CHAR and WM_SYSCHAR shouldn't cause first character
1141 // inputting if IME is opened. But then, we should redirect the native
1142 // keydown message to IME.
1143 // However, note that if focus has been already moved to another
1144 // application, we shouldn't redirect the message to it because the keydown
1145 // message is processed by us, so, nobody shouldn't process it.
1146 HWND focusedWnd
= ::GetFocus();
1147 if (!defaultPrevented
&& !mFakeCharMsgs
&& focusedWnd
&&
1148 !mWidget
->PluginHasFocus() && !isIMEEnabled
&&
1149 WinUtils::IsIMEEnabled(mWidget
->GetInputContext())) {
1150 RedirectedKeyDownMessageManager::RemoveNextCharMessage(focusedWnd
);
1153 keyinput
.type
= INPUT_KEYBOARD
;
1154 keyinput
.ki
.wVk
= mOriginalVirtualKeyCode
;
1155 keyinput
.ki
.wScan
= mScanCode
;
1156 keyinput
.ki
.dwFlags
= KEYEVENTF_SCANCODE
;
1158 keyinput
.ki
.dwFlags
|= KEYEVENTF_EXTENDEDKEY
;
1160 keyinput
.ki
.time
= 0;
1161 keyinput
.ki
.dwExtraInfo
= 0;
1163 RedirectedKeyDownMessageManager::WillRedirect(mMsg
, defaultPrevented
);
1165 ::SendInput(1, &keyinput
, sizeof(keyinput
));
1167 // Return here. We shouldn't dispatch keypress event for this WM_KEYDOWN.
1168 // If it's needed, it will be dispatched after next (redirected)
1173 defaultPrevented
= RedirectedKeyDownMessageManager::DefaultPrevented();
1174 // If this is redirected keydown message, we have dispatched the keydown
1176 if (aEventDispatched
) {
1177 *aEventDispatched
= true;
1181 RedirectedKeyDownMessageManager::Forget();
1183 // If the key was processed by IME, we shouldn't dispatch keypress event.
1184 if (mOriginalVirtualKeyCode
== VK_PROCESSKEY
) {
1185 return defaultPrevented
;
1188 // Don't dispatch keypress event for modifier keys.
1189 switch (mDOMKeyCode
) {
1193 case NS_VK_CAPS_LOCK
:
1194 case NS_VK_NUM_LOCK
:
1195 case NS_VK_SCROLL_LOCK
:
1197 return defaultPrevented
;
1200 if (defaultPrevented
) {
1201 DispatchPluginEventsAndDiscardsCharMessages();
1205 // If we won't be getting a WM_CHAR, WM_SYSCHAR or WM_DEADCHAR, synthesize a
1206 // keypress for almost all keys
1207 if (NeedsToHandleWithoutFollowingCharMessages()) {
1208 return (DispatchPluginEventsAndDiscardsCharMessages() ||
1209 DispatchKeyPressEventsWithKeyboardLayout());
1212 MSG followingCharMsg
;
1213 if (GetFollowingCharMessage(followingCharMsg
)) {
1214 // Even if there was char message, it might be redirected by different
1215 // window (perhaps, focus move?). Then, we shouldn't continue to handle
1216 // the message since no input should occur on the window.
1217 if (followingCharMsg
.message
== WM_NULL
||
1218 followingCharMsg
.hwnd
!= mMsg
.hwnd
) {
1221 return DispatchKeyPressEventForFollowingCharMessage(followingCharMsg
);
1224 if (!mModKeyState
.IsControl() && !mModKeyState
.IsAlt() &&
1225 !mModKeyState
.IsWin() && mIsPrintableKey
) {
1226 // If this is simple KeyDown event but next message is not WM_CHAR,
1227 // this event may not input text, so we should ignore this event.
1236 return DispatchKeyPressEventsWithKeyboardLayout();
1240 NativeKey::HandleCharMessage(const MSG
& aCharMsg
,
1241 bool* aEventDispatched
) const
1243 MOZ_ASSERT(IsKeyDownMessage() || IsPrintableCharMessage(mMsg
));
1244 MOZ_ASSERT(IsPrintableCharMessage(aCharMsg
.message
));
1246 if (aEventDispatched
) {
1247 *aEventDispatched
= false;
1250 // Alt+Space key is handled by OS, we shouldn't touch it.
1251 if (mModKeyState
.IsAlt() && !mModKeyState
.IsControl() &&
1252 mVirtualKeyCode
== VK_SPACE
) {
1256 // Bug 818235: Ignore Ctrl+Enter.
1257 if (!mModKeyState
.IsAlt() && mModKeyState
.IsControl() &&
1258 mVirtualKeyCode
== VK_RETURN
) {
1262 // XXXmnakao I think that if aNativeKeyDown is null, such lonely WM_CHAR
1263 // should cause composition events because they are not caused
1264 // by actual keyboard operation.
1266 static const char16_t U_SPACE
= 0x20;
1267 static const char16_t U_EQUAL
= 0x3D;
1269 // First, handle normal text input or non-printable key case here.
1270 if ((!mModKeyState
.IsAlt() && !mModKeyState
.IsControl()) ||
1271 mModKeyState
.IsAltGr() ||
1272 (mOriginalVirtualKeyCode
&&
1273 !KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode
))) {
1274 WidgetKeyboardEvent
keypressEvent(true, NS_KEY_PRESS
, mWidget
);
1275 if (aCharMsg
.wParam
>= U_SPACE
) {
1276 keypressEvent
.charCode
= static_cast<uint32_t>(aCharMsg
.wParam
);
1278 keypressEvent
.keyCode
= mDOMKeyCode
;
1280 // When AltGr (Alt+Ctrl) is pressed, that causes normal text input.
1281 // At this time, if either alt or ctrl flag is set, nsEditor ignores the
1282 // keypress event. For avoiding this issue, we should remove ctrl and alt
1284 ModifierKeyState
modKeyState(mModKeyState
);
1285 modKeyState
.Unset(MODIFIER_ALT
| MODIFIER_CONTROL
);
1286 InitKeyEvent(keypressEvent
, modKeyState
);
1287 if (aEventDispatched
) {
1288 *aEventDispatched
= true;
1290 return DispatchKeyEvent(keypressEvent
, &aCharMsg
);
1293 // XXX It seems that following code was implemented for shortcut key
1294 // handling. However, it's now handled in WM_KEYDOWN message handler.
1295 // So, this actually runs only when WM_CHAR is sent/posted without
1296 // WM_KEYDOWN. I think that we don't need to keypress event in such
1297 // case especially for shortcut keys.
1300 // Ctrl+A Ctrl+Z, see Programming Windows 3.1 page 110 for details
1301 if (mModKeyState
.IsControl() && aCharMsg
.wParam
<= 0x1A) {
1302 // Bug 16486: Need to account for shift here.
1303 uniChar
= aCharMsg
.wParam
- 1 + (mModKeyState
.IsShift() ? 'A' : 'a');
1304 } else if (mModKeyState
.IsControl() && aCharMsg
.wParam
<= 0x1F) {
1305 // Bug 50255: <ctrl><[> and <ctrl><]> are not being processed.
1306 // also fixes ctrl+\ (x1c), ctrl+^ (x1e) and ctrl+_ (x1f)
1307 // for some reason the keypress handler need to have the uniChar code set
1308 // with the addition of a upper case A not the lower case.
1309 uniChar
= aCharMsg
.wParam
- 1 + 'A';
1310 } else if (aCharMsg
.wParam
< U_SPACE
||
1311 (aCharMsg
.wParam
== U_EQUAL
&& mModKeyState
.IsControl())) {
1314 uniChar
= aCharMsg
.wParam
;
1317 // Bug 50255 and Bug 351310: Keep the characters unshifted for shortcuts and
1318 // accesskeys and make sure that numbers are always passed as such.
1319 if (uniChar
&& (mModKeyState
.IsControl() || mModKeyState
.IsAlt())) {
1320 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
1321 char16_t unshiftedCharCode
=
1322 (mVirtualKeyCode
>= '0' && mVirtualKeyCode
<= '9') ?
1323 mVirtualKeyCode
: mModKeyState
.IsShift() ?
1324 ComputeUnicharFromScanCode() : 0;
1325 // Ignore diacritics (top bit set) and key mapping errors (char code 0)
1326 if (static_cast<int32_t>(unshiftedCharCode
) > 0) {
1327 uniChar
= unshiftedCharCode
;
1331 // Bug 285161 and Bug 295095: They were caused by the initial fix for
1332 // bug 178110. When pressing (alt|ctrl)+char, the char must be lowercase
1333 // unless shift is pressed too.
1334 if (!mModKeyState
.IsShift() &&
1335 (mModKeyState
.IsAlt() || mModKeyState
.IsControl())) {
1336 uniChar
= towlower(uniChar
);
1339 WidgetKeyboardEvent
keypressEvent(true, NS_KEY_PRESS
, mWidget
);
1340 keypressEvent
.charCode
= uniChar
;
1341 if (!keypressEvent
.charCode
) {
1342 keypressEvent
.keyCode
= mDOMKeyCode
;
1344 InitKeyEvent(keypressEvent
, mModKeyState
);
1345 if (aEventDispatched
) {
1346 *aEventDispatched
= true;
1348 return DispatchKeyEvent(keypressEvent
, &aCharMsg
);
1352 NativeKey::HandleKeyUpMessage(bool* aEventDispatched
) const
1354 MOZ_ASSERT(IsKeyUpMessage());
1356 if (aEventDispatched
) {
1357 *aEventDispatched
= false;
1360 // Ignore [shift+]alt+space so the OS can handle it.
1361 if (mModKeyState
.IsAlt() && !mModKeyState
.IsControl() &&
1362 mVirtualKeyCode
== VK_SPACE
) {
1366 WidgetKeyboardEvent
keyupEvent(true, NS_KEY_UP
, mWidget
);
1367 InitKeyEvent(keyupEvent
, mModKeyState
);
1368 if (aEventDispatched
) {
1369 *aEventDispatched
= true;
1371 return DispatchKeyEvent(keyupEvent
, &mMsg
);
1375 NativeKey::NeedsToHandleWithoutFollowingCharMessages() const
1377 MOZ_ASSERT(IsKeyDownMessage());
1379 // Enter and backspace are always handled here to avoid for example the
1380 // confusion between ctrl-enter and ctrl-J.
1381 if (mDOMKeyCode
== NS_VK_RETURN
|| mDOMKeyCode
== NS_VK_BACK
) {
1385 // If any modifier keys which may cause printable keys becoming non-printable
1386 // are not pressed, we don't need special handling for the key.
1387 if (!mModKeyState
.IsControl() && !mModKeyState
.IsAlt() &&
1388 !mModKeyState
.IsWin()) {
1392 // If the key event causes dead key event, we don't need to dispatch keypress
1394 if (mIsDeadKey
&& mCommittedCharsAndModifiers
.IsEmpty()) {
1398 // Even if the key is a printable key, it might cause non-printable character
1399 // input with modifier key(s).
1400 return mIsPrintableKey
;
1403 #ifdef MOZ_CRASHREPORTER
1406 GetResultOfInSendMessageEx()
1408 DWORD ret
= ::InSendMessageEx(nullptr);
1410 return NS_LITERAL_CSTRING("ISMEX_NOSEND");
1412 nsAutoCString result
;
1413 if (ret
& ISMEX_CALLBACK
) {
1414 result
= "ISMEX_CALLBACK";
1416 if (ret
& ISMEX_NOTIFY
) {
1417 if (!result
.IsEmpty()) {
1420 result
+= "ISMEX_NOTIFY";
1422 if (ret
& ISMEX_REPLIED
) {
1423 if (!result
.IsEmpty()) {
1426 result
+= "ISMEX_REPLIED";
1428 if (ret
& ISMEX_SEND
) {
1429 if (!result
.IsEmpty()) {
1432 result
+= "ISMEX_SEND";
1438 GetMessageName(UINT aMessage
)
1441 case WM_KEYDOWN
: return "WM_KEYDOWN";
1442 case WM_SYSKEYDOWN
: return "WM_SYSKEYDOWN";
1443 case WM_KEYUP
: return "WM_KEYUP";
1444 case WM_SYSKEYUP
: return "WM_SYSKEYUP";
1445 case WM_CHAR
: return "WM_CHAR";
1446 case WM_DEADCHAR
: return "WM_DEADCHAR";
1447 case WM_SYSCHAR
: return "WM_SYSCHAR";
1448 case WM_SYSDEADCHAR
: return "WM_SYSDEADCHAR";
1449 case WM_UNICHAR
: return "WM_UNICHAR";
1450 case WM_QUIT
: return "WM_QUIT";
1451 case WM_NULL
: return "WM_NULL";
1452 default: return "Unknown";
1456 #endif // #ifdef MOZ_CRASHREPORTER
1459 NativeKey::MayBeSameCharMessage(const MSG
& aCharMsg1
,
1460 const MSG
& aCharMsg2
) const
1462 // NOTE: Although, we don't know when this case occurs, the scan code value
1463 // in lParam may be changed from 0 to something. The changed value
1464 // is different from the scan code of handling keydown message.
1465 static const LPARAM kScanCodeMask
= 0x00FF0000;
1467 aCharMsg1
.message
== aCharMsg2
.message
&&
1468 aCharMsg1
.wParam
== aCharMsg2
.wParam
&&
1469 (aCharMsg1
.lParam
& ~kScanCodeMask
) == (aCharMsg2
.lParam
& ~kScanCodeMask
);
1473 NativeKey::GetFollowingCharMessage(MSG
& aCharMsg
) const
1475 MOZ_ASSERT(IsKeyDownMessage());
1477 aCharMsg
.message
= WM_NULL
;
1479 if (mFakeCharMsgs
) {
1480 FakeCharMsg
& fakeCharMsg
= mFakeCharMsgs
->ElementAt(0);
1481 if (fakeCharMsg
.mConsumed
) {
1484 MSG charMsg
= fakeCharMsg
.GetCharMsg(mMsg
.hwnd
);
1485 fakeCharMsg
.mConsumed
= true;
1486 if (!IsCharMessage(charMsg
)) {
1493 // If next key message is not char message, we should give up to find a
1494 // related char message for the handling keydown event for now.
1495 // Note that it's possible other applications may send other key message
1496 // after we call TranslateMessage(). That may cause PeekMessage() failing
1497 // to get char message for the handling keydown message.
1499 if (!WinUtils::PeekMessage(&nextKeyMsg
, mMsg
.hwnd
, WM_KEYFIRST
, WM_KEYLAST
,
1500 PM_NOREMOVE
| PM_NOYIELD
) ||
1501 !IsCharMessage(nextKeyMsg
)) {
1505 // On Metrofox, PeekMessage() sometimes returns WM_NULL even if we specify
1506 // the message range. So, if it returns WM_NULL, we should retry to get
1507 // the following char message it was found above.
1508 for (uint32_t i
= 0; i
< 5; i
++) {
1509 MSG removedMsg
, nextKeyMsgInAllWindows
;
1510 bool doCrash
= false;
1511 if (!WinUtils::PeekMessage(&removedMsg
, mMsg
.hwnd
,
1512 nextKeyMsg
.message
, nextKeyMsg
.message
,
1513 PM_REMOVE
| PM_NOYIELD
)) {
1514 // We meets unexpected case. We should collect the message queue state
1515 // and crash for reporting the bug.
1517 // The char message is redirected to different thread's window by focus
1518 // move or something or just cancelled by external application.
1519 if (!WinUtils::PeekMessage(&nextKeyMsgInAllWindows
, 0,
1520 WM_KEYFIRST
, WM_KEYLAST
,
1521 PM_NOREMOVE
| PM_NOYIELD
)) {
1524 if (MayBeSameCharMessage(nextKeyMsgInAllWindows
, nextKeyMsg
)) {
1525 // The char message is redirected to different window created by our
1527 if (nextKeyMsgInAllWindows
.hwnd
!= mMsg
.hwnd
) {
1528 aCharMsg
= nextKeyMsgInAllWindows
;
1531 // The found char message still in the queue, but PeekMessage() failed
1532 // to remove it only with PM_REMOVE. Although, we don't know why this
1533 // occurs. However, this occurs acctually.
1534 // Try to remove the char message with GetMessage() again.
1535 if (WinUtils::GetMessage(&removedMsg
, mMsg
.hwnd
,
1536 nextKeyMsg
.message
, nextKeyMsg
.message
)) {
1537 // Cancel to crash, but we need to check the removed message value.
1544 #ifdef MOZ_CRASHREPORTER
1545 nsPrintfCString
info("\nPeekMessage() failed to remove char message! "
1546 "\nHandling message: %s (0x%08X), wParam: 0x%08X, "
1547 "lParam: 0x%08X, hwnd=0x%p, InSendMessageEx()=%s, \n"
1548 "Found message: %s (0x%08X), wParam: 0x%08X, "
1549 "lParam: 0x%08X, hwnd=0x%p, "
1550 "\nWM_NULL has been removed: %d, "
1551 "\nNext key message in all windows: %s (0x%08X), "
1552 "wParam: 0x%08X, lParam: 0x%08X, hwnd=0x%p, "
1554 GetMessageName(mMsg
.message
),
1555 mMsg
.message
, mMsg
.wParam
, mMsg
.lParam
,
1557 GetResultOfInSendMessageEx().get(),
1558 GetMessageName(nextKeyMsg
.message
),
1559 nextKeyMsg
.message
, nextKeyMsg
.wParam
,
1560 nextKeyMsg
.lParam
, nextKeyMsg
.hwnd
, i
,
1561 GetMessageName(nextKeyMsgInAllWindows
.message
),
1562 nextKeyMsgInAllWindows
.message
,
1563 nextKeyMsgInAllWindows
.wParam
,
1564 nextKeyMsgInAllWindows
.lParam
,
1565 nextKeyMsgInAllWindows
.hwnd
,
1566 nextKeyMsgInAllWindows
.time
);
1567 CrashReporter::AppendAppNotesToCrashReport(info
);
1569 if (WinUtils::PeekMessage(&nextMsg
, 0, 0, 0,
1570 PM_NOREMOVE
| PM_NOYIELD
)) {
1571 nsPrintfCString
info("\nNext message in all windows: %s (0x%08X), "
1572 "wParam: 0x%08X, lParam: 0x%08X, hwnd=0x%p, "
1574 GetMessageName(nextMsg
.message
),
1575 nextMsg
.message
, nextMsg
.wParam
, nextMsg
.lParam
,
1576 nextMsg
.hwnd
, nextMsg
.time
);
1577 CrashReporter::AppendAppNotesToCrashReport(info
);
1579 CrashReporter::AppendAppNotesToCrashReport(
1580 NS_LITERAL_CSTRING("\nThere is no message in any window"));
1582 #endif // #ifdef MOZ_CRASHREPORTER
1583 MOZ_CRASH("We lost the following char message");
1586 // Retry for the strange case.
1587 if (removedMsg
.message
== WM_NULL
) {
1591 // Typically, this case occurs with WM_DEADCHAR. If the removed message's
1592 // wParam becomes 0, that means that the key event shouldn't cause text
1593 // input. So, let's ignore the strange char message.
1594 if (removedMsg
.message
== nextKeyMsg
.message
&& !removedMsg
.wParam
) {
1598 // NOTE: Although, we don't know when this case occurs, the scan code value
1599 // in lParam may be changed from 0 to something. The changed value
1600 // is different from the scan code of handling keydown message.
1601 if (!MayBeSameCharMessage(removedMsg
, nextKeyMsg
)) {
1602 #ifdef MOZ_CRASHREPORTER
1603 nsPrintfCString
info("\nPeekMessage() removed unexpcted char message! "
1604 "\nHandling message: %s (0x%08X), wParam: 0x%08X, "
1605 "lParam: 0x%08X, hwnd=0x%p, InSendMessageEx()=%s, "
1606 "\nFound message: %s (0x%08X), wParam: 0x%08X, "
1607 "lParam: 0x%08X, hwnd=0x%p, "
1608 "\nRemoved message: %s (0x%08X), wParam: 0x%08X, "
1609 "lParam: 0x%08X, hwnd=0x%p, ",
1610 GetMessageName(mMsg
.message
),
1611 mMsg
.message
, mMsg
.wParam
, mMsg
.lParam
, mMsg
.hwnd
,
1612 GetResultOfInSendMessageEx().get(),
1613 GetMessageName(nextKeyMsg
.message
),
1614 nextKeyMsg
.message
, nextKeyMsg
.wParam
,
1615 nextKeyMsg
.lParam
, nextKeyMsg
.hwnd
,
1616 GetMessageName(removedMsg
.message
),
1617 removedMsg
.message
, removedMsg
.wParam
,
1618 removedMsg
.lParam
, removedMsg
.hwnd
);
1619 CrashReporter::AppendAppNotesToCrashReport(info
);
1620 // What's the next key message?
1621 MSG nextKeyMsgAfter
;
1622 if (WinUtils::PeekMessage(&nextKeyMsgAfter
, mMsg
.hwnd
,
1623 WM_KEYFIRST
, WM_KEYLAST
,
1624 PM_NOREMOVE
| PM_NOYIELD
)) {
1625 nsPrintfCString
info("\nNext key message after unexpected char message "
1626 "removed: %s (0x%08X), wParam: 0x%08X, "
1627 "lParam: 0x%08X, hwnd=0x%p, ",
1628 GetMessageName(nextKeyMsgAfter
.message
),
1629 nextKeyMsgAfter
.message
, nextKeyMsgAfter
.wParam
,
1630 nextKeyMsgAfter
.lParam
, nextKeyMsgAfter
.hwnd
);
1631 CrashReporter::AppendAppNotesToCrashReport(info
);
1633 CrashReporter::AppendAppNotesToCrashReport(
1634 NS_LITERAL_CSTRING("\nThere is no key message after unexpected char "
1635 "message removed, "));
1637 // Another window has a key message?
1638 MSG nextKeyMsgInAllWindows
;
1639 if (WinUtils::PeekMessage(&nextKeyMsgInAllWindows
, 0,
1640 WM_KEYFIRST
, WM_KEYLAST
,
1641 PM_NOREMOVE
| PM_NOYIELD
)) {
1642 nsPrintfCString
info("\nNext key message in all windows: %s (0x%08X), "
1643 "wParam: 0x%08X, lParam: 0x%08X, hwnd=0x%p.",
1644 GetMessageName(nextKeyMsgInAllWindows
.message
),
1645 nextKeyMsgInAllWindows
.message
,
1646 nextKeyMsgInAllWindows
.wParam
,
1647 nextKeyMsgInAllWindows
.lParam
,
1648 nextKeyMsgInAllWindows
.hwnd
);
1649 CrashReporter::AppendAppNotesToCrashReport(info
);
1651 CrashReporter::AppendAppNotesToCrashReport(
1652 NS_LITERAL_CSTRING("\nThere is no key message in any windows."));
1654 #endif // #ifdef MOZ_CRASHREPORTER
1655 MOZ_CRASH("PeekMessage() removed unexpected message");
1658 aCharMsg
= removedMsg
;
1661 #ifdef MOZ_CRASHREPORTER
1662 nsPrintfCString
info("\nWe lost following char message! "
1663 "\nHandling message: %s (0x%08X), wParam: 0x%08X, "
1664 "lParam: 0x%08X, InSendMessageEx()=%s, \n"
1665 "Found message: %s (0x%08X), wParam: 0x%08X, "
1666 "lParam: 0x%08X, removed a lot of WM_NULL",
1667 GetMessageName(mMsg
.message
),
1668 mMsg
.message
, mMsg
.wParam
, mMsg
.lParam
,
1669 GetResultOfInSendMessageEx().get(),
1670 GetMessageName(nextKeyMsg
.message
),
1671 nextKeyMsg
.message
, nextKeyMsg
.wParam
,
1673 CrashReporter::AppendAppNotesToCrashReport(info
);
1674 #endif // #ifdef MOZ_CRASHREPORTER
1675 MOZ_CRASH("We lost the following char message");
1680 NativeKey::DispatchPluginEventsAndDiscardsCharMessages() const
1682 MOZ_ASSERT(IsKeyDownMessage());
1684 // Remove a possible WM_CHAR or WM_SYSCHAR messages from the message queue.
1685 // They can be more than one because of:
1686 // * Dead-keys not pairing with base character
1687 // * Some keyboard layouts may map up to 4 characters to the single key
1688 bool anyCharMessagesRemoved
= false;
1690 while (GetFollowingCharMessage(msg
)) {
1691 if (msg
.message
== WM_NULL
) {
1694 anyCharMessagesRemoved
= true;
1695 // If the window handle is changed, focused window must be changed.
1696 // So, plugin shouldn't handle it anymore.
1697 if (msg
.hwnd
!= mMsg
.hwnd
) {
1700 MOZ_RELEASE_ASSERT(!mWidget
->Destroyed(),
1701 "NativeKey tries to dispatch a plugin event on destroyed widget");
1702 mWidget
->DispatchPluginEvent(msg
);
1703 if (mWidget
->Destroyed()) {
1708 if (!mFakeCharMsgs
&& !anyCharMessagesRemoved
&&
1709 mDOMKeyCode
== NS_VK_BACK
&& IsIMEDoingKakuteiUndo()) {
1710 // This is for a hack for ATOK and WXG. So, PeekMessage() must scceed!
1711 while (WinUtils::PeekMessage(&msg
, mMsg
.hwnd
, WM_CHAR
, WM_CHAR
,
1712 PM_REMOVE
| PM_NOYIELD
)) {
1713 if (msg
.message
!= WM_CHAR
) {
1714 MOZ_RELEASE_ASSERT(msg
.message
== WM_NULL
,
1715 "Unexpected message was removed");
1718 MOZ_RELEASE_ASSERT(!mWidget
->Destroyed(),
1719 "NativeKey tries to dispatch a plugin event on destroyed widget");
1720 mWidget
->DispatchPluginEvent(msg
);
1721 return mWidget
->Destroyed();
1723 MOZ_CRASH("NativeKey failed to get WM_CHAR for ATOK or WXG");
1730 NativeKey::DispatchKeyPressEventsWithKeyboardLayout() const
1732 MOZ_ASSERT(IsKeyDownMessage());
1733 MOZ_ASSERT(!mIsDeadKey
);
1735 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
1737 UniCharsAndModifiers
inputtingChars(mCommittedCharsAndModifiers
);
1738 UniCharsAndModifiers shiftedChars
;
1739 UniCharsAndModifiers unshiftedChars
;
1740 uint32_t shiftedLatinChar
= 0;
1741 uint32_t unshiftedLatinChar
= 0;
1743 if (!KeyboardLayout::IsPrintableCharKey(mVirtualKeyCode
)) {
1744 inputtingChars
.Clear();
1747 if (mModKeyState
.IsControl() ^ mModKeyState
.IsAlt()) {
1748 ModifierKeyState
capsLockState(
1749 mModKeyState
.GetModifiers() & MODIFIER_CAPSLOCK
);
1752 keyboardLayout
->GetUniCharsAndModifiers(mVirtualKeyCode
, capsLockState
);
1753 capsLockState
.Set(MODIFIER_SHIFT
);
1755 keyboardLayout
->GetUniCharsAndModifiers(mVirtualKeyCode
, capsLockState
);
1757 // The current keyboard cannot input alphabets or numerics,
1758 // we should append them for Shortcut/Access keys.
1759 // E.g., for Cyrillic keyboard layout.
1760 capsLockState
.Unset(MODIFIER_SHIFT
);
1761 WidgetUtils::GetLatinCharCodeForKeyCode(mDOMKeyCode
,
1762 capsLockState
.GetModifiers(),
1763 &unshiftedLatinChar
,
1766 // If the shiftedLatinChar isn't 0, the key code is NS_VK_[A-Z].
1767 if (shiftedLatinChar
) {
1768 // If the produced characters of the key on current keyboard layout
1769 // are same as computed Latin characters, we shouldn't append the
1770 // Latin characters to alternativeCharCode.
1771 if (unshiftedLatinChar
== unshiftedChars
.mChars
[0] &&
1772 shiftedLatinChar
== shiftedChars
.mChars
[0]) {
1773 shiftedLatinChar
= unshiftedLatinChar
= 0;
1775 } else if (unshiftedLatinChar
) {
1776 // If the shiftedLatinChar is 0, the keyCode doesn't produce
1777 // alphabet character. At that time, the character may be produced
1778 // with Shift key. E.g., on French keyboard layout, NS_VK_PERCENT
1779 // key produces LATIN SMALL LETTER U WITH GRAVE (U+00F9) without
1780 // Shift key but with Shift key, it produces '%'.
1781 // If the unshiftedLatinChar is produced by the key on current
1782 // keyboard layout, we shouldn't append it to alternativeCharCode.
1783 if (unshiftedLatinChar
== unshiftedChars
.mChars
[0] ||
1784 unshiftedLatinChar
== shiftedChars
.mChars
[0]) {
1785 unshiftedLatinChar
= 0;
1789 // If the charCode is not ASCII character, we should replace the
1790 // charCode with ASCII character only when Ctrl is pressed.
1791 // But don't replace the charCode when the charCode is not same as
1792 // unmodified characters. In such case, Ctrl is sometimes used for a
1793 // part of character inputting key combination like Shift.
1794 if (mModKeyState
.IsControl()) {
1796 mModKeyState
.IsShift() ? shiftedLatinChar
: unshiftedLatinChar
;
1798 (!inputtingChars
.mLength
||
1799 inputtingChars
.UniCharsCaseInsensitiveEqual(
1800 mModKeyState
.IsShift() ? shiftedChars
: unshiftedChars
))) {
1801 inputtingChars
.Clear();
1802 inputtingChars
.Append(ch
, mModKeyState
.GetModifiers());
1807 if (inputtingChars
.IsEmpty() &&
1808 shiftedChars
.IsEmpty() && unshiftedChars
.IsEmpty()) {
1809 WidgetKeyboardEvent
keypressEvent(true, NS_KEY_PRESS
, mWidget
);
1810 keypressEvent
.keyCode
= mDOMKeyCode
;
1811 InitKeyEvent(keypressEvent
, mModKeyState
);
1812 return DispatchKeyEvent(keypressEvent
);
1815 uint32_t longestLength
=
1816 std::max(inputtingChars
.mLength
,
1817 std::max(shiftedChars
.mLength
, unshiftedChars
.mLength
));
1818 uint32_t skipUniChars
= longestLength
- inputtingChars
.mLength
;
1819 uint32_t skipShiftedChars
= longestLength
- shiftedChars
.mLength
;
1820 uint32_t skipUnshiftedChars
= longestLength
- unshiftedChars
.mLength
;
1821 UINT keyCode
= !inputtingChars
.mLength
? mDOMKeyCode
: 0;
1822 bool defaultPrevented
= false;
1823 for (uint32_t cnt
= 0; cnt
< longestLength
; cnt
++) {
1824 uint16_t uniChar
, shiftedChar
, unshiftedChar
;
1825 uniChar
= shiftedChar
= unshiftedChar
= 0;
1826 ModifierKeyState
modKeyState(mModKeyState
);
1827 if (skipUniChars
<= cnt
) {
1828 if (cnt
- skipUniChars
< inputtingChars
.mLength
) {
1829 // If key in combination with Alt and/or Ctrl produces a different
1830 // character than without them then do not report these flags
1831 // because it is separate keyboard layout shift state. If dead-key
1832 // and base character does not produce a valid composite character
1833 // then both produced dead-key character and following base
1834 // character may have different modifier flags, too.
1835 modKeyState
.Unset(MODIFIER_SHIFT
| MODIFIER_CONTROL
| MODIFIER_ALT
|
1836 MODIFIER_ALTGRAPH
| MODIFIER_CAPSLOCK
);
1837 modKeyState
.Set(inputtingChars
.mModifiers
[cnt
- skipUniChars
]);
1839 uniChar
= inputtingChars
.mChars
[cnt
- skipUniChars
];
1841 if (skipShiftedChars
<= cnt
)
1842 shiftedChar
= shiftedChars
.mChars
[cnt
- skipShiftedChars
];
1843 if (skipUnshiftedChars
<= cnt
)
1844 unshiftedChar
= unshiftedChars
.mChars
[cnt
- skipUnshiftedChars
];
1845 nsAutoTArray
<AlternativeCharCode
, 5> altArray
;
1847 if (shiftedChar
|| unshiftedChar
) {
1848 AlternativeCharCode
chars(unshiftedChar
, shiftedChar
);
1849 altArray
.AppendElement(chars
);
1851 if (cnt
== longestLength
- 1) {
1852 if (unshiftedLatinChar
|| shiftedLatinChar
) {
1853 AlternativeCharCode
chars(unshiftedLatinChar
, shiftedLatinChar
);
1854 altArray
.AppendElement(chars
);
1857 // Typically, following virtual keycodes are used for a key which can
1858 // input the character. However, these keycodes are also used for
1859 // other keys on some keyboard layout. E.g., in spite of Shift+'1'
1860 // inputs '+' on Thai keyboard layout, a key which is at '=/+'
1861 // key on ANSI keyboard layout is VK_OEM_PLUS. Native applications
1862 // handle it as '+' key if Ctrl key is pressed.
1863 char16_t charForOEMKeyCode
= 0;
1864 switch (mVirtualKeyCode
) {
1865 case VK_OEM_PLUS
: charForOEMKeyCode
= '+'; break;
1866 case VK_OEM_COMMA
: charForOEMKeyCode
= ','; break;
1867 case VK_OEM_MINUS
: charForOEMKeyCode
= '-'; break;
1868 case VK_OEM_PERIOD
: charForOEMKeyCode
= '.'; break;
1870 if (charForOEMKeyCode
&&
1871 charForOEMKeyCode
!= unshiftedChars
.mChars
[0] &&
1872 charForOEMKeyCode
!= shiftedChars
.mChars
[0] &&
1873 charForOEMKeyCode
!= unshiftedLatinChar
&&
1874 charForOEMKeyCode
!= shiftedLatinChar
) {
1875 AlternativeCharCode
OEMChars(charForOEMKeyCode
, charForOEMKeyCode
);
1876 altArray
.AppendElement(OEMChars
);
1880 WidgetKeyboardEvent
keypressEvent(true, NS_KEY_PRESS
, mWidget
);
1881 keypressEvent
.charCode
= uniChar
;
1882 keypressEvent
.alternativeCharCodes
.AppendElements(altArray
);
1883 InitKeyEvent(keypressEvent
, modKeyState
);
1884 defaultPrevented
= (DispatchKeyEvent(keypressEvent
) || defaultPrevented
);
1885 if (mWidget
->Destroyed()) {
1890 return defaultPrevented
;
1894 NativeKey::DispatchKeyPressEventForFollowingCharMessage(
1895 const MSG
& aCharMsg
) const
1897 MOZ_ASSERT(IsKeyDownMessage());
1899 if (mFakeCharMsgs
) {
1900 if (IsDeadCharMessage(aCharMsg
)) {
1904 if (mIsPrintableKey
) {
1905 nsPrintfCString
log(
1906 "mOriginalVirtualKeyCode=0x%02X, mCommittedCharsAndModifiers={ "
1907 "mChars=[ 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X ], mLength=%d }, "
1909 mOriginalVirtualKeyCode
, mCommittedCharsAndModifiers
.mChars
[0],
1910 mCommittedCharsAndModifiers
.mChars
[1],
1911 mCommittedCharsAndModifiers
.mChars
[2],
1912 mCommittedCharsAndModifiers
.mChars
[3],
1913 mCommittedCharsAndModifiers
.mChars
[4],
1914 mCommittedCharsAndModifiers
.mLength
, aCharMsg
.wParam
);
1915 if (mCommittedCharsAndModifiers
.IsEmpty()) {
1916 log
.Insert("length is zero: ", 0);
1917 NS_ERROR(log
.get());
1919 } else if (mCommittedCharsAndModifiers
.mChars
[0] != aCharMsg
.wParam
) {
1920 log
.Insert("character mismatch: ", 0);
1921 NS_ERROR(log
.get());
1925 #endif // #ifdef DEBUG
1926 return HandleCharMessage(aCharMsg
);
1929 if (IsDeadCharMessage(aCharMsg
)) {
1930 if (!mWidget
->PluginHasFocus()) {
1933 return (mWidget
->DispatchPluginEvent(aCharMsg
) || mWidget
->Destroyed());
1936 bool defaultPrevented
= HandleCharMessage(aCharMsg
);
1937 // If a syschar keypress wasn't processed, Windows may want to
1938 // handle it to activate a native menu.
1939 if (!defaultPrevented
&& IsSysCharMessage(aCharMsg
)) {
1940 ::DefWindowProcW(aCharMsg
.hwnd
, aCharMsg
.message
,
1941 aCharMsg
.wParam
, aCharMsg
.lParam
);
1943 return defaultPrevented
;
1946 /*****************************************************************************
1947 * mozilla::widget::KeyboardLayout
1948 *****************************************************************************/
1950 KeyboardLayout
* KeyboardLayout::sInstance
= nullptr;
1951 nsIIdleServiceInternal
* KeyboardLayout::sIdleService
= nullptr;
1954 PRLogModuleInfo
* sKeyboardLayoutLogger
= nullptr;
1955 #endif // #ifdef PR_LOGGING
1959 KeyboardLayout::GetInstance()
1963 if (!sKeyboardLayoutLogger
) {
1964 sKeyboardLayoutLogger
= PR_NewLogModule("KeyboardLayoutWidgets");
1966 #endif // #ifdef PR_LOGGING
1967 sInstance
= new KeyboardLayout();
1968 nsCOMPtr
<nsIIdleServiceInternal
> idleService
=
1969 do_GetService("@mozilla.org/widget/idleservice;1");
1970 // The refcount will be decreased at shut down.
1971 sIdleService
= idleService
.forget().take();
1978 KeyboardLayout::Shutdown()
1981 sInstance
= nullptr;
1982 NS_IF_RELEASE(sIdleService
);
1987 KeyboardLayout::NotifyIdleServiceOfUserActivity()
1989 sIdleService
->ResetIdleTimeOut(0);
1992 KeyboardLayout::KeyboardLayout() :
1993 mKeyboardLayout(0), mIsOverridden(false),
1994 mIsPendingToRestoreKeyboardLayout(false)
1996 mDeadKeyTableListHead
= nullptr;
1998 // NOTE: LoadLayout() should be called via OnLayoutChange().
2001 KeyboardLayout::~KeyboardLayout()
2003 ReleaseDeadKeyTables();
2007 KeyboardLayout::IsPrintableCharKey(uint8_t aVirtualKey
)
2009 return GetKeyIndex(aVirtualKey
) >= 0;
2013 KeyboardLayout::ComputeScanCodeForVirtualKeyCode(uint8_t aVirtualKeyCode
) const
2015 return static_cast<WORD
>(
2016 ::MapVirtualKeyEx(aVirtualKeyCode
, MAPVK_VK_TO_VSC
, GetLayout()));
2020 KeyboardLayout::IsDeadKey(uint8_t aVirtualKey
,
2021 const ModifierKeyState
& aModKeyState
) const
2023 int32_t virtualKeyIndex
= GetKeyIndex(aVirtualKey
);
2024 if (virtualKeyIndex
< 0) {
2028 return mVirtualKeys
[virtualKeyIndex
].IsDeadKey(
2029 VirtualKey::ModifiersToShiftState(aModKeyState
.GetModifiers()));
2033 KeyboardLayout::InitNativeKey(NativeKey
& aNativeKey
,
2034 const ModifierKeyState
& aModKeyState
)
2036 if (mIsPendingToRestoreKeyboardLayout
) {
2037 LoadLayout(::GetKeyboardLayout(0));
2040 uint8_t virtualKey
= aNativeKey
.mOriginalVirtualKeyCode
;
2041 int32_t virtualKeyIndex
= GetKeyIndex(virtualKey
);
2043 if (virtualKeyIndex
< 0) {
2044 // Does not produce any printable characters, but still preserves the
2049 MOZ_ASSERT(aNativeKey
.mKeyNameIndex
== KEY_NAME_INDEX_USE_STRING
,
2050 "Printable key's key name index must be KEY_NAME_INDEX_USE_STRING");
2052 bool isKeyDown
= aNativeKey
.IsKeyDownMessage();
2053 uint8_t shiftState
=
2054 VirtualKey::ModifiersToShiftState(aModKeyState
.GetModifiers());
2056 if (mVirtualKeys
[virtualKeyIndex
].IsDeadKey(shiftState
)) {
2057 if ((isKeyDown
&& mActiveDeadKey
< 0) ||
2058 (!isKeyDown
&& mActiveDeadKey
== virtualKey
)) {
2059 // First dead key event doesn't generate characters.
2061 // Dead-key state activated at keydown.
2062 mActiveDeadKey
= virtualKey
;
2063 mDeadKeyShiftState
= shiftState
;
2065 UniCharsAndModifiers deadChars
=
2066 mVirtualKeys
[virtualKeyIndex
].GetNativeUniChars(shiftState
);
2067 NS_ASSERTION(deadChars
.mLength
== 1,
2068 "dead key must generate only one character");
2069 aNativeKey
.mKeyNameIndex
=
2070 WidgetUtils::GetDeadKeyNameIndex(deadChars
.mChars
[0]);
2074 // Dead key followed by another dead key causes inputting both character.
2075 // However, at keydown message handling, we need to forget the first
2076 // dead key because there is no guarantee coming WM_KEYUP for the second
2077 // dead key before next WM_KEYDOWN. E.g., due to auto key repeat or
2078 // pressing another dead key before releasing current key. Therefore,
2079 // we can set only a character for current key for keyup event.
2080 if (mActiveDeadKey
< 0) {
2081 aNativeKey
.mCommittedCharsAndModifiers
=
2082 mVirtualKeys
[virtualKeyIndex
].GetUniChars(shiftState
);
2086 int32_t activeDeadKeyIndex
= GetKeyIndex(mActiveDeadKey
);
2087 if (activeDeadKeyIndex
< 0 || activeDeadKeyIndex
>= NS_NUM_OF_KEYS
) {
2088 #if defined(DEBUG) || defined(MOZ_CRASHREPORTER)
2089 nsPrintfCString
warning("The virtual key index (%d) of mActiveDeadKey "
2090 "(0x%02X) is not a printable key (virtualKey="
2092 activeDeadKeyIndex
, mActiveDeadKey
, virtualKey
);
2093 NS_WARNING(warning
.get());
2094 #ifdef MOZ_CRASHREPORTER
2095 CrashReporter::AppendAppNotesToCrashReport(
2096 NS_LITERAL_CSTRING("\n") + warning
);
2097 #endif // #ifdef MOZ_CRASHREPORTER
2098 #endif // #if defined(DEBUG) || defined(MOZ_CRASHREPORTER)
2099 MOZ_CRASH("Trying to reference out of range of mVirtualKeys");
2101 UniCharsAndModifiers prevDeadChars
=
2102 mVirtualKeys
[activeDeadKeyIndex
].GetUniChars(mDeadKeyShiftState
);
2103 UniCharsAndModifiers newChars
=
2104 mVirtualKeys
[virtualKeyIndex
].GetUniChars(shiftState
);
2105 // But keypress events should be fired for each committed character.
2106 aNativeKey
.mCommittedCharsAndModifiers
= prevDeadChars
+ newChars
;
2108 DeactivateDeadKeyState();
2113 UniCharsAndModifiers baseChars
=
2114 mVirtualKeys
[virtualKeyIndex
].GetUniChars(shiftState
);
2115 if (mActiveDeadKey
< 0) {
2116 // No dead-keys are active. Just return the produced characters.
2117 aNativeKey
.mCommittedCharsAndModifiers
= baseChars
;
2121 // Dead-key was active. See if pressed base character does produce
2122 // valid composite character.
2123 int32_t activeDeadKeyIndex
= GetKeyIndex(mActiveDeadKey
);
2124 char16_t compositeChar
= (baseChars
.mLength
== 1 && baseChars
.mChars
[0]) ?
2125 mVirtualKeys
[activeDeadKeyIndex
].GetCompositeChar(mDeadKeyShiftState
,
2126 baseChars
.mChars
[0]) : 0;
2127 if (compositeChar
) {
2128 // Active dead-key and base character does produce exactly one
2129 // composite character.
2130 aNativeKey
.mCommittedCharsAndModifiers
.Append(compositeChar
,
2131 baseChars
.mModifiers
[0]);
2133 DeactivateDeadKeyState();
2138 // There is no valid dead-key and base character combination.
2139 // Return dead-key character followed by base character.
2140 UniCharsAndModifiers deadChars
=
2141 mVirtualKeys
[activeDeadKeyIndex
].GetUniChars(mDeadKeyShiftState
);
2142 // But keypress events should be fired for each committed character.
2143 aNativeKey
.mCommittedCharsAndModifiers
= deadChars
+ baseChars
;
2145 DeactivateDeadKeyState();
2151 UniCharsAndModifiers
2152 KeyboardLayout::GetUniCharsAndModifiers(
2153 uint8_t aVirtualKey
,
2154 const ModifierKeyState
& aModKeyState
) const
2156 UniCharsAndModifiers result
;
2157 int32_t key
= GetKeyIndex(aVirtualKey
);
2161 return mVirtualKeys
[key
].
2162 GetUniChars(VirtualKey::ModifiersToShiftState(aModKeyState
.GetModifiers()));
2166 KeyboardLayout::LoadLayout(HKL aLayout
)
2168 mIsPendingToRestoreKeyboardLayout
= false;
2170 if (mKeyboardLayout
== aLayout
) {
2174 mKeyboardLayout
= aLayout
;
2177 memset(kbdState
, 0, sizeof(kbdState
));
2179 BYTE originalKbdState
[256];
2180 // Bitfield with all shift states that have at least one dead-key.
2181 uint16_t shiftStatesWithDeadKeys
= 0;
2182 // Bitfield with all shift states that produce any possible dead-key base
2184 uint16_t shiftStatesWithBaseChars
= 0;
2186 mActiveDeadKey
= -1;
2188 ReleaseDeadKeyTables();
2190 ::GetKeyboardState(originalKbdState
);
2192 // For each shift state gather all printable characters that are produced
2193 // for normal case when no any dead-key is active.
2195 for (VirtualKey::ShiftState shiftState
= 0; shiftState
< 16; shiftState
++) {
2196 VirtualKey::FillKbdState(kbdState
, shiftState
);
2197 for (uint32_t virtualKey
= 0; virtualKey
< 256; virtualKey
++) {
2198 int32_t vki
= GetKeyIndex(virtualKey
);
2202 NS_ASSERTION(uint32_t(vki
) < ArrayLength(mVirtualKeys
), "invalid index");
2203 char16_t uniChars
[5];
2205 ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)uniChars
,
2206 ArrayLength(uniChars
), 0, mKeyboardLayout
);
2209 shiftStatesWithDeadKeys
|= (1 << shiftState
);
2210 // Repeat dead-key to deactivate it and get its character
2212 char16_t deadChar
[2];
2213 ret
= ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)deadChar
,
2214 ArrayLength(deadChar
), 0, mKeyboardLayout
);
2215 NS_ASSERTION(ret
== 2, "Expecting twice repeated dead-key character");
2216 mVirtualKeys
[vki
].SetDeadChar(shiftState
, deadChar
[0]);
2219 // dead-key can pair only with exactly one base character.
2220 shiftStatesWithBaseChars
|= (1 << shiftState
);
2222 mVirtualKeys
[vki
].SetNormalChars(shiftState
, uniChars
, ret
);
2227 // Now process each dead-key to find all its base characters and resulting
2228 // composite characters.
2229 for (VirtualKey::ShiftState shiftState
= 0; shiftState
< 16; shiftState
++) {
2230 if (!(shiftStatesWithDeadKeys
& (1 << shiftState
))) {
2234 VirtualKey::FillKbdState(kbdState
, shiftState
);
2236 for (uint32_t virtualKey
= 0; virtualKey
< 256; virtualKey
++) {
2237 int32_t vki
= GetKeyIndex(virtualKey
);
2238 if (vki
>= 0 && mVirtualKeys
[vki
].IsDeadKey(shiftState
)) {
2239 DeadKeyEntry deadKeyArray
[256];
2240 int32_t n
= GetDeadKeyCombinations(virtualKey
, kbdState
,
2241 shiftStatesWithBaseChars
,
2243 ArrayLength(deadKeyArray
));
2244 const DeadKeyTable
* dkt
=
2245 mVirtualKeys
[vki
].MatchingDeadKeyTable(deadKeyArray
, n
);
2247 dkt
= AddDeadKeyTable(deadKeyArray
, n
);
2249 mVirtualKeys
[vki
].AttachDeadKeyTable(shiftState
, dkt
);
2254 ::SetKeyboardState(originalKbdState
);
2257 if (PR_LOG_TEST(sKeyboardLayoutLogger
, PR_LOG_DEBUG
)) {
2258 static const UINT kExtendedScanCode
[] = { 0x0000, 0xE000 };
2259 static const UINT kMapType
=
2260 IsVistaOrLater() ? MAPVK_VSC_TO_VK_EX
: MAPVK_VSC_TO_VK
;
2261 PR_LOG(sKeyboardLayoutLogger
, PR_LOG_DEBUG
,
2262 ("Logging virtual keycode values for scancode (0x%08X)...",
2263 reinterpret_cast<const uint32_t>(mKeyboardLayout
)));
2264 for (uint32_t i
= 0; i
< ArrayLength(kExtendedScanCode
); i
++) {
2265 for (uint32_t j
= 1; j
<= 0xFF; j
++) {
2266 UINT scanCode
= kExtendedScanCode
[i
] + j
;
2267 UINT virtualKeyCode
=
2268 ::MapVirtualKeyEx(scanCode
, kMapType
, mKeyboardLayout
);
2269 PR_LOG(sKeyboardLayoutLogger
, PR_LOG_DEBUG
,
2270 ("0x%04X, %s", scanCode
, kVirtualKeyName
[virtualKeyCode
]));
2272 // XP and Server 2003 don't support 0xE0 prefix of the scancode.
2273 // Therefore, we don't need to continue on them.
2274 if (!IsVistaOrLater()) {
2279 #endif // #ifdef PR_LOGGING
2283 KeyboardLayout::GetKeyIndex(uint8_t aVirtualKey
)
2285 // Currently these 68 (NS_NUM_OF_KEYS) virtual keys are assumed
2286 // to produce visible representation:
2287 // 0x20 - VK_SPACE ' '
2288 // 0x30..0x39 '0'..'9'
2289 // 0x41..0x5A 'A'..'Z'
2290 // 0x60..0x69 '0'..'9' on numpad
2291 // 0x6A - VK_MULTIPLY '*' on numpad
2292 // 0x6B - VK_ADD '+' on numpad
2293 // 0x6D - VK_SUBTRACT '-' on numpad
2294 // 0x6E - VK_DECIMAL '.' on numpad
2295 // 0x6F - VK_DIVIDE '/' on numpad
2296 // 0x6E - VK_DECIMAL '.'
2297 // 0xBA - VK_OEM_1 ';:' for US
2298 // 0xBB - VK_OEM_PLUS '+' any country
2299 // 0xBC - VK_OEM_COMMA ',' any country
2300 // 0xBD - VK_OEM_MINUS '-' any country
2301 // 0xBE - VK_OEM_PERIOD '.' any country
2302 // 0xBF - VK_OEM_2 '/?' for US
2303 // 0xC0 - VK_OEM_3 '`~' for US
2304 // 0xC1 - VK_ABNT_C1 '/?' for Brazilian
2305 // 0xC2 - VK_ABNT_C2 separator key on numpad (Brazilian or JIS for Mac)
2306 // 0xDB - VK_OEM_4 '[{' for US
2307 // 0xDC - VK_OEM_5 '\|' for US
2308 // 0xDD - VK_OEM_6 ']}' for US
2309 // 0xDE - VK_OEM_7 ''"' for US
2312 // 0xE2 - VK_OEM_102 '\_' for JIS
2316 static const int8_t xlat
[256] =
2318 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
2319 //-----------------------------------------------------------------------
2320 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00
2321 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10
2322 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20
2323 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, // 30
2324 -1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 40
2325 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, -1, -1, // 50
2326 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, -1, 49, 50, 51, // 60
2327 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 70
2328 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
2329 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90
2330 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0
2331 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 53, 54, 55, 56, 57, // B0
2332 58, 59, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0
2333 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 61, 62, 63, 64, 65, // D0
2334 -1, 66, 67, 68, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0
2335 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // F0
2338 return xlat
[aVirtualKey
];
2342 KeyboardLayout::CompareDeadKeyEntries(const void* aArg1
,
2346 const DeadKeyEntry
* arg1
= static_cast<const DeadKeyEntry
*>(aArg1
);
2347 const DeadKeyEntry
* arg2
= static_cast<const DeadKeyEntry
*>(aArg2
);
2349 return arg1
->BaseChar
- arg2
->BaseChar
;
2353 KeyboardLayout::AddDeadKeyTable(const DeadKeyEntry
* aDeadKeyArray
,
2356 DeadKeyTableListEntry
* next
= mDeadKeyTableListHead
;
2358 const size_t bytes
= offsetof(DeadKeyTableListEntry
, data
) +
2359 DeadKeyTable::SizeInBytes(aEntries
);
2360 uint8_t* p
= new uint8_t[bytes
];
2362 mDeadKeyTableListHead
= reinterpret_cast<DeadKeyTableListEntry
*>(p
);
2363 mDeadKeyTableListHead
->next
= next
;
2366 reinterpret_cast<DeadKeyTable
*>(mDeadKeyTableListHead
->data
);
2368 dkt
->Init(aDeadKeyArray
, aEntries
);
2374 KeyboardLayout::ReleaseDeadKeyTables()
2376 while (mDeadKeyTableListHead
) {
2377 uint8_t* p
= reinterpret_cast<uint8_t*>(mDeadKeyTableListHead
);
2378 mDeadKeyTableListHead
= mDeadKeyTableListHead
->next
;
2385 KeyboardLayout::EnsureDeadKeyActive(bool aIsActive
,
2387 const PBYTE aDeadKeyKbdState
)
2391 char16_t dummyChars
[5];
2392 ret
= ::ToUnicodeEx(aDeadKey
, 0, (PBYTE
)aDeadKeyKbdState
,
2393 (LPWSTR
)dummyChars
, ArrayLength(dummyChars
), 0,
2396 // <0 - Dead key state is active. The keyboard driver will wait for next
2398 // 1 - Previous pressed key was a valid base character that produced
2399 // exactly one composite character.
2400 // >1 - Previous pressed key does not produce any composite characters.
2401 // Return dead-key character followed by base character(s).
2402 } while ((ret
< 0) != aIsActive
);
2408 KeyboardLayout::DeactivateDeadKeyState()
2410 if (mActiveDeadKey
< 0) {
2415 memset(kbdState
, 0, sizeof(kbdState
));
2417 VirtualKey::FillKbdState(kbdState
, mDeadKeyShiftState
);
2419 EnsureDeadKeyActive(false, mActiveDeadKey
, kbdState
);
2420 mActiveDeadKey
= -1;
2424 KeyboardLayout::AddDeadKeyEntry(char16_t aBaseChar
,
2425 char16_t aCompositeChar
,
2426 DeadKeyEntry
* aDeadKeyArray
,
2429 for (uint32_t index
= 0; index
< aEntries
; index
++) {
2430 if (aDeadKeyArray
[index
].BaseChar
== aBaseChar
) {
2435 aDeadKeyArray
[aEntries
].BaseChar
= aBaseChar
;
2436 aDeadKeyArray
[aEntries
].CompositeChar
= aCompositeChar
;
2442 KeyboardLayout::GetDeadKeyCombinations(uint8_t aDeadKey
,
2443 const PBYTE aDeadKeyKbdState
,
2444 uint16_t aShiftStatesWithBaseChars
,
2445 DeadKeyEntry
* aDeadKeyArray
,
2446 uint32_t aMaxEntries
)
2448 bool deadKeyActive
= false;
2449 uint32_t entries
= 0;
2451 memset(kbdState
, 0, sizeof(kbdState
));
2453 for (uint32_t shiftState
= 0; shiftState
< 16; shiftState
++) {
2454 if (!(aShiftStatesWithBaseChars
& (1 << shiftState
))) {
2458 VirtualKey::FillKbdState(kbdState
, shiftState
);
2460 for (uint32_t virtualKey
= 0; virtualKey
< 256; virtualKey
++) {
2461 int32_t vki
= GetKeyIndex(virtualKey
);
2462 // Dead-key can pair only with such key that produces exactly one base
2465 mVirtualKeys
[vki
].GetNativeUniChars(shiftState
).mLength
== 1) {
2466 // Ensure dead-key is in active state, when it swallows entered
2467 // character and waits for the next pressed key.
2468 if (!deadKeyActive
) {
2469 deadKeyActive
= EnsureDeadKeyActive(true, aDeadKey
,
2473 // Depending on the character the followed the dead-key, the keyboard
2474 // driver can produce one composite character, or a dead-key character
2475 // followed by a second character.
2476 char16_t compositeChars
[5];
2478 ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)compositeChars
,
2479 ArrayLength(compositeChars
), 0, mKeyboardLayout
);
2482 // This key combination does not produce any characters. The
2483 // dead-key is still in active state.
2486 // Exactly one composite character produced. Now, when dead-key
2487 // is not active, repeat the last character one more time to
2488 // determine the base character.
2489 char16_t baseChars
[5];
2490 ret
= ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)baseChars
,
2491 ArrayLength(baseChars
), 0, mKeyboardLayout
);
2492 NS_ASSERTION(ret
== 1, "One base character expected");
2493 if (ret
== 1 && entries
< aMaxEntries
&&
2494 AddDeadKeyEntry(baseChars
[0], compositeChars
[0],
2495 aDeadKeyArray
, entries
)) {
2498 deadKeyActive
= false;
2502 // 1. Unexpected dead-key. Dead-key chaining is not supported.
2503 // 2. More than one character generated. This is not a valid
2504 // dead-key and base character combination.
2505 deadKeyActive
= false;
2512 if (deadKeyActive
) {
2513 deadKeyActive
= EnsureDeadKeyActive(false, aDeadKey
, aDeadKeyKbdState
);
2516 NS_QuickSort(aDeadKeyArray
, entries
, sizeof(DeadKeyEntry
),
2517 CompareDeadKeyEntries
, nullptr);
2522 KeyboardLayout::ConvertNativeKeyCodeToDOMKeyCode(UINT aNativeKeyCode
) const
2524 // Alphabet or Numeric or Numpad or Function keys
2525 if ((aNativeKeyCode
>= 0x30 && aNativeKeyCode
<= 0x39) ||
2526 (aNativeKeyCode
>= 0x41 && aNativeKeyCode
<= 0x5A) ||
2527 (aNativeKeyCode
>= 0x60 && aNativeKeyCode
<= 0x87)) {
2528 return static_cast<uint32_t>(aNativeKeyCode
);
2530 switch (aNativeKeyCode
) {
2531 // Following keycodes are same as our DOM keycodes
2539 case VK_MENU
: // Alt
2541 case VK_CAPITAL
: // CAPS LOCK
2542 case VK_KANA
: // same as VK_HANGUL
2545 case VK_HANJA
: // same as VK_KANJI
2552 case VK_PRIOR
: // PAGE UP
2553 case VK_NEXT
: // PAGE DOWN
2566 case VK_APPS
: // Context Menu
2569 case VK_SCROLL
: // SCROLL LOCK
2570 case VK_ATTN
: // Attension key of IBM midrange computers, e.g., AS/400
2571 case VK_CRSEL
: // Cursor Selection
2572 case VK_EXSEL
: // Extend Selection
2573 case VK_EREOF
: // Erase EOF key of IBM 3270 keyboard layout
2576 case VK_PA1
: // PA1 key of IBM 3270 keyboard layout
2577 return uint32_t(aNativeKeyCode
);
2582 // Windows key should be mapped to a Win keycode
2583 // They should be able to be distinguished by DOM3 KeyboardEvent.location
2588 case VK_VOLUME_MUTE
:
2589 return NS_VK_VOLUME_MUTE
;
2590 case VK_VOLUME_DOWN
:
2591 return NS_VK_VOLUME_DOWN
;
2593 return NS_VK_VOLUME_UP
;
2595 // Following keycodes are not defined in our DOM keycodes.
2596 case VK_BROWSER_BACK
:
2597 case VK_BROWSER_FORWARD
:
2598 case VK_BROWSER_REFRESH
:
2599 case VK_BROWSER_STOP
:
2600 case VK_BROWSER_SEARCH
:
2601 case VK_BROWSER_FAVORITES
:
2602 case VK_BROWSER_HOME
:
2603 case VK_MEDIA_NEXT_TRACK
:
2605 case VK_MEDIA_PLAY_PAUSE
:
2606 case VK_LAUNCH_MAIL
:
2607 case VK_LAUNCH_MEDIA_SELECT
:
2608 case VK_LAUNCH_APP1
:
2609 case VK_LAUNCH_APP2
:
2612 // Following OEM specific virtual keycodes should pass through DOM keyCode
2613 // for compatibility with the other browsers on Windows.
2615 // Following OEM specific virtual keycodes are defined for Fujitsu/OASYS.
2616 case VK_OEM_FJ_JISHO
:
2617 case VK_OEM_FJ_MASSHOU
:
2618 case VK_OEM_FJ_TOUROKU
:
2619 case VK_OEM_FJ_LOYA
:
2620 case VK_OEM_FJ_ROYA
:
2621 // Not sure what means "ICO".
2625 // Following OEM specific virtual keycodes are defined for Nokia/Ericsson.
2638 case VK_OEM_BACKTAB
:
2639 // VK_OEM_CLEAR is defined as not OEM specific, but let's pass though
2640 // DOM keyCode like other OEM specific virtual keycodes.
2642 return uint32_t(aNativeKeyCode
);
2644 // 0xE1 is an OEM specific virtual keycode. However, the value is already
2645 // used in our DOM keyCode for AltGr on Linux. So, this virtual keycode
2646 // cannot pass through DOM keyCode.
2650 // Following keycodes are OEM keys which are keycodes for non-alphabet and
2651 // non-numeric keys, we should compute each keycode of them from unshifted
2652 // character which is inputted by each key. But if the unshifted character
2653 // is not an ASCII character but shifted character is an ASCII character,
2654 // we should refer it.
2670 NS_ASSERTION(IsPrintableCharKey(aNativeKeyCode
),
2671 "The key must be printable");
2672 ModifierKeyState
modKeyState(0);
2673 UniCharsAndModifiers uniChars
=
2674 GetUniCharsAndModifiers(aNativeKeyCode
, modKeyState
);
2675 if (uniChars
.mLength
!= 1 ||
2676 uniChars
.mChars
[0] < ' ' || uniChars
.mChars
[0] > 0x7F) {
2677 modKeyState
.Set(MODIFIER_SHIFT
);
2678 uniChars
= GetUniCharsAndModifiers(aNativeKeyCode
, modKeyState
);
2679 if (uniChars
.mLength
!= 1 ||
2680 uniChars
.mChars
[0] < ' ' || uniChars
.mChars
[0] > 0x7F) {
2684 return WidgetUtils::ComputeKeyCodeFromChar(uniChars
.mChars
[0]);
2687 // IE sets 0xC2 to the DOM keyCode for VK_ABNT_C2. However, we're already
2688 // using NS_VK_SEPARATOR for the separator key on Mac and Linux. Therefore,
2689 // We should keep consistency between Gecko on all platforms rather than
2690 // with other browsers since a lot of keyCode values are already different
2691 // between browsers.
2693 return NS_VK_SEPARATOR
;
2695 // VK_PROCESSKEY means IME already consumed the key event.
2698 // VK_PACKET is generated by SendInput() API, we don't need to
2699 // care this message as key event.
2702 // If a key is not mapped to a virtual keycode, 0xFF is used.
2704 NS_WARNING("The key is failed to be converted to a virtual keycode");
2708 nsPrintfCString
warning("Unknown virtual keycode (0x%08X), please check the "
2709 "latest MSDN document, there may be some new "
2710 "keycodes we've never known.",
2712 NS_WARNING(warning
.get());
2718 KeyboardLayout::ConvertNativeKeyCodeToKeyNameIndex(uint8_t aVirtualKey
) const
2720 if (IsPrintableCharKey(aVirtualKey
)) {
2721 return KEY_NAME_INDEX_USE_STRING
;
2724 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)
2725 #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)
2726 #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)
2727 #define NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)
2729 switch (aVirtualKey
) {
2731 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
2732 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
2733 case aNativeKey: return aKeyNameIndex;
2735 #include "NativeKeyToDOMKeyName.h"
2737 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
2738 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)
2744 HKL layout
= GetLayout();
2745 WORD langID
= LOWORD(static_cast<HKL
>(layout
));
2746 WORD primaryLangID
= PRIMARYLANGID(langID
);
2748 if (primaryLangID
== LANG_JAPANESE
) {
2749 switch (aVirtualKey
) {
2751 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
2752 #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)\
2753 case aNativeKey: return aKeyNameIndex;
2755 #include "NativeKeyToDOMKeyName.h"
2757 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
2758 #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)
2763 } else if (primaryLangID
== LANG_KOREAN
) {
2764 switch (aVirtualKey
) {
2766 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
2767 #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)\
2768 case aNativeKey: return aKeyNameIndex;
2770 #include "NativeKeyToDOMKeyName.h"
2772 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
2773 #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)
2776 return KEY_NAME_INDEX_Unidentified
;
2780 switch (aVirtualKey
) {
2782 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
2783 #define NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)\
2784 case aNativeKey: return aKeyNameIndex;
2786 #include "NativeKeyToDOMKeyName.h"
2788 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
2789 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
2790 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
2791 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
2794 return KEY_NAME_INDEX_Unidentified
;
2800 KeyboardLayout::ConvertScanCodeToCodeNameIndex(UINT aScanCode
)
2802 switch (aScanCode
) {
2804 #define NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX(aNativeKey, aCodeNameIndex) \
2805 case aNativeKey: return aCodeNameIndex;
2807 #include "NativeKeyToDOMCodeName.h"
2809 #undef NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX
2812 return CODE_NAME_INDEX_UNKNOWN
;
2817 KeyboardLayout::SynthesizeNativeKeyEvent(nsWindowBase
* aWidget
,
2818 int32_t aNativeKeyboardLayout
,
2819 int32_t aNativeKeyCode
,
2820 uint32_t aModifierFlags
,
2821 const nsAString
& aCharacters
,
2822 const nsAString
& aUnmodifiedCharacters
)
2824 UINT keyboardLayoutListCount
= ::GetKeyboardLayoutList(0, nullptr);
2825 NS_ASSERTION(keyboardLayoutListCount
> 0,
2826 "One keyboard layout must be installed at least");
2827 HKL keyboardLayoutListBuff
[50];
2828 HKL
* keyboardLayoutList
=
2829 keyboardLayoutListCount
< 50 ? keyboardLayoutListBuff
:
2830 new HKL
[keyboardLayoutListCount
];
2831 keyboardLayoutListCount
=
2832 ::GetKeyboardLayoutList(keyboardLayoutListCount
, keyboardLayoutList
);
2833 NS_ASSERTION(keyboardLayoutListCount
> 0,
2834 "Failed to get all keyboard layouts installed on the system");
2836 nsPrintfCString
layoutName("%08x", aNativeKeyboardLayout
);
2837 HKL loadedLayout
= LoadKeyboardLayoutA(layoutName
.get(), KLF_NOTELLSHELL
);
2838 if (loadedLayout
== nullptr) {
2839 if (keyboardLayoutListBuff
!= keyboardLayoutList
) {
2840 delete [] keyboardLayoutList
;
2842 return NS_ERROR_NOT_AVAILABLE
;
2845 // Setup clean key state and load desired layout
2846 BYTE originalKbdState
[256];
2847 ::GetKeyboardState(originalKbdState
);
2849 memset(kbdState
, 0, sizeof(kbdState
));
2850 // This changes the state of the keyboard for the current thread only,
2851 // and we'll restore it soon, so this should be OK.
2852 ::SetKeyboardState(kbdState
);
2854 OverrideLayout(loadedLayout
);
2856 uint8_t argumentKeySpecific
= 0;
2857 switch (aNativeKeyCode
) {
2859 aModifierFlags
&= ~(nsIWidget::SHIFT_L
| nsIWidget::SHIFT_R
);
2860 argumentKeySpecific
= VK_LSHIFT
;
2863 aModifierFlags
&= ~nsIWidget::SHIFT_L
;
2864 argumentKeySpecific
= aNativeKeyCode
;
2865 aNativeKeyCode
= VK_SHIFT
;
2868 aModifierFlags
&= ~nsIWidget::SHIFT_R
;
2869 argumentKeySpecific
= aNativeKeyCode
;
2870 aNativeKeyCode
= VK_SHIFT
;
2873 aModifierFlags
&= ~(nsIWidget::CTRL_L
| nsIWidget::CTRL_R
);
2874 argumentKeySpecific
= VK_LCONTROL
;
2877 aModifierFlags
&= ~nsIWidget::CTRL_L
;
2878 argumentKeySpecific
= aNativeKeyCode
;
2879 aNativeKeyCode
= VK_CONTROL
;
2882 aModifierFlags
&= ~nsIWidget::CTRL_R
;
2883 argumentKeySpecific
= aNativeKeyCode
;
2884 aNativeKeyCode
= VK_CONTROL
;
2887 aModifierFlags
&= ~(nsIWidget::ALT_L
| nsIWidget::ALT_R
);
2888 argumentKeySpecific
= VK_LMENU
;
2891 aModifierFlags
&= ~nsIWidget::ALT_L
;
2892 argumentKeySpecific
= aNativeKeyCode
;
2893 aNativeKeyCode
= VK_MENU
;
2896 aModifierFlags
&= ~nsIWidget::ALT_R
;
2897 argumentKeySpecific
= aNativeKeyCode
;
2898 aNativeKeyCode
= VK_MENU
;
2901 aModifierFlags
&= ~nsIWidget::CAPS_LOCK
;
2902 argumentKeySpecific
= VK_CAPITAL
;
2905 aModifierFlags
&= ~nsIWidget::NUM_LOCK
;
2906 argumentKeySpecific
= VK_NUMLOCK
;
2910 nsAutoTArray
<KeyPair
,10> keySequence
;
2911 WinUtils::SetupKeyModifiersSequence(&keySequence
, aModifierFlags
);
2912 NS_ASSERTION(aNativeKeyCode
>= 0 && aNativeKeyCode
< 256,
2913 "Native VK key code out of range");
2914 keySequence
.AppendElement(KeyPair(aNativeKeyCode
, argumentKeySpecific
));
2916 // Simulate the pressing of each modifier key and then the real key
2917 for (uint32_t i
= 0; i
< keySequence
.Length(); ++i
) {
2918 uint8_t key
= keySequence
[i
].mGeneral
;
2919 uint8_t keySpecific
= keySequence
[i
].mSpecific
;
2920 kbdState
[key
] = 0x81; // key is down and toggled on if appropriate
2922 kbdState
[keySpecific
] = 0x81;
2924 ::SetKeyboardState(kbdState
);
2925 ModifierKeyState modKeyState
;
2927 ComputeScanCodeForVirtualKeyCode(keySpecific
? keySpecific
: key
);
2928 LPARAM lParam
= static_cast<LPARAM
>(scanCode
<< 16);
2929 // Add extended key flag to the lParam for right control key and right alt
2931 if (keySpecific
== VK_RCONTROL
|| keySpecific
== VK_RMENU
) {
2932 lParam
|= 0x1000000;
2934 MSG keyDownMsg
= WinUtils::InitMSG(WM_KEYDOWN
, key
, lParam
,
2935 aWidget
->GetWindowHandle());
2936 if (i
== keySequence
.Length() - 1) {
2937 bool makeDeadCharMsg
=
2938 (IsDeadKey(key
, modKeyState
) && aCharacters
.IsEmpty());
2939 nsAutoString
chars(aCharacters
);
2940 if (makeDeadCharMsg
) {
2941 UniCharsAndModifiers deadChars
=
2942 GetUniCharsAndModifiers(key
, modKeyState
);
2943 chars
= deadChars
.ToString();
2944 NS_ASSERTION(chars
.Length() == 1,
2945 "Dead char must be only one character");
2947 if (chars
.IsEmpty()) {
2948 NativeKey
nativeKey(aWidget
, keyDownMsg
, modKeyState
);
2949 nativeKey
.HandleKeyDownMessage();
2951 nsAutoTArray
<NativeKey::FakeCharMsg
, 10> fakeCharMsgs
;
2952 for (uint32_t j
= 0; j
< chars
.Length(); j
++) {
2953 NativeKey::FakeCharMsg
* fakeCharMsg
= fakeCharMsgs
.AppendElement();
2954 fakeCharMsg
->mCharCode
= chars
.CharAt(j
);
2955 fakeCharMsg
->mScanCode
= scanCode
;
2956 fakeCharMsg
->mIsDeadKey
= makeDeadCharMsg
;
2958 NativeKey
nativeKey(aWidget
, keyDownMsg
, modKeyState
, &fakeCharMsgs
);
2960 nativeKey
.HandleKeyDownMessage(&dispatched
);
2961 // If some char messages are not consumed, let's emulate the widget
2962 // receiving the message directly.
2963 for (uint32_t j
= 1; j
< fakeCharMsgs
.Length(); j
++) {
2964 if (fakeCharMsgs
[j
].mConsumed
) {
2967 MSG charMsg
= fakeCharMsgs
[j
].GetCharMsg(aWidget
->GetWindowHandle());
2968 NativeKey
nativeKey(aWidget
, charMsg
, modKeyState
);
2969 nativeKey
.HandleCharMessage(charMsg
);
2973 NativeKey
nativeKey(aWidget
, keyDownMsg
, modKeyState
);
2974 nativeKey
.HandleKeyDownMessage();
2977 for (uint32_t i
= keySequence
.Length(); i
> 0; --i
) {
2978 uint8_t key
= keySequence
[i
- 1].mGeneral
;
2979 uint8_t keySpecific
= keySequence
[i
- 1].mSpecific
;
2980 kbdState
[key
] = 0; // key is up and toggled off if appropriate
2982 kbdState
[keySpecific
] = 0;
2984 ::SetKeyboardState(kbdState
);
2985 ModifierKeyState modKeyState
;
2987 ComputeScanCodeForVirtualKeyCode(keySpecific
? keySpecific
: key
);
2988 LPARAM lParam
= static_cast<LPARAM
>(scanCode
<< 16);
2989 // Add extended key flag to the lParam for right control key and right alt
2991 if (keySpecific
== VK_RCONTROL
|| keySpecific
== VK_RMENU
) {
2992 lParam
|= 0x1000000;
2994 MSG keyUpMsg
= WinUtils::InitMSG(WM_KEYUP
, key
, lParam
,
2995 aWidget
->GetWindowHandle());
2996 NativeKey
nativeKey(aWidget
, keyUpMsg
, modKeyState
);
2997 nativeKey
.HandleKeyUpMessage();
3000 // Restore old key state and layout
3001 ::SetKeyboardState(originalKbdState
);
3004 // Don't unload the layout if it's installed actually.
3005 for (uint32_t i
= 0; i
< keyboardLayoutListCount
; i
++) {
3006 if (keyboardLayoutList
[i
] == loadedLayout
) {
3011 if (keyboardLayoutListBuff
!= keyboardLayoutList
) {
3012 delete [] keyboardLayoutList
;
3015 ::UnloadKeyboardLayout(loadedLayout
);
3020 /*****************************************************************************
3021 * mozilla::widget::DeadKeyTable
3022 *****************************************************************************/
3025 DeadKeyTable::GetCompositeChar(char16_t aBaseChar
) const
3027 // Dead-key table is sorted by BaseChar in ascending order.
3028 // Usually they are too small to use binary search.
3030 for (uint32_t index
= 0; index
< mEntries
; index
++) {
3031 if (mTable
[index
].BaseChar
== aBaseChar
) {
3032 return mTable
[index
].CompositeChar
;
3034 if (mTable
[index
].BaseChar
> aBaseChar
) {
3042 /*****************************************************************************
3043 * mozilla::widget::RedirectedKeyDownMessage
3044 *****************************************************************************/
3046 MSG
RedirectedKeyDownMessageManager::sRedirectedKeyDownMsg
;
3047 bool RedirectedKeyDownMessageManager::sDefaultPreventedOfRedirectedMsg
= false;
3051 RedirectedKeyDownMessageManager::IsRedirectedMessage(const MSG
& aMsg
)
3053 return (aMsg
.message
== WM_KEYDOWN
|| aMsg
.message
== WM_SYSKEYDOWN
) &&
3054 (sRedirectedKeyDownMsg
.message
== aMsg
.message
&&
3055 WinUtils::GetScanCode(sRedirectedKeyDownMsg
.lParam
) ==
3056 WinUtils::GetScanCode(aMsg
.lParam
));
3061 RedirectedKeyDownMessageManager::RemoveNextCharMessage(HWND aWnd
)
3064 if (WinUtils::PeekMessage(&msg
, aWnd
, WM_KEYFIRST
, WM_KEYLAST
,
3065 PM_NOREMOVE
| PM_NOYIELD
) &&
3066 (msg
.message
== WM_CHAR
|| msg
.message
== WM_SYSCHAR
)) {
3067 WinUtils::PeekMessage(&msg
, aWnd
, msg
.message
, msg
.message
,
3068 PM_REMOVE
| PM_NOYIELD
);
3072 } // namespace widget
3073 } // namespace mozilla