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/DebugOnly.h"
11 #include "mozilla/MouseEvents.h"
12 #include "mozilla/MiscEvents.h"
13 #include "mozilla/Preferences.h"
14 #include "mozilla/TextEvents.h"
16 #include "nsAlgorithm.h"
17 #include "nsExceptionHandler.h"
18 #include "nsGkAtoms.h"
19 #include "nsIUserIdleServiceInternal.h"
20 #include "nsIWindowsRegKey.h"
21 #include "nsPrintfCString.h"
22 #include "nsQuickSort.h"
23 #include "nsReadableUtils.h"
24 #include "nsServiceManagerUtils.h"
25 #include "nsToolkit.h"
26 #include "nsUnicharUtils.h"
27 #include "nsWindowDbg.h"
29 #include "KeyboardLayout.h"
30 #include "WidgetUtils.h"
44 // In WinUser.h, MAPVK_VK_TO_VSC_EX is defined only when WINVER >= 0x0600
45 #ifndef MAPVK_VK_TO_VSC_EX
46 # define MAPVK_VK_TO_VSC_EX (4)
49 // For collecting other people's log, tell them `MOZ_LOG=KeyboardHandler:4,sync`
50 // rather than `MOZ_LOG=KeyboardHandler:5,sync` since using `5` may create too
52 // Therefore you shouldn't use `LogLevel::Verbose` for logging usual behavior.
53 mozilla::LazyLogModule
gKeyLog("KeyboardHandler");
58 static const char* const kVirtualKeyName
[] = {
214 "VK_OEM_NEC_EQUAL, VK_OEM_FJ_JISHO",
236 "VK_BROWSER_FORWARD",
237 "VK_BROWSER_REFRESH",
240 "VK_BROWSER_FAVORITES",
246 "VK_MEDIA_NEXT_TRACK",
247 "VK_MEDIA_PREV_TRACK",
249 "VK_MEDIA_PLAY_PAUSE",
251 "VK_LAUNCH_MEDIA_SELECT",
331 static_assert(sizeof(kVirtualKeyName
) / sizeof(const char*) == 0x100,
332 "The virtual key name must be defined just 256 keys");
334 static const char* GetBoolName(bool aBool
) { return aBool
? "true" : "false"; }
336 static const nsCString
GetCharacterCodeName(WPARAM aCharCode
) {
339 return "NULL (0x0000)"_ns
;
341 return "BACKSPACE (0x0008)"_ns
;
343 return "CHARACTER TABULATION (0x0009)"_ns
;
345 return "LINE FEED (0x000A)"_ns
;
347 return "LINE TABULATION (0x000B)"_ns
;
349 return "FORM FEED (0x000C)"_ns
;
351 return "CARRIAGE RETURN (0x000D)"_ns
;
353 return "CANCEL (0x0018)"_ns
;
355 return "ESCAPE (0x001B)"_ns
;
357 return "SPACE (0x0020)"_ns
;
359 return "DELETE (0x007F)"_ns
;
361 return "NO-BREAK SPACE (0x00A0)"_ns
;
363 return "SOFT HYPHEN (0x00AD)"_ns
;
365 return "EN QUAD (0x2000)"_ns
;
367 return "EM QUAD (0x2001)"_ns
;
369 return "EN SPACE (0x2002)"_ns
;
371 return "EM SPACE (0x2003)"_ns
;
373 return "THREE-PER-EM SPACE (0x2004)"_ns
;
375 return "FOUR-PER-EM SPACE (0x2005)"_ns
;
377 return "SIX-PER-EM SPACE (0x2006)"_ns
;
379 return "FIGURE SPACE (0x2007)"_ns
;
381 return "PUNCTUATION SPACE (0x2008)"_ns
;
383 return "THIN SPACE (0x2009)"_ns
;
385 return "HAIR SPACE (0x200A)"_ns
;
387 return "ZERO WIDTH SPACE (0x200B)"_ns
;
389 return "ZERO WIDTH NON-JOINER (0x200C)"_ns
;
391 return "ZERO WIDTH JOINER (0x200D)"_ns
;
393 return "LEFT-TO-RIGHT MARK (0x200E)"_ns
;
395 return "RIGHT-TO-LEFT MARK (0x200F)"_ns
;
397 return "PARAGRAPH SEPARATOR (0x2029)"_ns
;
399 return "LEFT-TO-RIGHT EMBEDDING (0x202A)"_ns
;
401 return "RIGHT-TO-LEFT EMBEDDING (0x202B)"_ns
;
403 return "LEFT-TO-RIGHT OVERRIDE (0x202D)"_ns
;
405 return "RIGHT-TO-LEFT OVERRIDE (0x202E)"_ns
;
407 return "NARROW NO-BREAK SPACE (0x202F)"_ns
;
409 return "MEDIUM MATHEMATICAL SPACE (0x205F)"_ns
;
411 return "WORD JOINER (0x2060)"_ns
;
413 return "LEFT-TO-RIGHT ISOLATE (0x2066)"_ns
;
415 return "RIGHT-TO-LEFT ISOLATE (0x2067)"_ns
;
417 return "IDEOGRAPHIC SPACE (0x3000)"_ns
;
419 return "ZERO WIDTH NO-BREAK SPACE (0xFEFF)"_ns
;
421 if (aCharCode
< ' ' || (aCharCode
>= 0x80 && aCharCode
< 0xA0)) {
422 return nsPrintfCString("control (0x%04zX)", aCharCode
);
424 if (NS_IS_HIGH_SURROGATE(aCharCode
)) {
425 return nsPrintfCString("high surrogate (0x%04zX)", aCharCode
);
427 if (NS_IS_LOW_SURROGATE(aCharCode
)) {
428 return nsPrintfCString("low surrogate (0x%04zX)", aCharCode
);
430 return IS_IN_BMP(aCharCode
)
433 NS_ConvertUTF16toUTF8(nsAutoString(aCharCode
)).get(),
437 NS_ConvertUTF16toUTF8(nsAutoString(aCharCode
)).get(),
443 static const nsCString
GetKeyLocationName(uint32_t aLocation
) {
445 case eKeyLocationLeft
:
446 return "KEY_LOCATION_LEFT"_ns
;
447 case eKeyLocationRight
:
448 return "KEY_LOCATION_RIGHT"_ns
;
449 case eKeyLocationStandard
:
450 return "KEY_LOCATION_STANDARD"_ns
;
451 case eKeyLocationNumpad
:
452 return "KEY_LOCATION_NUMPAD"_ns
;
454 return nsPrintfCString("Unknown (0x%04X)", aLocation
);
458 static const nsCString
GetCharacterCodeNames(const char16_t
* aChars
,
464 result
.AssignLiteral("\"");
465 StringJoinAppend(result
, ", "_ns
, Span
{aChars
, aLength
},
466 [](nsACString
& dest
, const char16_t charValue
) {
467 dest
.Append(GetCharacterCodeName(charValue
));
469 result
.AppendLiteral("\"");
473 static const nsCString
GetCharacterCodeNames(
474 const UniCharsAndModifiers
& aUniCharsAndModifiers
) {
475 if (aUniCharsAndModifiers
.IsEmpty()) {
479 result
.AssignLiteral("\"");
480 StringJoinAppend(result
, ", "_ns
, Span
{aUniCharsAndModifiers
.ToString()},
481 [](nsACString
& dest
, const char16_t charValue
) {
482 dest
.Append(GetCharacterCodeName(charValue
));
484 result
.AppendLiteral("\"");
488 class MOZ_STACK_CLASS GetShiftStateName final
: public nsAutoCString
{
490 explicit GetShiftStateName(VirtualKey::ShiftState aShiftState
) {
492 AssignLiteral("none");
495 if (aShiftState
& VirtualKey::STATE_SHIFT
) {
496 AssignLiteral("Shift");
497 aShiftState
&= ~VirtualKey::STATE_SHIFT
;
499 if (aShiftState
& VirtualKey::STATE_CONTROL
) {
500 MaybeAppendSeparator();
501 AssignLiteral("Ctrl");
502 aShiftState
&= ~VirtualKey::STATE_CONTROL
;
504 if (aShiftState
& VirtualKey::STATE_ALT
) {
505 MaybeAppendSeparator();
506 AssignLiteral("Alt");
507 aShiftState
&= ~VirtualKey::STATE_ALT
;
509 if (aShiftState
& VirtualKey::STATE_CAPSLOCK
) {
510 MaybeAppendSeparator();
511 AssignLiteral("CapsLock");
512 aShiftState
&= ~VirtualKey::STATE_CAPSLOCK
;
514 MOZ_ASSERT(!aShiftState
);
518 void MaybeAppendSeparator() {
520 AppendLiteral(" | ");
525 static const nsCString
GetMessageName(UINT aMessage
) {
530 return "WM_KEYDOWN"_ns
;
532 return "WM_KEYUP"_ns
;
534 return "WM_SYSKEYDOWN"_ns
;
536 return "WM_SYSKEYUP"_ns
;
540 return "WM_UNICHAR"_ns
;
542 return "WM_SYSCHAR"_ns
;
544 return "WM_DEADCHAR"_ns
;
546 return "WM_SYSDEADCHAR"_ns
;
548 return "WM_APPCOMMAND"_ns
;
552 return nsPrintfCString("Unknown Message (0x%04X)", aMessage
);
556 static const nsCString
GetVirtualKeyCodeName(WPARAM aVK
) {
557 if (aVK
>= ArrayLength(kVirtualKeyName
)) {
558 return nsPrintfCString("Invalid (0x%08zX)", aVK
);
560 return nsCString(kVirtualKeyName
[aVK
]);
563 static const nsCString
GetAppCommandName(WPARAM aCommand
) {
565 case APPCOMMAND_BASS_BOOST
:
566 return "APPCOMMAND_BASS_BOOST"_ns
;
567 case APPCOMMAND_BASS_DOWN
:
568 return "APPCOMMAND_BASS_DOWN"_ns
;
569 case APPCOMMAND_BASS_UP
:
570 return "APPCOMMAND_BASS_UP"_ns
;
571 case APPCOMMAND_BROWSER_BACKWARD
:
572 return "APPCOMMAND_BROWSER_BACKWARD"_ns
;
573 case APPCOMMAND_BROWSER_FAVORITES
:
574 return "APPCOMMAND_BROWSER_FAVORITES"_ns
;
575 case APPCOMMAND_BROWSER_FORWARD
:
576 return "APPCOMMAND_BROWSER_FORWARD"_ns
;
577 case APPCOMMAND_BROWSER_HOME
:
578 return "APPCOMMAND_BROWSER_HOME"_ns
;
579 case APPCOMMAND_BROWSER_REFRESH
:
580 return "APPCOMMAND_BROWSER_REFRESH"_ns
;
581 case APPCOMMAND_BROWSER_SEARCH
:
582 return "APPCOMMAND_BROWSER_SEARCH"_ns
;
583 case APPCOMMAND_BROWSER_STOP
:
584 return "APPCOMMAND_BROWSER_STOP"_ns
;
585 case APPCOMMAND_CLOSE
:
586 return "APPCOMMAND_CLOSE"_ns
;
587 case APPCOMMAND_COPY
:
588 return "APPCOMMAND_COPY"_ns
;
589 case APPCOMMAND_CORRECTION_LIST
:
590 return "APPCOMMAND_CORRECTION_LIST"_ns
;
592 return "APPCOMMAND_CUT"_ns
;
593 case APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE
:
594 return "APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE"_ns
;
595 case APPCOMMAND_FIND
:
596 return "APPCOMMAND_FIND"_ns
;
597 case APPCOMMAND_FORWARD_MAIL
:
598 return "APPCOMMAND_FORWARD_MAIL"_ns
;
599 case APPCOMMAND_HELP
:
600 return "APPCOMMAND_HELP"_ns
;
601 case APPCOMMAND_LAUNCH_APP1
:
602 return "APPCOMMAND_LAUNCH_APP1"_ns
;
603 case APPCOMMAND_LAUNCH_APP2
:
604 return "APPCOMMAND_LAUNCH_APP2"_ns
;
605 case APPCOMMAND_LAUNCH_MAIL
:
606 return "APPCOMMAND_LAUNCH_MAIL"_ns
;
607 case APPCOMMAND_LAUNCH_MEDIA_SELECT
:
608 return "APPCOMMAND_LAUNCH_MEDIA_SELECT"_ns
;
609 case APPCOMMAND_MEDIA_CHANNEL_DOWN
:
610 return "APPCOMMAND_MEDIA_CHANNEL_DOWN"_ns
;
611 case APPCOMMAND_MEDIA_CHANNEL_UP
:
612 return "APPCOMMAND_MEDIA_CHANNEL_UP"_ns
;
613 case APPCOMMAND_MEDIA_FAST_FORWARD
:
614 return "APPCOMMAND_MEDIA_FAST_FORWARD"_ns
;
615 case APPCOMMAND_MEDIA_NEXTTRACK
:
616 return "APPCOMMAND_MEDIA_NEXTTRACK"_ns
;
617 case APPCOMMAND_MEDIA_PAUSE
:
618 return "APPCOMMAND_MEDIA_PAUSE"_ns
;
619 case APPCOMMAND_MEDIA_PLAY
:
620 return "APPCOMMAND_MEDIA_PLAY"_ns
;
621 case APPCOMMAND_MEDIA_PLAY_PAUSE
:
622 return "APPCOMMAND_MEDIA_PLAY_PAUSE"_ns
;
623 case APPCOMMAND_MEDIA_PREVIOUSTRACK
:
624 return "APPCOMMAND_MEDIA_PREVIOUSTRACK"_ns
;
625 case APPCOMMAND_MEDIA_RECORD
:
626 return "APPCOMMAND_MEDIA_RECORD"_ns
;
627 case APPCOMMAND_MEDIA_REWIND
:
628 return "APPCOMMAND_MEDIA_REWIND"_ns
;
629 case APPCOMMAND_MEDIA_STOP
:
630 return "APPCOMMAND_MEDIA_STOP"_ns
;
631 case APPCOMMAND_MIC_ON_OFF_TOGGLE
:
632 return "APPCOMMAND_MIC_ON_OFF_TOGGLE"_ns
;
633 case APPCOMMAND_MICROPHONE_VOLUME_DOWN
:
634 return "APPCOMMAND_MICROPHONE_VOLUME_DOWN"_ns
;
635 case APPCOMMAND_MICROPHONE_VOLUME_MUTE
:
636 return "APPCOMMAND_MICROPHONE_VOLUME_MUTE"_ns
;
637 case APPCOMMAND_MICROPHONE_VOLUME_UP
:
638 return "APPCOMMAND_MICROPHONE_VOLUME_UP"_ns
;
640 return "APPCOMMAND_NEW"_ns
;
641 case APPCOMMAND_OPEN
:
642 return "APPCOMMAND_OPEN"_ns
;
643 case APPCOMMAND_PASTE
:
644 return "APPCOMMAND_PASTE"_ns
;
645 case APPCOMMAND_PRINT
:
646 return "APPCOMMAND_PRINT"_ns
;
647 case APPCOMMAND_REDO
:
648 return "APPCOMMAND_REDO"_ns
;
649 case APPCOMMAND_REPLY_TO_MAIL
:
650 return "APPCOMMAND_REPLY_TO_MAIL"_ns
;
651 case APPCOMMAND_SAVE
:
652 return "APPCOMMAND_SAVE"_ns
;
653 case APPCOMMAND_SEND_MAIL
:
654 return "APPCOMMAND_SEND_MAIL"_ns
;
655 case APPCOMMAND_SPELL_CHECK
:
656 return "APPCOMMAND_SPELL_CHECK"_ns
;
657 case APPCOMMAND_TREBLE_DOWN
:
658 return "APPCOMMAND_TREBLE_DOWN"_ns
;
659 case APPCOMMAND_TREBLE_UP
:
660 return "APPCOMMAND_TREBLE_UP"_ns
;
661 case APPCOMMAND_UNDO
:
662 return "APPCOMMAND_UNDO"_ns
;
663 case APPCOMMAND_VOLUME_DOWN
:
664 return "APPCOMMAND_VOLUME_DOWN"_ns
;
665 case APPCOMMAND_VOLUME_MUTE
:
666 return "APPCOMMAND_VOLUME_MUTE"_ns
;
667 case APPCOMMAND_VOLUME_UP
:
668 return "APPCOMMAND_VOLUME_UP"_ns
;
670 return nsPrintfCString("Unknown app command (0x%08zX)", aCommand
);
674 static const nsCString
GetAppCommandDeviceName(LPARAM aDevice
) {
676 case FAPPCOMMAND_KEY
:
677 return "FAPPCOMMAND_KEY"_ns
;
678 case FAPPCOMMAND_MOUSE
:
679 return "FAPPCOMMAND_MOUSE"_ns
;
680 case FAPPCOMMAND_OEM
:
681 return "FAPPCOMMAND_OEM"_ns
;
683 return nsPrintfCString("Unknown app command device (0x%04" PRIXLPTR
")",
688 class MOZ_STACK_CLASS GetAppCommandKeysName final
: public nsAutoCString
{
690 explicit GetAppCommandKeysName(WPARAM aKeys
) {
691 if (aKeys
& MK_CONTROL
) {
692 AppendLiteral("MK_CONTROL");
693 aKeys
&= ~MK_CONTROL
;
695 if (aKeys
& MK_LBUTTON
) {
696 MaybeAppendSeparator();
697 AppendLiteral("MK_LBUTTON");
698 aKeys
&= ~MK_LBUTTON
;
700 if (aKeys
& MK_MBUTTON
) {
701 MaybeAppendSeparator();
702 AppendLiteral("MK_MBUTTON");
703 aKeys
&= ~MK_MBUTTON
;
705 if (aKeys
& MK_RBUTTON
) {
706 MaybeAppendSeparator();
707 AppendLiteral("MK_RBUTTON");
708 aKeys
&= ~MK_RBUTTON
;
710 if (aKeys
& MK_SHIFT
) {
711 MaybeAppendSeparator();
712 AppendLiteral("MK_SHIFT");
715 if (aKeys
& MK_XBUTTON1
) {
716 MaybeAppendSeparator();
717 AppendLiteral("MK_XBUTTON1");
718 aKeys
&= ~MK_XBUTTON1
;
720 if (aKeys
& MK_XBUTTON2
) {
721 MaybeAppendSeparator();
722 AppendLiteral("MK_XBUTTON2");
723 aKeys
&= ~MK_XBUTTON2
;
726 MaybeAppendSeparator();
727 AppendPrintf("Unknown Flags (0x%04zX)", aKeys
);
730 AssignLiteral("none (0x0000)");
735 void MaybeAppendSeparator() {
737 AppendLiteral(" | ");
742 static const nsCString
ToString(const MSG
& aMSG
) {
744 result
.AssignLiteral("{ message=");
745 result
.Append(GetMessageName(aMSG
.message
).get());
746 result
.AppendLiteral(", ");
747 switch (aMSG
.message
) {
753 "virtual keycode=%s, repeat count=%" PRIdLPTR
755 "scancode=0x%02X, extended key=%s, "
756 "context code=%s, previous key state=%s, "
757 "transition state=%s",
758 GetVirtualKeyCodeName(aMSG
.wParam
).get(), aMSG
.lParam
& 0xFFFF,
759 WinUtils::GetScanCode(aMSG
.lParam
),
760 GetBoolName(WinUtils::IsExtendedScanCode(aMSG
.lParam
)),
761 GetBoolName((aMSG
.lParam
& (1 << 29)) != 0),
762 GetBoolName((aMSG
.lParam
& (1 << 30)) != 0),
763 GetBoolName((aMSG
.lParam
& (1 << 31)) != 0));
770 "character code=%s, repeat count=%" PRIdLPTR
772 "scancode=0x%02X, extended key=%s, "
773 "context code=%s, previous key state=%s, "
774 "transition state=%s",
775 GetCharacterCodeName(aMSG
.wParam
).get(), aMSG
.lParam
& 0xFFFF,
776 WinUtils::GetScanCode(aMSG
.lParam
),
777 GetBoolName(WinUtils::IsExtendedScanCode(aMSG
.lParam
)),
778 GetBoolName((aMSG
.lParam
& (1 << 29)) != 0),
779 GetBoolName((aMSG
.lParam
& (1 << 30)) != 0),
780 GetBoolName((aMSG
.lParam
& (1 << 31)) != 0));
784 "window handle=0x%zx, app command=%s, device=%s, dwKeys=%s",
786 GetAppCommandName(GET_APPCOMMAND_LPARAM(aMSG
.lParam
)).get(),
787 GetAppCommandDeviceName(GET_DEVICE_LPARAM(aMSG
.lParam
)).get(),
788 GetAppCommandKeysName(GET_KEYSTATE_LPARAM(aMSG
.lParam
)).get());
791 result
.AppendPrintf("wParam=%zu, lParam=%" PRIdLPTR
, aMSG
.wParam
,
795 result
.AppendPrintf(", hwnd=0x%p", aMSG
.hwnd
);
799 static const nsCString
ToString(
800 const UniCharsAndModifiers
& aUniCharsAndModifiers
) {
801 if (aUniCharsAndModifiers
.IsEmpty()) {
805 result
.AssignLiteral("{ ");
806 result
.Append(GetCharacterCodeName(aUniCharsAndModifiers
.CharAt(0)));
807 for (size_t i
= 1; i
< aUniCharsAndModifiers
.Length(); ++i
) {
808 if (aUniCharsAndModifiers
.ModifiersAt(i
- 1) !=
809 aUniCharsAndModifiers
.ModifiersAt(i
)) {
810 result
.AppendLiteral(" [");
811 result
.Append(GetModifiersName(aUniCharsAndModifiers
.ModifiersAt(0)));
812 result
.AppendLiteral("]");
814 result
.AppendLiteral(", ");
815 result
.Append(GetCharacterCodeName(aUniCharsAndModifiers
.CharAt(i
)));
817 result
.AppendLiteral(" [");
818 uint32_t lastIndex
= aUniCharsAndModifiers
.Length() - 1;
819 result
.Append(GetModifiersName(aUniCharsAndModifiers
.ModifiersAt(lastIndex
)));
820 result
.AppendLiteral("] }");
824 const nsCString
ToString(const ModifierKeyState
& aModifierKeyState
) {
826 result
.AssignLiteral("{ ");
827 result
.Append(GetModifiersName(aModifierKeyState
.GetModifiers()).get());
828 result
.AppendLiteral(" }");
832 // Unique id counter associated with a keydown / keypress events. Used in
833 // identifing keypress events for removal from async event dispatch queue
834 // in metrofx after preventDefault is called on keydown events.
835 static uint32_t sUniqueKeyEventId
= 0;
837 /*****************************************************************************
838 * mozilla::widget::ModifierKeyState
839 *****************************************************************************/
841 ModifierKeyState::ModifierKeyState() { Update(); }
843 ModifierKeyState::ModifierKeyState(Modifiers aModifiers
)
844 : mModifiers(aModifiers
) {
845 MOZ_ASSERT(!(mModifiers
& MODIFIER_ALTGRAPH
) || (!IsControl() && !IsAlt()),
846 "Neither MODIFIER_CONTROL nor MODIFIER_ALT should be set "
847 "if MODIFIER_ALTGRAPH is set");
850 void ModifierKeyState::Update() {
852 if (IS_VK_DOWN(VK_SHIFT
)) {
853 mModifiers
|= MODIFIER_SHIFT
;
855 // If AltGr key (i.e., VK_RMENU on some keyboard layout) is pressed, only
856 // MODIFIER_ALTGRAPH should be set. Otherwise, i.e., if both Ctrl and Alt
857 // keys are pressed to emulate AltGr key, MODIFIER_CONTROL and MODIFIER_ALT
858 // keys should be set separately.
859 if (KeyboardLayout::GetInstance()->HasAltGr() && IS_VK_DOWN(VK_RMENU
)) {
860 mModifiers
|= MODIFIER_ALTGRAPH
;
862 if (IS_VK_DOWN(VK_CONTROL
)) {
863 mModifiers
|= MODIFIER_CONTROL
;
865 if (IS_VK_DOWN(VK_MENU
)) {
866 mModifiers
|= MODIFIER_ALT
;
869 if (IS_VK_DOWN(VK_LWIN
) || IS_VK_DOWN(VK_RWIN
)) {
870 mModifiers
|= MODIFIER_META
;
872 if (::GetKeyState(VK_CAPITAL
) & 1) {
873 mModifiers
|= MODIFIER_CAPSLOCK
;
875 if (::GetKeyState(VK_NUMLOCK
) & 1) {
876 mModifiers
|= MODIFIER_NUMLOCK
;
878 if (::GetKeyState(VK_SCROLL
) & 1) {
879 mModifiers
|= MODIFIER_SCROLLLOCK
;
883 void ModifierKeyState::Unset(Modifiers aRemovingModifiers
) {
884 mModifiers
&= ~aRemovingModifiers
;
887 void ModifierKeyState::Set(Modifiers aAddingModifiers
) {
888 mModifiers
|= aAddingModifiers
;
889 MOZ_ASSERT(!(mModifiers
& MODIFIER_ALTGRAPH
) || (!IsControl() && !IsAlt()),
890 "Neither MODIFIER_CONTROL nor MODIFIER_ALT should be set "
891 "if MODIFIER_ALTGRAPH is set");
894 void ModifierKeyState::InitInputEvent(WidgetInputEvent
& aInputEvent
) const {
895 aInputEvent
.mModifiers
= mModifiers
;
897 switch (aInputEvent
.mClass
) {
898 case eMouseEventClass
:
899 case eMouseScrollEventClass
:
900 case eWheelEventClass
:
901 case eDragEventClass
:
902 case eSimpleGestureEventClass
:
903 InitMouseEvent(aInputEvent
);
910 void ModifierKeyState::InitMouseEvent(WidgetInputEvent
& aMouseEvent
) const {
911 NS_ASSERTION(aMouseEvent
.mClass
== eMouseEventClass
||
912 aMouseEvent
.mClass
== eWheelEventClass
||
913 aMouseEvent
.mClass
== eDragEventClass
||
914 aMouseEvent
.mClass
== eSimpleGestureEventClass
,
915 "called with non-mouse event");
917 WidgetMouseEventBase
& mouseEvent
= *aMouseEvent
.AsMouseEventBase();
918 mouseEvent
.mButtons
= 0;
919 if (::GetKeyState(VK_LBUTTON
) < 0) {
920 mouseEvent
.mButtons
|= MouseButtonsFlag::ePrimaryFlag
;
922 if (::GetKeyState(VK_RBUTTON
) < 0) {
923 mouseEvent
.mButtons
|= MouseButtonsFlag::eSecondaryFlag
;
925 if (::GetKeyState(VK_MBUTTON
) < 0) {
926 mouseEvent
.mButtons
|= MouseButtonsFlag::eMiddleFlag
;
928 if (::GetKeyState(VK_XBUTTON1
) < 0) {
929 mouseEvent
.mButtons
|= MouseButtonsFlag::e4thFlag
;
931 if (::GetKeyState(VK_XBUTTON2
) < 0) {
932 mouseEvent
.mButtons
|= MouseButtonsFlag::e5thFlag
;
936 bool ModifierKeyState::IsShift() const {
937 return (mModifiers
& MODIFIER_SHIFT
) != 0;
940 bool ModifierKeyState::IsControl() const {
941 return (mModifiers
& MODIFIER_CONTROL
) != 0;
944 bool ModifierKeyState::IsAlt() const {
945 return (mModifiers
& MODIFIER_ALT
) != 0;
948 bool ModifierKeyState::IsWin() const {
949 return (mModifiers
& MODIFIER_META
) != 0;
952 bool ModifierKeyState::MaybeMatchShortcutKey() const {
953 // If Windows key is pressed, even if both Ctrl key and Alt key are pressed,
954 // it's possible to match a shortcut key.
958 // Otherwise, when both Ctrl key and Alt key are pressed, it shouldn't be
959 // a shortcut key for Windows since it means pressing AltGr key on
960 // some keyboard layouts.
961 if (IsControl() ^ IsAlt()) {
964 // If no modifier key is active except a lockable modifier nor Shift key,
965 // the key shouldn't match any shortcut keys (there are Space and
966 // Shift+Space, though, let's ignore these special case...).
970 bool ModifierKeyState::IsCapsLocked() const {
971 return (mModifiers
& MODIFIER_CAPSLOCK
) != 0;
974 bool ModifierKeyState::IsNumLocked() const {
975 return (mModifiers
& MODIFIER_NUMLOCK
) != 0;
978 bool ModifierKeyState::IsScrollLocked() const {
979 return (mModifiers
& MODIFIER_SCROLLLOCK
) != 0;
982 /*****************************************************************************
983 * mozilla::widget::UniCharsAndModifiers
984 *****************************************************************************/
986 void UniCharsAndModifiers::Append(char16_t aUniChar
, Modifiers aModifiers
) {
987 mChars
.Append(aUniChar
);
988 mModifiers
.AppendElement(aModifiers
);
991 void UniCharsAndModifiers::FillModifiers(Modifiers aModifiers
) {
992 for (size_t i
= 0; i
< Length(); i
++) {
993 mModifiers
[i
] = aModifiers
;
997 void UniCharsAndModifiers::OverwriteModifiersIfBeginsWith(
998 const UniCharsAndModifiers
& aOther
) {
999 if (!BeginsWith(aOther
)) {
1002 for (size_t i
= 0; i
< aOther
.Length(); ++i
) {
1003 mModifiers
[i
] = aOther
.mModifiers
[i
];
1007 bool UniCharsAndModifiers::UniCharsEqual(
1008 const UniCharsAndModifiers
& aOther
) const {
1009 return mChars
.Equals(aOther
.mChars
);
1012 bool UniCharsAndModifiers::UniCharsCaseInsensitiveEqual(
1013 const UniCharsAndModifiers
& aOther
) const {
1014 return mChars
.Equals(aOther
.mChars
, nsCaseInsensitiveStringComparator
);
1017 bool UniCharsAndModifiers::BeginsWith(
1018 const UniCharsAndModifiers
& aOther
) const {
1019 return StringBeginsWith(mChars
, aOther
.mChars
);
1022 UniCharsAndModifiers
& UniCharsAndModifiers::operator+=(
1023 const UniCharsAndModifiers
& aOther
) {
1024 mChars
.Append(aOther
.mChars
);
1025 mModifiers
.AppendElements(aOther
.mModifiers
);
1029 UniCharsAndModifiers
UniCharsAndModifiers::operator+(
1030 const UniCharsAndModifiers
& aOther
) const {
1031 UniCharsAndModifiers
result(*this);
1036 /*****************************************************************************
1037 * mozilla::widget::VirtualKey
1038 *****************************************************************************/
1041 VirtualKey::ShiftState
VirtualKey::ModifiersToShiftState(Modifiers aModifiers
) {
1042 ShiftState state
= 0;
1043 if (aModifiers
& MODIFIER_SHIFT
) {
1044 state
|= STATE_SHIFT
;
1046 if (aModifiers
& MODIFIER_ALTGRAPH
) {
1047 state
|= STATE_ALTGRAPH
;
1049 if (aModifiers
& MODIFIER_CONTROL
) {
1050 state
|= STATE_CONTROL
;
1052 if (aModifiers
& MODIFIER_ALT
) {
1056 if (aModifiers
& MODIFIER_CAPSLOCK
) {
1057 state
|= STATE_CAPSLOCK
;
1063 Modifiers
VirtualKey::ShiftStateToModifiers(ShiftState aShiftState
) {
1064 Modifiers modifiers
= 0;
1065 if (aShiftState
& STATE_SHIFT
) {
1066 modifiers
|= MODIFIER_SHIFT
;
1068 if (aShiftState
& STATE_ALTGRAPH
) {
1069 modifiers
|= MODIFIER_ALTGRAPH
;
1071 if (aShiftState
& STATE_CONTROL
) {
1072 modifiers
|= MODIFIER_CONTROL
;
1074 if (aShiftState
& STATE_ALT
) {
1075 modifiers
|= MODIFIER_ALT
;
1078 if (aShiftState
& STATE_CAPSLOCK
) {
1079 modifiers
|= MODIFIER_CAPSLOCK
;
1084 const DeadKeyTable
* VirtualKey::MatchingDeadKeyTable(
1085 const DeadKeyEntry
* aDeadKeyArray
, uint32_t aEntries
) const {
1090 for (ShiftState shiftState
= 0; shiftState
< 16; shiftState
++) {
1091 if (!IsDeadKey(shiftState
)) {
1094 const DeadKeyTable
* dkt
= mShiftStates
[shiftState
].DeadKey
.Table
;
1095 if (dkt
&& dkt
->IsEqual(aDeadKeyArray
, aEntries
)) {
1103 void VirtualKey::SetNormalChars(ShiftState aShiftState
, const char16_t
* aChars
,
1104 uint32_t aNumOfChars
) {
1105 MOZ_ASSERT(aShiftState
== ToShiftStateIndex(aShiftState
));
1107 SetDeadKey(aShiftState
, false);
1109 for (uint32_t index
= 0; index
< aNumOfChars
; index
++) {
1110 // Ignore legacy non-printable control characters
1111 mShiftStates
[aShiftState
].Normal
.Chars
[index
] =
1112 (aChars
[index
] >= 0x20) ? aChars
[index
] : 0;
1115 uint32_t len
= ArrayLength(mShiftStates
[aShiftState
].Normal
.Chars
);
1116 for (uint32_t index
= aNumOfChars
; index
< len
; index
++) {
1117 mShiftStates
[aShiftState
].Normal
.Chars
[index
] = 0;
1121 void VirtualKey::SetDeadChar(ShiftState aShiftState
, char16_t aDeadChar
) {
1122 MOZ_ASSERT(aShiftState
== ToShiftStateIndex(aShiftState
));
1124 SetDeadKey(aShiftState
, true);
1126 mShiftStates
[aShiftState
].DeadKey
.DeadChar
= aDeadChar
;
1127 mShiftStates
[aShiftState
].DeadKey
.Table
= nullptr;
1130 UniCharsAndModifiers
VirtualKey::GetUniChars(ShiftState aShiftState
) const {
1131 UniCharsAndModifiers result
= GetNativeUniChars(aShiftState
);
1133 const uint8_t kShiftStateIndex
= ToShiftStateIndex(aShiftState
);
1134 if (!(kShiftStateIndex
& STATE_CONTROL_ALT
)) {
1135 // If neither Alt nor Ctrl key is pressed, just return stored data
1140 if (result
.IsEmpty()) {
1141 // If Alt and/or Control are pressed and the key produces no
1142 // character, return characters which is produced by the key without
1143 // Alt and Control, and return given modifiers as is.
1144 result
= GetNativeUniChars(kShiftStateIndex
& ~STATE_CONTROL_ALT
);
1145 result
.FillModifiers(ShiftStateToModifiers(aShiftState
));
1149 if (IsAltGrIndex(kShiftStateIndex
)) {
1150 // If AltGr or both Ctrl and Alt are pressed and the key produces
1151 // character(s), we need to clear MODIFIER_ALT and MODIFIER_CONTROL
1152 // since TextEditor won't handle eKeyPress event whose mModifiers
1153 // has MODIFIER_ALT or MODIFIER_CONTROL. Additionally, we need to
1154 // use MODIFIER_ALTGRAPH when a key produces character(s) with
1155 // AltGr or both Ctrl and Alt on Windows. See following spec issue:
1156 // <https://github.com/w3c/uievents/issues/147>
1157 Modifiers finalModifiers
= ShiftStateToModifiers(aShiftState
);
1158 finalModifiers
&= ~(MODIFIER_ALT
| MODIFIER_CONTROL
);
1159 finalModifiers
|= MODIFIER_ALTGRAPH
;
1160 result
.FillModifiers(finalModifiers
);
1164 // Otherwise, i.e., Alt or Ctrl is pressed and it produces character(s),
1165 // check if different character(s) is produced by the key without Alt/Ctrl.
1166 // If it produces different character, we need to consume the Alt and
1167 // Ctrl modifier for TextEditor. Otherwise, the key does not produces the
1168 // character actually. So, keep setting Alt and Ctrl modifiers.
1169 UniCharsAndModifiers unmodifiedReslt
=
1170 GetNativeUniChars(kShiftStateIndex
& ~STATE_CONTROL_ALT
);
1171 if (!result
.UniCharsEqual(unmodifiedReslt
)) {
1172 Modifiers finalModifiers
= ShiftStateToModifiers(aShiftState
);
1173 finalModifiers
&= ~(MODIFIER_ALT
| MODIFIER_CONTROL
);
1174 result
.FillModifiers(finalModifiers
);
1179 UniCharsAndModifiers
VirtualKey::GetNativeUniChars(
1180 ShiftState aShiftState
) const {
1181 const uint8_t kShiftStateIndex
= ToShiftStateIndex(aShiftState
);
1182 UniCharsAndModifiers result
;
1183 Modifiers modifiers
= ShiftStateToModifiers(aShiftState
);
1184 if (IsDeadKey(aShiftState
)) {
1185 result
.Append(mShiftStates
[kShiftStateIndex
].DeadKey
.DeadChar
, modifiers
);
1189 uint32_t len
= ArrayLength(mShiftStates
[kShiftStateIndex
].Normal
.Chars
);
1190 for (uint32_t i
= 0;
1191 i
< len
&& mShiftStates
[kShiftStateIndex
].Normal
.Chars
[i
]; i
++) {
1192 result
.Append(mShiftStates
[kShiftStateIndex
].Normal
.Chars
[i
], modifiers
);
1198 void VirtualKey::FillKbdState(PBYTE aKbdState
, const ShiftState aShiftState
) {
1199 if (aShiftState
& STATE_SHIFT
) {
1200 aKbdState
[VK_SHIFT
] |= 0x80;
1202 aKbdState
[VK_SHIFT
] &= ~0x80;
1203 aKbdState
[VK_LSHIFT
] &= ~0x80;
1204 aKbdState
[VK_RSHIFT
] &= ~0x80;
1207 if (aShiftState
& STATE_ALTGRAPH
) {
1208 aKbdState
[VK_CONTROL
] |= 0x80;
1209 aKbdState
[VK_LCONTROL
] |= 0x80;
1210 aKbdState
[VK_RCONTROL
] &= ~0x80;
1211 aKbdState
[VK_MENU
] |= 0x80;
1212 aKbdState
[VK_LMENU
] &= ~0x80;
1213 aKbdState
[VK_RMENU
] |= 0x80;
1215 if (aShiftState
& STATE_CONTROL
) {
1216 aKbdState
[VK_CONTROL
] |= 0x80;
1218 aKbdState
[VK_CONTROL
] &= ~0x80;
1219 aKbdState
[VK_LCONTROL
] &= ~0x80;
1220 aKbdState
[VK_RCONTROL
] &= ~0x80;
1223 if (aShiftState
& STATE_ALT
) {
1224 aKbdState
[VK_MENU
] |= 0x80;
1226 aKbdState
[VK_MENU
] &= ~0x80;
1227 aKbdState
[VK_LMENU
] &= ~0x80;
1228 aKbdState
[VK_RMENU
] &= ~0x80;
1232 if (aShiftState
& STATE_CAPSLOCK
) {
1233 aKbdState
[VK_CAPITAL
] |= 0x01;
1235 aKbdState
[VK_CAPITAL
] &= ~0x01;
1239 /*****************************************************************************
1240 * mozilla::widget::NativeKey
1241 *****************************************************************************/
1243 uint8_t NativeKey::sDispatchedKeyOfAppCommand
= 0;
1244 NativeKey
* NativeKey::sLatestInstance
= nullptr;
1245 const MSG
NativeKey::sEmptyMSG
= {};
1246 MSG
NativeKey::sLastKeyOrCharMSG
= {};
1247 MSG
NativeKey::sLastKeyMSG
= {};
1248 char16_t
NativeKey::sPendingHighSurrogate
= 0;
1250 NativeKey::NativeKey(nsWindow
* aWidget
, const MSG
& aMessage
,
1251 const ModifierKeyState
& aModKeyState
,
1252 HKL aOverrideKeyboardLayout
,
1253 nsTArray
<FakeCharMsg
>* aFakeCharMsgs
)
1254 : mLastInstance(sLatestInstance
),
1255 mRemovingMsg(sEmptyMSG
),
1256 mReceivedMsg(sEmptyMSG
),
1258 mDispatcher(aWidget
->GetTextEventDispatcher()),
1260 mFocusedWndBeforeDispatch(::GetFocus()),
1262 mKeyNameIndex(KEY_NAME_INDEX_Unidentified
),
1263 mCodeNameIndex(CODE_NAME_INDEX_UNKNOWN
),
1264 mModKeyState(aModKeyState
),
1266 mOriginalVirtualKeyCode(0),
1267 mShiftedLatinChar(0),
1268 mUnshiftedLatinChar(0),
1273 mIsPrintableKey(false),
1274 mIsSkippableInRemoteProcess(false),
1275 mCharMessageHasGone(false),
1276 mCanIgnoreModifierStateAtKeyPress(true),
1277 mFakeCharMsgs(aFakeCharMsgs
&& aFakeCharMsgs
->Length() ? aFakeCharMsgs
1279 MOZ_LOG(gKeyLog
, LogLevel::Info
,
1280 ("%p NativeKey::NativeKey(aWidget=0x%p { GetWindowHandle()=0x%p }, "
1281 "aMessage=%s, aModKeyState=%s), sLatestInstance=0x%p",
1282 this, aWidget
, aWidget
->GetWindowHandle(), ToString(aMessage
).get(),
1283 ToString(aModKeyState
).get(), sLatestInstance
));
1285 MOZ_ASSERT(aWidget
);
1286 MOZ_ASSERT(mDispatcher
);
1287 sLatestInstance
= this;
1288 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
1289 mKeyboardLayout
= keyboardLayout
->GetLayout();
1290 if (aOverrideKeyboardLayout
&& mKeyboardLayout
!= aOverrideKeyboardLayout
) {
1291 keyboardLayout
->OverrideLayout(aOverrideKeyboardLayout
);
1292 mKeyboardLayout
= keyboardLayout
->GetLayout();
1293 MOZ_ASSERT(mKeyboardLayout
== aOverrideKeyboardLayout
);
1294 mIsOverridingKeyboardLayout
= true;
1296 mIsOverridingKeyboardLayout
= false;
1297 sLastKeyOrCharMSG
= aMessage
;
1300 if (mMsg
.message
== WM_APPCOMMAND
) {
1301 InitWithAppCommand();
1303 InitWithKeyOrChar();
1306 MOZ_LOG(gKeyLog
, LogLevel::Info
,
1307 ("%p NativeKey::NativeKey(), mKeyboardLayout=0x%p, "
1308 "mFocusedWndBeforeDispatch=0x%p, mDOMKeyCode=%s, "
1309 "mKeyNameIndex=%s, mCodeNameIndex=%s, mModKeyState=%s, "
1310 "mVirtualKeyCode=%s, mOriginalVirtualKeyCode=%s, "
1311 "mCommittedCharsAndModifiers=%s, mInputtingStringAndModifiers=%s, "
1312 "mShiftedString=%s, mUnshiftedString=%s, mShiftedLatinChar=%s, "
1313 "mUnshiftedLatinChar=%s, mScanCode=0x%04X, mIsExtended=%s, "
1314 "mIsRepeat=%s, mIsDeadKey=%s, mIsPrintableKey=%s, "
1315 "mIsSkippableInRemoteProcess=%s, mCharMessageHasGone=%s, "
1316 "mIsOverridingKeyboardLayout=%s",
1317 this, mKeyboardLayout
, mFocusedWndBeforeDispatch
,
1318 GetDOMKeyCodeName(mDOMKeyCode
).get(), ToString(mKeyNameIndex
).get(),
1319 ToString(mCodeNameIndex
).get(), ToString(mModKeyState
).get(),
1320 GetVirtualKeyCodeName(mVirtualKeyCode
).get(),
1321 GetVirtualKeyCodeName(mOriginalVirtualKeyCode
).get(),
1322 ToString(mCommittedCharsAndModifiers
).get(),
1323 ToString(mInputtingStringAndModifiers
).get(),
1324 ToString(mShiftedString
).get(), ToString(mUnshiftedString
).get(),
1325 GetCharacterCodeName(mShiftedLatinChar
).get(),
1326 GetCharacterCodeName(mUnshiftedLatinChar
).get(), mScanCode
,
1327 GetBoolName(mIsExtended
), GetBoolName(mIsRepeat
),
1328 GetBoolName(mIsDeadKey
), GetBoolName(mIsPrintableKey
),
1329 GetBoolName(mIsSkippableInRemoteProcess
),
1330 GetBoolName(mCharMessageHasGone
),
1331 GetBoolName(mIsOverridingKeyboardLayout
)));
1334 void NativeKey::InitIsSkippableForKeyOrChar(const MSG
& aLastKeyMSG
) {
1335 mIsSkippableInRemoteProcess
= false;
1338 // If the message is not repeated key message, the event should be always
1339 // handled in remote process even if it's too old.
1343 // Keyboard utilities may send us some generated messages and such messages
1344 // may be marked as "repeated", e.g., SendInput() calls with
1345 // KEYEVENTF_UNICODE but without KEYEVENTF_KEYUP. However, key sequence
1346 // comes from such utilities may be really important. For example, utilities
1347 // may send WM_KEYDOWN for VK_BACK to remove previous character and send
1348 // WM_KEYDOWN for VK_PACKET to insert a composite character. Therefore, we
1349 // should check if current message and previous key message are caused by
1350 // same physical key. If not, the message may be generated by such
1352 // XXX With this approach, if VK_BACK messages are generated with known
1353 // scancode, we cannot distinguish whether coming VK_BACK message is
1354 // actually repeated by the auto-repeat feature. Currently, we need
1355 // this hack only for "SinhalaTamil IME" and fortunately, it generates
1356 // VK_BACK messages with odd scancode. So, we don't need to handle
1357 // VK_BACK specially at least for now.
1359 if (mCodeNameIndex
== CODE_NAME_INDEX_UNKNOWN
) {
1360 // If current event is not caused by physical key operation, it may be
1361 // caused by a keyboard utility. If so, the event shouldn't be ignored by
1362 // BrowserChild since it want to insert the character, delete a character or
1367 if (mOriginalVirtualKeyCode
== VK_PACKET
) {
1368 // If the message is VK_PACKET, that means that a keyboard utility
1369 // tries to insert a character.
1373 switch (mMsg
.message
) {
1379 case WM_SYSDEADCHAR
:
1380 // However, some keyboard layouts may send some keyboard messages with
1381 // activating the bit. If we dispatch repeated keyboard events, they
1382 // may be ignored by BrowserChild due to performance reason. So, we need
1383 // to check if actually a physical key is repeated by the auto-repeat
1385 switch (aLastKeyMSG
.message
) {
1388 if (aLastKeyMSG
.wParam
== VK_PACKET
) {
1389 // If the last message was VK_PACKET, that means that a keyboard
1390 // utility tried to insert a character. So, current message is
1391 // not repeated key event of the previous event.
1394 // Let's check whether current message and previous message are
1395 // caused by same physical key.
1396 mIsSkippableInRemoteProcess
=
1397 mScanCode
== WinUtils::GetScanCode(aLastKeyMSG
.lParam
) &&
1398 mIsExtended
== WinUtils::IsExtendedScanCode(aLastKeyMSG
.lParam
);
1401 // If previous message is not a keydown, this must not be generated
1402 // by the auto-repeat feature.
1406 MOZ_ASSERT_UNREACHABLE(
1407 "WM_APPCOMMAND should be handled in "
1408 "InitWithAppCommand()");
1411 // keyup message shouldn't be repeated by the auto-repeat feature.
1416 void NativeKey::InitWithKeyOrChar() {
1417 MSG lastKeyMSG
= sLastKeyMSG
;
1418 char16_t pendingHighSurrogate
= sPendingHighSurrogate
;
1419 mScanCode
= WinUtils::GetScanCode(mMsg
.lParam
);
1420 mIsExtended
= WinUtils::IsExtendedScanCode(mMsg
.lParam
);
1421 switch (mMsg
.message
) {
1424 sPendingHighSurrogate
= 0;
1428 // Modify sLastKeyMSG now since retrieving following char messages may
1429 // cause sending another key message if odd tool hooks GetMessage(),
1433 // Note that we don't need to compute raw virtual keycode here even when
1434 // it's VK_PROCESS (i.e., already handled by IME) because we need to
1435 // export it as DOM_VK_PROCESS and KEY_NAME_INDEX_Process.
1436 mOriginalVirtualKeyCode
= static_cast<uint8_t>(mMsg
.wParam
);
1438 // If the key message is sent from other application like a11y tools, the
1439 // scancode value might not be set proper value. Then, probably the value
1441 // NOTE: If the virtual keycode can be caused by both non-extended key
1442 // and extended key, the API returns the non-extended key's
1443 // scancode. E.g., VK_LEFT causes "4" key on numpad.
1444 if (!mScanCode
&& mOriginalVirtualKeyCode
!= VK_PACKET
) {
1445 uint16_t scanCodeEx
= ComputeScanCodeExFromVirtualKeyCode(mMsg
.wParam
);
1447 mScanCode
= static_cast<uint8_t>(scanCodeEx
& 0xFF);
1448 uint8_t extended
= static_cast<uint8_t>((scanCodeEx
& 0xFF00) >> 8);
1449 mIsExtended
= (extended
== 0xE0) || (extended
== 0xE1);
1453 // Most keys are not distinguished as left or right keys.
1454 bool isLeftRightDistinguishedKey
= false;
1456 // mOriginalVirtualKeyCode must not distinguish left or right of
1457 // Shift, Control or Alt.
1458 switch (mOriginalVirtualKeyCode
) {
1462 isLeftRightDistinguishedKey
= true;
1466 mVirtualKeyCode
= mOriginalVirtualKeyCode
;
1467 mOriginalVirtualKeyCode
= VK_SHIFT
;
1468 isLeftRightDistinguishedKey
= true;
1472 mVirtualKeyCode
= mOriginalVirtualKeyCode
;
1473 mOriginalVirtualKeyCode
= VK_CONTROL
;
1474 isLeftRightDistinguishedKey
= true;
1478 mVirtualKeyCode
= mOriginalVirtualKeyCode
;
1479 mOriginalVirtualKeyCode
= VK_MENU
;
1480 isLeftRightDistinguishedKey
= true;
1484 // If virtual keycode (left-right distinguished keycode) is already
1485 // computed, we don't need to do anymore.
1486 if (mVirtualKeyCode
) {
1490 // If the keycode doesn't have LR distinguished keycode, we just set
1491 // mOriginalVirtualKeyCode to mVirtualKeyCode. Note that don't compute
1492 // it from MapVirtualKeyEx() because the scan code might be wrong if
1493 // the message is sent/posted by other application. Then, we will compute
1494 // unexpected keycode from the scan code.
1495 if (!isLeftRightDistinguishedKey
) {
1499 NS_ASSERTION(!mVirtualKeyCode
,
1500 "mVirtualKeyCode has been computed already");
1502 // Otherwise, compute the virtual keycode with MapVirtualKeyEx().
1503 mVirtualKeyCode
= ComputeVirtualKeyCodeFromScanCodeEx();
1505 // Following code shouldn't be used now because we compute scancode value
1506 // if we detect that the sender doesn't set proper scancode.
1507 // However, the detection might fail. Therefore, let's keep using this.
1508 switch (mOriginalVirtualKeyCode
) {
1510 if (mVirtualKeyCode
!= VK_LCONTROL
&&
1511 mVirtualKeyCode
!= VK_RCONTROL
) {
1512 mVirtualKeyCode
= mIsExtended
? VK_RCONTROL
: VK_LCONTROL
;
1516 if (mVirtualKeyCode
!= VK_LMENU
&& mVirtualKeyCode
!= VK_RMENU
) {
1517 mVirtualKeyCode
= mIsExtended
? VK_RMENU
: VK_LMENU
;
1521 if (mVirtualKeyCode
!= VK_LSHIFT
&& mVirtualKeyCode
!= VK_RSHIFT
) {
1522 // Neither left shift nor right shift is an extended key,
1523 // let's use VK_LSHIFT for unknown mapping.
1524 mVirtualKeyCode
= VK_LSHIFT
;
1528 MOZ_CRASH("Unsupported mOriginalVirtualKeyCode");
1535 sPendingHighSurrogate
= 0;
1536 // If there is another instance and it is trying to remove a char message
1537 // from the queue, this message should be handled in the old instance.
1538 if (IsAnotherInstanceRemovingCharMessage()) {
1539 // XXX Do we need to make mReceivedMsg an array?
1540 MOZ_ASSERT(IsEmptyMSG(mLastInstance
->mReceivedMsg
));
1542 gKeyLog
, LogLevel::Warning
,
1543 ("%p NativeKey::InitWithKeyOrChar(), WARNING, detecting another "
1544 "instance is trying to remove a char message, so, this instance "
1545 "should do nothing, mLastInstance=0x%p, mRemovingMsg=%s, "
1547 this, mLastInstance
, ToString(mLastInstance
->mRemovingMsg
).get(),
1548 ToString(mLastInstance
->mReceivedMsg
).get()));
1549 mLastInstance
->mReceivedMsg
= mMsg
;
1553 // NOTE: If other applications like a11y tools sends WM_*CHAR without
1554 // scancode, we cannot compute virtual keycode. I.e., with such
1555 // applications, we cannot generate proper KeyboardEvent.code value.
1557 mVirtualKeyCode
= mOriginalVirtualKeyCode
=
1558 ComputeVirtualKeyCodeFromScanCodeEx();
1559 NS_ASSERTION(mVirtualKeyCode
, "Failed to compute virtual keycode");
1562 MOZ_CRASH_UNSAFE_PRINTF("Unsupported message: 0x%04X", mMsg
.message
);
1567 if (!mVirtualKeyCode
) {
1568 mVirtualKeyCode
= mOriginalVirtualKeyCode
;
1571 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
1573 keyboardLayout
->ConvertNativeKeyCodeToDOMKeyCode(mVirtualKeyCode
);
1574 // Be aware, keyboard utilities can change non-printable keys to printable
1575 // keys. In such case, we should make the key value as a printable key.
1576 // FYI: IsFollowedByPrintableCharMessage() returns true only when it's
1577 // handling a keydown message.
1579 IsFollowedByPrintableCharMessage()
1580 ? KEY_NAME_INDEX_USE_STRING
1581 : keyboardLayout
->ConvertNativeKeyCodeToKeyNameIndex(mVirtualKeyCode
);
1582 mCodeNameIndex
= KeyboardLayout::ConvertScanCodeToCodeNameIndex(
1583 GetScanCodeWithExtendedFlag());
1585 // If next message of WM_(SYS)KEYDOWN is WM_*CHAR message and the key
1586 // combination is not reserved by the system, let's consume it now.
1587 // TODO: We cannot initialize mCommittedCharsAndModifiers for VK_PACKET
1588 // if the message is WM_KEYUP because we don't have preceding
1590 // TODO: Like Edge, we shouldn't dispatch two sets of keyboard events
1591 // for a Unicode character in non-BMP because its key value looks
1592 // broken and not good thing for our editor if only one keydown or
1593 // keypress event's default is prevented. I guess, we should store
1594 // key message information globally and we should wait following
1595 // WM_KEYDOWN if following WM_CHAR is a part of a Unicode character.
1596 if ((mMsg
.message
== WM_KEYDOWN
|| mMsg
.message
== WM_SYSKEYDOWN
) &&
1597 !IsReservedBySystem()) {
1599 while (GetFollowingCharMessage(charMsg
)) {
1600 // Although, got message shouldn't be WM_NULL in desktop apps,
1601 // we should keep checking this. FYI: This was added for Metrofox.
1602 if (charMsg
.message
== WM_NULL
) {
1605 MOZ_LOG(gKeyLog
, LogLevel::Info
,
1606 ("%p NativeKey::InitWithKeyOrChar(), removed char message, %s",
1607 this, ToString(charMsg
).get()));
1608 Unused
<< NS_WARN_IF(charMsg
.hwnd
!= mMsg
.hwnd
);
1609 mFollowingCharMsgs
.AppendElement(charMsg
);
1611 if (mFollowingCharMsgs
.Length() == 1) {
1612 // If we receive a keydown message for a high-surrogate, a low-surrogate
1613 // keydown message **will** and should follow it. We cannot translate the
1614 // following WM_KEYDOWN message for the low-surrogate right now since
1615 // it's not yet queued into the message queue yet. Therefore, we need to
1616 // wait next one to dispatch keypress event with setting its `.key` value
1617 // to a surrogate pair rather than setting it to a lone surrogate.
1618 // FYI: This may happen with typing a non-BMP character on the touch
1619 // keyboard on Windows 10 or later except when an IME is installed. (If
1620 // IME is installed, composition is used instead.)
1621 if (IS_HIGH_SURROGATE(mFollowingCharMsgs
[0].wParam
)) {
1622 if (pendingHighSurrogate
) {
1623 MOZ_LOG(gKeyLog
, LogLevel::Warning
,
1624 ("%p NativeKey::InitWithKeyOrChar(), there is pending "
1625 "high surrogate input, but received another high surrogate "
1626 "input. The previous one is discarded",
1629 sPendingHighSurrogate
= mFollowingCharMsgs
[0].wParam
;
1630 mFollowingCharMsgs
.Clear();
1631 } else if (IS_LOW_SURROGATE(mFollowingCharMsgs
[0].wParam
)) {
1632 // If we stopped dispathing a keypress event for a preceding
1633 // high-surrogate, treat this keydown (for a low-surrogate) as
1634 // introducing both the high surrogate and the low surrogate.
1635 if (pendingHighSurrogate
) {
1636 MSG charMsg
= mFollowingCharMsgs
[0];
1637 mFollowingCharMsgs
[0].wParam
= pendingHighSurrogate
;
1638 mFollowingCharMsgs
.AppendElement(std::move(charMsg
));
1641 gKeyLog
, LogLevel::Warning
,
1642 ("%p NativeKey::InitWithKeyOrChar(), there is no pending high "
1643 "surrogate input, but received lone low surrogate input",
1647 MOZ_LOG(gKeyLog
, LogLevel::Warning
,
1648 ("%p NativeKey::InitWithKeyOrChar(), there is pending "
1649 "high surrogate input, but received non-surrogate input. "
1650 "The high surrogate input is discarded",
1654 MOZ_LOG(gKeyLog
, LogLevel::Warning
,
1655 ("%p NativeKey::InitWithKeyOrChar(), there is pending "
1656 "high surrogate input, but received 2 or more character input. "
1657 "The high surrogate input is discarded",
1662 keyboardLayout
->InitNativeKey(*this);
1664 // Now, we can know if the key produces character(s) or a dead key with
1665 // AltGraph modifier. When user emulates AltGr key press with pressing
1666 // both Ctrl and Alt and the key produces character(s) or a dead key, we
1667 // need to replace Control and Alt state with AltGraph if the keyboard
1668 // layout has AltGr key.
1669 // Note that if Ctrl and/or Alt are pressed (not to emulate to press AltGr),
1670 // we need to set actual modifiers to eKeyDown and eKeyUp.
1671 if (MaybeEmulatingAltGraph() &&
1672 (mCommittedCharsAndModifiers
.IsProducingCharsWithAltGr() ||
1673 mKeyNameIndex
== KEY_NAME_INDEX_Dead
)) {
1674 mModKeyState
.Unset(MODIFIER_CONTROL
| MODIFIER_ALT
);
1675 mModKeyState
.Set(MODIFIER_ALTGRAPH
);
1679 (IsFollowedByDeadCharMessage() ||
1680 keyboardLayout
->IsDeadKey(mOriginalVirtualKeyCode
, mModKeyState
));
1681 mIsPrintableKey
= mKeyNameIndex
== KEY_NAME_INDEX_USE_STRING
||
1682 KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode
);
1683 // The repeat count in mMsg.lParam isn't useful to check whether the event
1684 // is caused by the auto-repeat feature because it's not incremented even
1685 // if it's repeated twice or more (i.e., always 1). Therefore, we need to
1686 // check previous key state (31th bit) instead. If it's 1, the key was down
1687 // before the message was sent.
1688 mIsRepeat
= (mMsg
.lParam
& (1 << 30)) != 0;
1689 InitIsSkippableForKeyOrChar(lastKeyMSG
);
1691 if (IsKeyDownMessage()) {
1692 // Compute some strings which may be inputted by the key with various
1693 // modifier state if this key event won't cause text input actually.
1694 // They will be used for setting mAlternativeCharCodes in the callback
1695 // method which will be called by TextEventDispatcher.
1696 if (!IsFollowedByPrintableCharMessage()) {
1697 ComputeInputtingStringWithKeyboardLayout();
1699 // Remove odd char messages if there are.
1700 RemoveFollowingOddCharMessages();
1704 void NativeKey::InitCommittedCharsAndModifiersWithFollowingCharMessages() {
1705 mCommittedCharsAndModifiers
.Clear();
1706 // This should cause inputting text in focused editor. However, it
1707 // ignores keypress events whose altKey or ctrlKey is true.
1708 // Therefore, we need to remove these modifier state here.
1709 Modifiers modifiers
= mModKeyState
.GetModifiers();
1710 if (IsFollowedByPrintableCharMessage()) {
1711 modifiers
&= ~(MODIFIER_ALT
| MODIFIER_CONTROL
);
1712 if (MaybeEmulatingAltGraph()) {
1713 modifiers
|= MODIFIER_ALTGRAPH
;
1716 // NOTE: This method assumes that WM_CHAR and WM_SYSCHAR are never retrieved
1718 for (size_t i
= 0; i
< mFollowingCharMsgs
.Length(); ++i
) {
1719 // Ignore non-printable char messages.
1720 if (!IsPrintableCharOrSysCharMessage(mFollowingCharMsgs
[i
])) {
1723 char16_t ch
= static_cast<char16_t
>(mFollowingCharMsgs
[i
].wParam
);
1724 mCommittedCharsAndModifiers
.Append(ch
, modifiers
);
1728 NativeKey::~NativeKey() {
1729 MOZ_LOG(gKeyLog
, LogLevel::Debug
,
1730 ("%p NativeKey::~NativeKey(), destroyed", this));
1731 if (mIsOverridingKeyboardLayout
) {
1732 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
1733 keyboardLayout
->RestoreLayout();
1735 sLatestInstance
= mLastInstance
;
1738 void NativeKey::InitWithAppCommand() {
1739 if (GET_DEVICE_LPARAM(mMsg
.lParam
) != FAPPCOMMAND_KEY
) {
1743 uint32_t appCommand
= GET_APPCOMMAND_LPARAM(mMsg
.lParam
);
1744 switch (GET_APPCOMMAND_LPARAM(mMsg
.lParam
)) {
1745 #undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX
1746 #define NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX(aAppCommand, aKeyNameIndex) \
1748 mKeyNameIndex = aKeyNameIndex; \
1751 #include "NativeKeyToDOMKeyName.h"
1753 #undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX
1756 mKeyNameIndex
= KEY_NAME_INDEX_Unidentified
;
1759 // Guess the virtual keycode which caused this message.
1760 switch (appCommand
) {
1761 case APPCOMMAND_BROWSER_BACKWARD
:
1762 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_BACK
;
1764 case APPCOMMAND_BROWSER_FORWARD
:
1765 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_FORWARD
;
1767 case APPCOMMAND_BROWSER_REFRESH
:
1768 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_REFRESH
;
1770 case APPCOMMAND_BROWSER_STOP
:
1771 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_STOP
;
1773 case APPCOMMAND_BROWSER_SEARCH
:
1774 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_SEARCH
;
1776 case APPCOMMAND_BROWSER_FAVORITES
:
1777 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_FAVORITES
;
1779 case APPCOMMAND_BROWSER_HOME
:
1780 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_HOME
;
1782 case APPCOMMAND_VOLUME_MUTE
:
1783 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_VOLUME_MUTE
;
1785 case APPCOMMAND_VOLUME_DOWN
:
1786 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_VOLUME_DOWN
;
1788 case APPCOMMAND_VOLUME_UP
:
1789 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_VOLUME_UP
;
1791 case APPCOMMAND_MEDIA_NEXTTRACK
:
1792 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_MEDIA_NEXT_TRACK
;
1794 case APPCOMMAND_MEDIA_PREVIOUSTRACK
:
1795 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_MEDIA_PREV_TRACK
;
1797 case APPCOMMAND_MEDIA_STOP
:
1798 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_MEDIA_STOP
;
1800 case APPCOMMAND_MEDIA_PLAY_PAUSE
:
1801 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_MEDIA_PLAY_PAUSE
;
1803 case APPCOMMAND_LAUNCH_MAIL
:
1804 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_LAUNCH_MAIL
;
1806 case APPCOMMAND_LAUNCH_MEDIA_SELECT
:
1807 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_LAUNCH_MEDIA_SELECT
;
1809 case APPCOMMAND_LAUNCH_APP1
:
1810 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_LAUNCH_APP1
;
1812 case APPCOMMAND_LAUNCH_APP2
:
1813 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_LAUNCH_APP2
;
1819 uint16_t scanCodeEx
= ComputeScanCodeExFromVirtualKeyCode(mVirtualKeyCode
);
1820 mScanCode
= static_cast<uint8_t>(scanCodeEx
& 0xFF);
1821 uint8_t extended
= static_cast<uint8_t>((scanCodeEx
& 0xFF00) >> 8);
1822 mIsExtended
= (extended
== 0xE0) || (extended
== 0xE1);
1823 mDOMKeyCode
= KeyboardLayout::GetInstance()->ConvertNativeKeyCodeToDOMKeyCode(
1824 mOriginalVirtualKeyCode
);
1825 mCodeNameIndex
= KeyboardLayout::ConvertScanCodeToCodeNameIndex(
1826 GetScanCodeWithExtendedFlag());
1827 // If we can map the WM_APPCOMMAND to a virtual keycode, we can trust
1828 // the result of GetKeyboardState(). Otherwise, we dispatch both
1829 // keydown and keyup events from WM_APPCOMMAND handler. Therefore,
1830 // even if WM_APPCOMMAND is caused by auto key repeat, web apps receive
1831 // a pair of DOM keydown and keyup events. I.e., KeyboardEvent.repeat
1832 // should be never true of such keys.
1833 // XXX Isn't the key state always true? If the key press caused this
1834 // WM_APPCOMMAND, that means it's pressed at that time.
1835 if (mVirtualKeyCode
) {
1837 memset(kbdState
, 0, sizeof(kbdState
));
1838 ::GetKeyboardState(kbdState
);
1839 mIsSkippableInRemoteProcess
= mIsRepeat
= !!kbdState
[mVirtualKeyCode
];
1843 bool NativeKey::MaybeEmulatingAltGraph() const {
1844 return IsControl() && IsAlt() && KeyboardLayout::GetInstance()->HasAltGr();
1848 bool NativeKey::IsControlChar(char16_t aChar
) {
1849 static const char16_t U_SPACE
= 0x20;
1850 static const char16_t U_DELETE
= 0x7F;
1851 return aChar
< U_SPACE
|| aChar
== U_DELETE
;
1854 bool NativeKey::IsFollowedByDeadCharMessage() const {
1855 if (mFollowingCharMsgs
.IsEmpty()) {
1858 return IsDeadCharMessage(mFollowingCharMsgs
[0]);
1861 bool NativeKey::IsFollowedByPrintableCharMessage() const {
1862 for (size_t i
= 0; i
< mFollowingCharMsgs
.Length(); ++i
) {
1863 if (IsPrintableCharMessage(mFollowingCharMsgs
[i
])) {
1870 bool NativeKey::IsFollowedByPrintableCharOrSysCharMessage() const {
1871 for (size_t i
= 0; i
< mFollowingCharMsgs
.Length(); ++i
) {
1872 if (IsPrintableCharOrSysCharMessage(mFollowingCharMsgs
[i
])) {
1879 bool NativeKey::IsReservedBySystem() const {
1880 // Alt+Space key is handled by OS, we shouldn't touch it.
1881 if (mModKeyState
.IsAlt() && !mModKeyState
.IsControl() &&
1882 mVirtualKeyCode
== VK_SPACE
) {
1886 // XXX How about Alt+F4? We receive WM_SYSKEYDOWN for F4 before closing the
1887 // window. Although, we don't prevent to close the window but the key
1888 // event shouldn't be exposed to the web.
1893 bool NativeKey::IsIMEDoingKakuteiUndo() const {
1894 // Following message pattern is caused by "Kakutei-Undo" of ATOK or WXG:
1895 // ---------------------------------------------------------------------------
1896 // WM_KEYDOWN * n (wParam = VK_BACK, lParam = 0x1)
1897 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC0000001) # ATOK
1898 // WM_IME_STARTCOMPOSITION * 1 (wParam = 0x0, lParam = 0x0)
1899 // WM_IME_COMPOSITION * 1 (wParam = 0x0, lParam = 0x1BF)
1900 // WM_CHAR * n (wParam = VK_BACK, lParam = 0x1)
1901 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC00E0001)
1902 // ---------------------------------------------------------------------------
1903 // This doesn't match usual key message pattern such as:
1904 // WM_KEYDOWN -> WM_CHAR -> WM_KEYDOWN -> WM_CHAR -> ... -> WM_KEYUP
1905 // See following bugs for the detail.
1906 // https://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese)
1907 // https://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English)
1908 MSG startCompositionMsg
, compositionMsg
, charMsg
;
1909 return WinUtils::PeekMessage(&startCompositionMsg
, mMsg
.hwnd
,
1910 WM_IME_STARTCOMPOSITION
, WM_IME_STARTCOMPOSITION
,
1911 PM_NOREMOVE
| PM_NOYIELD
) &&
1912 WinUtils::PeekMessage(&compositionMsg
, mMsg
.hwnd
, WM_IME_COMPOSITION
,
1913 WM_IME_COMPOSITION
, PM_NOREMOVE
| PM_NOYIELD
) &&
1914 WinUtils::PeekMessage(&charMsg
, mMsg
.hwnd
, WM_CHAR
, WM_CHAR
,
1915 PM_NOREMOVE
| PM_NOYIELD
) &&
1916 startCompositionMsg
.wParam
== 0x0 &&
1917 startCompositionMsg
.lParam
== 0x0 && compositionMsg
.wParam
== 0x0 &&
1918 compositionMsg
.lParam
== 0x1BF && charMsg
.wParam
== VK_BACK
&&
1919 charMsg
.lParam
== 0x1 &&
1920 startCompositionMsg
.time
<= compositionMsg
.time
&&
1921 compositionMsg
.time
<= charMsg
.time
;
1924 void NativeKey::RemoveFollowingOddCharMessages() {
1925 MOZ_ASSERT(IsKeyDownMessage());
1927 // If the keydown message is synthesized for automated tests, there is
1928 // nothing to do here.
1929 if (mFakeCharMsgs
) {
1933 // If there are some following char messages before another key message,
1934 // there is nothing to do here.
1935 if (!mFollowingCharMsgs
.IsEmpty()) {
1939 // If the handling key isn't Backspace, there is nothing to do here.
1940 if (mOriginalVirtualKeyCode
!= VK_BACK
) {
1944 // If we don't see the odd message pattern, there is nothing to do here.
1945 if (!IsIMEDoingKakuteiUndo()) {
1949 // Otherwise, we need to remove odd WM_CHAR messages for ATOK or WXG (both
1950 // of them are Japanese IME).
1952 while (WinUtils::PeekMessage(&msg
, mMsg
.hwnd
, WM_CHAR
, WM_CHAR
,
1953 PM_REMOVE
| PM_NOYIELD
)) {
1954 if (msg
.message
!= WM_CHAR
) {
1955 MOZ_RELEASE_ASSERT(msg
.message
== WM_NULL
,
1956 "Unexpected message was removed");
1960 gKeyLog
, LogLevel::Info
,
1961 ("%p NativeKey::RemoveFollowingOddCharMessages(), removed odd char "
1963 this, ToString(msg
).get()));
1964 mRemovedOddCharMsgs
.AppendElement(msg
);
1968 UINT
NativeKey::GetScanCodeWithExtendedFlag() const {
1972 return (0xE000 | mScanCode
);
1975 uint32_t NativeKey::GetKeyLocation() const {
1976 switch (mVirtualKeyCode
) {
1981 return eKeyLocationLeft
;
1987 return eKeyLocationRight
;
1990 // XXX This code assumes that all keyboard drivers use same mapping.
1991 return !mIsExtended
? eKeyLocationStandard
: eKeyLocationNumpad
;
2004 // XXX This code assumes that all keyboard drivers use same mapping.
2005 return mIsExtended
? eKeyLocationStandard
: eKeyLocationNumpad
;
2007 // NumLock key isn't included due to IE9's behavior.
2023 // Separator key of Brazilian keyboard or JIS keyboard for Mac
2025 return eKeyLocationNumpad
;
2030 NS_WARNING("Failed to decide the key location?");
2034 return eKeyLocationStandard
;
2038 uint8_t NativeKey::ComputeVirtualKeyCodeFromScanCode() const {
2039 return static_cast<uint8_t>(
2040 ::MapVirtualKeyEx(mScanCode
, MAPVK_VSC_TO_VK
, mKeyboardLayout
));
2043 uint8_t NativeKey::ComputeVirtualKeyCodeFromScanCodeEx() const {
2044 // MapVirtualKeyEx() has been improved for supporting extended keys since
2045 // Vista. When we call it for mapping a scancode of an extended key and
2046 // a virtual keycode, we need to add 0xE000 to the scancode.
2047 return static_cast<uint8_t>(::MapVirtualKeyEx(
2048 GetScanCodeWithExtendedFlag(), MAPVK_VSC_TO_VK_EX
, mKeyboardLayout
));
2051 uint16_t NativeKey::ComputeScanCodeExFromVirtualKeyCode(
2052 UINT aVirtualKeyCode
) const {
2053 return static_cast<uint16_t>(
2054 ::MapVirtualKeyEx(aVirtualKeyCode
, MAPVK_VK_TO_VSC_EX
, mKeyboardLayout
));
2057 char16_t
NativeKey::ComputeUnicharFromScanCode() const {
2058 return static_cast<char16_t
>(::MapVirtualKeyEx(
2059 ComputeVirtualKeyCodeFromScanCode(), MAPVK_VK_TO_CHAR
, mKeyboardLayout
));
2062 nsEventStatus
NativeKey::InitKeyEvent(WidgetKeyboardEvent
& aKeyEvent
) const {
2063 return InitKeyEvent(aKeyEvent
, mModKeyState
);
2066 nsEventStatus
NativeKey::InitKeyEvent(
2067 WidgetKeyboardEvent
& aKeyEvent
,
2068 const ModifierKeyState
& aModKeyState
) const {
2069 if (mWidget
->Destroyed()) {
2070 MOZ_CRASH("NativeKey tries to dispatch a key event on destroyed widget");
2073 LayoutDeviceIntPoint
point(0, 0);
2074 mWidget
->InitEvent(aKeyEvent
, &point
);
2076 switch (aKeyEvent
.mMessage
) {
2078 // If it was followed by a char message but it was consumed by somebody,
2079 // we should mark it as consumed because somebody must have handled it
2080 // and we should prevent to do "double action" for the key operation.
2081 // However, for compatibility with older version and other browsers,
2082 // we should dispatch the events even in the web content.
2083 if (mCharMessageHasGone
) {
2084 aKeyEvent
.PreventDefaultBeforeDispatch(CrossProcessForwarding::eAllow
);
2086 aKeyEvent
.mKeyCode
= mDOMKeyCode
;
2087 // Unique id for this keydown event and its associated keypress.
2088 sUniqueKeyEventId
++;
2089 aKeyEvent
.mUniqueId
= sUniqueKeyEventId
;
2092 aKeyEvent
.mKeyCode
= mDOMKeyCode
;
2093 // Set defaultPrevented of the key event if the VK_MENU is not a system
2094 // key release, so that the menu bar does not trigger. This helps avoid
2095 // triggering the menu bar for ALT key accelerators used in assistive
2096 // technologies such as Window-Eyes and ZoomText or for switching open
2097 // state of IME. On the other hand, we should dispatch the events even
2098 // in the web content for compatibility with older version and other
2100 if (mOriginalVirtualKeyCode
== VK_MENU
&& mMsg
.message
!= WM_SYSKEYUP
) {
2101 aKeyEvent
.PreventDefaultBeforeDispatch(CrossProcessForwarding::eAllow
);
2105 MOZ_ASSERT(!mCharMessageHasGone
,
2106 "If following char message was consumed by somebody, "
2107 "keydown event should be consumed above");
2108 aKeyEvent
.mUniqueId
= sUniqueKeyEventId
;
2111 MOZ_CRASH("Invalid event message");
2114 aKeyEvent
.mIsRepeat
= mIsRepeat
;
2115 aKeyEvent
.mMaybeSkippableInRemoteProcess
= mIsSkippableInRemoteProcess
;
2116 aKeyEvent
.mKeyNameIndex
= mKeyNameIndex
;
2117 if (mKeyNameIndex
== KEY_NAME_INDEX_USE_STRING
) {
2118 aKeyEvent
.mKeyValue
= mCommittedCharsAndModifiers
.ToString();
2120 aKeyEvent
.mCodeNameIndex
= mCodeNameIndex
;
2121 MOZ_ASSERT(mCodeNameIndex
!= CODE_NAME_INDEX_USE_STRING
);
2122 aKeyEvent
.mLocation
= GetKeyLocation();
2123 aModKeyState
.InitInputEvent(aKeyEvent
);
2125 KeyboardLayout::NotifyIdleServiceOfUserActivity();
2128 gKeyLog
, LogLevel::Info
,
2129 ("%p NativeKey::InitKeyEvent(), initialized, aKeyEvent={ "
2130 "mMessage=%s, mKeyNameIndex=%s, mKeyValue=\"%s\", mCodeNameIndex=%s, "
2131 "mKeyCode=%s, mLocation=%s, mModifiers=%s, DefaultPrevented()=%s }",
2132 this, ToChar(aKeyEvent
.mMessage
),
2133 ToString(aKeyEvent
.mKeyNameIndex
).get(),
2134 NS_ConvertUTF16toUTF8(aKeyEvent
.mKeyValue
).get(),
2135 ToString(aKeyEvent
.mCodeNameIndex
).get(),
2136 GetDOMKeyCodeName(aKeyEvent
.mKeyCode
).get(),
2137 GetKeyLocationName(aKeyEvent
.mLocation
).get(),
2138 GetModifiersName(aKeyEvent
.mModifiers
).get(),
2139 GetBoolName(aKeyEvent
.DefaultPrevented())));
2141 return aKeyEvent
.DefaultPrevented() ? nsEventStatus_eConsumeNoDefault
2142 : nsEventStatus_eIgnore
;
2145 bool NativeKey::DispatchCommandEvent(uint32_t aEventCommand
) const {
2146 RefPtr
<nsAtom
> command
;
2147 switch (aEventCommand
) {
2148 case APPCOMMAND_BROWSER_BACKWARD
:
2149 command
= nsGkAtoms::Back
;
2151 case APPCOMMAND_BROWSER_FORWARD
:
2152 command
= nsGkAtoms::Forward
;
2154 case APPCOMMAND_BROWSER_REFRESH
:
2155 command
= nsGkAtoms::Reload
;
2157 case APPCOMMAND_BROWSER_STOP
:
2158 command
= nsGkAtoms::Stop
;
2160 case APPCOMMAND_BROWSER_SEARCH
:
2161 command
= nsGkAtoms::Search
;
2163 case APPCOMMAND_BROWSER_FAVORITES
:
2164 command
= nsGkAtoms::Bookmarks
;
2166 case APPCOMMAND_BROWSER_HOME
:
2167 command
= nsGkAtoms::Home
;
2169 case APPCOMMAND_CLOSE
:
2170 command
= nsGkAtoms::Close
;
2172 case APPCOMMAND_FIND
:
2173 command
= nsGkAtoms::Find
;
2175 case APPCOMMAND_HELP
:
2176 command
= nsGkAtoms::Help
;
2178 case APPCOMMAND_NEW
:
2179 command
= nsGkAtoms::New
;
2181 case APPCOMMAND_OPEN
:
2182 command
= nsGkAtoms::Open
;
2184 case APPCOMMAND_PRINT
:
2185 command
= nsGkAtoms::Print
;
2187 case APPCOMMAND_SAVE
:
2188 command
= nsGkAtoms::Save
;
2190 case APPCOMMAND_FORWARD_MAIL
:
2191 command
= nsGkAtoms::ForwardMail
;
2193 case APPCOMMAND_REPLY_TO_MAIL
:
2194 command
= nsGkAtoms::ReplyToMail
;
2196 case APPCOMMAND_SEND_MAIL
:
2197 command
= nsGkAtoms::SendMail
;
2199 case APPCOMMAND_MEDIA_NEXTTRACK
:
2200 command
= nsGkAtoms::NextTrack
;
2202 case APPCOMMAND_MEDIA_PREVIOUSTRACK
:
2203 command
= nsGkAtoms::PreviousTrack
;
2205 case APPCOMMAND_MEDIA_STOP
:
2206 command
= nsGkAtoms::MediaStop
;
2208 case APPCOMMAND_MEDIA_PLAY_PAUSE
:
2209 command
= nsGkAtoms::PlayPause
;
2213 gKeyLog
, LogLevel::Info
,
2214 ("%p NativeKey::DispatchCommandEvent(), doesn't dispatch command "
2219 WidgetCommandEvent
appCommandEvent(true, command
, mWidget
);
2221 mWidget
->InitEvent(appCommandEvent
);
2222 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2223 ("%p NativeKey::DispatchCommandEvent(), dispatching "
2224 "%s app command event...",
2225 this, nsAtomCString(command
).get()));
2227 mWidget
->DispatchWindowEvent(appCommandEvent
) || mWidget
->Destroyed();
2229 gKeyLog
, LogLevel::Info
,
2230 ("%p NativeKey::DispatchCommandEvent(), dispatched app command event, "
2231 "result=%s, mWidget->Destroyed()=%s",
2232 this, GetBoolName(ok
), GetBoolName(mWidget
->Destroyed())));
2236 bool NativeKey::HandleAppCommandMessage() const {
2237 // If the widget has gone, we should do nothing.
2238 if (mWidget
->Destroyed()) {
2239 MOZ_LOG(gKeyLog
, LogLevel::Warning
,
2240 ("%p NativeKey::HandleAppCommandMessage(), WARNING, not handled "
2242 "destroyed the widget",
2247 // NOTE: Typical behavior of WM_APPCOMMAND caused by key is, WM_APPCOMMAND
2248 // message is _sent_ first. Then, the DefaultWndProc will _post_
2249 // WM_KEYDOWN message and WM_KEYUP message if the keycode for the
2250 // command is available (i.e., mVirtualKeyCode is not 0).
2252 // NOTE: IntelliType (Microsoft's keyboard utility software) always consumes
2253 // WM_KEYDOWN and WM_KEYUP.
2255 // Let's dispatch keydown message before our chrome handles the command
2256 // when the message is caused by a keypress. This behavior makes handling
2257 // WM_APPCOMMAND be a default action of the keydown event. This means that
2258 // web applications can handle multimedia keys and prevent our default action.
2259 // This allow web applications to provide better UX for multimedia keyboard
2261 bool dispatchKeyEvent
= (GET_DEVICE_LPARAM(mMsg
.lParam
) == FAPPCOMMAND_KEY
);
2262 if (dispatchKeyEvent
) {
2263 // If a plug-in window has focus but it didn't consume the message, our
2264 // window receive WM_APPCOMMAND message. In this case, we shouldn't
2265 // dispatch KeyboardEvents because an event handler may access the
2266 // plug-in process synchronously.
2268 WinUtils::IsOurProcessWindow(reinterpret_cast<HWND
>(mMsg
.wParam
));
2271 bool consumed
= false;
2273 if (dispatchKeyEvent
) {
2274 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2275 if (NS_WARN_IF(NS_FAILED(rv
))) {
2276 MOZ_LOG(gKeyLog
, LogLevel::Error
,
2277 ("%p NativeKey::HandleAppCommandMessage(), FAILED due to "
2278 "BeginNativeInputTransaction() failure",
2282 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2283 ("%p NativeKey::HandleAppCommandMessage(), initializing keydown "
2286 WidgetKeyboardEvent
keydownEvent(true, eKeyDown
, mWidget
);
2287 nsEventStatus status
= InitKeyEvent(keydownEvent
, mModKeyState
);
2288 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2289 ("%p NativeKey::HandleAppCommandMessage(), tries to dispatch "
2292 // NOTE: If the keydown event is consumed by web contents, we shouldn't
2293 // continue to handle the command.
2294 if (!mDispatcher
->DispatchKeyboardEvent(eKeyDown
, keydownEvent
, status
,
2295 const_cast<NativeKey
*>(this))) {
2296 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2297 ("%p NativeKey::HandleAppCommandMessage(), keydown event isn't "
2300 // If keyboard event wasn't fired, there must be composition.
2301 // So, we don't need to dispatch a command event.
2304 consumed
= status
== nsEventStatus_eConsumeNoDefault
;
2305 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2306 ("%p NativeKey::HandleAppCommandMessage(), keydown event was "
2307 "dispatched, consumed=%s",
2308 this, GetBoolName(consumed
)));
2309 sDispatchedKeyOfAppCommand
= mVirtualKeyCode
;
2310 if (mWidget
->Destroyed()) {
2312 gKeyLog
, LogLevel::Info
,
2313 ("%p NativeKey::HandleAppCommandMessage(), keydown event caused "
2314 "destroying the widget",
2320 // Dispatch a command event or a content command event if the command is
2323 uint32_t appCommand
= GET_APPCOMMAND_LPARAM(mMsg
.lParam
);
2324 EventMessage contentCommandMessage
= eVoidEvent
;
2325 switch (appCommand
) {
2326 case APPCOMMAND_BROWSER_BACKWARD
:
2327 case APPCOMMAND_BROWSER_FORWARD
:
2328 case APPCOMMAND_BROWSER_REFRESH
:
2329 case APPCOMMAND_BROWSER_STOP
:
2330 case APPCOMMAND_BROWSER_SEARCH
:
2331 case APPCOMMAND_BROWSER_FAVORITES
:
2332 case APPCOMMAND_BROWSER_HOME
:
2333 case APPCOMMAND_CLOSE
:
2334 case APPCOMMAND_FIND
:
2335 case APPCOMMAND_HELP
:
2336 case APPCOMMAND_NEW
:
2337 case APPCOMMAND_OPEN
:
2338 case APPCOMMAND_PRINT
:
2339 case APPCOMMAND_SAVE
:
2340 case APPCOMMAND_FORWARD_MAIL
:
2341 case APPCOMMAND_REPLY_TO_MAIL
:
2342 case APPCOMMAND_SEND_MAIL
:
2343 case APPCOMMAND_MEDIA_NEXTTRACK
:
2344 case APPCOMMAND_MEDIA_PREVIOUSTRACK
:
2345 case APPCOMMAND_MEDIA_STOP
:
2346 case APPCOMMAND_MEDIA_PLAY_PAUSE
:
2347 // We shouldn't consume the message always because if we don't handle
2348 // the message, the sender (typically, utility of keyboard or mouse)
2349 // may send other key messages which indicate well known shortcut key.
2350 consumed
= DispatchCommandEvent(appCommand
);
2353 // Use content command for following commands:
2354 case APPCOMMAND_COPY
:
2355 contentCommandMessage
= eContentCommandCopy
;
2357 case APPCOMMAND_CUT
:
2358 contentCommandMessage
= eContentCommandCut
;
2360 case APPCOMMAND_PASTE
:
2361 contentCommandMessage
= eContentCommandPaste
;
2363 case APPCOMMAND_REDO
:
2364 contentCommandMessage
= eContentCommandRedo
;
2366 case APPCOMMAND_UNDO
:
2367 contentCommandMessage
= eContentCommandUndo
;
2371 if (contentCommandMessage
) {
2372 MOZ_ASSERT(!mWidget
->Destroyed());
2373 WidgetContentCommandEvent
contentCommandEvent(true, contentCommandMessage
,
2376 gKeyLog
, LogLevel::Info
,
2377 ("%p NativeKey::HandleAppCommandMessage(), dispatching %s event...",
2378 this, ToChar(contentCommandMessage
)));
2379 mWidget
->DispatchWindowEvent(contentCommandEvent
);
2380 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2381 ("%p NativeKey::HandleAppCommandMessage(), dispatched %s event",
2382 this, ToChar(contentCommandMessage
)));
2385 if (mWidget
->Destroyed()) {
2386 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2387 ("%p NativeKey::HandleAppCommandMessage(), %s event caused "
2388 "destroying the widget",
2389 this, ToChar(contentCommandMessage
)));
2393 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2394 ("%p NativeKey::HandleAppCommandMessage(), doesn't dispatch "
2401 // Dispatch a keyup event if the command is caused by pressing a key and
2402 // the key isn't mapped to a virtual keycode.
2403 if (dispatchKeyEvent
&& !mVirtualKeyCode
) {
2404 MOZ_ASSERT(!mWidget
->Destroyed());
2405 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2406 if (NS_WARN_IF(NS_FAILED(rv
))) {
2407 MOZ_LOG(gKeyLog
, LogLevel::Error
,
2408 ("%p NativeKey::HandleAppCommandMessage(), FAILED due to "
2409 "BeginNativeInputTransaction() failure",
2413 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2414 ("%p NativeKey::HandleAppCommandMessage(), initializing keyup "
2417 WidgetKeyboardEvent
keyupEvent(true, eKeyUp
, mWidget
);
2418 nsEventStatus status
= InitKeyEvent(keyupEvent
, mModKeyState
);
2419 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2420 ("%p NativeKey::HandleAppCommandMessage(), dispatching keyup "
2423 // NOTE: Ignore if the keyup event is consumed because keyup event
2424 // represents just a physical key event state change.
2425 mDispatcher
->DispatchKeyboardEvent(eKeyUp
, keyupEvent
, status
,
2426 const_cast<NativeKey
*>(this));
2428 gKeyLog
, LogLevel::Info
,
2429 ("%p NativeKey::HandleAppCommandMessage(), dispatched keyup event",
2431 if (mWidget
->Destroyed()) {
2432 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2433 ("%p NativeKey::HandleAppCommandMessage(), keyup event caused "
2434 "destroying the widget",
2443 bool NativeKey::HandleKeyDownMessage(bool* aEventDispatched
) const {
2444 MOZ_ASSERT(IsKeyDownMessage());
2446 if (aEventDispatched
) {
2447 *aEventDispatched
= false;
2450 if (sDispatchedKeyOfAppCommand
&&
2451 sDispatchedKeyOfAppCommand
== mOriginalVirtualKeyCode
) {
2452 // The multimedia key event has already been dispatch from
2453 // HandleAppCommandMessage().
2454 sDispatchedKeyOfAppCommand
= 0;
2455 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2456 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2457 "event due to already dispatched from HandleAppCommandMessage(), ",
2459 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg
)) {
2460 RedirectedKeyDownMessageManager::Forget();
2465 if (IsReservedBySystem()) {
2466 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2467 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2468 "event because the key combination is reserved by the system",
2470 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg
)) {
2471 RedirectedKeyDownMessageManager::Forget();
2476 if (sPendingHighSurrogate
) {
2477 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2478 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2479 "event because the key introduced only a high surrotate, so we "
2480 "should wait the following low surrogate input",
2482 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg
)) {
2483 RedirectedKeyDownMessageManager::Forget();
2488 // If the widget has gone, we should do nothing.
2489 if (mWidget
->Destroyed()) {
2491 gKeyLog
, LogLevel::Warning
,
2492 ("%p NativeKey::HandleKeyDownMessage(), WARNING, not handled due to "
2493 "destroyed the widget",
2495 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg
)) {
2496 RedirectedKeyDownMessageManager::Forget();
2501 bool defaultPrevented
= false;
2502 if (mFakeCharMsgs
||
2503 !RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg
)) {
2504 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2505 if (NS_WARN_IF(NS_FAILED(rv
))) {
2506 MOZ_LOG(gKeyLog
, LogLevel::Error
,
2507 ("%p NativeKey::HandleKeyDownMessage(), FAILED due to "
2508 "BeginNativeInputTransaction() failure",
2513 bool isIMEEnabled
= WinUtils::IsIMEEnabled(mWidget
->GetInputContext());
2515 MOZ_LOG(gKeyLog
, LogLevel::Debug
,
2516 ("%p NativeKey::HandleKeyDownMessage(), initializing keydown "
2520 WidgetKeyboardEvent
keydownEvent(true, eKeyDown
, mWidget
);
2521 nsEventStatus status
= InitKeyEvent(keydownEvent
, mModKeyState
);
2523 gKeyLog
, LogLevel::Info
,
2524 ("%p NativeKey::HandleKeyDownMessage(), dispatching keydown event...",
2526 bool dispatched
= mDispatcher
->DispatchKeyboardEvent(
2527 eKeyDown
, keydownEvent
, status
, const_cast<NativeKey
*>(this));
2528 if (aEventDispatched
) {
2529 *aEventDispatched
= dispatched
;
2532 // If the keydown event wasn't fired, there must be composition.
2533 // we don't need to do anything anymore.
2535 gKeyLog
, LogLevel::Info
,
2536 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keypress "
2537 "event(s) because keydown event isn't dispatched actually",
2541 defaultPrevented
= status
== nsEventStatus_eConsumeNoDefault
;
2543 if (mWidget
->Destroyed() || IsFocusedWindowChanged()) {
2544 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2545 ("%p NativeKey::HandleKeyDownMessage(), keydown event caused "
2546 "destroying the widget",
2552 gKeyLog
, LogLevel::Info
,
2553 ("%p NativeKey::HandleKeyDownMessage(), dispatched keydown event, "
2554 "dispatched=%s, defaultPrevented=%s",
2555 this, GetBoolName(dispatched
), GetBoolName(defaultPrevented
)));
2557 // If IMC wasn't associated to the window but is associated it now (i.e.,
2558 // focus is moved from a non-editable editor to an editor by keydown
2559 // event handler), WM_CHAR and WM_SYSCHAR shouldn't cause first character
2560 // inputting if IME is opened. But then, we should redirect the native
2561 // keydown message to IME.
2562 // However, note that if focus has been already moved to another
2563 // application, we shouldn't redirect the message to it because the keydown
2564 // message is processed by us, so, nobody shouldn't process it.
2565 HWND focusedWnd
= ::GetFocus();
2566 if (!defaultPrevented
&& !mFakeCharMsgs
&& focusedWnd
&& !isIMEEnabled
&&
2567 WinUtils::IsIMEEnabled(mWidget
->GetInputContext())) {
2568 RedirectedKeyDownMessageManager::RemoveNextCharMessage(focusedWnd
);
2571 keyinput
.type
= INPUT_KEYBOARD
;
2572 keyinput
.ki
.wVk
= mOriginalVirtualKeyCode
;
2573 keyinput
.ki
.wScan
= mScanCode
;
2574 keyinput
.ki
.dwFlags
= KEYEVENTF_SCANCODE
;
2576 keyinput
.ki
.dwFlags
|= KEYEVENTF_EXTENDEDKEY
;
2578 keyinput
.ki
.time
= 0;
2579 keyinput
.ki
.dwExtraInfo
= 0;
2581 RedirectedKeyDownMessageManager::WillRedirect(mMsg
, defaultPrevented
);
2583 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2584 ("%p NativeKey::HandleKeyDownMessage(), redirecting %s...",
2585 this, ToString(mMsg
).get()));
2587 ::SendInput(1, &keyinput
, sizeof(keyinput
));
2589 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2590 ("%p NativeKey::HandleKeyDownMessage(), redirected %s", this,
2591 ToString(mMsg
).get()));
2593 // Return here. We shouldn't dispatch keypress event for this WM_KEYDOWN.
2594 // If it's needed, it will be dispatched after next (redirected)
2599 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2600 ("%p NativeKey::HandleKeyDownMessage(), received a redirected %s",
2601 this, ToString(mMsg
).get()));
2603 defaultPrevented
= RedirectedKeyDownMessageManager::DefaultPrevented();
2604 // If this is redirected keydown message, we have dispatched the keydown
2606 if (aEventDispatched
) {
2607 *aEventDispatched
= true;
2611 RedirectedKeyDownMessageManager::Forget();
2613 MOZ_ASSERT(!mWidget
->Destroyed());
2615 // If the key was processed by IME and didn't cause WM_(SYS)CHAR messages, we
2616 // shouldn't dispatch keypress event.
2617 if (mOriginalVirtualKeyCode
== VK_PROCESSKEY
&&
2618 !IsFollowedByPrintableCharOrSysCharMessage()) {
2619 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2620 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2621 "event because the key was already handled by IME, "
2622 "defaultPrevented=%s",
2623 this, GetBoolName(defaultPrevented
)));
2624 return defaultPrevented
;
2627 if (defaultPrevented
) {
2628 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2629 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2630 "event because preceding keydown event was consumed",
2635 MOZ_ASSERT(!mCharMessageHasGone
,
2636 "If following char message was consumed by somebody, "
2637 "keydown event should have been consumed before dispatch");
2639 // If mCommittedCharsAndModifiers was initialized with following char
2640 // messages, we should dispatch keypress events with its information.
2641 if (IsFollowedByPrintableCharOrSysCharMessage()) {
2642 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2643 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2644 "keypress events with retrieved char messages...",
2646 return DispatchKeyPressEventsWithRetrievedCharMessages();
2649 // If we won't be getting a WM_CHAR, WM_SYSCHAR or WM_DEADCHAR, synthesize a
2650 // keypress for almost all keys
2651 if (NeedsToHandleWithoutFollowingCharMessages()) {
2652 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2653 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2654 "keypress events...",
2656 return DispatchKeyPressEventsWithoutCharMessage();
2659 // If WM_KEYDOWN of VK_PACKET isn't followed by WM_CHAR, we don't need to
2660 // dispatch keypress events.
2661 if (mVirtualKeyCode
== VK_PACKET
) {
2662 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2663 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2665 "because the key is VK_PACKET and there are no char messages",
2670 if (!mModKeyState
.IsControl() && !mModKeyState
.IsAlt() &&
2671 !mModKeyState
.IsWin() && mIsPrintableKey
) {
2672 // If this is simple KeyDown event but next message is not WM_CHAR,
2673 // this event may not input text, so we should ignore this event.
2675 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2676 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2678 "because the key event is simple printable key's event but not "
2686 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2687 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2689 "because the key is a dead key and not followed by char messages",
2694 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2695 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2696 "keypress events due to no following char messages...",
2698 return DispatchKeyPressEventsWithoutCharMessage();
2701 bool NativeKey::HandleCharMessage(bool* aEventDispatched
) const {
2702 MOZ_ASSERT(IsCharOrSysCharMessage(mMsg
));
2703 return HandleCharMessage(mMsg
, aEventDispatched
);
2706 bool NativeKey::HandleCharMessage(const MSG
& aCharMsg
,
2707 bool* aEventDispatched
) const {
2708 MOZ_ASSERT(IsKeyDownMessage() || IsCharOrSysCharMessage(mMsg
));
2709 MOZ_ASSERT(IsCharOrSysCharMessage(aCharMsg
.message
));
2711 if (aEventDispatched
) {
2712 *aEventDispatched
= false;
2715 if ((IsCharOrSysCharMessage(mMsg
) || IsEnterKeyPressCharMessage(mMsg
)) &&
2716 IsAnotherInstanceRemovingCharMessage()) {
2718 gKeyLog
, LogLevel::Warning
,
2719 ("%p NativeKey::HandleCharMessage(), WARNING, does nothing because "
2720 "the message should be handled in another instance removing this "
2723 // Consume this for now because it will be handled by another instance.
2727 // If the key combinations is reserved by the system, we shouldn't dispatch
2728 // eKeyPress event for it and passes the message to next wndproc.
2729 if (IsReservedBySystem()) {
2730 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2731 ("%p NativeKey::HandleCharMessage(), doesn't dispatch keypress "
2732 "event because the key combination is reserved by the system",
2737 // If the widget has gone, we should do nothing.
2738 if (mWidget
->Destroyed()) {
2739 MOZ_LOG(gKeyLog
, LogLevel::Warning
,
2740 ("%p NativeKey::HandleCharMessage(), WARNING, not handled due to "
2741 "destroyed the widget",
2746 // When a control key is inputted by a key, it should be handled without
2747 // WM_*CHAR messages at receiving WM_*KEYDOWN message. So, when we receive
2748 // WM_*CHAR message directly, we see a control character here.
2749 // Note that when the char is '\r', it means that the char message should
2750 // cause "Enter" keypress event for inserting a line break.
2751 if (IsControlCharMessage(aCharMsg
) && !IsEnterKeyPressCharMessage(aCharMsg
)) {
2752 // In this case, we don't need to dispatch eKeyPress event because:
2753 // 1. We're the only browser which dispatches "keypress" event for
2754 // non-printable characters (Although, both Chrome and Edge dispatch
2755 // "keypress" event for some keys accidentally. For example, "IntlRo"
2756 // key with Ctrl of Japanese keyboard layout).
2757 // 2. Currently, we may handle shortcut keys with "keydown" event if
2758 // it's reserved or something. So, we shouldn't dispatch "keypress"
2759 // event without it.
2760 // Note that this does NOT mean we stop dispatching eKeyPress event for
2761 // key presses causes a control character when Ctrl is pressed. In such
2762 // case, DispatchKeyPressEventsWithoutCharMessage() dispatches eKeyPress
2763 // instead of this method.
2765 gKeyLog
, LogLevel::Info
,
2766 ("%p NativeKey::HandleCharMessage(), doesn't dispatch keypress "
2767 "event because received a control character input without WM_KEYDOWN",
2772 // XXXmnakano I think that if mMsg is WM_CHAR, i.e., it comes without
2773 // preceding WM_KEYDOWN, we should should dispatch composition
2774 // events instead of eKeyPress because they are not caused by
2775 // actual keyboard operation.
2777 // First, handle normal text input or non-printable key case here.
2778 WidgetKeyboardEvent
keypressEvent(true, eKeyPress
, mWidget
);
2779 if (IsEnterKeyPressCharMessage(aCharMsg
)) {
2780 keypressEvent
.mKeyCode
= NS_VK_RETURN
;
2782 keypressEvent
.mCharCode
= static_cast<uint32_t>(aCharMsg
.wParam
);
2784 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2785 if (NS_WARN_IF(NS_FAILED(rv
))) {
2786 MOZ_LOG(gKeyLog
, LogLevel::Error
,
2787 ("%p NativeKey::HandleCharMessage(), FAILED due to "
2788 "BeginNativeInputTransaction() failure",
2793 MOZ_LOG(gKeyLog
, LogLevel::Debug
,
2794 ("%p NativeKey::HandleCharMessage(), initializing keypress "
2798 ModifierKeyState
modKeyState(mModKeyState
);
2799 // When AltGr is pressed, both Alt and Ctrl are active. However, when they
2800 // are active, TextEditor won't treat the keypress event as inputting a
2801 // character. Therefore, when AltGr is pressed and the key tries to input
2802 // a character, let's set them to false.
2803 if (modKeyState
.IsControl() && modKeyState
.IsAlt() &&
2804 IsPrintableCharMessage(aCharMsg
)) {
2805 modKeyState
.Unset(MODIFIER_ALT
| MODIFIER_CONTROL
);
2807 nsEventStatus status
= InitKeyEvent(keypressEvent
, modKeyState
);
2808 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2809 ("%p NativeKey::HandleCharMessage(), dispatching keypress event...",
2811 bool dispatched
= mDispatcher
->MaybeDispatchKeypressEvents(
2812 keypressEvent
, status
, const_cast<NativeKey
*>(this));
2813 if (aEventDispatched
) {
2814 *aEventDispatched
= dispatched
;
2816 if (mWidget
->Destroyed()) {
2817 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2818 ("%p NativeKey::HandleCharMessage(), keypress event caused "
2819 "destroying the widget",
2823 bool consumed
= status
== nsEventStatus_eConsumeNoDefault
;
2824 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2825 ("%p NativeKey::HandleCharMessage(), dispatched keypress event, "
2826 "dispatched=%s, consumed=%s",
2827 this, GetBoolName(dispatched
), GetBoolName(consumed
)));
2831 bool NativeKey::HandleKeyUpMessage(bool* aEventDispatched
) const {
2832 MOZ_ASSERT(IsKeyUpMessage());
2834 if (aEventDispatched
) {
2835 *aEventDispatched
= false;
2838 // If the key combinations is reserved by the system, we shouldn't dispatch
2839 // eKeyUp event for it and passes the message to next wndproc.
2840 if (IsReservedBySystem()) {
2841 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2842 ("%p NativeKey::HandleKeyUpMessage(), doesn't dispatch keyup "
2843 "event because the key combination is reserved by the system",
2848 if (sPendingHighSurrogate
) {
2849 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2850 ("%p NativeKey::HandleKeyUpMessage(), doesn't dispatch keyup "
2851 "event because the key introduced only a high surrotate, so we "
2852 "should wait the following low surrogate input",
2857 // If the widget has gone, we should do nothing.
2858 if (mWidget
->Destroyed()) {
2860 gKeyLog
, LogLevel::Warning
,
2861 ("%p NativeKey::HandleKeyUpMessage(), WARNING, not handled due to "
2862 "destroyed the widget",
2867 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2868 if (NS_WARN_IF(NS_FAILED(rv
))) {
2869 MOZ_LOG(gKeyLog
, LogLevel::Error
,
2870 ("%p NativeKey::HandleKeyUpMessage(), FAILED due to "
2871 "BeginNativeInputTransaction() failure",
2876 MOZ_LOG(gKeyLog
, LogLevel::Debug
,
2877 ("%p NativeKey::HandleKeyUpMessage(), initializing keyup event...",
2879 WidgetKeyboardEvent
keyupEvent(true, eKeyUp
, mWidget
);
2880 nsEventStatus status
= InitKeyEvent(keyupEvent
, mModKeyState
);
2881 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2882 ("%p NativeKey::HandleKeyUpMessage(), dispatching keyup event...",
2884 bool dispatched
= mDispatcher
->DispatchKeyboardEvent(
2885 eKeyUp
, keyupEvent
, status
, const_cast<NativeKey
*>(this));
2886 if (aEventDispatched
) {
2887 *aEventDispatched
= dispatched
;
2889 if (mWidget
->Destroyed()) {
2890 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2891 ("%p NativeKey::HandleKeyUpMessage(), keyup event caused "
2892 "destroying the widget",
2896 bool consumed
= status
== nsEventStatus_eConsumeNoDefault
;
2897 MOZ_LOG(gKeyLog
, LogLevel::Info
,
2898 ("%p NativeKey::HandleKeyUpMessage(), dispatched keyup event, "
2899 "dispatched=%s, consumed=%s",
2900 this, GetBoolName(dispatched
), GetBoolName(consumed
)));
2904 bool NativeKey::NeedsToHandleWithoutFollowingCharMessages() const {
2905 MOZ_ASSERT(IsKeyDownMessage());
2907 // If the key combination is reserved by the system, the caller shouldn't
2908 // do anything with following WM_*CHAR messages. So, let's return true here.
2909 if (IsReservedBySystem()) {
2913 // If the keydown message is generated for inputting some Unicode characters
2914 // via SendInput() API, we need to handle it only with WM_*CHAR messages.
2915 if (mVirtualKeyCode
== VK_PACKET
) {
2919 // If following char message is for a control character, it should be handled
2920 // without WM_CHAR message. This is typically Ctrl + [a-z].
2921 if (mFollowingCharMsgs
.Length() == 1 &&
2922 IsControlCharMessage(mFollowingCharMsgs
[0])) {
2926 // If keydown message is followed by WM_CHAR or WM_SYSCHAR whose wParam isn't
2927 // a control character, we should dispatch keypress event with the char
2928 // message even with any modifier state.
2929 if (IsFollowedByPrintableCharOrSysCharMessage()) {
2933 // If any modifier keys which may cause printable keys becoming non-printable
2934 // are not pressed, we don't need special handling for the key.
2935 // Note that if the key does not produce a character with AltGr and when
2936 // AltGr key is pressed, we don't need to dispatch eKeyPress event for it
2937 // because AltGr shouldn't be used for a modifier for a shortcut without
2938 // Ctrl, Alt or Win. That means that we should treat it in same path for
2940 if (!mModKeyState
.IsControl() && !mModKeyState
.IsAlt() &&
2941 !mModKeyState
.IsWin()) {
2945 // If the key event causes dead key event, we don't need to dispatch keypress
2947 if (mIsDeadKey
&& mCommittedCharsAndModifiers
.IsEmpty()) {
2951 // Even if the key is a printable key, it might cause non-printable character
2952 // input with modifier key(s).
2953 return mIsPrintableKey
;
2956 static nsCString
GetResultOfInSendMessageEx() {
2957 DWORD ret
= ::InSendMessageEx(nullptr);
2959 return "ISMEX_NOSEND"_ns
;
2962 if (ret
& ISMEX_CALLBACK
) {
2963 result
= "ISMEX_CALLBACK";
2965 if (ret
& ISMEX_NOTIFY
) {
2966 if (!result
.IsEmpty()) {
2969 result
+= "ISMEX_NOTIFY";
2971 if (ret
& ISMEX_REPLIED
) {
2972 if (!result
.IsEmpty()) {
2975 result
+= "ISMEX_REPLIED";
2977 if (ret
& ISMEX_SEND
) {
2978 if (!result
.IsEmpty()) {
2981 result
+= "ISMEX_SEND";
2986 bool NativeKey::MayBeSameCharMessage(const MSG
& aCharMsg1
,
2987 const MSG
& aCharMsg2
) const {
2988 // NOTE: Although, we don't know when this case occurs, the scan code value
2989 // in lParam may be changed from 0 to something. The changed value
2990 // is different from the scan code of handling keydown message.
2991 static const LPARAM kScanCodeMask
= 0x00FF0000;
2992 return aCharMsg1
.message
== aCharMsg2
.message
&&
2993 aCharMsg1
.wParam
== aCharMsg2
.wParam
&&
2994 (aCharMsg1
.lParam
& ~kScanCodeMask
) ==
2995 (aCharMsg2
.lParam
& ~kScanCodeMask
);
2998 bool NativeKey::IsSamePhysicalKeyMessage(const MSG
& aKeyOrCharMsg1
,
2999 const MSG
& aKeyOrCharMsg2
) const {
3000 if (NS_WARN_IF(aKeyOrCharMsg1
.message
< WM_KEYFIRST
) ||
3001 NS_WARN_IF(aKeyOrCharMsg1
.message
> WM_KEYLAST
) ||
3002 NS_WARN_IF(aKeyOrCharMsg2
.message
< WM_KEYFIRST
) ||
3003 NS_WARN_IF(aKeyOrCharMsg2
.message
> WM_KEYLAST
)) {
3006 return WinUtils::GetScanCode(aKeyOrCharMsg1
.lParam
) ==
3007 WinUtils::GetScanCode(aKeyOrCharMsg2
.lParam
) &&
3008 WinUtils::IsExtendedScanCode(aKeyOrCharMsg1
.lParam
) ==
3009 WinUtils::IsExtendedScanCode(aKeyOrCharMsg2
.lParam
);
3012 bool NativeKey::GetFollowingCharMessage(MSG
& aCharMsg
) {
3013 MOZ_ASSERT(IsKeyDownMessage());
3015 aCharMsg
.message
= WM_NULL
;
3017 if (mFakeCharMsgs
) {
3018 for (size_t i
= 0; i
< mFakeCharMsgs
->Length(); i
++) {
3019 FakeCharMsg
& fakeCharMsg
= mFakeCharMsgs
->ElementAt(i
);
3020 if (fakeCharMsg
.mConsumed
) {
3023 MSG charMsg
= fakeCharMsg
.GetCharMsg(mMsg
.hwnd
);
3024 fakeCharMsg
.mConsumed
= true;
3025 if (!IsCharMessage(charMsg
)) {
3034 // If next key message is not char message, we should give up to find a
3035 // related char message for the handling keydown event for now.
3036 // Note that it's possible other applications may send other key message
3037 // after we call TranslateMessage(). That may cause PeekMessage() failing
3038 // to get char message for the handling keydown message.
3040 if (!WinUtils::PeekMessage(&nextKeyMsg
, mMsg
.hwnd
, WM_KEYFIRST
, WM_KEYLAST
,
3041 PM_NOREMOVE
| PM_NOYIELD
) ||
3042 !IsCharMessage(nextKeyMsg
)) {
3043 MOZ_LOG(gKeyLog
, LogLevel::Debug
,
3044 ("%p NativeKey::GetFollowingCharMessage(), there are no char "
3049 const MSG kFoundCharMsg
= nextKeyMsg
;
3051 AutoRestore
<MSG
> saveLastRemovingMsg(mRemovingMsg
);
3052 mRemovingMsg
= nextKeyMsg
;
3054 mReceivedMsg
= sEmptyMSG
;
3055 AutoRestore
<MSG
> ensureToClearRecivedMsg(mReceivedMsg
);
3057 // On Metrofox, PeekMessage() sometimes returns WM_NULL even if we specify
3058 // the message range. So, if it returns WM_NULL, we should retry to get
3059 // the following char message it was found above.
3060 for (uint32_t i
= 0; i
< 50; i
++) {
3061 MSG removedMsg
, nextKeyMsgInAllWindows
;
3062 bool doCrash
= false;
3063 if (!WinUtils::PeekMessage(&removedMsg
, mMsg
.hwnd
, nextKeyMsg
.message
,
3064 nextKeyMsg
.message
, PM_REMOVE
| PM_NOYIELD
)) {
3065 // We meets unexpected case. We should collect the message queue state
3066 // and crash for reporting the bug.
3069 // If another instance was created for the removing message during trying
3070 // to remove a char message, the instance didn't handle it for preventing
3071 // recursive handling. So, let's handle it in this instance.
3072 if (!IsEmptyMSG(mReceivedMsg
)) {
3073 // If focus is moved to different window, we shouldn't handle it on
3074 // the widget. Let's discard it for now.
3075 if (mReceivedMsg
.hwnd
!= nextKeyMsg
.hwnd
) {
3077 gKeyLog
, LogLevel::Warning
,
3078 ("%p NativeKey::GetFollowingCharMessage(), WARNING, received a "
3079 "char message during removing it from the queue, but it's for "
3080 "different window, mReceivedMsg=%s, nextKeyMsg=%s, "
3082 this, ToString(mReceivedMsg
).get(), ToString(nextKeyMsg
).get(),
3083 ToString(kFoundCharMsg
).get()));
3084 // There might still exist char messages, the loop of calling
3085 // this method should be continued.
3086 aCharMsg
.message
= WM_NULL
;
3089 // Even if the received message is different from what we tried to
3090 // remove from the queue, let's take the received message as a part of
3091 // the result of this key sequence.
3092 if (mReceivedMsg
.message
!= nextKeyMsg
.message
||
3093 mReceivedMsg
.wParam
!= nextKeyMsg
.wParam
||
3094 mReceivedMsg
.lParam
!= nextKeyMsg
.lParam
) {
3096 gKeyLog
, LogLevel::Warning
,
3097 ("%p NativeKey::GetFollowingCharMessage(), WARNING, received a "
3098 "char message during removing it from the queue, but it's "
3099 "differnt from what trying to remove from the queue, "
3100 "aCharMsg=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3101 this, ToString(mReceivedMsg
).get(), ToString(nextKeyMsg
).get(),
3102 ToString(kFoundCharMsg
).get()));
3105 gKeyLog
, LogLevel::Debug
,
3106 ("%p NativeKey::GetFollowingCharMessage(), succeeded to "
3107 "retrieve next char message via another instance, aCharMsg=%s, "
3109 this, ToString(mReceivedMsg
).get(),
3110 ToString(kFoundCharMsg
).get()));
3112 aCharMsg
= mReceivedMsg
;
3116 // The char message is redirected to different thread's window by focus
3117 // move or something or just cancelled by external application.
3118 if (!WinUtils::PeekMessage(&nextKeyMsgInAllWindows
, 0, WM_KEYFIRST
,
3119 WM_KEYLAST
, PM_NOREMOVE
| PM_NOYIELD
)) {
3121 gKeyLog
, LogLevel::Warning
,
3122 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3123 "remove a char message, but it's already gone from all message "
3124 "queues, nextKeyMsg=%s, kFoundCharMsg=%s",
3125 this, ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3126 return true; // XXX should return false in this case
3128 // The next key message is redirected to different window created by our
3129 // thread, we should do nothing because we must not have focus.
3130 if (nextKeyMsgInAllWindows
.hwnd
!= mMsg
.hwnd
) {
3131 aCharMsg
= nextKeyMsgInAllWindows
;
3133 gKeyLog
, LogLevel::Warning
,
3134 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3135 "remove a char message, but found in another message queue, "
3136 "nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3137 this, ToString(nextKeyMsgInAllWindows
).get(),
3138 ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3141 // If next key message becomes non-char message, this key operation
3142 // may have already been consumed or canceled.
3143 if (!IsCharMessage(nextKeyMsgInAllWindows
)) {
3145 gKeyLog
, LogLevel::Warning
,
3146 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3147 "remove a char message and next key message becomes non-char "
3148 "message, nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, "
3150 this, ToString(nextKeyMsgInAllWindows
).get(),
3151 ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3152 MOZ_ASSERT(!mCharMessageHasGone
);
3153 mFollowingCharMsgs
.Clear();
3154 mCharMessageHasGone
= true;
3157 // If next key message is still a char message but different key message,
3158 // we should treat current key operation is consumed or canceled and
3159 // next char message should be handled as an orphan char message later.
3160 if (!IsSamePhysicalKeyMessage(nextKeyMsgInAllWindows
, kFoundCharMsg
)) {
3162 gKeyLog
, LogLevel::Warning
,
3163 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3164 "remove a char message and next key message becomes differnt "
3166 "char message, nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, "
3168 this, ToString(nextKeyMsgInAllWindows
).get(),
3169 ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3170 MOZ_ASSERT(!mCharMessageHasGone
);
3171 mFollowingCharMsgs
.Clear();
3172 mCharMessageHasGone
= true;
3175 // If next key message is still a char message but the message is changed,
3176 // we should retry to remove the new message with PeekMessage() again.
3177 if (nextKeyMsgInAllWindows
.message
!= nextKeyMsg
.message
) {
3179 gKeyLog
, LogLevel::Warning
,
3180 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3181 "remove a char message due to message change, let's retry to "
3182 "remove the message with newly found char message, "
3183 "nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3184 this, ToString(nextKeyMsgInAllWindows
).get(),
3185 ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3186 nextKeyMsg
= nextKeyMsgInAllWindows
;
3189 // If there is still existing a char message caused by same physical key
3190 // in the queue but PeekMessage(PM_REMOVE) failed to remove it from the
3191 // queue, it might be possible that the odd keyboard layout or utility
3192 // hooks only PeekMessage(PM_NOREMOVE) and GetMessage(). So, let's try
3193 // remove the char message with GetMessage() again.
3194 // FYI: The wParam might be different from the found message, but it's
3195 // okay because we assume that odd keyboard layouts return actual
3196 // inputting character at removing the char message.
3197 if (WinUtils::GetMessage(&removedMsg
, mMsg
.hwnd
, nextKeyMsg
.message
,
3198 nextKeyMsg
.message
)) {
3200 gKeyLog
, LogLevel::Warning
,
3201 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3202 "remove a char message, but succeeded with GetMessage(), "
3203 "removedMsg=%s, kFoundCharMsg=%s",
3204 this, ToString(removedMsg
).get(), ToString(kFoundCharMsg
).get()));
3205 // Cancel to crash, but we need to check the removed message value.
3208 // If we've already removed some WM_NULL messages from the queue and
3209 // the found message has already gone from the queue, let's treat the key
3210 // as inputting no characters and already consumed.
3213 gKeyLog
, LogLevel::Warning
,
3214 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3215 "remove a char message, but removed %d WM_NULL messages",
3217 // If the key is a printable key or a control key but tried to input
3218 // a character, mark mCharMessageHasGone true for handling the keydown
3219 // event as inputting empty string.
3220 MOZ_ASSERT(!mCharMessageHasGone
);
3221 mFollowingCharMsgs
.Clear();
3222 mCharMessageHasGone
= true;
3225 MOZ_LOG(gKeyLog
, LogLevel::Error
,
3226 ("%p NativeKey::GetFollowingCharMessage(), FAILED, lost target "
3227 "message to remove, nextKeyMsg=%s",
3228 this, ToString(nextKeyMsg
).get()));
3232 nsPrintfCString
info(
3233 "\nPeekMessage() failed to remove char message! "
3234 "\nActive keyboard layout=0x%p (%s), "
3235 "\nHandling message: %s, InSendMessageEx()=%s, "
3236 "\nFound message: %s, "
3237 "\nWM_NULL has been removed: %d, "
3238 "\nNext key message in all windows: %s, "
3240 KeyboardLayout::GetActiveLayout(),
3241 KeyboardLayout::GetActiveLayoutName().get(), ToString(mMsg
).get(),
3242 GetResultOfInSendMessageEx().get(), ToString(kFoundCharMsg
).get(), i
,
3243 ToString(nextKeyMsgInAllWindows
).get(), nextKeyMsgInAllWindows
.time
);
3244 CrashReporter::AppendAppNotesToCrashReport(info
);
3246 if (WinUtils::PeekMessage(&nextMsg
, 0, 0, 0, PM_NOREMOVE
| PM_NOYIELD
)) {
3247 nsPrintfCString
info("\nNext message in all windows: %s, time=%ld",
3248 ToString(nextMsg
).get(), nextMsg
.time
);
3249 CrashReporter::AppendAppNotesToCrashReport(info
);
3251 CrashReporter::AppendAppNotesToCrashReport(
3252 "\nThere is no message in any window"_ns
);
3255 MOZ_CRASH("We lost the following char message");
3258 // We're still not sure why ::PeekMessage() may return WM_NULL even with
3259 // its first message and its last message are same message. However,
3260 // at developing Metrofox, we met this case even with usual keyboard
3261 // layouts. So, it might be possible in desktop application or it really
3262 // occurs with some odd keyboard layouts which perhaps hook API.
3263 if (removedMsg
.message
== WM_NULL
) {
3264 MOZ_LOG(gKeyLog
, LogLevel::Warning
,
3265 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3266 "remove a char message, instead, removed WM_NULL message, "
3268 this, ToString(removedMsg
).get()));
3269 // Check if there is the message which we're trying to remove.
3271 if (!WinUtils::PeekMessage(&newNextKeyMsg
, mMsg
.hwnd
, WM_KEYFIRST
,
3272 WM_KEYLAST
, PM_NOREMOVE
| PM_NOYIELD
)) {
3273 // If there is no key message, we should mark this keydown as consumed
3274 // because the key operation may have already been handled or canceled.
3276 gKeyLog
, LogLevel::Warning
,
3277 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3278 "remove a char message because it's gone during removing it from "
3279 "the queue, nextKeyMsg=%s, kFoundCharMsg=%s",
3280 this, ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3281 MOZ_ASSERT(!mCharMessageHasGone
);
3282 mFollowingCharMsgs
.Clear();
3283 mCharMessageHasGone
= true;
3286 if (!IsCharMessage(newNextKeyMsg
)) {
3287 // If next key message becomes a non-char message, we should mark this
3288 // keydown as consumed because the key operation may have already been
3289 // handled or canceled.
3291 gKeyLog
, LogLevel::Warning
,
3292 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3293 "remove a char message because it's gone during removing it from "
3294 "the queue, nextKeyMsg=%s, newNextKeyMsg=%s, kFoundCharMsg=%s",
3295 this, ToString(nextKeyMsg
).get(), ToString(newNextKeyMsg
).get(),
3296 ToString(kFoundCharMsg
).get()));
3297 MOZ_ASSERT(!mCharMessageHasGone
);
3298 mFollowingCharMsgs
.Clear();
3299 mCharMessageHasGone
= true;
3303 gKeyLog
, LogLevel::Debug
,
3304 ("%p NativeKey::GetFollowingCharMessage(), there is the message "
3305 "which is being tried to be removed from the queue, trying again...",
3310 // Typically, this case occurs with WM_DEADCHAR. If the removed message's
3311 // wParam becomes 0, that means that the key event shouldn't cause text
3312 // input. So, let's ignore the strange char message.
3313 if (removedMsg
.message
== nextKeyMsg
.message
&& !removedMsg
.wParam
) {
3315 gKeyLog
, LogLevel::Warning
,
3316 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3317 "remove a char message, but the removed message's wParam is 0, "
3319 this, ToString(removedMsg
).get()));
3323 // This is normal case.
3324 if (MayBeSameCharMessage(removedMsg
, nextKeyMsg
)) {
3325 aCharMsg
= removedMsg
;
3327 gKeyLog
, LogLevel::Debug
,
3328 ("%p NativeKey::GetFollowingCharMessage(), succeeded to retrieve "
3329 "next char message, aCharMsg=%s",
3330 this, ToString(aCharMsg
).get()));
3334 // Even if removed message is different char message from the found char
3335 // message, when the scan code is same, we can assume that the message
3336 // is overwritten by somebody who hooks API. See bug 1336028 comment 0 for
3337 // the possible scenarios.
3338 if (IsCharMessage(removedMsg
) &&
3339 IsSamePhysicalKeyMessage(removedMsg
, nextKeyMsg
)) {
3340 aCharMsg
= removedMsg
;
3342 gKeyLog
, LogLevel::Warning
,
3343 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3344 "remove a char message, but the removed message was changed from "
3345 "the found message except their scancode, aCharMsg=%s, "
3346 "nextKeyMsg=%s, kFoundCharMsg=%s",
3347 this, ToString(aCharMsg
).get(), ToString(nextKeyMsg
).get(),
3348 ToString(kFoundCharMsg
).get()));
3352 // When found message's wParam is 0 and its scancode is 0xFF, we may remove
3353 // usual char message actually. In such case, we should use the removed
3355 if (IsCharMessage(removedMsg
) && !nextKeyMsg
.wParam
&&
3356 WinUtils::GetScanCode(nextKeyMsg
.lParam
) == 0xFF) {
3357 aCharMsg
= removedMsg
;
3359 gKeyLog
, LogLevel::Warning
,
3360 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3361 "remove a char message, but the removed message was changed from "
3362 "the found message but the found message was odd, so, ignoring the "
3363 "odd found message and respecting the removed message, aCharMsg=%s, "
3364 "nextKeyMsg=%s, kFoundCharMsg=%s",
3365 this, ToString(aCharMsg
).get(), ToString(nextKeyMsg
).get(),
3366 ToString(kFoundCharMsg
).get()));
3370 // NOTE: Although, we don't know when this case occurs, the scan code value
3371 // in lParam may be changed from 0 to something. The changed value
3372 // is different from the scan code of handling keydown message.
3374 gKeyLog
, LogLevel::Error
,
3375 ("%p NativeKey::GetFollowingCharMessage(), FAILED, removed message "
3376 "is really different from what we have already found, removedMsg=%s, "
3377 "nextKeyMsg=%s, kFoundCharMsg=%s",
3378 this, ToString(removedMsg
).get(), ToString(nextKeyMsg
).get(),
3379 ToString(kFoundCharMsg
).get()));
3380 nsPrintfCString
info(
3381 "\nPeekMessage() removed unexpcted char message! "
3382 "\nActive keyboard layout=0x%p (%s), "
3383 "\nHandling message: %s, InSendMessageEx()=%s, "
3384 "\nFound message: %s, "
3385 "\nRemoved message: %s, ",
3386 KeyboardLayout::GetActiveLayout(),
3387 KeyboardLayout::GetActiveLayoutName().get(), ToString(mMsg
).get(),
3388 GetResultOfInSendMessageEx().get(), ToString(kFoundCharMsg
).get(),
3389 ToString(removedMsg
).get());
3390 CrashReporter::AppendAppNotesToCrashReport(info
);
3391 // What's the next key message?
3392 MSG nextKeyMsgAfter
;
3393 if (WinUtils::PeekMessage(&nextKeyMsgAfter
, mMsg
.hwnd
, WM_KEYFIRST
,
3394 WM_KEYLAST
, PM_NOREMOVE
| PM_NOYIELD
)) {
3395 nsPrintfCString
info(
3396 "\nNext key message after unexpected char message "
3398 ToString(nextKeyMsgAfter
).get());
3399 CrashReporter::AppendAppNotesToCrashReport(info
);
3401 CrashReporter::AppendAppNotesToCrashReport(
3402 nsLiteralCString("\nThere is no key message after unexpected char "
3403 "message removed, "));
3405 // Another window has a key message?
3406 if (WinUtils::PeekMessage(&nextKeyMsgInAllWindows
, 0, WM_KEYFIRST
,
3407 WM_KEYLAST
, PM_NOREMOVE
| PM_NOYIELD
)) {
3408 nsPrintfCString
info("\nNext key message in all windows: %s.",
3409 ToString(nextKeyMsgInAllWindows
).get());
3410 CrashReporter::AppendAppNotesToCrashReport(info
);
3412 CrashReporter::AppendAppNotesToCrashReport(
3413 "\nThere is no key message in any windows."_ns
);
3416 MOZ_CRASH("PeekMessage() removed unexpected message");
3419 gKeyLog
, LogLevel::Error
,
3420 ("%p NativeKey::GetFollowingCharMessage(), FAILED, removed messages "
3421 "are all WM_NULL, nextKeyMsg=%s",
3422 this, ToString(nextKeyMsg
).get()));
3423 nsPrintfCString
info(
3424 "\nWe lost following char message! "
3425 "\nActive keyboard layout=0x%p (%s), "
3426 "\nHandling message: %s, InSendMessageEx()=%s, \n"
3427 "Found message: %s, removed a lot of WM_NULL",
3428 KeyboardLayout::GetActiveLayout(),
3429 KeyboardLayout::GetActiveLayoutName().get(), ToString(mMsg
).get(),
3430 GetResultOfInSendMessageEx().get(), ToString(kFoundCharMsg
).get());
3431 CrashReporter::AppendAppNotesToCrashReport(info
);
3432 MOZ_CRASH("We lost the following char message");
3436 void NativeKey::ComputeInputtingStringWithKeyboardLayout() {
3437 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
3439 if (KeyboardLayout::IsPrintableCharKey(mVirtualKeyCode
) ||
3440 mCharMessageHasGone
) {
3441 mInputtingStringAndModifiers
= mCommittedCharsAndModifiers
;
3443 mInputtingStringAndModifiers
.Clear();
3445 mShiftedString
.Clear();
3446 mUnshiftedString
.Clear();
3447 mShiftedLatinChar
= mUnshiftedLatinChar
= 0;
3449 // XXX How about when Win key is pressed?
3450 if (mModKeyState
.IsControl() == mModKeyState
.IsAlt()) {
3454 // If user is inputting a Unicode character with typing Alt + Numpad
3455 // keys, we shouldn't set alternative key codes because the key event
3456 // shouldn't match with a mnemonic. However, we should set only
3457 // mUnshiftedString for keeping traditional behavior at WM_SYSKEYDOWN.
3458 // FYI: I guess that it's okay that mUnshiftedString stays empty. So,
3459 // if its value breaks something, you must be able to just return here.
3460 if (MaybeTypingUnicodeScalarValue()) {
3461 if (!mCommittedCharsAndModifiers
.IsEmpty()) {
3462 MOZ_ASSERT(mMsg
.message
== WM_SYSKEYDOWN
);
3463 char16_t num
= mCommittedCharsAndModifiers
.CharAt(0);
3464 MOZ_ASSERT(num
>= '0' && num
<= '9');
3465 mUnshiftedString
.Append(num
, MODIFIER_NONE
);
3468 // If user presses a function key without NumLock or 3rd party utility
3469 // synthesizes a function key on numpad, we should handle it as-is because
3470 // the user's intention may be performing `Alt` + foo.
3471 MOZ_ASSERT(!KeyboardLayout::IsPrintableCharKey(mVirtualKeyCode
));
3475 ModifierKeyState
capsLockState(mModKeyState
.GetModifiers() &
3479 keyboardLayout
->GetUniCharsAndModifiers(mVirtualKeyCode
, capsLockState
);
3480 capsLockState
.Set(MODIFIER_SHIFT
);
3482 keyboardLayout
->GetUniCharsAndModifiers(mVirtualKeyCode
, capsLockState
);
3484 // The current keyboard cannot input alphabets or numerics,
3485 // we should append them for Shortcut/Access keys.
3486 // E.g., for Cyrillic keyboard layout.
3487 capsLockState
.Unset(MODIFIER_SHIFT
);
3488 WidgetUtils::GetLatinCharCodeForKeyCode(
3489 mDOMKeyCode
, capsLockState
.GetModifiers(), &mUnshiftedLatinChar
,
3490 &mShiftedLatinChar
);
3492 // If the mShiftedLatinChar isn't 0, the key code is NS_VK_[A-Z].
3493 if (mShiftedLatinChar
) {
3494 // If the produced characters of the key on current keyboard layout
3495 // are same as computed Latin characters, we shouldn't append the
3496 // Latin characters to alternativeCharCode.
3497 if (mUnshiftedLatinChar
== mUnshiftedString
.CharAt(0) &&
3498 mShiftedLatinChar
== mShiftedString
.CharAt(0)) {
3499 mShiftedLatinChar
= mUnshiftedLatinChar
= 0;
3501 } else if (mUnshiftedLatinChar
) {
3502 // If the mShiftedLatinChar is 0, the mKeyCode doesn't produce
3503 // alphabet character. At that time, the character may be produced
3504 // with Shift key. E.g., on French keyboard layout, NS_VK_PERCENT
3505 // key produces LATIN SMALL LETTER U WITH GRAVE (U+00F9) without
3506 // Shift key but with Shift key, it produces '%'.
3507 // If the mUnshiftedLatinChar is produced by the key on current
3508 // keyboard layout, we shouldn't append it to alternativeCharCode.
3509 if (mUnshiftedLatinChar
== mUnshiftedString
.CharAt(0) ||
3510 mUnshiftedLatinChar
== mShiftedString
.CharAt(0)) {
3511 mUnshiftedLatinChar
= 0;
3515 if (!mModKeyState
.IsControl()) {
3519 // If the mCharCode is not ASCII character, we should replace the
3520 // mCharCode with ASCII character only when Ctrl is pressed.
3521 // But don't replace the mCharCode when the mCharCode is not same as
3522 // unmodified characters. In such case, Ctrl is sometimes used for a
3523 // part of character inputting key combination like Shift.
3525 mModKeyState
.IsShift() ? mShiftedLatinChar
: mUnshiftedLatinChar
;
3529 if (mInputtingStringAndModifiers
.IsEmpty() ||
3530 mInputtingStringAndModifiers
.UniCharsCaseInsensitiveEqual(
3531 mModKeyState
.IsShift() ? mShiftedString
: mUnshiftedString
)) {
3532 mInputtingStringAndModifiers
.Clear();
3533 mInputtingStringAndModifiers
.Append(ch
, mModKeyState
.GetModifiers());
3537 bool NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages() const {
3538 MOZ_ASSERT(IsKeyDownMessage());
3539 MOZ_ASSERT(IsFollowedByPrintableCharOrSysCharMessage());
3540 MOZ_ASSERT(!mWidget
->Destroyed());
3542 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
3543 if (NS_WARN_IF(NS_FAILED(rv
))) {
3545 gKeyLog
, LogLevel::Error
,
3546 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3547 "FAILED due to BeginNativeInputTransaction() failure",
3551 WidgetKeyboardEvent
keypressEvent(true, eKeyPress
, mWidget
);
3552 MOZ_LOG(gKeyLog
, LogLevel::Debug
,
3553 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3554 "initializing keypress event...",
3556 ModifierKeyState
modKeyState(mModKeyState
);
3557 if (mCanIgnoreModifierStateAtKeyPress
&& IsFollowedByPrintableCharMessage()) {
3558 // If eKeyPress event should cause inputting text in focused editor,
3559 // we need to remove Alt and Ctrl state.
3560 modKeyState
.Unset(MODIFIER_ALT
| MODIFIER_CONTROL
);
3562 // We don't need to send char message here if there are two or more retrieved
3563 // messages because we need to set each message to each eKeyPress event.
3564 bool needsCallback
= mFollowingCharMsgs
.Length() > 1;
3565 nsEventStatus status
= InitKeyEvent(keypressEvent
, modKeyState
);
3566 MOZ_LOG(gKeyLog
, LogLevel::Info
,
3567 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3568 "dispatching keypress event(s)...",
3570 bool dispatched
= mDispatcher
->MaybeDispatchKeypressEvents(
3571 keypressEvent
, status
, const_cast<NativeKey
*>(this), needsCallback
);
3572 if (mWidget
->Destroyed()) {
3574 gKeyLog
, LogLevel::Info
,
3575 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3576 "keypress event(s) caused destroying the widget",
3580 bool consumed
= status
== nsEventStatus_eConsumeNoDefault
;
3581 MOZ_LOG(gKeyLog
, LogLevel::Info
,
3582 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3583 "dispatched keypress event(s), dispatched=%s, consumed=%s",
3584 this, GetBoolName(dispatched
), GetBoolName(consumed
)));
3588 bool NativeKey::DispatchKeyPressEventsWithoutCharMessage() const {
3589 MOZ_ASSERT(IsKeyDownMessage());
3590 MOZ_ASSERT(!mIsDeadKey
|| !mCommittedCharsAndModifiers
.IsEmpty());
3591 MOZ_ASSERT(!mWidget
->Destroyed());
3593 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
3594 if (NS_WARN_IF(NS_FAILED(rv
))) {
3595 MOZ_LOG(gKeyLog
, LogLevel::Error
,
3596 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3598 "to BeginNativeInputTransaction() failure",
3603 WidgetKeyboardEvent
keypressEvent(true, eKeyPress
, mWidget
);
3604 if (mInputtingStringAndModifiers
.IsEmpty() && mShiftedString
.IsEmpty() &&
3605 mUnshiftedString
.IsEmpty()) {
3606 keypressEvent
.mKeyCode
= mDOMKeyCode
;
3608 MOZ_LOG(gKeyLog
, LogLevel::Debug
,
3609 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3611 "keypress event...",
3613 nsEventStatus status
= InitKeyEvent(keypressEvent
, mModKeyState
);
3614 MOZ_LOG(gKeyLog
, LogLevel::Info
,
3615 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3617 "keypress event(s)...",
3619 bool dispatched
= mDispatcher
->MaybeDispatchKeypressEvents(
3620 keypressEvent
, status
, const_cast<NativeKey
*>(this));
3621 if (mWidget
->Destroyed()) {
3622 MOZ_LOG(gKeyLog
, LogLevel::Info
,
3623 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3624 "keypress event(s) caused destroying the widget",
3628 bool consumed
= status
== nsEventStatus_eConsumeNoDefault
;
3630 gKeyLog
, LogLevel::Info
,
3631 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), dispatched "
3632 "keypress event(s), dispatched=%s, consumed=%s",
3633 this, GetBoolName(dispatched
), GetBoolName(consumed
)));
3637 void NativeKey::WillDispatchKeyboardEvent(WidgetKeyboardEvent
& aKeyboardEvent
,
3639 // If it's an eKeyPress event and it's generated from retrieved char message,
3640 // we need to set raw message information for plugins.
3641 if (aKeyboardEvent
.mMessage
== eKeyPress
&&
3642 IsFollowedByPrintableCharOrSysCharMessage()) {
3643 MOZ_RELEASE_ASSERT(aIndex
< mCommittedCharsAndModifiers
.Length());
3644 uint32_t foundPrintableCharMessages
= 0;
3645 for (size_t i
= 0; i
< mFollowingCharMsgs
.Length(); ++i
) {
3646 if (!IsPrintableCharOrSysCharMessage(mFollowingCharMsgs
[i
])) {
3647 // XXX Should we dispatch a plugin event for WM_*DEADCHAR messages and
3648 // WM_CHAR with a control character here? But we're not sure
3649 // how can we create such message queue (i.e., WM_CHAR or
3650 // WM_SYSCHAR with a printable character and such message are
3651 // generated by a keydown). So, let's ignore such case until
3652 // we'd get some bug reports.
3653 MOZ_LOG(gKeyLog
, LogLevel::Warning
,
3654 ("%p NativeKey::WillDispatchKeyboardEvent(), WARNING, "
3655 "ignoring %zuth message due to non-printable char message, %s",
3656 this, i
+ 1, ToString(mFollowingCharMsgs
[i
]).get()));
3659 if (foundPrintableCharMessages
++ == aIndex
) {
3660 // Found message which caused the eKeyPress event.
3664 // Set modifier state from mCommittedCharsAndModifiers because some of them
3665 // might be different. For example, Shift key was pressed at inputting
3666 // dead char but Shift key was released before inputting next character.
3667 if (mCanIgnoreModifierStateAtKeyPress
) {
3668 ModifierKeyState
modKeyState(mModKeyState
);
3669 modKeyState
.Unset(MODIFIER_SHIFT
| MODIFIER_CONTROL
| MODIFIER_ALT
|
3670 MODIFIER_ALTGRAPH
| MODIFIER_CAPSLOCK
);
3671 modKeyState
.Set(mCommittedCharsAndModifiers
.ModifiersAt(aIndex
));
3672 modKeyState
.InitInputEvent(aKeyboardEvent
);
3673 MOZ_LOG(gKeyLog
, LogLevel::Info
,
3674 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3675 "setting %uth modifier state to %s",
3676 this, aIndex
+ 1, ToString(modKeyState
).get()));
3679 size_t longestLength
=
3680 std::max(mInputtingStringAndModifiers
.Length(),
3681 std::max(mShiftedString
.Length(), mUnshiftedString
.Length()));
3682 size_t skipUniChars
= longestLength
- mInputtingStringAndModifiers
.Length();
3683 size_t skipShiftedChars
= longestLength
- mShiftedString
.Length();
3684 size_t skipUnshiftedChars
= longestLength
- mUnshiftedString
.Length();
3685 if (aIndex
>= longestLength
) {
3687 gKeyLog
, LogLevel::Info
,
3688 ("%p NativeKey::WillDispatchKeyboardEvent(), does nothing for %uth "
3690 this, aIndex
+ 1, ToChar(aKeyboardEvent
.mMessage
)));
3694 // Check if aKeyboardEvent is the last event for a key press.
3695 // So, if it's not an eKeyPress event, it's always the last event.
3696 // Otherwise, check if the index is the last character of
3697 // mCommittedCharsAndModifiers.
3698 bool isLastIndex
= aKeyboardEvent
.mMessage
!= eKeyPress
||
3699 mCommittedCharsAndModifiers
.IsEmpty() ||
3700 mCommittedCharsAndModifiers
.Length() - 1 == aIndex
;
3702 nsTArray
<AlternativeCharCode
>& altArray
=
3703 aKeyboardEvent
.mAlternativeCharCodes
;
3705 // Set charCode and adjust modifier state for every eKeyPress event.
3706 // This is not necessary for the other keyboard events because the other
3707 // keyboard events shouldn't have non-zero charCode value and should have
3708 // current modifier state.
3709 if (aKeyboardEvent
.mMessage
== eKeyPress
&& skipUniChars
<= aIndex
) {
3710 // XXX Modifying modifier state of aKeyboardEvent is illegal, but no way
3711 // to set different modifier state per keypress event except this
3712 // hack. Note that ideally, dead key should cause composition events
3713 // instead of keypress events, though.
3714 if (aIndex
- skipUniChars
< mInputtingStringAndModifiers
.Length()) {
3715 ModifierKeyState
modKeyState(mModKeyState
);
3716 // If key in combination with Alt and/or Ctrl produces a different
3717 // character than without them then do not report these flags
3718 // because it is separate keyboard layout shift state. If dead-key
3719 // and base character does not produce a valid composite character
3720 // then both produced dead-key character and following base
3721 // character may have different modifier flags, too.
3722 modKeyState
.Unset(MODIFIER_SHIFT
| MODIFIER_CONTROL
| MODIFIER_ALT
|
3723 MODIFIER_ALTGRAPH
| MODIFIER_CAPSLOCK
);
3725 mInputtingStringAndModifiers
.ModifiersAt(aIndex
- skipUniChars
));
3726 modKeyState
.InitInputEvent(aKeyboardEvent
);
3727 MOZ_LOG(gKeyLog
, LogLevel::Info
,
3728 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3729 "setting %uth modifier state to %s",
3730 this, aIndex
+ 1, ToString(modKeyState
).get()));
3733 mInputtingStringAndModifiers
.CharAt(aIndex
- skipUniChars
);
3735 // The mCharCode was set from mKeyValue. However, for example, when Ctrl key
3736 // is pressed, its value should indicate an ASCII character for backward
3737 // compatibility rather than inputting character without the modifiers.
3738 // Therefore, we need to modify mCharCode value here.
3739 aKeyboardEvent
.SetCharCode(uniChar
);
3740 MOZ_LOG(gKeyLog
, LogLevel::Info
,
3741 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3742 "setting %uth charCode to %s",
3743 this, aIndex
+ 1, GetCharacterCodeName(uniChar
).get()));
3746 // We need to append alterntaive charCode values:
3747 // - if the event is eKeyPress, we need to append for the index because
3748 // eKeyPress event is dispatched for every character inputted by a
3750 // - if the event is not eKeyPress, we need to append for all characters
3751 // inputted by the key press because the other keyboard events (e.g.,
3752 // eKeyDown are eKeyUp) are fired only once for a key press.
3754 if (aKeyboardEvent
.mMessage
== eKeyPress
) {
3755 // Basically, append alternative charCode values only for the index.
3757 // However, if it's the last eKeyPress event but different shift state
3758 // can input longer string, the last eKeyPress event should have all
3759 // remaining alternative charCode values.
3761 count
= longestLength
- aIndex
;
3764 count
= longestLength
;
3766 for (size_t i
= 0; i
< count
; ++i
) {
3767 uint16_t shiftedChar
= 0, unshiftedChar
= 0;
3768 if (skipShiftedChars
<= aIndex
+ i
) {
3769 shiftedChar
= mShiftedString
.CharAt(aIndex
+ i
- skipShiftedChars
);
3771 if (skipUnshiftedChars
<= aIndex
+ i
) {
3772 unshiftedChar
= mUnshiftedString
.CharAt(aIndex
+ i
- skipUnshiftedChars
);
3775 if (shiftedChar
|| unshiftedChar
) {
3776 AlternativeCharCode
chars(unshiftedChar
, shiftedChar
);
3777 altArray
.AppendElement(chars
);
3784 if (mUnshiftedLatinChar
|| mShiftedLatinChar
) {
3785 AlternativeCharCode
chars(mUnshiftedLatinChar
, mShiftedLatinChar
);
3786 altArray
.AppendElement(chars
);
3789 // Typically, following virtual keycodes are used for a key which can
3790 // input the character. However, these keycodes are also used for
3791 // other keys on some keyboard layout. E.g., in spite of Shift+'1'
3792 // inputs '+' on Thai keyboard layout, a key which is at '=/+'
3793 // key on ANSI keyboard layout is VK_OEM_PLUS. Native applications
3794 // handle it as '+' key if Ctrl key is pressed.
3795 char16_t charForOEMKeyCode
= 0;
3796 switch (mVirtualKeyCode
) {
3798 charForOEMKeyCode
= '+';
3801 charForOEMKeyCode
= ',';
3804 charForOEMKeyCode
= '-';
3807 charForOEMKeyCode
= '.';
3810 if (charForOEMKeyCode
&& charForOEMKeyCode
!= mUnshiftedString
.CharAt(0) &&
3811 charForOEMKeyCode
!= mShiftedString
.CharAt(0) &&
3812 charForOEMKeyCode
!= mUnshiftedLatinChar
&&
3813 charForOEMKeyCode
!= mShiftedLatinChar
) {
3814 AlternativeCharCode
OEMChars(charForOEMKeyCode
, charForOEMKeyCode
);
3815 altArray
.AppendElement(OEMChars
);
3820 /*****************************************************************************
3821 * mozilla::widget::KeyboardLayout
3822 *****************************************************************************/
3824 KeyboardLayout
* KeyboardLayout::sInstance
= nullptr;
3825 nsIUserIdleServiceInternal
* KeyboardLayout::sIdleService
= nullptr;
3828 KeyboardLayout
* KeyboardLayout::GetInstance() {
3830 sInstance
= new KeyboardLayout();
3831 nsCOMPtr
<nsIUserIdleServiceInternal
> idleService
=
3832 do_GetService("@mozilla.org/widget/useridleservice;1");
3833 // The refcount will be decreased at shut down.
3834 sIdleService
= idleService
.forget().take();
3840 void KeyboardLayout::Shutdown() {
3842 sInstance
= nullptr;
3843 NS_IF_RELEASE(sIdleService
);
3847 void KeyboardLayout::NotifyIdleServiceOfUserActivity() {
3848 sIdleService
->ResetIdleTimeOut(0);
3851 KeyboardLayout::KeyboardLayout()
3852 : mKeyboardLayout(0),
3853 mIsOverridden(false),
3854 mIsPendingToRestoreKeyboardLayout(false),
3856 mDeadKeyTableListHead
= nullptr;
3857 // A dead key sequence should be made from up to 5 keys. Therefore, 4 is
3858 // enough and makes sense because the item is uint8_t.
3859 // (Although, even if it's possible to be 6 keys or more in a sequence,
3860 // this array will be re-allocated).
3861 mActiveDeadKeys
.SetCapacity(4);
3862 mDeadKeyShiftStates
.SetCapacity(4);
3864 // NOTE: LoadLayout() should be called via OnLayoutChange().
3867 KeyboardLayout::~KeyboardLayout() { ReleaseDeadKeyTables(); }
3869 bool KeyboardLayout::IsPrintableCharKey(uint8_t aVirtualKey
) {
3870 return GetKeyIndex(aVirtualKey
) >= 0;
3873 WORD
KeyboardLayout::ComputeScanCodeForVirtualKeyCode(
3874 uint8_t aVirtualKeyCode
) const {
3875 return static_cast<WORD
>(
3876 ::MapVirtualKeyEx(aVirtualKeyCode
, MAPVK_VK_TO_VSC
, GetLayout()));
3879 bool KeyboardLayout::IsDeadKey(uint8_t aVirtualKey
,
3880 const ModifierKeyState
& aModKeyState
) const {
3881 int32_t virtualKeyIndex
= GetKeyIndex(aVirtualKey
);
3883 // XXX KeyboardLayout class doesn't support unusual keyboard layout which
3884 // maps some function keys as dead keys.
3885 if (virtualKeyIndex
< 0) {
3889 return mVirtualKeys
[virtualKeyIndex
].IsDeadKey(
3890 VirtualKey::ModifiersToShiftState(aModKeyState
.GetModifiers()));
3893 bool KeyboardLayout::IsSysKey(uint8_t aVirtualKey
,
3894 const ModifierKeyState
& aModKeyState
) const {
3895 // If Alt key is not pressed, it's never a system key combination.
3896 // Additionally, if Ctrl key is pressed, it's never a system key combination
3898 // FYI: Windows logo key state won't affect if it's a system key.
3899 if (!aModKeyState
.IsAlt() || aModKeyState
.IsControl()) {
3903 int32_t virtualKeyIndex
= GetKeyIndex(aVirtualKey
);
3904 if (virtualKeyIndex
< 0) {
3908 UniCharsAndModifiers inputCharsAndModifiers
=
3909 GetUniCharsAndModifiers(aVirtualKey
, aModKeyState
);
3910 if (inputCharsAndModifiers
.IsEmpty()) {
3914 // If the Alt key state isn't consumed, that means that the key with Alt
3915 // doesn't cause text input. So, the combination is a system key.
3916 return !!(inputCharsAndModifiers
.ModifiersAt(0) & MODIFIER_ALT
);
3919 void KeyboardLayout::InitNativeKey(NativeKey
& aNativeKey
) {
3920 if (mIsPendingToRestoreKeyboardLayout
) {
3921 LoadLayout(::GetKeyboardLayout(0));
3924 // If the aNativeKey is initialized with WM_CHAR, the key information
3925 // should be discarded because mKeyValue should have the string to be
3927 if (aNativeKey
.mMsg
.message
== WM_CHAR
) {
3928 char16_t ch
= static_cast<char16_t
>(aNativeKey
.mMsg
.wParam
);
3929 // But don't set key value as printable key if the character is a control
3930 // character such as 0x0D at pressing Enter key.
3931 if (!NativeKey::IsControlChar(ch
)) {
3932 aNativeKey
.mKeyNameIndex
= KEY_NAME_INDEX_USE_STRING
;
3933 Modifiers modifiers
=
3934 aNativeKey
.GetModifiers() & ~(MODIFIER_ALT
| MODIFIER_CONTROL
);
3935 aNativeKey
.mCommittedCharsAndModifiers
.Append(ch
, modifiers
);
3940 // If the aNativeKey is in a sequence to input a Unicode character with
3941 // Alt + numpad keys, we should just set the number as the inputting charcter.
3942 // Note that we should compute the key value from the virtual key code
3943 // because they may be mapped to alphabets, but they should be treated as
3944 // Alt + [0-9] even by web apps.
3945 // However, we shouldn't touch the key value if given virtual key code is
3946 // not a printable key because it may be synthesized by 3rd party utility
3947 // or just NumLock is unlocked and user tries to use shortcut key. In the
3948 // latter case, we cannot solve the conflict issue with Alt+foo shortcut key
3949 // and inputting a Unicode scalar value like reported to bug 1606655, though,
3950 // I have no better idea. Perhaps, `Alt` shouldn't be used for shortcut key
3951 // combination on Windows.
3952 if (aNativeKey
.MaybeTypingUnicodeScalarValue() &&
3953 KeyboardLayout::IsPrintableCharKey(aNativeKey
.mVirtualKeyCode
)) {
3954 // If the key code value is mapped to a Numpad key, let's compute the key
3955 // value with it. This is same behavior as Chrome. In strictly speaking,
3956 // I think that the else block's computation is better because it seems
3957 // that Windows does not refer virtual key code value, but we should avoid
3958 // web-compat issues.
3960 if (aNativeKey
.mVirtualKeyCode
>= VK_NUMPAD0
&&
3961 aNativeKey
.mVirtualKeyCode
<= VK_NUMPAD9
) {
3962 num
= '0' + aNativeKey
.mVirtualKeyCode
- VK_NUMPAD0
;
3964 // Otherwise, let's use fake key value for making never match with
3967 switch (aNativeKey
.mScanCode
) {
3968 case 0x0052: // Numpad0
3971 case 0x004F: // Numpad1
3974 case 0x0050: // Numpad2
3977 case 0x0051: // Numpad3
3980 case 0x004B: // Numpad4
3983 case 0x004C: // Numpad5
3986 case 0x004D: // Numpad6
3989 case 0x0047: // Numpad7
3992 case 0x0048: // Numpad8
3995 case 0x0049: // Numpad9
3999 MOZ_ASSERT_UNREACHABLE(
4000 "IsTypingUnicodeScalarValue() must have returned true for wrong "
4005 aNativeKey
.mCommittedCharsAndModifiers
.Append(num
,
4006 aNativeKey
.GetModifiers());
4007 aNativeKey
.mKeyNameIndex
= KEY_NAME_INDEX_USE_STRING
;
4011 // When it's followed by non-dead char message(s) for printable character(s),
4012 // aNativeKey should dispatch eKeyPress events for them rather than
4013 // information from keyboard layout because respecting WM_(SYS)CHAR messages
4014 // guarantees that we can always input characters which is expected by
4015 // the user even if the user uses odd keyboard layout.
4016 // Or, when it was followed by non-dead char message for a printable character
4017 // but it's gone at removing the message from the queue, let's treat it
4018 // as a key inputting empty string.
4019 if (aNativeKey
.IsFollowedByPrintableCharOrSysCharMessage() ||
4020 aNativeKey
.mCharMessageHasGone
) {
4021 MOZ_ASSERT(!aNativeKey
.IsCharMessage(aNativeKey
.mMsg
));
4022 if (aNativeKey
.IsFollowedByPrintableCharOrSysCharMessage()) {
4023 // Initialize mCommittedCharsAndModifiers with following char messages.
4024 aNativeKey
.InitCommittedCharsAndModifiersWithFollowingCharMessages();
4025 MOZ_ASSERT(!aNativeKey
.mCommittedCharsAndModifiers
.IsEmpty());
4027 // Currently, we are doing a ugly hack to keypress events to cause
4028 // inputting character even if Ctrl or Alt key is pressed, that is, we
4029 // remove Ctrl and Alt modifier state from keypress event. However, for
4030 // example, Ctrl+Space which causes ' ' of WM_CHAR message never causes
4031 // keypress event whose ctrlKey is true. For preventing this problem,
4032 // we should mark as not removable if Ctrl or Alt key does not cause
4033 // changing inputting character.
4034 if (IsPrintableCharKey(aNativeKey
.mOriginalVirtualKeyCode
) &&
4035 (aNativeKey
.IsControl() ^ aNativeKey
.IsAlt())) {
4036 ModifierKeyState state
= aNativeKey
.ModifierKeyStateRef();
4037 state
.Unset(MODIFIER_ALT
| MODIFIER_CONTROL
);
4038 UniCharsAndModifiers charsWithoutModifier
=
4039 GetUniCharsAndModifiers(aNativeKey
.GenericVirtualKeyCode(), state
);
4040 aNativeKey
.mCanIgnoreModifierStateAtKeyPress
=
4041 !charsWithoutModifier
.UniCharsEqual(
4042 aNativeKey
.mCommittedCharsAndModifiers
);
4045 aNativeKey
.mCommittedCharsAndModifiers
.Clear();
4047 aNativeKey
.mKeyNameIndex
= KEY_NAME_INDEX_USE_STRING
;
4049 // If it's not in dead key sequence, we don't need to do anymore here.
4050 if (!IsInDeadKeySequence()) {
4054 // If it's in dead key sequence and dead char is inputted as is, we need to
4055 // set the previous modifier state which is stored when preceding dead key
4057 UniCharsAndModifiers deadChars
= GetDeadUniCharsAndModifiers();
4058 aNativeKey
.mCommittedCharsAndModifiers
.OverwriteModifiersIfBeginsWith(
4060 // Finish the dead key sequence.
4061 DeactivateDeadKeyState();
4065 // If it's a dead key, aNativeKey will be initialized by
4066 // MaybeInitNativeKeyAsDeadKey().
4067 if (MaybeInitNativeKeyAsDeadKey(aNativeKey
)) {
4071 // If the key is not a usual printable key, KeyboardLayout class assume that
4072 // it's not cause dead char nor printable char. Therefore, there are nothing
4073 // to do here fore such keys (e.g., function keys).
4074 // However, this should keep dead key state even if non-printable key is
4075 // pressed during a dead key sequence.
4076 if (!IsPrintableCharKey(aNativeKey
.mOriginalVirtualKeyCode
)) {
4080 MOZ_ASSERT(aNativeKey
.mOriginalVirtualKeyCode
!= VK_PACKET
,
4081 "At handling VK_PACKET, we shouldn't refer keyboard layout");
4083 aNativeKey
.mKeyNameIndex
== KEY_NAME_INDEX_USE_STRING
,
4084 "Printable key's key name index must be KEY_NAME_INDEX_USE_STRING");
4086 // If it's in dead key handling and the pressed key causes a composite
4087 // character, aNativeKey will be initialized by
4088 // MaybeInitNativeKeyWithCompositeChar().
4089 if (MaybeInitNativeKeyWithCompositeChar(aNativeKey
)) {
4093 UniCharsAndModifiers baseChars
= GetUniCharsAndModifiers(aNativeKey
);
4095 // If the key press isn't related to any dead keys, initialize aNativeKey
4096 // with the characters which should be caused by the key.
4097 if (!IsInDeadKeySequence()) {
4098 aNativeKey
.mCommittedCharsAndModifiers
= baseChars
;
4102 // If the key doesn't cause a composite character with preceding dead key,
4103 // initialize aNativeKey with the dead-key character followed by current
4105 UniCharsAndModifiers deadChars
= GetDeadUniCharsAndModifiers();
4106 aNativeKey
.mCommittedCharsAndModifiers
= deadChars
+ baseChars
;
4107 if (aNativeKey
.IsKeyDownMessage()) {
4108 DeactivateDeadKeyState();
4112 bool KeyboardLayout::MaybeInitNativeKeyAsDeadKey(NativeKey
& aNativeKey
) {
4113 // Only when it's not in dead key sequence, we can trust IsDeadKey() result.
4114 if (!IsInDeadKeySequence() && !IsDeadKey(aNativeKey
)) {
4118 // When keydown message is followed by a dead char message, it should be
4119 // initialized as dead key.
4120 bool isDeadKeyDownEvent
=
4121 aNativeKey
.IsKeyDownMessage() && aNativeKey
.IsFollowedByDeadCharMessage();
4123 // When keyup message is received, let's check if it's one of preceding
4124 // dead keys because keydown message order and keyup message order may be
4126 bool isDeadKeyUpEvent
=
4127 !aNativeKey
.IsKeyDownMessage() &&
4128 mActiveDeadKeys
.Contains(aNativeKey
.GenericVirtualKeyCode());
4130 if (isDeadKeyDownEvent
|| isDeadKeyUpEvent
) {
4131 ActivateDeadKeyState(aNativeKey
);
4132 // Any dead key events don't generate characters. So, a dead key should
4133 // cause only keydown event and keyup event whose KeyboardEvent.key
4134 // values are "Dead".
4135 aNativeKey
.mCommittedCharsAndModifiers
.Clear();
4136 aNativeKey
.mKeyNameIndex
= KEY_NAME_INDEX_Dead
;
4140 // At keydown message handling, we need to forget the first dead key
4141 // because there is no guarantee coming WM_KEYUP for the second dead
4142 // key before next WM_KEYDOWN. E.g., due to auto key repeat or pressing
4143 // another dead key before releasing current key. Therefore, we can
4144 // set only a character for current key for keyup event.
4145 if (!IsInDeadKeySequence()) {
4146 aNativeKey
.mCommittedCharsAndModifiers
=
4147 GetUniCharsAndModifiers(aNativeKey
);
4151 // When non-printable key event comes during a dead key sequence, that must
4152 // be a modifier key event. So, such events shouldn't be handled as a part
4153 // of the dead key sequence.
4154 if (!IsDeadKey(aNativeKey
)) {
4158 // FYI: Following code may run when the user doesn't input text actually
4159 // but the key sequence is a dead key sequence. For example,
4160 // ` -> Ctrl+` with Spanish keyboard layout. Let's keep using this
4161 // complicated code for now because this runs really rarely.
4163 // Dead key followed by another dead key may cause a composed character
4164 // (e.g., "Russian - Mnemonic" keyboard layout's 's' -> 'c').
4165 if (MaybeInitNativeKeyWithCompositeChar(aNativeKey
)) {
4169 // Otherwise, dead key followed by another dead key causes inputting both
4171 UniCharsAndModifiers prevDeadChars
= GetDeadUniCharsAndModifiers();
4172 UniCharsAndModifiers newChars
= GetUniCharsAndModifiers(aNativeKey
);
4173 // But keypress events should be fired for each committed character.
4174 aNativeKey
.mCommittedCharsAndModifiers
= prevDeadChars
+ newChars
;
4175 if (aNativeKey
.IsKeyDownMessage()) {
4176 DeactivateDeadKeyState();
4181 bool KeyboardLayout::MaybeInitNativeKeyWithCompositeChar(
4182 NativeKey
& aNativeKey
) {
4183 if (!IsInDeadKeySequence()) {
4187 if (NS_WARN_IF(!IsPrintableCharKey(aNativeKey
.mOriginalVirtualKeyCode
))) {
4191 UniCharsAndModifiers baseChars
= GetUniCharsAndModifiers(aNativeKey
);
4192 if (baseChars
.IsEmpty() || !baseChars
.CharAt(0)) {
4196 char16_t compositeChar
= GetCompositeChar(baseChars
.CharAt(0));
4197 if (!compositeChar
) {
4201 // Active dead-key and base character does produce exactly one composite
4203 aNativeKey
.mCommittedCharsAndModifiers
.Append(compositeChar
,
4204 baseChars
.ModifiersAt(0));
4205 if (aNativeKey
.IsKeyDownMessage()) {
4206 DeactivateDeadKeyState();
4211 UniCharsAndModifiers
KeyboardLayout::GetUniCharsAndModifiers(
4212 uint8_t aVirtualKey
, VirtualKey::ShiftState aShiftState
) const {
4213 UniCharsAndModifiers result
;
4214 int32_t key
= GetKeyIndex(aVirtualKey
);
4218 return mVirtualKeys
[key
].GetUniChars(aShiftState
);
4221 UniCharsAndModifiers
KeyboardLayout::GetDeadUniCharsAndModifiers() const {
4222 MOZ_RELEASE_ASSERT(mActiveDeadKeys
.Length() == mDeadKeyShiftStates
.Length());
4224 if (NS_WARN_IF(mActiveDeadKeys
.IsEmpty())) {
4225 return UniCharsAndModifiers();
4228 UniCharsAndModifiers result
;
4229 for (size_t i
= 0; i
< mActiveDeadKeys
.Length(); ++i
) {
4231 GetUniCharsAndModifiers(mActiveDeadKeys
[i
], mDeadKeyShiftStates
[i
]);
4236 char16_t
KeyboardLayout::GetCompositeChar(char16_t aBaseChar
) const {
4237 if (NS_WARN_IF(mActiveDeadKeys
.IsEmpty())) {
4240 // XXX Currently, we don't support computing a composite character with
4241 // two or more dead keys since it needs big table for supporting
4242 // long chained dead keys. However, this should be a minor bug
4243 // because this runs only when the latest keydown event does not cause
4244 // WM_(SYS)CHAR messages. So, when user wants to input a character,
4245 // this path never runs.
4246 if (mActiveDeadKeys
.Length() > 1) {
4249 int32_t key
= GetKeyIndex(mActiveDeadKeys
[0]);
4253 return mVirtualKeys
[key
].GetCompositeChar(mDeadKeyShiftStates
[0], aBaseChar
);
4257 HKL
KeyboardLayout::GetActiveLayout() { return GetInstance()->mKeyboardLayout
; }
4260 nsCString
KeyboardLayout::GetActiveLayoutName() {
4261 return GetInstance()->GetLayoutName(GetActiveLayout());
4264 static bool IsValidKeyboardLayoutsChild(const nsAString
& aChildName
) {
4265 if (aChildName
.Length() != 8) {
4268 for (size_t i
= 0; i
< aChildName
.Length(); i
++) {
4269 if ((aChildName
[i
] >= '0' && aChildName
[i
] <= '9') ||
4270 (aChildName
[i
] >= 'a' && aChildName
[i
] <= 'f') ||
4271 (aChildName
[i
] >= 'A' && aChildName
[i
] <= 'F')) {
4279 nsCString
KeyboardLayout::GetLayoutName(HKL aLayout
) const {
4280 const wchar_t kKeyboardLayouts
[] =
4281 L
"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\";
4282 uint16_t language
= reinterpret_cast<uintptr_t>(aLayout
) & 0xFFFF;
4283 uint16_t layout
= (reinterpret_cast<uintptr_t>(aLayout
) >> 16) & 0xFFFF;
4284 // If the layout is less than 0xA000XXXX (normal keyboard layout for the
4285 // language) or 0xEYYYXXXX (IMM-IME), we can retrieve its name simply.
4286 if (layout
< 0xA000 || (layout
& 0xF000) == 0xE000) {
4287 nsAutoString
key(kKeyboardLayouts
);
4288 key
.AppendPrintf("%08" PRIXPTR
, layout
< 0xA000
4290 : reinterpret_cast<uintptr_t>(aLayout
));
4292 if (NS_WARN_IF(!WinUtils::GetRegistryKey(
4293 HKEY_LOCAL_MACHINE
, key
.get(), L
"Layout Text", buf
, sizeof(buf
)))) {
4294 return "No name or too long name"_ns
;
4296 return NS_ConvertUTF16toUTF8(buf
);
4299 if (NS_WARN_IF((layout
& 0xF000) != 0xF000)) {
4301 result
.AppendPrintf("Odd HKL: 0x%08" PRIXPTR
,
4302 reinterpret_cast<uintptr_t>(aLayout
));
4306 // Otherwise, we need to walk the registry under "Keyboard Layouts".
4307 nsCOMPtr
<nsIWindowsRegKey
> regKey
=
4308 do_CreateInstance("@mozilla.org/windows-registry-key;1");
4309 if (NS_WARN_IF(!regKey
)) {
4313 regKey
->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE
,
4314 nsString(kKeyboardLayouts
), nsIWindowsRegKey::ACCESS_READ
);
4315 if (NS_WARN_IF(NS_FAILED(rv
))) {
4318 uint32_t childCount
= 0;
4319 if (NS_WARN_IF(NS_FAILED(regKey
->GetChildCount(&childCount
))) ||
4320 NS_WARN_IF(!childCount
)) {
4323 for (uint32_t i
= 0; i
< childCount
; i
++) {
4324 nsAutoString childName
;
4325 if (NS_WARN_IF(NS_FAILED(regKey
->GetChildName(i
, childName
))) ||
4326 !IsValidKeyboardLayoutsChild(childName
)) {
4329 uint32_t childNum
= static_cast<uint32_t>(childName
.ToInteger64(&rv
, 16));
4330 if (NS_WARN_IF(NS_FAILED(rv
))) {
4333 // Ignore normal keyboard layouts for each language.
4334 if (childNum
<= 0xFFFF) {
4337 // If it doesn't start with 'A' nor 'a', language should be matched.
4338 if ((childNum
& 0xFFFF) != language
&&
4339 (childNum
& 0xF0000000) != 0xA0000000) {
4342 // Then, the child should have "Layout Id" which is "YYY" of 0xFYYYXXXX.
4343 nsAutoString
key(kKeyboardLayouts
);
4346 if (NS_WARN_IF(!WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE
, key
.get(),
4347 L
"Layout Id", buf
, sizeof(buf
)))) {
4350 uint16_t layoutId
= wcstol(buf
, nullptr, 16);
4351 if (layoutId
!= (layout
& 0x0FFF)) {
4354 if (NS_WARN_IF(!WinUtils::GetRegistryKey(
4355 HKEY_LOCAL_MACHINE
, key
.get(), L
"Layout Text", buf
, sizeof(buf
)))) {
4358 return NS_ConvertUTF16toUTF8(buf
);
4363 void KeyboardLayout::LoadLayout(HKL aLayout
) {
4364 mIsPendingToRestoreKeyboardLayout
= false;
4366 if (mKeyboardLayout
== aLayout
) {
4370 mKeyboardLayout
= aLayout
;
4373 MOZ_LOG(gKeyLog
, LogLevel::Info
,
4374 ("KeyboardLayout::LoadLayout(aLayout=0x%p (%s))", aLayout
,
4375 GetLayoutName(aLayout
).get()));
4378 memset(kbdState
, 0, sizeof(kbdState
));
4380 BYTE originalKbdState
[256];
4381 // Bitfield with all shift states that have at least one dead-key.
4382 uint16_t shiftStatesWithDeadKeys
= 0;
4383 // Bitfield with all shift states that produce any possible dead-key base
4385 uint16_t shiftStatesWithBaseChars
= 0;
4387 mActiveDeadKeys
.Clear();
4388 mDeadKeyShiftStates
.Clear();
4390 ReleaseDeadKeyTables();
4392 ::GetKeyboardState(originalKbdState
);
4394 // For each shift state gather all printable characters that are produced
4395 // for normal case when no any dead-key is active.
4397 for (VirtualKey::ShiftState shiftState
= 0; shiftState
< 16; shiftState
++) {
4398 VirtualKey::FillKbdState(kbdState
, shiftState
);
4399 bool isAltGr
= VirtualKey::IsAltGrIndex(shiftState
);
4400 for (uint32_t virtualKey
= 0; virtualKey
< 256; virtualKey
++) {
4401 int32_t vki
= GetKeyIndex(virtualKey
);
4405 NS_ASSERTION(uint32_t(vki
) < ArrayLength(mVirtualKeys
), "invalid index");
4406 char16_t uniChars
[5];
4407 int32_t ret
= ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)uniChars
,
4408 ArrayLength(uniChars
), 0, mKeyboardLayout
);
4411 shiftStatesWithDeadKeys
|= (1 << shiftState
);
4412 // Repeat dead-key to deactivate it and get its character
4414 char16_t deadChar
[2];
4415 ret
= ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)deadChar
,
4416 ArrayLength(deadChar
), 0, mKeyboardLayout
);
4417 NS_ASSERTION(ret
== 2, "Expecting twice repeated dead-key character");
4418 mVirtualKeys
[vki
].SetDeadChar(shiftState
, deadChar
[0]);
4420 MOZ_LOG(gKeyLog
, LogLevel::Verbose
,
4421 (" %s (%d): DeadChar(%s, %s) (ret=%d)",
4422 kVirtualKeyName
[virtualKey
], vki
,
4423 GetShiftStateName(shiftState
).get(),
4424 GetCharacterCodeNames(deadChar
, 1).get(), ret
));
4427 // dead-key can pair only with exactly one base character.
4428 shiftStatesWithBaseChars
|= (1 << shiftState
);
4430 mVirtualKeys
[vki
].SetNormalChars(shiftState
, uniChars
, ret
);
4431 MOZ_LOG(gKeyLog
, LogLevel::Verbose
,
4432 (" %s (%d): NormalChar(%s, %s) (ret=%d)",
4433 kVirtualKeyName
[virtualKey
], vki
,
4434 GetShiftStateName(shiftState
).get(),
4435 GetCharacterCodeNames(uniChars
, ret
).get(), ret
));
4438 // If the key inputs at least one character with AltGr modifier,
4439 // check if AltGr changes inputting character. If it does, mark
4440 // this keyboard layout has AltGr modifier actually.
4441 if (!mHasAltGr
&& ret
> 0 && isAltGr
&&
4442 mVirtualKeys
[vki
].IsChangedByAltGr(shiftState
)) {
4444 MOZ_LOG(gKeyLog
, LogLevel::Info
,
4445 (" Found a key (%s) changed by AltGr: %s -> %s (%s) (ret=%d)",
4446 kVirtualKeyName
[virtualKey
],
4447 GetCharacterCodeNames(
4448 mVirtualKeys
[vki
].GetNativeUniChars(
4449 shiftState
- VirtualKey::ShiftStateIndex::eAltGr
))
4451 GetCharacterCodeNames(
4452 mVirtualKeys
[vki
].GetNativeUniChars(shiftState
))
4454 GetShiftStateName(shiftState
).get(), ret
));
4459 // Now process each dead-key to find all its base characters and resulting
4460 // composite characters.
4461 for (VirtualKey::ShiftState shiftState
= 0; shiftState
< 16; shiftState
++) {
4462 if (!(shiftStatesWithDeadKeys
& (1 << shiftState
))) {
4466 VirtualKey::FillKbdState(kbdState
, shiftState
);
4468 for (uint32_t virtualKey
= 0; virtualKey
< 256; virtualKey
++) {
4469 int32_t vki
= GetKeyIndex(virtualKey
);
4470 if (vki
>= 0 && mVirtualKeys
[vki
].IsDeadKey(shiftState
)) {
4471 DeadKeyEntry deadKeyArray
[256];
4472 int32_t n
= GetDeadKeyCombinations(
4473 virtualKey
, kbdState
, shiftStatesWithBaseChars
, deadKeyArray
,
4474 ArrayLength(deadKeyArray
));
4475 const DeadKeyTable
* dkt
=
4476 mVirtualKeys
[vki
].MatchingDeadKeyTable(deadKeyArray
, n
);
4478 dkt
= AddDeadKeyTable(deadKeyArray
, n
);
4480 mVirtualKeys
[vki
].AttachDeadKeyTable(shiftState
, dkt
);
4485 ::SetKeyboardState(originalKbdState
);
4487 if (MOZ_LOG_TEST(gKeyLog
, LogLevel::Verbose
)) {
4488 static const UINT kExtendedScanCode
[] = {0x0000, 0xE000};
4489 static const UINT kMapType
= MAPVK_VSC_TO_VK_EX
;
4490 MOZ_LOG(gKeyLog
, LogLevel::Verbose
,
4491 ("Logging virtual keycode values for scancode (0x%p)...",
4493 for (uint32_t i
= 0; i
< ArrayLength(kExtendedScanCode
); i
++) {
4494 for (uint32_t j
= 1; j
<= 0xFF; j
++) {
4495 UINT scanCode
= kExtendedScanCode
[i
] + j
;
4496 UINT virtualKeyCode
=
4497 ::MapVirtualKeyEx(scanCode
, kMapType
, mKeyboardLayout
);
4498 MOZ_LOG(gKeyLog
, LogLevel::Verbose
,
4499 ("0x%04X, %s", scanCode
, kVirtualKeyName
[virtualKeyCode
]));
4504 MOZ_LOG(gKeyLog
, LogLevel::Info
,
4505 (" AltGr key is %s in %s", mHasAltGr
? "found" : "not found",
4506 GetLayoutName(aLayout
).get()));
4509 inline int32_t KeyboardLayout::GetKeyIndex(uint8_t aVirtualKey
) {
4510 // Currently these 68 (NS_NUM_OF_KEYS) virtual keys are assumed
4511 // to produce visible representation:
4512 // 0x20 - VK_SPACE ' '
4513 // 0x30..0x39 '0'..'9'
4514 // 0x41..0x5A 'A'..'Z'
4515 // 0x60..0x69 '0'..'9' on numpad
4516 // 0x6A - VK_MULTIPLY '*' on numpad
4517 // 0x6B - VK_ADD '+' on numpad
4518 // 0x6D - VK_SUBTRACT '-' on numpad
4519 // 0x6E - VK_DECIMAL '.' on numpad
4520 // 0x6F - VK_DIVIDE '/' on numpad
4521 // 0x6E - VK_DECIMAL '.'
4522 // 0xBA - VK_OEM_1 ';:' for US
4523 // 0xBB - VK_OEM_PLUS '+' any country
4524 // 0xBC - VK_OEM_COMMA ',' any country
4525 // 0xBD - VK_OEM_MINUS '-' any country
4526 // 0xBE - VK_OEM_PERIOD '.' any country
4527 // 0xBF - VK_OEM_2 '/?' for US
4528 // 0xC0 - VK_OEM_3 '`~' for US
4529 // 0xC1 - VK_ABNT_C1 '/?' for Brazilian
4530 // 0xC2 - VK_ABNT_C2 separator key on numpad (Brazilian or JIS for Mac)
4531 // 0xDB - VK_OEM_4 '[{' for US
4532 // 0xDC - VK_OEM_5 '\|' for US
4533 // 0xDD - VK_OEM_6 ']}' for US
4534 // 0xDE - VK_OEM_7 ''"' for US
4537 // 0xE2 - VK_OEM_102 '\_' for JIS
4541 static const int8_t xlat
[256] = {
4542 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
4543 //-----------------------------------------------------------------------
4544 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00
4545 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10
4546 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20
4547 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, // 30
4548 -1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 40
4549 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, -1, -1, // 50
4550 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, -1, 49, 50, 51, // 60
4551 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 70
4552 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
4553 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90
4554 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0
4555 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 53, 54, 55, 56, 57, // B0
4556 58, 59, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0
4557 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 61, 62, 63, 64, 65, // D0
4558 -1, 66, 67, 68, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0
4559 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // F0
4562 return xlat
[aVirtualKey
];
4565 int KeyboardLayout::CompareDeadKeyEntries(const void* aArg1
, const void* aArg2
,
4567 const DeadKeyEntry
* arg1
= static_cast<const DeadKeyEntry
*>(aArg1
);
4568 const DeadKeyEntry
* arg2
= static_cast<const DeadKeyEntry
*>(aArg2
);
4570 return arg1
->BaseChar
- arg2
->BaseChar
;
4573 const DeadKeyTable
* KeyboardLayout::AddDeadKeyTable(
4574 const DeadKeyEntry
* aDeadKeyArray
, uint32_t aEntries
) {
4575 DeadKeyTableListEntry
* next
= mDeadKeyTableListHead
;
4577 const size_t bytes
= offsetof(DeadKeyTableListEntry
, data
) +
4578 DeadKeyTable::SizeInBytes(aEntries
);
4579 uint8_t* p
= new uint8_t[bytes
];
4581 mDeadKeyTableListHead
= reinterpret_cast<DeadKeyTableListEntry
*>(p
);
4582 mDeadKeyTableListHead
->next
= next
;
4585 reinterpret_cast<DeadKeyTable
*>(mDeadKeyTableListHead
->data
);
4587 dkt
->Init(aDeadKeyArray
, aEntries
);
4592 void KeyboardLayout::ReleaseDeadKeyTables() {
4593 while (mDeadKeyTableListHead
) {
4594 uint8_t* p
= reinterpret_cast<uint8_t*>(mDeadKeyTableListHead
);
4595 mDeadKeyTableListHead
= mDeadKeyTableListHead
->next
;
4601 bool KeyboardLayout::EnsureDeadKeyActive(bool aIsActive
, uint8_t aDeadKey
,
4602 const PBYTE aDeadKeyKbdState
) {
4605 char16_t dummyChars
[5];
4607 ::ToUnicodeEx(aDeadKey
, 0, (PBYTE
)aDeadKeyKbdState
, (LPWSTR
)dummyChars
,
4608 ArrayLength(dummyChars
), 0, mKeyboardLayout
);
4610 // <0 - Dead key state is active. The keyboard driver will wait for next
4612 // 1 - Previous pressed key was a valid base character that produced
4613 // exactly one composite character.
4614 // >1 - Previous pressed key does not produce any composite characters.
4615 // Return dead-key character followed by base character(s).
4616 } while ((ret
< 0) != aIsActive
);
4621 void KeyboardLayout::ActivateDeadKeyState(const NativeKey
& aNativeKey
) {
4622 // Dead-key state should be activated at keydown.
4623 if (!aNativeKey
.IsKeyDownMessage()) {
4627 mActiveDeadKeys
.AppendElement(aNativeKey
.mOriginalVirtualKeyCode
);
4628 mDeadKeyShiftStates
.AppendElement(aNativeKey
.GetShiftState());
4631 void KeyboardLayout::DeactivateDeadKeyState() {
4632 if (mActiveDeadKeys
.IsEmpty()) {
4637 memset(kbdState
, 0, sizeof(kbdState
));
4639 // Assume that the last dead key can finish dead key sequence.
4640 VirtualKey::FillKbdState(kbdState
, mDeadKeyShiftStates
.LastElement());
4641 EnsureDeadKeyActive(false, mActiveDeadKeys
.LastElement(), kbdState
);
4642 mActiveDeadKeys
.Clear();
4643 mDeadKeyShiftStates
.Clear();
4646 bool KeyboardLayout::AddDeadKeyEntry(char16_t aBaseChar
,
4647 char16_t aCompositeChar
,
4648 DeadKeyEntry
* aDeadKeyArray
,
4649 uint32_t aEntries
) {
4650 for (uint32_t index
= 0; index
< aEntries
; index
++) {
4651 if (aDeadKeyArray
[index
].BaseChar
== aBaseChar
) {
4656 aDeadKeyArray
[aEntries
].BaseChar
= aBaseChar
;
4657 aDeadKeyArray
[aEntries
].CompositeChar
= aCompositeChar
;
4662 uint32_t KeyboardLayout::GetDeadKeyCombinations(
4663 uint8_t aDeadKey
, const PBYTE aDeadKeyKbdState
,
4664 uint16_t aShiftStatesWithBaseChars
, DeadKeyEntry
* aDeadKeyArray
,
4665 uint32_t aMaxEntries
) {
4666 bool deadKeyActive
= false;
4667 uint32_t entries
= 0;
4669 memset(kbdState
, 0, sizeof(kbdState
));
4671 for (uint32_t shiftState
= 0; shiftState
< 16; shiftState
++) {
4672 if (!(aShiftStatesWithBaseChars
& (1 << shiftState
))) {
4676 VirtualKey::FillKbdState(kbdState
, shiftState
);
4678 for (uint32_t virtualKey
= 0; virtualKey
< 256; virtualKey
++) {
4679 int32_t vki
= GetKeyIndex(virtualKey
);
4680 // Dead-key can pair only with such key that produces exactly one base
4683 mVirtualKeys
[vki
].GetNativeUniChars(shiftState
).Length() == 1) {
4684 // Ensure dead-key is in active state, when it swallows entered
4685 // character and waits for the next pressed key.
4686 if (!deadKeyActive
) {
4687 deadKeyActive
= EnsureDeadKeyActive(true, aDeadKey
, aDeadKeyKbdState
);
4690 // Depending on the character the followed the dead-key, the keyboard
4691 // driver can produce one composite character, or a dead-key character
4692 // followed by a second character.
4693 char16_t compositeChars
[5];
4695 ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)compositeChars
,
4696 ArrayLength(compositeChars
), 0, mKeyboardLayout
);
4699 // This key combination does not produce any characters. The
4700 // dead-key is still in active state.
4703 char16_t baseChars
[5];
4704 ret
= ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)baseChars
,
4705 ArrayLength(baseChars
), 0, mKeyboardLayout
);
4706 if (entries
< aMaxEntries
) {
4709 // Exactly one composite character produced. Now, when
4710 // dead-key is not active, repeat the last character one more
4711 // time to determine the base character.
4712 if (AddDeadKeyEntry(baseChars
[0], compositeChars
[0],
4713 aDeadKeyArray
, entries
)) {
4716 deadKeyActive
= false;
4719 // If pressing another dead-key produces different character,
4720 // we should register the dead-key entry with first character
4721 // produced by current key.
4723 // First inactivate the dead-key state completely.
4725 EnsureDeadKeyActive(false, aDeadKey
, aDeadKeyKbdState
);
4726 if (NS_WARN_IF(deadKeyActive
)) {
4727 MOZ_LOG(gKeyLog
, LogLevel::Error
,
4728 (" failed to deactivating the dead-key state..."));
4731 for (int32_t i
= 0; i
< 5; ++i
) {
4732 ret
= ::ToUnicodeEx(
4733 virtualKey
, 0, kbdState
, (LPWSTR
)baseChars
,
4734 ArrayLength(baseChars
), 0, mKeyboardLayout
);
4740 AddDeadKeyEntry(baseChars
[0], compositeChars
[0],
4741 aDeadKeyArray
, entries
)) {
4744 // Inactivate dead-key state for current virtual keycode.
4745 EnsureDeadKeyActive(false, virtualKey
, kbdState
);
4749 NS_WARNING("File a bug for this dead-key handling!");
4750 deadKeyActive
= false;
4755 gKeyLog
, LogLevel::Verbose
,
4756 (" %s -> %s (%d): DeadKeyEntry(%s, %s) (ret=%d)",
4757 kVirtualKeyName
[aDeadKey
], kVirtualKeyName
[virtualKey
], vki
,
4758 GetCharacterCodeNames(compositeChars
, 1).get(),
4761 : GetCharacterCodeNames(baseChars
, std::min(ret
, 5)).get(),
4766 // 1. Unexpected dead-key. Dead-key chaining is not supported.
4767 // 2. More than one character generated. This is not a valid
4768 // dead-key and base character combination.
4769 deadKeyActive
= false;
4771 gKeyLog
, LogLevel::Verbose
,
4772 (" %s -> %s (%d): Unsupport dead key type(%s) (ret=%d)",
4773 kVirtualKeyName
[aDeadKey
], kVirtualKeyName
[virtualKey
], vki
,
4776 : GetCharacterCodeNames(compositeChars
, std::min(ret
, 5))
4785 if (deadKeyActive
) {
4786 deadKeyActive
= EnsureDeadKeyActive(false, aDeadKey
, aDeadKeyKbdState
);
4789 NS_QuickSort(aDeadKeyArray
, entries
, sizeof(DeadKeyEntry
),
4790 CompareDeadKeyEntries
, nullptr);
4794 uint32_t KeyboardLayout::ConvertNativeKeyCodeToDOMKeyCode(
4795 UINT aNativeKeyCode
) const {
4796 // Alphabet or Numeric or Numpad or Function keys
4797 if ((aNativeKeyCode
>= 0x30 && aNativeKeyCode
<= 0x39) ||
4798 (aNativeKeyCode
>= 0x41 && aNativeKeyCode
<= 0x5A) ||
4799 (aNativeKeyCode
>= 0x60 && aNativeKeyCode
<= 0x87)) {
4800 return static_cast<uint32_t>(aNativeKeyCode
);
4802 switch (aNativeKeyCode
) {
4803 // Following keycodes are same as our DOM keycodes
4811 case VK_MENU
: // Alt
4813 case VK_CAPITAL
: // CAPS LOCK
4814 case VK_KANA
: // same as VK_HANGUL
4817 case VK_HANJA
: // same as VK_KANJI
4824 case VK_PRIOR
: // PAGE UP
4825 case VK_NEXT
: // PAGE DOWN
4838 case VK_APPS
: // Context Menu
4841 case VK_SCROLL
: // SCROLL LOCK
4842 case VK_ATTN
: // Attension key of IBM midrange computers, e.g., AS/400
4843 case VK_CRSEL
: // Cursor Selection
4844 case VK_EXSEL
: // Extend Selection
4845 case VK_EREOF
: // Erase EOF key of IBM 3270 keyboard layout
4848 case VK_PA1
: // PA1 key of IBM 3270 keyboard layout
4849 return uint32_t(aNativeKeyCode
);
4854 // Windows key should be mapped to a Win keycode
4855 // They should be able to be distinguished by DOM3 KeyboardEvent.location
4860 case VK_VOLUME_MUTE
:
4861 return NS_VK_VOLUME_MUTE
;
4862 case VK_VOLUME_DOWN
:
4863 return NS_VK_VOLUME_DOWN
;
4865 return NS_VK_VOLUME_UP
;
4873 return NS_VK_CONTROL
;
4875 // Note that even if the key is AltGr, we should return NS_VK_ALT for
4876 // compatibility with both older Gecko and the other browsers.
4881 // Following keycodes are not defined in our DOM keycodes.
4882 case VK_BROWSER_BACK
:
4883 case VK_BROWSER_FORWARD
:
4884 case VK_BROWSER_REFRESH
:
4885 case VK_BROWSER_STOP
:
4886 case VK_BROWSER_SEARCH
:
4887 case VK_BROWSER_FAVORITES
:
4888 case VK_BROWSER_HOME
:
4889 case VK_MEDIA_NEXT_TRACK
:
4890 case VK_MEDIA_PREV_TRACK
:
4892 case VK_MEDIA_PLAY_PAUSE
:
4893 case VK_LAUNCH_MAIL
:
4894 case VK_LAUNCH_MEDIA_SELECT
:
4895 case VK_LAUNCH_APP1
:
4896 case VK_LAUNCH_APP2
:
4899 // Following OEM specific virtual keycodes should pass through DOM keyCode
4900 // for compatibility with the other browsers on Windows.
4902 // Following OEM specific virtual keycodes are defined for Fujitsu/OASYS.
4903 case VK_OEM_FJ_JISHO
:
4904 case VK_OEM_FJ_MASSHOU
:
4905 case VK_OEM_FJ_TOUROKU
:
4906 case VK_OEM_FJ_LOYA
:
4907 case VK_OEM_FJ_ROYA
:
4908 // Not sure what means "ICO".
4912 // Following OEM specific virtual keycodes are defined for Nokia/Ericsson.
4925 case VK_OEM_BACKTAB
:
4926 // VK_OEM_CLEAR is defined as not OEM specific, but let's pass though
4927 // DOM keyCode like other OEM specific virtual keycodes.
4929 return uint32_t(aNativeKeyCode
);
4931 // 0xE1 is an OEM specific virtual keycode. However, the value is already
4932 // used in our DOM keyCode for AltGr on Linux. So, this virtual keycode
4933 // cannot pass through DOM keyCode.
4937 // Following keycodes are OEM keys which are keycodes for non-alphabet and
4938 // non-numeric keys, we should compute each keycode of them from unshifted
4939 // character which is inputted by each key. But if the unshifted character
4940 // is not an ASCII character but shifted character is an ASCII character,
4941 // we should refer it.
4956 NS_ASSERTION(IsPrintableCharKey(aNativeKeyCode
),
4957 "The key must be printable");
4958 ModifierKeyState
modKeyState(0);
4959 UniCharsAndModifiers uniChars
=
4960 GetUniCharsAndModifiers(aNativeKeyCode
, modKeyState
);
4961 if (uniChars
.Length() != 1 || uniChars
.CharAt(0) < ' ' ||
4962 uniChars
.CharAt(0) > 0x7F) {
4963 modKeyState
.Set(MODIFIER_SHIFT
);
4964 uniChars
= GetUniCharsAndModifiers(aNativeKeyCode
, modKeyState
);
4965 if (uniChars
.Length() != 1 || uniChars
.CharAt(0) < ' ' ||
4966 uniChars
.CharAt(0) > 0x7F) {
4967 // In this case, we've returned 0 in this case for long time because
4968 // we decided that we should avoid setting same keyCode value to 2 or
4969 // more keys since active keyboard layout may have a key to input the
4970 // punctuation with different key. However, setting keyCode to 0
4971 // makes some web applications which are aware of neither
4972 // KeyboardEvent.key nor KeyboardEvent.code not work with Firefox
4973 // when user selects non-ASCII capable keyboard layout such as
4974 // Russian and Thai layout. So, let's decide keyCode value with
4975 // major keyboard layout's key which causes the OEM keycode.
4976 // Actually, this maps same keyCode value to 2 keys on Russian
4977 // keyboard layout. "Period" key causes VK_OEM_PERIOD but inputs
4978 // Yu of Cyrillic and "Slash" key causes VK_OEM_2 (same as US
4979 // keyboard layout) but inputs "." (period of ASCII). Therefore,
4980 // we return DOM_VK_PERIOD which is same as VK_OEM_PERIOD for
4981 // "Period" key. On the other hand, we use same keyCode value for
4982 // "Slash" key too because it inputs ".".
4984 switch (aNativeKeyCode
) {
4986 code
= CODE_NAME_INDEX_Semicolon
;
4989 code
= CODE_NAME_INDEX_Equal
;
4992 code
= CODE_NAME_INDEX_Comma
;
4995 code
= CODE_NAME_INDEX_Minus
;
4998 code
= CODE_NAME_INDEX_Period
;
5001 code
= CODE_NAME_INDEX_Slash
;
5004 code
= CODE_NAME_INDEX_Backquote
;
5007 code
= CODE_NAME_INDEX_BracketLeft
;
5010 code
= CODE_NAME_INDEX_Backslash
;
5013 code
= CODE_NAME_INDEX_BracketRight
;
5016 code
= CODE_NAME_INDEX_Quote
;
5019 // Use keyCode value for "Backquote" key on UK keyboard layout.
5020 code
= CODE_NAME_INDEX_Backquote
;
5023 // Use keyCode value for "IntlBackslash" key.
5024 code
= CODE_NAME_INDEX_IntlBackslash
;
5026 case VK_ABNT_C1
: // "/" of ABNT.
5027 // Use keyCode value for "IntlBackslash" key on ABNT keyboard
5029 code
= CODE_NAME_INDEX_IntlBackslash
;
5032 MOZ_ASSERT_UNREACHABLE("Handle all OEM keycode values");
5035 return WidgetKeyboardEvent::GetFallbackKeyCodeOfPunctuationKey(code
);
5038 return WidgetUtils::ComputeKeyCodeFromChar(uniChars
.CharAt(0));
5041 // IE sets 0xC2 to the DOM keyCode for VK_ABNT_C2. However, we're already
5042 // using NS_VK_SEPARATOR for the separator key on Mac and Linux. Therefore,
5043 // We should keep consistency between Gecko on all platforms rather than
5044 // with other browsers since a lot of keyCode values are already different
5045 // between browsers.
5047 return NS_VK_SEPARATOR
;
5049 // VK_PROCESSKEY means IME already consumed the key event.
5051 return NS_VK_PROCESSKEY
;
5052 // VK_PACKET is generated by SendInput() API, we don't need to
5053 // care this message as key event.
5056 // If a key is not mapped to a virtual keycode, 0xFF is used.
5058 NS_WARNING("The key is failed to be converted to a virtual keycode");
5062 nsPrintfCString
warning(
5063 "Unknown virtual keycode (0x%08X), please check the "
5064 "latest MSDN document, there may be some new "
5065 "keycodes we've never known.",
5067 NS_WARNING(warning
.get());
5072 KeyNameIndex
KeyboardLayout::ConvertNativeKeyCodeToKeyNameIndex(
5073 uint8_t aVirtualKey
) const {
5074 if (IsPrintableCharKey(aVirtualKey
) || aVirtualKey
== VK_PACKET
) {
5075 return KEY_NAME_INDEX_USE_STRING
;
5078 // If the keyboard layout has AltGr and AltRight key is pressed,
5080 if (aVirtualKey
== VK_RMENU
&& HasAltGr()) {
5081 return KEY_NAME_INDEX_AltGraph
;
5084 switch (aVirtualKey
) {
5085 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5086 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5088 return aKeyNameIndex;
5090 #include "NativeKeyToDOMKeyName.h"
5092 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5098 HKL layout
= GetLayout();
5099 WORD langID
= LOWORD(static_cast<HKL
>(layout
));
5100 WORD primaryLangID
= PRIMARYLANGID(langID
);
5102 if (primaryLangID
== LANG_JAPANESE
) {
5103 switch (aVirtualKey
) {
5104 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5105 #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, \
5108 return aKeyNameIndex;
5110 #include "NativeKeyToDOMKeyName.h"
5112 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5117 } else if (primaryLangID
== LANG_KOREAN
) {
5118 switch (aVirtualKey
) {
5119 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5120 #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5122 return aKeyNameIndex;
5124 #include "NativeKeyToDOMKeyName.h"
5126 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5129 return KEY_NAME_INDEX_Unidentified
;
5133 switch (aVirtualKey
) {
5134 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5135 #define NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5137 return aKeyNameIndex;
5139 #include "NativeKeyToDOMKeyName.h"
5141 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5144 return KEY_NAME_INDEX_Unidentified
;
5149 CodeNameIndex
KeyboardLayout::ConvertScanCodeToCodeNameIndex(UINT aScanCode
) {
5150 switch (aScanCode
) {
5151 #define NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX(aNativeKey, aCodeNameIndex) \
5153 return aCodeNameIndex;
5155 #include "NativeKeyToDOMCodeName.h"
5157 #undef NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX
5160 return CODE_NAME_INDEX_UNKNOWN
;
5164 nsresult
KeyboardLayout::SynthesizeNativeKeyEvent(
5165 nsWindow
* aWidget
, int32_t aNativeKeyboardLayout
, int32_t aNativeKeyCode
,
5166 uint32_t aModifierFlags
, const nsAString
& aCharacters
,
5167 const nsAString
& aUnmodifiedCharacters
) {
5168 UINT keyboardLayoutListCount
= ::GetKeyboardLayoutList(0, nullptr);
5169 NS_ASSERTION(keyboardLayoutListCount
> 0,
5170 "One keyboard layout must be installed at least");
5171 HKL keyboardLayoutListBuff
[50];
5172 HKL
* keyboardLayoutList
= keyboardLayoutListCount
< 50
5173 ? keyboardLayoutListBuff
5174 : new HKL
[keyboardLayoutListCount
];
5175 keyboardLayoutListCount
=
5176 ::GetKeyboardLayoutList(keyboardLayoutListCount
, keyboardLayoutList
);
5177 NS_ASSERTION(keyboardLayoutListCount
> 0,
5178 "Failed to get all keyboard layouts installed on the system");
5180 nsPrintfCString
layoutName("%08x", aNativeKeyboardLayout
);
5181 HKL loadedLayout
= LoadKeyboardLayoutA(layoutName
.get(), KLF_NOTELLSHELL
);
5182 if (loadedLayout
== nullptr) {
5183 if (keyboardLayoutListBuff
!= keyboardLayoutList
) {
5184 delete[] keyboardLayoutList
;
5186 return NS_ERROR_NOT_AVAILABLE
;
5189 // Setup clean key state and load desired layout
5190 BYTE originalKbdState
[256];
5191 ::GetKeyboardState(originalKbdState
);
5193 memset(kbdState
, 0, sizeof(kbdState
));
5194 // This changes the state of the keyboard for the current thread only,
5195 // and we'll restore it soon, so this should be OK.
5196 ::SetKeyboardState(kbdState
);
5198 OverrideLayout(loadedLayout
);
5200 bool isAltGrKeyPress
= false;
5201 if (aModifierFlags
& nsIWidget::ALTGRAPH
) {
5203 return NS_ERROR_INVALID_ARG
;
5205 // AltGr emulates ControlLeft key press and AltRight key press.
5206 // So, we should remove those flags from aModifierFlags before
5207 // calling WinUtils::SetupKeyModifiersSequence() to create correct
5209 // FYI: We don't support both ControlLeft and AltRight (AltGr) are
5210 // pressed at the same time unless synthesizing key is
5212 aModifierFlags
&= ~(nsIWidget::CTRL_L
| nsIWidget::ALT_R
);
5215 uint8_t argumentKeySpecific
= 0;
5216 switch (aNativeKeyCode
& 0xFF) {
5218 aModifierFlags
&= ~(nsIWidget::SHIFT_L
| nsIWidget::SHIFT_R
);
5219 argumentKeySpecific
= VK_LSHIFT
;
5222 aModifierFlags
&= ~nsIWidget::SHIFT_L
;
5223 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5224 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_SHIFT
;
5227 aModifierFlags
&= ~nsIWidget::SHIFT_R
;
5228 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5229 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_SHIFT
;
5232 aModifierFlags
&= ~(nsIWidget::CTRL_L
| nsIWidget::CTRL_R
);
5233 argumentKeySpecific
= VK_LCONTROL
;
5236 aModifierFlags
&= ~nsIWidget::CTRL_L
;
5237 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5238 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_CONTROL
;
5241 aModifierFlags
&= ~nsIWidget::CTRL_R
;
5242 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5243 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_CONTROL
;
5246 aModifierFlags
&= ~(nsIWidget::ALT_L
| nsIWidget::ALT_R
);
5247 argumentKeySpecific
= VK_LMENU
;
5250 aModifierFlags
&= ~nsIWidget::ALT_L
;
5251 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5252 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_MENU
;
5255 aModifierFlags
&= ~(nsIWidget::ALT_R
| nsIWidget::ALTGRAPH
);
5256 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5257 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_MENU
;
5258 // If AltRight key is AltGr in the keyboard layout, let's use
5259 // SetupKeyModifiersSequence() to emulate the native behavior
5260 // since the same event order between keydown and keyup makes
5261 // the following code complicated.
5263 isAltGrKeyPress
= true;
5264 aModifierFlags
&= ~nsIWidget::CTRL_L
;
5265 aModifierFlags
|= nsIWidget::ALTGRAPH
;
5269 aModifierFlags
&= ~nsIWidget::CAPS_LOCK
;
5270 argumentKeySpecific
= VK_CAPITAL
;
5273 aModifierFlags
&= ~nsIWidget::NUM_LOCK
;
5274 argumentKeySpecific
= VK_NUMLOCK
;
5278 AutoTArray
<KeyPair
, 10> keySequence
;
5279 WinUtils::SetupKeyModifiersSequence(&keySequence
, aModifierFlags
, WM_KEYDOWN
);
5280 if (!isAltGrKeyPress
) {
5281 keySequence
.AppendElement(KeyPair(aNativeKeyCode
, argumentKeySpecific
));
5284 // Simulate the pressing of each modifier key and then the real key
5285 // FYI: Each NativeKey instance here doesn't need to override keyboard layout
5286 // since this method overrides and restores the keyboard layout.
5287 for (uint32_t i
= 0; i
< keySequence
.Length(); ++i
) {
5288 uint8_t key
= keySequence
[i
].mGeneral
;
5289 uint8_t keySpecific
= keySequence
[i
].mSpecific
;
5290 uint16_t scanCode
= keySequence
[i
].mScanCode
;
5291 kbdState
[key
] = 0x81; // key is down and toggled on if appropriate
5293 kbdState
[keySpecific
] = 0x81;
5295 ::SetKeyboardState(kbdState
);
5296 ModifierKeyState modKeyState
;
5297 // If scan code isn't specified explicitly, let's compute it with current
5301 ComputeScanCodeForVirtualKeyCode(keySpecific
? keySpecific
: key
);
5303 LPARAM lParam
= static_cast<LPARAM
>(scanCode
<< 16);
5304 // If the scan code is for an extended key, set extended key flag.
5305 if ((scanCode
& 0xFF00) == 0xE000) {
5306 lParam
|= 0x1000000;
5308 // When AltGr key is pressed, both ControlLeft and AltRight cause
5309 // WM_KEYDOWN messages.
5310 bool makeSysKeyMsg
=
5311 !(aModifierFlags
& nsIWidget::ALTGRAPH
) && IsSysKey(key
, modKeyState
);
5313 WinUtils::InitMSG(makeSysKeyMsg
? WM_SYSKEYDOWN
: WM_KEYDOWN
, key
,
5314 lParam
, aWidget
->GetWindowHandle());
5315 if (i
== keySequence
.Length() - 1) {
5316 bool makeDeadCharMsg
=
5317 (IsDeadKey(key
, modKeyState
) && aCharacters
.IsEmpty());
5318 nsAutoString
chars(aCharacters
);
5319 if (makeDeadCharMsg
) {
5320 UniCharsAndModifiers deadChars
=
5321 GetUniCharsAndModifiers(key
, modKeyState
);
5322 chars
= deadChars
.ToString();
5323 NS_ASSERTION(chars
.Length() == 1,
5324 "Dead char must be only one character");
5326 if (chars
.IsEmpty()) {
5327 NativeKey
nativeKey(aWidget
, keyDownMsg
, modKeyState
);
5328 nativeKey
.HandleKeyDownMessage();
5330 AutoTArray
<NativeKey::FakeCharMsg
, 10> fakeCharMsgs
;
5331 for (uint32_t j
= 0; j
< chars
.Length(); j
++) {
5332 NativeKey::FakeCharMsg
* fakeCharMsg
= fakeCharMsgs
.AppendElement();
5333 fakeCharMsg
->mCharCode
= chars
.CharAt(j
);
5334 fakeCharMsg
->mScanCode
= scanCode
;
5335 fakeCharMsg
->mIsSysKey
= makeSysKeyMsg
;
5336 fakeCharMsg
->mIsDeadKey
= makeDeadCharMsg
;
5338 NativeKey
nativeKey(aWidget
, keyDownMsg
, modKeyState
, 0, &fakeCharMsgs
);
5340 nativeKey
.HandleKeyDownMessage(&dispatched
);
5341 // If some char messages are not consumed, let's emulate the widget
5342 // receiving the message directly.
5343 for (uint32_t j
= 1; j
< fakeCharMsgs
.Length(); j
++) {
5344 if (fakeCharMsgs
[j
].mConsumed
) {
5347 MSG charMsg
= fakeCharMsgs
[j
].GetCharMsg(aWidget
->GetWindowHandle());
5348 NativeKey
nativeKey(aWidget
, charMsg
, modKeyState
);
5349 nativeKey
.HandleCharMessage(charMsg
);
5353 NativeKey
nativeKey(aWidget
, keyDownMsg
, modKeyState
);
5354 nativeKey
.HandleKeyDownMessage();
5358 keySequence
.Clear();
5359 if (!isAltGrKeyPress
) {
5360 keySequence
.AppendElement(KeyPair(aNativeKeyCode
, argumentKeySpecific
));
5362 WinUtils::SetupKeyModifiersSequence(&keySequence
, aModifierFlags
, WM_KEYUP
);
5363 for (uint32_t i
= 0; i
< keySequence
.Length(); ++i
) {
5364 uint8_t key
= keySequence
[i
].mGeneral
;
5365 uint8_t keySpecific
= keySequence
[i
].mSpecific
;
5366 uint16_t scanCode
= keySequence
[i
].mScanCode
;
5367 kbdState
[key
] = 0; // key is up and toggled off if appropriate
5369 kbdState
[keySpecific
] = 0;
5371 ::SetKeyboardState(kbdState
);
5372 ModifierKeyState modKeyState
;
5373 // If scan code isn't specified explicitly, let's compute it with current
5377 ComputeScanCodeForVirtualKeyCode(keySpecific
? keySpecific
: key
);
5379 LPARAM lParam
= static_cast<LPARAM
>(scanCode
<< 16);
5380 // If the scan code is for an extended key, set extended key flag.
5381 if ((scanCode
& 0xFF00) == 0xE000) {
5382 lParam
|= 0x1000000;
5384 // Don't use WM_SYSKEYUP for Alt keyup.
5385 // NOTE: When AltGr was pressed, ControlLeft causes WM_SYSKEYUP normally.
5386 bool makeSysKeyMsg
= IsSysKey(key
, modKeyState
) && key
!= VK_MENU
;
5387 MSG keyUpMsg
= WinUtils::InitMSG(makeSysKeyMsg
? WM_SYSKEYUP
: WM_KEYUP
,
5388 key
, lParam
, aWidget
->GetWindowHandle());
5389 NativeKey
nativeKey(aWidget
, keyUpMsg
, modKeyState
);
5390 nativeKey
.HandleKeyUpMessage();
5393 // Restore old key state and layout
5394 ::SetKeyboardState(originalKbdState
);
5397 // Don't unload the layout if it's installed actually.
5398 for (uint32_t i
= 0; i
< keyboardLayoutListCount
; i
++) {
5399 if (keyboardLayoutList
[i
] == loadedLayout
) {
5404 if (keyboardLayoutListBuff
!= keyboardLayoutList
) {
5405 delete[] keyboardLayoutList
;
5408 ::UnloadKeyboardLayout(loadedLayout
);
5413 /*****************************************************************************
5414 * mozilla::widget::DeadKeyTable
5415 *****************************************************************************/
5417 char16_t
DeadKeyTable::GetCompositeChar(char16_t aBaseChar
) const {
5418 // Dead-key table is sorted by BaseChar in ascending order.
5419 // Usually they are too small to use binary search.
5421 for (uint32_t index
= 0; index
< mEntries
; index
++) {
5422 if (mTable
[index
].BaseChar
== aBaseChar
) {
5423 return mTable
[index
].CompositeChar
;
5425 if (mTable
[index
].BaseChar
> aBaseChar
) {
5433 /*****************************************************************************
5434 * mozilla::widget::RedirectedKeyDownMessage
5435 *****************************************************************************/
5437 MSG
RedirectedKeyDownMessageManager::sRedirectedKeyDownMsg
;
5438 bool RedirectedKeyDownMessageManager::sDefaultPreventedOfRedirectedMsg
= false;
5441 bool RedirectedKeyDownMessageManager::IsRedirectedMessage(const MSG
& aMsg
) {
5442 return (aMsg
.message
== WM_KEYDOWN
|| aMsg
.message
== WM_SYSKEYDOWN
) &&
5443 (sRedirectedKeyDownMsg
.message
== aMsg
.message
&&
5444 WinUtils::GetScanCode(sRedirectedKeyDownMsg
.lParam
) ==
5445 WinUtils::GetScanCode(aMsg
.lParam
));
5449 void RedirectedKeyDownMessageManager::RemoveNextCharMessage(HWND aWnd
) {
5451 if (WinUtils::PeekMessage(&msg
, aWnd
, WM_KEYFIRST
, WM_KEYLAST
,
5452 PM_NOREMOVE
| PM_NOYIELD
) &&
5453 (msg
.message
== WM_CHAR
|| msg
.message
== WM_SYSCHAR
)) {
5454 WinUtils::PeekMessage(&msg
, aWnd
, msg
.message
, msg
.message
,
5455 PM_REMOVE
| PM_NOYIELD
);
5459 } // namespace widget
5460 } // namespace mozilla