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 #include "mozilla/Logging.h"
8 #include "mozilla/ArrayUtils.h"
9 #include "mozilla/AutoRestore.h"
10 #include "mozilla/MouseEvents.h"
11 #include "mozilla/MiscEvents.h"
12 #include "mozilla/StaticPrefs_ui.h"
13 #include "mozilla/TextEvents.h"
14 #include "mozilla/widget/WinRegistry.h"
16 #include "nsExceptionHandler.h"
17 #include "nsGkAtoms.h"
18 #include "nsIUserIdleServiceInternal.h"
19 #include "nsIWindowsRegKey.h"
20 #include "nsPrintfCString.h"
21 #include "nsReadableUtils.h"
22 #include "nsServiceManagerUtils.h"
24 #include "nsUnicharUtils.h"
25 #include "nsWindowDbg.h"
27 #include "KeyboardLayout.h"
28 #include "WidgetUtils.h"
42 // For collecting other people's log, tell them `MOZ_LOG=KeyboardHandler:4,sync`
43 // rather than `MOZ_LOG=KeyboardHandler:5,sync` since using `5` may create too
45 // Therefore you shouldn't use `LogLevel::Verbose` for logging usual behavior.
46 mozilla::LazyLogModule
gKeyLog("KeyboardHandler");
51 static const char* const kVirtualKeyName
[] = {
207 "VK_OEM_NEC_EQUAL, VK_OEM_FJ_JISHO",
229 "VK_BROWSER_FORWARD",
230 "VK_BROWSER_REFRESH",
233 "VK_BROWSER_FAVORITES",
239 "VK_MEDIA_NEXT_TRACK",
240 "VK_MEDIA_PREV_TRACK",
242 "VK_MEDIA_PLAY_PAUSE",
244 "VK_LAUNCH_MEDIA_SELECT",
324 static_assert(sizeof(kVirtualKeyName
) / sizeof(const char*) == 0x100,
325 "The virtual key name must be defined just 256 keys");
327 static const char* GetBoolName(bool aBool
) { return aBool
? "true" : "false"; }
329 static const nsCString
GetCharacterCodeName(WPARAM aCharCode
) {
332 return "NULL (0x0000)"_ns
;
334 return "BACKSPACE (0x0008)"_ns
;
336 return "CHARACTER TABULATION (0x0009)"_ns
;
338 return "LINE FEED (0x000A)"_ns
;
340 return "LINE TABULATION (0x000B)"_ns
;
342 return "FORM FEED (0x000C)"_ns
;
344 return "CARRIAGE RETURN (0x000D)"_ns
;
346 return "CANCEL (0x0018)"_ns
;
348 return "ESCAPE (0x001B)"_ns
;
350 return "SPACE (0x0020)"_ns
;
352 return "DELETE (0x007F)"_ns
;
354 return "NO-BREAK SPACE (0x00A0)"_ns
;
356 return "SOFT HYPHEN (0x00AD)"_ns
;
358 return "EN QUAD (0x2000)"_ns
;
360 return "EM QUAD (0x2001)"_ns
;
362 return "EN SPACE (0x2002)"_ns
;
364 return "EM SPACE (0x2003)"_ns
;
366 return "THREE-PER-EM SPACE (0x2004)"_ns
;
368 return "FOUR-PER-EM SPACE (0x2005)"_ns
;
370 return "SIX-PER-EM SPACE (0x2006)"_ns
;
372 return "FIGURE SPACE (0x2007)"_ns
;
374 return "PUNCTUATION SPACE (0x2008)"_ns
;
376 return "THIN SPACE (0x2009)"_ns
;
378 return "HAIR SPACE (0x200A)"_ns
;
380 return "ZERO WIDTH SPACE (0x200B)"_ns
;
382 return "ZERO WIDTH NON-JOINER (0x200C)"_ns
;
384 return "ZERO WIDTH JOINER (0x200D)"_ns
;
386 return "LEFT-TO-RIGHT MARK (0x200E)"_ns
;
388 return "RIGHT-TO-LEFT MARK (0x200F)"_ns
;
390 return "PARAGRAPH SEPARATOR (0x2029)"_ns
;
392 return "LEFT-TO-RIGHT EMBEDDING (0x202A)"_ns
;
394 return "RIGHT-TO-LEFT EMBEDDING (0x202B)"_ns
;
396 return "LEFT-TO-RIGHT OVERRIDE (0x202D)"_ns
;
398 return "RIGHT-TO-LEFT OVERRIDE (0x202E)"_ns
;
400 return "NARROW NO-BREAK SPACE (0x202F)"_ns
;
402 return "MEDIUM MATHEMATICAL SPACE (0x205F)"_ns
;
404 return "WORD JOINER (0x2060)"_ns
;
406 return "LEFT-TO-RIGHT ISOLATE (0x2066)"_ns
;
408 return "RIGHT-TO-LEFT ISOLATE (0x2067)"_ns
;
410 return "IDEOGRAPHIC SPACE (0x3000)"_ns
;
412 return "ZERO WIDTH NO-BREAK SPACE (0xFEFF)"_ns
;
414 if (aCharCode
< ' ' || (aCharCode
>= 0x80 && aCharCode
< 0xA0)) {
415 return nsPrintfCString("control (0x%04zX)", aCharCode
);
417 if (NS_IS_HIGH_SURROGATE(aCharCode
)) {
418 return nsPrintfCString("high surrogate (0x%04zX)", aCharCode
);
420 if (NS_IS_LOW_SURROGATE(aCharCode
)) {
421 return nsPrintfCString("low surrogate (0x%04zX)", aCharCode
);
423 return IS_IN_BMP(aCharCode
)
426 NS_ConvertUTF16toUTF8(nsAutoString(aCharCode
)).get(),
430 NS_ConvertUTF16toUTF8(nsAutoString(aCharCode
)).get(),
436 static const nsCString
GetKeyLocationName(uint32_t aLocation
) {
438 case eKeyLocationLeft
:
439 return "KEY_LOCATION_LEFT"_ns
;
440 case eKeyLocationRight
:
441 return "KEY_LOCATION_RIGHT"_ns
;
442 case eKeyLocationStandard
:
443 return "KEY_LOCATION_STANDARD"_ns
;
444 case eKeyLocationNumpad
:
445 return "KEY_LOCATION_NUMPAD"_ns
;
447 return nsPrintfCString("Unknown (0x%04X)", aLocation
);
451 static const nsCString
GetCharacterCodeNames(const char16_t
* aChars
,
457 result
.AssignLiteral("\"");
458 StringJoinAppend(result
, ", "_ns
, Span
{aChars
, aLength
},
459 [](nsACString
& dest
, const char16_t charValue
) {
460 dest
.Append(GetCharacterCodeName(charValue
));
462 result
.AppendLiteral("\"");
466 static const nsCString
GetCharacterCodeNames(
467 const UniCharsAndModifiers
& aUniCharsAndModifiers
) {
468 if (aUniCharsAndModifiers
.IsEmpty()) {
472 result
.AssignLiteral("\"");
473 StringJoinAppend(result
, ", "_ns
, Span
{aUniCharsAndModifiers
.ToString()},
474 [](nsACString
& dest
, const char16_t charValue
) {
475 dest
.Append(GetCharacterCodeName(charValue
));
477 result
.AppendLiteral("\"");
481 class MOZ_STACK_CLASS GetShiftStateName final
: public nsAutoCString
{
483 explicit GetShiftStateName(VirtualKey::ShiftState aShiftState
) {
485 AssignLiteral("none");
488 if (aShiftState
& VirtualKey::STATE_SHIFT
) {
489 AssignLiteral("Shift");
490 aShiftState
&= ~VirtualKey::STATE_SHIFT
;
492 if (aShiftState
& VirtualKey::STATE_CONTROL
) {
493 MaybeAppendSeparator();
494 AssignLiteral("Ctrl");
495 aShiftState
&= ~VirtualKey::STATE_CONTROL
;
497 if (aShiftState
& VirtualKey::STATE_ALT
) {
498 MaybeAppendSeparator();
499 AssignLiteral("Alt");
500 aShiftState
&= ~VirtualKey::STATE_ALT
;
502 if (aShiftState
& VirtualKey::STATE_CAPSLOCK
) {
503 MaybeAppendSeparator();
504 AssignLiteral("CapsLock");
505 aShiftState
&= ~VirtualKey::STATE_CAPSLOCK
;
507 MOZ_ASSERT(!aShiftState
);
511 void MaybeAppendSeparator() {
513 AppendLiteral(" | ");
518 static const nsCString
GetMessageName(UINT aMessage
) {
523 return "WM_KEYDOWN"_ns
;
525 return "WM_KEYUP"_ns
;
527 return "WM_SYSKEYDOWN"_ns
;
529 return "WM_SYSKEYUP"_ns
;
533 return "WM_UNICHAR"_ns
;
535 return "WM_SYSCHAR"_ns
;
537 return "WM_DEADCHAR"_ns
;
539 return "WM_SYSDEADCHAR"_ns
;
541 return "WM_APPCOMMAND"_ns
;
545 return nsPrintfCString("Unknown Message (0x%04X)", aMessage
);
549 static const nsCString
GetVirtualKeyCodeName(WPARAM aVK
) {
550 if (aVK
>= std::size(kVirtualKeyName
)) {
551 return nsPrintfCString("Invalid (0x%08zX)", aVK
);
553 return nsCString(kVirtualKeyName
[aVK
]);
556 static const nsCString
GetAppCommandName(WPARAM aCommand
) {
558 case APPCOMMAND_BASS_BOOST
:
559 return "APPCOMMAND_BASS_BOOST"_ns
;
560 case APPCOMMAND_BASS_DOWN
:
561 return "APPCOMMAND_BASS_DOWN"_ns
;
562 case APPCOMMAND_BASS_UP
:
563 return "APPCOMMAND_BASS_UP"_ns
;
564 case APPCOMMAND_BROWSER_BACKWARD
:
565 return "APPCOMMAND_BROWSER_BACKWARD"_ns
;
566 case APPCOMMAND_BROWSER_FAVORITES
:
567 return "APPCOMMAND_BROWSER_FAVORITES"_ns
;
568 case APPCOMMAND_BROWSER_FORWARD
:
569 return "APPCOMMAND_BROWSER_FORWARD"_ns
;
570 case APPCOMMAND_BROWSER_HOME
:
571 return "APPCOMMAND_BROWSER_HOME"_ns
;
572 case APPCOMMAND_BROWSER_REFRESH
:
573 return "APPCOMMAND_BROWSER_REFRESH"_ns
;
574 case APPCOMMAND_BROWSER_SEARCH
:
575 return "APPCOMMAND_BROWSER_SEARCH"_ns
;
576 case APPCOMMAND_BROWSER_STOP
:
577 return "APPCOMMAND_BROWSER_STOP"_ns
;
578 case APPCOMMAND_CLOSE
:
579 return "APPCOMMAND_CLOSE"_ns
;
580 case APPCOMMAND_COPY
:
581 return "APPCOMMAND_COPY"_ns
;
582 case APPCOMMAND_CORRECTION_LIST
:
583 return "APPCOMMAND_CORRECTION_LIST"_ns
;
585 return "APPCOMMAND_CUT"_ns
;
586 case APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE
:
587 return "APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE"_ns
;
588 case APPCOMMAND_FIND
:
589 return "APPCOMMAND_FIND"_ns
;
590 case APPCOMMAND_FORWARD_MAIL
:
591 return "APPCOMMAND_FORWARD_MAIL"_ns
;
592 case APPCOMMAND_HELP
:
593 return "APPCOMMAND_HELP"_ns
;
594 case APPCOMMAND_LAUNCH_APP1
:
595 return "APPCOMMAND_LAUNCH_APP1"_ns
;
596 case APPCOMMAND_LAUNCH_APP2
:
597 return "APPCOMMAND_LAUNCH_APP2"_ns
;
598 case APPCOMMAND_LAUNCH_MAIL
:
599 return "APPCOMMAND_LAUNCH_MAIL"_ns
;
600 case APPCOMMAND_LAUNCH_MEDIA_SELECT
:
601 return "APPCOMMAND_LAUNCH_MEDIA_SELECT"_ns
;
602 case APPCOMMAND_MEDIA_CHANNEL_DOWN
:
603 return "APPCOMMAND_MEDIA_CHANNEL_DOWN"_ns
;
604 case APPCOMMAND_MEDIA_CHANNEL_UP
:
605 return "APPCOMMAND_MEDIA_CHANNEL_UP"_ns
;
606 case APPCOMMAND_MEDIA_FAST_FORWARD
:
607 return "APPCOMMAND_MEDIA_FAST_FORWARD"_ns
;
608 case APPCOMMAND_MEDIA_NEXTTRACK
:
609 return "APPCOMMAND_MEDIA_NEXTTRACK"_ns
;
610 case APPCOMMAND_MEDIA_PAUSE
:
611 return "APPCOMMAND_MEDIA_PAUSE"_ns
;
612 case APPCOMMAND_MEDIA_PLAY
:
613 return "APPCOMMAND_MEDIA_PLAY"_ns
;
614 case APPCOMMAND_MEDIA_PLAY_PAUSE
:
615 return "APPCOMMAND_MEDIA_PLAY_PAUSE"_ns
;
616 case APPCOMMAND_MEDIA_PREVIOUSTRACK
:
617 return "APPCOMMAND_MEDIA_PREVIOUSTRACK"_ns
;
618 case APPCOMMAND_MEDIA_RECORD
:
619 return "APPCOMMAND_MEDIA_RECORD"_ns
;
620 case APPCOMMAND_MEDIA_REWIND
:
621 return "APPCOMMAND_MEDIA_REWIND"_ns
;
622 case APPCOMMAND_MEDIA_STOP
:
623 return "APPCOMMAND_MEDIA_STOP"_ns
;
624 case APPCOMMAND_MIC_ON_OFF_TOGGLE
:
625 return "APPCOMMAND_MIC_ON_OFF_TOGGLE"_ns
;
626 case APPCOMMAND_MICROPHONE_VOLUME_DOWN
:
627 return "APPCOMMAND_MICROPHONE_VOLUME_DOWN"_ns
;
628 case APPCOMMAND_MICROPHONE_VOLUME_MUTE
:
629 return "APPCOMMAND_MICROPHONE_VOLUME_MUTE"_ns
;
630 case APPCOMMAND_MICROPHONE_VOLUME_UP
:
631 return "APPCOMMAND_MICROPHONE_VOLUME_UP"_ns
;
633 return "APPCOMMAND_NEW"_ns
;
634 case APPCOMMAND_OPEN
:
635 return "APPCOMMAND_OPEN"_ns
;
636 case APPCOMMAND_PASTE
:
637 return "APPCOMMAND_PASTE"_ns
;
638 case APPCOMMAND_PRINT
:
639 return "APPCOMMAND_PRINT"_ns
;
640 case APPCOMMAND_REDO
:
641 return "APPCOMMAND_REDO"_ns
;
642 case APPCOMMAND_REPLY_TO_MAIL
:
643 return "APPCOMMAND_REPLY_TO_MAIL"_ns
;
644 case APPCOMMAND_SAVE
:
645 return "APPCOMMAND_SAVE"_ns
;
646 case APPCOMMAND_SEND_MAIL
:
647 return "APPCOMMAND_SEND_MAIL"_ns
;
648 case APPCOMMAND_SPELL_CHECK
:
649 return "APPCOMMAND_SPELL_CHECK"_ns
;
650 case APPCOMMAND_TREBLE_DOWN
:
651 return "APPCOMMAND_TREBLE_DOWN"_ns
;
652 case APPCOMMAND_TREBLE_UP
:
653 return "APPCOMMAND_TREBLE_UP"_ns
;
654 case APPCOMMAND_UNDO
:
655 return "APPCOMMAND_UNDO"_ns
;
656 case APPCOMMAND_VOLUME_DOWN
:
657 return "APPCOMMAND_VOLUME_DOWN"_ns
;
658 case APPCOMMAND_VOLUME_MUTE
:
659 return "APPCOMMAND_VOLUME_MUTE"_ns
;
660 case APPCOMMAND_VOLUME_UP
:
661 return "APPCOMMAND_VOLUME_UP"_ns
;
663 return nsPrintfCString("Unknown app command (0x%08zX)", aCommand
);
667 static const nsCString
GetAppCommandDeviceName(LPARAM aDevice
) {
669 case FAPPCOMMAND_KEY
:
670 return "FAPPCOMMAND_KEY"_ns
;
671 case FAPPCOMMAND_MOUSE
:
672 return "FAPPCOMMAND_MOUSE"_ns
;
673 case FAPPCOMMAND_OEM
:
674 return "FAPPCOMMAND_OEM"_ns
;
676 return nsPrintfCString("Unknown app command device (0x%04" PRIXLPTR
")",
681 class MOZ_STACK_CLASS GetAppCommandKeysName final
: public nsAutoCString
{
683 explicit GetAppCommandKeysName(WPARAM aKeys
) {
684 if (aKeys
& MK_CONTROL
) {
685 AppendLiteral("MK_CONTROL");
686 aKeys
&= ~MK_CONTROL
;
688 if (aKeys
& MK_LBUTTON
) {
689 MaybeAppendSeparator();
690 AppendLiteral("MK_LBUTTON");
691 aKeys
&= ~MK_LBUTTON
;
693 if (aKeys
& MK_MBUTTON
) {
694 MaybeAppendSeparator();
695 AppendLiteral("MK_MBUTTON");
696 aKeys
&= ~MK_MBUTTON
;
698 if (aKeys
& MK_RBUTTON
) {
699 MaybeAppendSeparator();
700 AppendLiteral("MK_RBUTTON");
701 aKeys
&= ~MK_RBUTTON
;
703 if (aKeys
& MK_SHIFT
) {
704 MaybeAppendSeparator();
705 AppendLiteral("MK_SHIFT");
708 if (aKeys
& MK_XBUTTON1
) {
709 MaybeAppendSeparator();
710 AppendLiteral("MK_XBUTTON1");
711 aKeys
&= ~MK_XBUTTON1
;
713 if (aKeys
& MK_XBUTTON2
) {
714 MaybeAppendSeparator();
715 AppendLiteral("MK_XBUTTON2");
716 aKeys
&= ~MK_XBUTTON2
;
719 MaybeAppendSeparator();
720 AppendPrintf("Unknown Flags (0x%04zX)", aKeys
);
723 AssignLiteral("none (0x0000)");
728 void MaybeAppendSeparator() {
730 AppendLiteral(" | ");
735 static const nsCString
ToString(const MSG
& aMSG
) {
737 result
.AssignLiteral("{ message=");
738 result
.Append(GetMessageName(aMSG
.message
).get());
739 result
.AppendLiteral(", ");
740 switch (aMSG
.message
) {
746 "virtual keycode=%s, repeat count=%" PRIdLPTR
748 "scancode=0x%02X, extended key=%s, "
749 "context code=%s, previous key state=%s, "
750 "transition state=%s",
751 GetVirtualKeyCodeName(aMSG
.wParam
).get(), aMSG
.lParam
& 0xFFFF,
752 WinUtils::GetScanCode(aMSG
.lParam
),
753 GetBoolName(WinUtils::IsExtendedScanCode(aMSG
.lParam
)),
754 GetBoolName((aMSG
.lParam
& (1 << 29)) != 0),
755 GetBoolName((aMSG
.lParam
& (1 << 30)) != 0),
756 GetBoolName((aMSG
.lParam
& (1 << 31)) != 0));
763 "character code=%s, repeat count=%" PRIdLPTR
765 "scancode=0x%02X, extended key=%s, "
766 "context code=%s, previous key state=%s, "
767 "transition state=%s",
768 GetCharacterCodeName(aMSG
.wParam
).get(), aMSG
.lParam
& 0xFFFF,
769 WinUtils::GetScanCode(aMSG
.lParam
),
770 GetBoolName(WinUtils::IsExtendedScanCode(aMSG
.lParam
)),
771 GetBoolName((aMSG
.lParam
& (1 << 29)) != 0),
772 GetBoolName((aMSG
.lParam
& (1 << 30)) != 0),
773 GetBoolName((aMSG
.lParam
& (1 << 31)) != 0));
777 "window handle=0x%zx, app command=%s, device=%s, dwKeys=%s",
779 GetAppCommandName(GET_APPCOMMAND_LPARAM(aMSG
.lParam
)).get(),
780 GetAppCommandDeviceName(GET_DEVICE_LPARAM(aMSG
.lParam
)).get(),
781 GetAppCommandKeysName(GET_KEYSTATE_LPARAM(aMSG
.lParam
)).get());
784 result
.AppendPrintf("wParam=%zu, lParam=%" PRIdLPTR
, aMSG
.wParam
,
788 result
.AppendPrintf(", hwnd=0x%p", aMSG
.hwnd
);
792 static const nsCString
ToString(
793 const UniCharsAndModifiers
& aUniCharsAndModifiers
) {
794 if (aUniCharsAndModifiers
.IsEmpty()) {
798 result
.AssignLiteral("{ ");
799 result
.Append(GetCharacterCodeName(aUniCharsAndModifiers
.CharAt(0)));
800 for (size_t i
= 1; i
< aUniCharsAndModifiers
.Length(); ++i
) {
801 if (aUniCharsAndModifiers
.ModifiersAt(i
- 1) !=
802 aUniCharsAndModifiers
.ModifiersAt(i
)) {
803 result
.AppendLiteral(" [");
804 result
.Append(GetModifiersName(aUniCharsAndModifiers
.ModifiersAt(0)));
805 result
.AppendLiteral("]");
807 result
.AppendLiteral(", ");
808 result
.Append(GetCharacterCodeName(aUniCharsAndModifiers
.CharAt(i
)));
810 result
.AppendLiteral(" [");
811 uint32_t lastIndex
= aUniCharsAndModifiers
.Length() - 1;
812 result
.Append(GetModifiersName(aUniCharsAndModifiers
.ModifiersAt(lastIndex
)));
813 result
.AppendLiteral("] }");
817 const nsCString
ToString(const ModifierKeyState
& aModifierKeyState
) {
819 result
.AssignLiteral("{ ");
820 result
.Append(GetModifiersName(aModifierKeyState
.GetModifiers()).get());
821 result
.AppendLiteral(" }");
825 // Unique id counter associated with a keydown / keypress events. Used in
826 // identifing keypress events for removal from async event dispatch queue
827 // in metrofx after preventDefault is called on keydown events.
828 static uint32_t sUniqueKeyEventId
= 0;
830 /*****************************************************************************
831 * mozilla::widget::ModifierKeyState
832 *****************************************************************************/
834 ModifierKeyState::ModifierKeyState() { Update(); }
836 ModifierKeyState::ModifierKeyState(Modifiers aModifiers
)
837 : mModifiers(aModifiers
) {
838 MOZ_ASSERT(!(mModifiers
& MODIFIER_ALTGRAPH
) || (!IsControl() && !IsAlt()),
839 "Neither MODIFIER_CONTROL nor MODIFIER_ALT should be set "
840 "if MODIFIER_ALTGRAPH is set");
843 void ModifierKeyState::Update() {
845 if (IS_VK_DOWN(VK_SHIFT
)) {
846 mModifiers
|= MODIFIER_SHIFT
;
848 // If AltGr key (i.e., VK_RMENU on some keyboard layout) is pressed, only
849 // MODIFIER_ALTGRAPH should be set. Otherwise, i.e., if both Ctrl and Alt
850 // keys are pressed to emulate AltGr key, MODIFIER_CONTROL and MODIFIER_ALT
851 // keys should be set separately.
852 if (IS_VK_DOWN(VK_RMENU
) && KeyboardLayout::GetInstance()->HasAltGr()) {
853 mModifiers
|= MODIFIER_ALTGRAPH
;
855 if (IS_VK_DOWN(VK_CONTROL
)) {
856 mModifiers
|= MODIFIER_CONTROL
;
858 if (IS_VK_DOWN(VK_MENU
)) {
859 mModifiers
|= MODIFIER_ALT
;
862 if (IS_VK_DOWN(VK_LWIN
) || IS_VK_DOWN(VK_RWIN
)) {
863 mModifiers
|= MODIFIER_META
;
865 if (::GetKeyState(VK_CAPITAL
) & 1) {
866 mModifiers
|= MODIFIER_CAPSLOCK
;
868 if (::GetKeyState(VK_NUMLOCK
) & 1) {
869 mModifiers
|= MODIFIER_NUMLOCK
;
871 if (::GetKeyState(VK_SCROLL
) & 1) {
872 mModifiers
|= MODIFIER_SCROLLLOCK
;
876 void ModifierKeyState::Unset(Modifiers aRemovingModifiers
) {
877 mModifiers
&= ~aRemovingModifiers
;
880 void ModifierKeyState::Set(Modifiers aAddingModifiers
) {
881 mModifiers
|= aAddingModifiers
;
882 MOZ_ASSERT(!(mModifiers
& MODIFIER_ALTGRAPH
) || (!IsControl() && !IsAlt()),
883 "Neither MODIFIER_CONTROL nor MODIFIER_ALT should be set "
884 "if MODIFIER_ALTGRAPH is set");
887 void ModifierKeyState::InitInputEvent(WidgetInputEvent
& aInputEvent
) const {
888 aInputEvent
.mModifiers
= mModifiers
;
890 switch (aInputEvent
.mClass
) {
891 case eMouseEventClass
:
892 case ePointerEventClass
:
893 case eMouseScrollEventClass
:
894 case eWheelEventClass
:
895 case eDragEventClass
:
896 case eSimpleGestureEventClass
:
897 InitMouseEvent(aInputEvent
);
904 void ModifierKeyState::InitMouseEvent(WidgetInputEvent
& aMouseEvent
) const {
905 NS_ASSERTION(aMouseEvent
.mClass
== eMouseEventClass
||
906 aMouseEvent
.mClass
== ePointerEventClass
||
907 aMouseEvent
.mClass
== eWheelEventClass
||
908 aMouseEvent
.mClass
== eDragEventClass
||
909 aMouseEvent
.mClass
== eSimpleGestureEventClass
,
910 "called with non-mouse event");
912 WidgetMouseEventBase
& mouseEvent
= *aMouseEvent
.AsMouseEventBase();
913 mouseEvent
.mButtons
= 0;
914 if (::GetKeyState(VK_LBUTTON
) < 0) {
915 mouseEvent
.mButtons
|= MouseButtonsFlag::ePrimaryFlag
;
917 if (::GetKeyState(VK_RBUTTON
) < 0) {
918 mouseEvent
.mButtons
|= MouseButtonsFlag::eSecondaryFlag
;
920 if (::GetKeyState(VK_MBUTTON
) < 0) {
921 mouseEvent
.mButtons
|= MouseButtonsFlag::eMiddleFlag
;
923 if (::GetKeyState(VK_XBUTTON1
) < 0) {
924 mouseEvent
.mButtons
|= MouseButtonsFlag::e4thFlag
;
926 if (::GetKeyState(VK_XBUTTON2
) < 0) {
927 mouseEvent
.mButtons
|= MouseButtonsFlag::e5thFlag
;
931 bool ModifierKeyState::IsShift() const {
932 return (mModifiers
& MODIFIER_SHIFT
) != 0;
935 bool ModifierKeyState::IsControl() const {
936 return (mModifiers
& MODIFIER_CONTROL
) != 0;
939 bool ModifierKeyState::IsAlt() const {
940 return (mModifiers
& MODIFIER_ALT
) != 0;
943 bool ModifierKeyState::IsWin() const {
944 return (mModifiers
& MODIFIER_META
) != 0;
947 bool ModifierKeyState::MaybeMatchShortcutKey() const {
948 // If Windows key is pressed, even if both Ctrl key and Alt key are pressed,
949 // it's possible to match a shortcut key.
953 // Otherwise, when both Ctrl key and Alt key are pressed, it shouldn't be
954 // a shortcut key for Windows since it means pressing AltGr key on
955 // some keyboard layouts.
956 if (IsControl() ^ IsAlt()) {
959 // If no modifier key is active except a lockable modifier nor Shift key,
960 // the key shouldn't match any shortcut keys (there are Space and
961 // Shift+Space, though, let's ignore these special case...).
965 bool ModifierKeyState::IsCapsLocked() const {
966 return (mModifiers
& MODIFIER_CAPSLOCK
) != 0;
969 bool ModifierKeyState::IsNumLocked() const {
970 return (mModifiers
& MODIFIER_NUMLOCK
) != 0;
973 bool ModifierKeyState::IsScrollLocked() const {
974 return (mModifiers
& MODIFIER_SCROLLLOCK
) != 0;
977 /*****************************************************************************
978 * mozilla::widget::UniCharsAndModifiers
979 *****************************************************************************/
981 void UniCharsAndModifiers::Append(char16_t aUniChar
, Modifiers aModifiers
) {
982 mChars
.Append(aUniChar
);
983 mModifiers
.AppendElement(aModifiers
);
986 void UniCharsAndModifiers::FillModifiers(Modifiers aModifiers
) {
987 for (size_t i
= 0; i
< Length(); i
++) {
988 mModifiers
[i
] = aModifiers
;
992 void UniCharsAndModifiers::OverwriteModifiersIfBeginsWith(
993 const UniCharsAndModifiers
& aOther
) {
994 if (!BeginsWith(aOther
)) {
997 for (size_t i
= 0; i
< aOther
.Length(); ++i
) {
998 mModifiers
[i
] = aOther
.mModifiers
[i
];
1002 bool UniCharsAndModifiers::UniCharsEqual(
1003 const UniCharsAndModifiers
& aOther
) const {
1004 return mChars
.Equals(aOther
.mChars
);
1007 bool UniCharsAndModifiers::UniCharsCaseInsensitiveEqual(
1008 const UniCharsAndModifiers
& aOther
) const {
1009 return mChars
.Equals(aOther
.mChars
, nsCaseInsensitiveStringComparator
);
1012 bool UniCharsAndModifiers::BeginsWith(
1013 const UniCharsAndModifiers
& aOther
) const {
1014 return StringBeginsWith(mChars
, aOther
.mChars
);
1017 UniCharsAndModifiers
& UniCharsAndModifiers::operator+=(
1018 const UniCharsAndModifiers
& aOther
) {
1019 mChars
.Append(aOther
.mChars
);
1020 mModifiers
.AppendElements(aOther
.mModifiers
);
1024 UniCharsAndModifiers
UniCharsAndModifiers::operator+(
1025 const UniCharsAndModifiers
& aOther
) const {
1026 UniCharsAndModifiers
result(*this);
1031 /*****************************************************************************
1032 * mozilla::widget::VirtualKey
1033 *****************************************************************************/
1036 VirtualKey::ShiftState
VirtualKey::ModifiersToShiftState(Modifiers aModifiers
) {
1037 ShiftState state
= 0;
1038 if (aModifiers
& MODIFIER_SHIFT
) {
1039 state
|= STATE_SHIFT
;
1041 if (aModifiers
& MODIFIER_ALTGRAPH
) {
1042 state
|= STATE_ALTGRAPH
;
1044 if (aModifiers
& MODIFIER_CONTROL
) {
1045 state
|= STATE_CONTROL
;
1047 if (aModifiers
& MODIFIER_ALT
) {
1051 if (aModifiers
& MODIFIER_CAPSLOCK
) {
1052 state
|= STATE_CAPSLOCK
;
1058 Modifiers
VirtualKey::ShiftStateToModifiers(ShiftState aShiftState
) {
1059 Modifiers modifiers
= 0;
1060 if (aShiftState
& STATE_SHIFT
) {
1061 modifiers
|= MODIFIER_SHIFT
;
1063 if (aShiftState
& STATE_ALTGRAPH
) {
1064 modifiers
|= MODIFIER_ALTGRAPH
;
1066 if (aShiftState
& STATE_CONTROL
) {
1067 modifiers
|= MODIFIER_CONTROL
;
1069 if (aShiftState
& STATE_ALT
) {
1070 modifiers
|= MODIFIER_ALT
;
1073 if (aShiftState
& STATE_CAPSLOCK
) {
1074 modifiers
|= MODIFIER_CAPSLOCK
;
1079 const DeadKeyTable
* VirtualKey::MatchingDeadKeyTable(
1080 const DeadKeyEntry
* aDeadKeyArray
, uint32_t aEntries
) const {
1085 for (ShiftState shiftState
= 0; shiftState
< 16; shiftState
++) {
1086 if (!IsDeadKey(shiftState
)) {
1089 const DeadKeyTable
* dkt
= mShiftStates
[shiftState
].DeadKey
.Table
;
1090 if (dkt
&& dkt
->IsEqual(aDeadKeyArray
, aEntries
)) {
1098 void VirtualKey::SetNormalChars(ShiftState aShiftState
, const char16_t
* aChars
,
1099 uint32_t aNumOfChars
) {
1100 MOZ_ASSERT(aShiftState
== ToShiftStateIndex(aShiftState
));
1102 SetDeadKey(aShiftState
, false);
1104 for (uint32_t index
= 0; index
< aNumOfChars
; index
++) {
1105 // Ignore legacy non-printable control characters
1106 mShiftStates
[aShiftState
].Normal
.Chars
[index
] =
1107 (aChars
[index
] >= 0x20) ? aChars
[index
] : 0;
1110 uint32_t len
= std::size(mShiftStates
[aShiftState
].Normal
.Chars
);
1111 for (uint32_t index
= aNumOfChars
; index
< len
; index
++) {
1112 mShiftStates
[aShiftState
].Normal
.Chars
[index
] = 0;
1116 void VirtualKey::SetDeadChar(ShiftState aShiftState
, char16_t aDeadChar
) {
1117 MOZ_ASSERT(aShiftState
== ToShiftStateIndex(aShiftState
));
1119 SetDeadKey(aShiftState
, true);
1121 mShiftStates
[aShiftState
].DeadKey
.DeadChar
= aDeadChar
;
1122 mShiftStates
[aShiftState
].DeadKey
.Table
= nullptr;
1125 UniCharsAndModifiers
VirtualKey::GetUniChars(ShiftState aShiftState
) const {
1126 UniCharsAndModifiers result
= GetNativeUniChars(aShiftState
);
1128 const uint8_t kShiftStateIndex
= ToShiftStateIndex(aShiftState
);
1129 if (!(kShiftStateIndex
& STATE_CONTROL_ALT
)) {
1130 // If neither Alt nor Ctrl key is pressed, just return stored data
1135 if (result
.IsEmpty()) {
1136 // If Alt and/or Control are pressed and the key produces no
1137 // character, return characters which is produced by the key without
1138 // Alt and Control, and return given modifiers as is.
1139 result
= GetNativeUniChars(kShiftStateIndex
& ~STATE_CONTROL_ALT
);
1140 result
.FillModifiers(ShiftStateToModifiers(aShiftState
));
1144 if (IsAltGrIndex(kShiftStateIndex
)) {
1145 // If AltGr or both Ctrl and Alt are pressed and the key produces
1146 // character(s), we need to clear MODIFIER_ALT and MODIFIER_CONTROL
1147 // since TextEditor won't handle eKeyPress event whose mModifiers
1148 // has MODIFIER_ALT or MODIFIER_CONTROL. Additionally, we need to
1149 // use MODIFIER_ALTGRAPH when a key produces character(s) with
1150 // AltGr or both Ctrl and Alt on Windows. See following spec issue:
1151 // <https://github.com/w3c/uievents/issues/147>
1152 Modifiers finalModifiers
= ShiftStateToModifiers(aShiftState
);
1153 finalModifiers
&= ~(MODIFIER_ALT
| MODIFIER_CONTROL
);
1154 finalModifiers
|= MODIFIER_ALTGRAPH
;
1155 result
.FillModifiers(finalModifiers
);
1159 // Otherwise, i.e., Alt or Ctrl is pressed and it produces character(s),
1160 // check if different character(s) is produced by the key without Alt/Ctrl.
1161 // If it produces different character, we need to consume the Alt and
1162 // Ctrl modifier for TextEditor. Otherwise, the key does not produces the
1163 // character actually. So, keep setting Alt and Ctrl modifiers.
1164 UniCharsAndModifiers unmodifiedReslt
=
1165 GetNativeUniChars(kShiftStateIndex
& ~STATE_CONTROL_ALT
);
1166 if (!result
.UniCharsEqual(unmodifiedReslt
)) {
1167 Modifiers finalModifiers
= ShiftStateToModifiers(aShiftState
);
1168 finalModifiers
&= ~(MODIFIER_ALT
| MODIFIER_CONTROL
);
1169 result
.FillModifiers(finalModifiers
);
1174 UniCharsAndModifiers
VirtualKey::GetNativeUniChars(
1175 ShiftState aShiftState
) const {
1176 const uint8_t kShiftStateIndex
= ToShiftStateIndex(aShiftState
);
1177 UniCharsAndModifiers result
;
1178 Modifiers modifiers
= ShiftStateToModifiers(aShiftState
);
1179 if (IsDeadKey(aShiftState
)) {
1180 result
.Append(mShiftStates
[kShiftStateIndex
].DeadKey
.DeadChar
, modifiers
);
1184 uint32_t len
= std::size(mShiftStates
[kShiftStateIndex
].Normal
.Chars
);
1185 for (uint32_t i
= 0;
1186 i
< len
&& mShiftStates
[kShiftStateIndex
].Normal
.Chars
[i
]; i
++) {
1187 result
.Append(mShiftStates
[kShiftStateIndex
].Normal
.Chars
[i
], modifiers
);
1193 void VirtualKey::FillKbdState(PBYTE aKbdState
, const ShiftState aShiftState
) {
1194 if (aShiftState
& STATE_SHIFT
) {
1195 aKbdState
[VK_SHIFT
] |= 0x80;
1197 aKbdState
[VK_SHIFT
] &= ~0x80;
1198 aKbdState
[VK_LSHIFT
] &= ~0x80;
1199 aKbdState
[VK_RSHIFT
] &= ~0x80;
1202 if (aShiftState
& STATE_ALTGRAPH
) {
1203 aKbdState
[VK_CONTROL
] |= 0x80;
1204 aKbdState
[VK_LCONTROL
] |= 0x80;
1205 aKbdState
[VK_RCONTROL
] &= ~0x80;
1206 aKbdState
[VK_MENU
] |= 0x80;
1207 aKbdState
[VK_LMENU
] &= ~0x80;
1208 aKbdState
[VK_RMENU
] |= 0x80;
1210 if (aShiftState
& STATE_CONTROL
) {
1211 aKbdState
[VK_CONTROL
] |= 0x80;
1213 aKbdState
[VK_CONTROL
] &= ~0x80;
1214 aKbdState
[VK_LCONTROL
] &= ~0x80;
1215 aKbdState
[VK_RCONTROL
] &= ~0x80;
1218 if (aShiftState
& STATE_ALT
) {
1219 aKbdState
[VK_MENU
] |= 0x80;
1221 aKbdState
[VK_MENU
] &= ~0x80;
1222 aKbdState
[VK_LMENU
] &= ~0x80;
1223 aKbdState
[VK_RMENU
] &= ~0x80;
1227 if (aShiftState
& STATE_CAPSLOCK
) {
1228 aKbdState
[VK_CAPITAL
] |= 0x01;
1230 aKbdState
[VK_CAPITAL
] &= ~0x01;
1234 /*****************************************************************************
1235 * mozilla::widget::NativeKey
1236 *****************************************************************************/
1238 uint8_t NativeKey::sDispatchedKeyOfAppCommand
= 0;
1239 NativeKey
* NativeKey::sLatestInstance
= nullptr;
1240 const MSG
NativeKey::sEmptyMSG
= {};
1241 MSG
NativeKey::sLastKeyOrCharMSG
= {};
1242 MSG
NativeKey::sLastKeyMSG
= {};
1243 char16_t
NativeKey::sPendingHighSurrogate
= 0;
1245 NativeKey::NativeKey(nsWindow
* aWidget
, const MSG
& aMessage
,
1246 const ModifierKeyState
& aModKeyState
,
1247 HKL aOverrideKeyboardLayout
,
1248 nsTArray
<FakeCharMsg
>* aFakeCharMsgs
)
1249 : mLastInstance(sLatestInstance
),
1250 mRemovingMsg(sEmptyMSG
),
1251 mReceivedMsg(sEmptyMSG
),
1253 mDispatcher(aWidget
->GetTextEventDispatcher()),
1255 mFocusedWndBeforeDispatch(::GetFocus()),
1257 mKeyNameIndex(KEY_NAME_INDEX_Unidentified
),
1258 mCodeNameIndex(CODE_NAME_INDEX_UNKNOWN
),
1259 mModKeyState(aModKeyState
),
1261 mOriginalVirtualKeyCode(0),
1262 mShiftedLatinChar(0),
1263 mUnshiftedLatinChar(0),
1268 mIsPrintableKey(false),
1269 mIsSkippableInRemoteProcess(false),
1270 mCharMessageHasGone(false),
1271 mCanIgnoreModifierStateAtKeyPress(true),
1272 mFakeCharMsgs(aFakeCharMsgs
&& aFakeCharMsgs
->Length() ? aFakeCharMsgs
1274 MOZ_LOG(gKeyLog
, LogLevel::Info
,
1275 ("%p NativeKey::NativeKey(aWidget=0x%p { GetWindowHandle()=0x%p }, "
1276 "aMessage=%s, aModKeyState=%s), sLatestInstance=0x%p",
1277 this, aWidget
, aWidget
->GetWindowHandle(), ToString(aMessage
).get(),
1278 ToString(aModKeyState
).get(), sLatestInstance
));
1280 MOZ_ASSERT(aWidget
);
1281 MOZ_ASSERT(mDispatcher
);
1282 sLatestInstance
= this;
1283 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
1284 mKeyboardLayout
= KeyboardLayout::GetLayout();
1285 if (aOverrideKeyboardLayout
&& mKeyboardLayout
!= aOverrideKeyboardLayout
) {
1286 keyboardLayout
->OverrideLayout(aOverrideKeyboardLayout
);
1287 mKeyboardLayout
= keyboardLayout
->GetLoadedLayout();
1288 MOZ_ASSERT(mKeyboardLayout
== aOverrideKeyboardLayout
);
1289 mIsOverridingKeyboardLayout
= true;
1291 mIsOverridingKeyboardLayout
= false;
1292 sLastKeyOrCharMSG
= aMessage
;
1295 if (mMsg
.message
== WM_APPCOMMAND
) {
1296 InitWithAppCommand();
1298 InitWithKeyOrChar();
1301 MOZ_LOG(gKeyLog
, LogLevel::Info
,
1302 ("%p NativeKey::NativeKey(), mKeyboardLayout=0x%p, "
1303 "mFocusedWndBeforeDispatch=0x%p, mDOMKeyCode=%s, "
1304 "mKeyNameIndex=%s, mCodeNameIndex=%s, mModKeyState=%s, "
1305 "mVirtualKeyCode=%s, mOriginalVirtualKeyCode=%s, "
1306 "mCommittedCharsAndModifiers=%s, mInputtingStringAndModifiers=%s, "
1307 "mShiftedString=%s, mUnshiftedString=%s, mShiftedLatinChar=%s, "
1308 "mUnshiftedLatinChar=%s, mScanCode=0x%04X, mIsExtended=%s, "
1309 "mIsRepeat=%s, mIsDeadKey=%s, mIsPrintableKey=%s, "
1310 "mIsSkippableInRemoteProcess=%s, mCharMessageHasGone=%s, "
1311 "mIsOverridingKeyboardLayout=%s",
1312 this, mKeyboardLayout
, mFocusedWndBeforeDispatch
,
1313 GetDOMKeyCodeName(mDOMKeyCode
).get(), ToString(mKeyNameIndex
).get(),
1314 ToString(mCodeNameIndex
).get(), ToString(mModKeyState
).get(),
1315 GetVirtualKeyCodeName(mVirtualKeyCode
).get(),
1316 GetVirtualKeyCodeName(mOriginalVirtualKeyCode
).get(),
1317 ToString(mCommittedCharsAndModifiers
).get(),
1318 ToString(mInputtingStringAndModifiers
).get(),
1319 ToString(mShiftedString
).get(), ToString(mUnshiftedString
).get(),
1320 GetCharacterCodeName(mShiftedLatinChar
).get(),
1321 GetCharacterCodeName(mUnshiftedLatinChar
).get(), mScanCode
,
1322 GetBoolName(mIsExtended
), GetBoolName(mIsRepeat
),
1323 GetBoolName(mIsDeadKey
), GetBoolName(mIsPrintableKey
),
1324 GetBoolName(mIsSkippableInRemoteProcess
),
1325 GetBoolName(mCharMessageHasGone
),
1326 GetBoolName(mIsOverridingKeyboardLayout
)));
1329 void NativeKey::InitIsSkippableForKeyOrChar(const MSG
& aLastKeyMSG
) {
1330 mIsSkippableInRemoteProcess
= false;
1333 // If the message is not repeated key message, the event should be always
1334 // handled in remote process even if it's too old.
1338 // Keyboard utilities may send us some generated messages and such messages
1339 // may be marked as "repeated", e.g., SendInput() calls with
1340 // KEYEVENTF_UNICODE but without KEYEVENTF_KEYUP. However, key sequence
1341 // comes from such utilities may be really important. For example, utilities
1342 // may send WM_KEYDOWN for VK_BACK to remove previous character and send
1343 // WM_KEYDOWN for VK_PACKET to insert a composite character. Therefore, we
1344 // should check if current message and previous key message are caused by
1345 // same physical key. If not, the message may be generated by such
1347 // XXX With this approach, if VK_BACK messages are generated with known
1348 // scancode, we cannot distinguish whether coming VK_BACK message is
1349 // actually repeated by the auto-repeat feature. Currently, we need
1350 // this hack only for "SinhalaTamil IME" and fortunately, it generates
1351 // VK_BACK messages with odd scancode. So, we don't need to handle
1352 // VK_BACK specially at least for now.
1354 if (mCodeNameIndex
== CODE_NAME_INDEX_UNKNOWN
) {
1355 // If current event is not caused by physical key operation, it may be
1356 // caused by a keyboard utility. If so, the event shouldn't be ignored by
1357 // BrowserChild since it want to insert the character, delete a character or
1362 if (mOriginalVirtualKeyCode
== VK_PACKET
) {
1363 // If the message is VK_PACKET, that means that a keyboard utility
1364 // tries to insert a character.
1368 switch (mMsg
.message
) {
1374 case WM_SYSDEADCHAR
:
1375 // However, some keyboard layouts may send some keyboard messages with
1376 // activating the bit. If we dispatch repeated keyboard events, they
1377 // may be ignored by BrowserChild due to performance reason. So, we need
1378 // to check if actually a physical key is repeated by the auto-repeat
1380 switch (aLastKeyMSG
.message
) {
1383 if (aLastKeyMSG
.wParam
== VK_PACKET
) {
1384 // If the last message was VK_PACKET, that means that a keyboard
1385 // utility tried to insert a character. So, current message is
1386 // not repeated key event of the previous event.
1389 // Let's check whether current message and previous message are
1390 // caused by same physical key.
1391 mIsSkippableInRemoteProcess
=
1392 mScanCode
== WinUtils::GetScanCode(aLastKeyMSG
.lParam
) &&
1393 mIsExtended
== WinUtils::IsExtendedScanCode(aLastKeyMSG
.lParam
);
1396 // If previous message is not a keydown, this must not be generated
1397 // by the auto-repeat feature.
1401 MOZ_ASSERT_UNREACHABLE(
1402 "WM_APPCOMMAND should be handled in "
1403 "InitWithAppCommand()");
1406 // keyup message shouldn't be repeated by the auto-repeat feature.
1411 void NativeKey::InitWithKeyOrChar() {
1412 MSG lastKeyMSG
= sLastKeyMSG
;
1413 char16_t pendingHighSurrogate
= sPendingHighSurrogate
;
1414 mScanCode
= WinUtils::GetScanCode(mMsg
.lParam
);
1415 mIsExtended
= WinUtils::IsExtendedScanCode(mMsg
.lParam
);
1416 switch (mMsg
.message
) {
1419 sPendingHighSurrogate
= 0;
1423 // Modify sLastKeyMSG now since retrieving following char messages may
1424 // cause sending another key message if odd tool hooks GetMessage(),
1428 // Note that we don't need to compute raw virtual keycode here even when
1429 // it's VK_PROCESS (i.e., already handled by IME) because we need to
1430 // export it as DOM_VK_PROCESS and KEY_NAME_INDEX_Process.
1431 mOriginalVirtualKeyCode
= static_cast<uint8_t>(mMsg
.wParam
);
1433 // If the key message is sent from other application like a11y tools, the
1434 // scancode value might not be set proper value. Then, probably the value
1436 // NOTE: If the virtual keycode can be caused by both non-extended key
1437 // and extended key, the API returns the non-extended key's
1438 // scancode. E.g., VK_LEFT causes "4" key on numpad.
1439 if (!mScanCode
&& mOriginalVirtualKeyCode
!= VK_PACKET
) {
1440 uint16_t scanCodeEx
= ComputeScanCodeExFromVirtualKeyCode(mMsg
.wParam
);
1442 mScanCode
= static_cast<uint8_t>(scanCodeEx
& 0xFF);
1443 uint8_t extended
= static_cast<uint8_t>((scanCodeEx
& 0xFF00) >> 8);
1444 mIsExtended
= (extended
== 0xE0) || (extended
== 0xE1);
1448 // Most keys are not distinguished as left or right keys.
1449 bool isLeftRightDistinguishedKey
= false;
1451 // mOriginalVirtualKeyCode must not distinguish left or right of
1452 // Shift, Control or Alt.
1453 switch (mOriginalVirtualKeyCode
) {
1457 isLeftRightDistinguishedKey
= true;
1461 mVirtualKeyCode
= mOriginalVirtualKeyCode
;
1462 mOriginalVirtualKeyCode
= VK_SHIFT
;
1463 isLeftRightDistinguishedKey
= true;
1467 mVirtualKeyCode
= mOriginalVirtualKeyCode
;
1468 mOriginalVirtualKeyCode
= VK_CONTROL
;
1469 isLeftRightDistinguishedKey
= true;
1473 mVirtualKeyCode
= mOriginalVirtualKeyCode
;
1474 mOriginalVirtualKeyCode
= VK_MENU
;
1475 isLeftRightDistinguishedKey
= true;
1479 // If virtual keycode (left-right distinguished keycode) is already
1480 // computed, we don't need to do anymore.
1481 if (mVirtualKeyCode
) {
1485 // If the keycode doesn't have LR distinguished keycode, we just set
1486 // mOriginalVirtualKeyCode to mVirtualKeyCode. Note that don't compute
1487 // it from MapVirtualKeyEx() because the scan code might be wrong if
1488 // the message is sent/posted by other application. Then, we will compute
1489 // unexpected keycode from the scan code.
1490 if (!isLeftRightDistinguishedKey
) {
1494 NS_ASSERTION(!mVirtualKeyCode
,
1495 "mVirtualKeyCode has been computed already");
1497 // Otherwise, compute the virtual keycode with MapVirtualKeyEx().
1498 mVirtualKeyCode
= ComputeVirtualKeyCodeFromScanCodeEx();
1500 // Following code shouldn't be used now because we compute scancode value
1501 // if we detect that the sender doesn't set proper scancode.
1502 // However, the detection might fail. Therefore, let's keep using this.
1503 switch (mOriginalVirtualKeyCode
) {
1505 if (mVirtualKeyCode
!= VK_LCONTROL
&&
1506 mVirtualKeyCode
!= VK_RCONTROL
) {
1507 mVirtualKeyCode
= mIsExtended
? VK_RCONTROL
: VK_LCONTROL
;
1511 if (mVirtualKeyCode
!= VK_LMENU
&& mVirtualKeyCode
!= VK_RMENU
) {
1512 mVirtualKeyCode
= mIsExtended
? VK_RMENU
: VK_LMENU
;
1516 if (mVirtualKeyCode
!= VK_LSHIFT
&& mVirtualKeyCode
!= VK_RSHIFT
) {
1517 // Neither left shift nor right shift is an extended key,
1518 // let's use VK_LSHIFT for unknown mapping.
1519 mVirtualKeyCode
= VK_LSHIFT
;
1523 MOZ_CRASH("Unsupported mOriginalVirtualKeyCode");
1530 sPendingHighSurrogate
= 0;
1531 // If there is another instance and it is trying to remove a char message
1532 // from the queue, this message should be handled in the old instance.
1533 if (IsAnotherInstanceRemovingCharMessage()) {
1534 // XXX Do we need to make mReceivedMsg an array?
1535 MOZ_ASSERT(IsEmptyMSG(mLastInstance
->mReceivedMsg
));
1537 gKeyLog
, LogLevel::Warning
,
1538 ("%p NativeKey::InitWithKeyOrChar(), WARNING, detecting another "
1539 "instance is trying to remove a char message, so, this instance "
1540 "should do nothing, mLastInstance=0x%p, mRemovingMsg=%s, "
1542 this, mLastInstance
, ToString(mLastInstance
->mRemovingMsg
).get(),
1543 ToString(mLastInstance
->mReceivedMsg
).get()));
1544 mLastInstance
->mReceivedMsg
= mMsg
;
1548 // NOTE: If other applications like a11y tools sends WM_*CHAR without
1549 // scancode, we cannot compute virtual keycode. I.e., with such
1550 // applications, we cannot generate proper KeyboardEvent.code value.
1552 mVirtualKeyCode
= mOriginalVirtualKeyCode
=
1553 ComputeVirtualKeyCodeFromScanCodeEx();
1554 NS_ASSERTION(mVirtualKeyCode
, "Failed to compute virtual keycode");
1557 MOZ_CRASH_UNSAFE_PRINTF("Unsupported message: 0x%04X", mMsg
.message
);
1562 if (!mVirtualKeyCode
) {
1563 mVirtualKeyCode
= mOriginalVirtualKeyCode
;
1566 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
1568 keyboardLayout
->ConvertNativeKeyCodeToDOMKeyCode(mVirtualKeyCode
);
1569 // Be aware, keyboard utilities can change non-printable keys to printable
1570 // keys. In such case, we should make the key value as a printable key.
1571 // FYI: IsFollowedByPrintableCharMessage() returns true only when it's
1572 // handling a keydown message.
1574 IsFollowedByPrintableCharMessage()
1575 ? KEY_NAME_INDEX_USE_STRING
1576 : keyboardLayout
->ConvertNativeKeyCodeToKeyNameIndex(mVirtualKeyCode
);
1577 mCodeNameIndex
= KeyboardLayout::ConvertScanCodeToCodeNameIndex(
1578 GetScanCodeWithExtendedFlag());
1580 // If next message of WM_(SYS)KEYDOWN is WM_*CHAR message and the key
1581 // combination is not reserved by the system, let's consume it now.
1582 // TODO: We cannot initialize mCommittedCharsAndModifiers for VK_PACKET
1583 // if the message is WM_KEYUP because we don't have preceding
1585 // TODO: Like Edge, we shouldn't dispatch two sets of keyboard events
1586 // for a Unicode character in non-BMP because its key value looks
1587 // broken and not good thing for our editor if only one keydown or
1588 // keypress event's default is prevented. I guess, we should store
1589 // key message information globally and we should wait following
1590 // WM_KEYDOWN if following WM_CHAR is a part of a Unicode character.
1591 if ((mMsg
.message
== WM_KEYDOWN
|| mMsg
.message
== WM_SYSKEYDOWN
) &&
1592 !IsReservedBySystem()) {
1594 while (GetFollowingCharMessage(charMsg
)) {
1595 // Although, got message shouldn't be WM_NULL in desktop apps,
1596 // we should keep checking this. FYI: This was added for Metrofox.
1597 if (charMsg
.message
== WM_NULL
) {
1600 MOZ_LOG(gKeyLog
, LogLevel::Info
,
1601 ("%p NativeKey::InitWithKeyOrChar(), removed char message, %s",
1602 this, ToString(charMsg
).get()));
1603 NS_WARNING_ASSERTION(
1604 charMsg
.hwnd
== mMsg
.hwnd
,
1605 "The retrieved char message was targeted to differnet window");
1606 mFollowingCharMsgs
.AppendElement(charMsg
);
1608 if (mFollowingCharMsgs
.Length() == 1) {
1609 // If we receive a keydown message for a high-surrogate, a low-surrogate
1610 // keydown message **will** and should follow it. We cannot translate the
1611 // following WM_KEYDOWN message for the low-surrogate right now since
1612 // it's not yet queued into the message queue yet. Therefore, we need to
1613 // wait next one to dispatch keypress event with setting its `.key` value
1614 // to a surrogate pair rather than setting it to a lone surrogate.
1615 // FYI: This may happen with typing a non-BMP character on the touch
1616 // keyboard on Windows 10 or later except when an IME is installed. (If
1617 // IME is installed, composition is used instead.)
1618 if (IS_HIGH_SURROGATE(mFollowingCharMsgs
[0].wParam
)) {
1619 if (pendingHighSurrogate
) {
1620 MOZ_LOG(gKeyLog
, LogLevel::Warning
,
1621 ("%p NativeKey::InitWithKeyOrChar(), there is pending "
1622 "high surrogate input, but received another high surrogate "
1623 "input. The previous one is discarded",
1626 sPendingHighSurrogate
= mFollowingCharMsgs
[0].wParam
;
1627 mFollowingCharMsgs
.Clear();
1628 } else if (IS_LOW_SURROGATE(mFollowingCharMsgs
[0].wParam
)) {
1629 // If we stopped dispathing a keypress event for a preceding
1630 // high-surrogate, treat this keydown (for a low-surrogate) as
1631 // introducing both the high surrogate and the low surrogate.
1632 if (pendingHighSurrogate
) {
1633 MSG charMsg
= mFollowingCharMsgs
[0];
1634 mFollowingCharMsgs
[0].wParam
= pendingHighSurrogate
;
1635 mFollowingCharMsgs
.AppendElement(std::move(charMsg
));
1638 gKeyLog
, LogLevel::Warning
,
1639 ("%p NativeKey::InitWithKeyOrChar(), there is no pending high "
1640 "surrogate input, but received lone low surrogate input",
1643 } else if (MOZ_UNLIKELY(pendingHighSurrogate
)) {
1644 MOZ_LOG(gKeyLog
, LogLevel::Warning
,
1645 ("%p NativeKey::InitWithKeyOrChar(), there is pending "
1646 "high surrogate input, but received non-surrogate input. "
1647 "The high surrogate input is discarded",
1650 } else if (MOZ_UNLIKELY(pendingHighSurrogate
&&
1651 !mFollowingCharMsgs
.IsEmpty())) {
1652 MOZ_LOG(gKeyLog
, LogLevel::Warning
,
1653 ("%p NativeKey::InitWithKeyOrChar(), there is pending "
1654 "high surrogate input, but received 2 or more character input. "
1655 "The high surrogate input is discarded",
1660 keyboardLayout
->InitNativeKey(*this);
1662 // Now, we can know if the key produces character(s) or a dead key with
1663 // AltGraph modifier. When user emulates AltGr key press with pressing
1664 // both Ctrl and Alt and the key produces character(s) or a dead key, we
1665 // need to replace Control and Alt state with AltGraph if the keyboard
1666 // layout has AltGr key.
1667 // Note that if Ctrl and/or Alt are pressed (not to emulate to press AltGr),
1668 // we need to set actual modifiers to eKeyDown and eKeyUp.
1669 if (MaybeEmulatingAltGraph() &&
1670 (mCommittedCharsAndModifiers
.IsProducingCharsWithAltGr() ||
1671 mKeyNameIndex
== KEY_NAME_INDEX_Dead
)) {
1672 mModKeyState
.Unset(MODIFIER_CONTROL
| MODIFIER_ALT
);
1673 mModKeyState
.Set(MODIFIER_ALTGRAPH
);
1677 (IsFollowedByDeadCharMessage() ||
1678 keyboardLayout
->IsDeadKey(mOriginalVirtualKeyCode
, mModKeyState
));
1679 mIsPrintableKey
= mKeyNameIndex
== KEY_NAME_INDEX_USE_STRING
||
1680 KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode
);
1681 // The repeat count in mMsg.lParam isn't useful to check whether the event
1682 // is caused by the auto-repeat feature because it's not incremented even
1683 // if it's repeated twice or more (i.e., always 1). Therefore, we need to
1684 // check previous key state (31th bit) instead. If it's 1, the key was down
1685 // before the message was sent.
1686 mIsRepeat
= (mMsg
.lParam
& (1 << 30)) != 0;
1687 InitIsSkippableForKeyOrChar(lastKeyMSG
);
1689 if (IsKeyDownMessage()) {
1690 // Compute some strings which may be inputted by the key with various
1691 // modifier state if this key event won't cause text input actually.
1692 // They will be used for setting mAlternativeCharCodes in the callback
1693 // method which will be called by TextEventDispatcher.
1694 if (!IsFollowedByPrintableCharMessage()) {
1695 ComputeInputtingStringWithKeyboardLayout();
1697 // Remove odd char messages if there are.
1698 RemoveFollowingOddCharMessages();
1702 void NativeKey::InitCommittedCharsAndModifiersWithFollowingCharMessages() {
1703 mCommittedCharsAndModifiers
.Clear();
1704 // This should cause inputting text in focused editor. However, it
1705 // ignores keypress events whose altKey or ctrlKey is true.
1706 // Therefore, we need to remove these modifier state here.
1707 Modifiers modifiers
= mModKeyState
.GetModifiers();
1708 if (IsFollowedByPrintableCharMessage()) {
1709 modifiers
&= ~(MODIFIER_ALT
| MODIFIER_CONTROL
);
1710 if (MaybeEmulatingAltGraph()) {
1711 modifiers
|= MODIFIER_ALTGRAPH
;
1714 // NOTE: This method assumes that WM_CHAR and WM_SYSCHAR are never retrieved
1716 for (size_t i
= 0; i
< mFollowingCharMsgs
.Length(); ++i
) {
1717 // Ignore non-printable char messages.
1718 if (!IsPrintableCharOrSysCharMessage(mFollowingCharMsgs
[i
])) {
1721 char16_t ch
= static_cast<char16_t
>(mFollowingCharMsgs
[i
].wParam
);
1722 mCommittedCharsAndModifiers
.Append(ch
, modifiers
);
1726 NativeKey::~NativeKey() {
1727 MOZ_LOG(gKeyLog
, LogLevel::Debug
,
1728 ("%p NativeKey::~NativeKey(), destroyed", this));
1729 if (mIsOverridingKeyboardLayout
) {
1730 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
1731 keyboardLayout
->RestoreLayout();
1733 sLatestInstance
= mLastInstance
;
1736 void NativeKey::InitWithAppCommand() {
1737 if (GET_DEVICE_LPARAM(mMsg
.lParam
) != FAPPCOMMAND_KEY
) {
1741 uint32_t appCommand
= GET_APPCOMMAND_LPARAM(mMsg
.lParam
);
1742 switch (GET_APPCOMMAND_LPARAM(mMsg
.lParam
)) {
1743 #undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX
1744 #define NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX(aAppCommand, aKeyNameIndex) \
1746 mKeyNameIndex = aKeyNameIndex; \
1749 #include "NativeKeyToDOMKeyName.h"
1751 #undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX
1754 mKeyNameIndex
= KEY_NAME_INDEX_Unidentified
;
1757 // Guess the virtual keycode which caused this message.
1758 switch (appCommand
) {
1759 case APPCOMMAND_BROWSER_BACKWARD
:
1760 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_BACK
;
1762 case APPCOMMAND_BROWSER_FORWARD
:
1763 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_FORWARD
;
1765 case APPCOMMAND_BROWSER_REFRESH
:
1766 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_REFRESH
;
1768 case APPCOMMAND_BROWSER_STOP
:
1769 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_STOP
;
1771 case APPCOMMAND_BROWSER_SEARCH
:
1772 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_SEARCH
;
1774 case APPCOMMAND_BROWSER_FAVORITES
:
1775 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_FAVORITES
;
1777 case APPCOMMAND_BROWSER_HOME
:
1778 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_HOME
;
1780 case APPCOMMAND_VOLUME_MUTE
:
1781 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_VOLUME_MUTE
;
1783 case APPCOMMAND_VOLUME_DOWN
:
1784 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_VOLUME_DOWN
;
1786 case APPCOMMAND_VOLUME_UP
:
1787 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_VOLUME_UP
;
1789 case APPCOMMAND_MEDIA_NEXTTRACK
:
1790 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_MEDIA_NEXT_TRACK
;
1792 case APPCOMMAND_MEDIA_PREVIOUSTRACK
:
1793 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_MEDIA_PREV_TRACK
;
1795 case APPCOMMAND_MEDIA_STOP
:
1796 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_MEDIA_STOP
;
1798 case APPCOMMAND_MEDIA_PLAY_PAUSE
:
1799 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_MEDIA_PLAY_PAUSE
;
1801 case APPCOMMAND_LAUNCH_MAIL
:
1802 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_LAUNCH_MAIL
;
1804 case APPCOMMAND_LAUNCH_MEDIA_SELECT
:
1805 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_LAUNCH_MEDIA_SELECT
;
1807 case APPCOMMAND_LAUNCH_APP1
:
1808 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_LAUNCH_APP1
;
1810 case APPCOMMAND_LAUNCH_APP2
:
1811 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_LAUNCH_APP2
;
1817 uint16_t scanCodeEx
= ComputeScanCodeExFromVirtualKeyCode(mVirtualKeyCode
);
1818 mScanCode
= static_cast<uint8_t>(scanCodeEx
& 0xFF);
1819 uint8_t extended
= static_cast<uint8_t>((scanCodeEx
& 0xFF00) >> 8);
1820 mIsExtended
= (extended
== 0xE0) || (extended
== 0xE1);
1821 mDOMKeyCode
= KeyboardLayout::GetInstance()->ConvertNativeKeyCodeToDOMKeyCode(
1822 mOriginalVirtualKeyCode
);
1823 mCodeNameIndex
= KeyboardLayout::ConvertScanCodeToCodeNameIndex(
1824 GetScanCodeWithExtendedFlag());
1825 // If we can map the WM_APPCOMMAND to a virtual keycode, we can trust
1826 // the result of GetKeyboardState(). Otherwise, we dispatch both
1827 // keydown and keyup events from WM_APPCOMMAND handler. Therefore,
1828 // even if WM_APPCOMMAND is caused by auto key repeat, web apps receive
1829 // a pair of DOM keydown and keyup events. I.e., KeyboardEvent.repeat
1830 // should be never true of such keys.
1831 // XXX Isn't the key state always true? If the key press caused this
1832 // WM_APPCOMMAND, that means it's pressed at that time.
1833 if (mVirtualKeyCode
) {
1835 memset(kbdState
, 0, sizeof(kbdState
));
1836 ::GetKeyboardState(kbdState
);
1837 mIsSkippableInRemoteProcess
= mIsRepeat
= !!kbdState
[mVirtualKeyCode
];
1841 bool NativeKey::MaybeEmulatingAltGraph() const {
1842 return IsControl() && IsAlt() && KeyboardLayout::GetInstance()->HasAltGr();
1846 bool NativeKey::IsControlChar(char16_t aChar
) {
1847 static const char16_t U_SPACE
= 0x20;
1848 static const char16_t U_DELETE
= 0x7F;
1849 return aChar
< U_SPACE
|| aChar
== U_DELETE
;
1852 bool NativeKey::IsFollowedByDeadCharMessage() const {
1853 if (mFollowingCharMsgs
.IsEmpty()) {
1856 return IsDeadCharMessage(mFollowingCharMsgs
[0]);
1859 bool NativeKey::IsFollowedByPrintableCharMessage() const {
1860 for (size_t i
= 0; i
< mFollowingCharMsgs
.Length(); ++i
) {
1861 if (IsPrintableCharMessage(mFollowingCharMsgs
[i
])) {
1868 bool NativeKey::IsFollowedByPrintableCharOrSysCharMessage() const {
1869 for (size_t i
= 0; i
< mFollowingCharMsgs
.Length(); ++i
) {
1870 if (IsPrintableCharOrSysCharMessage(mFollowingCharMsgs
[i
])) {
1877 bool NativeKey::IsReservedBySystem() const {
1878 // Alt+Space key is handled by OS, we shouldn't touch it.
1879 if (mModKeyState
.IsAlt() && !mModKeyState
.IsControl() &&
1880 mVirtualKeyCode
== VK_SPACE
) {
1884 // XXX How about Alt+F4? We receive WM_SYSKEYDOWN for F4 before closing the
1885 // window. Although, we don't prevent to close the window but the key
1886 // event shouldn't be exposed to the web.
1891 bool NativeKey::IsIMEDoingKakuteiUndo() const {
1892 // Following message pattern is caused by "Kakutei-Undo" of ATOK or WXG:
1893 // ---------------------------------------------------------------------------
1894 // WM_KEYDOWN * n (wParam = VK_BACK, lParam = 0x1)
1895 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC0000001) # ATOK
1896 // WM_IME_STARTCOMPOSITION * 1 (wParam = 0x0, lParam = 0x0)
1897 // WM_IME_COMPOSITION * 1 (wParam = 0x0, lParam = 0x1BF)
1898 // WM_CHAR * n (wParam = VK_BACK, lParam = 0x1)
1899 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC00E0001)
1900 // ---------------------------------------------------------------------------
1901 // This doesn't match usual key message pattern such as:
1902 // WM_KEYDOWN -> WM_CHAR -> WM_KEYDOWN -> WM_CHAR -> ... -> WM_KEYUP
1903 // See following bugs for the detail.
1904 // https://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese)
1905 // https://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English)
1906 MSG startCompositionMsg
, compositionMsg
, charMsg
;
1907 return WinUtils::PeekMessage(&startCompositionMsg
, mMsg
.hwnd
,
1908 WM_IME_STARTCOMPOSITION
, WM_IME_STARTCOMPOSITION
,
1909 PM_NOREMOVE
| PM_NOYIELD
) &&
1910 WinUtils::PeekMessage(&compositionMsg
, mMsg
.hwnd
, WM_IME_COMPOSITION
,
1911 WM_IME_COMPOSITION
, PM_NOREMOVE
| PM_NOYIELD
) &&
1912 WinUtils::PeekMessage(&charMsg
, mMsg
.hwnd
, WM_CHAR
, WM_CHAR
,
1913 PM_NOREMOVE
| PM_NOYIELD
) &&
1914 startCompositionMsg
.wParam
== 0x0 &&
1915 startCompositionMsg
.lParam
== 0x0 && compositionMsg
.wParam
== 0x0 &&
1916 compositionMsg
.lParam
== 0x1BF && charMsg
.wParam
== VK_BACK
&&
1917 charMsg
.lParam
== 0x1 &&
1918 startCompositionMsg
.time
<= compositionMsg
.time
&&
1919 compositionMsg
.time
<= charMsg
.time
;
1922 void NativeKey::RemoveFollowingOddCharMessages() {
1923 MOZ_ASSERT(IsKeyDownMessage());
1925 // If the keydown message is synthesized for automated tests, there is
1926 // nothing to do here.
1927 if (mFakeCharMsgs
) {
1931 // If there are some following char messages before another key message,
1932 // there is nothing to do here.
1933 if (!mFollowingCharMsgs
.IsEmpty()) {
1937 // If the handling key isn't Backspace, there is nothing to do here.
1938 if (mOriginalVirtualKeyCode
!= VK_BACK
) {
1942 // If we don't see the odd message pattern, there is nothing to do here.
1943 if (!IsIMEDoingKakuteiUndo()) {
1947 // Otherwise, we need to remove odd WM_CHAR messages for ATOK or WXG (both
1948 // of them are Japanese IME).
1950 while (WinUtils::PeekMessage(&msg
, mMsg
.hwnd
, WM_CHAR
, WM_CHAR
,
1951 PM_REMOVE
| PM_NOYIELD
)) {
1952 if (msg
.message
!= WM_CHAR
) {
1953 MOZ_RELEASE_ASSERT(msg
.message
== WM_NULL
,
1954 "Unexpected message was removed");
1958 gKeyLog
, LogLevel::Info
,
1959 ("%p NativeKey::RemoveFollowingOddCharMessages(), removed odd char "
1961 this, ToString(msg
).get()));
1962 mRemovedOddCharMsgs
.AppendElement(msg
);
1966 UINT
NativeKey::GetScanCodeWithExtendedFlag() const {
1970 return (0xE000 | mScanCode
);
1973 uint32_t NativeKey::GetKeyLocation() const {
1974 switch (mVirtualKeyCode
) {
1979 return eKeyLocationLeft
;
1985 return eKeyLocationRight
;
1988 // XXX This code assumes that all keyboard drivers use same mapping.
1989 return !mIsExtended
? eKeyLocationStandard
: eKeyLocationNumpad
;
2002 // XXX This code assumes that all keyboard drivers use same mapping.
2003 return mIsExtended
? eKeyLocationStandard
: eKeyLocationNumpad
;
2005 // NumLock key isn't included due to IE9's behavior.
2021 // Separator key of Brazilian keyboard or JIS keyboard for Mac
2023 return eKeyLocationNumpad
;
2028 NS_WARNING("Failed to decide the key location?");
2032 return eKeyLocationStandard
;
2036 uint8_t NativeKey::ComputeVirtualKeyCodeFromScanCode() const {
2037 return static_cast<uint8_t>(
2038 ::MapVirtualKeyEx(mScanCode
, MAPVK_VSC_TO_VK
, mKeyboardLayout
));
2041 uint8_t NativeKey::ComputeVirtualKeyCodeFromScanCodeEx() const {
2042 // MapVirtualKeyEx() has been improved for supporting extended keys since
2043 // Vista. When we call it for mapping a scancode of an extended key and
2044 // a virtual keycode, we need to add 0xE000 to the scancode.
2045 return static_cast<uint8_t>(::MapVirtualKeyEx(
2046 GetScanCodeWithExtendedFlag(), MAPVK_VSC_TO_VK_EX
, mKeyboardLayout
));
2049 uint16_t NativeKey::ComputeScanCodeExFromVirtualKeyCode(
2050 UINT aVirtualKeyCode
) const {
2051 return static_cast<uint16_t>(
2052 ::MapVirtualKeyEx(aVirtualKeyCode
, MAPVK_VK_TO_VSC_EX
, mKeyboardLayout
));
2055 char16_t
NativeKey::ComputeUnicharFromScanCode() const {
2056 return static_cast<char16_t
>(::MapVirtualKeyEx(
2057 ComputeVirtualKeyCodeFromScanCode(), MAPVK_VK_TO_CHAR
, mKeyboardLayout
));
2060 nsEventStatus
NativeKey::InitKeyEvent(WidgetKeyboardEvent
& aKeyEvent
) const {
2061 return InitKeyEvent(aKeyEvent
, mModKeyState
);
2064 nsEventStatus
NativeKey::InitKeyEvent(
2065 WidgetKeyboardEvent
& aKeyEvent
,
2066 const ModifierKeyState
& aModKeyState
) const {
2067 if (mWidget
->Destroyed()) {
2068 MOZ_CRASH("NativeKey tries to dispatch a key event on destroyed widget");
2071 LayoutDeviceIntPoint
point(0, 0);
2072 mWidget
->InitEvent(aKeyEvent
, &point
);
2074 switch (aKeyEvent
.mMessage
) {
2076 // If it was followed by a char message but it was consumed by somebody,
2077 // we should mark it as consumed because somebody must have handled it
2078 // and we should prevent to do "double action" for the key operation.
2079 // However, for compatibility with older version and other browsers,
2080 // we should dispatch the events even in the web content.
2081 // And also if it's a WM_SYSKEYDOWN which is not followed by WM_SYSCHAR,
2082 // the input may be consumed by the builtin IME to input a Unicode
2083 // character from the code point.
2084 if (mCharMessageHasGone
|| (IsSysKeyDownMessage() && mIsPrintableKey
&&
2085 mFollowingCharMsgs
.IsEmpty())) {
2086 aKeyEvent
.PreventDefaultBeforeDispatch(CrossProcessForwarding::eAllow
);
2088 aKeyEvent
.mKeyCode
= mDOMKeyCode
;
2089 // Unique id for this keydown event and its associated keypress.
2090 sUniqueKeyEventId
++;
2091 aKeyEvent
.mUniqueId
= sUniqueKeyEventId
;
2094 aKeyEvent
.mKeyCode
= mDOMKeyCode
;
2095 // Set defaultPrevented of the key event if the VK_MENU is not a system
2096 // key release, so that the menu bar does not trigger. This helps avoid
2097 // triggering the menu bar for ALT key accelerators used in assistive
2098 // technologies such as Window-Eyes and ZoomText or for switching open
2099 // state of IME. On the other hand, we should dispatch the events even
2100 // in the web content for compatibility with older version and other
2102 if (mOriginalVirtualKeyCode
== VK_MENU
&& mMsg
.message
!= WM_SYSKEYUP
) {
2103 aKeyEvent
.PreventDefaultBeforeDispatch(CrossProcessForwarding::eAllow
);
2107 MOZ_ASSERT(!mCharMessageHasGone
,
2108 "If following char message was consumed by somebody, "
2109 "keydown event should be consumed above");
2110 aKeyEvent
.mUniqueId
= sUniqueKeyEventId
;
2113 MOZ_CRASH("Invalid event message");
2116 aKeyEvent
.mIsRepeat
= mIsRepeat
;
2117 aKeyEvent
.mMaybeSkippableInRemoteProcess
= mIsSkippableInRemoteProcess
;
2118 aKeyEvent
.mKeyNameIndex
= mKeyNameIndex
;
2119 if (mKeyNameIndex
== KEY_NAME_INDEX_USE_STRING
) {
2120 aKeyEvent
.mKeyValue
= mCommittedCharsAndModifiers
.ToString();
2122 aKeyEvent
.mCodeNameIndex
= mCodeNameIndex
;
2123 MOZ_ASSERT(mCodeNameIndex
!= CODE_NAME_INDEX_USE_STRING
);
2124 aKeyEvent
.mLocation
= GetKeyLocation();
2125 aModKeyState
.InitInputEvent(aKeyEvent
);
2127 KeyboardLayout::NotifyIdleServiceOfUserActivity();
2130 gKeyLog
, LogLevel::Info
,
2131 ("%p NativeKey::InitKeyEvent(), initialized, aKeyEvent={ "
2132 "mMessage=%s, mKeyNameIndex=%s, mKeyValue=\"%s\", mCodeNameIndex=%s, "
2133 "mKeyCode=%s, mLocation=%s, mModifiers=%s, DefaultPrevented()=%s }",
2134 this, ToChar(aKeyEvent
.mMessage
),
2135 ToString(aKeyEvent
.mKeyNameIndex
).get(),
2136 NS_ConvertUTF16toUTF8(aKeyEvent
.mKeyValue
).get(),
2137 ToString(aKeyEvent
.mCodeNameIndex
).get(),
2138 GetDOMKeyCodeName(aKeyEvent
.mKeyCode
).get(),
2139 GetKeyLocationName(aKeyEvent
.mLocation
).get(),
2140 GetModifiersName(aKeyEvent
.mModifiers
).get(),
2141 GetBoolName(aKeyEvent
.DefaultPrevented())));
2143 return aKeyEvent
.DefaultPrevented() ? nsEventStatus_eConsumeNoDefault
2144 : nsEventStatus_eIgnore
;
2147 bool NativeKey::DispatchCommandEvent(uint32_t aEventCommand
) const {
2148 RefPtr
<nsAtom
> command
;
2149 switch (aEventCommand
) {
2150 case APPCOMMAND_BROWSER_BACKWARD
:
2151 command
= nsGkAtoms::Back
;
2153 case APPCOMMAND_BROWSER_FORWARD
:
2154 command
= nsGkAtoms::Forward
;
2156 case APPCOMMAND_BROWSER_REFRESH
:
2157 command
= nsGkAtoms::Reload
;
2159 case APPCOMMAND_BROWSER_STOP
:
2160 command
= nsGkAtoms::Stop
;
2162 case APPCOMMAND_BROWSER_SEARCH
:
2163 command
= nsGkAtoms::Search
;
2165 case APPCOMMAND_BROWSER_FAVORITES
:
2166 command
= nsGkAtoms::Bookmarks
;
2168 case APPCOMMAND_BROWSER_HOME
:
2169 command
= nsGkAtoms::Home
;
2171 case APPCOMMAND_CLOSE
:
2172 command
= nsGkAtoms::Close
;
2174 case APPCOMMAND_FIND
:
2175 command
= nsGkAtoms::Find
;
2177 case APPCOMMAND_HELP
:
2178 command
= nsGkAtoms::Help
;
2180 case APPCOMMAND_NEW
:
2181 command
= nsGkAtoms::New
;
2183 case APPCOMMAND_OPEN
:
2184 command
= nsGkAtoms::Open
;
2186 case APPCOMMAND_PRINT
:
2187 command
= nsGkAtoms::Print
;
2189 case APPCOMMAND_SAVE
:
2190 command
= nsGkAtoms::Save
;
2192 case APPCOMMAND_FORWARD_MAIL
:
2193 command
= nsGkAtoms::ForwardMail
;
2195 case APPCOMMAND_REPLY_TO_MAIL
:
2196 command
= nsGkAtoms::ReplyToMail
;
2198 case APPCOMMAND_SEND_MAIL
:
2199 command
= nsGkAtoms::SendMail
;
2201 case APPCOMMAND_MEDIA_NEXTTRACK
:
2202 command
= nsGkAtoms::NextTrack
;
2204 case APPCOMMAND_MEDIA_PREVIOUSTRACK
:
2205 command
= nsGkAtoms::PreviousTrack
;
2207 case APPCOMMAND_MEDIA_STOP
:
2208 command
= nsGkAtoms::MediaStop
;
2210 case APPCOMMAND_MEDIA_PLAY_PAUSE
:
2211 command
= nsGkAtoms::PlayPause
;
2215 gKeyLog
, LogLevel::Info
,
2216 ("%p NativeKey::DispatchCommandEvent(), doesn't dispatch command "
2221 WidgetCommandEvent
appCommandEvent(true, command
, mWidget
);
2223 mWidget
->InitEvent(appCommandEvent
);
2224 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2225 ("%p NativeKey::DispatchCommandEvent(), dispatching "
2226 "%s app command event...",
2227 this, nsAtomCString(command
).get()));
2229 mWidget
->DispatchWindowEvent(appCommandEvent
) || mWidget
->Destroyed();
2231 gKeyLog
, LogLevel::Info
,
2232 ("%p NativeKey::DispatchCommandEvent(), dispatched app command event, "
2233 "result=%s, mWidget->Destroyed()=%s",
2234 this, GetBoolName(ok
), GetBoolName(mWidget
->Destroyed())));
2238 bool NativeKey::HandleAppCommandMessage() const {
2239 // If the widget has gone, we should do nothing.
2240 if (mWidget
->Destroyed()) {
2241 MOZ_LOG(gKeyLog
, LogLevel::Warning
,
2242 ("%p NativeKey::HandleAppCommandMessage(), WARNING, not handled "
2244 "destroyed the widget",
2249 // NOTE: Typical behavior of WM_APPCOMMAND caused by key is, WM_APPCOMMAND
2250 // message is _sent_ first. Then, the DefaultWndProc will _post_
2251 // WM_KEYDOWN message and WM_KEYUP message if the keycode for the
2252 // command is available (i.e., mVirtualKeyCode is not 0).
2254 // NOTE: IntelliType (Microsoft's keyboard utility software) always consumes
2255 // WM_KEYDOWN and WM_KEYUP.
2257 // Let's dispatch keydown message before our chrome handles the command
2258 // when the message is caused by a keypress. This behavior makes handling
2259 // WM_APPCOMMAND be a default action of the keydown event. This means that
2260 // web applications can handle multimedia keys and prevent our default action.
2261 // This allow web applications to provide better UX for multimedia keyboard
2263 bool dispatchKeyEvent
= (GET_DEVICE_LPARAM(mMsg
.lParam
) == FAPPCOMMAND_KEY
);
2264 if (dispatchKeyEvent
) {
2265 // If a plug-in window has focus but it didn't consume the message, our
2266 // window receive WM_APPCOMMAND message. In this case, we shouldn't
2267 // dispatch KeyboardEvents because an event handler may access the
2268 // plug-in process synchronously.
2270 WinUtils::IsOurProcessWindow(reinterpret_cast<HWND
>(mMsg
.wParam
));
2273 bool consumed
= false;
2275 if (dispatchKeyEvent
) {
2276 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2277 if (NS_WARN_IF(NS_FAILED(rv
))) {
2278 MOZ_LOG(gKeyLog
, LogLevel::Error
,
2279 ("%p NativeKey::HandleAppCommandMessage(), FAILED due to "
2280 "BeginNativeInputTransaction() failure",
2284 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2285 ("%p NativeKey::HandleAppCommandMessage(), initializing keydown "
2288 WidgetKeyboardEvent
keydownEvent(true, eKeyDown
, mWidget
);
2289 nsEventStatus status
= InitKeyEvent(keydownEvent
, mModKeyState
);
2290 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2291 ("%p NativeKey::HandleAppCommandMessage(), tries to dispatch "
2294 // NOTE: If the keydown event is consumed by web contents, we shouldn't
2295 // continue to handle the command.
2296 if (!mDispatcher
->DispatchKeyboardEvent(eKeyDown
, keydownEvent
, status
,
2297 const_cast<NativeKey
*>(this))) {
2298 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2299 ("%p NativeKey::HandleAppCommandMessage(), keydown event isn't "
2302 // If keyboard event wasn't fired, there must be composition.
2303 // So, we don't need to dispatch a command event.
2306 consumed
= status
== nsEventStatus_eConsumeNoDefault
;
2307 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2308 ("%p NativeKey::HandleAppCommandMessage(), keydown event was "
2309 "dispatched, consumed=%s",
2310 this, GetBoolName(consumed
)));
2311 sDispatchedKeyOfAppCommand
= mVirtualKeyCode
;
2312 if (mWidget
->Destroyed()) {
2314 gKeyLog
, LogLevel::Info
,
2315 ("%p NativeKey::HandleAppCommandMessage(), keydown event caused "
2316 "destroying the widget",
2322 // Dispatch a command event or a content command event if the command is
2325 uint32_t appCommand
= GET_APPCOMMAND_LPARAM(mMsg
.lParam
);
2326 EventMessage contentCommandMessage
= eVoidEvent
;
2327 switch (appCommand
) {
2328 case APPCOMMAND_BROWSER_BACKWARD
:
2329 case APPCOMMAND_BROWSER_FORWARD
:
2330 case APPCOMMAND_BROWSER_REFRESH
:
2331 case APPCOMMAND_BROWSER_STOP
:
2332 case APPCOMMAND_BROWSER_SEARCH
:
2333 case APPCOMMAND_BROWSER_FAVORITES
:
2334 case APPCOMMAND_BROWSER_HOME
:
2335 case APPCOMMAND_CLOSE
:
2336 case APPCOMMAND_FIND
:
2337 case APPCOMMAND_HELP
:
2338 case APPCOMMAND_NEW
:
2339 case APPCOMMAND_OPEN
:
2340 case APPCOMMAND_PRINT
:
2341 case APPCOMMAND_SAVE
:
2342 case APPCOMMAND_FORWARD_MAIL
:
2343 case APPCOMMAND_REPLY_TO_MAIL
:
2344 case APPCOMMAND_SEND_MAIL
:
2345 case APPCOMMAND_MEDIA_NEXTTRACK
:
2346 case APPCOMMAND_MEDIA_PREVIOUSTRACK
:
2347 case APPCOMMAND_MEDIA_STOP
:
2348 case APPCOMMAND_MEDIA_PLAY_PAUSE
:
2349 // We shouldn't consume the message always because if we don't handle
2350 // the message, the sender (typically, utility of keyboard or mouse)
2351 // may send other key messages which indicate well known shortcut key.
2352 consumed
= DispatchCommandEvent(appCommand
);
2355 // Use content command for following commands:
2356 case APPCOMMAND_COPY
:
2357 contentCommandMessage
= eContentCommandCopy
;
2359 case APPCOMMAND_CUT
:
2360 contentCommandMessage
= eContentCommandCut
;
2362 case APPCOMMAND_PASTE
:
2363 contentCommandMessage
= eContentCommandPaste
;
2365 case APPCOMMAND_REDO
:
2366 contentCommandMessage
= eContentCommandRedo
;
2368 case APPCOMMAND_UNDO
:
2369 contentCommandMessage
= eContentCommandUndo
;
2373 if (contentCommandMessage
) {
2374 MOZ_ASSERT(!mWidget
->Destroyed());
2375 WidgetContentCommandEvent
contentCommandEvent(true, contentCommandMessage
,
2378 gKeyLog
, LogLevel::Info
,
2379 ("%p NativeKey::HandleAppCommandMessage(), dispatching %s event...",
2380 this, ToChar(contentCommandMessage
)));
2381 mWidget
->DispatchWindowEvent(contentCommandEvent
);
2382 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2383 ("%p NativeKey::HandleAppCommandMessage(), dispatched %s event",
2384 this, ToChar(contentCommandMessage
)));
2387 if (mWidget
->Destroyed()) {
2388 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2389 ("%p NativeKey::HandleAppCommandMessage(), %s event caused "
2390 "destroying the widget",
2391 this, ToChar(contentCommandMessage
)));
2395 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2396 ("%p NativeKey::HandleAppCommandMessage(), doesn't dispatch "
2403 // Dispatch a keyup event if the command is caused by pressing a key and
2404 // the key isn't mapped to a virtual keycode.
2405 if (dispatchKeyEvent
&& !mVirtualKeyCode
) {
2406 MOZ_ASSERT(!mWidget
->Destroyed());
2407 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2408 if (NS_WARN_IF(NS_FAILED(rv
))) {
2409 MOZ_LOG(gKeyLog
, LogLevel::Error
,
2410 ("%p NativeKey::HandleAppCommandMessage(), FAILED due to "
2411 "BeginNativeInputTransaction() failure",
2415 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2416 ("%p NativeKey::HandleAppCommandMessage(), initializing keyup "
2419 WidgetKeyboardEvent
keyupEvent(true, eKeyUp
, mWidget
);
2420 nsEventStatus status
= InitKeyEvent(keyupEvent
, mModKeyState
);
2421 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2422 ("%p NativeKey::HandleAppCommandMessage(), dispatching keyup "
2425 // NOTE: Ignore if the keyup event is consumed because keyup event
2426 // represents just a physical key event state change.
2427 mDispatcher
->DispatchKeyboardEvent(eKeyUp
, keyupEvent
, status
,
2428 const_cast<NativeKey
*>(this));
2430 gKeyLog
, LogLevel::Info
,
2431 ("%p NativeKey::HandleAppCommandMessage(), dispatched keyup event",
2433 if (mWidget
->Destroyed()) {
2434 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2435 ("%p NativeKey::HandleAppCommandMessage(), keyup event caused "
2436 "destroying the widget",
2445 bool NativeKey::HandleKeyDownMessage(bool* aEventDispatched
) const {
2446 MOZ_ASSERT(IsKeyDownMessage());
2448 if (aEventDispatched
) {
2449 *aEventDispatched
= false;
2452 if (sDispatchedKeyOfAppCommand
&&
2453 sDispatchedKeyOfAppCommand
== mOriginalVirtualKeyCode
) {
2454 // The multimedia key event has already been dispatch from
2455 // HandleAppCommandMessage().
2456 sDispatchedKeyOfAppCommand
= 0;
2457 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2458 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2459 "event due to already dispatched from HandleAppCommandMessage(), ",
2461 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg
)) {
2462 RedirectedKeyDownMessageManager::Forget();
2467 if (IsReservedBySystem()) {
2468 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2469 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2470 "event because the key combination is reserved by the system",
2472 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg
)) {
2473 RedirectedKeyDownMessageManager::Forget();
2478 if (sPendingHighSurrogate
) {
2479 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2480 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2481 "event because the key introduced only a high surrotate, so we "
2482 "should wait the following low surrogate input",
2484 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg
)) {
2485 RedirectedKeyDownMessageManager::Forget();
2490 // If the widget has gone, we should do nothing.
2491 if (mWidget
->Destroyed()) {
2493 gKeyLog
, LogLevel::Warning
,
2494 ("%p NativeKey::HandleKeyDownMessage(), WARNING, not handled due to "
2495 "destroyed the widget",
2497 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg
)) {
2498 RedirectedKeyDownMessageManager::Forget();
2503 bool defaultPrevented
= false;
2504 if (mFakeCharMsgs
||
2505 !RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg
)) {
2506 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2507 if (NS_WARN_IF(NS_FAILED(rv
))) {
2508 MOZ_LOG(gKeyLog
, LogLevel::Error
,
2509 ("%p NativeKey::HandleKeyDownMessage(), FAILED due to "
2510 "BeginNativeInputTransaction() failure",
2515 bool isIMEEnabled
= WinUtils::IsIMEEnabled(mWidget
->GetInputContext());
2517 MOZ_LOG(gKeyLog
, LogLevel::Debug
,
2518 ("%p NativeKey::HandleKeyDownMessage(), initializing keydown "
2522 WidgetKeyboardEvent
keydownEvent(true, eKeyDown
, mWidget
);
2523 nsEventStatus status
= InitKeyEvent(keydownEvent
, mModKeyState
);
2525 gKeyLog
, LogLevel::Info
,
2526 ("%p NativeKey::HandleKeyDownMessage(), dispatching keydown event...",
2528 bool dispatched
= mDispatcher
->DispatchKeyboardEvent(
2529 eKeyDown
, keydownEvent
, status
, const_cast<NativeKey
*>(this));
2530 if (aEventDispatched
) {
2531 *aEventDispatched
= dispatched
;
2534 // If the keydown event wasn't fired, there must be composition.
2535 // we don't need to do anything anymore.
2537 gKeyLog
, LogLevel::Info
,
2538 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keypress "
2539 "event(s) because keydown event isn't dispatched actually",
2543 defaultPrevented
= status
== nsEventStatus_eConsumeNoDefault
;
2545 if (mWidget
->Destroyed() || IsFocusedWindowChanged()) {
2546 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2547 ("%p NativeKey::HandleKeyDownMessage(), keydown event caused "
2548 "destroying the widget",
2554 gKeyLog
, LogLevel::Info
,
2555 ("%p NativeKey::HandleKeyDownMessage(), dispatched keydown event, "
2556 "dispatched=%s, defaultPrevented=%s",
2557 this, GetBoolName(dispatched
), GetBoolName(defaultPrevented
)));
2559 // If IMC wasn't associated to the window but is associated it now (i.e.,
2560 // focus is moved from a non-editable editor to an editor by keydown
2561 // event handler), WM_CHAR and WM_SYSCHAR shouldn't cause first character
2562 // inputting if IME is opened. But then, we should redirect the native
2563 // keydown message to IME.
2564 // However, note that if focus has been already moved to another
2565 // application, we shouldn't redirect the message to it because the keydown
2566 // message is processed by us, so, nobody shouldn't process it.
2567 HWND focusedWnd
= ::GetFocus();
2568 if (!defaultPrevented
&& !mFakeCharMsgs
&& focusedWnd
&& !isIMEEnabled
&&
2569 WinUtils::IsIMEEnabled(mWidget
->GetInputContext())) {
2570 RedirectedKeyDownMessageManager::RemoveNextCharMessage(focusedWnd
);
2573 keyinput
.type
= INPUT_KEYBOARD
;
2574 keyinput
.ki
.wVk
= mOriginalVirtualKeyCode
;
2575 keyinput
.ki
.wScan
= mScanCode
;
2576 keyinput
.ki
.dwFlags
= KEYEVENTF_SCANCODE
;
2578 keyinput
.ki
.dwFlags
|= KEYEVENTF_EXTENDEDKEY
;
2580 keyinput
.ki
.time
= 0;
2581 keyinput
.ki
.dwExtraInfo
= 0;
2583 RedirectedKeyDownMessageManager::WillRedirect(mMsg
, defaultPrevented
);
2585 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2586 ("%p NativeKey::HandleKeyDownMessage(), redirecting %s...",
2587 this, ToString(mMsg
).get()));
2589 ::SendInput(1, &keyinput
, sizeof(keyinput
));
2591 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2592 ("%p NativeKey::HandleKeyDownMessage(), redirected %s", this,
2593 ToString(mMsg
).get()));
2595 // Return here. We shouldn't dispatch keypress event for this WM_KEYDOWN.
2596 // If it's needed, it will be dispatched after next (redirected)
2601 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2602 ("%p NativeKey::HandleKeyDownMessage(), received a redirected %s",
2603 this, ToString(mMsg
).get()));
2605 defaultPrevented
= RedirectedKeyDownMessageManager::DefaultPrevented();
2606 // If this is redirected keydown message, we have dispatched the keydown
2608 if (aEventDispatched
) {
2609 *aEventDispatched
= true;
2613 RedirectedKeyDownMessageManager::Forget();
2615 MOZ_ASSERT(!mWidget
->Destroyed());
2617 // If the key was processed by IME and didn't cause WM_(SYS)CHAR messages, we
2618 // shouldn't dispatch keypress event.
2619 if (mOriginalVirtualKeyCode
== VK_PROCESSKEY
&&
2620 !IsFollowedByPrintableCharOrSysCharMessage()) {
2621 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2622 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2623 "event because the key was already handled by IME, "
2624 "defaultPrevented=%s",
2625 this, GetBoolName(defaultPrevented
)));
2626 return defaultPrevented
;
2629 if (defaultPrevented
) {
2630 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2631 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2632 "event because preceding keydown event was consumed",
2637 MOZ_ASSERT(!mCharMessageHasGone
,
2638 "If following char message was consumed by somebody, "
2639 "keydown event should have been consumed before dispatch");
2641 // If mCommittedCharsAndModifiers was initialized with following char
2642 // messages, we should dispatch keypress events with its information.
2643 if (IsFollowedByPrintableCharOrSysCharMessage()) {
2644 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2645 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2646 "keypress events with retrieved char messages...",
2648 return DispatchKeyPressEventsWithRetrievedCharMessages();
2651 // If we won't be getting a WM_CHAR, WM_SYSCHAR or WM_DEADCHAR, synthesize a
2652 // keypress for almost all keys
2653 if (NeedsToHandleWithoutFollowingCharMessages()) {
2654 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2655 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2656 "keypress events...",
2658 return DispatchKeyPressEventsWithoutCharMessage();
2661 // If WM_KEYDOWN of VK_PACKET isn't followed by WM_CHAR, we don't need to
2662 // dispatch keypress events.
2663 if (mVirtualKeyCode
== VK_PACKET
) {
2664 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2665 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2667 "because the key is VK_PACKET and there are no char messages",
2672 if (!mModKeyState
.IsControl() && !mModKeyState
.IsAlt() &&
2673 !mModKeyState
.IsWin() && mIsPrintableKey
) {
2674 // If this is simple KeyDown event but next message is not WM_CHAR,
2675 // this event may not input text, so we should ignore this event.
2677 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2678 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2680 "because the key event is simple printable key's event but not "
2688 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2689 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2691 "because the key is a dead key and not followed by char messages",
2696 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2697 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2698 "keypress events due to no following char messages...",
2700 return DispatchKeyPressEventsWithoutCharMessage();
2703 bool NativeKey::HandleCharMessage(bool* aEventDispatched
) const {
2704 MOZ_ASSERT(IsCharOrSysCharMessage(mMsg
));
2705 return HandleCharMessage(mMsg
, aEventDispatched
);
2708 bool NativeKey::HandleCharMessage(const MSG
& aCharMsg
,
2709 bool* aEventDispatched
) const {
2710 MOZ_ASSERT(IsKeyDownMessage() || IsCharOrSysCharMessage(mMsg
));
2711 MOZ_ASSERT(IsCharOrSysCharMessage(aCharMsg
.message
));
2713 if (aEventDispatched
) {
2714 *aEventDispatched
= false;
2717 if ((IsCharOrSysCharMessage(mMsg
) || IsEnterKeyPressCharMessage(mMsg
)) &&
2718 IsAnotherInstanceRemovingCharMessage()) {
2720 gKeyLog
, LogLevel::Warning
,
2721 ("%p NativeKey::HandleCharMessage(), WARNING, does nothing because "
2722 "the message should be handled in another instance removing this "
2725 // Consume this for now because it will be handled by another instance.
2729 // If the key combinations is reserved by the system, we shouldn't dispatch
2730 // eKeyPress event for it and passes the message to next wndproc.
2731 if (IsReservedBySystem()) {
2732 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2733 ("%p NativeKey::HandleCharMessage(), doesn't dispatch keypress "
2734 "event because the key combination is reserved by the system",
2739 // If the widget has gone, we should do nothing.
2740 if (mWidget
->Destroyed()) {
2741 MOZ_LOG(gKeyLog
, LogLevel::Warning
,
2742 ("%p NativeKey::HandleCharMessage(), WARNING, not handled due to "
2743 "destroyed the widget",
2748 // When a control key is inputted by a key, it should be handled without
2749 // WM_*CHAR messages at receiving WM_*KEYDOWN message. So, when we receive
2750 // WM_*CHAR message directly, we see a control character here.
2751 // Note that when the char is '\r', it means that the char message should
2752 // cause "Enter" keypress event for inserting a line break.
2753 if (IsControlCharMessage(aCharMsg
) && !IsEnterKeyPressCharMessage(aCharMsg
)) {
2754 // In this case, we don't need to dispatch eKeyPress event because:
2755 // 1. We're the only browser which dispatches "keypress" event for
2756 // non-printable characters (Although, both Chrome and Edge dispatch
2757 // "keypress" event for some keys accidentally. For example, "IntlRo"
2758 // key with Ctrl of Japanese keyboard layout).
2759 // 2. Currently, we may handle shortcut keys with "keydown" event if
2760 // it's reserved or something. So, we shouldn't dispatch "keypress"
2761 // event without it.
2762 // Note that this does NOT mean we stop dispatching eKeyPress event for
2763 // key presses causes a control character when Ctrl is pressed. In such
2764 // case, DispatchKeyPressEventsWithoutCharMessage() dispatches eKeyPress
2765 // instead of this method.
2767 gKeyLog
, LogLevel::Info
,
2768 ("%p NativeKey::HandleCharMessage(), doesn't dispatch keypress "
2769 "event because received a control character input without WM_KEYDOWN",
2774 // XXXmnakano I think that if mMsg is WM_CHAR, i.e., it comes without
2775 // preceding WM_KEYDOWN, we should should dispatch composition
2776 // events instead of eKeyPress because they are not caused by
2777 // actual keyboard operation.
2779 // First, handle normal text input or non-printable key case here.
2780 WidgetKeyboardEvent
keypressEvent(true, eKeyPress
, mWidget
);
2781 if (IsEnterKeyPressCharMessage(aCharMsg
)) {
2782 keypressEvent
.mKeyCode
= NS_VK_RETURN
;
2784 keypressEvent
.mCharCode
= static_cast<uint32_t>(aCharMsg
.wParam
);
2786 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2787 if (NS_WARN_IF(NS_FAILED(rv
))) {
2788 MOZ_LOG(gKeyLog
, LogLevel::Error
,
2789 ("%p NativeKey::HandleCharMessage(), FAILED due to "
2790 "BeginNativeInputTransaction() failure",
2795 MOZ_LOG(gKeyLog
, LogLevel::Debug
,
2796 ("%p NativeKey::HandleCharMessage(), initializing keypress "
2800 ModifierKeyState
modKeyState(mModKeyState
);
2801 // When AltGr is pressed, both Alt and Ctrl are active. However, when they
2802 // are active, TextEditor won't treat the keypress event as inputting a
2803 // character. Therefore, when AltGr is pressed and the key tries to input
2804 // a character, let's set them to false.
2805 if (modKeyState
.IsControl() && modKeyState
.IsAlt() &&
2806 IsPrintableCharMessage(aCharMsg
)) {
2807 modKeyState
.Unset(MODIFIER_ALT
| MODIFIER_CONTROL
);
2809 nsEventStatus status
= InitKeyEvent(keypressEvent
, modKeyState
);
2810 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2811 ("%p NativeKey::HandleCharMessage(), dispatching keypress event...",
2813 bool dispatched
= mDispatcher
->MaybeDispatchKeypressEvents(
2814 keypressEvent
, status
, const_cast<NativeKey
*>(this));
2815 if (aEventDispatched
) {
2816 *aEventDispatched
= dispatched
;
2818 if (mWidget
->Destroyed()) {
2819 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2820 ("%p NativeKey::HandleCharMessage(), keypress event caused "
2821 "destroying the widget",
2825 bool consumed
= status
== nsEventStatus_eConsumeNoDefault
;
2826 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2827 ("%p NativeKey::HandleCharMessage(), dispatched keypress event, "
2828 "dispatched=%s, consumed=%s",
2829 this, GetBoolName(dispatched
), GetBoolName(consumed
)));
2833 bool NativeKey::HandleKeyUpMessage(bool* aEventDispatched
) const {
2834 MOZ_ASSERT(IsKeyUpMessage());
2836 if (aEventDispatched
) {
2837 *aEventDispatched
= false;
2840 // If the key combinations is reserved by the system, we shouldn't dispatch
2841 // eKeyUp event for it and passes the message to next wndproc.
2842 if (IsReservedBySystem()) {
2843 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2844 ("%p NativeKey::HandleKeyUpMessage(), doesn't dispatch keyup "
2845 "event because the key combination is reserved by the system",
2850 if (sPendingHighSurrogate
) {
2851 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2852 ("%p NativeKey::HandleKeyUpMessage(), doesn't dispatch keyup "
2853 "event because the key introduced only a high surrotate, so we "
2854 "should wait the following low surrogate input",
2859 // If the widget has gone, we should do nothing.
2860 if (mWidget
->Destroyed()) {
2862 gKeyLog
, LogLevel::Warning
,
2863 ("%p NativeKey::HandleKeyUpMessage(), WARNING, not handled due to "
2864 "destroyed the widget",
2869 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2870 if (NS_WARN_IF(NS_FAILED(rv
))) {
2871 MOZ_LOG(gKeyLog
, LogLevel::Error
,
2872 ("%p NativeKey::HandleKeyUpMessage(), FAILED due to "
2873 "BeginNativeInputTransaction() failure",
2878 MOZ_LOG(gKeyLog
, LogLevel::Debug
,
2879 ("%p NativeKey::HandleKeyUpMessage(), initializing keyup event...",
2881 WidgetKeyboardEvent
keyupEvent(true, eKeyUp
, mWidget
);
2882 nsEventStatus status
= InitKeyEvent(keyupEvent
, mModKeyState
);
2883 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2884 ("%p NativeKey::HandleKeyUpMessage(), dispatching keyup event...",
2886 bool dispatched
= mDispatcher
->DispatchKeyboardEvent(
2887 eKeyUp
, keyupEvent
, status
, const_cast<NativeKey
*>(this));
2888 if (aEventDispatched
) {
2889 *aEventDispatched
= dispatched
;
2891 if (mWidget
->Destroyed()) {
2892 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2893 ("%p NativeKey::HandleKeyUpMessage(), keyup event caused "
2894 "destroying the widget",
2898 bool consumed
= status
== nsEventStatus_eConsumeNoDefault
;
2899 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2900 ("%p NativeKey::HandleKeyUpMessage(), dispatched keyup event, "
2901 "dispatched=%s, consumed=%s",
2902 this, GetBoolName(dispatched
), GetBoolName(consumed
)));
2906 bool NativeKey::NeedsToHandleWithoutFollowingCharMessages() const {
2907 MOZ_ASSERT(IsKeyDownMessage());
2909 // If the key combination is reserved by the system, the caller shouldn't
2910 // do anything with following WM_*CHAR messages. So, let's return true here.
2911 if (IsReservedBySystem()) {
2915 // If the keydown message is generated for inputting some Unicode characters
2916 // via SendInput() API, we need to handle it only with WM_*CHAR messages.
2917 if (mVirtualKeyCode
== VK_PACKET
) {
2921 // If following char message is for a control character, it should be handled
2922 // without WM_CHAR message. This is typically Ctrl + [a-z].
2923 if (mFollowingCharMsgs
.Length() == 1 &&
2924 IsControlCharMessage(mFollowingCharMsgs
[0])) {
2928 // If keydown message is followed by WM_CHAR or WM_SYSCHAR whose wParam isn't
2929 // a control character, we should dispatch keypress event with the char
2930 // message even with any modifier state.
2931 if (IsFollowedByPrintableCharOrSysCharMessage()) {
2935 // If any modifier keys which may cause printable keys becoming non-printable
2936 // are not pressed, we don't need special handling for the key.
2937 // Note that if the key does not produce a character with AltGr and when
2938 // AltGr key is pressed, we don't need to dispatch eKeyPress event for it
2939 // because AltGr shouldn't be used for a modifier for a shortcut without
2940 // Ctrl, Alt or Win. That means that we should treat it in same path for
2942 if (!mModKeyState
.IsControl() && !mModKeyState
.IsAlt() &&
2943 !mModKeyState
.IsWin()) {
2947 // If the key event causes dead key event, we don't need to dispatch keypress
2949 if (mIsDeadKey
&& mCommittedCharsAndModifiers
.IsEmpty()) {
2953 // Even if the key is a printable key, it might cause non-printable character
2954 // input with modifier key(s).
2955 return mIsPrintableKey
;
2958 static nsCString
GetResultOfInSendMessageEx() {
2959 DWORD ret
= ::InSendMessageEx(nullptr);
2961 return "ISMEX_NOSEND"_ns
;
2964 if (ret
& ISMEX_CALLBACK
) {
2965 result
= "ISMEX_CALLBACK";
2967 if (ret
& ISMEX_NOTIFY
) {
2968 if (!result
.IsEmpty()) {
2971 result
+= "ISMEX_NOTIFY";
2973 if (ret
& ISMEX_REPLIED
) {
2974 if (!result
.IsEmpty()) {
2977 result
+= "ISMEX_REPLIED";
2979 if (ret
& ISMEX_SEND
) {
2980 if (!result
.IsEmpty()) {
2983 result
+= "ISMEX_SEND";
2988 bool NativeKey::MayBeSameCharMessage(const MSG
& aCharMsg1
,
2989 const MSG
& aCharMsg2
) const {
2990 // NOTE: Although, we don't know when this case occurs, the scan code value
2991 // in lParam may be changed from 0 to something. The changed value
2992 // is different from the scan code of handling keydown message.
2993 static const LPARAM kScanCodeMask
= 0x00FF0000;
2994 return aCharMsg1
.message
== aCharMsg2
.message
&&
2995 aCharMsg1
.wParam
== aCharMsg2
.wParam
&&
2996 (aCharMsg1
.lParam
& ~kScanCodeMask
) ==
2997 (aCharMsg2
.lParam
& ~kScanCodeMask
);
3000 bool NativeKey::IsSamePhysicalKeyMessage(const MSG
& aKeyOrCharMsg1
,
3001 const MSG
& aKeyOrCharMsg2
) const {
3002 if (NS_WARN_IF(aKeyOrCharMsg1
.message
< WM_KEYFIRST
) ||
3003 NS_WARN_IF(aKeyOrCharMsg1
.message
> WM_KEYLAST
) ||
3004 NS_WARN_IF(aKeyOrCharMsg2
.message
< WM_KEYFIRST
) ||
3005 NS_WARN_IF(aKeyOrCharMsg2
.message
> WM_KEYLAST
)) {
3008 return WinUtils::GetScanCode(aKeyOrCharMsg1
.lParam
) ==
3009 WinUtils::GetScanCode(aKeyOrCharMsg2
.lParam
) &&
3010 WinUtils::IsExtendedScanCode(aKeyOrCharMsg1
.lParam
) ==
3011 WinUtils::IsExtendedScanCode(aKeyOrCharMsg2
.lParam
);
3014 bool NativeKey::GetFollowingCharMessage(MSG
& aCharMsg
) {
3015 MOZ_ASSERT(IsKeyDownMessage());
3017 aCharMsg
.message
= WM_NULL
;
3019 if (mFakeCharMsgs
) {
3020 for (size_t i
= 0; i
< mFakeCharMsgs
->Length(); i
++) {
3021 FakeCharMsg
& fakeCharMsg
= mFakeCharMsgs
->ElementAt(i
);
3022 if (fakeCharMsg
.mConsumed
) {
3025 MSG charMsg
= fakeCharMsg
.GetCharMsg(mMsg
.hwnd
);
3026 fakeCharMsg
.mConsumed
= true;
3027 if (!IsCharMessage(charMsg
)) {
3036 // If next key message is not char message, we should give up to find a
3037 // related char message for the handling keydown event for now.
3038 // Note that it's possible other applications may send other key message
3039 // after we call TranslateMessage(). That may cause PeekMessage() failing
3040 // to get char message for the handling keydown message.
3042 if (!WinUtils::PeekMessage(&nextKeyMsg
, mMsg
.hwnd
, WM_KEYFIRST
, WM_KEYLAST
,
3043 PM_NOREMOVE
| PM_NOYIELD
) ||
3044 !IsCharMessage(nextKeyMsg
)) {
3045 MOZ_LOG(gKeyLog
, LogLevel::Debug
,
3046 ("%p NativeKey::GetFollowingCharMessage(), there are no char "
3051 const MSG kFoundCharMsg
= nextKeyMsg
;
3053 AutoRestore
<MSG
> saveLastRemovingMsg(mRemovingMsg
);
3054 mRemovingMsg
= nextKeyMsg
;
3056 mReceivedMsg
= sEmptyMSG
;
3057 AutoRestore
<MSG
> ensureToClearRecivedMsg(mReceivedMsg
);
3059 // On Metrofox, PeekMessage() sometimes returns WM_NULL even if we specify
3060 // the message range. So, if it returns WM_NULL, we should retry to get
3061 // the following char message it was found above.
3062 for (uint32_t i
= 0; i
< 50; i
++) {
3063 MSG removedMsg
, nextKeyMsgInAllWindows
;
3064 bool doCrash
= false;
3065 if (!WinUtils::PeekMessage(&removedMsg
, mMsg
.hwnd
, nextKeyMsg
.message
,
3066 nextKeyMsg
.message
, PM_REMOVE
| PM_NOYIELD
)) {
3067 // We meets unexpected case. We should collect the message queue state
3068 // and crash for reporting the bug.
3071 // If another instance was created for the removing message during trying
3072 // to remove a char message, the instance didn't handle it for preventing
3073 // recursive handling. So, let's handle it in this instance.
3074 if (!IsEmptyMSG(mReceivedMsg
)) {
3075 // If focus is moved to different window, we shouldn't handle it on
3076 // the widget. Let's discard it for now.
3077 if (mReceivedMsg
.hwnd
!= nextKeyMsg
.hwnd
) {
3079 gKeyLog
, LogLevel::Warning
,
3080 ("%p NativeKey::GetFollowingCharMessage(), WARNING, received a "
3081 "char message during removing it from the queue, but it's for "
3082 "different window, mReceivedMsg=%s, nextKeyMsg=%s, "
3084 this, ToString(mReceivedMsg
).get(), ToString(nextKeyMsg
).get(),
3085 ToString(kFoundCharMsg
).get()));
3086 // There might still exist char messages, the loop of calling
3087 // this method should be continued.
3088 aCharMsg
.message
= WM_NULL
;
3091 // Even if the received message is different from what we tried to
3092 // remove from the queue, let's take the received message as a part of
3093 // the result of this key sequence.
3094 if (mReceivedMsg
.message
!= nextKeyMsg
.message
||
3095 mReceivedMsg
.wParam
!= nextKeyMsg
.wParam
||
3096 mReceivedMsg
.lParam
!= nextKeyMsg
.lParam
) {
3098 gKeyLog
, LogLevel::Warning
,
3099 ("%p NativeKey::GetFollowingCharMessage(), WARNING, received a "
3100 "char message during removing it from the queue, but it's "
3101 "differnt from what trying to remove from the queue, "
3102 "aCharMsg=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3103 this, ToString(mReceivedMsg
).get(), ToString(nextKeyMsg
).get(),
3104 ToString(kFoundCharMsg
).get()));
3107 gKeyLog
, LogLevel::Debug
,
3108 ("%p NativeKey::GetFollowingCharMessage(), succeeded to "
3109 "retrieve next char message via another instance, aCharMsg=%s, "
3111 this, ToString(mReceivedMsg
).get(),
3112 ToString(kFoundCharMsg
).get()));
3114 aCharMsg
= mReceivedMsg
;
3118 // The char message is redirected to different thread's window by focus
3119 // move or something or just cancelled by external application.
3120 if (!WinUtils::PeekMessage(&nextKeyMsgInAllWindows
, 0, WM_KEYFIRST
,
3121 WM_KEYLAST
, PM_NOREMOVE
| PM_NOYIELD
)) {
3123 gKeyLog
, LogLevel::Warning
,
3124 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3125 "remove a char message, but it's already gone from all message "
3126 "queues, nextKeyMsg=%s, kFoundCharMsg=%s",
3127 this, ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3128 return true; // XXX should return false in this case
3130 // The next key message is redirected to different window created by our
3131 // thread, we should do nothing because we must not have focus.
3132 if (nextKeyMsgInAllWindows
.hwnd
!= mMsg
.hwnd
) {
3133 aCharMsg
= nextKeyMsgInAllWindows
;
3135 gKeyLog
, LogLevel::Warning
,
3136 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3137 "remove a char message, but found in another message queue, "
3138 "nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3139 this, ToString(nextKeyMsgInAllWindows
).get(),
3140 ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3143 // If next key message becomes non-char message, this key operation
3144 // may have already been consumed or canceled.
3145 if (!IsCharMessage(nextKeyMsgInAllWindows
)) {
3147 gKeyLog
, LogLevel::Warning
,
3148 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3149 "remove a char message and next key message becomes non-char "
3150 "message, nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, "
3152 this, ToString(nextKeyMsgInAllWindows
).get(),
3153 ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3154 MOZ_ASSERT(!mCharMessageHasGone
);
3155 mFollowingCharMsgs
.Clear();
3156 mCharMessageHasGone
= true;
3159 // If next key message is still a char message but different key message,
3160 // we should treat current key operation is consumed or canceled and
3161 // next char message should be handled as an orphan char message later.
3162 if (!IsSamePhysicalKeyMessage(nextKeyMsgInAllWindows
, kFoundCharMsg
)) {
3164 gKeyLog
, LogLevel::Warning
,
3165 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3166 "remove a char message and next key message becomes differnt "
3167 "key's char message, nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, "
3169 this, ToString(nextKeyMsgInAllWindows
).get(),
3170 ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3171 MOZ_ASSERT(!mCharMessageHasGone
);
3172 mFollowingCharMsgs
.Clear();
3173 mCharMessageHasGone
= true;
3176 // If next key message is still a char message but the message is changed,
3177 // we should retry to remove the new message with PeekMessage() again.
3178 if (nextKeyMsgInAllWindows
.message
!= nextKeyMsg
.message
) {
3180 gKeyLog
, LogLevel::Warning
,
3181 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3182 "remove a char message due to message change, let's retry to "
3183 "remove the message with newly found char message, "
3184 "nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3185 this, ToString(nextKeyMsgInAllWindows
).get(),
3186 ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3187 nextKeyMsg
= nextKeyMsgInAllWindows
;
3190 // If there is still existing a char message caused by same physical key
3191 // in the queue but PeekMessage(PM_REMOVE) failed to remove it from the
3192 // queue, it might be possible that the odd keyboard layout or utility
3193 // hooks only PeekMessage(PM_NOREMOVE) and GetMessage(). So, let's try
3194 // remove the char message with GetMessage() again.
3195 // FYI: The wParam might be different from the found message, but it's
3196 // okay because we assume that odd keyboard layouts return actual
3197 // inputting character at removing the char message.
3198 if (WinUtils::GetMessage(&removedMsg
, mMsg
.hwnd
, nextKeyMsg
.message
,
3199 nextKeyMsg
.message
)) {
3201 gKeyLog
, LogLevel::Warning
,
3202 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3203 "remove a char message, but succeeded with GetMessage(), "
3204 "removedMsg=%s, kFoundCharMsg=%s",
3205 this, ToString(removedMsg
).get(), ToString(kFoundCharMsg
).get()));
3206 // Cancel to crash, but we need to check the removed message value.
3209 // If we've already removed some WM_NULL messages from the queue and
3210 // the found message has already gone from the queue, let's treat the key
3211 // as inputting no characters and already consumed.
3214 gKeyLog
, LogLevel::Warning
,
3215 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3216 "remove a char message, but removed %d WM_NULL messages",
3218 // If the key is a printable key or a control key but tried to input
3219 // a character, mark mCharMessageHasGone true for handling the keydown
3220 // event as inputting empty string.
3221 MOZ_ASSERT(!mCharMessageHasGone
);
3222 mFollowingCharMsgs
.Clear();
3223 mCharMessageHasGone
= true;
3226 MOZ_LOG(gKeyLog
, LogLevel::Error
,
3227 ("%p NativeKey::GetFollowingCharMessage(), FAILED, lost target "
3228 "message to remove, nextKeyMsg=%s",
3229 this, ToString(nextKeyMsg
).get()));
3233 nsPrintfCString
info(
3234 "\nPeekMessage() failed to remove char message! "
3235 "\nActive keyboard layout=0x%p (%s), "
3236 "\nHandling message: %s, InSendMessageEx()=%s, "
3237 "\nFound message: %s, "
3238 "\nWM_NULL has been removed: %d, "
3239 "\nNext key message in all windows: %s, "
3241 KeyboardLayout::GetInstance()->GetLoadedLayout(),
3242 KeyboardLayout::GetInstance()->GetLoadedLayoutName().get(),
3243 ToString(mMsg
).get(), GetResultOfInSendMessageEx().get(),
3244 ToString(kFoundCharMsg
).get(), i
,
3245 ToString(nextKeyMsgInAllWindows
).get(), nextKeyMsgInAllWindows
.time
);
3246 CrashReporter::AppendAppNotesToCrashReport(info
);
3248 if (WinUtils::PeekMessage(&nextMsg
, 0, 0, 0, PM_NOREMOVE
| PM_NOYIELD
)) {
3249 nsPrintfCString
info("\nNext message in all windows: %s, time=%ld",
3250 ToString(nextMsg
).get(), nextMsg
.time
);
3251 CrashReporter::AppendAppNotesToCrashReport(info
);
3253 CrashReporter::AppendAppNotesToCrashReport(
3254 "\nThere is no message in any window"_ns
);
3257 MOZ_CRASH("We lost the following char message");
3260 // We're still not sure why ::PeekMessage() may return WM_NULL even with
3261 // its first message and its last message are same message. However,
3262 // at developing Metrofox, we met this case even with usual keyboard
3263 // layouts. So, it might be possible in desktop application or it really
3264 // occurs with some odd keyboard layouts which perhaps hook API.
3265 if (removedMsg
.message
== WM_NULL
) {
3266 MOZ_LOG(gKeyLog
, LogLevel::Warning
,
3267 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3268 "remove a char message, instead, removed WM_NULL message, "
3270 this, ToString(removedMsg
).get()));
3271 // Check if there is the message which we're trying to remove.
3273 if (!WinUtils::PeekMessage(&newNextKeyMsg
, mMsg
.hwnd
, WM_KEYFIRST
,
3274 WM_KEYLAST
, PM_NOREMOVE
| PM_NOYIELD
)) {
3275 // If there is no key message, we should mark this keydown as consumed
3276 // because the key operation may have already been handled or canceled.
3278 gKeyLog
, LogLevel::Warning
,
3279 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3280 "remove a char message because it's gone during removing it from "
3281 "the queue, nextKeyMsg=%s, kFoundCharMsg=%s",
3282 this, ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3283 MOZ_ASSERT(!mCharMessageHasGone
);
3284 mFollowingCharMsgs
.Clear();
3285 mCharMessageHasGone
= true;
3288 if (!IsCharMessage(newNextKeyMsg
)) {
3289 // If next key message becomes a non-char message, we should mark this
3290 // keydown as consumed because the key operation may have already been
3291 // handled or canceled.
3293 gKeyLog
, LogLevel::Warning
,
3294 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3295 "remove a char message because it's gone during removing it from "
3296 "the queue, nextKeyMsg=%s, newNextKeyMsg=%s, kFoundCharMsg=%s",
3297 this, ToString(nextKeyMsg
).get(), ToString(newNextKeyMsg
).get(),
3298 ToString(kFoundCharMsg
).get()));
3299 MOZ_ASSERT(!mCharMessageHasGone
);
3300 mFollowingCharMsgs
.Clear();
3301 mCharMessageHasGone
= true;
3305 gKeyLog
, LogLevel::Debug
,
3306 ("%p NativeKey::GetFollowingCharMessage(), there is the message "
3307 "which is being tried to be removed from the queue, trying again...",
3312 // wParam of WM_DEADCHAR may be 0, but for the other char messages, this is
3313 // an odd case because it means that the key event shouldn't cause text
3314 // input. So, let's ignore the strange char message.
3315 if (removedMsg
.message
!= WM_DEADCHAR
&&
3316 removedMsg
.message
== nextKeyMsg
.message
&& !removedMsg
.wParam
) {
3318 gKeyLog
, LogLevel::Warning
,
3319 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3320 "remove a char message, but the removed message's wParam is 0, "
3322 this, ToString(removedMsg
).get()));
3326 // This is normal case.
3327 if (MayBeSameCharMessage(removedMsg
, nextKeyMsg
)) {
3328 aCharMsg
= removedMsg
;
3330 gKeyLog
, LogLevel::Debug
,
3331 ("%p NativeKey::GetFollowingCharMessage(), succeeded to retrieve "
3332 "next char message, aCharMsg=%s",
3333 this, ToString(aCharMsg
).get()));
3337 // Even if removed message is different char message from the found char
3338 // message, when the scan code is same, we can assume that the message
3339 // is overwritten by somebody who hooks API. See bug 1336028 comment 0 for
3340 // the possible scenarios.
3341 if (IsCharMessage(removedMsg
) &&
3342 IsSamePhysicalKeyMessage(removedMsg
, nextKeyMsg
)) {
3343 aCharMsg
= removedMsg
;
3345 gKeyLog
, LogLevel::Warning
,
3346 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3347 "remove a char message, but the removed message was changed from "
3348 "the found message except their scancode, aCharMsg=%s, "
3349 "nextKeyMsg=%s, kFoundCharMsg=%s",
3350 this, ToString(aCharMsg
).get(), ToString(nextKeyMsg
).get(),
3351 ToString(kFoundCharMsg
).get()));
3355 // When found message's wParam is 0 and its scancode is 0xFF, we may remove
3356 // usual char message actually. In such case, we should use the removed
3358 if (IsCharMessage(removedMsg
) && !nextKeyMsg
.wParam
&&
3359 WinUtils::GetScanCode(nextKeyMsg
.lParam
) == 0xFF) {
3360 aCharMsg
= removedMsg
;
3362 gKeyLog
, LogLevel::Warning
,
3363 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3364 "remove a char message, but the removed message was changed from "
3365 "the found message but the found message was odd, so, ignoring the "
3366 "odd found message and respecting the removed message, aCharMsg=%s, "
3367 "nextKeyMsg=%s, kFoundCharMsg=%s",
3368 this, ToString(aCharMsg
).get(), ToString(nextKeyMsg
).get(),
3369 ToString(kFoundCharMsg
).get()));
3373 const auto nextKeyMsgAfter
= [&]() -> Maybe
<MSG
> {
3374 MSG nextKeyMsgAfter
;
3375 if (WinUtils::PeekMessage(&nextKeyMsgAfter
, mMsg
.hwnd
, WM_KEYFIRST
,
3376 WM_KEYLAST
, PM_NOREMOVE
| PM_NOYIELD
)) {
3377 return Some(nextKeyMsgAfter
);
3382 // We have some crash reports for Ethiopic Syllable. In the case, we found
3383 // a WM_DEADCHAR whose wParam may be not NULL, first. Then, we remove the
3384 // message from the queue. However, the keyboard or IME makes us to remove
3385 // a dummy message whose wParam (inputting character) is NULL and scancode
3386 // is 0. Finally, the found message keeps staying in the queue. The crash
3387 // report comes after we started accepting WM_DEADCHAR with NULL character
3388 // above. However, we haven't handled only a part of dead key sequence but
3389 // the user tried to input Ethiopic Syllable. Therefore, it's hard to guess
3390 // that the user starts using Firefox with the keyboard/IME at the timing
3391 // and the the user keeps using Firefox with unfunctional keyboard/IME.
3392 // Perhaps the keyboard/IME does something different hack for us to make
3393 // itself available on Firefox. Therefore, we should just return `false` to
3394 // make the caller give up removing the following messages.
3395 if (IsDeadCharMessage(removedMsg
.message
) && !removedMsg
.wParam
&&
3396 !WinUtils::GetScanCode(removedMsg
.lParam
) && nextKeyMsgAfter
.isSome() &&
3397 MayBeSameCharMessage(nextKeyMsgAfter
.ref(), nextKeyMsg
)) {
3399 gKeyLog
, LogLevel::Warning
,
3400 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3401 "remove a dead char message, but the removed message's wParam is 0 "
3402 "and the found message still in the queue, nextKeyMsg=%s but "
3404 this, ToString(nextKeyMsg
).get(), ToString(removedMsg
).get()));
3408 // NOTE: Although, we don't know when this case occurs, the scan code value
3409 // in lParam may be changed from 0 to something. The changed value
3410 // is different from the scan code of handling keydown message.
3412 gKeyLog
, LogLevel::Error
,
3413 ("%p NativeKey::GetFollowingCharMessage(), FAILED, removed message "
3414 "is really different from what we have already found, removedMsg=%s, "
3415 "nextKeyMsg=%s, kFoundCharMsg=%s",
3416 this, ToString(removedMsg
).get(), ToString(nextKeyMsg
).get(),
3417 ToString(kFoundCharMsg
).get()));
3418 nsPrintfCString
info(
3419 "\nPeekMessage() removed unexpcted char message! "
3420 "\nActive keyboard layout=0x%p (%s), "
3421 "\nHandling message: %s, InSendMessageEx()=%s, "
3422 "\nFound message: %s, "
3423 "\nRemoved message: %s, ",
3424 KeyboardLayout::GetInstance()->GetLoadedLayout(),
3425 KeyboardLayout::GetInstance()->GetLoadedLayoutName().get(),
3426 ToString(mMsg
).get(), GetResultOfInSendMessageEx().get(),
3427 ToString(kFoundCharMsg
).get(), ToString(removedMsg
).get());
3428 CrashReporter::AppendAppNotesToCrashReport(info
);
3429 // What's the next key message?
3430 if (nextKeyMsgAfter
.isSome()) {
3431 nsPrintfCString
info(
3432 "\nNext key message after unexpected char message "
3434 ToString(nextKeyMsgAfter
.ref()).get());
3435 CrashReporter::AppendAppNotesToCrashReport(info
);
3437 CrashReporter::AppendAppNotesToCrashReport(
3438 nsLiteralCString("\nThere is no key message after unexpected char "
3439 "message removed, "));
3441 // Another window has a key message?
3442 if (WinUtils::PeekMessage(&nextKeyMsgInAllWindows
, 0, WM_KEYFIRST
,
3443 WM_KEYLAST
, PM_NOREMOVE
| PM_NOYIELD
)) {
3444 nsPrintfCString
info("\nNext key message in all windows: %s.",
3445 ToString(nextKeyMsgInAllWindows
).get());
3446 CrashReporter::AppendAppNotesToCrashReport(info
);
3448 CrashReporter::AppendAppNotesToCrashReport(
3449 "\nThere is no key message in any windows."_ns
);
3452 MOZ_CRASH("PeekMessage() removed unexpected message");
3455 gKeyLog
, LogLevel::Error
,
3456 ("%p NativeKey::GetFollowingCharMessage(), FAILED, removed messages "
3457 "are all WM_NULL, nextKeyMsg=%s",
3458 this, ToString(nextKeyMsg
).get()));
3459 nsPrintfCString
info(
3460 "\nWe lost following char message! "
3461 "\nActive keyboard layout=0x%p (%s), "
3462 "\nHandling message: %s, InSendMessageEx()=%s, \n"
3463 "Found message: %s, removed a lot of WM_NULL",
3464 KeyboardLayout::GetInstance()->GetLoadedLayout(),
3465 KeyboardLayout::GetInstance()->GetLoadedLayoutName().get(),
3466 ToString(mMsg
).get(), GetResultOfInSendMessageEx().get(),
3467 ToString(kFoundCharMsg
).get());
3468 CrashReporter::AppendAppNotesToCrashReport(info
);
3469 MOZ_CRASH("We lost the following char message");
3473 void NativeKey::ComputeInputtingStringWithKeyboardLayout() {
3474 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
3476 if (KeyboardLayout::IsPrintableCharKey(mVirtualKeyCode
) ||
3477 mCharMessageHasGone
) {
3478 mInputtingStringAndModifiers
= mCommittedCharsAndModifiers
;
3480 mInputtingStringAndModifiers
.Clear();
3482 mShiftedString
.Clear();
3483 mUnshiftedString
.Clear();
3484 mShiftedLatinChar
= mUnshiftedLatinChar
= 0;
3486 // XXX How about when Win key is pressed?
3487 if (!mModKeyState
.IsControl() && !mModKeyState
.IsAlt()) {
3491 // If user is inputting a Unicode character with typing Alt + Numpad
3492 // keys, we shouldn't set alternative key codes because the key event
3493 // shouldn't match with a mnemonic. However, we should set only
3494 // mUnshiftedString for keeping traditional behavior at WM_SYSKEYDOWN.
3495 // FYI: I guess that it's okay that mUnshiftedString stays empty. So,
3496 // if its value breaks something, you must be able to just return here.
3497 if (MaybeTypingUnicodeScalarValue()) {
3498 if (!mCommittedCharsAndModifiers
.IsEmpty()) {
3499 MOZ_ASSERT(mMsg
.message
== WM_SYSKEYDOWN
);
3500 char16_t num
= mCommittedCharsAndModifiers
.CharAt(0);
3501 MOZ_ASSERT(num
>= '0' && num
<= '9');
3502 mUnshiftedString
.Append(num
, MODIFIER_NONE
);
3505 // If user presses a function key without NumLock or 3rd party utility
3506 // synthesizes a function key on numpad, we should handle it as-is because
3507 // the user's intention may be performing `Alt` + foo.
3508 MOZ_ASSERT(!KeyboardLayout::IsPrintableCharKey(mVirtualKeyCode
));
3512 ModifierKeyState
capsLockState(mModKeyState
.GetModifiers() &
3516 keyboardLayout
->GetUniCharsAndModifiers(mVirtualKeyCode
, capsLockState
);
3517 capsLockState
.Set(MODIFIER_SHIFT
);
3519 keyboardLayout
->GetUniCharsAndModifiers(mVirtualKeyCode
, capsLockState
);
3521 // The current keyboard cannot input alphabets or numerics,
3522 // we should append them for Shortcut/Access keys.
3523 // E.g., for Cyrillic keyboard layout.
3524 capsLockState
.Unset(MODIFIER_SHIFT
);
3525 WidgetUtils::GetLatinCharCodeForKeyCode(
3526 mDOMKeyCode
, capsLockState
.GetModifiers(), &mUnshiftedLatinChar
,
3527 &mShiftedLatinChar
);
3529 // If the mShiftedLatinChar isn't 0, the key code is NS_VK_[A-Z].
3530 if (mShiftedLatinChar
) {
3531 // If the produced characters of the key on current keyboard layout
3532 // are same as computed Latin characters, we shouldn't append the
3533 // Latin characters to alternativeCharCode.
3534 if (mUnshiftedLatinChar
== mUnshiftedString
.CharAt(0) &&
3535 mShiftedLatinChar
== mShiftedString
.CharAt(0)) {
3536 mShiftedLatinChar
= mUnshiftedLatinChar
= 0;
3538 } else if (mUnshiftedLatinChar
) {
3539 // If the mShiftedLatinChar is 0, the mKeyCode doesn't produce
3540 // alphabet character. At that time, the character may be produced
3541 // with Shift key. E.g., on French keyboard layout, NS_VK_PERCENT
3542 // key produces LATIN SMALL LETTER U WITH GRAVE (U+00F9) without
3543 // Shift key but with Shift key, it produces '%'.
3544 // If the mUnshiftedLatinChar is produced by the key on current
3545 // keyboard layout, we shouldn't append it to alternativeCharCode.
3546 if (mUnshiftedLatinChar
== mUnshiftedString
.CharAt(0) ||
3547 mUnshiftedLatinChar
== mShiftedString
.CharAt(0)) {
3548 mUnshiftedLatinChar
= 0;
3552 if (!mModKeyState
.IsControl()) {
3556 // If the mCharCode is not ASCII character, we should replace the
3557 // mCharCode with ASCII character only when Ctrl is pressed.
3558 // But don't replace the mCharCode when the mCharCode is not same as
3559 // unmodified characters. In such case, Ctrl is sometimes used for a
3560 // part of character inputting key combination like Shift.
3562 mModKeyState
.IsShift() ? mShiftedLatinChar
: mUnshiftedLatinChar
;
3566 if (mInputtingStringAndModifiers
.IsEmpty() ||
3567 mInputtingStringAndModifiers
.UniCharsCaseInsensitiveEqual(
3568 mModKeyState
.IsShift() ? mShiftedString
: mUnshiftedString
)) {
3569 mInputtingStringAndModifiers
.Clear();
3570 mInputtingStringAndModifiers
.Append(ch
, mModKeyState
.GetModifiers());
3574 bool NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages() const {
3575 MOZ_ASSERT(IsKeyDownMessage());
3576 MOZ_ASSERT(IsFollowedByPrintableCharOrSysCharMessage());
3577 MOZ_ASSERT(!mWidget
->Destroyed());
3579 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
3580 if (NS_WARN_IF(NS_FAILED(rv
))) {
3582 gKeyLog
, LogLevel::Error
,
3583 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3584 "FAILED due to BeginNativeInputTransaction() failure",
3588 WidgetKeyboardEvent
keypressEvent(true, eKeyPress
, mWidget
);
3589 MOZ_LOG(gKeyLog
, LogLevel::Debug
,
3590 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3591 "initializing keypress event...",
3593 ModifierKeyState
modKeyState(mModKeyState
);
3594 if (mCanIgnoreModifierStateAtKeyPress
&& IsFollowedByPrintableCharMessage()) {
3595 // If eKeyPress event should cause inputting text in focused editor,
3596 // we need to remove Alt and Ctrl state.
3597 modKeyState
.Unset(MODIFIER_ALT
| MODIFIER_CONTROL
);
3599 // We don't need to send char message here if there are two or more retrieved
3600 // messages because we need to set each message to each eKeyPress event.
3601 bool needsCallback
= mFollowingCharMsgs
.Length() > 1;
3602 nsEventStatus status
= InitKeyEvent(keypressEvent
, modKeyState
);
3603 MOZ_LOG(gKeyLog
, LogLevel::Info
,
3604 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3605 "dispatching keypress event(s)...",
3607 bool dispatched
= mDispatcher
->MaybeDispatchKeypressEvents(
3608 keypressEvent
, status
, const_cast<NativeKey
*>(this), needsCallback
);
3609 if (mWidget
->Destroyed()) {
3611 gKeyLog
, LogLevel::Info
,
3612 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3613 "keypress event(s) caused destroying the widget",
3617 bool consumed
= status
== nsEventStatus_eConsumeNoDefault
;
3618 MOZ_LOG(gKeyLog
, LogLevel::Info
,
3619 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3620 "dispatched keypress event(s), dispatched=%s, consumed=%s",
3621 this, GetBoolName(dispatched
), GetBoolName(consumed
)));
3625 bool NativeKey::DispatchKeyPressEventsWithoutCharMessage() const {
3626 MOZ_ASSERT(IsKeyDownMessage());
3627 MOZ_ASSERT(!mIsDeadKey
|| !mCommittedCharsAndModifiers
.IsEmpty());
3628 MOZ_ASSERT(!mWidget
->Destroyed());
3630 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
3631 if (NS_WARN_IF(NS_FAILED(rv
))) {
3632 MOZ_LOG(gKeyLog
, LogLevel::Error
,
3633 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3635 "to BeginNativeInputTransaction() failure",
3640 WidgetKeyboardEvent
keypressEvent(true, eKeyPress
, mWidget
);
3641 if (mInputtingStringAndModifiers
.IsEmpty() && mShiftedString
.IsEmpty() &&
3642 mUnshiftedString
.IsEmpty()) {
3643 keypressEvent
.mKeyCode
= mDOMKeyCode
;
3645 MOZ_LOG(gKeyLog
, LogLevel::Debug
,
3646 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3648 "keypress event...",
3650 nsEventStatus status
= InitKeyEvent(keypressEvent
, mModKeyState
);
3651 MOZ_LOG(gKeyLog
, LogLevel::Info
,
3652 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3654 "keypress event(s)...",
3656 bool dispatched
= mDispatcher
->MaybeDispatchKeypressEvents(
3657 keypressEvent
, status
, const_cast<NativeKey
*>(this));
3658 if (mWidget
->Destroyed()) {
3659 MOZ_LOG(gKeyLog
, LogLevel::Info
,
3660 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3661 "keypress event(s) caused destroying the widget",
3665 bool consumed
= status
== nsEventStatus_eConsumeNoDefault
;
3667 gKeyLog
, LogLevel::Info
,
3668 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), dispatched "
3669 "keypress event(s), dispatched=%s, consumed=%s",
3670 this, GetBoolName(dispatched
), GetBoolName(consumed
)));
3674 void NativeKey::WillDispatchKeyboardEvent(WidgetKeyboardEvent
& aKeyboardEvent
,
3676 // If it's an eKeyPress event and it's generated from retrieved char message,
3677 // we need to set raw message information for plugins.
3678 if (aKeyboardEvent
.mMessage
== eKeyPress
&&
3679 IsFollowedByPrintableCharOrSysCharMessage()) {
3680 MOZ_RELEASE_ASSERT(aIndex
< mCommittedCharsAndModifiers
.Length());
3681 uint32_t foundPrintableCharMessages
= 0;
3682 for (size_t i
= 0; i
< mFollowingCharMsgs
.Length(); ++i
) {
3683 if (!IsPrintableCharOrSysCharMessage(mFollowingCharMsgs
[i
])) {
3684 // XXX Should we dispatch a plugin event for WM_*DEADCHAR messages and
3685 // WM_CHAR with a control character here? But we're not sure
3686 // how can we create such message queue (i.e., WM_CHAR or
3687 // WM_SYSCHAR with a printable character and such message are
3688 // generated by a keydown). So, let's ignore such case until
3689 // we'd get some bug reports.
3690 MOZ_LOG(gKeyLog
, LogLevel::Warning
,
3691 ("%p NativeKey::WillDispatchKeyboardEvent(), WARNING, "
3692 "ignoring %zuth message due to non-printable char message, %s",
3693 this, i
+ 1, ToString(mFollowingCharMsgs
[i
]).get()));
3696 if (foundPrintableCharMessages
++ == aIndex
) {
3697 // Found message which caused the eKeyPress event.
3701 // Set modifier state from mCommittedCharsAndModifiers because some of them
3702 // might be different. For example, Shift key was pressed at inputting
3703 // dead char but Shift key was released before inputting next character.
3704 if (mCanIgnoreModifierStateAtKeyPress
) {
3705 ModifierKeyState
modKeyState(mModKeyState
);
3706 modKeyState
.Unset(MODIFIER_SHIFT
| MODIFIER_CONTROL
| MODIFIER_ALT
|
3707 MODIFIER_ALTGRAPH
| MODIFIER_CAPSLOCK
);
3708 modKeyState
.Set(mCommittedCharsAndModifiers
.ModifiersAt(aIndex
));
3709 modKeyState
.InitInputEvent(aKeyboardEvent
);
3710 MOZ_LOG(gKeyLog
, LogLevel::Info
,
3711 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3712 "setting %uth modifier state to %s",
3713 this, aIndex
+ 1, ToString(modKeyState
).get()));
3716 size_t longestLength
=
3717 std::max(mInputtingStringAndModifiers
.Length(),
3718 std::max(mShiftedString
.Length(), mUnshiftedString
.Length()));
3719 size_t skipUniChars
= longestLength
- mInputtingStringAndModifiers
.Length();
3720 size_t skipShiftedChars
= longestLength
- mShiftedString
.Length();
3721 size_t skipUnshiftedChars
= longestLength
- mUnshiftedString
.Length();
3722 if (aIndex
>= longestLength
) {
3724 gKeyLog
, LogLevel::Info
,
3725 ("%p NativeKey::WillDispatchKeyboardEvent(), does nothing for %uth "
3727 this, aIndex
+ 1, ToChar(aKeyboardEvent
.mMessage
)));
3731 // Check if aKeyboardEvent is the last event for a key press.
3732 // So, if it's not an eKeyPress event, it's always the last event.
3733 // Otherwise, check if the index is the last character of
3734 // mCommittedCharsAndModifiers.
3735 bool isLastIndex
= aKeyboardEvent
.mMessage
!= eKeyPress
||
3736 mCommittedCharsAndModifiers
.IsEmpty() ||
3737 mCommittedCharsAndModifiers
.Length() - 1 == aIndex
;
3739 nsTArray
<AlternativeCharCode
>& altArray
=
3740 aKeyboardEvent
.mAlternativeCharCodes
;
3742 // Set charCode and adjust modifier state for every eKeyPress event.
3743 // This is not necessary for the other keyboard events because the other
3744 // keyboard events shouldn't have non-zero charCode value and should have
3745 // current modifier state.
3746 if (aKeyboardEvent
.mMessage
== eKeyPress
&& skipUniChars
<= aIndex
) {
3747 // XXX Modifying modifier state of aKeyboardEvent is illegal, but no way
3748 // to set different modifier state per keypress event except this
3749 // hack. Note that ideally, dead key should cause composition events
3750 // instead of keypress events, though.
3751 if (aIndex
- skipUniChars
< mInputtingStringAndModifiers
.Length()) {
3752 ModifierKeyState
modKeyState(mModKeyState
);
3753 // If key in combination with Alt and/or Ctrl produces a different
3754 // character than without them then do not report these flags
3755 // because it is separate keyboard layout shift state. If dead-key
3756 // and base character does not produce a valid composite character
3757 // then both produced dead-key character and following base
3758 // character may have different modifier flags, too.
3759 modKeyState
.Unset(MODIFIER_SHIFT
| MODIFIER_CONTROL
| MODIFIER_ALT
|
3760 MODIFIER_ALTGRAPH
| MODIFIER_CAPSLOCK
);
3762 mInputtingStringAndModifiers
.ModifiersAt(aIndex
- skipUniChars
));
3763 modKeyState
.InitInputEvent(aKeyboardEvent
);
3764 MOZ_LOG(gKeyLog
, LogLevel::Info
,
3765 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3766 "setting %uth modifier state to %s",
3767 this, aIndex
+ 1, ToString(modKeyState
).get()));
3770 mInputtingStringAndModifiers
.CharAt(aIndex
- skipUniChars
);
3772 // The mCharCode was set from mKeyValue. However, for example, when Ctrl key
3773 // is pressed, its value should indicate an ASCII character for backward
3774 // compatibility rather than inputting character without the modifiers.
3775 // Therefore, we need to modify mCharCode value here.
3776 aKeyboardEvent
.SetCharCode(uniChar
);
3777 MOZ_LOG(gKeyLog
, LogLevel::Info
,
3778 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3779 "setting %uth charCode to %s",
3780 this, aIndex
+ 1, GetCharacterCodeName(uniChar
).get()));
3783 // We need to append alterntaive charCode values:
3784 // - if the event is eKeyPress, we need to append for the index because
3785 // eKeyPress event is dispatched for every character inputted by a
3787 // - if the event is not eKeyPress, we need to append for all characters
3788 // inputted by the key press because the other keyboard events (e.g.,
3789 // eKeyDown are eKeyUp) are fired only once for a key press.
3791 if (aKeyboardEvent
.mMessage
== eKeyPress
) {
3792 // Basically, append alternative charCode values only for the index.
3794 // However, if it's the last eKeyPress event but different shift state
3795 // can input longer string, the last eKeyPress event should have all
3796 // remaining alternative charCode values.
3798 count
= longestLength
- aIndex
;
3801 count
= longestLength
;
3803 for (size_t i
= 0; i
< count
; ++i
) {
3804 uint16_t shiftedChar
= 0, unshiftedChar
= 0;
3805 if (skipShiftedChars
<= aIndex
+ i
) {
3806 shiftedChar
= mShiftedString
.CharAt(aIndex
+ i
- skipShiftedChars
);
3808 if (skipUnshiftedChars
<= aIndex
+ i
) {
3809 unshiftedChar
= mUnshiftedString
.CharAt(aIndex
+ i
- skipUnshiftedChars
);
3812 if (shiftedChar
|| unshiftedChar
) {
3813 AlternativeCharCode
chars(unshiftedChar
, shiftedChar
);
3814 altArray
.AppendElement(chars
);
3821 if (mUnshiftedLatinChar
|| mShiftedLatinChar
) {
3822 AlternativeCharCode
chars(mUnshiftedLatinChar
, mShiftedLatinChar
);
3823 altArray
.AppendElement(chars
);
3826 // Typically, following virtual keycodes are used for a key which can
3827 // input the character. However, these keycodes are also used for
3828 // other keys on some keyboard layout. E.g., in spite of Shift+'1'
3829 // inputs '+' on Thai keyboard layout, a key which is at '=/+'
3830 // key on ANSI keyboard layout is VK_OEM_PLUS. Native applications
3831 // handle it as '+' key if Ctrl key is pressed.
3832 char16_t charForOEMKeyCode
= 0;
3833 switch (mVirtualKeyCode
) {
3835 charForOEMKeyCode
= '+';
3838 charForOEMKeyCode
= ',';
3841 charForOEMKeyCode
= '-';
3844 charForOEMKeyCode
= '.';
3847 if (charForOEMKeyCode
&& charForOEMKeyCode
!= mUnshiftedString
.CharAt(0) &&
3848 charForOEMKeyCode
!= mShiftedString
.CharAt(0) &&
3849 charForOEMKeyCode
!= mUnshiftedLatinChar
&&
3850 charForOEMKeyCode
!= mShiftedLatinChar
) {
3851 AlternativeCharCode
OEMChars(charForOEMKeyCode
, charForOEMKeyCode
);
3852 altArray
.AppendElement(OEMChars
);
3857 /*****************************************************************************
3858 * mozilla::widget::KeyboardLayout
3859 *****************************************************************************/
3861 KeyboardLayout
* KeyboardLayout::sInstance
= nullptr;
3862 StaticRefPtr
<nsIUserIdleServiceInternal
> KeyboardLayout::sIdleService
;
3865 KeyboardLayout
* KeyboardLayout::GetInstance() {
3867 sInstance
= new KeyboardLayout();
3873 void KeyboardLayout::Shutdown() {
3875 sInstance
= nullptr;
3876 sIdleService
= nullptr;
3880 void KeyboardLayout::NotifyIdleServiceOfUserActivity() {
3881 if (!sIdleService
) {
3882 sIdleService
= nsCOMPtr
<nsIUserIdleServiceInternal
>(
3883 do_GetService("@mozilla.org/widget/useridleservice;1"))
3885 if (NS_WARN_IF(!sIdleService
)) {
3889 sIdleService
->ResetIdleTimeOut(0);
3892 KeyboardLayout::KeyboardLayout() {
3893 mDeadKeyTableListHead
= nullptr;
3894 // A dead key sequence should be made from up to 5 keys. Therefore, 4 is
3895 // enough and makes sense because the item is uint8_t.
3896 // (Although, even if it's possible to be 6 keys or more in a sequence,
3897 // this array will be re-allocated).
3898 mActiveDeadKeys
.SetCapacity(4);
3899 mDeadKeyShiftStates
.SetCapacity(4);
3901 // If we put it off to load active keyboard layout when first needed, we need
3902 // to load it at instanciation. That makes us save the cost of a call of
3903 // GetKeyboardLayout() API.
3904 if (StaticPrefs::ui_key_layout_load_when_first_needed()) {
3905 OnLayoutChange(::GetKeyboardLayout(0));
3909 KeyboardLayout::~KeyboardLayout() { ReleaseDeadKeyTables(); }
3911 bool KeyboardLayout::IsPrintableCharKey(uint8_t aVirtualKey
) {
3912 return GetKeyIndex(aVirtualKey
) >= 0;
3915 WORD
KeyboardLayout::ComputeScanCodeForVirtualKeyCode(
3916 uint8_t aVirtualKeyCode
) const {
3917 return static_cast<WORD
>(::MapVirtualKeyEx(aVirtualKeyCode
, MAPVK_VK_TO_VSC
,
3918 KeyboardLayout::GetLayout()));
3921 bool KeyboardLayout::IsDeadKey(uint8_t aVirtualKey
,
3922 const ModifierKeyState
& aModKeyState
) const {
3923 int32_t virtualKeyIndex
= GetKeyIndex(aVirtualKey
);
3925 // XXX KeyboardLayout class doesn't support unusual keyboard layout which
3926 // maps some function keys as dead keys.
3927 if (virtualKeyIndex
< 0) {
3931 return mVirtualKeys
[virtualKeyIndex
].IsDeadKey(
3932 VirtualKey::ModifiersToShiftState(aModKeyState
.GetModifiers()));
3935 bool KeyboardLayout::IsSysKey(uint8_t aVirtualKey
,
3936 const ModifierKeyState
& aModKeyState
) const {
3937 // If Alt key is not pressed, it's never a system key combination.
3938 // Additionally, if Ctrl key is pressed, it's never a system key combination
3940 // FYI: Windows logo key state won't affect if it's a system key.
3941 if (!aModKeyState
.IsAlt() || aModKeyState
.IsControl()) {
3945 int32_t virtualKeyIndex
= GetKeyIndex(aVirtualKey
);
3946 if (virtualKeyIndex
< 0) {
3950 UniCharsAndModifiers inputCharsAndModifiers
=
3951 GetUniCharsAndModifiers(aVirtualKey
, aModKeyState
);
3952 if (inputCharsAndModifiers
.IsEmpty()) {
3956 // If the Alt key state isn't consumed, that means that the key with Alt
3957 // doesn't cause text input. So, the combination is a system key.
3958 return !!(inputCharsAndModifiers
.ModifiersAt(0) & MODIFIER_ALT
);
3961 void KeyboardLayout::InitNativeKey(NativeKey
& aNativeKey
) {
3962 if (mIsPendingToRestoreKeyboardLayout
) {
3963 LoadLayout(::GetKeyboardLayout(0));
3966 // If the aNativeKey is initialized with WM_CHAR, the key information
3967 // should be discarded because mKeyValue should have the string to be
3969 if (aNativeKey
.mMsg
.message
== WM_CHAR
) {
3970 char16_t ch
= static_cast<char16_t
>(aNativeKey
.mMsg
.wParam
);
3971 // But don't set key value as printable key if the character is a control
3972 // character such as 0x0D at pressing Enter key.
3973 if (!NativeKey::IsControlChar(ch
)) {
3974 aNativeKey
.mKeyNameIndex
= KEY_NAME_INDEX_USE_STRING
;
3975 Modifiers modifiers
=
3976 aNativeKey
.GetModifiers() & ~(MODIFIER_ALT
| MODIFIER_CONTROL
);
3977 aNativeKey
.mCommittedCharsAndModifiers
.Append(ch
, modifiers
);
3982 // If the aNativeKey is in a sequence to input a Unicode character with
3983 // Alt + numpad keys, we should just set the number as the inputting charcter.
3984 // Note that we should compute the key value from the virtual key code
3985 // because they may be mapped to alphabets, but they should be treated as
3986 // Alt + [0-9] even by web apps.
3987 // However, we shouldn't touch the key value if given virtual key code is
3988 // not a printable key because it may be synthesized by 3rd party utility
3989 // or just NumLock is unlocked and user tries to use shortcut key. In the
3990 // latter case, we cannot solve the conflict issue with Alt+foo shortcut key
3991 // and inputting a Unicode scalar value like reported to bug 1606655, though,
3992 // I have no better idea. Perhaps, `Alt` shouldn't be used for shortcut key
3993 // combination on Windows.
3994 if (aNativeKey
.MaybeTypingUnicodeScalarValue() &&
3995 KeyboardLayout::IsPrintableCharKey(aNativeKey
.mVirtualKeyCode
)) {
3996 // If the key code value is mapped to a Numpad key, let's compute the key
3997 // value with it. This is same behavior as Chrome. In strictly speaking,
3998 // I think that the else block's computation is better because it seems
3999 // that Windows does not refer virtual key code value, but we should avoid
4000 // web-compat issues.
4002 if (aNativeKey
.mVirtualKeyCode
>= VK_NUMPAD0
&&
4003 aNativeKey
.mVirtualKeyCode
<= VK_NUMPAD9
) {
4004 num
= '0' + aNativeKey
.mVirtualKeyCode
- VK_NUMPAD0
;
4006 // Otherwise, let's use fake key value for making never match with
4009 switch (aNativeKey
.mScanCode
) {
4010 case 0x0052: // Numpad0
4013 case 0x004F: // Numpad1
4016 case 0x0050: // Numpad2
4019 case 0x0051: // Numpad3
4022 case 0x004B: // Numpad4
4025 case 0x004C: // Numpad5
4028 case 0x004D: // Numpad6
4031 case 0x0047: // Numpad7
4034 case 0x0048: // Numpad8
4037 case 0x0049: // Numpad9
4041 MOZ_ASSERT_UNREACHABLE(
4042 "IsTypingUnicodeScalarValue() must have returned true for wrong "
4047 aNativeKey
.mCommittedCharsAndModifiers
.Append(num
,
4048 aNativeKey
.GetModifiers());
4049 aNativeKey
.mKeyNameIndex
= KEY_NAME_INDEX_USE_STRING
;
4053 // When it's followed by non-dead char message(s) for printable character(s),
4054 // aNativeKey should dispatch eKeyPress events for them rather than
4055 // information from keyboard layout because respecting WM_(SYS)CHAR messages
4056 // guarantees that we can always input characters which is expected by
4057 // the user even if the user uses odd keyboard layout.
4058 // Or, when it was followed by non-dead char message for a printable character
4059 // but it's gone at removing the message from the queue, let's treat it
4060 // as a key inputting empty string.
4061 if (aNativeKey
.IsFollowedByPrintableCharOrSysCharMessage() ||
4062 aNativeKey
.mCharMessageHasGone
) {
4063 MOZ_ASSERT(!aNativeKey
.IsCharMessage(aNativeKey
.mMsg
));
4064 if (aNativeKey
.IsFollowedByPrintableCharOrSysCharMessage()) {
4065 // Initialize mCommittedCharsAndModifiers with following char messages.
4066 aNativeKey
.InitCommittedCharsAndModifiersWithFollowingCharMessages();
4067 MOZ_ASSERT(!aNativeKey
.mCommittedCharsAndModifiers
.IsEmpty());
4069 // Currently, we are doing a ugly hack to keypress events to cause
4070 // inputting character even if Ctrl or Alt key is pressed, that is, we
4071 // remove Ctrl and Alt modifier state from keypress event. However, for
4072 // example, Ctrl+Space which causes ' ' of WM_CHAR message never causes
4073 // keypress event whose ctrlKey is true. For preventing this problem,
4074 // we should mark as not removable if Ctrl or Alt key does not cause
4075 // changing inputting character.
4076 if (IsPrintableCharKey(aNativeKey
.mOriginalVirtualKeyCode
) &&
4077 (aNativeKey
.IsControl() ^ aNativeKey
.IsAlt())) {
4078 ModifierKeyState state
= aNativeKey
.ModifierKeyStateRef();
4079 state
.Unset(MODIFIER_ALT
| MODIFIER_CONTROL
);
4080 UniCharsAndModifiers charsWithoutModifier
=
4081 GetUniCharsAndModifiers(aNativeKey
.GenericVirtualKeyCode(), state
);
4082 aNativeKey
.mCanIgnoreModifierStateAtKeyPress
=
4083 !charsWithoutModifier
.UniCharsEqual(
4084 aNativeKey
.mCommittedCharsAndModifiers
);
4087 aNativeKey
.mCommittedCharsAndModifiers
.Clear();
4089 aNativeKey
.mKeyNameIndex
= KEY_NAME_INDEX_USE_STRING
;
4091 // If it's not in dead key sequence, we don't need to do anymore here.
4092 if (!IsInDeadKeySequence()) {
4096 // If it's in dead key sequence and dead char is inputted as is, we need to
4097 // set the previous modifier state which is stored when preceding dead key
4099 UniCharsAndModifiers deadChars
= GetDeadUniCharsAndModifiers();
4100 aNativeKey
.mCommittedCharsAndModifiers
.OverwriteModifiersIfBeginsWith(
4102 // Finish the dead key sequence.
4103 DeactivateDeadKeyState();
4107 // If it's a dead key, aNativeKey will be initialized by
4108 // MaybeInitNativeKeyAsDeadKey().
4109 if (MaybeInitNativeKeyAsDeadKey(aNativeKey
)) {
4113 // If the key is not a usual printable key, KeyboardLayout class assume that
4114 // it's not cause dead char nor printable char. Therefore, there are nothing
4115 // to do here fore such keys (e.g., function keys).
4116 // However, this should keep dead key state even if non-printable key is
4117 // pressed during a dead key sequence.
4118 if (!IsPrintableCharKey(aNativeKey
.mOriginalVirtualKeyCode
)) {
4122 MOZ_ASSERT(aNativeKey
.mOriginalVirtualKeyCode
!= VK_PACKET
,
4123 "At handling VK_PACKET, we shouldn't refer keyboard layout");
4125 aNativeKey
.mKeyNameIndex
== KEY_NAME_INDEX_USE_STRING
,
4126 "Printable key's key name index must be KEY_NAME_INDEX_USE_STRING");
4128 // If it's in dead key handling and the pressed key causes a composite
4129 // character, aNativeKey will be initialized by
4130 // MaybeInitNativeKeyWithCompositeChar().
4131 if (MaybeInitNativeKeyWithCompositeChar(aNativeKey
)) {
4135 UniCharsAndModifiers baseChars
= GetUniCharsAndModifiers(aNativeKey
);
4137 // If the key press isn't related to any dead keys, initialize aNativeKey
4138 // with the characters which should be caused by the key.
4139 if (!IsInDeadKeySequence()) {
4140 aNativeKey
.mCommittedCharsAndModifiers
= baseChars
;
4144 // If the key doesn't cause a composite character with preceding dead key,
4145 // initialize aNativeKey with the dead-key character followed by current
4147 UniCharsAndModifiers deadChars
= GetDeadUniCharsAndModifiers();
4148 aNativeKey
.mCommittedCharsAndModifiers
= deadChars
+ baseChars
;
4149 if (aNativeKey
.IsKeyDownMessage()) {
4150 DeactivateDeadKeyState();
4154 bool KeyboardLayout::MaybeInitNativeKeyAsDeadKey(NativeKey
& aNativeKey
) {
4155 // Only when it's not in dead key sequence, we can trust IsDeadKey() result.
4156 if (!IsInDeadKeySequence() && !IsDeadKey(aNativeKey
)) {
4160 // When keydown message is followed by a dead char message, it should be
4161 // initialized as dead key.
4162 const bool isDeadKeyDownEvent
=
4163 aNativeKey
.IsKeyDownMessage() && aNativeKey
.IsFollowedByDeadCharMessage();
4165 // When keyup message is received, let's check if it's one of preceding
4166 // dead keys because keydown message order and keyup message order may be
4168 const bool isDeadKeyUpEvent
=
4169 !aNativeKey
.IsKeyDownMessage() &&
4170 mActiveDeadKeys
.Contains(aNativeKey
.GenericVirtualKeyCode());
4172 if (isDeadKeyDownEvent
|| isDeadKeyUpEvent
) {
4173 ActivateDeadKeyState(aNativeKey
);
4174 // Any dead key events don't generate characters. So, a dead key should
4175 // cause only keydown event and keyup event whose KeyboardEvent.key
4176 // values are "Dead".
4177 aNativeKey
.mCommittedCharsAndModifiers
.Clear();
4178 aNativeKey
.mKeyNameIndex
= KEY_NAME_INDEX_Dead
;
4182 // At keydown message handling, we need to forget the first dead key
4183 // because there is no guarantee coming WM_KEYUP for the second dead
4184 // key before next WM_KEYDOWN. E.g., due to auto key repeat or pressing
4185 // another dead key before releasing current key. Therefore, we can
4186 // set only a character for current key for keyup event.
4187 if (!IsInDeadKeySequence()) {
4188 aNativeKey
.mCommittedCharsAndModifiers
=
4189 GetUniCharsAndModifiers(aNativeKey
);
4193 // When non-printable key event comes during a dead key sequence, that must
4194 // be a modifier key event. So, such events shouldn't be handled as a part
4195 // of the dead key sequence.
4196 if (!IsDeadKey(aNativeKey
)) {
4200 // FYI: Following code may run when the user doesn't input text actually
4201 // but the key sequence is a dead key sequence. For example,
4202 // ` -> Ctrl+` with Spanish keyboard layout. Let's keep using this
4203 // complicated code for now because this runs really rarely.
4205 // Dead key followed by another dead key may cause a composed character
4206 // (e.g., "Russian - Mnemonic" keyboard layout's 's' -> 'c').
4207 if (MaybeInitNativeKeyWithCompositeChar(aNativeKey
)) {
4211 // Otherwise, dead key followed by another dead key causes inputting both
4213 UniCharsAndModifiers prevDeadChars
= GetDeadUniCharsAndModifiers();
4214 UniCharsAndModifiers newChars
= GetUniCharsAndModifiers(aNativeKey
);
4215 // But keypress events should be fired for each committed character.
4216 aNativeKey
.mCommittedCharsAndModifiers
= prevDeadChars
+ newChars
;
4217 if (aNativeKey
.IsKeyDownMessage()) {
4218 DeactivateDeadKeyState();
4223 bool KeyboardLayout::MaybeInitNativeKeyWithCompositeChar(
4224 NativeKey
& aNativeKey
) {
4225 if (!IsInDeadKeySequence()) {
4229 if (NS_WARN_IF(!IsPrintableCharKey(aNativeKey
.mOriginalVirtualKeyCode
))) {
4233 UniCharsAndModifiers baseChars
= GetUniCharsAndModifiers(aNativeKey
);
4234 if (baseChars
.IsEmpty() || !baseChars
.CharAt(0)) {
4238 char16_t compositeChar
= GetCompositeChar(baseChars
.CharAt(0));
4239 if (!compositeChar
) {
4243 // Active dead-key and base character does produce exactly one composite
4245 aNativeKey
.mCommittedCharsAndModifiers
.Append(compositeChar
,
4246 baseChars
.ModifiersAt(0));
4247 if (aNativeKey
.IsKeyDownMessage()) {
4248 DeactivateDeadKeyState();
4253 UniCharsAndModifiers
KeyboardLayout::GetUniCharsAndModifiers(
4254 uint8_t aVirtualKey
, VirtualKey::ShiftState aShiftState
) const {
4255 UniCharsAndModifiers result
;
4256 int32_t key
= GetKeyIndex(aVirtualKey
);
4260 return mVirtualKeys
[key
].GetUniChars(aShiftState
);
4263 UniCharsAndModifiers
KeyboardLayout::GetDeadUniCharsAndModifiers() const {
4264 MOZ_RELEASE_ASSERT(mActiveDeadKeys
.Length() == mDeadKeyShiftStates
.Length());
4266 if (NS_WARN_IF(mActiveDeadKeys
.IsEmpty())) {
4267 return UniCharsAndModifiers();
4270 UniCharsAndModifiers result
;
4271 for (size_t i
= 0; i
< mActiveDeadKeys
.Length(); ++i
) {
4273 GetUniCharsAndModifiers(mActiveDeadKeys
[i
], mDeadKeyShiftStates
[i
]);
4278 char16_t
KeyboardLayout::GetCompositeChar(char16_t aBaseChar
) const {
4279 if (NS_WARN_IF(mActiveDeadKeys
.IsEmpty())) {
4282 // XXX Currently, we don't support computing a composite character with
4283 // two or more dead keys since it needs big table for supporting
4284 // long chained dead keys. However, this should be a minor bug
4285 // because this runs only when the latest keydown event does not cause
4286 // WM_(SYS)CHAR messages. So, when user wants to input a character,
4287 // this path never runs.
4288 if (mActiveDeadKeys
.Length() > 1) {
4291 int32_t key
= GetKeyIndex(mActiveDeadKeys
[0]);
4295 return mVirtualKeys
[key
].GetCompositeChar(mDeadKeyShiftStates
[0], aBaseChar
);
4298 static bool IsValidKeyboardLayoutsChild(const nsAString
& aChildName
) {
4299 if (aChildName
.Length() != 8) {
4302 for (size_t i
= 0; i
< aChildName
.Length(); i
++) {
4303 if ((aChildName
[i
] >= '0' && aChildName
[i
] <= '9') ||
4304 (aChildName
[i
] >= 'a' && aChildName
[i
] <= 'f') ||
4305 (aChildName
[i
] >= 'A' && aChildName
[i
] <= 'F')) {
4314 nsCString
KeyboardLayout::GetLayoutName(HKL aLayout
) {
4315 constexpr auto kKeyboardLayouts
=
4316 u
"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\"_ns
;
4317 uint16_t language
= reinterpret_cast<uintptr_t>(aLayout
) & 0xFFFF;
4318 uint16_t layout
= (reinterpret_cast<uintptr_t>(aLayout
) >> 16) & 0xFFFF;
4319 // If the layout is less than 0xA000XXXX (normal keyboard layout for the
4320 // language) or 0xEYYYXXXX (IMM-IME), we can retrieve its name simply.
4321 if (layout
< 0xA000 || (layout
& 0xF000) == 0xE000) {
4322 nsAutoString
key(kKeyboardLayouts
);
4323 key
.AppendPrintf("%08" PRIXPTR
, layout
< 0xA000
4325 : reinterpret_cast<uintptr_t>(aLayout
));
4327 if (NS_WARN_IF(!WinRegistry::GetString(
4328 HKEY_LOCAL_MACHINE
, key
, u
"Layout Text"_ns
, buf
,
4329 WinRegistry::kLegacyWinUtilsStringFlags
))) {
4330 return "No name or too long name"_ns
;
4332 return NS_ConvertUTF16toUTF8(buf
);
4335 if (NS_WARN_IF((layout
& 0xF000) != 0xF000)) {
4337 result
.AppendPrintf("Odd HKL: 0x%08" PRIXPTR
,
4338 reinterpret_cast<uintptr_t>(aLayout
));
4342 // Otherwise, we need to walk the registry under "Keyboard Layouts".
4343 WinRegistry::Key
regKey(HKEY_LOCAL_MACHINE
, kKeyboardLayouts
,
4344 WinRegistry::KeyMode::Read
);
4345 if (NS_WARN_IF(!regKey
)) {
4348 uint32_t childCount
= regKey
.GetChildCount();
4349 if (NS_WARN_IF(!childCount
)) {
4352 for (uint32_t i
= 0; i
< childCount
; i
++) {
4353 nsAutoString childName
;
4354 if (NS_WARN_IF(!regKey
.GetChildName(i
, childName
)) ||
4355 !IsValidKeyboardLayoutsChild(childName
)) {
4358 nsresult rv
= NS_OK
;
4359 uint32_t childNum
= static_cast<uint32_t>(childName
.ToInteger64(&rv
, 16));
4360 if (NS_WARN_IF(NS_FAILED(rv
))) {
4363 // Ignore normal keyboard layouts for each language.
4364 if (childNum
<= 0xFFFF) {
4367 // If it doesn't start with 'A' nor 'a', language should be matched.
4368 if ((childNum
& 0xFFFF) != language
&&
4369 (childNum
& 0xF0000000) != 0xA0000000) {
4372 // Then, the child should have "Layout Id" which is "YYY" of 0xFYYYXXXX.
4373 nsAutoString
key(kKeyboardLayouts
);
4375 WinRegistry::Key
regKey(HKEY_LOCAL_MACHINE
, key
,
4376 WinRegistry::KeyMode::QueryValue
);
4377 if (NS_WARN_IF(!regKey
)) {
4381 if (NS_WARN_IF(!regKey
.GetValueAsString(
4382 u
"Layout Id"_ns
, buf
, WinRegistry::kLegacyWinUtilsStringFlags
))) {
4385 uint16_t layoutId
= wcstol(buf
, nullptr, 16);
4386 if (layoutId
!= (layout
& 0x0FFF)) {
4389 if (NS_WARN_IF(!regKey
.GetValueAsString(
4390 u
"Layout Text"_ns
, buf
, WinRegistry::kLegacyWinUtilsStringFlags
))) {
4393 return NS_ConvertUTF16toUTF8(buf
);
4398 void KeyboardLayout::LoadLayout(HKL aLayout
) {
4399 mIsPendingToRestoreKeyboardLayout
= false;
4401 if (mKeyboardLayout
== aLayout
) {
4405 mKeyboardLayout
= aLayout
;
4408 MOZ_LOG(gKeyLog
, LogLevel::Info
,
4409 ("KeyboardLayout::LoadLayout(aLayout=0x%p (%s))", aLayout
,
4410 GetLayoutName(aLayout
).get()));
4413 memset(kbdState
, 0, sizeof(kbdState
));
4415 BYTE originalKbdState
[256];
4416 // Bitfield with all shift states that have at least one dead-key.
4417 uint16_t shiftStatesWithDeadKeys
= 0;
4418 // Bitfield with all shift states that produce any possible dead-key base
4420 uint16_t shiftStatesWithBaseChars
= 0;
4422 mActiveDeadKeys
.Clear();
4423 mDeadKeyShiftStates
.Clear();
4425 ReleaseDeadKeyTables();
4427 ::GetKeyboardState(originalKbdState
);
4429 // For each shift state gather all printable characters that are produced
4430 // for normal case when no any dead-key is active.
4432 for (VirtualKey::ShiftState shiftState
= 0; shiftState
< 16; shiftState
++) {
4433 VirtualKey::FillKbdState(kbdState
, shiftState
);
4434 bool isAltGr
= VirtualKey::IsAltGrIndex(shiftState
);
4435 for (uint32_t virtualKey
= 0; virtualKey
< 256; virtualKey
++) {
4436 int32_t vki
= GetKeyIndex(virtualKey
);
4440 NS_ASSERTION(uint32_t(vki
) < std::size(mVirtualKeys
), "invalid index");
4441 char16_t uniChars
[5];
4442 int32_t ret
= ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)uniChars
,
4443 std::size(uniChars
), 0, mKeyboardLayout
);
4446 shiftStatesWithDeadKeys
|= (1 << shiftState
);
4447 // Repeat dead-key to deactivate it and get its character
4449 char16_t deadChar
[2];
4450 ret
= ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)deadChar
,
4451 std::size(deadChar
), 0, mKeyboardLayout
);
4452 NS_ASSERTION(ret
== 2, "Expecting twice repeated dead-key character");
4453 mVirtualKeys
[vki
].SetDeadChar(shiftState
, deadChar
[0]);
4455 MOZ_LOG(gKeyLog
, LogLevel::Verbose
,
4456 (" %s (%d): DeadChar(%s, %s) (ret=%d)",
4457 kVirtualKeyName
[virtualKey
], vki
,
4458 GetShiftStateName(shiftState
).get(),
4459 GetCharacterCodeNames(deadChar
, 1).get(), ret
));
4462 // dead-key can pair only with exactly one base character.
4463 shiftStatesWithBaseChars
|= (1 << shiftState
);
4465 mVirtualKeys
[vki
].SetNormalChars(shiftState
, uniChars
, ret
);
4466 MOZ_LOG(gKeyLog
, LogLevel::Verbose
,
4467 (" %s (%d): NormalChar(%s, %s) (ret=%d)",
4468 kVirtualKeyName
[virtualKey
], vki
,
4469 GetShiftStateName(shiftState
).get(),
4470 GetCharacterCodeNames(uniChars
, ret
).get(), ret
));
4473 // If the key inputs at least one character with AltGr modifier,
4474 // check if AltGr changes inputting character. If it does, mark
4475 // this keyboard layout has AltGr modifier actually.
4476 if (!mHasAltGr
&& ret
> 0 && isAltGr
&&
4477 mVirtualKeys
[vki
].IsChangedByAltGr(shiftState
)) {
4479 MOZ_LOG(gKeyLog
, LogLevel::Info
,
4480 (" Found a key (%s) changed by AltGr: %s -> %s (%s) (ret=%d)",
4481 kVirtualKeyName
[virtualKey
],
4482 GetCharacterCodeNames(
4483 mVirtualKeys
[vki
].GetNativeUniChars(
4484 shiftState
- VirtualKey::ShiftStateIndex::eAltGr
))
4486 GetCharacterCodeNames(
4487 mVirtualKeys
[vki
].GetNativeUniChars(shiftState
))
4489 GetShiftStateName(shiftState
).get(), ret
));
4494 // Now process each dead-key to find all its base characters and resulting
4495 // composite characters.
4496 for (VirtualKey::ShiftState shiftState
= 0; shiftState
< 16; shiftState
++) {
4497 if (!(shiftStatesWithDeadKeys
& (1 << shiftState
))) {
4501 VirtualKey::FillKbdState(kbdState
, shiftState
);
4503 for (uint32_t virtualKey
= 0; virtualKey
< 256; virtualKey
++) {
4504 int32_t vki
= GetKeyIndex(virtualKey
);
4505 if (vki
>= 0 && mVirtualKeys
[vki
].IsDeadKey(shiftState
)) {
4506 AutoTArray
<DeadKeyEntry
, 256> deadKeyArray
;
4507 uint32_t n
= GetDeadKeyCombinations(
4508 virtualKey
, kbdState
, shiftStatesWithBaseChars
, deadKeyArray
);
4509 const DeadKeyTable
* dkt
=
4510 mVirtualKeys
[vki
].MatchingDeadKeyTable(deadKeyArray
.Elements(), n
);
4512 dkt
= AddDeadKeyTable(deadKeyArray
.Elements(), n
);
4514 mVirtualKeys
[vki
].AttachDeadKeyTable(shiftState
, dkt
);
4519 ::SetKeyboardState(originalKbdState
);
4521 if (MOZ_LOG_TEST(gKeyLog
, LogLevel::Verbose
)) {
4522 static const UINT kExtendedScanCode
[] = {0x0000, 0xE000};
4523 static const UINT kMapType
= MAPVK_VSC_TO_VK_EX
;
4524 MOZ_LOG(gKeyLog
, LogLevel::Verbose
,
4525 ("Logging virtual keycode values for scancode (0x%p)...",
4527 for (uint32_t i
= 0; i
< std::size(kExtendedScanCode
); i
++) {
4528 for (uint32_t j
= 1; j
<= 0xFF; j
++) {
4529 UINT scanCode
= kExtendedScanCode
[i
] + j
;
4530 UINT virtualKeyCode
=
4531 ::MapVirtualKeyEx(scanCode
, kMapType
, mKeyboardLayout
);
4532 MOZ_LOG(gKeyLog
, LogLevel::Verbose
,
4533 ("0x%04X, %s", scanCode
, kVirtualKeyName
[virtualKeyCode
]));
4538 MOZ_LOG(gKeyLog
, LogLevel::Info
,
4539 (" AltGr key is %s in %s", mHasAltGr
? "found" : "not found",
4540 GetLayoutName(aLayout
).get()));
4543 inline int32_t KeyboardLayout::GetKeyIndex(uint8_t aVirtualKey
) {
4544 // Currently these 68 (NS_NUM_OF_KEYS) virtual keys are assumed
4545 // to produce visible representation:
4546 // 0x20 - VK_SPACE ' '
4547 // 0x30..0x39 '0'..'9'
4548 // 0x41..0x5A 'A'..'Z'
4549 // 0x60..0x69 '0'..'9' on numpad
4550 // 0x6A - VK_MULTIPLY '*' on numpad
4551 // 0x6B - VK_ADD '+' on numpad
4552 // 0x6D - VK_SUBTRACT '-' on numpad
4553 // 0x6E - VK_DECIMAL '.' on numpad
4554 // 0x6F - VK_DIVIDE '/' on numpad
4555 // 0x6E - VK_DECIMAL '.'
4556 // 0xBA - VK_OEM_1 ';:' for US
4557 // 0xBB - VK_OEM_PLUS '+' any country
4558 // 0xBC - VK_OEM_COMMA ',' any country
4559 // 0xBD - VK_OEM_MINUS '-' any country
4560 // 0xBE - VK_OEM_PERIOD '.' any country
4561 // 0xBF - VK_OEM_2 '/?' for US
4562 // 0xC0 - VK_OEM_3 '`~' for US
4563 // 0xC1 - VK_ABNT_C1 '/?' for Brazilian
4564 // 0xC2 - VK_ABNT_C2 separator key on numpad (Brazilian or JIS for Mac)
4565 // 0xDB - VK_OEM_4 '[{' for US
4566 // 0xDC - VK_OEM_5 '\|' for US
4567 // 0xDD - VK_OEM_6 ']}' for US
4568 // 0xDE - VK_OEM_7 ''"' for US
4571 // 0xE2 - VK_OEM_102 '\_' for JIS
4575 static const int8_t xlat
[256] = {
4576 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
4577 //-----------------------------------------------------------------------
4578 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00
4579 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10
4580 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20
4581 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, // 30
4582 -1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 40
4583 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, -1, -1, // 50
4584 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, -1, 49, 50, 51, // 60
4585 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 70
4586 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
4587 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90
4588 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0
4589 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 53, 54, 55, 56, 57, // B0
4590 58, 59, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0
4591 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 61, 62, 63, 64, 65, // D0
4592 -1, 66, 67, 68, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0
4593 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // F0
4596 return xlat
[aVirtualKey
];
4599 const DeadKeyTable
* KeyboardLayout::AddDeadKeyTable(
4600 const DeadKeyEntry
* aDeadKeyArray
, uint32_t aEntries
) {
4601 DeadKeyTableListEntry
* next
= mDeadKeyTableListHead
;
4603 const size_t bytes
= offsetof(DeadKeyTableListEntry
, data
) +
4604 DeadKeyTable::SizeInBytes(aEntries
);
4605 uint8_t* p
= new uint8_t[bytes
];
4607 mDeadKeyTableListHead
= reinterpret_cast<DeadKeyTableListEntry
*>(p
);
4608 mDeadKeyTableListHead
->next
= next
;
4611 reinterpret_cast<DeadKeyTable
*>(mDeadKeyTableListHead
->data
);
4613 dkt
->Init(aDeadKeyArray
, aEntries
);
4618 void KeyboardLayout::ReleaseDeadKeyTables() {
4619 while (mDeadKeyTableListHead
) {
4620 uint8_t* p
= reinterpret_cast<uint8_t*>(mDeadKeyTableListHead
);
4621 mDeadKeyTableListHead
= mDeadKeyTableListHead
->next
;
4627 bool KeyboardLayout::EnsureDeadKeyActive(bool aIsActive
, uint8_t aDeadKey
,
4628 const PBYTE aDeadKeyKbdState
) {
4631 char16_t dummyChars
[5];
4633 ::ToUnicodeEx(aDeadKey
, 0, (PBYTE
)aDeadKeyKbdState
, (LPWSTR
)dummyChars
,
4634 std::size(dummyChars
), 0, mKeyboardLayout
);
4636 // <0 - Dead key state is active. The keyboard driver will wait for next
4638 // 1 - Previous pressed key was a valid base character that produced
4639 // exactly one composite character.
4640 // >1 - Previous pressed key does not produce any composite characters.
4641 // Return dead-key character followed by base character(s).
4642 } while ((ret
< 0) != aIsActive
);
4647 void KeyboardLayout::ActivateDeadKeyState(const NativeKey
& aNativeKey
) {
4648 // Dead-key state should be activated at keydown.
4649 if (!aNativeKey
.IsKeyDownMessage()) {
4653 mActiveDeadKeys
.AppendElement(aNativeKey
.mOriginalVirtualKeyCode
);
4654 mDeadKeyShiftStates
.AppendElement(aNativeKey
.GetShiftState());
4657 void KeyboardLayout::DeactivateDeadKeyState() {
4658 if (mActiveDeadKeys
.IsEmpty()) {
4663 memset(kbdState
, 0, sizeof(kbdState
));
4665 // Assume that the last dead key can finish dead key sequence.
4666 VirtualKey::FillKbdState(kbdState
, mDeadKeyShiftStates
.LastElement());
4667 EnsureDeadKeyActive(false, mActiveDeadKeys
.LastElement(), kbdState
);
4668 mActiveDeadKeys
.Clear();
4669 mDeadKeyShiftStates
.Clear();
4672 bool KeyboardLayout::AddDeadKeyEntry(char16_t aBaseChar
,
4673 char16_t aCompositeChar
,
4674 nsTArray
<DeadKeyEntry
>& aDeadKeyArray
) {
4675 auto dke
= DeadKeyEntry(aBaseChar
, aCompositeChar
);
4676 for (uint32_t index
= 0; index
< aDeadKeyArray
.Length(); index
++) {
4677 if (aDeadKeyArray
[index
] == dke
) {
4682 aDeadKeyArray
.AppendElement(dke
);
4687 uint32_t KeyboardLayout::GetDeadKeyCombinations(
4688 uint8_t aDeadKey
, const PBYTE aDeadKeyKbdState
,
4689 uint16_t aShiftStatesWithBaseChars
, nsTArray
<DeadKeyEntry
>& aDeadKeyArray
) {
4690 bool deadKeyActive
= false;
4691 uint32_t entries
= 0;
4693 memset(kbdState
, 0, sizeof(kbdState
));
4695 for (uint32_t shiftState
= 0; shiftState
< 16; shiftState
++) {
4696 if (!(aShiftStatesWithBaseChars
& (1 << shiftState
))) {
4700 VirtualKey::FillKbdState(kbdState
, shiftState
);
4702 for (uint32_t virtualKey
= 0; virtualKey
< 256; virtualKey
++) {
4703 int32_t vki
= GetKeyIndex(virtualKey
);
4704 // Dead-key can pair only with such key that produces exactly one base
4707 mVirtualKeys
[vki
].GetNativeUniChars(shiftState
).Length() == 1) {
4708 // Ensure dead-key is in active state, when it swallows entered
4709 // character and waits for the next pressed key.
4710 if (!deadKeyActive
) {
4711 deadKeyActive
= EnsureDeadKeyActive(true, aDeadKey
, aDeadKeyKbdState
);
4714 // Depending on the character the followed the dead-key, the keyboard
4715 // driver can produce one composite character, or a dead-key character
4716 // followed by a second character.
4717 char16_t compositeChars
[5];
4719 ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)compositeChars
,
4720 std::size(compositeChars
), 0, mKeyboardLayout
);
4723 // This key combination does not produce any characters. The
4724 // dead-key is still in active state.
4727 char16_t baseChars
[5];
4728 ret
= ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)baseChars
,
4729 std::size(baseChars
), 0, mKeyboardLayout
);
4730 if (entries
< aDeadKeyArray
.Capacity()) {
4733 // Exactly one composite character produced. Now, when
4734 // dead-key is not active, repeat the last character one more
4735 // time to determine the base character.
4736 if (AddDeadKeyEntry(baseChars
[0], compositeChars
[0],
4740 deadKeyActive
= false;
4743 // If pressing another dead-key produces different character,
4744 // we should register the dead-key entry with first character
4745 // produced by current key.
4747 // First inactivate the dead-key state completely.
4749 EnsureDeadKeyActive(false, aDeadKey
, aDeadKeyKbdState
);
4750 if (NS_WARN_IF(deadKeyActive
)) {
4751 MOZ_LOG(gKeyLog
, LogLevel::Error
,
4752 (" failed to deactivating the dead-key state..."));
4755 for (int32_t i
= 0; i
< 5; ++i
) {
4756 ret
= ::ToUnicodeEx(virtualKey
, 0, kbdState
,
4757 (LPWSTR
)baseChars
, std::size(baseChars
),
4758 0, mKeyboardLayout
);
4764 AddDeadKeyEntry(baseChars
[0], compositeChars
[0],
4768 // Inactivate dead-key state for current virtual keycode.
4769 EnsureDeadKeyActive(false, virtualKey
, kbdState
);
4773 NS_WARNING("File a bug for this dead-key handling!");
4774 deadKeyActive
= false;
4779 gKeyLog
, LogLevel::Verbose
,
4780 (" %s -> %s (%d): DeadKeyEntry(%s, %s) (ret=%d)",
4781 kVirtualKeyName
[aDeadKey
], kVirtualKeyName
[virtualKey
], vki
,
4782 GetCharacterCodeNames(compositeChars
, 1).get(),
4785 : GetCharacterCodeNames(baseChars
, std::min(ret
, 5)).get(),
4790 // 1. Unexpected dead-key. Dead-key chaining is not supported.
4791 // 2. More than one character generated. This is not a valid
4792 // dead-key and base character combination.
4793 deadKeyActive
= false;
4795 gKeyLog
, LogLevel::Verbose
,
4796 (" %s -> %s (%d): Unsupport dead key type(%s) (ret=%d)",
4797 kVirtualKeyName
[aDeadKey
], kVirtualKeyName
[virtualKey
], vki
,
4800 : GetCharacterCodeNames(compositeChars
, std::min(ret
, 5))
4809 if (deadKeyActive
) {
4810 deadKeyActive
= EnsureDeadKeyActive(false, aDeadKey
, aDeadKeyKbdState
);
4813 aDeadKeyArray
.Sort();
4818 uint32_t KeyboardLayout::ConvertNativeKeyCodeToDOMKeyCode(
4819 UINT aNativeKeyCode
) const {
4820 // Alphabet or Numeric or Numpad or Function keys
4821 if ((aNativeKeyCode
>= 0x30 && aNativeKeyCode
<= 0x39) ||
4822 (aNativeKeyCode
>= 0x41 && aNativeKeyCode
<= 0x5A) ||
4823 (aNativeKeyCode
>= 0x60 && aNativeKeyCode
<= 0x87)) {
4824 return static_cast<uint32_t>(aNativeKeyCode
);
4826 switch (aNativeKeyCode
) {
4827 // Following keycodes are same as our DOM keycodes
4835 case VK_MENU
: // Alt
4837 case VK_CAPITAL
: // CAPS LOCK
4838 case VK_KANA
: // same as VK_HANGUL
4841 case VK_HANJA
: // same as VK_KANJI
4848 case VK_PRIOR
: // PAGE UP
4849 case VK_NEXT
: // PAGE DOWN
4862 case VK_APPS
: // Context Menu
4865 case VK_SCROLL
: // SCROLL LOCK
4866 case VK_ATTN
: // Attension key of IBM midrange computers, e.g., AS/400
4867 case VK_CRSEL
: // Cursor Selection
4868 case VK_EXSEL
: // Extend Selection
4869 case VK_EREOF
: // Erase EOF key of IBM 3270 keyboard layout
4872 case VK_PA1
: // PA1 key of IBM 3270 keyboard layout
4873 return uint32_t(aNativeKeyCode
);
4878 // Windows key should be mapped to a Win keycode
4879 // They should be able to be distinguished by DOM3 KeyboardEvent.location
4884 case VK_VOLUME_MUTE
:
4885 return NS_VK_VOLUME_MUTE
;
4886 case VK_VOLUME_DOWN
:
4887 return NS_VK_VOLUME_DOWN
;
4889 return NS_VK_VOLUME_UP
;
4897 return NS_VK_CONTROL
;
4899 // Note that even if the key is AltGr, we should return NS_VK_ALT for
4900 // compatibility with both older Gecko and the other browsers.
4905 // Following keycodes are not defined in our DOM keycodes.
4906 case VK_BROWSER_BACK
:
4907 case VK_BROWSER_FORWARD
:
4908 case VK_BROWSER_REFRESH
:
4909 case VK_BROWSER_STOP
:
4910 case VK_BROWSER_SEARCH
:
4911 case VK_BROWSER_FAVORITES
:
4912 case VK_BROWSER_HOME
:
4913 case VK_MEDIA_NEXT_TRACK
:
4914 case VK_MEDIA_PREV_TRACK
:
4916 case VK_MEDIA_PLAY_PAUSE
:
4917 case VK_LAUNCH_MAIL
:
4918 case VK_LAUNCH_MEDIA_SELECT
:
4919 case VK_LAUNCH_APP1
:
4920 case VK_LAUNCH_APP2
:
4923 // Following OEM specific virtual keycodes should pass through DOM keyCode
4924 // for compatibility with the other browsers on Windows.
4926 // Following OEM specific virtual keycodes are defined for Fujitsu/OASYS.
4927 case VK_OEM_FJ_JISHO
:
4928 case VK_OEM_FJ_MASSHOU
:
4929 case VK_OEM_FJ_TOUROKU
:
4930 case VK_OEM_FJ_LOYA
:
4931 case VK_OEM_FJ_ROYA
:
4932 // Not sure what means "ICO".
4936 // Following OEM specific virtual keycodes are defined for Nokia/Ericsson.
4949 case VK_OEM_BACKTAB
:
4950 // VK_OEM_CLEAR is defined as not OEM specific, but let's pass though
4951 // DOM keyCode like other OEM specific virtual keycodes.
4953 return uint32_t(aNativeKeyCode
);
4955 // 0xE1 is an OEM specific virtual keycode. However, the value is already
4956 // used in our DOM keyCode for AltGr on Linux. So, this virtual keycode
4957 // cannot pass through DOM keyCode.
4961 // Following keycodes are OEM keys which are keycodes for non-alphabet and
4962 // non-numeric keys, we should compute each keycode of them from unshifted
4963 // character which is inputted by each key. But if the unshifted character
4964 // is not an ASCII character but shifted character is an ASCII character,
4965 // we should refer it.
4980 NS_ASSERTION(IsPrintableCharKey(aNativeKeyCode
),
4981 "The key must be printable");
4982 ModifierKeyState
modKeyState(0);
4983 UniCharsAndModifiers uniChars
=
4984 GetUniCharsAndModifiers(aNativeKeyCode
, modKeyState
);
4985 if (uniChars
.Length() != 1 || uniChars
.CharAt(0) < ' ' ||
4986 uniChars
.CharAt(0) > 0x7F) {
4987 modKeyState
.Set(MODIFIER_SHIFT
);
4988 uniChars
= GetUniCharsAndModifiers(aNativeKeyCode
, modKeyState
);
4989 if (uniChars
.Length() != 1 || uniChars
.CharAt(0) < ' ' ||
4990 uniChars
.CharAt(0) > 0x7F) {
4991 // In this case, we've returned 0 in this case for long time because
4992 // we decided that we should avoid setting same keyCode value to 2 or
4993 // more keys since active keyboard layout may have a key to input the
4994 // punctuation with different key. However, setting keyCode to 0
4995 // makes some web applications which are aware of neither
4996 // KeyboardEvent.key nor KeyboardEvent.code not work with Firefox
4997 // when user selects non-ASCII capable keyboard layout such as
4998 // Russian and Thai layout. So, let's decide keyCode value with
4999 // major keyboard layout's key which causes the OEM keycode.
5000 // Actually, this maps same keyCode value to 2 keys on Russian
5001 // keyboard layout. "Period" key causes VK_OEM_PERIOD but inputs
5002 // Yu of Cyrillic and "Slash" key causes VK_OEM_2 (same as US
5003 // keyboard layout) but inputs "." (period of ASCII). Therefore,
5004 // we return DOM_VK_PERIOD which is same as VK_OEM_PERIOD for
5005 // "Period" key. On the other hand, we use same keyCode value for
5006 // "Slash" key too because it inputs ".".
5008 switch (aNativeKeyCode
) {
5010 code
= CODE_NAME_INDEX_Semicolon
;
5013 code
= CODE_NAME_INDEX_Equal
;
5016 code
= CODE_NAME_INDEX_Comma
;
5019 code
= CODE_NAME_INDEX_Minus
;
5022 code
= CODE_NAME_INDEX_Period
;
5025 code
= CODE_NAME_INDEX_Slash
;
5028 code
= CODE_NAME_INDEX_Backquote
;
5031 code
= CODE_NAME_INDEX_BracketLeft
;
5034 code
= CODE_NAME_INDEX_Backslash
;
5037 code
= CODE_NAME_INDEX_BracketRight
;
5040 code
= CODE_NAME_INDEX_Quote
;
5043 // Use keyCode value for "Backquote" key on UK keyboard layout.
5044 code
= CODE_NAME_INDEX_Backquote
;
5047 // Use keyCode value for "IntlBackslash" key.
5048 code
= CODE_NAME_INDEX_IntlBackslash
;
5050 case VK_ABNT_C1
: // "/" of ABNT.
5051 // Use keyCode value for "IntlBackslash" key on ABNT keyboard
5053 code
= CODE_NAME_INDEX_IntlBackslash
;
5056 MOZ_ASSERT_UNREACHABLE("Handle all OEM keycode values");
5059 return WidgetKeyboardEvent::GetFallbackKeyCodeOfPunctuationKey(code
);
5062 return WidgetUtils::ComputeKeyCodeFromChar(uniChars
.CharAt(0));
5065 // IE sets 0xC2 to the DOM keyCode for VK_ABNT_C2. However, we're already
5066 // using NS_VK_SEPARATOR for the separator key on Mac and Linux. Therefore,
5067 // We should keep consistency between Gecko on all platforms rather than
5068 // with other browsers since a lot of keyCode values are already different
5069 // between browsers.
5071 return NS_VK_SEPARATOR
;
5073 // VK_PROCESSKEY means IME already consumed the key event.
5075 return NS_VK_PROCESSKEY
;
5076 // VK_PACKET is generated by SendInput() API, we don't need to
5077 // care this message as key event.
5080 // If a key is not mapped to a virtual keycode, 0xFF is used.
5082 NS_WARNING("The key is failed to be converted to a virtual keycode");
5086 nsPrintfCString
warning(
5087 "Unknown virtual keycode (0x%08X), please check the "
5088 "latest MSDN document, there may be some new "
5089 "keycodes we've never known.",
5091 NS_WARNING(warning
.get());
5096 KeyNameIndex
KeyboardLayout::ConvertNativeKeyCodeToKeyNameIndex(
5097 uint8_t aVirtualKey
) const {
5098 if (IsPrintableCharKey(aVirtualKey
) || aVirtualKey
== VK_PACKET
) {
5099 return KEY_NAME_INDEX_USE_STRING
;
5102 // If the keyboard layout has AltGr and AltRight key is pressed,
5104 if (aVirtualKey
== VK_RMENU
&& HasAltGr()) {
5105 return KEY_NAME_INDEX_AltGraph
;
5108 switch (aVirtualKey
) {
5109 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5110 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5112 return aKeyNameIndex;
5114 #include "NativeKeyToDOMKeyName.h"
5116 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5122 HKL layout
= KeyboardLayout::GetLayout();
5123 WORD langID
= LOWORD(static_cast<HKL
>(layout
));
5124 WORD primaryLangID
= PRIMARYLANGID(langID
);
5126 if (primaryLangID
== LANG_JAPANESE
) {
5127 switch (aVirtualKey
) {
5128 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5129 #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, \
5132 return aKeyNameIndex;
5134 #include "NativeKeyToDOMKeyName.h"
5136 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5141 } else if (primaryLangID
== LANG_KOREAN
) {
5142 switch (aVirtualKey
) {
5143 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5144 #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5146 return aKeyNameIndex;
5148 #include "NativeKeyToDOMKeyName.h"
5150 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5153 return KEY_NAME_INDEX_Unidentified
;
5157 switch (aVirtualKey
) {
5158 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5159 #define NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5161 return aKeyNameIndex;
5163 #include "NativeKeyToDOMKeyName.h"
5165 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5168 return KEY_NAME_INDEX_Unidentified
;
5173 CodeNameIndex
KeyboardLayout::ConvertScanCodeToCodeNameIndex(UINT aScanCode
) {
5174 switch (aScanCode
) {
5175 #define NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX(aNativeKey, aCodeNameIndex) \
5177 return aCodeNameIndex;
5179 #include "NativeKeyToDOMCodeName.h"
5181 #undef NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX
5184 return CODE_NAME_INDEX_UNKNOWN
;
5188 nsresult
KeyboardLayout::SynthesizeNativeKeyEvent(
5189 nsWindow
* aWidget
, int32_t aNativeKeyboardLayout
, int32_t aNativeKeyCode
,
5190 uint32_t aModifierFlags
, const nsAString
& aCharacters
,
5191 const nsAString
& aUnmodifiedCharacters
) {
5192 UINT keyboardLayoutListCount
= ::GetKeyboardLayoutList(0, nullptr);
5193 NS_ASSERTION(keyboardLayoutListCount
> 0,
5194 "One keyboard layout must be installed at least");
5195 HKL keyboardLayoutListBuff
[50];
5196 HKL
* keyboardLayoutList
= keyboardLayoutListCount
< 50
5197 ? keyboardLayoutListBuff
5198 : new HKL
[keyboardLayoutListCount
];
5199 keyboardLayoutListCount
=
5200 ::GetKeyboardLayoutList(keyboardLayoutListCount
, keyboardLayoutList
);
5201 NS_ASSERTION(keyboardLayoutListCount
> 0,
5202 "Failed to get all keyboard layouts installed on the system");
5204 nsPrintfCString
layoutName("%08x", aNativeKeyboardLayout
);
5205 HKL loadedLayout
= LoadKeyboardLayoutA(layoutName
.get(), KLF_NOTELLSHELL
);
5206 if (loadedLayout
== nullptr) {
5207 if (keyboardLayoutListBuff
!= keyboardLayoutList
) {
5208 delete[] keyboardLayoutList
;
5210 return NS_ERROR_NOT_AVAILABLE
;
5213 // Setup clean key state and load desired layout
5214 BYTE originalKbdState
[256];
5215 ::GetKeyboardState(originalKbdState
);
5217 memset(kbdState
, 0, sizeof(kbdState
));
5218 // This changes the state of the keyboard for the current thread only,
5219 // and we'll restore it soon, so this should be OK.
5220 ::SetKeyboardState(kbdState
);
5222 OverrideLayout(loadedLayout
);
5224 bool isAltGrKeyPress
= false;
5225 if (aModifierFlags
& nsIWidget::ALTGRAPH
) {
5227 return NS_ERROR_INVALID_ARG
;
5229 // AltGr emulates ControlLeft key press and AltRight key press.
5230 // So, we should remove those flags from aModifierFlags before
5231 // calling WinUtils::SetupKeyModifiersSequence() to create correct
5233 // FYI: We don't support both ControlLeft and AltRight (AltGr) are
5234 // pressed at the same time unless synthesizing key is
5236 aModifierFlags
&= ~(nsIWidget::CTRL_L
| nsIWidget::ALT_R
);
5239 uint8_t argumentKeySpecific
= 0;
5240 switch (aNativeKeyCode
& 0xFF) {
5242 aModifierFlags
&= ~(nsIWidget::SHIFT_L
| nsIWidget::SHIFT_R
);
5243 argumentKeySpecific
= VK_LSHIFT
;
5246 aModifierFlags
&= ~nsIWidget::SHIFT_L
;
5247 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5248 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_SHIFT
;
5251 aModifierFlags
&= ~nsIWidget::SHIFT_R
;
5252 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5253 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_SHIFT
;
5256 aModifierFlags
&= ~(nsIWidget::CTRL_L
| nsIWidget::CTRL_R
);
5257 argumentKeySpecific
= VK_LCONTROL
;
5260 aModifierFlags
&= ~nsIWidget::CTRL_L
;
5261 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5262 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_CONTROL
;
5265 aModifierFlags
&= ~nsIWidget::CTRL_R
;
5266 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5267 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_CONTROL
;
5270 aModifierFlags
&= ~(nsIWidget::ALT_L
| nsIWidget::ALT_R
);
5271 argumentKeySpecific
= VK_LMENU
;
5274 aModifierFlags
&= ~nsIWidget::ALT_L
;
5275 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5276 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_MENU
;
5279 aModifierFlags
&= ~(nsIWidget::ALT_R
| nsIWidget::ALTGRAPH
);
5280 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5281 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_MENU
;
5282 // If AltRight key is AltGr in the keyboard layout, let's use
5283 // SetupKeyModifiersSequence() to emulate the native behavior
5284 // since the same event order between keydown and keyup makes
5285 // the following code complicated.
5287 isAltGrKeyPress
= true;
5288 aModifierFlags
&= ~nsIWidget::CTRL_L
;
5289 aModifierFlags
|= nsIWidget::ALTGRAPH
;
5293 aModifierFlags
&= ~nsIWidget::CAPS_LOCK
;
5294 argumentKeySpecific
= VK_CAPITAL
;
5297 aModifierFlags
&= ~nsIWidget::NUM_LOCK
;
5298 argumentKeySpecific
= VK_NUMLOCK
;
5302 AutoTArray
<KeyPair
, 10> keySequence
;
5303 WinUtils::SetupKeyModifiersSequence(&keySequence
, aModifierFlags
, WM_KEYDOWN
);
5304 if (!isAltGrKeyPress
) {
5305 keySequence
.AppendElement(KeyPair(aNativeKeyCode
, argumentKeySpecific
));
5308 // Simulate the pressing of each modifier key and then the real key
5309 // FYI: Each NativeKey instance here doesn't need to override keyboard layout
5310 // since this method overrides and restores the keyboard layout.
5311 for (uint32_t i
= 0; i
< keySequence
.Length(); ++i
) {
5312 uint8_t key
= keySequence
[i
].mGeneral
;
5313 uint8_t keySpecific
= keySequence
[i
].mSpecific
;
5314 uint16_t scanCode
= keySequence
[i
].mScanCode
;
5315 kbdState
[key
] = 0x81; // key is down and toggled on if appropriate
5317 kbdState
[keySpecific
] = 0x81;
5319 ::SetKeyboardState(kbdState
);
5320 ModifierKeyState modKeyState
;
5321 // If scan code isn't specified explicitly, let's compute it with current
5325 ComputeScanCodeForVirtualKeyCode(keySpecific
? keySpecific
: key
);
5327 LPARAM lParam
= static_cast<LPARAM
>(scanCode
<< 16);
5328 // If the scan code is for an extended key, set extended key flag.
5329 if ((scanCode
& 0xFF00) == 0xE000) {
5330 lParam
|= 0x1000000;
5332 // When AltGr key is pressed, both ControlLeft and AltRight cause
5333 // WM_KEYDOWN messages.
5334 bool makeSysKeyMsg
=
5335 !(aModifierFlags
& nsIWidget::ALTGRAPH
) && IsSysKey(key
, modKeyState
);
5337 WinUtils::InitMSG(makeSysKeyMsg
? WM_SYSKEYDOWN
: WM_KEYDOWN
, key
,
5338 lParam
, aWidget
->GetWindowHandle());
5339 if (i
== keySequence
.Length() - 1) {
5340 bool makeDeadCharMsg
=
5341 (IsDeadKey(key
, modKeyState
) && aCharacters
.IsEmpty());
5342 nsAutoString
chars(aCharacters
);
5343 if (makeDeadCharMsg
) {
5344 UniCharsAndModifiers deadChars
=
5345 GetUniCharsAndModifiers(key
, modKeyState
);
5346 chars
= deadChars
.ToString();
5347 NS_ASSERTION(chars
.Length() == 1,
5348 "Dead char must be only one character");
5350 if (chars
.IsEmpty()) {
5351 NativeKey
nativeKey(aWidget
, keyDownMsg
, modKeyState
);
5352 nativeKey
.HandleKeyDownMessage();
5354 AutoTArray
<NativeKey::FakeCharMsg
, 10> fakeCharMsgs
;
5355 for (uint32_t j
= 0; j
< chars
.Length(); j
++) {
5356 NativeKey::FakeCharMsg
* fakeCharMsg
= fakeCharMsgs
.AppendElement();
5357 fakeCharMsg
->mCharCode
= chars
.CharAt(j
);
5358 fakeCharMsg
->mScanCode
= scanCode
;
5359 fakeCharMsg
->mIsSysKey
= makeSysKeyMsg
;
5360 fakeCharMsg
->mIsDeadKey
= makeDeadCharMsg
;
5362 NativeKey
nativeKey(aWidget
, keyDownMsg
, modKeyState
, 0, &fakeCharMsgs
);
5364 nativeKey
.HandleKeyDownMessage(&dispatched
);
5365 // If some char messages are not consumed, let's emulate the widget
5366 // receiving the message directly.
5367 for (uint32_t j
= 1; j
< fakeCharMsgs
.Length(); j
++) {
5368 if (fakeCharMsgs
[j
].mConsumed
) {
5371 MSG charMsg
= fakeCharMsgs
[j
].GetCharMsg(aWidget
->GetWindowHandle());
5372 NativeKey
nativeKey(aWidget
, charMsg
, modKeyState
);
5373 nativeKey
.HandleCharMessage(charMsg
);
5377 NativeKey
nativeKey(aWidget
, keyDownMsg
, modKeyState
);
5378 nativeKey
.HandleKeyDownMessage();
5382 keySequence
.Clear();
5383 if (!isAltGrKeyPress
) {
5384 keySequence
.AppendElement(KeyPair(aNativeKeyCode
, argumentKeySpecific
));
5386 WinUtils::SetupKeyModifiersSequence(&keySequence
, aModifierFlags
, WM_KEYUP
);
5387 for (uint32_t i
= 0; i
< keySequence
.Length(); ++i
) {
5388 uint8_t key
= keySequence
[i
].mGeneral
;
5389 uint8_t keySpecific
= keySequence
[i
].mSpecific
;
5390 uint16_t scanCode
= keySequence
[i
].mScanCode
;
5391 kbdState
[key
] = 0; // key is up and toggled off if appropriate
5393 kbdState
[keySpecific
] = 0;
5395 ::SetKeyboardState(kbdState
);
5396 ModifierKeyState modKeyState
;
5397 // If scan code isn't specified explicitly, let's compute it with current
5401 ComputeScanCodeForVirtualKeyCode(keySpecific
? keySpecific
: key
);
5403 LPARAM lParam
= static_cast<LPARAM
>(scanCode
<< 16);
5404 // If the scan code is for an extended key, set extended key flag.
5405 if ((scanCode
& 0xFF00) == 0xE000) {
5406 lParam
|= 0x1000000;
5408 // Don't use WM_SYSKEYUP for Alt keyup.
5409 // NOTE: When AltGr was pressed, ControlLeft causes WM_SYSKEYUP normally.
5410 bool makeSysKeyMsg
= IsSysKey(key
, modKeyState
) && key
!= VK_MENU
;
5411 MSG keyUpMsg
= WinUtils::InitMSG(makeSysKeyMsg
? WM_SYSKEYUP
: WM_KEYUP
,
5412 key
, lParam
, aWidget
->GetWindowHandle());
5413 NativeKey
nativeKey(aWidget
, keyUpMsg
, modKeyState
);
5414 nativeKey
.HandleKeyUpMessage();
5417 // Restore old key state and layout
5418 ::SetKeyboardState(originalKbdState
);
5421 // Don't unload the layout if it's installed actually.
5422 for (uint32_t i
= 0; i
< keyboardLayoutListCount
; i
++) {
5423 if (keyboardLayoutList
[i
] == loadedLayout
) {
5428 if (keyboardLayoutListBuff
!= keyboardLayoutList
) {
5429 delete[] keyboardLayoutList
;
5432 ::UnloadKeyboardLayout(loadedLayout
);
5437 /*****************************************************************************
5438 * mozilla::widget::DeadKeyTable
5439 *****************************************************************************/
5441 char16_t
DeadKeyTable::GetCompositeChar(char16_t aBaseChar
) const {
5442 // Dead-key table is sorted by BaseChar in ascending order.
5443 // Usually they are too small to use binary search.
5445 for (uint32_t index
= 0; index
< mEntries
; index
++) {
5446 if (mTable
[index
].BaseChar
== aBaseChar
) {
5447 return mTable
[index
].CompositeChar
;
5449 if (mTable
[index
].BaseChar
> aBaseChar
) {
5457 /*****************************************************************************
5458 * mozilla::widget::RedirectedKeyDownMessage
5459 *****************************************************************************/
5461 MSG
RedirectedKeyDownMessageManager::sRedirectedKeyDownMsg
;
5462 bool RedirectedKeyDownMessageManager::sDefaultPreventedOfRedirectedMsg
= false;
5465 bool RedirectedKeyDownMessageManager::IsRedirectedMessage(const MSG
& aMsg
) {
5466 return (aMsg
.message
== WM_KEYDOWN
|| aMsg
.message
== WM_SYSKEYDOWN
) &&
5467 (sRedirectedKeyDownMsg
.message
== aMsg
.message
&&
5468 WinUtils::GetScanCode(sRedirectedKeyDownMsg
.lParam
) ==
5469 WinUtils::GetScanCode(aMsg
.lParam
));
5473 void RedirectedKeyDownMessageManager::RemoveNextCharMessage(HWND aWnd
) {
5475 if (WinUtils::PeekMessage(&msg
, aWnd
, WM_KEYFIRST
, WM_KEYLAST
,
5476 PM_NOREMOVE
| PM_NOYIELD
) &&
5477 (msg
.message
== WM_CHAR
|| msg
.message
== WM_SYSCHAR
)) {
5478 WinUtils::PeekMessage(&msg
, aWnd
, msg
.message
, msg
.message
,
5479 PM_REMOVE
| PM_NOYIELD
);
5483 } // namespace widget
5484 } // namespace mozilla