Bumping manifests a=b2g-bump
[gecko.git] / widget / windows / KeyboardLayout.cpp
blobe248235b9c7c6c88103fd19871161be81ba56dc8
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/. */
6 #ifdef MOZ_LOGGING
7 #define FORCE_PR_LOG /* Allow logging in the release build */
8 #endif // MOZ_LOGGING
9 #include "prlog.h"
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"
20 #include "nsMemory.h"
21 #include "nsToolkit.h"
22 #include "nsQuickSort.h"
23 #include "nsAlgorithm.h"
24 #include "nsUnicharUtils.h"
25 #include "WidgetUtils.h"
26 #include "WinUtils.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"
36 #endif
38 #include "npapi.h"
40 #include <windows.h>
41 #include <winuser.h>
42 #include <algorithm>
44 #ifndef WINABLEAPI
45 #include <winable.h>
46 #endif
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)
51 #endif
53 namespace mozilla {
54 namespace widget {
56 #ifdef PR_LOGGING
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",
104 "VK_OEM_FJ_MASSHOU",
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",
118 "VK_LAUNCH_APP2",
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;
153 struct DeadKeyEntry
155 char16_t BaseChar;
156 char16_t CompositeChar;
160 class DeadKeyTable
162 friend class KeyboardLayout;
164 uint16_t mEntries;
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)
171 mEntries = aEntries;
172 memcpy(mTable, aDeadKeyArray, aEntries * sizeof(DeadKeyEntry));
175 static uint32_t SizeInBytes(uint32_t aEntries)
177 return offsetof(DeadKeyTable, mTable) + aEntries * sizeof(DeadKeyEntry);
180 public:
181 uint32_t Entries() const
183 return mEntries;
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()
203 Update();
206 ModifierKeyState::ModifierKeyState(bool aIsShiftDown,
207 bool aIsControlDown,
208 bool aIsAltDown)
210 Update();
211 Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT | MODIFIER_ALTGRAPH);
212 Modifiers modifiers = 0;
213 if (aIsShiftDown) {
214 modifiers |= MODIFIER_SHIFT;
216 if (aIsControlDown) {
217 modifiers |= MODIFIER_CONTROL;
219 if (aIsAltDown) {
220 modifiers |= MODIFIER_ALT;
222 if (modifiers) {
223 Set(modifiers);
227 ModifierKeyState::ModifierKeyState(Modifiers aModifiers) :
228 mModifiers(aModifiers)
230 EnsureAltGr();
233 void
234 ModifierKeyState::Update()
236 mModifiers = 0;
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;
259 EnsureAltGr();
262 void
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.
271 void
272 ModifierKeyState::Set(Modifiers aAddingModifiers)
274 mModifiers |= aAddingModifiers;
275 EnsureAltGr();
278 void
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);
290 break;
291 default:
292 break;
296 void
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.
307 return;
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;
329 bool
330 ModifierKeyState::IsShift() const
332 return (mModifiers & MODIFIER_SHIFT) != 0;
335 bool
336 ModifierKeyState::IsControl() const
338 return (mModifiers & MODIFIER_CONTROL) != 0;
341 bool
342 ModifierKeyState::IsAlt() const
344 return (mModifiers & MODIFIER_ALT) != 0;
347 bool
348 ModifierKeyState::IsAltGr() const
350 return IsControl() && IsAlt();
353 bool
354 ModifierKeyState::IsWin() const
356 return (mModifiers & MODIFIER_OS) != 0;
359 bool
360 ModifierKeyState::IsCapsLocked() const
362 return (mModifiers & MODIFIER_CAPSLOCK) != 0;
365 bool
366 ModifierKeyState::IsNumLocked() const
368 return (mModifiers & MODIFIER_NUMLOCK) != 0;
371 bool
372 ModifierKeyState::IsScrollLocked() const
374 return (mModifiers & MODIFIER_SCROLLLOCK) != 0;
377 Modifiers
378 ModifierKeyState::GetModifiers() const
380 return mModifiers;
383 void
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.
390 if (IsAltGr()) {
391 mModifiers |= MODIFIER_ALTGRAPH;
395 /*****************************************************************************
396 * mozilla::widget::UniCharsAndModifiers
397 *****************************************************************************/
399 void
400 UniCharsAndModifiers::Append(char16_t aUniChar, Modifiers aModifiers)
402 MOZ_ASSERT(mLength < 5);
403 mChars[mLength] = aUniChar;
404 mModifiers[mLength] = aModifiers;
405 mLength++;
408 void
409 UniCharsAndModifiers::FillModifiers(Modifiers aModifiers)
411 for (uint32_t i = 0; i < mLength; i++) {
412 mModifiers[i] = aModifiers;
416 bool
417 UniCharsAndModifiers::UniCharsEqual(const UniCharsAndModifiers& aOther) const
419 if (mLength != aOther.mLength) {
420 return false;
422 return !memcmp(mChars, aOther.mChars, mLength * sizeof(char16_t));
425 bool
426 UniCharsAndModifiers::UniCharsCaseInsensitiveEqual(
427 const UniCharsAndModifiers& aOther) const
429 if (mLength != aOther.mLength) {
430 return false;
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;
446 return *this;
449 UniCharsAndModifiers
450 UniCharsAndModifiers::operator+(const UniCharsAndModifiers& aOther) const
452 UniCharsAndModifiers result(*this);
453 result += aOther;
454 return result;
457 /*****************************************************************************
458 * mozilla::widget::VirtualKey
459 *****************************************************************************/
461 // static
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) {
473 state |= STATE_ALT;
475 if (aModifiers & MODIFIER_CAPSLOCK) {
476 state |= STATE_CAPSLOCK;
478 return state;
481 // static
482 Modifiers
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;
502 return modifiers;
505 inline char16_t
506 VirtualKey::GetCompositeChar(ShiftState aShiftState, char16_t aBaseChar) const
508 return mShiftStates[aShiftState].DeadKey.Table->GetCompositeChar(aBaseChar);
511 const DeadKeyTable*
512 VirtualKey::MatchingDeadKeyTable(const DeadKeyEntry* aDeadKeyArray,
513 uint32_t aEntries) const
515 if (!mIsDeadKey) {
516 return nullptr;
519 for (ShiftState shiftState = 0; shiftState < 16; shiftState++) {
520 if (!IsDeadKey(shiftState)) {
521 continue;
523 const DeadKeyTable* dkt = mShiftStates[shiftState].DeadKey.Table;
524 if (dkt && dkt->IsEqual(aDeadKeyArray, aEntries)) {
525 return dkt;
529 return nullptr;
532 void
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;
553 void
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;
564 UniCharsAndModifiers
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)) {
571 return result;
574 if (!result.mLength) {
575 result = GetNativeUniChars(aShiftState & ~STATE_ALT_CONTROL);
576 result.FillModifiers(ShiftStateToModifiers(aShiftState));
577 return result;
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);
589 return result;
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);
601 return result;
605 UniCharsAndModifiers
606 VirtualKey::GetNativeUniChars(ShiftState aShiftState) const
608 #ifdef DEBUG
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());
615 #endif
617 UniCharsAndModifiers result;
618 Modifiers modifiers = ShiftStateToModifiers(aShiftState);
619 if (IsDeadKey(aShiftState)) {
620 result.Append(mShiftStates[aShiftState].DeadKey.DeadChar, modifiers);
621 return result;
624 uint32_t index;
625 uint32_t len = ArrayLength(mShiftStates[aShiftState].Normal.Chars);
626 for (index = 0;
627 index < len && mShiftStates[aShiftState].Normal.Chars[index]; index++) {
628 result.Append(mShiftStates[aShiftState].Normal.Chars[index], modifiers);
630 return result;
633 // static
634 void
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;
642 } else {
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;
650 } else {
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;
658 } else {
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;
666 } else {
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)
684 MOZ_ASSERT(aWidget);
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) {
690 case WM_KEYDOWN:
691 case WM_SYSKEYDOWN:
692 case WM_KEYUP:
693 case WM_SYSKEYUP: {
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
696 // is 0.
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.
700 if (!mScanCode) {
701 uint16_t scanCodeEx = ComputeScanCodeExFromVirtualKeyCode(mMsg.wParam);
702 if (scanCodeEx) {
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
709 // keycode.
710 if (mMsg.wParam == VK_PROCESSKEY) {
711 mOriginalVirtualKeyCode =
712 static_cast<uint8_t>(::ImmGetVirtualKey(mMsg.hwnd));
713 } else {
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) {
723 case VK_SHIFT:
724 case VK_CONTROL:
725 case VK_MENU:
726 isLeftRightDistinguishedKey = true;
727 break;
728 case VK_LSHIFT:
729 case VK_RSHIFT:
730 mVirtualKeyCode = mOriginalVirtualKeyCode;
731 mOriginalVirtualKeyCode = VK_SHIFT;
732 isLeftRightDistinguishedKey = true;
733 break;
734 case VK_LCONTROL:
735 case VK_RCONTROL:
736 mVirtualKeyCode = mOriginalVirtualKeyCode;
737 mOriginalVirtualKeyCode = VK_CONTROL;
738 isLeftRightDistinguishedKey = true;
739 break;
740 case VK_LMENU:
741 case VK_RMENU:
742 mVirtualKeyCode = mOriginalVirtualKeyCode;
743 mOriginalVirtualKeyCode = VK_MENU;
744 isLeftRightDistinguishedKey = true;
745 break;
748 // If virtual keycode (left-right distinguished keycode) is already
749 // computed, we don't need to do anymore.
750 if (mVirtualKeyCode) {
751 break;
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) {
760 break;
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) {
771 case VK_CONTROL:
772 mVirtualKeyCode = VK_RCONTROL;
773 break;
774 case VK_MENU:
775 mVirtualKeyCode = VK_RMENU;
776 break;
777 case VK_SHIFT:
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;
781 break;
782 default:
783 MOZ_CRASH("Unsupported mOriginalVirtualKeyCode");
785 break;
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) {
798 case VK_CONTROL:
799 if (mVirtualKeyCode != VK_LCONTROL &&
800 mVirtualKeyCode != VK_RCONTROL) {
801 mVirtualKeyCode = mIsExtended ? VK_RCONTROL : VK_LCONTROL;
803 break;
804 case VK_MENU:
805 if (mVirtualKeyCode != VK_LMENU && mVirtualKeyCode != VK_RMENU) {
806 mVirtualKeyCode = mIsExtended ? VK_RMENU : VK_LMENU;
808 break;
809 case VK_SHIFT:
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;
815 break;
816 default:
817 MOZ_CRASH("Unsupported mOriginalVirtualKeyCode");
819 break;
821 case WM_CHAR:
822 case WM_UNICHAR:
823 case WM_SYSCHAR:
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()) {
831 break;
833 mVirtualKeyCode = mOriginalVirtualKeyCode =
834 ComputeVirtualKeyCodeFromScanCodeEx();
835 NS_ASSERTION(mVirtualKeyCode, "Failed to compute virtual keycode");
836 break;
837 default:
838 MOZ_CRASH("Unsupported message");
841 if (!mVirtualKeyCode) {
842 mVirtualKeyCode = mOriginalVirtualKeyCode;
845 mDOMKeyCode =
846 keyboardLayout->ConvertNativeKeyCodeToDOMKeyCode(mOriginalVirtualKeyCode);
847 mKeyNameIndex =
848 keyboardLayout->ConvertNativeKeyCodeToKeyNameIndex(mOriginalVirtualKeyCode);
849 mCodeNameIndex =
850 KeyboardLayout::ConvertScanCodeToCodeNameIndex(
851 GetScanCodeWithExtendedFlag());
853 keyboardLayout->InitNativeKey(*this, mModKeyState);
855 mIsDeadKey =
856 (IsFollowedByDeadCharMessage() ||
857 keyboardLayout->IsDeadKey(mOriginalVirtualKeyCode, mModKeyState));
858 mIsPrintableKey = KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode);
861 bool
862 NativeKey::IsFollowedByDeadCharMessage() const
864 MSG nextMsg;
865 if (mFakeCharMsgs) {
866 nextMsg = mFakeCharMsgs->ElementAt(0).GetCharMsg(mMsg.hwnd);
867 } else {
868 if (!WinUtils::PeekMessage(&nextMsg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST,
869 PM_NOREMOVE | PM_NOYIELD)) {
870 return false;
873 return IsDeadCharMessage(nextMsg);
876 bool
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;
910 UINT
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()) {
919 return mScanCode;
921 return (0xE000 | mScanCode);
924 uint32_t
925 NativeKey::GetKeyLocation() const
927 switch (mVirtualKeyCode) {
928 case VK_LSHIFT:
929 case VK_LCONTROL:
930 case VK_LMENU:
931 case VK_LWIN:
932 return nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT;
934 case VK_RSHIFT:
935 case VK_RCONTROL:
936 case VK_RMENU:
937 case VK_RWIN:
938 return nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT;
940 case VK_RETURN:
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;
945 case VK_INSERT:
946 case VK_DELETE:
947 case VK_END:
948 case VK_DOWN:
949 case VK_NEXT:
950 case VK_LEFT:
951 case VK_CLEAR:
952 case VK_RIGHT:
953 case VK_HOME:
954 case VK_UP:
955 case VK_PRIOR:
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.
961 case VK_NUMPAD0:
962 case VK_NUMPAD1:
963 case VK_NUMPAD2:
964 case VK_NUMPAD3:
965 case VK_NUMPAD4:
966 case VK_NUMPAD5:
967 case VK_NUMPAD6:
968 case VK_NUMPAD7:
969 case VK_NUMPAD8:
970 case VK_NUMPAD9:
971 case VK_DECIMAL:
972 case VK_DIVIDE:
973 case VK_MULTIPLY:
974 case VK_SUBTRACT:
975 case VK_ADD:
976 // Separator key of Brazilian keyboard or JIS keyboard for Mac
977 case VK_ABNT_C2:
978 return nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;
980 case VK_SHIFT:
981 case VK_CONTROL:
982 case VK_MENU:
983 NS_WARNING("Failed to decide the key location?");
985 default:
986 return nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD;
990 bool
991 NativeKey::CanComputeVirtualKeyCodeFromScanCode() const
993 // Vista or later supports ScanCodeEx.
994 if (IsVistaOrLater()) {
995 return true;
997 // Otherwise, MapVirtualKeyEx() can compute virtual keycode only with
998 // non-extended key.
999 return !mIsExtended;
1002 uint8_t
1003 NativeKey::ComputeVirtualKeyCodeFromScanCode() const
1005 return static_cast<uint8_t>(
1006 ::MapVirtualKeyEx(mScanCode, MAPVK_VSC_TO_VK, mKeyboardLayout));
1009 uint8_t
1010 NativeKey::ComputeVirtualKeyCodeFromScanCodeEx() const
1012 if (NS_WARN_IF(!CanComputeVirtualKeyCodeFromScanCode())) {
1013 return 0;
1015 return static_cast<uint8_t>(
1016 ::MapVirtualKeyEx(GetScanCodeWithExtendedFlag(), MAPVK_VSC_TO_VK_EX,
1017 mKeyboardLayout));
1020 uint16_t
1021 NativeKey::ComputeScanCodeExFromVirtualKeyCode(UINT aVirtualKeyCode) const
1023 return static_cast<uint16_t>(
1024 ::MapVirtualKeyEx(aVirtualKeyCode,
1025 IsVistaOrLater() ? MAPVK_VK_TO_VSC_EX :
1026 MAPVK_VK_TO_VSC,
1027 mKeyboardLayout));
1030 char16_t
1031 NativeKey::ComputeUnicharFromScanCode() const
1033 return static_cast<char16_t>(
1034 ::MapVirtualKeyEx(ComputeVirtualKeyCodeFromScanCode(),
1035 MAPVK_VK_TO_CHAR, mKeyboardLayout));
1038 void
1039 NativeKey::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const
1041 InitKeyEvent(aKeyEvent, mModKeyState);
1044 void
1045 NativeKey::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent,
1046 const ModifierKeyState& aModKeyState) const
1048 nsIntPoint point(0, 0);
1049 mWidget->InitEvent(aKeyEvent, &point);
1051 switch (aKeyEvent.message) {
1052 case NS_KEY_DOWN:
1053 aKeyEvent.keyCode = mDOMKeyCode;
1054 // Unique id for this keydown event and its associated keypress.
1055 sUniqueKeyEventId++;
1056 aKeyEvent.mUniqueId = sUniqueKeyEventId;
1057 break;
1058 case NS_KEY_UP:
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
1064 // state of IME.
1065 aKeyEvent.mFlags.mDefaultPrevented =
1066 (mOriginalVirtualKeyCode == VK_MENU && mMsg.message != WM_SYSKEYUP);
1067 break;
1068 case NS_KEY_PRESS:
1069 aKeyEvent.mUniqueId = sUniqueKeyEventId;
1070 break;
1071 default:
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);
1086 bool
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());
1108 bool
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) {
1123 return false;
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()) {
1135 return true;
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);
1152 INPUT keyinput;
1153 keyinput.type = INPUT_KEYBOARD;
1154 keyinput.ki.wVk = mOriginalVirtualKeyCode;
1155 keyinput.ki.wScan = mScanCode;
1156 keyinput.ki.dwFlags = KEYEVENTF_SCANCODE;
1157 if (mIsExtended) {
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)
1169 // WM_KEYDOWN.
1170 return true;
1172 } else {
1173 defaultPrevented = RedirectedKeyDownMessageManager::DefaultPrevented();
1174 // If this is redirected keydown message, we have dispatched the keydown
1175 // event already.
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) {
1190 case NS_VK_SHIFT:
1191 case NS_VK_CONTROL:
1192 case NS_VK_ALT:
1193 case NS_VK_CAPS_LOCK:
1194 case NS_VK_NUM_LOCK:
1195 case NS_VK_SCROLL_LOCK:
1196 case NS_VK_WIN:
1197 return defaultPrevented;
1200 if (defaultPrevented) {
1201 DispatchPluginEventsAndDiscardsCharMessages();
1202 return true;
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) {
1219 return false;
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.
1228 // See bug 314130.
1229 return false;
1232 if (mIsDeadKey) {
1233 return false;
1236 return DispatchKeyPressEventsWithKeyboardLayout();
1239 bool
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) {
1253 return false;
1256 // Bug 818235: Ignore Ctrl+Enter.
1257 if (!mModKeyState.IsAlt() && mModKeyState.IsControl() &&
1258 mVirtualKeyCode == VK_RETURN) {
1259 return false;
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);
1277 } else {
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
1283 // flags.
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.
1299 char16_t uniChar;
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())) {
1312 uniChar = 0;
1313 } else {
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);
1351 bool
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) {
1363 return false;
1366 WidgetKeyboardEvent keyupEvent(true, NS_KEY_UP, mWidget);
1367 InitKeyEvent(keyupEvent, mModKeyState);
1368 if (aEventDispatched) {
1369 *aEventDispatched = true;
1371 return DispatchKeyEvent(keyupEvent, &mMsg);
1374 bool
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) {
1382 return true;
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()) {
1389 return false;
1392 // If the key event causes dead key event, we don't need to dispatch keypress
1393 // event.
1394 if (mIsDeadKey && mCommittedCharsAndModifiers.IsEmpty()) {
1395 return false;
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
1405 static nsCString
1406 GetResultOfInSendMessageEx()
1408 DWORD ret = ::InSendMessageEx(nullptr);
1409 if (!ret) {
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()) {
1418 result += " | ";
1420 result += "ISMEX_NOTIFY";
1422 if (ret & ISMEX_REPLIED) {
1423 if (!result.IsEmpty()) {
1424 result += " | ";
1426 result += "ISMEX_REPLIED";
1428 if (ret & ISMEX_SEND) {
1429 if (!result.IsEmpty()) {
1430 result += " | ";
1432 result += "ISMEX_SEND";
1434 return result;
1437 static const char*
1438 GetMessageName(UINT aMessage)
1440 switch (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
1458 bool
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;
1466 return
1467 aCharMsg1.message == aCharMsg2.message &&
1468 aCharMsg1.wParam == aCharMsg2.wParam &&
1469 (aCharMsg1.lParam & ~kScanCodeMask) == (aCharMsg2.lParam & ~kScanCodeMask);
1472 bool
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) {
1482 return false;
1484 MSG charMsg = fakeCharMsg.GetCharMsg(mMsg.hwnd);
1485 fakeCharMsg.mConsumed = true;
1486 if (!IsCharMessage(charMsg)) {
1487 return false;
1489 aCharMsg = charMsg;
1490 return true;
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.
1498 MSG nextKeyMsg;
1499 if (!WinUtils::PeekMessage(&nextKeyMsg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST,
1500 PM_NOREMOVE | PM_NOYIELD) ||
1501 !IsCharMessage(nextKeyMsg)) {
1502 return false;
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.
1516 doCrash = true;
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)) {
1522 return true;
1524 if (MayBeSameCharMessage(nextKeyMsgInAllWindows, nextKeyMsg)) {
1525 // The char message is redirected to different window created by our
1526 // thread.
1527 if (nextKeyMsgInAllWindows.hwnd != mMsg.hwnd) {
1528 aCharMsg = nextKeyMsgInAllWindows;
1529 return true;
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.
1538 doCrash = false;
1543 if (doCrash) {
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, "
1553 "time=%d, ",
1554 GetMessageName(mMsg.message),
1555 mMsg.message, mMsg.wParam, mMsg.lParam,
1556 nextKeyMsg.hwnd,
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);
1568 MSG nextMsg;
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, "
1573 "time=%d",
1574 GetMessageName(nextMsg.message),
1575 nextMsg.message, nextMsg.wParam, nextMsg.lParam,
1576 nextMsg.hwnd, nextMsg.time);
1577 CrashReporter::AppendAppNotesToCrashReport(info);
1578 } else {
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) {
1588 continue;
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) {
1595 return false;
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);
1632 } else {
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);
1650 } else {
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;
1659 return true;
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,
1672 nextKeyMsg.lParam);
1673 CrashReporter::AppendAppNotesToCrashReport(info);
1674 #endif // #ifdef MOZ_CRASHREPORTER
1675 MOZ_CRASH("We lost the following char message");
1676 return false;
1679 bool
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;
1689 MSG msg;
1690 while (GetFollowingCharMessage(msg)) {
1691 if (msg.message == WM_NULL) {
1692 continue;
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) {
1698 break;
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()) {
1704 return true;
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");
1716 continue;
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");
1726 return false;
1729 bool
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);
1751 unshiftedChars =
1752 keyboardLayout->GetUniCharsAndModifiers(mVirtualKeyCode, capsLockState);
1753 capsLockState.Set(MODIFIER_SHIFT);
1754 shiftedChars =
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,
1764 &shiftedLatinChar);
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()) {
1795 uint32_t ch =
1796 mModKeyState.IsShift() ? shiftedLatinChar : unshiftedLatinChar;
1797 if (ch &&
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()) {
1886 return true;
1890 return defaultPrevented;
1893 bool
1894 NativeKey::DispatchKeyPressEventForFollowingCharMessage(
1895 const MSG& aCharMsg) const
1897 MOZ_ASSERT(IsKeyDownMessage());
1899 if (mFakeCharMsgs) {
1900 if (IsDeadCharMessage(aCharMsg)) {
1901 return false;
1903 #ifdef DEBUG
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 }, "
1908 "wParam=0x%04X",
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());
1918 NS_ABORT();
1919 } else if (mCommittedCharsAndModifiers.mChars[0] != aCharMsg.wParam) {
1920 log.Insert("character mismatch: ", 0);
1921 NS_ERROR(log.get());
1922 NS_ABORT();
1925 #endif // #ifdef DEBUG
1926 return HandleCharMessage(aCharMsg);
1929 if (IsDeadCharMessage(aCharMsg)) {
1930 if (!mWidget->PluginHasFocus()) {
1931 return false;
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;
1953 #ifdef PR_LOGGING
1954 PRLogModuleInfo* sKeyboardLayoutLogger = nullptr;
1955 #endif // #ifdef PR_LOGGING
1957 // static
1958 KeyboardLayout*
1959 KeyboardLayout::GetInstance()
1961 if (!sInstance) {
1962 #ifdef PR_LOGGING
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();
1973 return sInstance;
1976 // static
1977 void
1978 KeyboardLayout::Shutdown()
1980 delete sInstance;
1981 sInstance = nullptr;
1982 NS_IF_RELEASE(sIdleService);
1985 // static
1986 void
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();
2006 bool
2007 KeyboardLayout::IsPrintableCharKey(uint8_t aVirtualKey)
2009 return GetKeyIndex(aVirtualKey) >= 0;
2012 WORD
2013 KeyboardLayout::ComputeScanCodeForVirtualKeyCode(uint8_t aVirtualKeyCode) const
2015 return static_cast<WORD>(
2016 ::MapVirtualKeyEx(aVirtualKeyCode, MAPVK_VK_TO_VSC, GetLayout()));
2019 bool
2020 KeyboardLayout::IsDeadKey(uint8_t aVirtualKey,
2021 const ModifierKeyState& aModKeyState) const
2023 int32_t virtualKeyIndex = GetKeyIndex(aVirtualKey);
2024 if (virtualKeyIndex < 0) {
2025 return false;
2028 return mVirtualKeys[virtualKeyIndex].IsDeadKey(
2029 VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()));
2032 void
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
2045 // dead-key state.
2046 return;
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.
2060 if (isKeyDown) {
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]);
2071 return;
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);
2083 return;
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="
2091 "0x%02X)",
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;
2107 if (isKeyDown) {
2108 DeactivateDeadKeyState();
2110 return;
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;
2118 return;
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]);
2132 if (isKeyDown) {
2133 DeactivateDeadKeyState();
2135 return;
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;
2144 if (isKeyDown) {
2145 DeactivateDeadKeyState();
2148 return;
2151 UniCharsAndModifiers
2152 KeyboardLayout::GetUniCharsAndModifiers(
2153 uint8_t aVirtualKey,
2154 const ModifierKeyState& aModKeyState) const
2156 UniCharsAndModifiers result;
2157 int32_t key = GetKeyIndex(aVirtualKey);
2158 if (key < 0) {
2159 return result;
2161 return mVirtualKeys[key].
2162 GetUniChars(VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()));
2165 void
2166 KeyboardLayout::LoadLayout(HKL aLayout)
2168 mIsPendingToRestoreKeyboardLayout = false;
2170 if (mKeyboardLayout == aLayout) {
2171 return;
2174 mKeyboardLayout = aLayout;
2176 BYTE kbdState[256];
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
2183 // characters.
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);
2199 if (vki < 0) {
2200 continue;
2202 NS_ASSERTION(uint32_t(vki) < ArrayLength(mVirtualKeys), "invalid index");
2203 char16_t uniChars[5];
2204 int32_t ret =
2205 ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)uniChars,
2206 ArrayLength(uniChars), 0, mKeyboardLayout);
2207 // dead-key
2208 if (ret < 0) {
2209 shiftStatesWithDeadKeys |= (1 << shiftState);
2210 // Repeat dead-key to deactivate it and get its character
2211 // representation.
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]);
2217 } else {
2218 if (ret == 1) {
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))) {
2231 continue;
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,
2242 deadKeyArray,
2243 ArrayLength(deadKeyArray));
2244 const DeadKeyTable* dkt =
2245 mVirtualKeys[vki].MatchingDeadKeyTable(deadKeyArray, n);
2246 if (!dkt) {
2247 dkt = AddDeadKeyTable(deadKeyArray, n);
2249 mVirtualKeys[vki].AttachDeadKeyTable(shiftState, dkt);
2254 ::SetKeyboardState(originalKbdState);
2256 #ifdef PR_LOGGING
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()) {
2275 break;
2279 #endif // #ifdef PR_LOGGING
2282 inline int32_t
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
2310 // 0xDF - VK_OEM_8
2311 // 0xE1 - no name
2312 // 0xE2 - VK_OEM_102 '\_' for JIS
2313 // 0xE3 - no name
2314 // 0xE4 - no name
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,
2343 const void* aArg2,
2344 void*)
2346 const DeadKeyEntry* arg1 = static_cast<const DeadKeyEntry*>(aArg1);
2347 const DeadKeyEntry* arg2 = static_cast<const DeadKeyEntry*>(aArg2);
2349 return arg1->BaseChar - arg2->BaseChar;
2352 const DeadKeyTable*
2353 KeyboardLayout::AddDeadKeyTable(const DeadKeyEntry* aDeadKeyArray,
2354 uint32_t aEntries)
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;
2365 DeadKeyTable* dkt =
2366 reinterpret_cast<DeadKeyTable*>(mDeadKeyTableListHead->data);
2368 dkt->Init(aDeadKeyArray, aEntries);
2370 return dkt;
2373 void
2374 KeyboardLayout::ReleaseDeadKeyTables()
2376 while (mDeadKeyTableListHead) {
2377 uint8_t* p = reinterpret_cast<uint8_t*>(mDeadKeyTableListHead);
2378 mDeadKeyTableListHead = mDeadKeyTableListHead->next;
2380 delete [] p;
2384 bool
2385 KeyboardLayout::EnsureDeadKeyActive(bool aIsActive,
2386 uint8_t aDeadKey,
2387 const PBYTE aDeadKeyKbdState)
2389 int32_t ret;
2390 do {
2391 char16_t dummyChars[5];
2392 ret = ::ToUnicodeEx(aDeadKey, 0, (PBYTE)aDeadKeyKbdState,
2393 (LPWSTR)dummyChars, ArrayLength(dummyChars), 0,
2394 mKeyboardLayout);
2395 // returned values:
2396 // <0 - Dead key state is active. The keyboard driver will wait for next
2397 // character.
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);
2404 return (ret < 0);
2407 void
2408 KeyboardLayout::DeactivateDeadKeyState()
2410 if (mActiveDeadKey < 0) {
2411 return;
2414 BYTE kbdState[256];
2415 memset(kbdState, 0, sizeof(kbdState));
2417 VirtualKey::FillKbdState(kbdState, mDeadKeyShiftState);
2419 EnsureDeadKeyActive(false, mActiveDeadKey, kbdState);
2420 mActiveDeadKey = -1;
2423 bool
2424 KeyboardLayout::AddDeadKeyEntry(char16_t aBaseChar,
2425 char16_t aCompositeChar,
2426 DeadKeyEntry* aDeadKeyArray,
2427 uint32_t aEntries)
2429 for (uint32_t index = 0; index < aEntries; index++) {
2430 if (aDeadKeyArray[index].BaseChar == aBaseChar) {
2431 return false;
2435 aDeadKeyArray[aEntries].BaseChar = aBaseChar;
2436 aDeadKeyArray[aEntries].CompositeChar = aCompositeChar;
2438 return true;
2441 uint32_t
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;
2450 BYTE kbdState[256];
2451 memset(kbdState, 0, sizeof(kbdState));
2453 for (uint32_t shiftState = 0; shiftState < 16; shiftState++) {
2454 if (!(aShiftStatesWithBaseChars & (1 << shiftState))) {
2455 continue;
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
2463 // character.
2464 if (vki >= 0 &&
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,
2470 aDeadKeyKbdState);
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];
2477 int32_t ret =
2478 ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)compositeChars,
2479 ArrayLength(compositeChars), 0, mKeyboardLayout);
2480 switch (ret) {
2481 case 0:
2482 // This key combination does not produce any characters. The
2483 // dead-key is still in active state.
2484 break;
2485 case 1: {
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)) {
2496 entries++;
2498 deadKeyActive = false;
2499 break;
2501 default:
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;
2506 break;
2512 if (deadKeyActive) {
2513 deadKeyActive = EnsureDeadKeyActive(false, aDeadKey, aDeadKeyKbdState);
2516 NS_QuickSort(aDeadKeyArray, entries, sizeof(DeadKeyEntry),
2517 CompareDeadKeyEntries, nullptr);
2518 return entries;
2521 uint32_t
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
2532 case VK_CANCEL:
2533 case VK_BACK:
2534 case VK_TAB:
2535 case VK_CLEAR:
2536 case VK_RETURN:
2537 case VK_SHIFT:
2538 case VK_CONTROL:
2539 case VK_MENU: // Alt
2540 case VK_PAUSE:
2541 case VK_CAPITAL: // CAPS LOCK
2542 case VK_KANA: // same as VK_HANGUL
2543 case VK_JUNJA:
2544 case VK_FINAL:
2545 case VK_HANJA: // same as VK_KANJI
2546 case VK_ESCAPE:
2547 case VK_CONVERT:
2548 case VK_NONCONVERT:
2549 case VK_ACCEPT:
2550 case VK_MODECHANGE:
2551 case VK_SPACE:
2552 case VK_PRIOR: // PAGE UP
2553 case VK_NEXT: // PAGE DOWN
2554 case VK_END:
2555 case VK_HOME:
2556 case VK_LEFT:
2557 case VK_UP:
2558 case VK_RIGHT:
2559 case VK_DOWN:
2560 case VK_SELECT:
2561 case VK_PRINT:
2562 case VK_EXECUTE:
2563 case VK_SNAPSHOT:
2564 case VK_INSERT:
2565 case VK_DELETE:
2566 case VK_APPS: // Context Menu
2567 case VK_SLEEP:
2568 case VK_NUMLOCK:
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
2574 case VK_PLAY:
2575 case VK_ZOOM:
2576 case VK_PA1: // PA1 key of IBM 3270 keyboard layout
2577 return uint32_t(aNativeKeyCode);
2579 case VK_HELP:
2580 return NS_VK_HELP;
2582 // Windows key should be mapped to a Win keycode
2583 // They should be able to be distinguished by DOM3 KeyboardEvent.location
2584 case VK_LWIN:
2585 case VK_RWIN:
2586 return NS_VK_WIN;
2588 case VK_VOLUME_MUTE:
2589 return NS_VK_VOLUME_MUTE;
2590 case VK_VOLUME_DOWN:
2591 return NS_VK_VOLUME_DOWN;
2592 case VK_VOLUME_UP:
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:
2604 case VK_MEDIA_STOP:
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:
2610 return 0;
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".
2622 case VK_ICO_HELP:
2623 case VK_ICO_00:
2624 case VK_ICO_CLEAR:
2625 // Following OEM specific virtual keycodes are defined for Nokia/Ericsson.
2626 case VK_OEM_RESET:
2627 case VK_OEM_JUMP:
2628 case VK_OEM_PA1:
2629 case VK_OEM_PA2:
2630 case VK_OEM_PA3:
2631 case VK_OEM_WSCTRL:
2632 case VK_OEM_CUSEL:
2633 case VK_OEM_ATTN:
2634 case VK_OEM_FINISH:
2635 case VK_OEM_COPY:
2636 case VK_OEM_AUTO:
2637 case VK_OEM_ENLW:
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.
2641 case VK_OEM_CLEAR:
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.
2647 case 0xE1:
2648 return 0;
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.
2655 case VK_OEM_1:
2656 case VK_OEM_PLUS:
2657 case VK_OEM_COMMA:
2658 case VK_OEM_MINUS:
2659 case VK_OEM_PERIOD:
2660 case VK_OEM_2:
2661 case VK_OEM_3:
2662 case VK_OEM_4:
2663 case VK_OEM_5:
2664 case VK_OEM_6:
2665 case VK_OEM_7:
2666 case VK_OEM_8:
2667 case VK_OEM_102:
2668 case VK_ABNT_C1:
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) {
2681 return 0;
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.
2692 case VK_ABNT_C2:
2693 return NS_VK_SEPARATOR;
2695 // VK_PROCESSKEY means IME already consumed the key event.
2696 case VK_PROCESSKEY:
2697 return 0;
2698 // VK_PACKET is generated by SendInput() API, we don't need to
2699 // care this message as key event.
2700 case VK_PACKET:
2701 return 0;
2702 // If a key is not mapped to a virtual keycode, 0xFF is used.
2703 case 0xFF:
2704 NS_WARNING("The key is failed to be converted to a virtual keycode");
2705 return 0;
2707 #ifdef DEBUG
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.",
2711 aNativeKeyCode);
2712 NS_WARNING(warning.get());
2713 #endif
2714 return 0;
2717 KeyNameIndex
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)
2740 default:
2741 break;
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)
2760 default:
2761 break;
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)
2775 default:
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
2793 default:
2794 return KEY_NAME_INDEX_Unidentified;
2798 // static
2799 CodeNameIndex
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
2811 default:
2812 return CODE_NAME_INDEX_UNKNOWN;
2816 nsresult
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);
2848 BYTE kbdState[256];
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) {
2858 case VK_SHIFT:
2859 aModifierFlags &= ~(nsIWidget::SHIFT_L | nsIWidget::SHIFT_R);
2860 argumentKeySpecific = VK_LSHIFT;
2861 break;
2862 case VK_LSHIFT:
2863 aModifierFlags &= ~nsIWidget::SHIFT_L;
2864 argumentKeySpecific = aNativeKeyCode;
2865 aNativeKeyCode = VK_SHIFT;
2866 break;
2867 case VK_RSHIFT:
2868 aModifierFlags &= ~nsIWidget::SHIFT_R;
2869 argumentKeySpecific = aNativeKeyCode;
2870 aNativeKeyCode = VK_SHIFT;
2871 break;
2872 case VK_CONTROL:
2873 aModifierFlags &= ~(nsIWidget::CTRL_L | nsIWidget::CTRL_R);
2874 argumentKeySpecific = VK_LCONTROL;
2875 break;
2876 case VK_LCONTROL:
2877 aModifierFlags &= ~nsIWidget::CTRL_L;
2878 argumentKeySpecific = aNativeKeyCode;
2879 aNativeKeyCode = VK_CONTROL;
2880 break;
2881 case VK_RCONTROL:
2882 aModifierFlags &= ~nsIWidget::CTRL_R;
2883 argumentKeySpecific = aNativeKeyCode;
2884 aNativeKeyCode = VK_CONTROL;
2885 break;
2886 case VK_MENU:
2887 aModifierFlags &= ~(nsIWidget::ALT_L | nsIWidget::ALT_R);
2888 argumentKeySpecific = VK_LMENU;
2889 break;
2890 case VK_LMENU:
2891 aModifierFlags &= ~nsIWidget::ALT_L;
2892 argumentKeySpecific = aNativeKeyCode;
2893 aNativeKeyCode = VK_MENU;
2894 break;
2895 case VK_RMENU:
2896 aModifierFlags &= ~nsIWidget::ALT_R;
2897 argumentKeySpecific = aNativeKeyCode;
2898 aNativeKeyCode = VK_MENU;
2899 break;
2900 case VK_CAPITAL:
2901 aModifierFlags &= ~nsIWidget::CAPS_LOCK;
2902 argumentKeySpecific = VK_CAPITAL;
2903 break;
2904 case VK_NUMLOCK:
2905 aModifierFlags &= ~nsIWidget::NUM_LOCK;
2906 argumentKeySpecific = VK_NUMLOCK;
2907 break;
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
2921 if (keySpecific) {
2922 kbdState[keySpecific] = 0x81;
2924 ::SetKeyboardState(kbdState);
2925 ModifierKeyState modKeyState;
2926 UINT scanCode =
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
2930 // key.
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();
2950 } else {
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);
2959 bool dispatched;
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) {
2965 continue;
2967 MSG charMsg = fakeCharMsgs[j].GetCharMsg(aWidget->GetWindowHandle());
2968 NativeKey nativeKey(aWidget, charMsg, modKeyState);
2969 nativeKey.HandleCharMessage(charMsg);
2972 } else {
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
2981 if (keySpecific) {
2982 kbdState[keySpecific] = 0;
2984 ::SetKeyboardState(kbdState);
2985 ModifierKeyState modKeyState;
2986 UINT scanCode =
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
2990 // key.
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);
3002 RestoreLayout();
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) {
3007 loadedLayout = 0;
3008 break;
3011 if (keyboardLayoutListBuff != keyboardLayoutList) {
3012 delete [] keyboardLayoutList;
3014 if (loadedLayout) {
3015 ::UnloadKeyboardLayout(loadedLayout);
3017 return NS_OK;
3020 /*****************************************************************************
3021 * mozilla::widget::DeadKeyTable
3022 *****************************************************************************/
3024 char16_t
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) {
3035 break;
3039 return 0;
3042 /*****************************************************************************
3043 * mozilla::widget::RedirectedKeyDownMessage
3044 *****************************************************************************/
3046 MSG RedirectedKeyDownMessageManager::sRedirectedKeyDownMsg;
3047 bool RedirectedKeyDownMessageManager::sDefaultPreventedOfRedirectedMsg = false;
3049 // static
3050 bool
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));
3059 // static
3060 void
3061 RedirectedKeyDownMessageManager::RemoveNextCharMessage(HWND aWnd)
3063 MSG msg;
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