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 "nsIIdleServiceInternal.h"
20 #include "nsIWindowsRegKey.h"
22 #include "nsPrintfCString.h"
23 #include "nsQuickSort.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"
43 // In WinUser.h, MAPVK_VK_TO_VSC_EX is defined only when WINVER >= 0x0600
44 #ifndef MAPVK_VK_TO_VSC_EX
45 # define MAPVK_VK_TO_VSC_EX (4)
51 static const char* const kVirtualKeyName
[] = {
207 "VK_OEM_NEC_EQUAL, VK_OEM_FJ_JISHO",
229 "VK_BROWSER_FORWARD",
230 "VK_BROWSER_REFRESH",
233 "VK_BROWSER_FAVORITES",
239 "VK_MEDIA_NEXT_TRACK",
240 "VK_MEDIA_PREV_TRACK",
242 "VK_MEDIA_PLAY_PAUSE",
244 "VK_LAUNCH_MEDIA_SELECT",
324 static_assert(sizeof(kVirtualKeyName
) / sizeof(const char*) == 0x100,
325 "The virtual key name must be defined just 256 keys");
327 static const char* GetBoolName(bool aBool
) { return aBool
? "true" : "false"; }
329 static const nsCString
GetCharacterCodeName(WPARAM aCharCode
) {
332 return NS_LITERAL_CSTRING("NULL (0x0000)");
334 return NS_LITERAL_CSTRING("BACKSPACE (0x0008)");
336 return NS_LITERAL_CSTRING("CHARACTER TABULATION (0x0009)");
338 return NS_LITERAL_CSTRING("LINE FEED (0x000A)");
340 return NS_LITERAL_CSTRING("LINE TABULATION (0x000B)");
342 return NS_LITERAL_CSTRING("FORM FEED (0x000C)");
344 return NS_LITERAL_CSTRING("CARRIAGE RETURN (0x000D)");
346 return NS_LITERAL_CSTRING("CANCEL (0x0018)");
348 return NS_LITERAL_CSTRING("ESCAPE (0x001B)");
350 return NS_LITERAL_CSTRING("SPACE (0x0020)");
352 return NS_LITERAL_CSTRING("DELETE (0x007F)");
354 return NS_LITERAL_CSTRING("NO-BREAK SPACE (0x00A0)");
356 return NS_LITERAL_CSTRING("SOFT HYPHEN (0x00AD)");
358 return NS_LITERAL_CSTRING("EN QUAD (0x2000)");
360 return NS_LITERAL_CSTRING("EM QUAD (0x2001)");
362 return NS_LITERAL_CSTRING("EN SPACE (0x2002)");
364 return NS_LITERAL_CSTRING("EM SPACE (0x2003)");
366 return NS_LITERAL_CSTRING("THREE-PER-EM SPACE (0x2004)");
368 return NS_LITERAL_CSTRING("FOUR-PER-EM SPACE (0x2005)");
370 return NS_LITERAL_CSTRING("SIX-PER-EM SPACE (0x2006)");
372 return NS_LITERAL_CSTRING("FIGURE SPACE (0x2007)");
374 return NS_LITERAL_CSTRING("PUNCTUATION SPACE (0x2008)");
376 return NS_LITERAL_CSTRING("THIN SPACE (0x2009)");
378 return NS_LITERAL_CSTRING("HAIR SPACE (0x200A)");
380 return NS_LITERAL_CSTRING("ZERO WIDTH SPACE (0x200B)");
382 return NS_LITERAL_CSTRING("ZERO WIDTH NON-JOINER (0x200C)");
384 return NS_LITERAL_CSTRING("ZERO WIDTH JOINER (0x200D)");
386 return NS_LITERAL_CSTRING("LEFT-TO-RIGHT MARK (0x200E)");
388 return NS_LITERAL_CSTRING("RIGHT-TO-LEFT MARK (0x200F)");
390 return NS_LITERAL_CSTRING("PARAGRAPH SEPARATOR (0x2029)");
392 return NS_LITERAL_CSTRING("LEFT-TO-RIGHT EMBEDDING (0x202A)");
394 return NS_LITERAL_CSTRING("RIGHT-TO-LEFT EMBEDDING (0x202B)");
396 return NS_LITERAL_CSTRING("LEFT-TO-RIGHT OVERRIDE (0x202D)");
398 return NS_LITERAL_CSTRING("RIGHT-TO-LEFT OVERRIDE (0x202E)");
400 return NS_LITERAL_CSTRING("NARROW NO-BREAK SPACE (0x202F)");
402 return NS_LITERAL_CSTRING("MEDIUM MATHEMATICAL SPACE (0x205F)");
404 return NS_LITERAL_CSTRING("WORD JOINER (0x2060)");
406 return NS_LITERAL_CSTRING("LEFT-TO-RIGHT ISOLATE (0x2066)");
408 return NS_LITERAL_CSTRING("RIGHT-TO-LEFT ISOLATE (0x2067)");
410 return NS_LITERAL_CSTRING("IDEOGRAPHIC SPACE (0x3000)");
412 return NS_LITERAL_CSTRING("ZERO WIDTH NO-BREAK SPACE (0xFEFF)");
414 if (aCharCode
< ' ' || (aCharCode
>= 0x80 && aCharCode
< 0xA0)) {
415 return nsPrintfCString("control (0x%04X)", aCharCode
);
417 if (NS_IS_HIGH_SURROGATE(aCharCode
)) {
418 return nsPrintfCString("high surrogate (0x%04X)", aCharCode
);
420 if (NS_IS_LOW_SURROGATE(aCharCode
)) {
421 return nsPrintfCString("low surrogate (0x%04X)", aCharCode
);
423 return IS_IN_BMP(aCharCode
)
426 NS_ConvertUTF16toUTF8(nsAutoString(aCharCode
)).get(),
430 NS_ConvertUTF16toUTF8(nsAutoString(aCharCode
)).get(),
436 static const nsCString
GetKeyLocationName(uint32_t aLocation
) {
438 case eKeyLocationLeft
:
439 return NS_LITERAL_CSTRING("KEY_LOCATION_LEFT");
440 case eKeyLocationRight
:
441 return NS_LITERAL_CSTRING("KEY_LOCATION_RIGHT");
442 case eKeyLocationStandard
:
443 return NS_LITERAL_CSTRING("KEY_LOCATION_STANDARD");
444 case eKeyLocationNumpad
:
445 return NS_LITERAL_CSTRING("KEY_LOCATION_NUMPAD");
447 return nsPrintfCString("Unknown (0x%04X)", aLocation
);
451 static const nsCString
GetCharacterCodeName(const char16_t
* aChars
,
454 return EmptyCString();
457 for (uint32_t i
= 0; i
< aLength
; ++i
) {
458 if (!result
.IsEmpty()) {
459 result
.AppendLiteral(", ");
461 result
.AssignLiteral("\"");
463 result
.Append(GetCharacterCodeName(aChars
[i
]));
465 result
.AppendLiteral("\"");
469 static const nsCString
GetCharacterCodeName(
470 const UniCharsAndModifiers
& aUniCharsAndModifiers
) {
471 if (aUniCharsAndModifiers
.IsEmpty()) {
472 return EmptyCString();
475 for (uint32_t i
= 0; i
< aUniCharsAndModifiers
.Length(); i
++) {
476 if (!result
.IsEmpty()) {
477 result
.AppendLiteral(", ");
479 result
.AssignLiteral("\"");
481 result
.Append(GetCharacterCodeName(aUniCharsAndModifiers
.CharAt(i
)));
483 result
.AppendLiteral("\"");
487 class MOZ_STACK_CLASS GetShiftStateName final
: public nsAutoCString
{
489 explicit GetShiftStateName(VirtualKey::ShiftState aShiftState
) {
491 AssignLiteral("none");
494 if (aShiftState
& VirtualKey::STATE_SHIFT
) {
495 AssignLiteral("Shift");
496 aShiftState
&= ~VirtualKey::STATE_SHIFT
;
498 if (aShiftState
& VirtualKey::STATE_CONTROL
) {
499 MaybeAppendSeparator();
500 AssignLiteral("Ctrl");
501 aShiftState
&= ~VirtualKey::STATE_CONTROL
;
503 if (aShiftState
& VirtualKey::STATE_ALT
) {
504 MaybeAppendSeparator();
505 AssignLiteral("Alt");
506 aShiftState
&= ~VirtualKey::STATE_ALT
;
508 if (aShiftState
& VirtualKey::STATE_CAPSLOCK
) {
509 MaybeAppendSeparator();
510 AssignLiteral("CapsLock");
511 aShiftState
&= ~VirtualKey::STATE_CAPSLOCK
;
513 MOZ_ASSERT(!aShiftState
);
517 void MaybeAppendSeparator() {
519 AppendLiteral(" | ");
524 static const nsCString
GetMessageName(UINT aMessage
) {
527 return NS_LITERAL_CSTRING("WM_NULL");
529 return NS_LITERAL_CSTRING("WM_KEYDOWN");
531 return NS_LITERAL_CSTRING("WM_KEYUP");
533 return NS_LITERAL_CSTRING("WM_SYSKEYDOWN");
535 return NS_LITERAL_CSTRING("WM_SYSKEYUP");
537 return NS_LITERAL_CSTRING("WM_CHAR");
539 return NS_LITERAL_CSTRING("WM_UNICHAR");
541 return NS_LITERAL_CSTRING("WM_SYSCHAR");
543 return NS_LITERAL_CSTRING("WM_DEADCHAR");
545 return NS_LITERAL_CSTRING("WM_SYSDEADCHAR");
547 return NS_LITERAL_CSTRING("MOZ_WM_KEYDOWN");
549 return NS_LITERAL_CSTRING("MOZ_WM_KEYUP");
551 return NS_LITERAL_CSTRING("WM_APPCOMMAND");
553 return NS_LITERAL_CSTRING("WM_QUIT");
555 return nsPrintfCString("Unknown Message (0x%04X)", aMessage
);
559 static const nsCString
GetVirtualKeyCodeName(WPARAM aVK
) {
560 if (aVK
>= ArrayLength(kVirtualKeyName
)) {
561 return nsPrintfCString("Invalid (0x%08X)", aVK
);
563 return nsCString(kVirtualKeyName
[aVK
]);
566 static const nsCString
GetAppCommandName(WPARAM aCommand
) {
568 case APPCOMMAND_BASS_BOOST
:
569 return NS_LITERAL_CSTRING("APPCOMMAND_BASS_BOOST");
570 case APPCOMMAND_BASS_DOWN
:
571 return NS_LITERAL_CSTRING("APPCOMMAND_BASS_DOWN");
572 case APPCOMMAND_BASS_UP
:
573 return NS_LITERAL_CSTRING("APPCOMMAND_BASS_UP");
574 case APPCOMMAND_BROWSER_BACKWARD
:
575 return NS_LITERAL_CSTRING("APPCOMMAND_BROWSER_BACKWARD");
576 case APPCOMMAND_BROWSER_FAVORITES
:
577 return NS_LITERAL_CSTRING("APPCOMMAND_BROWSER_FAVORITES");
578 case APPCOMMAND_BROWSER_FORWARD
:
579 return NS_LITERAL_CSTRING("APPCOMMAND_BROWSER_FORWARD");
580 case APPCOMMAND_BROWSER_HOME
:
581 return NS_LITERAL_CSTRING("APPCOMMAND_BROWSER_HOME");
582 case APPCOMMAND_BROWSER_REFRESH
:
583 return NS_LITERAL_CSTRING("APPCOMMAND_BROWSER_REFRESH");
584 case APPCOMMAND_BROWSER_SEARCH
:
585 return NS_LITERAL_CSTRING("APPCOMMAND_BROWSER_SEARCH");
586 case APPCOMMAND_BROWSER_STOP
:
587 return NS_LITERAL_CSTRING("APPCOMMAND_BROWSER_STOP");
588 case APPCOMMAND_CLOSE
:
589 return NS_LITERAL_CSTRING("APPCOMMAND_CLOSE");
590 case APPCOMMAND_COPY
:
591 return NS_LITERAL_CSTRING("APPCOMMAND_COPY");
592 case APPCOMMAND_CORRECTION_LIST
:
593 return NS_LITERAL_CSTRING("APPCOMMAND_CORRECTION_LIST");
595 return NS_LITERAL_CSTRING("APPCOMMAND_CUT");
596 case APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE
:
597 return NS_LITERAL_CSTRING("APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE");
598 case APPCOMMAND_FIND
:
599 return NS_LITERAL_CSTRING("APPCOMMAND_FIND");
600 case APPCOMMAND_FORWARD_MAIL
:
601 return NS_LITERAL_CSTRING("APPCOMMAND_FORWARD_MAIL");
602 case APPCOMMAND_HELP
:
603 return NS_LITERAL_CSTRING("APPCOMMAND_HELP");
604 case APPCOMMAND_LAUNCH_APP1
:
605 return NS_LITERAL_CSTRING("APPCOMMAND_LAUNCH_APP1");
606 case APPCOMMAND_LAUNCH_APP2
:
607 return NS_LITERAL_CSTRING("APPCOMMAND_LAUNCH_APP2");
608 case APPCOMMAND_LAUNCH_MAIL
:
609 return NS_LITERAL_CSTRING("APPCOMMAND_LAUNCH_MAIL");
610 case APPCOMMAND_LAUNCH_MEDIA_SELECT
:
611 return NS_LITERAL_CSTRING("APPCOMMAND_LAUNCH_MEDIA_SELECT");
612 case APPCOMMAND_MEDIA_CHANNEL_DOWN
:
613 return NS_LITERAL_CSTRING("APPCOMMAND_MEDIA_CHANNEL_DOWN");
614 case APPCOMMAND_MEDIA_CHANNEL_UP
:
615 return NS_LITERAL_CSTRING("APPCOMMAND_MEDIA_CHANNEL_UP");
616 case APPCOMMAND_MEDIA_FAST_FORWARD
:
617 return NS_LITERAL_CSTRING("APPCOMMAND_MEDIA_FAST_FORWARD");
618 case APPCOMMAND_MEDIA_NEXTTRACK
:
619 return NS_LITERAL_CSTRING("APPCOMMAND_MEDIA_NEXTTRACK");
620 case APPCOMMAND_MEDIA_PAUSE
:
621 return NS_LITERAL_CSTRING("APPCOMMAND_MEDIA_PAUSE");
622 case APPCOMMAND_MEDIA_PLAY
:
623 return NS_LITERAL_CSTRING("APPCOMMAND_MEDIA_PLAY");
624 case APPCOMMAND_MEDIA_PLAY_PAUSE
:
625 return NS_LITERAL_CSTRING("APPCOMMAND_MEDIA_PLAY_PAUSE");
626 case APPCOMMAND_MEDIA_PREVIOUSTRACK
:
627 return NS_LITERAL_CSTRING("APPCOMMAND_MEDIA_PREVIOUSTRACK");
628 case APPCOMMAND_MEDIA_RECORD
:
629 return NS_LITERAL_CSTRING("APPCOMMAND_MEDIA_RECORD");
630 case APPCOMMAND_MEDIA_REWIND
:
631 return NS_LITERAL_CSTRING("APPCOMMAND_MEDIA_REWIND");
632 case APPCOMMAND_MEDIA_STOP
:
633 return NS_LITERAL_CSTRING("APPCOMMAND_MEDIA_STOP");
634 case APPCOMMAND_MIC_ON_OFF_TOGGLE
:
635 return NS_LITERAL_CSTRING("APPCOMMAND_MIC_ON_OFF_TOGGLE");
636 case APPCOMMAND_MICROPHONE_VOLUME_DOWN
:
637 return NS_LITERAL_CSTRING("APPCOMMAND_MICROPHONE_VOLUME_DOWN");
638 case APPCOMMAND_MICROPHONE_VOLUME_MUTE
:
639 return NS_LITERAL_CSTRING("APPCOMMAND_MICROPHONE_VOLUME_MUTE");
640 case APPCOMMAND_MICROPHONE_VOLUME_UP
:
641 return NS_LITERAL_CSTRING("APPCOMMAND_MICROPHONE_VOLUME_UP");
643 return NS_LITERAL_CSTRING("APPCOMMAND_NEW");
644 case APPCOMMAND_OPEN
:
645 return NS_LITERAL_CSTRING("APPCOMMAND_OPEN");
646 case APPCOMMAND_PASTE
:
647 return NS_LITERAL_CSTRING("APPCOMMAND_PASTE");
648 case APPCOMMAND_PRINT
:
649 return NS_LITERAL_CSTRING("APPCOMMAND_PRINT");
650 case APPCOMMAND_REDO
:
651 return NS_LITERAL_CSTRING("APPCOMMAND_REDO");
652 case APPCOMMAND_REPLY_TO_MAIL
:
653 return NS_LITERAL_CSTRING("APPCOMMAND_REPLY_TO_MAIL");
654 case APPCOMMAND_SAVE
:
655 return NS_LITERAL_CSTRING("APPCOMMAND_SAVE");
656 case APPCOMMAND_SEND_MAIL
:
657 return NS_LITERAL_CSTRING("APPCOMMAND_SEND_MAIL");
658 case APPCOMMAND_SPELL_CHECK
:
659 return NS_LITERAL_CSTRING("APPCOMMAND_SPELL_CHECK");
660 case APPCOMMAND_TREBLE_DOWN
:
661 return NS_LITERAL_CSTRING("APPCOMMAND_TREBLE_DOWN");
662 case APPCOMMAND_TREBLE_UP
:
663 return NS_LITERAL_CSTRING("APPCOMMAND_TREBLE_UP");
664 case APPCOMMAND_UNDO
:
665 return NS_LITERAL_CSTRING("APPCOMMAND_UNDO");
666 case APPCOMMAND_VOLUME_DOWN
:
667 return NS_LITERAL_CSTRING("APPCOMMAND_VOLUME_DOWN");
668 case APPCOMMAND_VOLUME_MUTE
:
669 return NS_LITERAL_CSTRING("APPCOMMAND_VOLUME_MUTE");
670 case APPCOMMAND_VOLUME_UP
:
671 return NS_LITERAL_CSTRING("APPCOMMAND_VOLUME_UP");
673 return nsPrintfCString("Unknown app command (0x%08X)", aCommand
);
677 static const nsCString
GetAppCommandDeviceName(LPARAM aDevice
) {
679 case FAPPCOMMAND_KEY
:
680 return NS_LITERAL_CSTRING("FAPPCOMMAND_KEY");
681 case FAPPCOMMAND_MOUSE
:
682 return NS_LITERAL_CSTRING("FAPPCOMMAND_MOUSE");
683 case FAPPCOMMAND_OEM
:
684 return NS_LITERAL_CSTRING("FAPPCOMMAND_OEM");
686 return nsPrintfCString("Unknown app command device (0x%04X)", aDevice
);
690 class MOZ_STACK_CLASS GetAppCommandKeysName final
: public nsAutoCString
{
692 explicit GetAppCommandKeysName(WPARAM aKeys
) {
693 if (aKeys
& MK_CONTROL
) {
694 AppendLiteral("MK_CONTROL");
695 aKeys
&= ~MK_CONTROL
;
697 if (aKeys
& MK_LBUTTON
) {
698 MaybeAppendSeparator();
699 AppendLiteral("MK_LBUTTON");
700 aKeys
&= ~MK_LBUTTON
;
702 if (aKeys
& MK_MBUTTON
) {
703 MaybeAppendSeparator();
704 AppendLiteral("MK_MBUTTON");
705 aKeys
&= ~MK_MBUTTON
;
707 if (aKeys
& MK_RBUTTON
) {
708 MaybeAppendSeparator();
709 AppendLiteral("MK_RBUTTON");
710 aKeys
&= ~MK_RBUTTON
;
712 if (aKeys
& MK_SHIFT
) {
713 MaybeAppendSeparator();
714 AppendLiteral("MK_SHIFT");
717 if (aKeys
& MK_XBUTTON1
) {
718 MaybeAppendSeparator();
719 AppendLiteral("MK_XBUTTON1");
720 aKeys
&= ~MK_XBUTTON1
;
722 if (aKeys
& MK_XBUTTON2
) {
723 MaybeAppendSeparator();
724 AppendLiteral("MK_XBUTTON2");
725 aKeys
&= ~MK_XBUTTON2
;
728 MaybeAppendSeparator();
729 AppendPrintf("Unknown Flags (0x%04X)", aKeys
);
732 AssignLiteral("none (0x0000)");
737 void MaybeAppendSeparator() {
739 AppendLiteral(" | ");
744 static const nsCString
ToString(const MSG
& aMSG
) {
746 result
.AssignLiteral("{ message=");
747 result
.Append(GetMessageName(aMSG
.message
).get());
748 result
.AppendLiteral(", ");
749 switch (aMSG
.message
) {
757 "virtual keycode=%s, repeat count=%d, "
758 "scancode=0x%02X, extended key=%s, "
759 "context code=%s, previous key state=%s, "
760 "transition state=%s",
761 GetVirtualKeyCodeName(aMSG
.wParam
).get(), aMSG
.lParam
& 0xFFFF,
762 WinUtils::GetScanCode(aMSG
.lParam
),
763 GetBoolName(WinUtils::IsExtendedScanCode(aMSG
.lParam
)),
764 GetBoolName((aMSG
.lParam
& (1 << 29)) != 0),
765 GetBoolName((aMSG
.lParam
& (1 << 30)) != 0),
766 GetBoolName((aMSG
.lParam
& (1 << 31)) != 0));
773 "character code=%s, repeat count=%d, "
774 "scancode=0x%02X, extended key=%s, "
775 "context code=%s, previous key state=%s, "
776 "transition state=%s",
777 GetCharacterCodeName(aMSG
.wParam
).get(), aMSG
.lParam
& 0xFFFF,
778 WinUtils::GetScanCode(aMSG
.lParam
),
779 GetBoolName(WinUtils::IsExtendedScanCode(aMSG
.lParam
)),
780 GetBoolName((aMSG
.lParam
& (1 << 29)) != 0),
781 GetBoolName((aMSG
.lParam
& (1 << 30)) != 0),
782 GetBoolName((aMSG
.lParam
& (1 << 31)) != 0));
786 "window handle=0x%p, app command=%s, device=%s, dwKeys=%s",
788 GetAppCommandName(GET_APPCOMMAND_LPARAM(aMSG
.lParam
)).get(),
789 GetAppCommandDeviceName(GET_DEVICE_LPARAM(aMSG
.lParam
)).get(),
790 GetAppCommandKeysName(GET_KEYSTATE_LPARAM(aMSG
.lParam
)).get());
793 result
.AppendPrintf("wParam=%u, lParam=%u", aMSG
.wParam
, aMSG
.lParam
);
796 result
.AppendPrintf(", hwnd=0x%p", aMSG
.hwnd
);
800 static const nsCString
ToString(
801 const UniCharsAndModifiers
& aUniCharsAndModifiers
) {
802 if (aUniCharsAndModifiers
.IsEmpty()) {
803 return NS_LITERAL_CSTRING("{}");
806 result
.AssignLiteral("{ ");
807 result
.Append(GetCharacterCodeName(aUniCharsAndModifiers
.CharAt(0)));
808 for (size_t i
= 1; i
< aUniCharsAndModifiers
.Length(); ++i
) {
809 if (aUniCharsAndModifiers
.ModifiersAt(i
- 1) !=
810 aUniCharsAndModifiers
.ModifiersAt(i
)) {
811 result
.AppendLiteral(" [");
812 result
.Append(GetModifiersName(aUniCharsAndModifiers
.ModifiersAt(0)));
813 result
.AppendLiteral("]");
815 result
.AppendLiteral(", ");
816 result
.Append(GetCharacterCodeName(aUniCharsAndModifiers
.CharAt(i
)));
818 result
.AppendLiteral(" [");
819 uint32_t lastIndex
= aUniCharsAndModifiers
.Length() - 1;
820 result
.Append(GetModifiersName(aUniCharsAndModifiers
.ModifiersAt(lastIndex
)));
821 result
.AppendLiteral("] }");
825 const nsCString
ToString(const ModifierKeyState
& aModifierKeyState
) {
827 result
.AssignLiteral("{ ");
828 result
.Append(GetModifiersName(aModifierKeyState
.GetModifiers()).get());
829 result
.AppendLiteral(" }");
833 // Unique id counter associated with a keydown / keypress events. Used in
834 // identifing keypress events for removal from async event dispatch queue
835 // in metrofx after preventDefault is called on keydown events.
836 static uint32_t sUniqueKeyEventId
= 0;
838 /*****************************************************************************
839 * mozilla::widget::ModifierKeyState
840 *****************************************************************************/
842 ModifierKeyState::ModifierKeyState() { Update(); }
844 ModifierKeyState::ModifierKeyState(Modifiers aModifiers
)
845 : mModifiers(aModifiers
) {
846 MOZ_ASSERT(!(mModifiers
& MODIFIER_ALTGRAPH
) || (!IsControl() && !IsAlt()),
847 "Neither MODIFIER_CONTROL nor MODIFIER_ALT should be set "
848 "if MODIFIER_ALTGRAPH is set");
851 void ModifierKeyState::Update() {
853 if (IS_VK_DOWN(VK_SHIFT
)) {
854 mModifiers
|= MODIFIER_SHIFT
;
856 // If AltGr key (i.e., VK_RMENU on some keyboard layout) is pressed, only
857 // MODIFIER_ALTGRAPH should be set. Otherwise, i.e., if both Ctrl and Alt
858 // keys are pressed to emulate AltGr key, MODIFIER_CONTROL and MODIFIER_ALT
859 // keys should be set separately.
860 if (KeyboardLayout::GetInstance()->HasAltGr() && IS_VK_DOWN(VK_RMENU
)) {
861 mModifiers
|= MODIFIER_ALTGRAPH
;
863 if (IS_VK_DOWN(VK_CONTROL
)) {
864 mModifiers
|= MODIFIER_CONTROL
;
866 if (IS_VK_DOWN(VK_MENU
)) {
867 mModifiers
|= MODIFIER_ALT
;
870 if (IS_VK_DOWN(VK_LWIN
) || IS_VK_DOWN(VK_RWIN
)) {
871 mModifiers
|= MODIFIER_OS
;
873 if (::GetKeyState(VK_CAPITAL
) & 1) {
874 mModifiers
|= MODIFIER_CAPSLOCK
;
876 if (::GetKeyState(VK_NUMLOCK
) & 1) {
877 mModifiers
|= MODIFIER_NUMLOCK
;
879 if (::GetKeyState(VK_SCROLL
) & 1) {
880 mModifiers
|= MODIFIER_SCROLLLOCK
;
884 void ModifierKeyState::Unset(Modifiers aRemovingModifiers
) {
885 mModifiers
&= ~aRemovingModifiers
;
888 void ModifierKeyState::Set(Modifiers aAddingModifiers
) {
889 mModifiers
|= aAddingModifiers
;
890 MOZ_ASSERT(!(mModifiers
& MODIFIER_ALTGRAPH
) || (!IsControl() && !IsAlt()),
891 "Neither MODIFIER_CONTROL nor MODIFIER_ALT should be set "
892 "if MODIFIER_ALTGRAPH is set");
895 void ModifierKeyState::InitInputEvent(WidgetInputEvent
& aInputEvent
) const {
896 aInputEvent
.mModifiers
= mModifiers
;
898 switch (aInputEvent
.mClass
) {
899 case eMouseEventClass
:
900 case eMouseScrollEventClass
:
901 case eWheelEventClass
:
902 case eDragEventClass
:
903 case eSimpleGestureEventClass
:
904 InitMouseEvent(aInputEvent
);
911 void ModifierKeyState::InitMouseEvent(WidgetInputEvent
& aMouseEvent
) const {
912 NS_ASSERTION(aMouseEvent
.mClass
== eMouseEventClass
||
913 aMouseEvent
.mClass
== eWheelEventClass
||
914 aMouseEvent
.mClass
== eDragEventClass
||
915 aMouseEvent
.mClass
== eSimpleGestureEventClass
,
916 "called with non-mouse event");
918 WidgetMouseEventBase
& mouseEvent
= *aMouseEvent
.AsMouseEventBase();
919 mouseEvent
.mButtons
= 0;
920 if (::GetKeyState(VK_LBUTTON
) < 0) {
921 mouseEvent
.mButtons
|= MouseButtonsFlag::eLeftFlag
;
923 if (::GetKeyState(VK_RBUTTON
) < 0) {
924 mouseEvent
.mButtons
|= MouseButtonsFlag::eRightFlag
;
926 if (::GetKeyState(VK_MBUTTON
) < 0) {
927 mouseEvent
.mButtons
|= MouseButtonsFlag::eMiddleFlag
;
929 if (::GetKeyState(VK_XBUTTON1
) < 0) {
930 mouseEvent
.mButtons
|= MouseButtonsFlag::e4thFlag
;
932 if (::GetKeyState(VK_XBUTTON2
) < 0) {
933 mouseEvent
.mButtons
|= MouseButtonsFlag::e5thFlag
;
937 bool ModifierKeyState::IsShift() const {
938 return (mModifiers
& MODIFIER_SHIFT
) != 0;
941 bool ModifierKeyState::IsControl() const {
942 return (mModifiers
& MODIFIER_CONTROL
) != 0;
945 bool ModifierKeyState::IsAlt() const {
946 return (mModifiers
& MODIFIER_ALT
) != 0;
949 bool ModifierKeyState::IsWin() const { return (mModifiers
& MODIFIER_OS
) != 0; }
951 bool ModifierKeyState::MaybeMatchShortcutKey() const {
952 // If Windows key is pressed, even if both Ctrl key and Alt key are pressed,
953 // it's possible to match a shortcut key.
957 // Otherwise, when both Ctrl key and Alt key are pressed, it shouldn't be
958 // a shortcut key for Windows since it means pressing AltGr key on
959 // some keyboard layouts.
960 if (IsControl() ^ IsAlt()) {
963 // If no modifier key is active except a lockable modifier nor Shift key,
964 // the key shouldn't match any shortcut keys (there are Space and
965 // Shift+Space, though, let's ignore these special case...).
969 bool ModifierKeyState::IsCapsLocked() const {
970 return (mModifiers
& MODIFIER_CAPSLOCK
) != 0;
973 bool ModifierKeyState::IsNumLocked() const {
974 return (mModifiers
& MODIFIER_NUMLOCK
) != 0;
977 bool ModifierKeyState::IsScrollLocked() const {
978 return (mModifiers
& MODIFIER_SCROLLLOCK
) != 0;
981 /*****************************************************************************
982 * mozilla::widget::UniCharsAndModifiers
983 *****************************************************************************/
985 void UniCharsAndModifiers::Append(char16_t aUniChar
, Modifiers aModifiers
) {
986 mChars
.Append(aUniChar
);
987 mModifiers
.AppendElement(aModifiers
);
990 void UniCharsAndModifiers::FillModifiers(Modifiers aModifiers
) {
991 for (size_t i
= 0; i
< Length(); i
++) {
992 mModifiers
[i
] = aModifiers
;
996 void UniCharsAndModifiers::OverwriteModifiersIfBeginsWith(
997 const UniCharsAndModifiers
& aOther
) {
998 if (!BeginsWith(aOther
)) {
1001 for (size_t i
= 0; i
< aOther
.Length(); ++i
) {
1002 mModifiers
[i
] = aOther
.mModifiers
[i
];
1006 bool UniCharsAndModifiers::UniCharsEqual(
1007 const UniCharsAndModifiers
& aOther
) const {
1008 return mChars
.Equals(aOther
.mChars
);
1011 bool UniCharsAndModifiers::UniCharsCaseInsensitiveEqual(
1012 const UniCharsAndModifiers
& aOther
) const {
1013 nsCaseInsensitiveStringComparator comp
;
1014 return mChars
.Equals(aOther
.mChars
, comp
);
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
= {};
1249 LazyLogModule
sNativeKeyLogger("NativeKeyWidgets");
1251 NativeKey::NativeKey(nsWindowBase
* aWidget
, const MSG
& aMessage
,
1252 const ModifierKeyState
& aModKeyState
,
1253 HKL aOverrideKeyboardLayout
,
1254 nsTArray
<FakeCharMsg
>* aFakeCharMsgs
)
1255 : mLastInstance(sLatestInstance
),
1256 mRemovingMsg(sEmptyMSG
),
1257 mReceivedMsg(sEmptyMSG
),
1259 mDispatcher(aWidget
->GetTextEventDispatcher()),
1261 mFocusedWndBeforeDispatch(::GetFocus()),
1263 mKeyNameIndex(KEY_NAME_INDEX_Unidentified
),
1264 mCodeNameIndex(CODE_NAME_INDEX_UNKNOWN
),
1265 mModKeyState(aModKeyState
),
1267 mOriginalVirtualKeyCode(0),
1268 mShiftedLatinChar(0),
1269 mUnshiftedLatinChar(0),
1274 mIsPrintableKey(false),
1275 mIsSkippableInRemoteProcess(false),
1276 mCharMessageHasGone(false),
1277 mCanIgnoreModifierStateAtKeyPress(true),
1278 mFakeCharMsgs(aFakeCharMsgs
&& aFakeCharMsgs
->Length() ? aFakeCharMsgs
1280 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
1281 ("%p NativeKey::NativeKey(aWidget=0x%p { GetWindowHandle()=0x%p }, "
1282 "aMessage=%s, aModKeyState=%s), sLatestInstance=0x%p",
1283 this, aWidget
, aWidget
->GetWindowHandle(), ToString(aMessage
).get(),
1284 ToString(aModKeyState
).get(), sLatestInstance
));
1286 MOZ_ASSERT(aWidget
);
1287 MOZ_ASSERT(mDispatcher
);
1288 sLatestInstance
= this;
1289 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
1290 mKeyboardLayout
= keyboardLayout
->GetLayout();
1291 if (aOverrideKeyboardLayout
&& mKeyboardLayout
!= aOverrideKeyboardLayout
) {
1292 keyboardLayout
->OverrideLayout(aOverrideKeyboardLayout
);
1293 mKeyboardLayout
= keyboardLayout
->GetLayout();
1294 MOZ_ASSERT(mKeyboardLayout
== aOverrideKeyboardLayout
);
1295 mIsOverridingKeyboardLayout
= true;
1297 mIsOverridingKeyboardLayout
= false;
1298 sLastKeyOrCharMSG
= aMessage
;
1301 if (mMsg
.message
== WM_APPCOMMAND
) {
1302 InitWithAppCommand();
1304 InitWithKeyOrChar();
1307 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
1308 ("%p NativeKey::NativeKey(), mKeyboardLayout=0x%08X, "
1309 "mFocusedWndBeforeDispatch=0x%p, mDOMKeyCode=%s, "
1310 "mKeyNameIndex=%s, mCodeNameIndex=%s, mModKeyState=%s, "
1311 "mVirtualKeyCode=%s, mOriginalVirtualKeyCode=%s, "
1312 "mCommittedCharsAndModifiers=%s, mInputtingStringAndModifiers=%s, "
1313 "mShiftedString=%s, mUnshiftedString=%s, mShiftedLatinChar=%s, "
1314 "mUnshiftedLatinChar=%s, mScanCode=0x%04X, mIsExtended=%s, "
1315 "mIsRepeat=%s, mIsDeadKey=%s, mIsPrintableKey=%s, "
1316 "mIsSkippableInRemoteProcess=%s, mCharMessageHasGone=%s, "
1317 "mIsOverridingKeyboardLayout=%s",
1318 this, mKeyboardLayout
, mFocusedWndBeforeDispatch
,
1319 GetDOMKeyCodeName(mDOMKeyCode
).get(), ToString(mKeyNameIndex
).get(),
1320 ToString(mCodeNameIndex
).get(), ToString(mModKeyState
).get(),
1321 GetVirtualKeyCodeName(mVirtualKeyCode
).get(),
1322 GetVirtualKeyCodeName(mOriginalVirtualKeyCode
).get(),
1323 ToString(mCommittedCharsAndModifiers
).get(),
1324 ToString(mInputtingStringAndModifiers
).get(),
1325 ToString(mShiftedString
).get(), ToString(mUnshiftedString
).get(),
1326 GetCharacterCodeName(mShiftedLatinChar
).get(),
1327 GetCharacterCodeName(mUnshiftedLatinChar
).get(), mScanCode
,
1328 GetBoolName(mIsExtended
), GetBoolName(mIsRepeat
),
1329 GetBoolName(mIsDeadKey
), GetBoolName(mIsPrintableKey
),
1330 GetBoolName(mIsSkippableInRemoteProcess
),
1331 GetBoolName(mCharMessageHasGone
),
1332 GetBoolName(mIsOverridingKeyboardLayout
)));
1335 void NativeKey::InitIsSkippableForKeyOrChar(const MSG
& aLastKeyMSG
) {
1336 mIsSkippableInRemoteProcess
= false;
1339 // If the message is not repeated key message, the event should be always
1340 // handled in remote process even if it's too old.
1344 // Keyboard utilities may send us some generated messages and such messages
1345 // may be marked as "repeated", e.g., SendInput() calls with
1346 // KEYEVENTF_UNICODE but without KEYEVENTF_KEYUP. However, key sequence
1347 // comes from such utilities may be really important. For example, utilities
1348 // may send WM_KEYDOWN for VK_BACK to remove previous character and send
1349 // WM_KEYDOWN for VK_PACKET to insert a composite character. Therefore, we
1350 // should check if current message and previous key message are caused by
1351 // same physical key. If not, the message may be generated by such
1353 // XXX With this approach, if VK_BACK messages are generated with known
1354 // scancode, we cannot distinguish whether coming VK_BACK message is
1355 // actually repeated by the auto-repeat feature. Currently, we need
1356 // this hack only for "SinhalaTamil IME" and fortunately, it generates
1357 // VK_BACK messages with odd scancode. So, we don't need to handle
1358 // VK_BACK specially at least for now.
1360 if (mCodeNameIndex
== CODE_NAME_INDEX_UNKNOWN
) {
1361 // If current event is not caused by physical key operation, it may be
1362 // caused by a keyboard utility. If so, the event shouldn't be ignored by
1363 // BrowserChild since it want to insert the character, delete a character or
1368 if (mOriginalVirtualKeyCode
== VK_PACKET
) {
1369 // If the message is VK_PACKET, that means that a keyboard utility
1370 // tries to insert a character.
1374 switch (mMsg
.message
) {
1377 case MOZ_WM_KEYDOWN
:
1381 case WM_SYSDEADCHAR
:
1382 // However, some keyboard layouts may send some keyboard messages with
1383 // activating the bit. If we dispatch repeated keyboard events, they
1384 // may be ignored by BrowserChild due to performance reason. So, we need
1385 // to check if actually a physical key is repeated by the auto-repeat
1387 switch (aLastKeyMSG
.message
) {
1390 case MOZ_WM_KEYDOWN
:
1391 if (aLastKeyMSG
.wParam
== VK_PACKET
) {
1392 // If the last message was VK_PACKET, that means that a keyboard
1393 // utility tried to insert a character. So, current message is
1394 // not repeated key event of the previous event.
1397 // Let's check whether current message and previous message are
1398 // caused by same physical key.
1399 mIsSkippableInRemoteProcess
=
1400 mScanCode
== WinUtils::GetScanCode(aLastKeyMSG
.lParam
) &&
1401 mIsExtended
== WinUtils::IsExtendedScanCode(aLastKeyMSG
.lParam
);
1404 // If previous message is not a keydown, this must not be generated
1405 // by the auto-repeat feature.
1410 MOZ_ASSERT_UNREACHABLE(
1411 "WM_APPCOMMAND should be handled in "
1412 "InitWithAppCommand()");
1415 // keyup message shouldn't be repeated by the auto-repeat feature.
1420 void NativeKey::InitWithKeyOrChar() {
1421 MSG lastKeyMSG
= sLastKeyMSG
;
1422 mScanCode
= WinUtils::GetScanCode(mMsg
.lParam
);
1423 mIsExtended
= WinUtils::IsExtendedScanCode(mMsg
.lParam
);
1424 switch (mMsg
.message
) {
1429 case MOZ_WM_KEYDOWN
:
1430 case MOZ_WM_KEYUP
: {
1431 // Modify sLastKeyMSG now since retrieving following char messages may
1432 // cause sending another key message if odd tool hooks GetMessage(),
1436 // Note that we don't need to compute raw virtual keycode here even when
1437 // it's VK_PROCESS (i.e., already handled by IME) because we need to
1438 // export it as DOM_VK_PROCESS and KEY_NAME_INDEX_Process.
1439 mOriginalVirtualKeyCode
= static_cast<uint8_t>(mMsg
.wParam
);
1441 // If the key message is sent from other application like a11y tools, the
1442 // scancode value might not be set proper value. Then, probably the value
1444 // NOTE: If the virtual keycode can be caused by both non-extended key
1445 // and extended key, the API returns the non-extended key's
1446 // scancode. E.g., VK_LEFT causes "4" key on numpad.
1447 if (!mScanCode
&& mOriginalVirtualKeyCode
!= VK_PACKET
) {
1448 uint16_t scanCodeEx
= ComputeScanCodeExFromVirtualKeyCode(mMsg
.wParam
);
1450 mScanCode
= static_cast<uint8_t>(scanCodeEx
& 0xFF);
1451 uint8_t extended
= static_cast<uint8_t>((scanCodeEx
& 0xFF00) >> 8);
1452 mIsExtended
= (extended
== 0xE0) || (extended
== 0xE1);
1456 // Most keys are not distinguished as left or right keys.
1457 bool isLeftRightDistinguishedKey
= false;
1459 // mOriginalVirtualKeyCode must not distinguish left or right of
1460 // Shift, Control or Alt.
1461 switch (mOriginalVirtualKeyCode
) {
1465 isLeftRightDistinguishedKey
= true;
1469 mVirtualKeyCode
= mOriginalVirtualKeyCode
;
1470 mOriginalVirtualKeyCode
= VK_SHIFT
;
1471 isLeftRightDistinguishedKey
= true;
1475 mVirtualKeyCode
= mOriginalVirtualKeyCode
;
1476 mOriginalVirtualKeyCode
= VK_CONTROL
;
1477 isLeftRightDistinguishedKey
= true;
1481 mVirtualKeyCode
= mOriginalVirtualKeyCode
;
1482 mOriginalVirtualKeyCode
= VK_MENU
;
1483 isLeftRightDistinguishedKey
= true;
1487 // If virtual keycode (left-right distinguished keycode) is already
1488 // computed, we don't need to do anymore.
1489 if (mVirtualKeyCode
) {
1493 // If the keycode doesn't have LR distinguished keycode, we just set
1494 // mOriginalVirtualKeyCode to mVirtualKeyCode. Note that don't compute
1495 // it from MapVirtualKeyEx() because the scan code might be wrong if
1496 // the message is sent/posted by other application. Then, we will compute
1497 // unexpected keycode from the scan code.
1498 if (!isLeftRightDistinguishedKey
) {
1502 NS_ASSERTION(!mVirtualKeyCode
,
1503 "mVirtualKeyCode has been computed already");
1505 // Otherwise, compute the virtual keycode with MapVirtualKeyEx().
1506 mVirtualKeyCode
= ComputeVirtualKeyCodeFromScanCodeEx();
1508 // Following code shouldn't be used now because we compute scancode value
1509 // if we detect that the sender doesn't set proper scancode.
1510 // However, the detection might fail. Therefore, let's keep using this.
1511 switch (mOriginalVirtualKeyCode
) {
1513 if (mVirtualKeyCode
!= VK_LCONTROL
&&
1514 mVirtualKeyCode
!= VK_RCONTROL
) {
1515 mVirtualKeyCode
= mIsExtended
? VK_RCONTROL
: VK_LCONTROL
;
1519 if (mVirtualKeyCode
!= VK_LMENU
&& mVirtualKeyCode
!= VK_RMENU
) {
1520 mVirtualKeyCode
= mIsExtended
? VK_RMENU
: VK_LMENU
;
1524 if (mVirtualKeyCode
!= VK_LSHIFT
&& mVirtualKeyCode
!= VK_RSHIFT
) {
1525 // Neither left shift nor right shift is an extended key,
1526 // let's use VK_LSHIFT for unknown mapping.
1527 mVirtualKeyCode
= VK_LSHIFT
;
1531 MOZ_CRASH("Unsupported mOriginalVirtualKeyCode");
1538 // If there is another instance and it is trying to remove a char message
1539 // from the queue, this message should be handled in the old instance.
1540 if (IsAnotherInstanceRemovingCharMessage()) {
1541 // XXX Do we need to make mReceivedMsg an array?
1542 MOZ_ASSERT(IsEmptyMSG(mLastInstance
->mReceivedMsg
));
1544 sNativeKeyLogger
, LogLevel::Warning
,
1545 ("%p NativeKey::InitWithKeyOrChar(), WARNING, detecting another "
1546 "instance is trying to remove a char message, so, this instance "
1547 "should do nothing, mLastInstance=0x%p, mRemovingMsg=%s, "
1549 this, mLastInstance
, ToString(mLastInstance
->mRemovingMsg
).get(),
1550 ToString(mLastInstance
->mReceivedMsg
).get()));
1551 mLastInstance
->mReceivedMsg
= mMsg
;
1555 // NOTE: If other applications like a11y tools sends WM_*CHAR without
1556 // scancode, we cannot compute virtual keycode. I.e., with such
1557 // applications, we cannot generate proper KeyboardEvent.code value.
1559 mVirtualKeyCode
= mOriginalVirtualKeyCode
=
1560 ComputeVirtualKeyCodeFromScanCodeEx();
1561 NS_ASSERTION(mVirtualKeyCode
, "Failed to compute virtual keycode");
1564 MOZ_CRASH("Unsupported 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(sNativeKeyLogger
, 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
);
1613 keyboardLayout
->InitNativeKey(*this);
1615 // Now, we can know if the key produces character(s) or a dead key with
1616 // AltGraph modifier. When user emulates AltGr key press with pressing
1617 // both Ctrl and Alt and the key produces character(s) or a dead key, we
1618 // need to replace Control and Alt state with AltGraph if the keyboard
1619 // layout has AltGr key.
1620 // Note that if Ctrl and/or Alt are pressed (not to emulate to press AltGr),
1621 // we need to set actual modifiers to eKeyDown and eKeyUp.
1622 if (MaybeEmulatingAltGraph() &&
1623 (mCommittedCharsAndModifiers
.IsProducingCharsWithAltGr() ||
1624 mKeyNameIndex
== KEY_NAME_INDEX_Dead
)) {
1625 mModKeyState
.Unset(MODIFIER_CONTROL
| MODIFIER_ALT
);
1626 mModKeyState
.Set(MODIFIER_ALTGRAPH
);
1630 (IsFollowedByDeadCharMessage() ||
1631 keyboardLayout
->IsDeadKey(mOriginalVirtualKeyCode
, mModKeyState
));
1632 mIsPrintableKey
= mKeyNameIndex
== KEY_NAME_INDEX_USE_STRING
||
1633 KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode
);
1634 // The repeat count in mMsg.lParam isn't useful to check whether the event
1635 // is caused by the auto-repeat feature because it's not incremented even
1636 // if it's repeated twice or more (i.e., always 1). Therefore, we need to
1637 // check previous key state (31th bit) instead. If it's 1, the key was down
1638 // before the message was sent.
1639 mIsRepeat
= (mMsg
.lParam
& (1 << 30)) != 0;
1640 InitIsSkippableForKeyOrChar(lastKeyMSG
);
1642 if (IsKeyDownMessage()) {
1643 // Compute some strings which may be inputted by the key with various
1644 // modifier state if this key event won't cause text input actually.
1645 // They will be used for setting mAlternativeCharCodes in the callback
1646 // method which will be called by TextEventDispatcher.
1647 if (!IsFollowedByPrintableCharMessage()) {
1648 ComputeInputtingStringWithKeyboardLayout();
1650 // Remove odd char messages if there are.
1651 RemoveFollowingOddCharMessages();
1655 void NativeKey::InitCommittedCharsAndModifiersWithFollowingCharMessages() {
1656 mCommittedCharsAndModifiers
.Clear();
1657 // This should cause inputting text in focused editor. However, it
1658 // ignores keypress events whose altKey or ctrlKey is true.
1659 // Therefore, we need to remove these modifier state here.
1660 Modifiers modifiers
= mModKeyState
.GetModifiers();
1661 if (IsFollowedByPrintableCharMessage()) {
1662 modifiers
&= ~(MODIFIER_ALT
| MODIFIER_CONTROL
);
1663 if (MaybeEmulatingAltGraph()) {
1664 modifiers
|= MODIFIER_ALTGRAPH
;
1667 // NOTE: This method assumes that WM_CHAR and WM_SYSCHAR are never retrieved
1669 for (size_t i
= 0; i
< mFollowingCharMsgs
.Length(); ++i
) {
1670 // Ignore non-printable char messages.
1671 if (!IsPrintableCharOrSysCharMessage(mFollowingCharMsgs
[i
])) {
1674 char16_t ch
= static_cast<char16_t
>(mFollowingCharMsgs
[i
].wParam
);
1675 mCommittedCharsAndModifiers
.Append(ch
, modifiers
);
1679 NativeKey::~NativeKey() {
1680 MOZ_LOG(sNativeKeyLogger
, LogLevel::Debug
,
1681 ("%p NativeKey::~NativeKey(), destroyed", this));
1682 if (mIsOverridingKeyboardLayout
) {
1683 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
1684 keyboardLayout
->RestoreLayout();
1686 sLatestInstance
= mLastInstance
;
1689 void NativeKey::InitWithAppCommand() {
1690 if (GET_DEVICE_LPARAM(mMsg
.lParam
) != FAPPCOMMAND_KEY
) {
1694 uint32_t appCommand
= GET_APPCOMMAND_LPARAM(mMsg
.lParam
);
1695 switch (GET_APPCOMMAND_LPARAM(mMsg
.lParam
)) {
1696 #undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX
1697 #define NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX(aAppCommand, aKeyNameIndex) \
1699 mKeyNameIndex = aKeyNameIndex; \
1702 #include "NativeKeyToDOMKeyName.h"
1704 #undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX
1707 mKeyNameIndex
= KEY_NAME_INDEX_Unidentified
;
1710 // Guess the virtual keycode which caused this message.
1711 switch (appCommand
) {
1712 case APPCOMMAND_BROWSER_BACKWARD
:
1713 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_BACK
;
1715 case APPCOMMAND_BROWSER_FORWARD
:
1716 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_FORWARD
;
1718 case APPCOMMAND_BROWSER_REFRESH
:
1719 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_REFRESH
;
1721 case APPCOMMAND_BROWSER_STOP
:
1722 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_STOP
;
1724 case APPCOMMAND_BROWSER_SEARCH
:
1725 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_SEARCH
;
1727 case APPCOMMAND_BROWSER_FAVORITES
:
1728 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_FAVORITES
;
1730 case APPCOMMAND_BROWSER_HOME
:
1731 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_BROWSER_HOME
;
1733 case APPCOMMAND_VOLUME_MUTE
:
1734 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_VOLUME_MUTE
;
1736 case APPCOMMAND_VOLUME_DOWN
:
1737 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_VOLUME_DOWN
;
1739 case APPCOMMAND_VOLUME_UP
:
1740 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_VOLUME_UP
;
1742 case APPCOMMAND_MEDIA_NEXTTRACK
:
1743 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_MEDIA_NEXT_TRACK
;
1745 case APPCOMMAND_MEDIA_PREVIOUSTRACK
:
1746 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_MEDIA_PREV_TRACK
;
1748 case APPCOMMAND_MEDIA_STOP
:
1749 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_MEDIA_STOP
;
1751 case APPCOMMAND_MEDIA_PLAY_PAUSE
:
1752 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_MEDIA_PLAY_PAUSE
;
1754 case APPCOMMAND_LAUNCH_MAIL
:
1755 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_LAUNCH_MAIL
;
1757 case APPCOMMAND_LAUNCH_MEDIA_SELECT
:
1758 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_LAUNCH_MEDIA_SELECT
;
1760 case APPCOMMAND_LAUNCH_APP1
:
1761 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_LAUNCH_APP1
;
1763 case APPCOMMAND_LAUNCH_APP2
:
1764 mVirtualKeyCode
= mOriginalVirtualKeyCode
= VK_LAUNCH_APP2
;
1770 uint16_t scanCodeEx
= ComputeScanCodeExFromVirtualKeyCode(mVirtualKeyCode
);
1771 mScanCode
= static_cast<uint8_t>(scanCodeEx
& 0xFF);
1772 uint8_t extended
= static_cast<uint8_t>((scanCodeEx
& 0xFF00) >> 8);
1773 mIsExtended
= (extended
== 0xE0) || (extended
== 0xE1);
1774 mDOMKeyCode
= KeyboardLayout::GetInstance()->ConvertNativeKeyCodeToDOMKeyCode(
1775 mOriginalVirtualKeyCode
);
1776 mCodeNameIndex
= KeyboardLayout::ConvertScanCodeToCodeNameIndex(
1777 GetScanCodeWithExtendedFlag());
1778 // If we can map the WM_APPCOMMAND to a virtual keycode, we can trust
1779 // the result of GetKeyboardState(). Otherwise, we dispatch both
1780 // keydown and keyup events from WM_APPCOMMAND handler. Therefore,
1781 // even if WM_APPCOMMAND is caused by auto key repeat, web apps receive
1782 // a pair of DOM keydown and keyup events. I.e., KeyboardEvent.repeat
1783 // should be never true of such keys.
1784 // XXX Isn't the key state always true? If the key press caused this
1785 // WM_APPCOMMAND, that means it's pressed at that time.
1786 if (mVirtualKeyCode
) {
1788 memset(kbdState
, 0, sizeof(kbdState
));
1789 ::GetKeyboardState(kbdState
);
1790 mIsSkippableInRemoteProcess
= mIsRepeat
= !!kbdState
[mVirtualKeyCode
];
1794 bool NativeKey::MaybeEmulatingAltGraph() const {
1795 return IsControl() && IsAlt() && KeyboardLayout::GetInstance()->HasAltGr();
1799 bool NativeKey::IsControlChar(char16_t aChar
) {
1800 static const char16_t U_SPACE
= 0x20;
1801 static const char16_t U_DELETE
= 0x7F;
1802 return aChar
< U_SPACE
|| aChar
== U_DELETE
;
1805 bool NativeKey::IsFollowedByDeadCharMessage() const {
1806 if (mFollowingCharMsgs
.IsEmpty()) {
1809 return IsDeadCharMessage(mFollowingCharMsgs
[0]);
1812 bool NativeKey::IsFollowedByPrintableCharMessage() const {
1813 for (size_t i
= 0; i
< mFollowingCharMsgs
.Length(); ++i
) {
1814 if (IsPrintableCharMessage(mFollowingCharMsgs
[i
])) {
1821 bool NativeKey::IsFollowedByPrintableCharOrSysCharMessage() const {
1822 for (size_t i
= 0; i
< mFollowingCharMsgs
.Length(); ++i
) {
1823 if (IsPrintableCharOrSysCharMessage(mFollowingCharMsgs
[i
])) {
1830 bool NativeKey::IsReservedBySystem() const {
1831 // Alt+Space key is handled by OS, we shouldn't touch it.
1832 if (mModKeyState
.IsAlt() && !mModKeyState
.IsControl() &&
1833 mVirtualKeyCode
== VK_SPACE
) {
1837 // XXX How about Alt+F4? We receive WM_SYSKEYDOWN for F4 before closing the
1838 // window. Although, we don't prevent to close the window but the key
1839 // event shouldn't be exposed to the web.
1844 bool NativeKey::IsIMEDoingKakuteiUndo() const {
1845 // Following message pattern is caused by "Kakutei-Undo" of ATOK or WXG:
1846 // ---------------------------------------------------------------------------
1847 // WM_KEYDOWN * n (wParam = VK_BACK, lParam = 0x1)
1848 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC0000001) # ATOK
1849 // WM_IME_STARTCOMPOSITION * 1 (wParam = 0x0, lParam = 0x0)
1850 // WM_IME_COMPOSITION * 1 (wParam = 0x0, lParam = 0x1BF)
1851 // WM_CHAR * n (wParam = VK_BACK, lParam = 0x1)
1852 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC00E0001)
1853 // ---------------------------------------------------------------------------
1854 // This doesn't match usual key message pattern such as:
1855 // WM_KEYDOWN -> WM_CHAR -> WM_KEYDOWN -> WM_CHAR -> ... -> WM_KEYUP
1856 // See following bugs for the detail.
1857 // https://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese)
1858 // https://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English)
1859 MSG startCompositionMsg
, compositionMsg
, charMsg
;
1860 return WinUtils::PeekMessage(&startCompositionMsg
, mMsg
.hwnd
,
1861 WM_IME_STARTCOMPOSITION
, WM_IME_STARTCOMPOSITION
,
1862 PM_NOREMOVE
| PM_NOYIELD
) &&
1863 WinUtils::PeekMessage(&compositionMsg
, mMsg
.hwnd
, WM_IME_COMPOSITION
,
1864 WM_IME_COMPOSITION
, PM_NOREMOVE
| PM_NOYIELD
) &&
1865 WinUtils::PeekMessage(&charMsg
, mMsg
.hwnd
, WM_CHAR
, WM_CHAR
,
1866 PM_NOREMOVE
| PM_NOYIELD
) &&
1867 startCompositionMsg
.wParam
== 0x0 &&
1868 startCompositionMsg
.lParam
== 0x0 && compositionMsg
.wParam
== 0x0 &&
1869 compositionMsg
.lParam
== 0x1BF && charMsg
.wParam
== VK_BACK
&&
1870 charMsg
.lParam
== 0x1 &&
1871 startCompositionMsg
.time
<= compositionMsg
.time
&&
1872 compositionMsg
.time
<= charMsg
.time
;
1875 void NativeKey::RemoveFollowingOddCharMessages() {
1876 MOZ_ASSERT(IsKeyDownMessage());
1878 // If the keydown message is synthesized for automated tests, there is
1879 // nothing to do here.
1880 if (mFakeCharMsgs
) {
1884 // If there are some following char messages before another key message,
1885 // there is nothing to do here.
1886 if (!mFollowingCharMsgs
.IsEmpty()) {
1890 // If the handling key isn't Backspace, there is nothing to do here.
1891 if (mOriginalVirtualKeyCode
!= VK_BACK
) {
1895 // If we don't see the odd message pattern, there is nothing to do here.
1896 if (!IsIMEDoingKakuteiUndo()) {
1900 // Otherwise, we need to remove odd WM_CHAR messages for ATOK or WXG (both
1901 // of them are Japanese IME).
1903 while (WinUtils::PeekMessage(&msg
, mMsg
.hwnd
, WM_CHAR
, WM_CHAR
,
1904 PM_REMOVE
| PM_NOYIELD
)) {
1905 if (msg
.message
!= WM_CHAR
) {
1906 MOZ_RELEASE_ASSERT(msg
.message
== WM_NULL
,
1907 "Unexpected message was removed");
1911 sNativeKeyLogger
, LogLevel::Info
,
1912 ("%p NativeKey::RemoveFollowingOddCharMessages(), removed odd char "
1914 this, ToString(msg
).get()));
1915 mRemovedOddCharMsgs
.AppendElement(msg
);
1919 UINT
NativeKey::GetScanCodeWithExtendedFlag() const {
1923 return (0xE000 | mScanCode
);
1926 uint32_t NativeKey::GetKeyLocation() const {
1927 switch (mVirtualKeyCode
) {
1932 return eKeyLocationLeft
;
1938 return eKeyLocationRight
;
1941 // XXX This code assumes that all keyboard drivers use same mapping.
1942 return !mIsExtended
? eKeyLocationStandard
: eKeyLocationNumpad
;
1955 // XXX This code assumes that all keyboard drivers use same mapping.
1956 return mIsExtended
? eKeyLocationStandard
: eKeyLocationNumpad
;
1958 // NumLock key isn't included due to IE9's behavior.
1974 // Separator key of Brazilian keyboard or JIS keyboard for Mac
1976 return eKeyLocationNumpad
;
1981 NS_WARNING("Failed to decide the key location?");
1984 return eKeyLocationStandard
;
1988 uint8_t NativeKey::ComputeVirtualKeyCodeFromScanCode() const {
1989 return static_cast<uint8_t>(
1990 ::MapVirtualKeyEx(mScanCode
, MAPVK_VSC_TO_VK
, mKeyboardLayout
));
1993 uint8_t NativeKey::ComputeVirtualKeyCodeFromScanCodeEx() const {
1994 // MapVirtualKeyEx() has been improved for supporting extended keys since
1995 // Vista. When we call it for mapping a scancode of an extended key and
1996 // a virtual keycode, we need to add 0xE000 to the scancode.
1997 return static_cast<uint8_t>(::MapVirtualKeyEx(
1998 GetScanCodeWithExtendedFlag(), MAPVK_VSC_TO_VK_EX
, mKeyboardLayout
));
2001 uint16_t NativeKey::ComputeScanCodeExFromVirtualKeyCode(
2002 UINT aVirtualKeyCode
) const {
2003 return static_cast<uint16_t>(
2004 ::MapVirtualKeyEx(aVirtualKeyCode
, MAPVK_VK_TO_VSC_EX
, mKeyboardLayout
));
2007 char16_t
NativeKey::ComputeUnicharFromScanCode() const {
2008 return static_cast<char16_t
>(::MapVirtualKeyEx(
2009 ComputeVirtualKeyCodeFromScanCode(), MAPVK_VK_TO_CHAR
, mKeyboardLayout
));
2012 nsEventStatus
NativeKey::InitKeyEvent(WidgetKeyboardEvent
& aKeyEvent
,
2013 const MSG
* aMsgSentToPlugin
) const {
2014 return InitKeyEvent(aKeyEvent
, mModKeyState
, aMsgSentToPlugin
);
2017 nsEventStatus
NativeKey::InitKeyEvent(WidgetKeyboardEvent
& aKeyEvent
,
2018 const ModifierKeyState
& aModKeyState
,
2019 const MSG
* aMsgSentToPlugin
) const {
2020 if (mWidget
->Destroyed()) {
2021 MOZ_CRASH("NativeKey tries to dispatch a key event on destroyed widget");
2024 LayoutDeviceIntPoint
point(0, 0);
2025 mWidget
->InitEvent(aKeyEvent
, &point
);
2027 switch (aKeyEvent
.mMessage
) {
2029 // If it was followed by a char message but it was consumed by somebody,
2030 // we should mark it as consumed because somebody must have handled it
2031 // and we should prevent to do "double action" for the key operation.
2032 // However, for compatibility with older version and other browsers,
2033 // we should dispatch the events even in the web content.
2034 if (mCharMessageHasGone
) {
2035 aKeyEvent
.PreventDefaultBeforeDispatch(CrossProcessForwarding::eAllow
);
2038 case eKeyDownOnPlugin
:
2039 aKeyEvent
.mKeyCode
= mDOMKeyCode
;
2040 // Unique id for this keydown event and its associated keypress.
2041 sUniqueKeyEventId
++;
2042 aKeyEvent
.mUniqueId
= sUniqueKeyEventId
;
2045 case eKeyUpOnPlugin
:
2046 aKeyEvent
.mKeyCode
= mDOMKeyCode
;
2047 // Set defaultPrevented of the key event if the VK_MENU is not a system
2048 // key release, so that the menu bar does not trigger. This helps avoid
2049 // triggering the menu bar for ALT key accelerators used in assistive
2050 // technologies such as Window-Eyes and ZoomText or for switching open
2051 // state of IME. On the other hand, we should dispatch the events even
2052 // in the web content for compatibility with older version and other
2054 if (mOriginalVirtualKeyCode
== VK_MENU
&& mMsg
.message
!= WM_SYSKEYUP
) {
2055 aKeyEvent
.PreventDefaultBeforeDispatch(CrossProcessForwarding::eAllow
);
2059 MOZ_ASSERT(!mCharMessageHasGone
,
2060 "If following char message was consumed by somebody, "
2061 "keydown event should be consumed above");
2062 aKeyEvent
.mUniqueId
= sUniqueKeyEventId
;
2065 MOZ_CRASH("Invalid event message");
2068 aKeyEvent
.mIsRepeat
= mIsRepeat
;
2069 aKeyEvent
.mMaybeSkippableInRemoteProcess
= mIsSkippableInRemoteProcess
;
2070 aKeyEvent
.mKeyNameIndex
= mKeyNameIndex
;
2071 if (mKeyNameIndex
== KEY_NAME_INDEX_USE_STRING
) {
2072 aKeyEvent
.mKeyValue
= mCommittedCharsAndModifiers
.ToString();
2074 aKeyEvent
.mCodeNameIndex
= mCodeNameIndex
;
2075 MOZ_ASSERT(mCodeNameIndex
!= CODE_NAME_INDEX_USE_STRING
);
2076 aKeyEvent
.mLocation
= GetKeyLocation();
2077 aModKeyState
.InitInputEvent(aKeyEvent
);
2079 if (aMsgSentToPlugin
) {
2080 MaybeInitPluginEventOfKeyEvent(aKeyEvent
, *aMsgSentToPlugin
);
2083 KeyboardLayout::NotifyIdleServiceOfUserActivity();
2086 sNativeKeyLogger
, LogLevel::Info
,
2087 ("%p NativeKey::InitKeyEvent(), initialized, aKeyEvent={ "
2088 "mMessage=%s, mKeyNameIndex=%s, mKeyValue=\"%s\", mCodeNameIndex=%s, "
2089 "mKeyCode=%s, mLocation=%s, mModifiers=%s, DefaultPrevented()=%s }",
2090 this, ToChar(aKeyEvent
.mMessage
),
2091 ToString(aKeyEvent
.mKeyNameIndex
).get(),
2092 NS_ConvertUTF16toUTF8(aKeyEvent
.mKeyValue
).get(),
2093 ToString(aKeyEvent
.mCodeNameIndex
).get(),
2094 GetDOMKeyCodeName(aKeyEvent
.mKeyCode
).get(),
2095 GetKeyLocationName(aKeyEvent
.mLocation
).get(),
2096 GetModifiersName(aKeyEvent
.mModifiers
).get(),
2097 GetBoolName(aKeyEvent
.DefaultPrevented())));
2099 return aKeyEvent
.DefaultPrevented() ? nsEventStatus_eConsumeNoDefault
2100 : nsEventStatus_eIgnore
;
2103 void NativeKey::MaybeInitPluginEventOfKeyEvent(
2104 WidgetKeyboardEvent
& aKeyEvent
, const MSG
& aMsgSentToPlugin
) const {
2105 if (mWidget
->GetInputContext().mIMEState
.mEnabled
!= IMEState::PLUGIN
) {
2108 NPEvent pluginEvent
;
2109 pluginEvent
.event
= aMsgSentToPlugin
.message
;
2110 pluginEvent
.wParam
= aMsgSentToPlugin
.wParam
;
2111 pluginEvent
.lParam
= aMsgSentToPlugin
.lParam
;
2112 aKeyEvent
.mPluginEvent
.Copy(pluginEvent
);
2115 bool NativeKey::DispatchCommandEvent(uint32_t aEventCommand
) const {
2116 RefPtr
<nsAtom
> command
;
2117 switch (aEventCommand
) {
2118 case APPCOMMAND_BROWSER_BACKWARD
:
2119 command
= nsGkAtoms::Back
;
2121 case APPCOMMAND_BROWSER_FORWARD
:
2122 command
= nsGkAtoms::Forward
;
2124 case APPCOMMAND_BROWSER_REFRESH
:
2125 command
= nsGkAtoms::Reload
;
2127 case APPCOMMAND_BROWSER_STOP
:
2128 command
= nsGkAtoms::Stop
;
2130 case APPCOMMAND_BROWSER_SEARCH
:
2131 command
= nsGkAtoms::Search
;
2133 case APPCOMMAND_BROWSER_FAVORITES
:
2134 command
= nsGkAtoms::Bookmarks
;
2136 case APPCOMMAND_BROWSER_HOME
:
2137 command
= nsGkAtoms::Home
;
2139 case APPCOMMAND_CLOSE
:
2140 command
= nsGkAtoms::Close
;
2142 case APPCOMMAND_FIND
:
2143 command
= nsGkAtoms::Find
;
2145 case APPCOMMAND_HELP
:
2146 command
= nsGkAtoms::Help
;
2148 case APPCOMMAND_NEW
:
2149 command
= nsGkAtoms::New
;
2151 case APPCOMMAND_OPEN
:
2152 command
= nsGkAtoms::Open
;
2154 case APPCOMMAND_PRINT
:
2155 command
= nsGkAtoms::Print
;
2157 case APPCOMMAND_SAVE
:
2158 command
= nsGkAtoms::Save
;
2160 case APPCOMMAND_FORWARD_MAIL
:
2161 command
= nsGkAtoms::ForwardMail
;
2163 case APPCOMMAND_REPLY_TO_MAIL
:
2164 command
= nsGkAtoms::ReplyToMail
;
2166 case APPCOMMAND_SEND_MAIL
:
2167 command
= nsGkAtoms::SendMail
;
2169 case APPCOMMAND_MEDIA_NEXTTRACK
:
2170 command
= nsGkAtoms::NextTrack
;
2172 case APPCOMMAND_MEDIA_PREVIOUSTRACK
:
2173 command
= nsGkAtoms::PreviousTrack
;
2175 case APPCOMMAND_MEDIA_STOP
:
2176 command
= nsGkAtoms::MediaStop
;
2178 case APPCOMMAND_MEDIA_PLAY_PAUSE
:
2179 command
= nsGkAtoms::PlayPause
;
2183 sNativeKeyLogger
, LogLevel::Info
,
2184 ("%p NativeKey::DispatchCommandEvent(), doesn't dispatch command "
2189 WidgetCommandEvent
appCommandEvent(true, command
, mWidget
);
2191 mWidget
->InitEvent(appCommandEvent
);
2192 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2193 ("%p NativeKey::DispatchCommandEvent(), dispatching "
2194 "%s app command event...",
2195 this, nsAtomCString(command
).get()));
2197 mWidget
->DispatchWindowEvent(&appCommandEvent
) || mWidget
->Destroyed();
2199 sNativeKeyLogger
, LogLevel::Info
,
2200 ("%p NativeKey::DispatchCommandEvent(), dispatched app command event, "
2201 "result=%s, mWidget->Destroyed()=%s",
2202 this, GetBoolName(ok
), GetBoolName(mWidget
->Destroyed())));
2206 bool NativeKey::HandleAppCommandMessage() const {
2207 // If the widget has gone, we should do nothing.
2208 if (mWidget
->Destroyed()) {
2209 MOZ_LOG(sNativeKeyLogger
, LogLevel::Warning
,
2210 ("%p NativeKey::HandleAppCommandMessage(), WARNING, not handled "
2212 "destroyed the widget",
2217 // NOTE: Typical behavior of WM_APPCOMMAND caused by key is, WM_APPCOMMAND
2218 // message is _sent_ first. Then, the DefaultWndProc will _post_
2219 // WM_KEYDOWN message and WM_KEYUP message if the keycode for the
2220 // command is available (i.e., mVirtualKeyCode is not 0).
2222 // NOTE: IntelliType (Microsoft's keyboard utility software) always consumes
2223 // WM_KEYDOWN and WM_KEYUP.
2225 // Let's dispatch keydown message before our chrome handles the command
2226 // when the message is caused by a keypress. This behavior makes handling
2227 // WM_APPCOMMAND be a default action of the keydown event. This means that
2228 // web applications can handle multimedia keys and prevent our default action.
2229 // This allow web applications to provide better UX for multimedia keyboard
2231 bool dispatchKeyEvent
= (GET_DEVICE_LPARAM(mMsg
.lParam
) == FAPPCOMMAND_KEY
);
2232 if (dispatchKeyEvent
) {
2233 // If a plug-in window has focus but it didn't consume the message, our
2234 // window receive WM_APPCOMMAND message. In this case, we shouldn't
2235 // dispatch KeyboardEvents because an event handler may access the
2236 // plug-in process synchronously.
2238 WinUtils::IsOurProcessWindow(reinterpret_cast<HWND
>(mMsg
.wParam
));
2241 bool consumed
= false;
2243 if (dispatchKeyEvent
) {
2244 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2245 if (NS_WARN_IF(NS_FAILED(rv
))) {
2246 MOZ_LOG(sNativeKeyLogger
, LogLevel::Error
,
2247 ("%p NativeKey::HandleAppCommandMessage(), FAILED due to "
2248 "BeginNativeInputTransaction() failure",
2252 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2253 ("%p NativeKey::HandleAppCommandMessage(), initializing keydown "
2256 WidgetKeyboardEvent
keydownEvent(true, eKeyDown
, mWidget
);
2257 nsEventStatus status
= InitKeyEvent(keydownEvent
, mModKeyState
, &mMsg
);
2258 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2259 ("%p NativeKey::HandleAppCommandMessage(), tries to dispatch "
2262 // NOTE: If the keydown event is consumed by web contents, we shouldn't
2263 // continue to handle the command.
2264 if (!mDispatcher
->DispatchKeyboardEvent(eKeyDown
, keydownEvent
, status
,
2265 const_cast<NativeKey
*>(this))) {
2266 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2267 ("%p NativeKey::HandleAppCommandMessage(), keydown event isn't "
2270 // If keyboard event wasn't fired, there must be composition.
2271 // So, we don't need to dispatch a command event.
2274 consumed
= status
== nsEventStatus_eConsumeNoDefault
;
2275 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2276 ("%p NativeKey::HandleAppCommandMessage(), keydown event was "
2277 "dispatched, consumed=%s",
2278 this, GetBoolName(consumed
)));
2279 sDispatchedKeyOfAppCommand
= mVirtualKeyCode
;
2280 if (mWidget
->Destroyed()) {
2282 sNativeKeyLogger
, LogLevel::Info
,
2283 ("%p NativeKey::HandleAppCommandMessage(), keydown event caused "
2284 "destroying the widget",
2290 // Dispatch a command event or a content command event if the command is
2293 uint32_t appCommand
= GET_APPCOMMAND_LPARAM(mMsg
.lParam
);
2294 EventMessage contentCommandMessage
= eVoidEvent
;
2295 switch (appCommand
) {
2296 case APPCOMMAND_BROWSER_BACKWARD
:
2297 case APPCOMMAND_BROWSER_FORWARD
:
2298 case APPCOMMAND_BROWSER_REFRESH
:
2299 case APPCOMMAND_BROWSER_STOP
:
2300 case APPCOMMAND_BROWSER_SEARCH
:
2301 case APPCOMMAND_BROWSER_FAVORITES
:
2302 case APPCOMMAND_BROWSER_HOME
:
2303 case APPCOMMAND_CLOSE
:
2304 case APPCOMMAND_FIND
:
2305 case APPCOMMAND_HELP
:
2306 case APPCOMMAND_NEW
:
2307 case APPCOMMAND_OPEN
:
2308 case APPCOMMAND_PRINT
:
2309 case APPCOMMAND_SAVE
:
2310 case APPCOMMAND_FORWARD_MAIL
:
2311 case APPCOMMAND_REPLY_TO_MAIL
:
2312 case APPCOMMAND_SEND_MAIL
:
2313 case APPCOMMAND_MEDIA_NEXTTRACK
:
2314 case APPCOMMAND_MEDIA_PREVIOUSTRACK
:
2315 case APPCOMMAND_MEDIA_STOP
:
2316 case APPCOMMAND_MEDIA_PLAY_PAUSE
:
2317 // We shouldn't consume the message always because if we don't handle
2318 // the message, the sender (typically, utility of keyboard or mouse)
2319 // may send other key messages which indicate well known shortcut key.
2320 consumed
= DispatchCommandEvent(appCommand
);
2323 // Use content command for following commands:
2324 case APPCOMMAND_COPY
:
2325 contentCommandMessage
= eContentCommandCopy
;
2327 case APPCOMMAND_CUT
:
2328 contentCommandMessage
= eContentCommandCut
;
2330 case APPCOMMAND_PASTE
:
2331 contentCommandMessage
= eContentCommandPaste
;
2333 case APPCOMMAND_REDO
:
2334 contentCommandMessage
= eContentCommandRedo
;
2336 case APPCOMMAND_UNDO
:
2337 contentCommandMessage
= eContentCommandUndo
;
2341 if (contentCommandMessage
) {
2342 MOZ_ASSERT(!mWidget
->Destroyed());
2343 WidgetContentCommandEvent
contentCommandEvent(true, contentCommandMessage
,
2346 sNativeKeyLogger
, LogLevel::Info
,
2347 ("%p NativeKey::HandleAppCommandMessage(), dispatching %s event...",
2348 this, ToChar(contentCommandMessage
)));
2349 mWidget
->DispatchWindowEvent(&contentCommandEvent
);
2350 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2351 ("%p NativeKey::HandleAppCommandMessage(), dispatched %s event",
2352 this, ToChar(contentCommandMessage
)));
2355 if (mWidget
->Destroyed()) {
2356 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2357 ("%p NativeKey::HandleAppCommandMessage(), %s event caused "
2358 "destroying the widget",
2359 this, ToChar(contentCommandMessage
)));
2363 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2364 ("%p NativeKey::HandleAppCommandMessage(), doesn't dispatch "
2371 // Dispatch a keyup event if the command is caused by pressing a key and
2372 // the key isn't mapped to a virtual keycode.
2373 if (dispatchKeyEvent
&& !mVirtualKeyCode
) {
2374 MOZ_ASSERT(!mWidget
->Destroyed());
2375 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2376 if (NS_WARN_IF(NS_FAILED(rv
))) {
2377 MOZ_LOG(sNativeKeyLogger
, LogLevel::Error
,
2378 ("%p NativeKey::HandleAppCommandMessage(), FAILED due to "
2379 "BeginNativeInputTransaction() failure",
2383 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2384 ("%p NativeKey::HandleAppCommandMessage(), initializing keyup "
2387 WidgetKeyboardEvent
keyupEvent(true, eKeyUp
, mWidget
);
2388 nsEventStatus status
= InitKeyEvent(keyupEvent
, mModKeyState
, &mMsg
);
2389 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2390 ("%p NativeKey::HandleAppCommandMessage(), dispatching keyup "
2393 // NOTE: Ignore if the keyup event is consumed because keyup event
2394 // represents just a physical key event state change.
2395 mDispatcher
->DispatchKeyboardEvent(eKeyUp
, keyupEvent
, status
,
2396 const_cast<NativeKey
*>(this));
2398 sNativeKeyLogger
, LogLevel::Info
,
2399 ("%p NativeKey::HandleAppCommandMessage(), dispatched keyup event",
2401 if (mWidget
->Destroyed()) {
2402 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2403 ("%p NativeKey::HandleAppCommandMessage(), %s event caused "
2404 "destroying the widget",
2413 bool NativeKey::HandleKeyDownMessage(bool* aEventDispatched
) const {
2414 MOZ_ASSERT(IsKeyDownMessage());
2416 if (aEventDispatched
) {
2417 *aEventDispatched
= false;
2420 if (sDispatchedKeyOfAppCommand
&&
2421 sDispatchedKeyOfAppCommand
== mOriginalVirtualKeyCode
) {
2422 // The multimedia key event has already been dispatch from
2423 // HandleAppCommandMessage().
2424 sDispatchedKeyOfAppCommand
= 0;
2425 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2426 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2427 "event due to already dispatched from HandleAppCommandMessage(), ",
2429 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg
)) {
2430 RedirectedKeyDownMessageManager::Forget();
2435 if (IsReservedBySystem()) {
2436 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2437 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2438 "event because the key combination is reserved by the system",
2440 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg
)) {
2441 RedirectedKeyDownMessageManager::Forget();
2446 // If the widget has gone, we should do nothing.
2447 if (mWidget
->Destroyed()) {
2449 sNativeKeyLogger
, LogLevel::Warning
,
2450 ("%p NativeKey::HandleKeyDownMessage(), WARNING, not handled due to "
2451 "destroyed the widget",
2453 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg
)) {
2454 RedirectedKeyDownMessageManager::Forget();
2459 bool defaultPrevented
= false;
2460 if (mFakeCharMsgs
|| IsKeyMessageOnPlugin() ||
2461 !RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg
)) {
2462 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2463 if (NS_WARN_IF(NS_FAILED(rv
))) {
2464 MOZ_LOG(sNativeKeyLogger
, LogLevel::Error
,
2465 ("%p NativeKey::HandleKeyDownMessage(), FAILED due to "
2466 "BeginNativeInputTransaction() failure",
2471 bool isIMEEnabled
= WinUtils::IsIMEEnabled(mWidget
->GetInputContext());
2473 MOZ_LOG(sNativeKeyLogger
, LogLevel::Debug
,
2474 ("%p NativeKey::HandleKeyDownMessage(), initializing keydown "
2478 EventMessage keyDownMessage
=
2479 IsKeyMessageOnPlugin() ? eKeyDownOnPlugin
: eKeyDown
;
2480 WidgetKeyboardEvent
keydownEvent(true, keyDownMessage
, mWidget
);
2481 nsEventStatus status
= InitKeyEvent(keydownEvent
, mModKeyState
, &mMsg
);
2483 sNativeKeyLogger
, LogLevel::Info
,
2484 ("%p NativeKey::HandleKeyDownMessage(), dispatching keydown event...",
2486 bool dispatched
= mDispatcher
->DispatchKeyboardEvent(
2487 keyDownMessage
, keydownEvent
, status
, const_cast<NativeKey
*>(this));
2488 if (aEventDispatched
) {
2489 *aEventDispatched
= dispatched
;
2492 // If the keydown event wasn't fired, there must be composition.
2493 // we don't need to do anything anymore.
2495 sNativeKeyLogger
, LogLevel::Info
,
2496 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keypress "
2497 "event(s) because keydown event isn't dispatched actually",
2501 defaultPrevented
= status
== nsEventStatus_eConsumeNoDefault
;
2503 // We don't need to handle key messages on plugin for eKeyPress since
2504 // eKeyDownOnPlugin is handled as both eKeyDown and eKeyPress.
2505 if (IsKeyMessageOnPlugin()) {
2507 sNativeKeyLogger
, LogLevel::Info
,
2508 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keypress "
2509 "event(s) because it's a keydown message on windowed plugin, "
2510 "defaultPrevented=%s",
2511 this, GetBoolName(defaultPrevented
)));
2512 return defaultPrevented
;
2515 if (mWidget
->Destroyed() || IsFocusedWindowChanged()) {
2516 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2517 ("%p NativeKey::HandleKeyDownMessage(), keydown event caused "
2518 "destroying the widget",
2524 sNativeKeyLogger
, LogLevel::Info
,
2525 ("%p NativeKey::HandleKeyDownMessage(), dispatched keydown event, "
2526 "dispatched=%s, defaultPrevented=%s",
2527 this, GetBoolName(dispatched
), GetBoolName(defaultPrevented
)));
2529 // If IMC wasn't associated to the window but is associated it now (i.e.,
2530 // focus is moved from a non-editable editor to an editor by keydown
2531 // event handler), WM_CHAR and WM_SYSCHAR shouldn't cause first character
2532 // inputting if IME is opened. But then, we should redirect the native
2533 // keydown message to IME.
2534 // However, note that if focus has been already moved to another
2535 // application, we shouldn't redirect the message to it because the keydown
2536 // message is processed by us, so, nobody shouldn't process it.
2537 HWND focusedWnd
= ::GetFocus();
2538 if (!defaultPrevented
&& !mFakeCharMsgs
&& !IsKeyMessageOnPlugin() &&
2539 focusedWnd
&& !mWidget
->PluginHasFocus() && !isIMEEnabled
&&
2540 WinUtils::IsIMEEnabled(mWidget
->GetInputContext())) {
2541 RedirectedKeyDownMessageManager::RemoveNextCharMessage(focusedWnd
);
2544 keyinput
.type
= INPUT_KEYBOARD
;
2545 keyinput
.ki
.wVk
= mOriginalVirtualKeyCode
;
2546 keyinput
.ki
.wScan
= mScanCode
;
2547 keyinput
.ki
.dwFlags
= KEYEVENTF_SCANCODE
;
2549 keyinput
.ki
.dwFlags
|= KEYEVENTF_EXTENDEDKEY
;
2551 keyinput
.ki
.time
= 0;
2552 keyinput
.ki
.dwExtraInfo
= 0;
2554 RedirectedKeyDownMessageManager::WillRedirect(mMsg
, defaultPrevented
);
2556 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2557 ("%p NativeKey::HandleKeyDownMessage(), redirecting %s...",
2558 this, ToString(mMsg
).get()));
2560 ::SendInput(1, &keyinput
, sizeof(keyinput
));
2562 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2563 ("%p NativeKey::HandleKeyDownMessage(), redirected %s", this,
2564 ToString(mMsg
).get()));
2566 // Return here. We shouldn't dispatch keypress event for this WM_KEYDOWN.
2567 // If it's needed, it will be dispatched after next (redirected)
2572 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2573 ("%p NativeKey::HandleKeyDownMessage(), received a redirected %s",
2574 this, ToString(mMsg
).get()));
2576 defaultPrevented
= RedirectedKeyDownMessageManager::DefaultPrevented();
2577 // If this is redirected keydown message, we have dispatched the keydown
2579 if (aEventDispatched
) {
2580 *aEventDispatched
= true;
2584 RedirectedKeyDownMessageManager::Forget();
2586 MOZ_ASSERT(!mWidget
->Destroyed());
2588 // If the key was processed by IME and didn't cause WM_(SYS)CHAR messages, we
2589 // shouldn't dispatch keypress event.
2590 if (mOriginalVirtualKeyCode
== VK_PROCESSKEY
&&
2591 !IsFollowedByPrintableCharOrSysCharMessage()) {
2592 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2593 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2594 "event because the key was already handled by IME, "
2595 "defaultPrevented=%s",
2596 this, GetBoolName(defaultPrevented
)));
2597 return defaultPrevented
;
2600 if (defaultPrevented
) {
2601 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2602 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2603 "event because preceding keydown event was consumed",
2605 MaybeDispatchPluginEventsForRemovedCharMessages();
2609 MOZ_ASSERT(!mCharMessageHasGone
,
2610 "If following char message was consumed by somebody, "
2611 "keydown event should have been consumed before dispatch");
2613 // If mCommittedCharsAndModifiers was initialized with following char
2614 // messages, we should dispatch keypress events with its information.
2615 if (IsFollowedByPrintableCharOrSysCharMessage()) {
2616 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2617 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2618 "keypress events with retrieved char messages...",
2620 return DispatchKeyPressEventsWithRetrievedCharMessages();
2623 // If we won't be getting a WM_CHAR, WM_SYSCHAR or WM_DEADCHAR, synthesize a
2624 // keypress for almost all keys
2625 if (NeedsToHandleWithoutFollowingCharMessages()) {
2626 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2627 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2628 "keypress events...",
2630 return (MaybeDispatchPluginEventsForRemovedCharMessages() ||
2631 DispatchKeyPressEventsWithoutCharMessage());
2634 // If WM_KEYDOWN of VK_PACKET isn't followed by WM_CHAR, we don't need to
2635 // dispatch keypress events.
2636 if (mVirtualKeyCode
== VK_PACKET
) {
2637 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2638 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2640 "because the key is VK_PACKET and there are no char messages",
2645 if (!mModKeyState
.IsControl() && !mModKeyState
.IsAlt() &&
2646 !(mModKeyState
.GetModifiers() & MODIFIER_ALTGRAPH
) &&
2647 !mModKeyState
.IsWin() && mIsPrintableKey
) {
2648 // If this is simple KeyDown event but next message is not WM_CHAR,
2649 // this event may not input text, so we should ignore this event.
2651 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2652 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2654 "because the key event is simple printable key's event but not "
2662 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2663 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2665 "because the key is a dead key and not followed by char messages",
2670 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2671 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2672 "keypress events due to no following char messages...",
2674 return DispatchKeyPressEventsWithoutCharMessage();
2677 bool NativeKey::HandleCharMessage(bool* aEventDispatched
) const {
2678 MOZ_ASSERT(IsCharOrSysCharMessage(mMsg
));
2679 return HandleCharMessage(mMsg
, aEventDispatched
);
2682 bool NativeKey::HandleCharMessage(const MSG
& aCharMsg
,
2683 bool* aEventDispatched
) const {
2684 MOZ_ASSERT(IsKeyDownMessage() || IsCharOrSysCharMessage(mMsg
));
2685 MOZ_ASSERT(IsCharOrSysCharMessage(aCharMsg
.message
));
2687 if (aEventDispatched
) {
2688 *aEventDispatched
= false;
2691 if ((IsCharOrSysCharMessage(mMsg
) || IsEnterKeyPressCharMessage(mMsg
)) &&
2692 IsAnotherInstanceRemovingCharMessage()) {
2694 sNativeKeyLogger
, LogLevel::Warning
,
2695 ("%p NativeKey::HandleCharMessage(), WARNING, does nothing because "
2696 "the message should be handled in another instance removing this "
2699 // Consume this for now because it will be handled by another instance.
2703 // If the key combinations is reserved by the system, we shouldn't dispatch
2704 // eKeyPress event for it and passes the message to next wndproc.
2705 if (IsReservedBySystem()) {
2706 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2707 ("%p NativeKey::HandleCharMessage(), doesn't dispatch keypress "
2708 "event because the key combination is reserved by the system",
2713 // If the widget has gone, we should do nothing.
2714 if (mWidget
->Destroyed()) {
2715 MOZ_LOG(sNativeKeyLogger
, LogLevel::Warning
,
2716 ("%p NativeKey::HandleCharMessage(), WARNING, not handled due to "
2717 "destroyed the widget",
2722 // When a control key is inputted by a key, it should be handled without
2723 // WM_*CHAR messages at receiving WM_*KEYDOWN message. So, when we receive
2724 // WM_*CHAR message directly, we see a control character here.
2725 // Note that when the char is '\r', it means that the char message should
2726 // cause "Enter" keypress event for inserting a line break.
2727 if (IsControlCharMessage(aCharMsg
) && !IsEnterKeyPressCharMessage(aCharMsg
)) {
2728 // In this case, we don't need to dispatch eKeyPress event because:
2729 // 1. We're the only browser which dispatches "keypress" event for
2730 // non-printable characters (Although, both Chrome and Edge dispatch
2731 // "keypress" event for some keys accidentally. For example, "IntlRo"
2732 // key with Ctrl of Japanese keyboard layout).
2733 // 2. Currently, we may handle shortcut keys with "keydown" event if
2734 // it's reserved or something. So, we shouldn't dispatch "keypress"
2735 // event without it.
2736 // Note that this does NOT mean we stop dispatching eKeyPress event for
2737 // key presses causes a control character when Ctrl is pressed. In such
2738 // case, DispatchKeyPressEventsWithoutCharMessage() dispatches eKeyPress
2739 // instead of this method.
2741 sNativeKeyLogger
, LogLevel::Info
,
2742 ("%p NativeKey::HandleCharMessage(), doesn't dispatch keypress "
2743 "event because received a control character input without WM_KEYDOWN",
2748 // XXXmnakano I think that if mMsg is WM_CHAR, i.e., it comes without
2749 // preceding WM_KEYDOWN, we should should dispatch composition
2750 // events instead of eKeyPress because they are not caused by
2751 // actual keyboard operation.
2753 // First, handle normal text input or non-printable key case here.
2754 WidgetKeyboardEvent
keypressEvent(true, eKeyPress
, mWidget
);
2755 if (IsEnterKeyPressCharMessage(aCharMsg
)) {
2756 keypressEvent
.mKeyCode
= NS_VK_RETURN
;
2758 keypressEvent
.mCharCode
= static_cast<uint32_t>(aCharMsg
.wParam
);
2760 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2761 if (NS_WARN_IF(NS_FAILED(rv
))) {
2762 MOZ_LOG(sNativeKeyLogger
, LogLevel::Error
,
2763 ("%p NativeKey::HandleCharMessage(), FAILED due to "
2764 "BeginNativeInputTransaction() failure",
2769 MOZ_LOG(sNativeKeyLogger
, LogLevel::Debug
,
2770 ("%p NativeKey::HandleCharMessage(), initializing keypress "
2774 ModifierKeyState
modKeyState(mModKeyState
);
2775 // When AltGr is pressed, both Alt and Ctrl are active. However, when they
2776 // are active, TextEditor won't treat the keypress event as inputting a
2777 // character. Therefore, when AltGr is pressed and the key tries to input
2778 // a character, let's set them to false.
2779 if (modKeyState
.IsControl() && modKeyState
.IsAlt() &&
2780 IsPrintableCharMessage(aCharMsg
)) {
2781 modKeyState
.Unset(MODIFIER_ALT
| MODIFIER_CONTROL
);
2783 nsEventStatus status
= InitKeyEvent(keypressEvent
, modKeyState
, &aCharMsg
);
2784 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2785 ("%p NativeKey::HandleCharMessage(), dispatching keypress event...",
2787 bool dispatched
= mDispatcher
->MaybeDispatchKeypressEvents(
2788 keypressEvent
, status
, const_cast<NativeKey
*>(this));
2789 if (aEventDispatched
) {
2790 *aEventDispatched
= dispatched
;
2792 if (mWidget
->Destroyed()) {
2793 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2794 ("%p NativeKey::HandleCharMessage(), keypress event caused "
2795 "destroying the widget",
2799 bool consumed
= status
== nsEventStatus_eConsumeNoDefault
;
2800 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2801 ("%p NativeKey::HandleCharMessage(), dispatched keypress event, "
2802 "dispatched=%s, consumed=%s",
2803 this, GetBoolName(dispatched
), GetBoolName(consumed
)));
2807 bool NativeKey::HandleKeyUpMessage(bool* aEventDispatched
) const {
2808 MOZ_ASSERT(IsKeyUpMessage());
2810 if (aEventDispatched
) {
2811 *aEventDispatched
= false;
2814 // If the key combinations is reserved by the system, we shouldn't dispatch
2815 // eKeyUp event for it and passes the message to next wndproc.
2816 if (IsReservedBySystem()) {
2817 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2818 ("%p NativeKey::HandleKeyUpMessage(), doesn't dispatch keyup "
2819 "event because the key combination is reserved by the system",
2824 // If the widget has gone, we should do nothing.
2825 if (mWidget
->Destroyed()) {
2827 sNativeKeyLogger
, LogLevel::Warning
,
2828 ("%p NativeKey::HandleKeyUpMessage(), WARNING, not handled due to "
2829 "destroyed the widget",
2834 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2835 if (NS_WARN_IF(NS_FAILED(rv
))) {
2836 MOZ_LOG(sNativeKeyLogger
, LogLevel::Error
,
2837 ("%p NativeKey::HandleKeyUpMessage(), FAILED due to "
2838 "BeginNativeInputTransaction() failure",
2843 MOZ_LOG(sNativeKeyLogger
, LogLevel::Debug
,
2844 ("%p NativeKey::HandleKeyUpMessage(), initializing keyup event...",
2846 EventMessage keyUpMessage
= IsKeyMessageOnPlugin() ? eKeyUpOnPlugin
: eKeyUp
;
2847 WidgetKeyboardEvent
keyupEvent(true, keyUpMessage
, mWidget
);
2848 nsEventStatus status
= InitKeyEvent(keyupEvent
, mModKeyState
, &mMsg
);
2849 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2850 ("%p NativeKey::HandleKeyUpMessage(), dispatching keyup event...",
2852 bool dispatched
= mDispatcher
->DispatchKeyboardEvent(
2853 keyUpMessage
, keyupEvent
, status
, const_cast<NativeKey
*>(this));
2854 if (aEventDispatched
) {
2855 *aEventDispatched
= dispatched
;
2857 if (mWidget
->Destroyed()) {
2858 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2859 ("%p NativeKey::HandleKeyUpMessage(), keyup event caused "
2860 "destroying the widget",
2864 bool consumed
= status
== nsEventStatus_eConsumeNoDefault
;
2865 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
2866 ("%p NativeKey::HandleKeyUpMessage(), dispatched keyup event, "
2867 "dispatched=%s, consumed=%s",
2868 this, GetBoolName(dispatched
), GetBoolName(consumed
)));
2872 bool NativeKey::NeedsToHandleWithoutFollowingCharMessages() const {
2873 MOZ_ASSERT(IsKeyDownMessage());
2875 // We cannot know following char messages of key messages in a plugin
2876 // process. So, let's compute the character to be inputted with every
2877 // printable key should be computed with the keyboard layout.
2878 if (IsKeyMessageOnPlugin()) {
2882 // If the key combination is reserved by the system, the caller shouldn't
2883 // do anything with following WM_*CHAR messages. So, let's return true here.
2884 if (IsReservedBySystem()) {
2888 // If the keydown message is generated for inputting some Unicode characters
2889 // via SendInput() API, we need to handle it only with WM_*CHAR messages.
2890 if (mVirtualKeyCode
== VK_PACKET
) {
2894 // If following char message is for a control character, it should be handled
2895 // without WM_CHAR message. This is typically Ctrl + [a-z].
2896 if (mFollowingCharMsgs
.Length() == 1 &&
2897 IsControlCharMessage(mFollowingCharMsgs
[0])) {
2901 // If keydown message is followed by WM_CHAR or WM_SYSCHAR whose wParam isn't
2902 // a control character, we should dispatch keypress event with the char
2903 // message even with any modifier state.
2904 if (IsFollowedByPrintableCharOrSysCharMessage()) {
2908 // If any modifier keys which may cause printable keys becoming non-printable
2909 // are not pressed, we don't need special handling for the key.
2910 // Note that AltGraph may map a printable key to input no character.
2911 // In such case, we need to eKeyPress event for backward compatibility.
2912 if (!mModKeyState
.IsControl() && !mModKeyState
.IsAlt() &&
2913 !(mModKeyState
.GetModifiers() & MODIFIER_ALTGRAPH
) &&
2914 !mModKeyState
.IsWin()) {
2918 // If the key event causes dead key event, we don't need to dispatch keypress
2920 if (mIsDeadKey
&& mCommittedCharsAndModifiers
.IsEmpty()) {
2924 // Even if the key is a printable key, it might cause non-printable character
2925 // input with modifier key(s).
2926 return mIsPrintableKey
;
2929 static nsCString
GetResultOfInSendMessageEx() {
2930 DWORD ret
= ::InSendMessageEx(nullptr);
2932 return NS_LITERAL_CSTRING("ISMEX_NOSEND");
2935 if (ret
& ISMEX_CALLBACK
) {
2936 result
= "ISMEX_CALLBACK";
2938 if (ret
& ISMEX_NOTIFY
) {
2939 if (!result
.IsEmpty()) {
2942 result
+= "ISMEX_NOTIFY";
2944 if (ret
& ISMEX_REPLIED
) {
2945 if (!result
.IsEmpty()) {
2948 result
+= "ISMEX_REPLIED";
2950 if (ret
& ISMEX_SEND
) {
2951 if (!result
.IsEmpty()) {
2954 result
+= "ISMEX_SEND";
2959 bool NativeKey::MayBeSameCharMessage(const MSG
& aCharMsg1
,
2960 const MSG
& aCharMsg2
) const {
2961 // NOTE: Although, we don't know when this case occurs, the scan code value
2962 // in lParam may be changed from 0 to something. The changed value
2963 // is different from the scan code of handling keydown message.
2964 static const LPARAM kScanCodeMask
= 0x00FF0000;
2965 return aCharMsg1
.message
== aCharMsg2
.message
&&
2966 aCharMsg1
.wParam
== aCharMsg2
.wParam
&&
2967 (aCharMsg1
.lParam
& ~kScanCodeMask
) ==
2968 (aCharMsg2
.lParam
& ~kScanCodeMask
);
2971 bool NativeKey::IsSamePhysicalKeyMessage(const MSG
& aKeyOrCharMsg1
,
2972 const MSG
& aKeyOrCharMsg2
) const {
2973 if (NS_WARN_IF(aKeyOrCharMsg1
.message
< WM_KEYFIRST
) ||
2974 NS_WARN_IF(aKeyOrCharMsg1
.message
> WM_KEYLAST
) ||
2975 NS_WARN_IF(aKeyOrCharMsg2
.message
< WM_KEYFIRST
) ||
2976 NS_WARN_IF(aKeyOrCharMsg2
.message
> WM_KEYLAST
)) {
2979 return WinUtils::GetScanCode(aKeyOrCharMsg1
.lParam
) ==
2980 WinUtils::GetScanCode(aKeyOrCharMsg2
.lParam
) &&
2981 WinUtils::IsExtendedScanCode(aKeyOrCharMsg1
.lParam
) ==
2982 WinUtils::IsExtendedScanCode(aKeyOrCharMsg2
.lParam
);
2985 bool NativeKey::GetFollowingCharMessage(MSG
& aCharMsg
) {
2986 MOZ_ASSERT(IsKeyDownMessage());
2987 MOZ_ASSERT(!IsKeyMessageOnPlugin());
2989 aCharMsg
.message
= WM_NULL
;
2991 if (mFakeCharMsgs
) {
2992 for (size_t i
= 0; i
< mFakeCharMsgs
->Length(); i
++) {
2993 FakeCharMsg
& fakeCharMsg
= mFakeCharMsgs
->ElementAt(i
);
2994 if (fakeCharMsg
.mConsumed
) {
2997 MSG charMsg
= fakeCharMsg
.GetCharMsg(mMsg
.hwnd
);
2998 fakeCharMsg
.mConsumed
= true;
2999 if (!IsCharMessage(charMsg
)) {
3008 // If next key message is not char message, we should give up to find a
3009 // related char message for the handling keydown event for now.
3010 // Note that it's possible other applications may send other key message
3011 // after we call TranslateMessage(). That may cause PeekMessage() failing
3012 // to get char message for the handling keydown message.
3014 if (!WinUtils::PeekMessage(&nextKeyMsg
, mMsg
.hwnd
, WM_KEYFIRST
, WM_KEYLAST
,
3015 PM_NOREMOVE
| PM_NOYIELD
) ||
3016 !IsCharMessage(nextKeyMsg
)) {
3017 MOZ_LOG(sNativeKeyLogger
, LogLevel::Verbose
,
3018 ("%p NativeKey::GetFollowingCharMessage(), there are no char "
3023 const MSG kFoundCharMsg
= nextKeyMsg
;
3025 AutoRestore
<MSG
> saveLastRemovingMsg(mRemovingMsg
);
3026 mRemovingMsg
= nextKeyMsg
;
3028 mReceivedMsg
= sEmptyMSG
;
3029 AutoRestore
<MSG
> ensureToClearRecivedMsg(mReceivedMsg
);
3031 // On Metrofox, PeekMessage() sometimes returns WM_NULL even if we specify
3032 // the message range. So, if it returns WM_NULL, we should retry to get
3033 // the following char message it was found above.
3034 for (uint32_t i
= 0; i
< 50; i
++) {
3035 MSG removedMsg
, nextKeyMsgInAllWindows
;
3036 bool doCrash
= false;
3037 if (!WinUtils::PeekMessage(&removedMsg
, mMsg
.hwnd
, nextKeyMsg
.message
,
3038 nextKeyMsg
.message
, PM_REMOVE
| PM_NOYIELD
)) {
3039 // We meets unexpected case. We should collect the message queue state
3040 // and crash for reporting the bug.
3043 // If another instance was created for the removing message during trying
3044 // to remove a char message, the instance didn't handle it for preventing
3045 // recursive handling. So, let's handle it in this instance.
3046 if (!IsEmptyMSG(mReceivedMsg
)) {
3047 // If focus is moved to different window, we shouldn't handle it on
3048 // the widget. Let's discard it for now.
3049 if (mReceivedMsg
.hwnd
!= nextKeyMsg
.hwnd
) {
3051 sNativeKeyLogger
, LogLevel::Warning
,
3052 ("%p NativeKey::GetFollowingCharMessage(), WARNING, received a "
3053 "char message during removing it from the queue, but it's for "
3054 "different window, mReceivedMsg=%s, nextKeyMsg=%s, "
3056 this, ToString(mReceivedMsg
).get(), ToString(nextKeyMsg
).get(),
3057 ToString(kFoundCharMsg
).get()));
3058 // There might still exist char messages, the loop of calling
3059 // this method should be continued.
3060 aCharMsg
.message
= WM_NULL
;
3063 // Even if the received message is different from what we tried to
3064 // remove from the queue, let's take the received message as a part of
3065 // the result of this key sequence.
3066 if (mReceivedMsg
.message
!= nextKeyMsg
.message
||
3067 mReceivedMsg
.wParam
!= nextKeyMsg
.wParam
||
3068 mReceivedMsg
.lParam
!= nextKeyMsg
.lParam
) {
3070 sNativeKeyLogger
, LogLevel::Warning
,
3071 ("%p NativeKey::GetFollowingCharMessage(), WARNING, received a "
3072 "char message during removing it from the queue, but it's "
3073 "differnt from what trying to remove from the queue, "
3074 "aCharMsg=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3075 this, ToString(mReceivedMsg
).get(), ToString(nextKeyMsg
).get(),
3076 ToString(kFoundCharMsg
).get()));
3078 MOZ_LOG(sNativeKeyLogger
, LogLevel::Verbose
,
3079 ("%p NativeKey::GetFollowingCharMessage(), succeeded to "
3081 "next char message via another instance, aCharMsg=%s, "
3083 this, ToString(mReceivedMsg
).get(),
3084 ToString(kFoundCharMsg
).get()));
3086 aCharMsg
= mReceivedMsg
;
3090 // The char message is redirected to different thread's window by focus
3091 // move or something or just cancelled by external application.
3092 if (!WinUtils::PeekMessage(&nextKeyMsgInAllWindows
, 0, WM_KEYFIRST
,
3093 WM_KEYLAST
, PM_NOREMOVE
| PM_NOYIELD
)) {
3095 sNativeKeyLogger
, LogLevel::Warning
,
3096 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3097 "remove a char message, but it's already gone from all message "
3098 "queues, nextKeyMsg=%s, kFoundCharMsg=%s",
3099 this, ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3100 return true; // XXX should return false in this case
3102 // The next key message is redirected to different window created by our
3103 // thread, we should do nothing because we must not have focus.
3104 if (nextKeyMsgInAllWindows
.hwnd
!= mMsg
.hwnd
) {
3105 aCharMsg
= nextKeyMsgInAllWindows
;
3107 sNativeKeyLogger
, LogLevel::Warning
,
3108 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3109 "remove a char message, but found in another message queue, "
3110 "nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3111 this, ToString(nextKeyMsgInAllWindows
).get(),
3112 ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3115 // If next key message becomes non-char message, this key operation
3116 // may have already been consumed or canceled.
3117 if (!IsCharMessage(nextKeyMsgInAllWindows
)) {
3119 sNativeKeyLogger
, LogLevel::Warning
,
3120 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3121 "remove a char message and next key message becomes non-char "
3122 "message, nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, "
3124 this, ToString(nextKeyMsgInAllWindows
).get(),
3125 ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3126 MOZ_ASSERT(!mCharMessageHasGone
);
3127 mFollowingCharMsgs
.Clear();
3128 mCharMessageHasGone
= true;
3131 // If next key message is still a char message but different key message,
3132 // we should treat current key operation is consumed or canceled and
3133 // next char message should be handled as an orphan char message later.
3134 if (!IsSamePhysicalKeyMessage(nextKeyMsgInAllWindows
, kFoundCharMsg
)) {
3136 sNativeKeyLogger
, LogLevel::Warning
,
3137 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3138 "remove a char message and next key message becomes differnt "
3140 "char message, nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, "
3142 this, ToString(nextKeyMsgInAllWindows
).get(),
3143 ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3144 MOZ_ASSERT(!mCharMessageHasGone
);
3145 mFollowingCharMsgs
.Clear();
3146 mCharMessageHasGone
= true;
3149 // If next key message is still a char message but the message is changed,
3150 // we should retry to remove the new message with PeekMessage() again.
3151 if (nextKeyMsgInAllWindows
.message
!= nextKeyMsg
.message
) {
3153 sNativeKeyLogger
, LogLevel::Warning
,
3154 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3155 "remove a char message due to message change, let's retry to "
3156 "remove the message with newly found char message, ",
3157 "nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, kFoundCharMsg=%s", this,
3158 ToString(nextKeyMsgInAllWindows
).get(), ToString(nextKeyMsg
).get(),
3159 ToString(kFoundCharMsg
).get()));
3160 nextKeyMsg
= nextKeyMsgInAllWindows
;
3163 // If there is still existing a char message caused by same physical key
3164 // in the queue but PeekMessage(PM_REMOVE) failed to remove it from the
3165 // queue, it might be possible that the odd keyboard layout or utility
3166 // hooks only PeekMessage(PM_NOREMOVE) and GetMessage(). So, let's try
3167 // remove the char message with GetMessage() again.
3168 // FYI: The wParam might be different from the found message, but it's
3169 // okay because we assume that odd keyboard layouts return actual
3170 // inputting character at removing the char message.
3171 if (WinUtils::GetMessage(&removedMsg
, mMsg
.hwnd
, nextKeyMsg
.message
,
3172 nextKeyMsg
.message
)) {
3174 sNativeKeyLogger
, LogLevel::Warning
,
3175 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3176 "remove a char message, but succeeded with GetMessage(), "
3177 "removedMsg=%s, kFoundCharMsg=%s",
3178 this, ToString(removedMsg
).get(), ToString(kFoundCharMsg
).get()));
3179 // Cancel to crash, but we need to check the removed message value.
3182 // If we've already removed some WM_NULL messages from the queue and
3183 // the found message has already gone from the queue, let's treat the key
3184 // as inputting no characters and already consumed.
3187 sNativeKeyLogger
, LogLevel::Warning
,
3188 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3189 "remove a char message, but removed %d WM_NULL messages",
3191 // If the key is a printable key or a control key but tried to input
3192 // a character, mark mCharMessageHasGone true for handling the keydown
3193 // event as inputting empty string.
3194 MOZ_ASSERT(!mCharMessageHasGone
);
3195 mFollowingCharMsgs
.Clear();
3196 mCharMessageHasGone
= true;
3199 MOZ_LOG(sNativeKeyLogger
, LogLevel::Error
,
3200 ("%p NativeKey::GetFollowingCharMessage(), FAILED, lost target "
3201 "message to remove, nextKeyMsg=%s",
3202 this, ToString(nextKeyMsg
).get()));
3206 nsPrintfCString
info(
3207 "\nPeekMessage() failed to remove char message! "
3208 "\nActive keyboard layout=0x%08X (%s), "
3209 "\nHandling message: %s, InSendMessageEx()=%s, "
3210 "\nFound message: %s, "
3211 "\nWM_NULL has been removed: %d, "
3212 "\nNext key message in all windows: %s, "
3214 KeyboardLayout::GetActiveLayout(),
3215 KeyboardLayout::GetActiveLayoutName().get(), ToString(mMsg
).get(),
3216 GetResultOfInSendMessageEx().get(), ToString(kFoundCharMsg
).get(), i
,
3217 ToString(nextKeyMsgInAllWindows
).get(), nextKeyMsgInAllWindows
.time
);
3218 CrashReporter::AppendAppNotesToCrashReport(info
);
3220 if (WinUtils::PeekMessage(&nextMsg
, 0, 0, 0, PM_NOREMOVE
| PM_NOYIELD
)) {
3221 nsPrintfCString
info("\nNext message in all windows: %s, time=%d",
3222 ToString(nextMsg
).get(), nextMsg
.time
);
3223 CrashReporter::AppendAppNotesToCrashReport(info
);
3225 CrashReporter::AppendAppNotesToCrashReport(
3226 NS_LITERAL_CSTRING("\nThere is no message in any window"));
3229 MOZ_CRASH("We lost the following char message");
3232 // We're still not sure why ::PeekMessage() may return WM_NULL even with
3233 // its first message and its last message are same message. However,
3234 // at developing Metrofox, we met this case even with usual keyboard
3235 // layouts. So, it might be possible in desktop application or it really
3236 // occurs with some odd keyboard layouts which perhaps hook API.
3237 if (removedMsg
.message
== WM_NULL
) {
3238 MOZ_LOG(sNativeKeyLogger
, LogLevel::Warning
,
3239 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3240 "remove a char message, instead, removed WM_NULL message, ",
3241 "removedMsg=%s", this, ToString(removedMsg
).get()));
3242 // Check if there is the message which we're trying to remove.
3244 if (!WinUtils::PeekMessage(&newNextKeyMsg
, mMsg
.hwnd
, WM_KEYFIRST
,
3245 WM_KEYLAST
, PM_NOREMOVE
| PM_NOYIELD
)) {
3246 // If there is no key message, we should mark this keydown as consumed
3247 // because the key operation may have already been handled or canceled.
3249 sNativeKeyLogger
, LogLevel::Warning
,
3250 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3251 "remove a char message because it's gone during removing it from "
3252 "the queue, nextKeyMsg=%s, kFoundCharMsg=%s",
3253 this, ToString(nextKeyMsg
).get(), ToString(kFoundCharMsg
).get()));
3254 MOZ_ASSERT(!mCharMessageHasGone
);
3255 mFollowingCharMsgs
.Clear();
3256 mCharMessageHasGone
= true;
3259 if (!IsCharMessage(newNextKeyMsg
)) {
3260 // If next key message becomes a non-char message, we should mark this
3261 // keydown as consumed because the key operation may have already been
3262 // handled or canceled.
3264 sNativeKeyLogger
, LogLevel::Warning
,
3265 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3266 "remove a char message because it's gone during removing it from "
3267 "the queue, nextKeyMsg=%s, newNextKeyMsg=%s, kFoundCharMsg=%s",
3268 this, ToString(nextKeyMsg
).get(), ToString(newNextKeyMsg
).get(),
3269 ToString(kFoundCharMsg
).get()));
3270 MOZ_ASSERT(!mCharMessageHasGone
);
3271 mFollowingCharMsgs
.Clear();
3272 mCharMessageHasGone
= true;
3276 sNativeKeyLogger
, LogLevel::Debug
,
3277 ("%p NativeKey::GetFollowingCharMessage(), there is the message "
3278 "which is being tried to be removed from the queue, trying again...",
3283 // Typically, this case occurs with WM_DEADCHAR. If the removed message's
3284 // wParam becomes 0, that means that the key event shouldn't cause text
3285 // input. So, let's ignore the strange char message.
3286 if (removedMsg
.message
== nextKeyMsg
.message
&& !removedMsg
.wParam
) {
3288 sNativeKeyLogger
, LogLevel::Warning
,
3289 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3290 "remove a char message, but the removed message's wParam is 0, "
3292 this, ToString(removedMsg
).get()));
3296 // This is normal case.
3297 if (MayBeSameCharMessage(removedMsg
, nextKeyMsg
)) {
3298 aCharMsg
= removedMsg
;
3300 sNativeKeyLogger
, LogLevel::Verbose
,
3301 ("%p NativeKey::GetFollowingCharMessage(), succeeded to retrieve "
3302 "next char message, aCharMsg=%s",
3303 this, ToString(aCharMsg
).get()));
3307 // Even if removed message is different char message from the found char
3308 // message, when the scan code is same, we can assume that the message
3309 // is overwritten by somebody who hooks API. See bug 1336028 comment 0 for
3310 // the possible scenarios.
3311 if (IsCharMessage(removedMsg
) &&
3312 IsSamePhysicalKeyMessage(removedMsg
, nextKeyMsg
)) {
3313 aCharMsg
= removedMsg
;
3315 sNativeKeyLogger
, LogLevel::Warning
,
3316 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3317 "remove a char message, but the removed message was changed from "
3318 "the found message except their scancode, aCharMsg=%s, "
3319 "nextKeyMsg=%s, kFoundCharMsg=%s",
3320 this, ToString(aCharMsg
).get(), ToString(nextKeyMsg
).get(),
3321 ToString(kFoundCharMsg
).get()));
3325 // When found message's wParam is 0 and its scancode is 0xFF, we may remove
3326 // usual char message actually. In such case, we should use the removed
3328 if (IsCharMessage(removedMsg
) && !nextKeyMsg
.wParam
&&
3329 WinUtils::GetScanCode(nextKeyMsg
.lParam
) == 0xFF) {
3330 aCharMsg
= removedMsg
;
3332 sNativeKeyLogger
, LogLevel::Warning
,
3333 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3334 "remove a char message, but the removed message was changed from "
3335 "the found message but the found message was odd, so, ignoring the "
3336 "odd found message and respecting the removed message, aCharMsg=%s, "
3337 "nextKeyMsg=%s, kFoundCharMsg=%s",
3338 this, ToString(aCharMsg
).get(), ToString(nextKeyMsg
).get(),
3339 ToString(kFoundCharMsg
).get()));
3343 // NOTE: Although, we don't know when this case occurs, the scan code value
3344 // in lParam may be changed from 0 to something. The changed value
3345 // is different from the scan code of handling keydown message.
3347 sNativeKeyLogger
, LogLevel::Error
,
3348 ("%p NativeKey::GetFollowingCharMessage(), FAILED, removed message "
3349 "is really different from what we have already found, removedMsg=%s, "
3350 "nextKeyMsg=%s, kFoundCharMsg=%s",
3351 this, ToString(removedMsg
).get(), ToString(nextKeyMsg
).get(),
3352 ToString(kFoundCharMsg
).get()));
3353 nsPrintfCString
info(
3354 "\nPeekMessage() removed unexpcted char message! "
3355 "\nActive keyboard layout=0x%08X (%s), "
3356 "\nHandling message: %s, InSendMessageEx()=%s, "
3357 "\nFound message: %s, "
3358 "\nRemoved message: %s, ",
3359 KeyboardLayout::GetActiveLayout(),
3360 KeyboardLayout::GetActiveLayoutName().get(), ToString(mMsg
).get(),
3361 GetResultOfInSendMessageEx().get(), ToString(kFoundCharMsg
).get(),
3362 ToString(removedMsg
).get());
3363 CrashReporter::AppendAppNotesToCrashReport(info
);
3364 // What's the next key message?
3365 MSG nextKeyMsgAfter
;
3366 if (WinUtils::PeekMessage(&nextKeyMsgAfter
, mMsg
.hwnd
, WM_KEYFIRST
,
3367 WM_KEYLAST
, PM_NOREMOVE
| PM_NOYIELD
)) {
3368 nsPrintfCString
info(
3369 "\nNext key message after unexpected char message "
3371 ToString(nextKeyMsgAfter
).get());
3372 CrashReporter::AppendAppNotesToCrashReport(info
);
3374 CrashReporter::AppendAppNotesToCrashReport(
3375 NS_LITERAL_CSTRING("\nThere is no key message after unexpected char "
3376 "message removed, "));
3378 // Another window has a key message?
3379 if (WinUtils::PeekMessage(&nextKeyMsgInAllWindows
, 0, WM_KEYFIRST
,
3380 WM_KEYLAST
, PM_NOREMOVE
| PM_NOYIELD
)) {
3381 nsPrintfCString
info("\nNext key message in all windows: %s.",
3382 ToString(nextKeyMsgInAllWindows
).get());
3383 CrashReporter::AppendAppNotesToCrashReport(info
);
3385 CrashReporter::AppendAppNotesToCrashReport(
3386 NS_LITERAL_CSTRING("\nThere is no key message in any windows."));
3389 MOZ_CRASH("PeekMessage() removed unexpected message");
3392 sNativeKeyLogger
, LogLevel::Error
,
3393 ("%p NativeKey::GetFollowingCharMessage(), FAILED, removed messages "
3394 "are all WM_NULL, nextKeyMsg=%s",
3395 this, ToString(nextKeyMsg
).get()));
3396 nsPrintfCString
info(
3397 "\nWe lost following char message! "
3398 "\nActive keyboard layout=0x%08X (%s), "
3399 "\nHandling message: %s, InSendMessageEx()=%s, \n"
3400 "Found message: %s, removed a lot of WM_NULL",
3401 KeyboardLayout::GetActiveLayout(),
3402 KeyboardLayout::GetActiveLayoutName().get(), ToString(mMsg
).get(),
3403 GetResultOfInSendMessageEx().get(), ToString(kFoundCharMsg
).get());
3404 CrashReporter::AppendAppNotesToCrashReport(info
);
3405 MOZ_CRASH("We lost the following char message");
3409 bool NativeKey::MaybeDispatchPluginEventsForRemovedCharMessages() const {
3410 MOZ_ASSERT(IsKeyDownMessage());
3411 MOZ_ASSERT(!IsKeyMessageOnPlugin());
3414 i
< mFollowingCharMsgs
.Length() && mWidget
->ShouldDispatchPluginEvent();
3417 sNativeKeyLogger
, LogLevel::Info
,
3418 ("%p NativeKey::MaybeDispatchPluginEventsForRemovedCharMessages(), "
3419 "dispatching %uth plugin event for %s...",
3420 this, i
+ 1, ToString(mFollowingCharMsgs
[i
]).get()));
3422 !mWidget
->Destroyed(),
3423 "NativeKey tries to dispatch a plugin event on destroyed widget");
3424 mWidget
->DispatchPluginEvent(mFollowingCharMsgs
[i
]);
3425 if (mWidget
->Destroyed() || IsFocusedWindowChanged()) {
3427 sNativeKeyLogger
, LogLevel::Info
,
3428 ("%p NativeKey::MaybeDispatchPluginEventsForRemovedCharMessages(), "
3429 "%uth plugin event caused %s",
3431 mWidget
->Destroyed() ? "destroying the widget" : "focus change"));
3435 sNativeKeyLogger
, LogLevel::Info
,
3436 ("%p NativeKey::MaybeDispatchPluginEventsForRemovedCharMessages(), "
3437 "dispatched %uth plugin event",
3441 // Dispatch odd char messages which are caused by ATOK or WXG (both of them
3442 // are Japanese IME) and removed by RemoveFollowingOddCharMessages().
3444 i
< mRemovedOddCharMsgs
.Length() && mWidget
->ShouldDispatchPluginEvent();
3447 sNativeKeyLogger
, LogLevel::Info
,
3448 ("%p NativeKey::MaybeDispatchPluginEventsForRemovedCharMessages(), "
3449 "dispatching %uth plugin event for odd char message, %s...",
3450 this, i
+ 1, ToString(mFollowingCharMsgs
[i
]).get()));
3452 !mWidget
->Destroyed(),
3453 "NativeKey tries to dispatch a plugin event on destroyed widget");
3454 mWidget
->DispatchPluginEvent(mRemovedOddCharMsgs
[i
]);
3455 if (mWidget
->Destroyed() || IsFocusedWindowChanged()) {
3457 sNativeKeyLogger
, LogLevel::Info
,
3458 ("%p NativeKey::MaybeDispatchPluginEventsForRemovedCharMessages(), "
3459 "%uth plugin event for odd char message caused %s",
3461 mWidget
->Destroyed() ? "destroying the widget" : "focus change"));
3465 sNativeKeyLogger
, LogLevel::Info
,
3466 ("%p NativeKey::MaybeDispatchPluginEventsForRemovedCharMessages(), "
3467 "dispatched %uth plugin event for odd char message",
3474 void NativeKey::ComputeInputtingStringWithKeyboardLayout() {
3475 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
3477 if (KeyboardLayout::IsPrintableCharKey(mVirtualKeyCode
) ||
3478 mCharMessageHasGone
) {
3479 mInputtingStringAndModifiers
= mCommittedCharsAndModifiers
;
3481 mInputtingStringAndModifiers
.Clear();
3483 mShiftedString
.Clear();
3484 mUnshiftedString
.Clear();
3485 mShiftedLatinChar
= mUnshiftedLatinChar
= 0;
3487 // XXX How about when Win key is pressed?
3488 if (mModKeyState
.IsControl() == mModKeyState
.IsAlt()) {
3492 // If user is inputting a Unicode character with typing Alt + Numpad
3493 // keys, we shouldn't set alternative key codes because the key event
3494 // shouldn't match with a mnemonic. However, we should set only
3495 // mUnshiftedString for keeping traditional behavior at WM_SYSKEYDOWN.
3496 // FYI: I guess that it's okay that mUnshiftedString stays empty. So,
3497 // if its value breaks something, you must be able to just return here.
3498 if (IsTypingUnicodeScalarValue()) {
3499 MOZ_ASSERT(mMsg
.message
== WM_SYSKEYDOWN
);
3500 MOZ_ASSERT(!mCommittedCharsAndModifiers
.IsEmpty());
3501 char16_t num
= mCommittedCharsAndModifiers
.CharAt(0);
3502 MOZ_ASSERT(num
>= '0' && num
<= '9');
3503 mUnshiftedString
.Append(num
, MODIFIER_NONE
);
3507 ModifierKeyState
capsLockState(mModKeyState
.GetModifiers() &
3511 keyboardLayout
->GetUniCharsAndModifiers(mVirtualKeyCode
, capsLockState
);
3512 capsLockState
.Set(MODIFIER_SHIFT
);
3514 keyboardLayout
->GetUniCharsAndModifiers(mVirtualKeyCode
, capsLockState
);
3516 // The current keyboard cannot input alphabets or numerics,
3517 // we should append them for Shortcut/Access keys.
3518 // E.g., for Cyrillic keyboard layout.
3519 capsLockState
.Unset(MODIFIER_SHIFT
);
3520 WidgetUtils::GetLatinCharCodeForKeyCode(
3521 mDOMKeyCode
, capsLockState
.GetModifiers(), &mUnshiftedLatinChar
,
3522 &mShiftedLatinChar
);
3524 // If the mShiftedLatinChar isn't 0, the key code is NS_VK_[A-Z].
3525 if (mShiftedLatinChar
) {
3526 // If the produced characters of the key on current keyboard layout
3527 // are same as computed Latin characters, we shouldn't append the
3528 // Latin characters to alternativeCharCode.
3529 if (mUnshiftedLatinChar
== mUnshiftedString
.CharAt(0) &&
3530 mShiftedLatinChar
== mShiftedString
.CharAt(0)) {
3531 mShiftedLatinChar
= mUnshiftedLatinChar
= 0;
3533 } else if (mUnshiftedLatinChar
) {
3534 // If the mShiftedLatinChar is 0, the mKeyCode doesn't produce
3535 // alphabet character. At that time, the character may be produced
3536 // with Shift key. E.g., on French keyboard layout, NS_VK_PERCENT
3537 // key produces LATIN SMALL LETTER U WITH GRAVE (U+00F9) without
3538 // Shift key but with Shift key, it produces '%'.
3539 // If the mUnshiftedLatinChar is produced by the key on current
3540 // keyboard layout, we shouldn't append it to alternativeCharCode.
3541 if (mUnshiftedLatinChar
== mUnshiftedString
.CharAt(0) ||
3542 mUnshiftedLatinChar
== mShiftedString
.CharAt(0)) {
3543 mUnshiftedLatinChar
= 0;
3547 if (!mModKeyState
.IsControl()) {
3551 // If the mCharCode is not ASCII character, we should replace the
3552 // mCharCode with ASCII character only when Ctrl is pressed.
3553 // But don't replace the mCharCode when the mCharCode is not same as
3554 // unmodified characters. In such case, Ctrl is sometimes used for a
3555 // part of character inputting key combination like Shift.
3557 mModKeyState
.IsShift() ? mShiftedLatinChar
: mUnshiftedLatinChar
;
3561 if (mInputtingStringAndModifiers
.IsEmpty() ||
3562 mInputtingStringAndModifiers
.UniCharsCaseInsensitiveEqual(
3563 mModKeyState
.IsShift() ? mShiftedString
: mUnshiftedString
)) {
3564 mInputtingStringAndModifiers
.Clear();
3565 mInputtingStringAndModifiers
.Append(ch
, mModKeyState
.GetModifiers());
3569 bool NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages() const {
3570 MOZ_ASSERT(IsKeyDownMessage());
3571 MOZ_ASSERT(IsFollowedByPrintableCharOrSysCharMessage());
3572 MOZ_ASSERT(!mWidget
->Destroyed());
3574 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
3575 if (NS_WARN_IF(NS_FAILED(rv
))) {
3577 sNativeKeyLogger
, LogLevel::Error
,
3578 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3579 "FAILED due to BeginNativeInputTransaction() failure",
3583 WidgetKeyboardEvent
keypressEvent(true, eKeyPress
, mWidget
);
3584 MOZ_LOG(sNativeKeyLogger
, LogLevel::Debug
,
3585 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3586 "initializing keypress event...",
3588 ModifierKeyState
modKeyState(mModKeyState
);
3589 if (mCanIgnoreModifierStateAtKeyPress
&& IsFollowedByPrintableCharMessage()) {
3590 // If eKeyPress event should cause inputting text in focused editor,
3591 // we need to remove Alt and Ctrl state.
3592 modKeyState
.Unset(MODIFIER_ALT
| MODIFIER_CONTROL
);
3594 // We don't need to send char message here if there are two or more retrieved
3595 // messages because we need to set each message to each eKeyPress event.
3596 bool needsCallback
= mFollowingCharMsgs
.Length() > 1;
3597 nsEventStatus status
=
3598 InitKeyEvent(keypressEvent
, modKeyState
,
3599 !needsCallback
? &mFollowingCharMsgs
[0] : nullptr);
3600 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
3601 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3602 "dispatching keypress event(s)...",
3604 bool dispatched
= mDispatcher
->MaybeDispatchKeypressEvents(
3605 keypressEvent
, status
, const_cast<NativeKey
*>(this), needsCallback
);
3606 if (mWidget
->Destroyed()) {
3608 sNativeKeyLogger
, LogLevel::Info
,
3609 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3610 "keypress event(s) caused destroying the widget",
3614 bool consumed
= status
== nsEventStatus_eConsumeNoDefault
;
3615 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
3616 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3617 "dispatched keypress event(s), dispatched=%s, consumed=%s",
3618 this, GetBoolName(dispatched
), GetBoolName(consumed
)));
3622 bool NativeKey::DispatchKeyPressEventsWithoutCharMessage() const {
3623 MOZ_ASSERT(IsKeyDownMessage());
3624 MOZ_ASSERT(!mIsDeadKey
|| !mCommittedCharsAndModifiers
.IsEmpty());
3625 MOZ_ASSERT(!mWidget
->Destroyed());
3627 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
3628 if (NS_WARN_IF(NS_FAILED(rv
))) {
3629 MOZ_LOG(sNativeKeyLogger
, LogLevel::Error
,
3630 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3632 "to BeginNativeInputTransaction() failure",
3637 WidgetKeyboardEvent
keypressEvent(true, eKeyPress
, mWidget
);
3638 if (mInputtingStringAndModifiers
.IsEmpty() && mShiftedString
.IsEmpty() &&
3639 mUnshiftedString
.IsEmpty()) {
3640 keypressEvent
.mKeyCode
= mDOMKeyCode
;
3642 MOZ_LOG(sNativeKeyLogger
, LogLevel::Debug
,
3643 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3645 "keypress event...",
3647 nsEventStatus status
= InitKeyEvent(keypressEvent
, mModKeyState
);
3648 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
3649 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3651 "keypress event(s)...",
3653 bool dispatched
= mDispatcher
->MaybeDispatchKeypressEvents(
3654 keypressEvent
, status
, const_cast<NativeKey
*>(this));
3655 if (mWidget
->Destroyed()) {
3656 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
3657 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3658 "keypress event(s) caused destroying the widget",
3662 bool consumed
= status
== nsEventStatus_eConsumeNoDefault
;
3664 sNativeKeyLogger
, LogLevel::Info
,
3665 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), dispatched "
3666 "keypress event(s), dispatched=%s, consumed=%s",
3667 this, GetBoolName(dispatched
), GetBoolName(consumed
)));
3671 void NativeKey::WillDispatchKeyboardEvent(WidgetKeyboardEvent
& aKeyboardEvent
,
3673 // If it's an eKeyPress event and it's generated from retrieved char message,
3674 // we need to set raw message information for plugins.
3675 if (aKeyboardEvent
.mMessage
== eKeyPress
&&
3676 IsFollowedByPrintableCharOrSysCharMessage()) {
3677 MOZ_RELEASE_ASSERT(aIndex
< mCommittedCharsAndModifiers
.Length());
3678 uint32_t foundPrintableCharMessages
= 0;
3679 for (size_t i
= 0; i
< mFollowingCharMsgs
.Length(); ++i
) {
3680 if (!IsPrintableCharOrSysCharMessage(mFollowingCharMsgs
[i
])) {
3681 // XXX Should we dispatch a plugin event for WM_*DEADCHAR messages and
3682 // WM_CHAR with a control character here? But we're not sure
3683 // how can we create such message queue (i.e., WM_CHAR or
3684 // WM_SYSCHAR with a printable character and such message are
3685 // generated by a keydown). So, let's ignore such case until
3686 // we'd get some bug reports.
3687 MOZ_LOG(sNativeKeyLogger
, LogLevel::Warning
,
3688 ("%p NativeKey::WillDispatchKeyboardEvent(), WARNING, "
3689 "ignoring %uth message due to non-printable char message, %s",
3690 this, i
+ 1, ToString(mFollowingCharMsgs
[i
]).get()));
3693 if (foundPrintableCharMessages
++ == aIndex
) {
3694 // Found message which caused the eKeyPress event. Let's set the
3695 // message for plugin if it's necessary.
3696 MaybeInitPluginEventOfKeyEvent(aKeyboardEvent
, mFollowingCharMsgs
[i
]);
3700 // Set modifier state from mCommittedCharsAndModifiers because some of them
3701 // might be different. For example, Shift key was pressed at inputting
3702 // dead char but Shift key was released before inputting next character.
3703 if (mCanIgnoreModifierStateAtKeyPress
) {
3704 ModifierKeyState
modKeyState(mModKeyState
);
3705 modKeyState
.Unset(MODIFIER_SHIFT
| MODIFIER_CONTROL
| MODIFIER_ALT
|
3706 MODIFIER_ALTGRAPH
| MODIFIER_CAPSLOCK
);
3707 modKeyState
.Set(mCommittedCharsAndModifiers
.ModifiersAt(aIndex
));
3708 modKeyState
.InitInputEvent(aKeyboardEvent
);
3709 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
3710 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3711 "setting %uth modifier state to %s",
3712 this, aIndex
+ 1, ToString(modKeyState
).get()));
3715 size_t longestLength
=
3716 std::max(mInputtingStringAndModifiers
.Length(),
3717 std::max(mShiftedString
.Length(), mUnshiftedString
.Length()));
3718 size_t skipUniChars
= longestLength
- mInputtingStringAndModifiers
.Length();
3719 size_t skipShiftedChars
= longestLength
- mShiftedString
.Length();
3720 size_t skipUnshiftedChars
= longestLength
- mUnshiftedString
.Length();
3721 if (aIndex
>= longestLength
) {
3723 sNativeKeyLogger
, LogLevel::Info
,
3724 ("%p NativeKey::WillDispatchKeyboardEvent(), does nothing for %uth "
3726 this, aIndex
+ 1, ToChar(aKeyboardEvent
.mMessage
)));
3730 // Check if aKeyboardEvent is the last event for a key press.
3731 // So, if it's not an eKeyPress event, it's always the last event.
3732 // Otherwise, check if the index is the last character of
3733 // mCommittedCharsAndModifiers.
3734 bool isLastIndex
= aKeyboardEvent
.mMessage
!= eKeyPress
||
3735 mCommittedCharsAndModifiers
.IsEmpty() ||
3736 mCommittedCharsAndModifiers
.Length() - 1 == aIndex
;
3738 nsTArray
<AlternativeCharCode
>& altArray
=
3739 aKeyboardEvent
.mAlternativeCharCodes
;
3741 // Set charCode and adjust modifier state for every eKeyPress event.
3742 // This is not necessary for the other keyboard events because the other
3743 // keyboard events shouldn't have non-zero charCode value and should have
3744 // current modifier state.
3745 if (aKeyboardEvent
.mMessage
== eKeyPress
&& skipUniChars
<= aIndex
) {
3746 // XXX Modifying modifier state of aKeyboardEvent is illegal, but no way
3747 // to set different modifier state per keypress event except this
3748 // hack. Note that ideally, dead key should cause composition events
3749 // instead of keypress events, though.
3750 if (aIndex
- skipUniChars
< mInputtingStringAndModifiers
.Length()) {
3751 ModifierKeyState
modKeyState(mModKeyState
);
3752 // If key in combination with Alt and/or Ctrl produces a different
3753 // character than without them then do not report these flags
3754 // because it is separate keyboard layout shift state. If dead-key
3755 // and base character does not produce a valid composite character
3756 // then both produced dead-key character and following base
3757 // character may have different modifier flags, too.
3758 modKeyState
.Unset(MODIFIER_SHIFT
| MODIFIER_CONTROL
| MODIFIER_ALT
|
3759 MODIFIER_ALTGRAPH
| MODIFIER_CAPSLOCK
);
3761 mInputtingStringAndModifiers
.ModifiersAt(aIndex
- skipUniChars
));
3762 modKeyState
.InitInputEvent(aKeyboardEvent
);
3763 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
3764 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3765 "setting %uth modifier state to %s",
3766 this, aIndex
+ 1, ToString(modKeyState
).get()));
3769 mInputtingStringAndModifiers
.CharAt(aIndex
- skipUniChars
);
3771 // The mCharCode was set from mKeyValue. However, for example, when Ctrl key
3772 // is pressed, its value should indicate an ASCII character for backward
3773 // compatibility rather than inputting character without the modifiers.
3774 // Therefore, we need to modify mCharCode value here.
3775 aKeyboardEvent
.SetCharCode(uniChar
);
3776 MOZ_LOG(sNativeKeyLogger
, LogLevel::Info
,
3777 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3778 "setting %uth charCode to %s",
3779 this, aIndex
+ 1, GetCharacterCodeName(uniChar
).get()));
3782 // We need to append alterntaive charCode values:
3783 // - if the event is eKeyPress, we need to append for the index because
3784 // eKeyPress event is dispatched for every character inputted by a
3786 // - if the event is not eKeyPress, we need to append for all characters
3787 // inputted by the key press because the other keyboard events (e.g.,
3788 // eKeyDown are eKeyUp) are fired only once for a key press.
3790 if (aKeyboardEvent
.mMessage
== eKeyPress
) {
3791 // Basically, append alternative charCode values only for the index.
3793 // However, if it's the last eKeyPress event but different shift state
3794 // can input longer string, the last eKeyPress event should have all
3795 // remaining alternative charCode values.
3797 count
= longestLength
- aIndex
;
3800 count
= longestLength
;
3802 for (size_t i
= 0; i
< count
; ++i
) {
3803 uint16_t shiftedChar
= 0, unshiftedChar
= 0;
3804 if (skipShiftedChars
<= aIndex
+ i
) {
3805 shiftedChar
= mShiftedString
.CharAt(aIndex
+ i
- skipShiftedChars
);
3807 if (skipUnshiftedChars
<= aIndex
+ i
) {
3808 unshiftedChar
= mUnshiftedString
.CharAt(aIndex
+ i
- skipUnshiftedChars
);
3811 if (shiftedChar
|| unshiftedChar
) {
3812 AlternativeCharCode
chars(unshiftedChar
, shiftedChar
);
3813 altArray
.AppendElement(chars
);
3820 if (mUnshiftedLatinChar
|| mShiftedLatinChar
) {
3821 AlternativeCharCode
chars(mUnshiftedLatinChar
, mShiftedLatinChar
);
3822 altArray
.AppendElement(chars
);
3825 // Typically, following virtual keycodes are used for a key which can
3826 // input the character. However, these keycodes are also used for
3827 // other keys on some keyboard layout. E.g., in spite of Shift+'1'
3828 // inputs '+' on Thai keyboard layout, a key which is at '=/+'
3829 // key on ANSI keyboard layout is VK_OEM_PLUS. Native applications
3830 // handle it as '+' key if Ctrl key is pressed.
3831 char16_t charForOEMKeyCode
= 0;
3832 switch (mVirtualKeyCode
) {
3834 charForOEMKeyCode
= '+';
3837 charForOEMKeyCode
= ',';
3840 charForOEMKeyCode
= '-';
3843 charForOEMKeyCode
= '.';
3846 if (charForOEMKeyCode
&& charForOEMKeyCode
!= mUnshiftedString
.CharAt(0) &&
3847 charForOEMKeyCode
!= mShiftedString
.CharAt(0) &&
3848 charForOEMKeyCode
!= mUnshiftedLatinChar
&&
3849 charForOEMKeyCode
!= mShiftedLatinChar
) {
3850 AlternativeCharCode
OEMChars(charForOEMKeyCode
, charForOEMKeyCode
);
3851 altArray
.AppendElement(OEMChars
);
3856 /*****************************************************************************
3857 * mozilla::widget::KeyboardLayout
3858 *****************************************************************************/
3860 KeyboardLayout
* KeyboardLayout::sInstance
= nullptr;
3861 nsIIdleServiceInternal
* KeyboardLayout::sIdleService
= nullptr;
3863 // This log is very noisy if you don't want to retrieve the mapping table
3864 // of specific keyboard layout. LogLevel::Debug and LogLevel::Verbose are
3865 // used to log the layout mapping. If you need to log some behavior of
3866 // KeyboardLayout class, you should use LogLevel::Info or lower level.
3867 LazyLogModule
sKeyboardLayoutLogger("KeyboardLayoutWidgets");
3870 KeyboardLayout
* KeyboardLayout::GetInstance() {
3872 sInstance
= new KeyboardLayout();
3873 nsCOMPtr
<nsIIdleServiceInternal
> idleService
=
3874 do_GetService("@mozilla.org/widget/idleservice;1");
3875 // The refcount will be decreased at shut down.
3876 sIdleService
= idleService
.forget().take();
3882 void KeyboardLayout::Shutdown() {
3884 sInstance
= nullptr;
3885 NS_IF_RELEASE(sIdleService
);
3889 void KeyboardLayout::NotifyIdleServiceOfUserActivity() {
3890 sIdleService
->ResetIdleTimeOut(0);
3893 KeyboardLayout::KeyboardLayout()
3894 : mKeyboardLayout(0),
3895 mIsOverridden(false),
3896 mIsPendingToRestoreKeyboardLayout(false),
3898 mDeadKeyTableListHead
= nullptr;
3899 // A dead key sequence should be made from up to 5 keys. Therefore, 4 is
3900 // enough and makes sense because the item is uint8_t.
3901 // (Although, even if it's possible to be 6 keys or more in a sequence,
3902 // this array will be re-allocated).
3903 mActiveDeadKeys
.SetCapacity(4);
3904 mDeadKeyShiftStates
.SetCapacity(4);
3906 // NOTE: LoadLayout() should be called via OnLayoutChange().
3909 KeyboardLayout::~KeyboardLayout() { ReleaseDeadKeyTables(); }
3911 bool KeyboardLayout::IsPrintableCharKey(uint8_t aVirtualKey
) {
3912 return GetKeyIndex(aVirtualKey
) >= 0;
3915 WORD
KeyboardLayout::ComputeScanCodeForVirtualKeyCode(
3916 uint8_t aVirtualKeyCode
) const {
3917 return static_cast<WORD
>(
3918 ::MapVirtualKeyEx(aVirtualKeyCode
, MAPVK_VK_TO_VSC
, GetLayout()));
3921 bool KeyboardLayout::IsDeadKey(uint8_t aVirtualKey
,
3922 const ModifierKeyState
& aModKeyState
) const {
3923 int32_t virtualKeyIndex
= GetKeyIndex(aVirtualKey
);
3925 // XXX KeyboardLayout class doesn't support unusual keyboard layout which
3926 // maps some function keys as dead keys.
3927 if (virtualKeyIndex
< 0) {
3931 return mVirtualKeys
[virtualKeyIndex
].IsDeadKey(
3932 VirtualKey::ModifiersToShiftState(aModKeyState
.GetModifiers()));
3935 bool KeyboardLayout::IsSysKey(uint8_t aVirtualKey
,
3936 const ModifierKeyState
& aModKeyState
) const {
3937 // If Alt key is not pressed, it's never a system key combination.
3938 // Additionally, if Ctrl key is pressed, it's never a system key combination
3940 // FYI: Windows logo key state won't affect if it's a system key.
3941 if (!aModKeyState
.IsAlt() || aModKeyState
.IsControl()) {
3945 int32_t virtualKeyIndex
= GetKeyIndex(aVirtualKey
);
3946 if (virtualKeyIndex
< 0) {
3950 UniCharsAndModifiers inputCharsAndModifiers
=
3951 GetUniCharsAndModifiers(aVirtualKey
, aModKeyState
);
3952 if (inputCharsAndModifiers
.IsEmpty()) {
3956 // If the Alt key state isn't consumed, that means that the key with Alt
3957 // doesn't cause text input. So, the combination is a system key.
3958 return !!(inputCharsAndModifiers
.ModifiersAt(0) & MODIFIER_ALT
);
3961 void KeyboardLayout::InitNativeKey(NativeKey
& aNativeKey
) {
3962 if (mIsPendingToRestoreKeyboardLayout
) {
3963 LoadLayout(::GetKeyboardLayout(0));
3966 // If the aNativeKey is initialized with WM_CHAR, the key information
3967 // should be discarded because mKeyValue should have the string to be
3969 if (aNativeKey
.mMsg
.message
== WM_CHAR
) {
3970 char16_t ch
= static_cast<char16_t
>(aNativeKey
.mMsg
.wParam
);
3971 // But don't set key value as printable key if the character is a control
3972 // character such as 0x0D at pressing Enter key.
3973 if (!NativeKey::IsControlChar(ch
)) {
3974 aNativeKey
.mKeyNameIndex
= KEY_NAME_INDEX_USE_STRING
;
3975 Modifiers modifiers
=
3976 aNativeKey
.GetModifiers() & ~(MODIFIER_ALT
| MODIFIER_CONTROL
);
3977 aNativeKey
.mCommittedCharsAndModifiers
.Append(ch
, modifiers
);
3982 // If the aNativeKey is in a sequence to input a Unicode character with
3983 // Alt + numpad keys, we should just set the number as the inputting charcter.
3984 // Note that we should compute the key value from the virtual key code
3985 // because they may be mapped to alphabets, but they should be treated as
3986 // Alt + [0-9] even by web apps.
3987 if (aNativeKey
.IsTypingUnicodeScalarValue()) {
3988 // If the key code value is mapped to a Numpad key, let's compute the key
3989 // value with it. This is same behavior as Chrome. In strictly speaking,
3990 // I think that the else block's computation is better because it seems
3991 // that Windows does not refer virtual key code value, but we should avoid
3992 // web-compat issues.
3994 if (aNativeKey
.mVirtualKeyCode
>= VK_NUMPAD0
&&
3995 aNativeKey
.mVirtualKeyCode
<= VK_NUMPAD9
) {
3996 num
= '0' + aNativeKey
.mVirtualKeyCode
- VK_NUMPAD0
;
3998 // Otherwise, let's use fake key value for making never match with
4001 switch (aNativeKey
.mScanCode
) {
4002 case 0x0052: // Numpad0
4005 case 0x004F: // Numpad1
4008 case 0x0050: // Numpad2
4011 case 0x0051: // Numpad3
4014 case 0x004B: // Numpad4
4017 case 0x004C: // Numpad5
4020 case 0x004D: // Numpad6
4023 case 0x0047: // Numpad7
4026 case 0x0048: // Numpad8
4029 case 0x0049: // Numpad9
4033 MOZ_ASSERT_UNREACHABLE(
4034 "IsTypingUnicodeScalarValue() must have returned true for wrong "
4039 aNativeKey
.mCommittedCharsAndModifiers
.Append(num
,
4040 aNativeKey
.GetModifiers());
4041 aNativeKey
.mKeyNameIndex
= KEY_NAME_INDEX_USE_STRING
;
4045 // When it's followed by non-dead char message(s) for printable character(s),
4046 // aNativeKey should dispatch eKeyPress events for them rather than
4047 // information from keyboard layout because respecting WM_(SYS)CHAR messages
4048 // guarantees that we can always input characters which is expected by
4049 // the user even if the user uses odd keyboard layout.
4050 // Or, when it was followed by non-dead char message for a printable character
4051 // but it's gone at removing the message from the queue, let's treat it
4052 // as a key inputting empty string.
4053 if (aNativeKey
.IsFollowedByPrintableCharOrSysCharMessage() ||
4054 aNativeKey
.mCharMessageHasGone
) {
4055 MOZ_ASSERT(!aNativeKey
.IsCharMessage(aNativeKey
.mMsg
));
4056 if (aNativeKey
.IsFollowedByPrintableCharOrSysCharMessage()) {
4057 // Initialize mCommittedCharsAndModifiers with following char messages.
4058 aNativeKey
.InitCommittedCharsAndModifiersWithFollowingCharMessages();
4059 MOZ_ASSERT(!aNativeKey
.mCommittedCharsAndModifiers
.IsEmpty());
4061 // Currently, we are doing a ugly hack to keypress events to cause
4062 // inputting character even if Ctrl or Alt key is pressed, that is, we
4063 // remove Ctrl and Alt modifier state from keypress event. However, for
4064 // example, Ctrl+Space which causes ' ' of WM_CHAR message never causes
4065 // keypress event whose ctrlKey is true. For preventing this problem,
4066 // we should mark as not removable if Ctrl or Alt key does not cause
4067 // changing inputting character.
4068 if (IsPrintableCharKey(aNativeKey
.mOriginalVirtualKeyCode
) &&
4069 (aNativeKey
.IsControl() ^ aNativeKey
.IsAlt())) {
4070 ModifierKeyState state
= aNativeKey
.ModifierKeyStateRef();
4071 state
.Unset(MODIFIER_ALT
| MODIFIER_CONTROL
);
4072 UniCharsAndModifiers charsWithoutModifier
=
4073 GetUniCharsAndModifiers(aNativeKey
.GenericVirtualKeyCode(), state
);
4074 aNativeKey
.mCanIgnoreModifierStateAtKeyPress
=
4075 !charsWithoutModifier
.UniCharsEqual(
4076 aNativeKey
.mCommittedCharsAndModifiers
);
4079 aNativeKey
.mCommittedCharsAndModifiers
.Clear();
4081 aNativeKey
.mKeyNameIndex
= KEY_NAME_INDEX_USE_STRING
;
4083 // If it's not in dead key sequence, we don't need to do anymore here.
4084 if (!IsInDeadKeySequence()) {
4088 // If it's in dead key sequence and dead char is inputted as is, we need to
4089 // set the previous modifier state which is stored when preceding dead key
4091 UniCharsAndModifiers deadChars
= GetDeadUniCharsAndModifiers();
4092 aNativeKey
.mCommittedCharsAndModifiers
.OverwriteModifiersIfBeginsWith(
4094 // Finish the dead key sequence.
4095 DeactivateDeadKeyState();
4099 // If it's a dead key, aNativeKey will be initialized by
4100 // MaybeInitNativeKeyAsDeadKey().
4101 if (MaybeInitNativeKeyAsDeadKey(aNativeKey
)) {
4105 // If the key is not a usual printable key, KeyboardLayout class assume that
4106 // it's not cause dead char nor printable char. Therefore, there are nothing
4107 // to do here fore such keys (e.g., function keys).
4108 // However, this should keep dead key state even if non-printable key is
4109 // pressed during a dead key sequence.
4110 if (!IsPrintableCharKey(aNativeKey
.mOriginalVirtualKeyCode
)) {
4114 MOZ_ASSERT(aNativeKey
.mOriginalVirtualKeyCode
!= VK_PACKET
,
4115 "At handling VK_PACKET, we shouldn't refer keyboard layout");
4117 aNativeKey
.mKeyNameIndex
== KEY_NAME_INDEX_USE_STRING
,
4118 "Printable key's key name index must be KEY_NAME_INDEX_USE_STRING");
4120 // If it's in dead key handling and the pressed key causes a composite
4121 // character, aNativeKey will be initialized by
4122 // MaybeInitNativeKeyWithCompositeChar().
4123 if (MaybeInitNativeKeyWithCompositeChar(aNativeKey
)) {
4127 UniCharsAndModifiers baseChars
= GetUniCharsAndModifiers(aNativeKey
);
4129 // If the key press isn't related to any dead keys, initialize aNativeKey
4130 // with the characters which should be caused by the key.
4131 if (!IsInDeadKeySequence()) {
4132 aNativeKey
.mCommittedCharsAndModifiers
= baseChars
;
4136 // If the key doesn't cause a composite character with preceding dead key,
4137 // initialize aNativeKey with the dead-key character followed by current
4139 UniCharsAndModifiers deadChars
= GetDeadUniCharsAndModifiers();
4140 aNativeKey
.mCommittedCharsAndModifiers
= deadChars
+ baseChars
;
4141 if (aNativeKey
.IsKeyDownMessage()) {
4142 DeactivateDeadKeyState();
4146 bool KeyboardLayout::MaybeInitNativeKeyAsDeadKey(NativeKey
& aNativeKey
) {
4147 // Only when it's not in dead key sequence, we can trust IsDeadKey() result.
4148 if (!IsInDeadKeySequence() && !IsDeadKey(aNativeKey
)) {
4152 // When keydown message is followed by a dead char message, it should be
4153 // initialized as dead key.
4154 bool isDeadKeyDownEvent
=
4155 aNativeKey
.IsKeyDownMessage() && aNativeKey
.IsFollowedByDeadCharMessage();
4157 // When keyup message is received, let's check if it's one of preceding
4158 // dead keys because keydown message order and keyup message order may be
4160 bool isDeadKeyUpEvent
=
4161 !aNativeKey
.IsKeyDownMessage() &&
4162 mActiveDeadKeys
.Contains(aNativeKey
.GenericVirtualKeyCode());
4164 if (isDeadKeyDownEvent
|| isDeadKeyUpEvent
) {
4165 ActivateDeadKeyState(aNativeKey
);
4166 // Any dead key events don't generate characters. So, a dead key should
4167 // cause only keydown event and keyup event whose KeyboardEvent.key
4168 // values are "Dead".
4169 aNativeKey
.mCommittedCharsAndModifiers
.Clear();
4170 aNativeKey
.mKeyNameIndex
= KEY_NAME_INDEX_Dead
;
4174 // At keydown message handling, we need to forget the first dead key
4175 // because there is no guarantee coming WM_KEYUP for the second dead
4176 // key before next WM_KEYDOWN. E.g., due to auto key repeat or pressing
4177 // another dead key before releasing current key. Therefore, we can
4178 // set only a character for current key for keyup event.
4179 if (!IsInDeadKeySequence()) {
4180 aNativeKey
.mCommittedCharsAndModifiers
=
4181 GetUniCharsAndModifiers(aNativeKey
);
4185 // When non-printable key event comes during a dead key sequence, that must
4186 // be a modifier key event. So, such events shouldn't be handled as a part
4187 // of the dead key sequence.
4188 if (!IsDeadKey(aNativeKey
)) {
4192 // FYI: Following code may run when the user doesn't input text actually
4193 // but the key sequence is a dead key sequence. For example,
4194 // ` -> Ctrl+` with Spanish keyboard layout. Let's keep using this
4195 // complicated code for now because this runs really rarely.
4197 // Dead key followed by another dead key may cause a composed character
4198 // (e.g., "Russian - Mnemonic" keyboard layout's 's' -> 'c').
4199 if (MaybeInitNativeKeyWithCompositeChar(aNativeKey
)) {
4203 // Otherwise, dead key followed by another dead key causes inputting both
4205 UniCharsAndModifiers prevDeadChars
= GetDeadUniCharsAndModifiers();
4206 UniCharsAndModifiers newChars
= GetUniCharsAndModifiers(aNativeKey
);
4207 // But keypress events should be fired for each committed character.
4208 aNativeKey
.mCommittedCharsAndModifiers
= prevDeadChars
+ newChars
;
4209 if (aNativeKey
.IsKeyDownMessage()) {
4210 DeactivateDeadKeyState();
4215 bool KeyboardLayout::MaybeInitNativeKeyWithCompositeChar(
4216 NativeKey
& aNativeKey
) {
4217 if (!IsInDeadKeySequence()) {
4221 if (NS_WARN_IF(!IsPrintableCharKey(aNativeKey
.mOriginalVirtualKeyCode
))) {
4225 UniCharsAndModifiers baseChars
= GetUniCharsAndModifiers(aNativeKey
);
4226 if (baseChars
.IsEmpty() || !baseChars
.CharAt(0)) {
4230 char16_t compositeChar
= GetCompositeChar(baseChars
.CharAt(0));
4231 if (!compositeChar
) {
4235 // Active dead-key and base character does produce exactly one composite
4237 aNativeKey
.mCommittedCharsAndModifiers
.Append(compositeChar
,
4238 baseChars
.ModifiersAt(0));
4239 if (aNativeKey
.IsKeyDownMessage()) {
4240 DeactivateDeadKeyState();
4245 UniCharsAndModifiers
KeyboardLayout::GetUniCharsAndModifiers(
4246 uint8_t aVirtualKey
, VirtualKey::ShiftState aShiftState
) const {
4247 UniCharsAndModifiers result
;
4248 int32_t key
= GetKeyIndex(aVirtualKey
);
4252 return mVirtualKeys
[key
].GetUniChars(aShiftState
);
4255 UniCharsAndModifiers
KeyboardLayout::GetDeadUniCharsAndModifiers() const {
4256 MOZ_RELEASE_ASSERT(mActiveDeadKeys
.Length() == mDeadKeyShiftStates
.Length());
4258 if (NS_WARN_IF(mActiveDeadKeys
.IsEmpty())) {
4259 return UniCharsAndModifiers();
4262 UniCharsAndModifiers result
;
4263 for (size_t i
= 0; i
< mActiveDeadKeys
.Length(); ++i
) {
4265 GetUniCharsAndModifiers(mActiveDeadKeys
[i
], mDeadKeyShiftStates
[i
]);
4270 char16_t
KeyboardLayout::GetCompositeChar(char16_t aBaseChar
) const {
4271 if (NS_WARN_IF(mActiveDeadKeys
.IsEmpty())) {
4274 // XXX Currently, we don't support computing a composite character with
4275 // two or more dead keys since it needs big table for supporting
4276 // long chained dead keys. However, this should be a minor bug
4277 // because this runs only when the latest keydown event does not cause
4278 // WM_(SYS)CHAR messages. So, when user wants to input a character,
4279 // this path never runs.
4280 if (mActiveDeadKeys
.Length() > 1) {
4283 int32_t key
= GetKeyIndex(mActiveDeadKeys
[0]);
4287 return mVirtualKeys
[key
].GetCompositeChar(mDeadKeyShiftStates
[0], aBaseChar
);
4291 HKL
KeyboardLayout::GetActiveLayout() { return GetInstance()->mKeyboardLayout
; }
4294 nsCString
KeyboardLayout::GetActiveLayoutName() {
4295 return GetInstance()->GetLayoutName(GetActiveLayout());
4298 static bool IsValidKeyboardLayoutsChild(const nsAString
& aChildName
) {
4299 if (aChildName
.Length() != 8) {
4302 for (size_t i
= 0; i
< aChildName
.Length(); i
++) {
4303 if ((aChildName
[i
] >= '0' && aChildName
[i
] <= '9') ||
4304 (aChildName
[i
] >= 'a' && aChildName
[i
] <= 'f') ||
4305 (aChildName
[i
] >= 'A' && aChildName
[i
] <= 'F')) {
4313 nsCString
KeyboardLayout::GetLayoutName(HKL aLayout
) const {
4314 const wchar_t kKeyboardLayouts
[] =
4315 L
"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\";
4316 uint16_t language
= reinterpret_cast<uintptr_t>(aLayout
) & 0xFFFF;
4317 uint16_t layout
= (reinterpret_cast<uintptr_t>(aLayout
) >> 16) & 0xFFFF;
4318 // If the layout is less than 0xA000XXXX (normal keyboard layout for the
4319 // language) or 0xEYYYXXXX (IMM-IME), we can retrieve its name simply.
4320 if (layout
< 0xA000 || (layout
& 0xF000) == 0xE000) {
4321 nsAutoString
key(kKeyboardLayouts
);
4322 key
.AppendPrintf("%08X", layout
< 0xA000
4324 : reinterpret_cast<uintptr_t>(aLayout
));
4326 if (NS_WARN_IF(!WinUtils::GetRegistryKey(
4327 HKEY_LOCAL_MACHINE
, key
.get(), L
"Layout Text", buf
, sizeof(buf
)))) {
4328 return NS_LITERAL_CSTRING("No name or too long name");
4330 return NS_ConvertUTF16toUTF8(buf
);
4333 if (NS_WARN_IF((layout
& 0xF000) != 0xF000)) {
4335 result
.AppendPrintf("Odd HKL: 0x%08X",
4336 reinterpret_cast<uintptr_t>(aLayout
));
4340 // Otherwise, we need to walk the registry under "Keyboard Layouts".
4341 nsCOMPtr
<nsIWindowsRegKey
> regKey
=
4342 do_CreateInstance("@mozilla.org/windows-registry-key;1");
4343 if (NS_WARN_IF(!regKey
)) {
4344 return EmptyCString();
4347 regKey
->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE
,
4348 nsString(kKeyboardLayouts
), nsIWindowsRegKey::ACCESS_READ
);
4349 if (NS_WARN_IF(NS_FAILED(rv
))) {
4350 return EmptyCString();
4352 uint32_t childCount
= 0;
4353 if (NS_WARN_IF(NS_FAILED(regKey
->GetChildCount(&childCount
))) ||
4354 NS_WARN_IF(!childCount
)) {
4355 return EmptyCString();
4357 for (uint32_t i
= 0; i
< childCount
; i
++) {
4358 nsAutoString childName
;
4359 if (NS_WARN_IF(NS_FAILED(regKey
->GetChildName(i
, childName
))) ||
4360 !IsValidKeyboardLayoutsChild(childName
)) {
4363 uint32_t childNum
= static_cast<uint32_t>(childName
.ToInteger64(&rv
, 16));
4364 if (NS_WARN_IF(NS_FAILED(rv
))) {
4367 // Ignore normal keyboard layouts for each language.
4368 if (childNum
<= 0xFFFF) {
4371 // If it doesn't start with 'A' nor 'a', language should be matched.
4372 if ((childNum
& 0xFFFF) != language
&&
4373 (childNum
& 0xF0000000) != 0xA0000000) {
4376 // Then, the child should have "Layout Id" which is "YYY" of 0xFYYYXXXX.
4377 nsAutoString
key(kKeyboardLayouts
);
4380 if (NS_WARN_IF(!WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE
, key
.get(),
4381 L
"Layout Id", buf
, sizeof(buf
)))) {
4384 uint16_t layoutId
= wcstol(buf
, nullptr, 16);
4385 if (layoutId
!= (layout
& 0x0FFF)) {
4388 if (NS_WARN_IF(!WinUtils::GetRegistryKey(
4389 HKEY_LOCAL_MACHINE
, key
.get(), L
"Layout Text", buf
, sizeof(buf
)))) {
4392 return NS_ConvertUTF16toUTF8(buf
);
4394 return EmptyCString();
4397 void KeyboardLayout::LoadLayout(HKL aLayout
) {
4398 mIsPendingToRestoreKeyboardLayout
= false;
4400 if (mKeyboardLayout
== aLayout
) {
4404 mKeyboardLayout
= aLayout
;
4407 MOZ_LOG(sKeyboardLayoutLogger
, LogLevel::Info
,
4408 ("KeyboardLayout::LoadLayout(aLayout=0x%08X (%s))", aLayout
,
4409 GetLayoutName(aLayout
).get()));
4412 memset(kbdState
, 0, sizeof(kbdState
));
4414 BYTE originalKbdState
[256];
4415 // Bitfield with all shift states that have at least one dead-key.
4416 uint16_t shiftStatesWithDeadKeys
= 0;
4417 // Bitfield with all shift states that produce any possible dead-key base
4419 uint16_t shiftStatesWithBaseChars
= 0;
4421 mActiveDeadKeys
.Clear();
4422 mDeadKeyShiftStates
.Clear();
4424 ReleaseDeadKeyTables();
4426 ::GetKeyboardState(originalKbdState
);
4428 // For each shift state gather all printable characters that are produced
4429 // for normal case when no any dead-key is active.
4431 for (VirtualKey::ShiftState shiftState
= 0; shiftState
< 16; shiftState
++) {
4432 VirtualKey::FillKbdState(kbdState
, shiftState
);
4433 bool isAltGr
= VirtualKey::IsAltGrIndex(shiftState
);
4434 for (uint32_t virtualKey
= 0; virtualKey
< 256; virtualKey
++) {
4435 int32_t vki
= GetKeyIndex(virtualKey
);
4439 NS_ASSERTION(uint32_t(vki
) < ArrayLength(mVirtualKeys
), "invalid index");
4440 char16_t uniChars
[5];
4441 int32_t ret
= ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)uniChars
,
4442 ArrayLength(uniChars
), 0, mKeyboardLayout
);
4445 shiftStatesWithDeadKeys
|= (1 << shiftState
);
4446 // Repeat dead-key to deactivate it and get its character
4448 char16_t deadChar
[2];
4449 ret
= ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)deadChar
,
4450 ArrayLength(deadChar
), 0, mKeyboardLayout
);
4451 NS_ASSERTION(ret
== 2, "Expecting twice repeated dead-key character");
4452 mVirtualKeys
[vki
].SetDeadChar(shiftState
, deadChar
[0]);
4454 MOZ_LOG(sKeyboardLayoutLogger
, LogLevel::Debug
,
4455 (" %s (%d): DeadChar(%s, %s) (ret=%d)",
4456 kVirtualKeyName
[virtualKey
], vki
,
4457 GetShiftStateName(shiftState
).get(),
4458 GetCharacterCodeName(deadChar
, 1).get(), ret
));
4461 // dead-key can pair only with exactly one base character.
4462 shiftStatesWithBaseChars
|= (1 << shiftState
);
4464 mVirtualKeys
[vki
].SetNormalChars(shiftState
, uniChars
, ret
);
4465 MOZ_LOG(sKeyboardLayoutLogger
, LogLevel::Verbose
,
4466 (" %s (%d): NormalChar(%s, %s) (ret=%d)",
4467 kVirtualKeyName
[virtualKey
], vki
,
4468 GetShiftStateName(shiftState
).get(),
4469 GetCharacterCodeName(uniChars
, ret
).get(), ret
));
4472 // If the key inputs at least one character with AltGr modifier,
4473 // check if AltGr changes inputting character. If it does, mark
4474 // this keyboard layout has AltGr modifier actually.
4475 if (!mHasAltGr
&& ret
> 0 && isAltGr
&&
4476 mVirtualKeys
[vki
].IsChangedByAltGr(shiftState
)) {
4478 MOZ_LOG(sKeyboardLayoutLogger
, LogLevel::Info
,
4479 (" Found a key (%s) changed by AltGr: %s -> %s (%s) (ret=%d)",
4480 kVirtualKeyName
[virtualKey
],
4481 GetCharacterCodeName(
4482 mVirtualKeys
[vki
].GetNativeUniChars(
4483 shiftState
- VirtualKey::ShiftStateIndex::eAltGr
))
4485 GetCharacterCodeName(
4486 mVirtualKeys
[vki
].GetNativeUniChars(shiftState
))
4488 GetShiftStateName(shiftState
).get(), ret
));
4493 // Now process each dead-key to find all its base characters and resulting
4494 // composite characters.
4495 for (VirtualKey::ShiftState shiftState
= 0; shiftState
< 16; shiftState
++) {
4496 if (!(shiftStatesWithDeadKeys
& (1 << shiftState
))) {
4500 VirtualKey::FillKbdState(kbdState
, shiftState
);
4502 for (uint32_t virtualKey
= 0; virtualKey
< 256; virtualKey
++) {
4503 int32_t vki
= GetKeyIndex(virtualKey
);
4504 if (vki
>= 0 && mVirtualKeys
[vki
].IsDeadKey(shiftState
)) {
4505 DeadKeyEntry deadKeyArray
[256];
4506 int32_t n
= GetDeadKeyCombinations(
4507 virtualKey
, kbdState
, shiftStatesWithBaseChars
, deadKeyArray
,
4508 ArrayLength(deadKeyArray
));
4509 const DeadKeyTable
* dkt
=
4510 mVirtualKeys
[vki
].MatchingDeadKeyTable(deadKeyArray
, n
);
4512 dkt
= AddDeadKeyTable(deadKeyArray
, n
);
4514 mVirtualKeys
[vki
].AttachDeadKeyTable(shiftState
, dkt
);
4519 ::SetKeyboardState(originalKbdState
);
4521 if (MOZ_LOG_TEST(sKeyboardLayoutLogger
, LogLevel::Verbose
)) {
4522 static const UINT kExtendedScanCode
[] = {0x0000, 0xE000};
4523 static const UINT kMapType
= MAPVK_VSC_TO_VK_EX
;
4524 MOZ_LOG(sKeyboardLayoutLogger
, LogLevel::Verbose
,
4525 ("Logging virtual keycode values for scancode (0x%p)...",
4527 for (uint32_t i
= 0; i
< ArrayLength(kExtendedScanCode
); i
++) {
4528 for (uint32_t j
= 1; j
<= 0xFF; j
++) {
4529 UINT scanCode
= kExtendedScanCode
[i
] + j
;
4530 UINT virtualKeyCode
=
4531 ::MapVirtualKeyEx(scanCode
, kMapType
, mKeyboardLayout
);
4532 MOZ_LOG(sKeyboardLayoutLogger
, LogLevel::Verbose
,
4533 ("0x%04X, %s", scanCode
, kVirtualKeyName
[virtualKeyCode
]));
4538 MOZ_LOG(sKeyboardLayoutLogger
, LogLevel::Info
,
4539 (" AltGr key is %s in %s", mHasAltGr
? "found" : "not found",
4540 GetLayoutName(aLayout
).get()));
4543 inline int32_t KeyboardLayout::GetKeyIndex(uint8_t aVirtualKey
) {
4544 // Currently these 68 (NS_NUM_OF_KEYS) virtual keys are assumed
4545 // to produce visible representation:
4546 // 0x20 - VK_SPACE ' '
4547 // 0x30..0x39 '0'..'9'
4548 // 0x41..0x5A 'A'..'Z'
4549 // 0x60..0x69 '0'..'9' on numpad
4550 // 0x6A - VK_MULTIPLY '*' on numpad
4551 // 0x6B - VK_ADD '+' on numpad
4552 // 0x6D - VK_SUBTRACT '-' on numpad
4553 // 0x6E - VK_DECIMAL '.' on numpad
4554 // 0x6F - VK_DIVIDE '/' on numpad
4555 // 0x6E - VK_DECIMAL '.'
4556 // 0xBA - VK_OEM_1 ';:' for US
4557 // 0xBB - VK_OEM_PLUS '+' any country
4558 // 0xBC - VK_OEM_COMMA ',' any country
4559 // 0xBD - VK_OEM_MINUS '-' any country
4560 // 0xBE - VK_OEM_PERIOD '.' any country
4561 // 0xBF - VK_OEM_2 '/?' for US
4562 // 0xC0 - VK_OEM_3 '`~' for US
4563 // 0xC1 - VK_ABNT_C1 '/?' for Brazilian
4564 // 0xC2 - VK_ABNT_C2 separator key on numpad (Brazilian or JIS for Mac)
4565 // 0xDB - VK_OEM_4 '[{' for US
4566 // 0xDC - VK_OEM_5 '\|' for US
4567 // 0xDD - VK_OEM_6 ']}' for US
4568 // 0xDE - VK_OEM_7 ''"' for US
4571 // 0xE2 - VK_OEM_102 '\_' for JIS
4575 static const int8_t xlat
[256] = {
4576 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
4577 //-----------------------------------------------------------------------
4578 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00
4579 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10
4580 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20
4581 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, // 30
4582 -1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 40
4583 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, -1, -1, // 50
4584 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, -1, 49, 50, 51, // 60
4585 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 70
4586 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
4587 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90
4588 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0
4589 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 53, 54, 55, 56, 57, // B0
4590 58, 59, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0
4591 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 61, 62, 63, 64, 65, // D0
4592 -1, 66, 67, 68, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0
4593 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // F0
4596 return xlat
[aVirtualKey
];
4599 int KeyboardLayout::CompareDeadKeyEntries(const void* aArg1
, const void* aArg2
,
4601 const DeadKeyEntry
* arg1
= static_cast<const DeadKeyEntry
*>(aArg1
);
4602 const DeadKeyEntry
* arg2
= static_cast<const DeadKeyEntry
*>(aArg2
);
4604 return arg1
->BaseChar
- arg2
->BaseChar
;
4607 const DeadKeyTable
* KeyboardLayout::AddDeadKeyTable(
4608 const DeadKeyEntry
* aDeadKeyArray
, uint32_t aEntries
) {
4609 DeadKeyTableListEntry
* next
= mDeadKeyTableListHead
;
4611 const size_t bytes
= offsetof(DeadKeyTableListEntry
, data
) +
4612 DeadKeyTable::SizeInBytes(aEntries
);
4613 uint8_t* p
= new uint8_t[bytes
];
4615 mDeadKeyTableListHead
= reinterpret_cast<DeadKeyTableListEntry
*>(p
);
4616 mDeadKeyTableListHead
->next
= next
;
4619 reinterpret_cast<DeadKeyTable
*>(mDeadKeyTableListHead
->data
);
4621 dkt
->Init(aDeadKeyArray
, aEntries
);
4626 void KeyboardLayout::ReleaseDeadKeyTables() {
4627 while (mDeadKeyTableListHead
) {
4628 uint8_t* p
= reinterpret_cast<uint8_t*>(mDeadKeyTableListHead
);
4629 mDeadKeyTableListHead
= mDeadKeyTableListHead
->next
;
4635 bool KeyboardLayout::EnsureDeadKeyActive(bool aIsActive
, uint8_t aDeadKey
,
4636 const PBYTE aDeadKeyKbdState
) {
4639 char16_t dummyChars
[5];
4641 ::ToUnicodeEx(aDeadKey
, 0, (PBYTE
)aDeadKeyKbdState
, (LPWSTR
)dummyChars
,
4642 ArrayLength(dummyChars
), 0, mKeyboardLayout
);
4644 // <0 - Dead key state is active. The keyboard driver will wait for next
4646 // 1 - Previous pressed key was a valid base character that produced
4647 // exactly one composite character.
4648 // >1 - Previous pressed key does not produce any composite characters.
4649 // Return dead-key character followed by base character(s).
4650 } while ((ret
< 0) != aIsActive
);
4655 void KeyboardLayout::ActivateDeadKeyState(const NativeKey
& aNativeKey
) {
4656 // Dead-key state should be activated at keydown.
4657 if (!aNativeKey
.IsKeyDownMessage()) {
4661 mActiveDeadKeys
.AppendElement(aNativeKey
.mOriginalVirtualKeyCode
);
4662 mDeadKeyShiftStates
.AppendElement(aNativeKey
.GetShiftState());
4665 void KeyboardLayout::DeactivateDeadKeyState() {
4666 if (mActiveDeadKeys
.IsEmpty()) {
4671 memset(kbdState
, 0, sizeof(kbdState
));
4673 // Assume that the last dead key can finish dead key sequence.
4674 VirtualKey::FillKbdState(kbdState
, mDeadKeyShiftStates
.LastElement());
4675 EnsureDeadKeyActive(false, mActiveDeadKeys
.LastElement(), kbdState
);
4676 mActiveDeadKeys
.Clear();
4677 mDeadKeyShiftStates
.Clear();
4680 bool KeyboardLayout::AddDeadKeyEntry(char16_t aBaseChar
,
4681 char16_t aCompositeChar
,
4682 DeadKeyEntry
* aDeadKeyArray
,
4683 uint32_t aEntries
) {
4684 for (uint32_t index
= 0; index
< aEntries
; index
++) {
4685 if (aDeadKeyArray
[index
].BaseChar
== aBaseChar
) {
4690 aDeadKeyArray
[aEntries
].BaseChar
= aBaseChar
;
4691 aDeadKeyArray
[aEntries
].CompositeChar
= aCompositeChar
;
4696 uint32_t KeyboardLayout::GetDeadKeyCombinations(
4697 uint8_t aDeadKey
, const PBYTE aDeadKeyKbdState
,
4698 uint16_t aShiftStatesWithBaseChars
, DeadKeyEntry
* aDeadKeyArray
,
4699 uint32_t aMaxEntries
) {
4700 bool deadKeyActive
= false;
4701 uint32_t entries
= 0;
4703 memset(kbdState
, 0, sizeof(kbdState
));
4705 for (uint32_t shiftState
= 0; shiftState
< 16; shiftState
++) {
4706 if (!(aShiftStatesWithBaseChars
& (1 << shiftState
))) {
4710 VirtualKey::FillKbdState(kbdState
, shiftState
);
4712 for (uint32_t virtualKey
= 0; virtualKey
< 256; virtualKey
++) {
4713 int32_t vki
= GetKeyIndex(virtualKey
);
4714 // Dead-key can pair only with such key that produces exactly one base
4717 mVirtualKeys
[vki
].GetNativeUniChars(shiftState
).Length() == 1) {
4718 // Ensure dead-key is in active state, when it swallows entered
4719 // character and waits for the next pressed key.
4720 if (!deadKeyActive
) {
4721 deadKeyActive
= EnsureDeadKeyActive(true, aDeadKey
, aDeadKeyKbdState
);
4724 // Depending on the character the followed the dead-key, the keyboard
4725 // driver can produce one composite character, or a dead-key character
4726 // followed by a second character.
4727 char16_t compositeChars
[5];
4729 ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)compositeChars
,
4730 ArrayLength(compositeChars
), 0, mKeyboardLayout
);
4733 // This key combination does not produce any characters. The
4734 // dead-key is still in active state.
4737 char16_t baseChars
[5];
4738 ret
= ::ToUnicodeEx(virtualKey
, 0, kbdState
, (LPWSTR
)baseChars
,
4739 ArrayLength(baseChars
), 0, mKeyboardLayout
);
4740 if (entries
< aMaxEntries
) {
4743 // Exactly one composite character produced. Now, when
4744 // dead-key is not active, repeat the last character one more
4745 // time to determine the base character.
4746 if (AddDeadKeyEntry(baseChars
[0], compositeChars
[0],
4747 aDeadKeyArray
, entries
)) {
4750 deadKeyActive
= false;
4753 // If pressing another dead-key produces different character,
4754 // we should register the dead-key entry with first character
4755 // produced by current key.
4757 // First inactivate the dead-key state completely.
4759 EnsureDeadKeyActive(false, aDeadKey
, aDeadKeyKbdState
);
4760 if (NS_WARN_IF(deadKeyActive
)) {
4761 MOZ_LOG(sKeyboardLayoutLogger
, LogLevel::Error
,
4762 (" failed to deactivating the dead-key state..."));
4765 for (int32_t i
= 0; i
< 5; ++i
) {
4766 ret
= ::ToUnicodeEx(
4767 virtualKey
, 0, kbdState
, (LPWSTR
)baseChars
,
4768 ArrayLength(baseChars
), 0, mKeyboardLayout
);
4774 AddDeadKeyEntry(baseChars
[0], compositeChars
[0],
4775 aDeadKeyArray
, entries
)) {
4778 // Inactivate dead-key state for current virtual keycode.
4779 EnsureDeadKeyActive(false, virtualKey
, kbdState
);
4783 NS_WARNING("File a bug for this dead-key handling!");
4784 deadKeyActive
= false;
4789 sKeyboardLayoutLogger
, LogLevel::Debug
,
4790 (" %s -> %s (%d): DeadKeyEntry(%s, %s) (ret=%d)",
4791 kVirtualKeyName
[aDeadKey
], kVirtualKeyName
[virtualKey
], vki
,
4792 GetCharacterCodeName(compositeChars
, 1).get(),
4795 : GetCharacterCodeName(baseChars
, std::min(ret
, 5)).get(),
4800 // 1. Unexpected dead-key. Dead-key chaining is not supported.
4801 // 2. More than one character generated. This is not a valid
4802 // dead-key and base character combination.
4803 deadKeyActive
= false;
4805 sKeyboardLayoutLogger
, LogLevel::Verbose
,
4806 (" %s -> %s (%d): Unsupport dead key type(%s) (ret=%d)",
4807 kVirtualKeyName
[aDeadKey
], kVirtualKeyName
[virtualKey
], vki
,
4810 : GetCharacterCodeName(compositeChars
, std::min(ret
, 5))
4819 if (deadKeyActive
) {
4820 deadKeyActive
= EnsureDeadKeyActive(false, aDeadKey
, aDeadKeyKbdState
);
4823 NS_QuickSort(aDeadKeyArray
, entries
, sizeof(DeadKeyEntry
),
4824 CompareDeadKeyEntries
, nullptr);
4828 uint32_t KeyboardLayout::ConvertNativeKeyCodeToDOMKeyCode(
4829 UINT aNativeKeyCode
) const {
4830 // Alphabet or Numeric or Numpad or Function keys
4831 if ((aNativeKeyCode
>= 0x30 && aNativeKeyCode
<= 0x39) ||
4832 (aNativeKeyCode
>= 0x41 && aNativeKeyCode
<= 0x5A) ||
4833 (aNativeKeyCode
>= 0x60 && aNativeKeyCode
<= 0x87)) {
4834 return static_cast<uint32_t>(aNativeKeyCode
);
4836 switch (aNativeKeyCode
) {
4837 // Following keycodes are same as our DOM keycodes
4845 case VK_MENU
: // Alt
4847 case VK_CAPITAL
: // CAPS LOCK
4848 case VK_KANA
: // same as VK_HANGUL
4851 case VK_HANJA
: // same as VK_KANJI
4858 case VK_PRIOR
: // PAGE UP
4859 case VK_NEXT
: // PAGE DOWN
4872 case VK_APPS
: // Context Menu
4875 case VK_SCROLL
: // SCROLL LOCK
4876 case VK_ATTN
: // Attension key of IBM midrange computers, e.g., AS/400
4877 case VK_CRSEL
: // Cursor Selection
4878 case VK_EXSEL
: // Extend Selection
4879 case VK_EREOF
: // Erase EOF key of IBM 3270 keyboard layout
4882 case VK_PA1
: // PA1 key of IBM 3270 keyboard layout
4883 return uint32_t(aNativeKeyCode
);
4888 // Windows key should be mapped to a Win keycode
4889 // They should be able to be distinguished by DOM3 KeyboardEvent.location
4894 case VK_VOLUME_MUTE
:
4895 return NS_VK_VOLUME_MUTE
;
4896 case VK_VOLUME_DOWN
:
4897 return NS_VK_VOLUME_DOWN
;
4899 return NS_VK_VOLUME_UP
;
4907 return NS_VK_CONTROL
;
4909 // Note that even if the key is AltGr, we should return NS_VK_ALT for
4910 // compatibility with both older Gecko and the other browsers.
4915 // Following keycodes are not defined in our DOM keycodes.
4916 case VK_BROWSER_BACK
:
4917 case VK_BROWSER_FORWARD
:
4918 case VK_BROWSER_REFRESH
:
4919 case VK_BROWSER_STOP
:
4920 case VK_BROWSER_SEARCH
:
4921 case VK_BROWSER_FAVORITES
:
4922 case VK_BROWSER_HOME
:
4923 case VK_MEDIA_NEXT_TRACK
:
4924 case VK_MEDIA_PREV_TRACK
:
4926 case VK_MEDIA_PLAY_PAUSE
:
4927 case VK_LAUNCH_MAIL
:
4928 case VK_LAUNCH_MEDIA_SELECT
:
4929 case VK_LAUNCH_APP1
:
4930 case VK_LAUNCH_APP2
:
4933 // Following OEM specific virtual keycodes should pass through DOM keyCode
4934 // for compatibility with the other browsers on Windows.
4936 // Following OEM specific virtual keycodes are defined for Fujitsu/OASYS.
4937 case VK_OEM_FJ_JISHO
:
4938 case VK_OEM_FJ_MASSHOU
:
4939 case VK_OEM_FJ_TOUROKU
:
4940 case VK_OEM_FJ_LOYA
:
4941 case VK_OEM_FJ_ROYA
:
4942 // Not sure what means "ICO".
4946 // Following OEM specific virtual keycodes are defined for Nokia/Ericsson.
4959 case VK_OEM_BACKTAB
:
4960 // VK_OEM_CLEAR is defined as not OEM specific, but let's pass though
4961 // DOM keyCode like other OEM specific virtual keycodes.
4963 return uint32_t(aNativeKeyCode
);
4965 // 0xE1 is an OEM specific virtual keycode. However, the value is already
4966 // used in our DOM keyCode for AltGr on Linux. So, this virtual keycode
4967 // cannot pass through DOM keyCode.
4971 // Following keycodes are OEM keys which are keycodes for non-alphabet and
4972 // non-numeric keys, we should compute each keycode of them from unshifted
4973 // character which is inputted by each key. But if the unshifted character
4974 // is not an ASCII character but shifted character is an ASCII character,
4975 // we should refer it.
4990 NS_ASSERTION(IsPrintableCharKey(aNativeKeyCode
),
4991 "The key must be printable");
4992 ModifierKeyState
modKeyState(0);
4993 UniCharsAndModifiers uniChars
=
4994 GetUniCharsAndModifiers(aNativeKeyCode
, modKeyState
);
4995 if (uniChars
.Length() != 1 || uniChars
.CharAt(0) < ' ' ||
4996 uniChars
.CharAt(0) > 0x7F) {
4997 modKeyState
.Set(MODIFIER_SHIFT
);
4998 uniChars
= GetUniCharsAndModifiers(aNativeKeyCode
, modKeyState
);
4999 if (uniChars
.Length() != 1 || uniChars
.CharAt(0) < ' ' ||
5000 uniChars
.CharAt(0) > 0x7F) {
5001 // In this case, we've returned 0 in this case for long time because
5002 // we decided that we should avoid setting same keyCode value to 2 or
5003 // more keys since active keyboard layout may have a key to input the
5004 // punctuation with different key. However, setting keyCode to 0
5005 // makes some web applications which are aware of neither
5006 // KeyboardEvent.key nor KeyboardEvent.code not work with Firefox
5007 // when user selects non-ASCII capable keyboard layout such as
5008 // Russian and Thai layout. So, let's decide keyCode value with
5009 // major keyboard layout's key which causes the OEM keycode.
5010 // Actually, this maps same keyCode value to 2 keys on Russian
5011 // keyboard layout. "Period" key causes VK_OEM_PERIOD but inputs
5012 // Yu of Cyrillic and "Slash" key causes VK_OEM_2 (same as US
5013 // keyboard layout) but inputs "." (period of ASCII). Therefore,
5014 // we return DOM_VK_PERIOD which is same as VK_OEM_PERIOD for
5015 // "Period" key. On the other hand, we use same keyCode value for
5016 // "Slash" key too because it inputs ".".
5018 switch (aNativeKeyCode
) {
5020 code
= CODE_NAME_INDEX_Semicolon
;
5023 code
= CODE_NAME_INDEX_Equal
;
5026 code
= CODE_NAME_INDEX_Comma
;
5029 code
= CODE_NAME_INDEX_Minus
;
5032 code
= CODE_NAME_INDEX_Period
;
5035 code
= CODE_NAME_INDEX_Slash
;
5038 code
= CODE_NAME_INDEX_Backquote
;
5041 code
= CODE_NAME_INDEX_BracketLeft
;
5044 code
= CODE_NAME_INDEX_Backslash
;
5047 code
= CODE_NAME_INDEX_BracketRight
;
5050 code
= CODE_NAME_INDEX_Quote
;
5053 // Use keyCode value for "Backquote" key on UK keyboard layout.
5054 code
= CODE_NAME_INDEX_Backquote
;
5057 // Use keyCode value for "IntlBackslash" key.
5058 code
= CODE_NAME_INDEX_IntlBackslash
;
5060 case VK_ABNT_C1
: // "/" of ABNT.
5061 // Use keyCode value for "IntlBackslash" key on ABNT keyboard
5063 code
= CODE_NAME_INDEX_IntlBackslash
;
5066 MOZ_ASSERT_UNREACHABLE("Handle all OEM keycode values");
5069 return WidgetKeyboardEvent::GetFallbackKeyCodeOfPunctuationKey(code
);
5072 return WidgetUtils::ComputeKeyCodeFromChar(uniChars
.CharAt(0));
5075 // IE sets 0xC2 to the DOM keyCode for VK_ABNT_C2. However, we're already
5076 // using NS_VK_SEPARATOR for the separator key on Mac and Linux. Therefore,
5077 // We should keep consistency between Gecko on all platforms rather than
5078 // with other browsers since a lot of keyCode values are already different
5079 // between browsers.
5081 return NS_VK_SEPARATOR
;
5083 // VK_PROCESSKEY means IME already consumed the key event.
5085 return NS_VK_PROCESSKEY
;
5086 // VK_PACKET is generated by SendInput() API, we don't need to
5087 // care this message as key event.
5090 // If a key is not mapped to a virtual keycode, 0xFF is used.
5092 NS_WARNING("The key is failed to be converted to a virtual keycode");
5096 nsPrintfCString
warning(
5097 "Unknown virtual keycode (0x%08X), please check the "
5098 "latest MSDN document, there may be some new "
5099 "keycodes we've never known.",
5101 NS_WARNING(warning
.get());
5106 KeyNameIndex
KeyboardLayout::ConvertNativeKeyCodeToKeyNameIndex(
5107 uint8_t aVirtualKey
) const {
5108 if (IsPrintableCharKey(aVirtualKey
) || aVirtualKey
== VK_PACKET
) {
5109 return KEY_NAME_INDEX_USE_STRING
;
5112 // If the keyboard layout has AltGr and AltRight key is pressed,
5114 if (aVirtualKey
== VK_RMENU
&& HasAltGr()) {
5115 return KEY_NAME_INDEX_AltGraph
;
5118 switch (aVirtualKey
) {
5119 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5120 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5122 return aKeyNameIndex;
5124 #include "NativeKeyToDOMKeyName.h"
5126 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5132 HKL layout
= GetLayout();
5133 WORD langID
= LOWORD(static_cast<HKL
>(layout
));
5134 WORD primaryLangID
= PRIMARYLANGID(langID
);
5136 if (primaryLangID
== LANG_JAPANESE
) {
5137 switch (aVirtualKey
) {
5138 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5139 #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, \
5142 return aKeyNameIndex;
5144 #include "NativeKeyToDOMKeyName.h"
5146 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5151 } else if (primaryLangID
== LANG_KOREAN
) {
5152 switch (aVirtualKey
) {
5153 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5154 #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5156 return aKeyNameIndex;
5158 #include "NativeKeyToDOMKeyName.h"
5160 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5163 return KEY_NAME_INDEX_Unidentified
;
5167 switch (aVirtualKey
) {
5168 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5169 #define NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5171 return aKeyNameIndex;
5173 #include "NativeKeyToDOMKeyName.h"
5175 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5178 return KEY_NAME_INDEX_Unidentified
;
5183 CodeNameIndex
KeyboardLayout::ConvertScanCodeToCodeNameIndex(UINT aScanCode
) {
5184 switch (aScanCode
) {
5185 #define NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX(aNativeKey, aCodeNameIndex) \
5187 return aCodeNameIndex;
5189 #include "NativeKeyToDOMCodeName.h"
5191 #undef NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX
5194 return CODE_NAME_INDEX_UNKNOWN
;
5198 nsresult
KeyboardLayout::SynthesizeNativeKeyEvent(
5199 nsWindowBase
* aWidget
, int32_t aNativeKeyboardLayout
,
5200 int32_t aNativeKeyCode
, uint32_t aModifierFlags
,
5201 const nsAString
& aCharacters
, const nsAString
& aUnmodifiedCharacters
) {
5202 UINT keyboardLayoutListCount
= ::GetKeyboardLayoutList(0, nullptr);
5203 NS_ASSERTION(keyboardLayoutListCount
> 0,
5204 "One keyboard layout must be installed at least");
5205 HKL keyboardLayoutListBuff
[50];
5206 HKL
* keyboardLayoutList
= keyboardLayoutListCount
< 50
5207 ? keyboardLayoutListBuff
5208 : new HKL
[keyboardLayoutListCount
];
5209 keyboardLayoutListCount
=
5210 ::GetKeyboardLayoutList(keyboardLayoutListCount
, keyboardLayoutList
);
5211 NS_ASSERTION(keyboardLayoutListCount
> 0,
5212 "Failed to get all keyboard layouts installed on the system");
5214 nsPrintfCString
layoutName("%08x", aNativeKeyboardLayout
);
5215 HKL loadedLayout
= LoadKeyboardLayoutA(layoutName
.get(), KLF_NOTELLSHELL
);
5216 if (loadedLayout
== nullptr) {
5217 if (keyboardLayoutListBuff
!= keyboardLayoutList
) {
5218 delete[] keyboardLayoutList
;
5220 return NS_ERROR_NOT_AVAILABLE
;
5223 // Setup clean key state and load desired layout
5224 BYTE originalKbdState
[256];
5225 ::GetKeyboardState(originalKbdState
);
5227 memset(kbdState
, 0, sizeof(kbdState
));
5228 // This changes the state of the keyboard for the current thread only,
5229 // and we'll restore it soon, so this should be OK.
5230 ::SetKeyboardState(kbdState
);
5232 OverrideLayout(loadedLayout
);
5234 bool isAltGrKeyPress
= false;
5235 if (aModifierFlags
& nsIWidget::ALTGRAPH
) {
5237 return NS_ERROR_INVALID_ARG
;
5239 // AltGr emulates ControlLeft key press and AltRight key press.
5240 // So, we should remove those flags from aModifierFlags before
5241 // calling WinUtils::SetupKeyModifiersSequence() to create correct
5243 // FYI: We don't support both ControlLeft and AltRight (AltGr) are
5244 // pressed at the same time unless synthesizing key is
5246 aModifierFlags
&= ~(nsIWidget::CTRL_L
| nsIWidget::ALT_R
);
5249 uint8_t argumentKeySpecific
= 0;
5250 switch (aNativeKeyCode
& 0xFF) {
5252 aModifierFlags
&= ~(nsIWidget::SHIFT_L
| nsIWidget::SHIFT_R
);
5253 argumentKeySpecific
= VK_LSHIFT
;
5256 aModifierFlags
&= ~nsIWidget::SHIFT_L
;
5257 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5258 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_SHIFT
;
5261 aModifierFlags
&= ~nsIWidget::SHIFT_R
;
5262 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5263 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_SHIFT
;
5266 aModifierFlags
&= ~(nsIWidget::CTRL_L
| nsIWidget::CTRL_R
);
5267 argumentKeySpecific
= VK_LCONTROL
;
5270 aModifierFlags
&= ~nsIWidget::CTRL_L
;
5271 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5272 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_CONTROL
;
5275 aModifierFlags
&= ~nsIWidget::CTRL_R
;
5276 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5277 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_CONTROL
;
5280 aModifierFlags
&= ~(nsIWidget::ALT_L
| nsIWidget::ALT_R
);
5281 argumentKeySpecific
= VK_LMENU
;
5284 aModifierFlags
&= ~nsIWidget::ALT_L
;
5285 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5286 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_MENU
;
5289 aModifierFlags
&= ~(nsIWidget::ALT_R
| nsIWidget::ALTGRAPH
);
5290 argumentKeySpecific
= aNativeKeyCode
& 0xFF;
5291 aNativeKeyCode
= (aNativeKeyCode
& 0xFFFF0000) | VK_MENU
;
5292 // If AltRight key is AltGr in the keyboard layout, let's use
5293 // SetupKeyModifiersSequence() to emulate the native behavior
5294 // since the same event order between keydown and keyup makes
5295 // the following code complicated.
5297 isAltGrKeyPress
= true;
5298 aModifierFlags
&= ~nsIWidget::CTRL_L
;
5299 aModifierFlags
|= nsIWidget::ALTGRAPH
;
5303 aModifierFlags
&= ~nsIWidget::CAPS_LOCK
;
5304 argumentKeySpecific
= VK_CAPITAL
;
5307 aModifierFlags
&= ~nsIWidget::NUM_LOCK
;
5308 argumentKeySpecific
= VK_NUMLOCK
;
5312 AutoTArray
<KeyPair
, 10> keySequence
;
5313 WinUtils::SetupKeyModifiersSequence(&keySequence
, aModifierFlags
, WM_KEYDOWN
);
5314 if (!isAltGrKeyPress
) {
5315 keySequence
.AppendElement(KeyPair(aNativeKeyCode
, argumentKeySpecific
));
5318 // Simulate the pressing of each modifier key and then the real key
5319 // FYI: Each NativeKey instance here doesn't need to override keyboard layout
5320 // since this method overrides and restores the keyboard layout.
5321 for (uint32_t i
= 0; i
< keySequence
.Length(); ++i
) {
5322 uint8_t key
= keySequence
[i
].mGeneral
;
5323 uint8_t keySpecific
= keySequence
[i
].mSpecific
;
5324 uint16_t scanCode
= keySequence
[i
].mScanCode
;
5325 kbdState
[key
] = 0x81; // key is down and toggled on if appropriate
5327 kbdState
[keySpecific
] = 0x81;
5329 ::SetKeyboardState(kbdState
);
5330 ModifierKeyState modKeyState
;
5331 // If scan code isn't specified explicitly, let's compute it with current
5335 ComputeScanCodeForVirtualKeyCode(keySpecific
? keySpecific
: key
);
5337 LPARAM lParam
= static_cast<LPARAM
>(scanCode
<< 16);
5338 // If the scan code is for an extended key, set extended key flag.
5339 if ((scanCode
& 0xFF00) == 0xE000) {
5340 lParam
|= 0x1000000;
5342 // When AltGr key is pressed, both ControlLeft and AltRight cause
5343 // WM_KEYDOWN messages.
5344 bool makeSysKeyMsg
=
5345 !(aModifierFlags
& nsIWidget::ALTGRAPH
) && IsSysKey(key
, modKeyState
);
5347 WinUtils::InitMSG(makeSysKeyMsg
? WM_SYSKEYDOWN
: WM_KEYDOWN
, key
,
5348 lParam
, aWidget
->GetWindowHandle());
5349 if (i
== keySequence
.Length() - 1) {
5350 bool makeDeadCharMsg
=
5351 (IsDeadKey(key
, modKeyState
) && aCharacters
.IsEmpty());
5352 nsAutoString
chars(aCharacters
);
5353 if (makeDeadCharMsg
) {
5354 UniCharsAndModifiers deadChars
=
5355 GetUniCharsAndModifiers(key
, modKeyState
);
5356 chars
= deadChars
.ToString();
5357 NS_ASSERTION(chars
.Length() == 1,
5358 "Dead char must be only one character");
5360 if (chars
.IsEmpty()) {
5361 NativeKey
nativeKey(aWidget
, keyDownMsg
, modKeyState
);
5362 nativeKey
.HandleKeyDownMessage();
5364 AutoTArray
<NativeKey::FakeCharMsg
, 10> fakeCharMsgs
;
5365 for (uint32_t j
= 0; j
< chars
.Length(); j
++) {
5366 NativeKey::FakeCharMsg
* fakeCharMsg
= fakeCharMsgs
.AppendElement();
5367 fakeCharMsg
->mCharCode
= chars
.CharAt(j
);
5368 fakeCharMsg
->mScanCode
= scanCode
;
5369 fakeCharMsg
->mIsSysKey
= makeSysKeyMsg
;
5370 fakeCharMsg
->mIsDeadKey
= makeDeadCharMsg
;
5372 NativeKey
nativeKey(aWidget
, keyDownMsg
, modKeyState
, 0, &fakeCharMsgs
);
5374 nativeKey
.HandleKeyDownMessage(&dispatched
);
5375 // If some char messages are not consumed, let's emulate the widget
5376 // receiving the message directly.
5377 for (uint32_t j
= 1; j
< fakeCharMsgs
.Length(); j
++) {
5378 if (fakeCharMsgs
[j
].mConsumed
) {
5381 MSG charMsg
= fakeCharMsgs
[j
].GetCharMsg(aWidget
->GetWindowHandle());
5382 NativeKey
nativeKey(aWidget
, charMsg
, modKeyState
);
5383 nativeKey
.HandleCharMessage(charMsg
);
5387 NativeKey
nativeKey(aWidget
, keyDownMsg
, modKeyState
);
5388 nativeKey
.HandleKeyDownMessage();
5392 keySequence
.Clear();
5393 if (!isAltGrKeyPress
) {
5394 keySequence
.AppendElement(KeyPair(aNativeKeyCode
, argumentKeySpecific
));
5396 WinUtils::SetupKeyModifiersSequence(&keySequence
, aModifierFlags
, WM_KEYUP
);
5397 for (uint32_t i
= 0; i
< keySequence
.Length(); ++i
) {
5398 uint8_t key
= keySequence
[i
].mGeneral
;
5399 uint8_t keySpecific
= keySequence
[i
].mSpecific
;
5400 uint16_t scanCode
= keySequence
[i
].mScanCode
;
5401 kbdState
[key
] = 0; // key is up and toggled off if appropriate
5403 kbdState
[keySpecific
] = 0;
5405 ::SetKeyboardState(kbdState
);
5406 ModifierKeyState modKeyState
;
5407 // If scan code isn't specified explicitly, let's compute it with current
5411 ComputeScanCodeForVirtualKeyCode(keySpecific
? keySpecific
: key
);
5413 LPARAM lParam
= static_cast<LPARAM
>(scanCode
<< 16);
5414 // If the scan code is for an extended key, set extended key flag.
5415 if ((scanCode
& 0xFF00) == 0xE000) {
5416 lParam
|= 0x1000000;
5418 // Don't use WM_SYSKEYUP for Alt keyup.
5419 // NOTE: When AltGr was pressed, ControlLeft causes WM_SYSKEYUP normally.
5420 bool makeSysKeyMsg
= IsSysKey(key
, modKeyState
) && key
!= VK_MENU
;
5421 MSG keyUpMsg
= WinUtils::InitMSG(makeSysKeyMsg
? WM_SYSKEYUP
: WM_KEYUP
,
5422 key
, lParam
, aWidget
->GetWindowHandle());
5423 NativeKey
nativeKey(aWidget
, keyUpMsg
, modKeyState
);
5424 nativeKey
.HandleKeyUpMessage();
5427 // Restore old key state and layout
5428 ::SetKeyboardState(originalKbdState
);
5431 // Don't unload the layout if it's installed actually.
5432 for (uint32_t i
= 0; i
< keyboardLayoutListCount
; i
++) {
5433 if (keyboardLayoutList
[i
] == loadedLayout
) {
5438 if (keyboardLayoutListBuff
!= keyboardLayoutList
) {
5439 delete[] keyboardLayoutList
;
5442 ::UnloadKeyboardLayout(loadedLayout
);
5447 /*****************************************************************************
5448 * mozilla::widget::DeadKeyTable
5449 *****************************************************************************/
5451 char16_t
DeadKeyTable::GetCompositeChar(char16_t aBaseChar
) const {
5452 // Dead-key table is sorted by BaseChar in ascending order.
5453 // Usually they are too small to use binary search.
5455 for (uint32_t index
= 0; index
< mEntries
; index
++) {
5456 if (mTable
[index
].BaseChar
== aBaseChar
) {
5457 return mTable
[index
].CompositeChar
;
5459 if (mTable
[index
].BaseChar
> aBaseChar
) {
5467 /*****************************************************************************
5468 * mozilla::widget::RedirectedKeyDownMessage
5469 *****************************************************************************/
5471 MSG
RedirectedKeyDownMessageManager::sRedirectedKeyDownMsg
;
5472 bool RedirectedKeyDownMessageManager::sDefaultPreventedOfRedirectedMsg
= false;
5475 bool RedirectedKeyDownMessageManager::IsRedirectedMessage(const MSG
& aMsg
) {
5476 return (aMsg
.message
== WM_KEYDOWN
|| aMsg
.message
== WM_SYSKEYDOWN
) &&
5477 (sRedirectedKeyDownMsg
.message
== aMsg
.message
&&
5478 WinUtils::GetScanCode(sRedirectedKeyDownMsg
.lParam
) ==
5479 WinUtils::GetScanCode(aMsg
.lParam
));
5483 void RedirectedKeyDownMessageManager::RemoveNextCharMessage(HWND aWnd
) {
5485 if (WinUtils::PeekMessage(&msg
, aWnd
, WM_KEYFIRST
, WM_KEYLAST
,
5486 PM_NOREMOVE
| PM_NOYIELD
) &&
5487 (msg
.message
== WM_CHAR
|| msg
.message
== WM_SYSCHAR
)) {
5488 WinUtils::PeekMessage(&msg
, aWnd
, msg
.message
, msg
.message
,
5489 PM_REMOVE
| PM_NOYIELD
);
5493 } // namespace widget
5494 } // namespace mozilla