Backed out 15 changesets (bug 1852806) for causing mda failures on test_video_low_pow...
[gecko.git] / widget / windows / KeyboardLayout.cpp
blob07a8f892818986e3a87a501d2c2da7a7e70f187f
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/Logging.h"
8 #include "mozilla/ArrayUtils.h"
9 #include "mozilla/AutoRestore.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/MouseEvents.h"
12 #include "mozilla/MiscEvents.h"
13 #include "mozilla/Preferences.h"
14 #include "mozilla/TextEvents.h"
16 #include "nsAlgorithm.h"
17 #include "nsExceptionHandler.h"
18 #include "nsGkAtoms.h"
19 #include "nsIUserIdleServiceInternal.h"
20 #include "nsIWindowsRegKey.h"
21 #include "nsPrintfCString.h"
22 #include "nsQuickSort.h"
23 #include "nsReadableUtils.h"
24 #include "nsServiceManagerUtils.h"
25 #include "nsToolkit.h"
26 #include "nsUnicharUtils.h"
27 #include "nsWindowDbg.h"
29 #include "KeyboardLayout.h"
30 #include "WidgetUtils.h"
31 #include "WinUtils.h"
33 #include "npapi.h"
35 #include <windows.h>
36 #include <winnls.h>
37 #include <winuser.h>
38 #include <algorithm>
40 #ifndef WINABLEAPI
41 # include <winable.h>
42 #endif
44 // In WinUser.h, MAPVK_VK_TO_VSC_EX is defined only when WINVER >= 0x0600
45 #ifndef MAPVK_VK_TO_VSC_EX
46 # define MAPVK_VK_TO_VSC_EX (4)
47 #endif
49 // For collecting other people's log, tell them `MOZ_LOG=KeyboardHandler:4,sync`
50 // rather than `MOZ_LOG=KeyboardHandler:5,sync` since using `5` may create too
51 // big file.
52 // Therefore you shouldn't use `LogLevel::Verbose` for logging usual behavior.
53 mozilla::LazyLogModule gKeyLog("KeyboardHandler");
55 namespace mozilla {
56 namespace widget {
58 static const char* const kVirtualKeyName[] = {
59 "NULL",
60 "VK_LBUTTON",
61 "VK_RBUTTON",
62 "VK_CANCEL",
63 "VK_MBUTTON",
64 "VK_XBUTTON1",
65 "VK_XBUTTON2",
66 "0x07",
67 "VK_BACK",
68 "VK_TAB",
69 "0x0A",
70 "0x0B",
71 "VK_CLEAR",
72 "VK_RETURN",
73 "0x0E",
74 "0x0F",
76 "VK_SHIFT",
77 "VK_CONTROL",
78 "VK_MENU",
79 "VK_PAUSE",
80 "VK_CAPITAL",
81 "VK_KANA, VK_HANGUL",
82 "0x16",
83 "VK_JUNJA",
84 "VK_FINAL",
85 "VK_HANJA, VK_KANJI",
86 "0x1A",
87 "VK_ESCAPE",
88 "VK_CONVERT",
89 "VK_NONCONVERT",
90 "VK_ACCEPT",
91 "VK_MODECHANGE",
93 "VK_SPACE",
94 "VK_PRIOR",
95 "VK_NEXT",
96 "VK_END",
97 "VK_HOME",
98 "VK_LEFT",
99 "VK_UP",
100 "VK_RIGHT",
101 "VK_DOWN",
102 "VK_SELECT",
103 "VK_PRINT",
104 "VK_EXECUTE",
105 "VK_SNAPSHOT",
106 "VK_INSERT",
107 "VK_DELETE",
108 "VK_HELP",
110 "VK_0",
111 "VK_1",
112 "VK_2",
113 "VK_3",
114 "VK_4",
115 "VK_5",
116 "VK_6",
117 "VK_7",
118 "VK_8",
119 "VK_9",
120 "0x3A",
121 "0x3B",
122 "0x3C",
123 "0x3D",
124 "0x3E",
125 "0x3F",
127 "0x40",
128 "VK_A",
129 "VK_B",
130 "VK_C",
131 "VK_D",
132 "VK_E",
133 "VK_F",
134 "VK_G",
135 "VK_H",
136 "VK_I",
137 "VK_J",
138 "VK_K",
139 "VK_L",
140 "VK_M",
141 "VK_N",
142 "VK_O",
144 "VK_P",
145 "VK_Q",
146 "VK_R",
147 "VK_S",
148 "VK_T",
149 "VK_U",
150 "VK_V",
151 "VK_W",
152 "VK_X",
153 "VK_Y",
154 "VK_Z",
155 "VK_LWIN",
156 "VK_RWIN",
157 "VK_APPS",
158 "0x5E",
159 "VK_SLEEP",
161 "VK_NUMPAD0",
162 "VK_NUMPAD1",
163 "VK_NUMPAD2",
164 "VK_NUMPAD3",
165 "VK_NUMPAD4",
166 "VK_NUMPAD5",
167 "VK_NUMPAD6",
168 "VK_NUMPAD7",
169 "VK_NUMPAD8",
170 "VK_NUMPAD9",
171 "VK_MULTIPLY",
172 "VK_ADD",
173 "VK_SEPARATOR",
174 "VK_SUBTRACT",
175 "VK_DECIMAL",
176 "VK_DIVIDE",
178 "VK_F1",
179 "VK_F2",
180 "VK_F3",
181 "VK_F4",
182 "VK_F5",
183 "VK_F6",
184 "VK_F7",
185 "VK_F8",
186 "VK_F9",
187 "VK_F10",
188 "VK_F11",
189 "VK_F12",
190 "VK_F13",
191 "VK_F14",
192 "VK_F15",
193 "VK_F16",
195 "VK_F17",
196 "VK_F18",
197 "VK_F19",
198 "VK_F20",
199 "VK_F21",
200 "VK_F22",
201 "VK_F23",
202 "VK_F24",
203 "0x88",
204 "0x89",
205 "0x8A",
206 "0x8B",
207 "0x8C",
208 "0x8D",
209 "0x8E",
210 "0x8F",
212 "VK_NUMLOCK",
213 "VK_SCROLL",
214 "VK_OEM_NEC_EQUAL, VK_OEM_FJ_JISHO",
215 "VK_OEM_FJ_MASSHOU",
216 "VK_OEM_FJ_TOUROKU",
217 "VK_OEM_FJ_LOYA",
218 "VK_OEM_FJ_ROYA",
219 "0x97",
220 "0x98",
221 "0x99",
222 "0x9A",
223 "0x9B",
224 "0x9C",
225 "0x9D",
226 "0x9E",
227 "0x9F",
229 "VK_LSHIFT",
230 "VK_RSHIFT",
231 "VK_LCONTROL",
232 "VK_RCONTROL",
233 "VK_LMENU",
234 "VK_RMENU",
235 "VK_BROWSER_BACK",
236 "VK_BROWSER_FORWARD",
237 "VK_BROWSER_REFRESH",
238 "VK_BROWSER_STOP",
239 "VK_BROWSER_SEARCH",
240 "VK_BROWSER_FAVORITES",
241 "VK_BROWSER_HOME",
242 "VK_VOLUME_MUTE",
243 "VK_VOLUME_DOWN",
244 "VK_VOLUME_UP",
246 "VK_MEDIA_NEXT_TRACK",
247 "VK_MEDIA_PREV_TRACK",
248 "VK_MEDIA_STOP",
249 "VK_MEDIA_PLAY_PAUSE",
250 "VK_LAUNCH_MAIL",
251 "VK_LAUNCH_MEDIA_SELECT",
252 "VK_LAUNCH_APP1",
253 "VK_LAUNCH_APP2",
254 "0xB8",
255 "0xB9",
256 "VK_OEM_1",
257 "VK_OEM_PLUS",
258 "VK_OEM_COMMA",
259 "VK_OEM_MINUS",
260 "VK_OEM_PERIOD",
261 "VK_OEM_2",
263 "VK_OEM_3",
264 "VK_ABNT_C1",
265 "VK_ABNT_C2",
266 "0xC3",
267 "0xC4",
268 "0xC5",
269 "0xC6",
270 "0xC7",
271 "0xC8",
272 "0xC9",
273 "0xCA",
274 "0xCB",
275 "0xCC",
276 "0xCD",
277 "0xCE",
278 "0xCF",
280 "0xD0",
281 "0xD1",
282 "0xD2",
283 "0xD3",
284 "0xD4",
285 "0xD5",
286 "0xD6",
287 "0xD7",
288 "0xD8",
289 "0xD9",
290 "0xDA",
291 "VK_OEM_4",
292 "VK_OEM_5",
293 "VK_OEM_6",
294 "VK_OEM_7",
295 "VK_OEM_8",
297 "0xE0",
298 "VK_OEM_AX",
299 "VK_OEM_102",
300 "VK_ICO_HELP",
301 "VK_ICO_00",
302 "VK_PROCESSKEY",
303 "VK_ICO_CLEAR",
304 "VK_PACKET",
305 "0xE8",
306 "VK_OEM_RESET",
307 "VK_OEM_JUMP",
308 "VK_OEM_PA1",
309 "VK_OEM_PA2",
310 "VK_OEM_PA3",
311 "VK_OEM_WSCTRL",
312 "VK_OEM_CUSEL",
314 "VK_OEM_ATTN",
315 "VK_OEM_FINISH",
316 "VK_OEM_COPY",
317 "VK_OEM_AUTO",
318 "VK_OEM_ENLW",
319 "VK_OEM_BACKTAB",
320 "VK_ATTN",
321 "VK_CRSEL",
322 "VK_EXSEL",
323 "VK_EREOF",
324 "VK_PLAY",
325 "VK_ZOOM",
326 "VK_NONAME",
327 "VK_PA1",
328 "VK_OEM_CLEAR",
329 "0xFF"};
331 static_assert(sizeof(kVirtualKeyName) / sizeof(const char*) == 0x100,
332 "The virtual key name must be defined just 256 keys");
334 static const char* GetBoolName(bool aBool) { return aBool ? "true" : "false"; }
336 static const nsCString GetCharacterCodeName(WPARAM aCharCode) {
337 switch (aCharCode) {
338 case 0x0000:
339 return "NULL (0x0000)"_ns;
340 case 0x0008:
341 return "BACKSPACE (0x0008)"_ns;
342 case 0x0009:
343 return "CHARACTER TABULATION (0x0009)"_ns;
344 case 0x000A:
345 return "LINE FEED (0x000A)"_ns;
346 case 0x000B:
347 return "LINE TABULATION (0x000B)"_ns;
348 case 0x000C:
349 return "FORM FEED (0x000C)"_ns;
350 case 0x000D:
351 return "CARRIAGE RETURN (0x000D)"_ns;
352 case 0x0018:
353 return "CANCEL (0x0018)"_ns;
354 case 0x001B:
355 return "ESCAPE (0x001B)"_ns;
356 case 0x0020:
357 return "SPACE (0x0020)"_ns;
358 case 0x007F:
359 return "DELETE (0x007F)"_ns;
360 case 0x00A0:
361 return "NO-BREAK SPACE (0x00A0)"_ns;
362 case 0x00AD:
363 return "SOFT HYPHEN (0x00AD)"_ns;
364 case 0x2000:
365 return "EN QUAD (0x2000)"_ns;
366 case 0x2001:
367 return "EM QUAD (0x2001)"_ns;
368 case 0x2002:
369 return "EN SPACE (0x2002)"_ns;
370 case 0x2003:
371 return "EM SPACE (0x2003)"_ns;
372 case 0x2004:
373 return "THREE-PER-EM SPACE (0x2004)"_ns;
374 case 0x2005:
375 return "FOUR-PER-EM SPACE (0x2005)"_ns;
376 case 0x2006:
377 return "SIX-PER-EM SPACE (0x2006)"_ns;
378 case 0x2007:
379 return "FIGURE SPACE (0x2007)"_ns;
380 case 0x2008:
381 return "PUNCTUATION SPACE (0x2008)"_ns;
382 case 0x2009:
383 return "THIN SPACE (0x2009)"_ns;
384 case 0x200A:
385 return "HAIR SPACE (0x200A)"_ns;
386 case 0x200B:
387 return "ZERO WIDTH SPACE (0x200B)"_ns;
388 case 0x200C:
389 return "ZERO WIDTH NON-JOINER (0x200C)"_ns;
390 case 0x200D:
391 return "ZERO WIDTH JOINER (0x200D)"_ns;
392 case 0x200E:
393 return "LEFT-TO-RIGHT MARK (0x200E)"_ns;
394 case 0x200F:
395 return "RIGHT-TO-LEFT MARK (0x200F)"_ns;
396 case 0x2029:
397 return "PARAGRAPH SEPARATOR (0x2029)"_ns;
398 case 0x202A:
399 return "LEFT-TO-RIGHT EMBEDDING (0x202A)"_ns;
400 case 0x202B:
401 return "RIGHT-TO-LEFT EMBEDDING (0x202B)"_ns;
402 case 0x202D:
403 return "LEFT-TO-RIGHT OVERRIDE (0x202D)"_ns;
404 case 0x202E:
405 return "RIGHT-TO-LEFT OVERRIDE (0x202E)"_ns;
406 case 0x202F:
407 return "NARROW NO-BREAK SPACE (0x202F)"_ns;
408 case 0x205F:
409 return "MEDIUM MATHEMATICAL SPACE (0x205F)"_ns;
410 case 0x2060:
411 return "WORD JOINER (0x2060)"_ns;
412 case 0x2066:
413 return "LEFT-TO-RIGHT ISOLATE (0x2066)"_ns;
414 case 0x2067:
415 return "RIGHT-TO-LEFT ISOLATE (0x2067)"_ns;
416 case 0x3000:
417 return "IDEOGRAPHIC SPACE (0x3000)"_ns;
418 case 0xFEFF:
419 return "ZERO WIDTH NO-BREAK SPACE (0xFEFF)"_ns;
420 default: {
421 if (aCharCode < ' ' || (aCharCode >= 0x80 && aCharCode < 0xA0)) {
422 return nsPrintfCString("control (0x%04zX)", aCharCode);
424 if (NS_IS_HIGH_SURROGATE(aCharCode)) {
425 return nsPrintfCString("high surrogate (0x%04zX)", aCharCode);
427 if (NS_IS_LOW_SURROGATE(aCharCode)) {
428 return nsPrintfCString("low surrogate (0x%04zX)", aCharCode);
430 return IS_IN_BMP(aCharCode)
431 ? nsPrintfCString(
432 "'%s' (0x%04zX)",
433 NS_ConvertUTF16toUTF8(nsAutoString(aCharCode)).get(),
434 aCharCode)
435 : nsPrintfCString(
436 "'%s' (0x%08zX)",
437 NS_ConvertUTF16toUTF8(nsAutoString(aCharCode)).get(),
438 aCharCode);
443 static const nsCString GetKeyLocationName(uint32_t aLocation) {
444 switch (aLocation) {
445 case eKeyLocationLeft:
446 return "KEY_LOCATION_LEFT"_ns;
447 case eKeyLocationRight:
448 return "KEY_LOCATION_RIGHT"_ns;
449 case eKeyLocationStandard:
450 return "KEY_LOCATION_STANDARD"_ns;
451 case eKeyLocationNumpad:
452 return "KEY_LOCATION_NUMPAD"_ns;
453 default:
454 return nsPrintfCString("Unknown (0x%04X)", aLocation);
458 static const nsCString GetCharacterCodeNames(const char16_t* aChars,
459 uint32_t aLength) {
460 if (!aLength) {
461 return ""_ns;
463 nsCString result;
464 result.AssignLiteral("\"");
465 StringJoinAppend(result, ", "_ns, Span{aChars, aLength},
466 [](nsACString& dest, const char16_t charValue) {
467 dest.Append(GetCharacterCodeName(charValue));
469 result.AppendLiteral("\"");
470 return result;
473 static const nsCString GetCharacterCodeNames(
474 const UniCharsAndModifiers& aUniCharsAndModifiers) {
475 if (aUniCharsAndModifiers.IsEmpty()) {
476 return ""_ns;
478 nsCString result;
479 result.AssignLiteral("\"");
480 StringJoinAppend(result, ", "_ns, Span{aUniCharsAndModifiers.ToString()},
481 [](nsACString& dest, const char16_t charValue) {
482 dest.Append(GetCharacterCodeName(charValue));
484 result.AppendLiteral("\"");
485 return result;
488 class MOZ_STACK_CLASS GetShiftStateName final : public nsAutoCString {
489 public:
490 explicit GetShiftStateName(VirtualKey::ShiftState aShiftState) {
491 if (!aShiftState) {
492 AssignLiteral("none");
493 return;
495 if (aShiftState & VirtualKey::STATE_SHIFT) {
496 AssignLiteral("Shift");
497 aShiftState &= ~VirtualKey::STATE_SHIFT;
499 if (aShiftState & VirtualKey::STATE_CONTROL) {
500 MaybeAppendSeparator();
501 AssignLiteral("Ctrl");
502 aShiftState &= ~VirtualKey::STATE_CONTROL;
504 if (aShiftState & VirtualKey::STATE_ALT) {
505 MaybeAppendSeparator();
506 AssignLiteral("Alt");
507 aShiftState &= ~VirtualKey::STATE_ALT;
509 if (aShiftState & VirtualKey::STATE_CAPSLOCK) {
510 MaybeAppendSeparator();
511 AssignLiteral("CapsLock");
512 aShiftState &= ~VirtualKey::STATE_CAPSLOCK;
514 MOZ_ASSERT(!aShiftState);
517 private:
518 void MaybeAppendSeparator() {
519 if (!IsEmpty()) {
520 AppendLiteral(" | ");
525 static const nsCString GetMessageName(UINT aMessage) {
526 switch (aMessage) {
527 case WM_NULL:
528 return "WM_NULL"_ns;
529 case WM_KEYDOWN:
530 return "WM_KEYDOWN"_ns;
531 case WM_KEYUP:
532 return "WM_KEYUP"_ns;
533 case WM_SYSKEYDOWN:
534 return "WM_SYSKEYDOWN"_ns;
535 case WM_SYSKEYUP:
536 return "WM_SYSKEYUP"_ns;
537 case WM_CHAR:
538 return "WM_CHAR"_ns;
539 case WM_UNICHAR:
540 return "WM_UNICHAR"_ns;
541 case WM_SYSCHAR:
542 return "WM_SYSCHAR"_ns;
543 case WM_DEADCHAR:
544 return "WM_DEADCHAR"_ns;
545 case WM_SYSDEADCHAR:
546 return "WM_SYSDEADCHAR"_ns;
547 case WM_APPCOMMAND:
548 return "WM_APPCOMMAND"_ns;
549 case WM_QUIT:
550 return "WM_QUIT"_ns;
551 default:
552 return nsPrintfCString("Unknown Message (0x%04X)", aMessage);
556 static const nsCString GetVirtualKeyCodeName(WPARAM aVK) {
557 if (aVK >= ArrayLength(kVirtualKeyName)) {
558 return nsPrintfCString("Invalid (0x%08zX)", aVK);
560 return nsCString(kVirtualKeyName[aVK]);
563 static const nsCString GetAppCommandName(WPARAM aCommand) {
564 switch (aCommand) {
565 case APPCOMMAND_BASS_BOOST:
566 return "APPCOMMAND_BASS_BOOST"_ns;
567 case APPCOMMAND_BASS_DOWN:
568 return "APPCOMMAND_BASS_DOWN"_ns;
569 case APPCOMMAND_BASS_UP:
570 return "APPCOMMAND_BASS_UP"_ns;
571 case APPCOMMAND_BROWSER_BACKWARD:
572 return "APPCOMMAND_BROWSER_BACKWARD"_ns;
573 case APPCOMMAND_BROWSER_FAVORITES:
574 return "APPCOMMAND_BROWSER_FAVORITES"_ns;
575 case APPCOMMAND_BROWSER_FORWARD:
576 return "APPCOMMAND_BROWSER_FORWARD"_ns;
577 case APPCOMMAND_BROWSER_HOME:
578 return "APPCOMMAND_BROWSER_HOME"_ns;
579 case APPCOMMAND_BROWSER_REFRESH:
580 return "APPCOMMAND_BROWSER_REFRESH"_ns;
581 case APPCOMMAND_BROWSER_SEARCH:
582 return "APPCOMMAND_BROWSER_SEARCH"_ns;
583 case APPCOMMAND_BROWSER_STOP:
584 return "APPCOMMAND_BROWSER_STOP"_ns;
585 case APPCOMMAND_CLOSE:
586 return "APPCOMMAND_CLOSE"_ns;
587 case APPCOMMAND_COPY:
588 return "APPCOMMAND_COPY"_ns;
589 case APPCOMMAND_CORRECTION_LIST:
590 return "APPCOMMAND_CORRECTION_LIST"_ns;
591 case APPCOMMAND_CUT:
592 return "APPCOMMAND_CUT"_ns;
593 case APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE:
594 return "APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE"_ns;
595 case APPCOMMAND_FIND:
596 return "APPCOMMAND_FIND"_ns;
597 case APPCOMMAND_FORWARD_MAIL:
598 return "APPCOMMAND_FORWARD_MAIL"_ns;
599 case APPCOMMAND_HELP:
600 return "APPCOMMAND_HELP"_ns;
601 case APPCOMMAND_LAUNCH_APP1:
602 return "APPCOMMAND_LAUNCH_APP1"_ns;
603 case APPCOMMAND_LAUNCH_APP2:
604 return "APPCOMMAND_LAUNCH_APP2"_ns;
605 case APPCOMMAND_LAUNCH_MAIL:
606 return "APPCOMMAND_LAUNCH_MAIL"_ns;
607 case APPCOMMAND_LAUNCH_MEDIA_SELECT:
608 return "APPCOMMAND_LAUNCH_MEDIA_SELECT"_ns;
609 case APPCOMMAND_MEDIA_CHANNEL_DOWN:
610 return "APPCOMMAND_MEDIA_CHANNEL_DOWN"_ns;
611 case APPCOMMAND_MEDIA_CHANNEL_UP:
612 return "APPCOMMAND_MEDIA_CHANNEL_UP"_ns;
613 case APPCOMMAND_MEDIA_FAST_FORWARD:
614 return "APPCOMMAND_MEDIA_FAST_FORWARD"_ns;
615 case APPCOMMAND_MEDIA_NEXTTRACK:
616 return "APPCOMMAND_MEDIA_NEXTTRACK"_ns;
617 case APPCOMMAND_MEDIA_PAUSE:
618 return "APPCOMMAND_MEDIA_PAUSE"_ns;
619 case APPCOMMAND_MEDIA_PLAY:
620 return "APPCOMMAND_MEDIA_PLAY"_ns;
621 case APPCOMMAND_MEDIA_PLAY_PAUSE:
622 return "APPCOMMAND_MEDIA_PLAY_PAUSE"_ns;
623 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
624 return "APPCOMMAND_MEDIA_PREVIOUSTRACK"_ns;
625 case APPCOMMAND_MEDIA_RECORD:
626 return "APPCOMMAND_MEDIA_RECORD"_ns;
627 case APPCOMMAND_MEDIA_REWIND:
628 return "APPCOMMAND_MEDIA_REWIND"_ns;
629 case APPCOMMAND_MEDIA_STOP:
630 return "APPCOMMAND_MEDIA_STOP"_ns;
631 case APPCOMMAND_MIC_ON_OFF_TOGGLE:
632 return "APPCOMMAND_MIC_ON_OFF_TOGGLE"_ns;
633 case APPCOMMAND_MICROPHONE_VOLUME_DOWN:
634 return "APPCOMMAND_MICROPHONE_VOLUME_DOWN"_ns;
635 case APPCOMMAND_MICROPHONE_VOLUME_MUTE:
636 return "APPCOMMAND_MICROPHONE_VOLUME_MUTE"_ns;
637 case APPCOMMAND_MICROPHONE_VOLUME_UP:
638 return "APPCOMMAND_MICROPHONE_VOLUME_UP"_ns;
639 case APPCOMMAND_NEW:
640 return "APPCOMMAND_NEW"_ns;
641 case APPCOMMAND_OPEN:
642 return "APPCOMMAND_OPEN"_ns;
643 case APPCOMMAND_PASTE:
644 return "APPCOMMAND_PASTE"_ns;
645 case APPCOMMAND_PRINT:
646 return "APPCOMMAND_PRINT"_ns;
647 case APPCOMMAND_REDO:
648 return "APPCOMMAND_REDO"_ns;
649 case APPCOMMAND_REPLY_TO_MAIL:
650 return "APPCOMMAND_REPLY_TO_MAIL"_ns;
651 case APPCOMMAND_SAVE:
652 return "APPCOMMAND_SAVE"_ns;
653 case APPCOMMAND_SEND_MAIL:
654 return "APPCOMMAND_SEND_MAIL"_ns;
655 case APPCOMMAND_SPELL_CHECK:
656 return "APPCOMMAND_SPELL_CHECK"_ns;
657 case APPCOMMAND_TREBLE_DOWN:
658 return "APPCOMMAND_TREBLE_DOWN"_ns;
659 case APPCOMMAND_TREBLE_UP:
660 return "APPCOMMAND_TREBLE_UP"_ns;
661 case APPCOMMAND_UNDO:
662 return "APPCOMMAND_UNDO"_ns;
663 case APPCOMMAND_VOLUME_DOWN:
664 return "APPCOMMAND_VOLUME_DOWN"_ns;
665 case APPCOMMAND_VOLUME_MUTE:
666 return "APPCOMMAND_VOLUME_MUTE"_ns;
667 case APPCOMMAND_VOLUME_UP:
668 return "APPCOMMAND_VOLUME_UP"_ns;
669 default:
670 return nsPrintfCString("Unknown app command (0x%08zX)", aCommand);
674 static const nsCString GetAppCommandDeviceName(LPARAM aDevice) {
675 switch (aDevice) {
676 case FAPPCOMMAND_KEY:
677 return "FAPPCOMMAND_KEY"_ns;
678 case FAPPCOMMAND_MOUSE:
679 return "FAPPCOMMAND_MOUSE"_ns;
680 case FAPPCOMMAND_OEM:
681 return "FAPPCOMMAND_OEM"_ns;
682 default:
683 return nsPrintfCString("Unknown app command device (0x%04" PRIXLPTR ")",
684 aDevice);
688 class MOZ_STACK_CLASS GetAppCommandKeysName final : public nsAutoCString {
689 public:
690 explicit GetAppCommandKeysName(WPARAM aKeys) {
691 if (aKeys & MK_CONTROL) {
692 AppendLiteral("MK_CONTROL");
693 aKeys &= ~MK_CONTROL;
695 if (aKeys & MK_LBUTTON) {
696 MaybeAppendSeparator();
697 AppendLiteral("MK_LBUTTON");
698 aKeys &= ~MK_LBUTTON;
700 if (aKeys & MK_MBUTTON) {
701 MaybeAppendSeparator();
702 AppendLiteral("MK_MBUTTON");
703 aKeys &= ~MK_MBUTTON;
705 if (aKeys & MK_RBUTTON) {
706 MaybeAppendSeparator();
707 AppendLiteral("MK_RBUTTON");
708 aKeys &= ~MK_RBUTTON;
710 if (aKeys & MK_SHIFT) {
711 MaybeAppendSeparator();
712 AppendLiteral("MK_SHIFT");
713 aKeys &= ~MK_SHIFT;
715 if (aKeys & MK_XBUTTON1) {
716 MaybeAppendSeparator();
717 AppendLiteral("MK_XBUTTON1");
718 aKeys &= ~MK_XBUTTON1;
720 if (aKeys & MK_XBUTTON2) {
721 MaybeAppendSeparator();
722 AppendLiteral("MK_XBUTTON2");
723 aKeys &= ~MK_XBUTTON2;
725 if (aKeys) {
726 MaybeAppendSeparator();
727 AppendPrintf("Unknown Flags (0x%04zX)", aKeys);
729 if (IsEmpty()) {
730 AssignLiteral("none (0x0000)");
734 private:
735 void MaybeAppendSeparator() {
736 if (!IsEmpty()) {
737 AppendLiteral(" | ");
742 static const nsCString ToString(const MSG& aMSG) {
743 nsCString result;
744 result.AssignLiteral("{ message=");
745 result.Append(GetMessageName(aMSG.message).get());
746 result.AppendLiteral(", ");
747 switch (aMSG.message) {
748 case WM_KEYDOWN:
749 case WM_KEYUP:
750 case WM_SYSKEYDOWN:
751 case WM_SYSKEYUP:
752 result.AppendPrintf(
753 "virtual keycode=%s, repeat count=%" PRIdLPTR
754 ", "
755 "scancode=0x%02X, extended key=%s, "
756 "context code=%s, previous key state=%s, "
757 "transition state=%s",
758 GetVirtualKeyCodeName(aMSG.wParam).get(), aMSG.lParam & 0xFFFF,
759 WinUtils::GetScanCode(aMSG.lParam),
760 GetBoolName(WinUtils::IsExtendedScanCode(aMSG.lParam)),
761 GetBoolName((aMSG.lParam & (1 << 29)) != 0),
762 GetBoolName((aMSG.lParam & (1 << 30)) != 0),
763 GetBoolName((aMSG.lParam & (1 << 31)) != 0));
764 break;
765 case WM_CHAR:
766 case WM_DEADCHAR:
767 case WM_SYSCHAR:
768 case WM_SYSDEADCHAR:
769 result.AppendPrintf(
770 "character code=%s, repeat count=%" PRIdLPTR
771 ", "
772 "scancode=0x%02X, extended key=%s, "
773 "context code=%s, previous key state=%s, "
774 "transition state=%s",
775 GetCharacterCodeName(aMSG.wParam).get(), aMSG.lParam & 0xFFFF,
776 WinUtils::GetScanCode(aMSG.lParam),
777 GetBoolName(WinUtils::IsExtendedScanCode(aMSG.lParam)),
778 GetBoolName((aMSG.lParam & (1 << 29)) != 0),
779 GetBoolName((aMSG.lParam & (1 << 30)) != 0),
780 GetBoolName((aMSG.lParam & (1 << 31)) != 0));
781 break;
782 case WM_APPCOMMAND:
783 result.AppendPrintf(
784 "window handle=0x%zx, app command=%s, device=%s, dwKeys=%s",
785 aMSG.wParam,
786 GetAppCommandName(GET_APPCOMMAND_LPARAM(aMSG.lParam)).get(),
787 GetAppCommandDeviceName(GET_DEVICE_LPARAM(aMSG.lParam)).get(),
788 GetAppCommandKeysName(GET_KEYSTATE_LPARAM(aMSG.lParam)).get());
789 break;
790 default:
791 result.AppendPrintf("wParam=%zu, lParam=%" PRIdLPTR, aMSG.wParam,
792 aMSG.lParam);
793 break;
795 result.AppendPrintf(", hwnd=0x%p", aMSG.hwnd);
796 return result;
799 static const nsCString ToString(
800 const UniCharsAndModifiers& aUniCharsAndModifiers) {
801 if (aUniCharsAndModifiers.IsEmpty()) {
802 return "{}"_ns;
804 nsCString result;
805 result.AssignLiteral("{ ");
806 result.Append(GetCharacterCodeName(aUniCharsAndModifiers.CharAt(0)));
807 for (size_t i = 1; i < aUniCharsAndModifiers.Length(); ++i) {
808 if (aUniCharsAndModifiers.ModifiersAt(i - 1) !=
809 aUniCharsAndModifiers.ModifiersAt(i)) {
810 result.AppendLiteral(" [");
811 result.Append(GetModifiersName(aUniCharsAndModifiers.ModifiersAt(0)));
812 result.AppendLiteral("]");
814 result.AppendLiteral(", ");
815 result.Append(GetCharacterCodeName(aUniCharsAndModifiers.CharAt(i)));
817 result.AppendLiteral(" [");
818 uint32_t lastIndex = aUniCharsAndModifiers.Length() - 1;
819 result.Append(GetModifiersName(aUniCharsAndModifiers.ModifiersAt(lastIndex)));
820 result.AppendLiteral("] }");
821 return result;
824 const nsCString ToString(const ModifierKeyState& aModifierKeyState) {
825 nsCString result;
826 result.AssignLiteral("{ ");
827 result.Append(GetModifiersName(aModifierKeyState.GetModifiers()).get());
828 result.AppendLiteral(" }");
829 return result;
832 // Unique id counter associated with a keydown / keypress events. Used in
833 // identifing keypress events for removal from async event dispatch queue
834 // in metrofx after preventDefault is called on keydown events.
835 static uint32_t sUniqueKeyEventId = 0;
837 /*****************************************************************************
838 * mozilla::widget::ModifierKeyState
839 *****************************************************************************/
841 ModifierKeyState::ModifierKeyState() { Update(); }
843 ModifierKeyState::ModifierKeyState(Modifiers aModifiers)
844 : mModifiers(aModifiers) {
845 MOZ_ASSERT(!(mModifiers & MODIFIER_ALTGRAPH) || (!IsControl() && !IsAlt()),
846 "Neither MODIFIER_CONTROL nor MODIFIER_ALT should be set "
847 "if MODIFIER_ALTGRAPH is set");
850 void ModifierKeyState::Update() {
851 mModifiers = 0;
852 if (IS_VK_DOWN(VK_SHIFT)) {
853 mModifiers |= MODIFIER_SHIFT;
855 // If AltGr key (i.e., VK_RMENU on some keyboard layout) is pressed, only
856 // MODIFIER_ALTGRAPH should be set. Otherwise, i.e., if both Ctrl and Alt
857 // keys are pressed to emulate AltGr key, MODIFIER_CONTROL and MODIFIER_ALT
858 // keys should be set separately.
859 if (KeyboardLayout::GetInstance()->HasAltGr() && IS_VK_DOWN(VK_RMENU)) {
860 mModifiers |= MODIFIER_ALTGRAPH;
861 } else {
862 if (IS_VK_DOWN(VK_CONTROL)) {
863 mModifiers |= MODIFIER_CONTROL;
865 if (IS_VK_DOWN(VK_MENU)) {
866 mModifiers |= MODIFIER_ALT;
869 if (IS_VK_DOWN(VK_LWIN) || IS_VK_DOWN(VK_RWIN)) {
870 mModifiers |= MODIFIER_META;
872 if (::GetKeyState(VK_CAPITAL) & 1) {
873 mModifiers |= MODIFIER_CAPSLOCK;
875 if (::GetKeyState(VK_NUMLOCK) & 1) {
876 mModifiers |= MODIFIER_NUMLOCK;
878 if (::GetKeyState(VK_SCROLL) & 1) {
879 mModifiers |= MODIFIER_SCROLLLOCK;
883 void ModifierKeyState::Unset(Modifiers aRemovingModifiers) {
884 mModifiers &= ~aRemovingModifiers;
887 void ModifierKeyState::Set(Modifiers aAddingModifiers) {
888 mModifiers |= aAddingModifiers;
889 MOZ_ASSERT(!(mModifiers & MODIFIER_ALTGRAPH) || (!IsControl() && !IsAlt()),
890 "Neither MODIFIER_CONTROL nor MODIFIER_ALT should be set "
891 "if MODIFIER_ALTGRAPH is set");
894 void ModifierKeyState::InitInputEvent(WidgetInputEvent& aInputEvent) const {
895 aInputEvent.mModifiers = mModifiers;
897 switch (aInputEvent.mClass) {
898 case eMouseEventClass:
899 case eMouseScrollEventClass:
900 case eWheelEventClass:
901 case eDragEventClass:
902 case eSimpleGestureEventClass:
903 InitMouseEvent(aInputEvent);
904 break;
905 default:
906 break;
910 void ModifierKeyState::InitMouseEvent(WidgetInputEvent& aMouseEvent) const {
911 NS_ASSERTION(aMouseEvent.mClass == eMouseEventClass ||
912 aMouseEvent.mClass == eWheelEventClass ||
913 aMouseEvent.mClass == eDragEventClass ||
914 aMouseEvent.mClass == eSimpleGestureEventClass,
915 "called with non-mouse event");
917 WidgetMouseEventBase& mouseEvent = *aMouseEvent.AsMouseEventBase();
918 mouseEvent.mButtons = 0;
919 if (::GetKeyState(VK_LBUTTON) < 0) {
920 mouseEvent.mButtons |= MouseButtonsFlag::ePrimaryFlag;
922 if (::GetKeyState(VK_RBUTTON) < 0) {
923 mouseEvent.mButtons |= MouseButtonsFlag::eSecondaryFlag;
925 if (::GetKeyState(VK_MBUTTON) < 0) {
926 mouseEvent.mButtons |= MouseButtonsFlag::eMiddleFlag;
928 if (::GetKeyState(VK_XBUTTON1) < 0) {
929 mouseEvent.mButtons |= MouseButtonsFlag::e4thFlag;
931 if (::GetKeyState(VK_XBUTTON2) < 0) {
932 mouseEvent.mButtons |= MouseButtonsFlag::e5thFlag;
936 bool ModifierKeyState::IsShift() const {
937 return (mModifiers & MODIFIER_SHIFT) != 0;
940 bool ModifierKeyState::IsControl() const {
941 return (mModifiers & MODIFIER_CONTROL) != 0;
944 bool ModifierKeyState::IsAlt() const {
945 return (mModifiers & MODIFIER_ALT) != 0;
948 bool ModifierKeyState::IsWin() const {
949 return (mModifiers & MODIFIER_META) != 0;
952 bool ModifierKeyState::MaybeMatchShortcutKey() const {
953 // If Windows key is pressed, even if both Ctrl key and Alt key are pressed,
954 // it's possible to match a shortcut key.
955 if (IsWin()) {
956 return true;
958 // Otherwise, when both Ctrl key and Alt key are pressed, it shouldn't be
959 // a shortcut key for Windows since it means pressing AltGr key on
960 // some keyboard layouts.
961 if (IsControl() ^ IsAlt()) {
962 return true;
964 // If no modifier key is active except a lockable modifier nor Shift key,
965 // the key shouldn't match any shortcut keys (there are Space and
966 // Shift+Space, though, let's ignore these special case...).
967 return false;
970 bool ModifierKeyState::IsCapsLocked() const {
971 return (mModifiers & MODIFIER_CAPSLOCK) != 0;
974 bool ModifierKeyState::IsNumLocked() const {
975 return (mModifiers & MODIFIER_NUMLOCK) != 0;
978 bool ModifierKeyState::IsScrollLocked() const {
979 return (mModifiers & MODIFIER_SCROLLLOCK) != 0;
982 /*****************************************************************************
983 * mozilla::widget::UniCharsAndModifiers
984 *****************************************************************************/
986 void UniCharsAndModifiers::Append(char16_t aUniChar, Modifiers aModifiers) {
987 mChars.Append(aUniChar);
988 mModifiers.AppendElement(aModifiers);
991 void UniCharsAndModifiers::FillModifiers(Modifiers aModifiers) {
992 for (size_t i = 0; i < Length(); i++) {
993 mModifiers[i] = aModifiers;
997 void UniCharsAndModifiers::OverwriteModifiersIfBeginsWith(
998 const UniCharsAndModifiers& aOther) {
999 if (!BeginsWith(aOther)) {
1000 return;
1002 for (size_t i = 0; i < aOther.Length(); ++i) {
1003 mModifiers[i] = aOther.mModifiers[i];
1007 bool UniCharsAndModifiers::UniCharsEqual(
1008 const UniCharsAndModifiers& aOther) const {
1009 return mChars.Equals(aOther.mChars);
1012 bool UniCharsAndModifiers::UniCharsCaseInsensitiveEqual(
1013 const UniCharsAndModifiers& aOther) const {
1014 return mChars.Equals(aOther.mChars, nsCaseInsensitiveStringComparator);
1017 bool UniCharsAndModifiers::BeginsWith(
1018 const UniCharsAndModifiers& aOther) const {
1019 return StringBeginsWith(mChars, aOther.mChars);
1022 UniCharsAndModifiers& UniCharsAndModifiers::operator+=(
1023 const UniCharsAndModifiers& aOther) {
1024 mChars.Append(aOther.mChars);
1025 mModifiers.AppendElements(aOther.mModifiers);
1026 return *this;
1029 UniCharsAndModifiers UniCharsAndModifiers::operator+(
1030 const UniCharsAndModifiers& aOther) const {
1031 UniCharsAndModifiers result(*this);
1032 result += aOther;
1033 return result;
1036 /*****************************************************************************
1037 * mozilla::widget::VirtualKey
1038 *****************************************************************************/
1040 // static
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;
1048 } else {
1049 if (aModifiers & MODIFIER_CONTROL) {
1050 state |= STATE_CONTROL;
1052 if (aModifiers & MODIFIER_ALT) {
1053 state |= STATE_ALT;
1056 if (aModifiers & MODIFIER_CAPSLOCK) {
1057 state |= STATE_CAPSLOCK;
1059 return state;
1062 // static
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;
1070 } else {
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;
1081 return modifiers;
1084 const DeadKeyTable* VirtualKey::MatchingDeadKeyTable(
1085 const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries) const {
1086 if (!mIsDeadKey) {
1087 return nullptr;
1090 for (ShiftState shiftState = 0; shiftState < 16; shiftState++) {
1091 if (!IsDeadKey(shiftState)) {
1092 continue;
1094 const DeadKeyTable* dkt = mShiftStates[shiftState].DeadKey.Table;
1095 if (dkt && dkt->IsEqual(aDeadKeyArray, aEntries)) {
1096 return dkt;
1100 return nullptr;
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
1136 // for the key.
1137 return result;
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));
1146 return result;
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);
1161 return result;
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);
1176 return result;
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);
1186 return result;
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);
1194 return result;
1197 // static
1198 void VirtualKey::FillKbdState(PBYTE aKbdState, const ShiftState aShiftState) {
1199 if (aShiftState & STATE_SHIFT) {
1200 aKbdState[VK_SHIFT] |= 0x80;
1201 } else {
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;
1214 } else {
1215 if (aShiftState & STATE_CONTROL) {
1216 aKbdState[VK_CONTROL] |= 0x80;
1217 } else {
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;
1225 } else {
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;
1234 } else {
1235 aKbdState[VK_CAPITAL] &= ~0x01;
1239 /*****************************************************************************
1240 * mozilla::widget::NativeKey
1241 *****************************************************************************/
1243 uint8_t NativeKey::sDispatchedKeyOfAppCommand = 0;
1244 NativeKey* NativeKey::sLatestInstance = nullptr;
1245 const MSG NativeKey::sEmptyMSG = {};
1246 MSG NativeKey::sLastKeyOrCharMSG = {};
1247 MSG NativeKey::sLastKeyMSG = {};
1248 char16_t NativeKey::sPendingHighSurrogate = 0;
1250 NativeKey::NativeKey(nsWindow* aWidget, const MSG& aMessage,
1251 const ModifierKeyState& aModKeyState,
1252 HKL aOverrideKeyboardLayout,
1253 nsTArray<FakeCharMsg>* aFakeCharMsgs)
1254 : mLastInstance(sLatestInstance),
1255 mRemovingMsg(sEmptyMSG),
1256 mReceivedMsg(sEmptyMSG),
1257 mWidget(aWidget),
1258 mDispatcher(aWidget->GetTextEventDispatcher()),
1259 mMsg(aMessage),
1260 mFocusedWndBeforeDispatch(::GetFocus()),
1261 mDOMKeyCode(0),
1262 mKeyNameIndex(KEY_NAME_INDEX_Unidentified),
1263 mCodeNameIndex(CODE_NAME_INDEX_UNKNOWN),
1264 mModKeyState(aModKeyState),
1265 mVirtualKeyCode(0),
1266 mOriginalVirtualKeyCode(0),
1267 mShiftedLatinChar(0),
1268 mUnshiftedLatinChar(0),
1269 mScanCode(0),
1270 mIsExtended(false),
1271 mIsRepeat(false),
1272 mIsDeadKey(false),
1273 mIsPrintableKey(false),
1274 mIsSkippableInRemoteProcess(false),
1275 mCharMessageHasGone(false),
1276 mCanIgnoreModifierStateAtKeyPress(true),
1277 mFakeCharMsgs(aFakeCharMsgs && aFakeCharMsgs->Length() ? aFakeCharMsgs
1278 : nullptr) {
1279 MOZ_LOG(gKeyLog, LogLevel::Info,
1280 ("%p NativeKey::NativeKey(aWidget=0x%p { GetWindowHandle()=0x%p }, "
1281 "aMessage=%s, aModKeyState=%s), sLatestInstance=0x%p",
1282 this, aWidget, aWidget->GetWindowHandle(), ToString(aMessage).get(),
1283 ToString(aModKeyState).get(), sLatestInstance));
1285 MOZ_ASSERT(aWidget);
1286 MOZ_ASSERT(mDispatcher);
1287 sLatestInstance = this;
1288 KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
1289 mKeyboardLayout = keyboardLayout->GetLayout();
1290 if (aOverrideKeyboardLayout && mKeyboardLayout != aOverrideKeyboardLayout) {
1291 keyboardLayout->OverrideLayout(aOverrideKeyboardLayout);
1292 mKeyboardLayout = keyboardLayout->GetLayout();
1293 MOZ_ASSERT(mKeyboardLayout == aOverrideKeyboardLayout);
1294 mIsOverridingKeyboardLayout = true;
1295 } else {
1296 mIsOverridingKeyboardLayout = false;
1297 sLastKeyOrCharMSG = aMessage;
1300 if (mMsg.message == WM_APPCOMMAND) {
1301 InitWithAppCommand();
1302 } else {
1303 InitWithKeyOrChar();
1306 MOZ_LOG(gKeyLog, LogLevel::Info,
1307 ("%p NativeKey::NativeKey(), mKeyboardLayout=0x%p, "
1308 "mFocusedWndBeforeDispatch=0x%p, mDOMKeyCode=%s, "
1309 "mKeyNameIndex=%s, mCodeNameIndex=%s, mModKeyState=%s, "
1310 "mVirtualKeyCode=%s, mOriginalVirtualKeyCode=%s, "
1311 "mCommittedCharsAndModifiers=%s, mInputtingStringAndModifiers=%s, "
1312 "mShiftedString=%s, mUnshiftedString=%s, mShiftedLatinChar=%s, "
1313 "mUnshiftedLatinChar=%s, mScanCode=0x%04X, mIsExtended=%s, "
1314 "mIsRepeat=%s, mIsDeadKey=%s, mIsPrintableKey=%s, "
1315 "mIsSkippableInRemoteProcess=%s, mCharMessageHasGone=%s, "
1316 "mIsOverridingKeyboardLayout=%s",
1317 this, mKeyboardLayout, mFocusedWndBeforeDispatch,
1318 GetDOMKeyCodeName(mDOMKeyCode).get(), ToString(mKeyNameIndex).get(),
1319 ToString(mCodeNameIndex).get(), ToString(mModKeyState).get(),
1320 GetVirtualKeyCodeName(mVirtualKeyCode).get(),
1321 GetVirtualKeyCodeName(mOriginalVirtualKeyCode).get(),
1322 ToString(mCommittedCharsAndModifiers).get(),
1323 ToString(mInputtingStringAndModifiers).get(),
1324 ToString(mShiftedString).get(), ToString(mUnshiftedString).get(),
1325 GetCharacterCodeName(mShiftedLatinChar).get(),
1326 GetCharacterCodeName(mUnshiftedLatinChar).get(), mScanCode,
1327 GetBoolName(mIsExtended), GetBoolName(mIsRepeat),
1328 GetBoolName(mIsDeadKey), GetBoolName(mIsPrintableKey),
1329 GetBoolName(mIsSkippableInRemoteProcess),
1330 GetBoolName(mCharMessageHasGone),
1331 GetBoolName(mIsOverridingKeyboardLayout)));
1334 void NativeKey::InitIsSkippableForKeyOrChar(const MSG& aLastKeyMSG) {
1335 mIsSkippableInRemoteProcess = false;
1337 if (!mIsRepeat) {
1338 // If the message is not repeated key message, the event should be always
1339 // handled in remote process even if it's too old.
1340 return;
1343 // Keyboard utilities may send us some generated messages and such messages
1344 // may be marked as "repeated", e.g., SendInput() calls with
1345 // KEYEVENTF_UNICODE but without KEYEVENTF_KEYUP. However, key sequence
1346 // comes from such utilities may be really important. For example, utilities
1347 // may send WM_KEYDOWN for VK_BACK to remove previous character and send
1348 // WM_KEYDOWN for VK_PACKET to insert a composite character. Therefore, we
1349 // should check if current message and previous key message are caused by
1350 // same physical key. If not, the message may be generated by such
1351 // utility.
1352 // XXX With this approach, if VK_BACK messages are generated with known
1353 // scancode, we cannot distinguish whether coming VK_BACK message is
1354 // actually repeated by the auto-repeat feature. Currently, we need
1355 // this hack only for "SinhalaTamil IME" and fortunately, it generates
1356 // VK_BACK messages with odd scancode. So, we don't need to handle
1357 // VK_BACK specially at least for now.
1359 if (mCodeNameIndex == CODE_NAME_INDEX_UNKNOWN) {
1360 // If current event is not caused by physical key operation, it may be
1361 // caused by a keyboard utility. If so, the event shouldn't be ignored by
1362 // BrowserChild since it want to insert the character, delete a character or
1363 // move caret.
1364 return;
1367 if (mOriginalVirtualKeyCode == VK_PACKET) {
1368 // If the message is VK_PACKET, that means that a keyboard utility
1369 // tries to insert a character.
1370 return;
1373 switch (mMsg.message) {
1374 case WM_KEYDOWN:
1375 case WM_SYSKEYDOWN:
1376 case WM_CHAR:
1377 case WM_SYSCHAR:
1378 case WM_DEADCHAR:
1379 case WM_SYSDEADCHAR:
1380 // However, some keyboard layouts may send some keyboard messages with
1381 // activating the bit. If we dispatch repeated keyboard events, they
1382 // may be ignored by BrowserChild due to performance reason. So, we need
1383 // to check if actually a physical key is repeated by the auto-repeat
1384 // feature.
1385 switch (aLastKeyMSG.message) {
1386 case WM_KEYDOWN:
1387 case WM_SYSKEYDOWN:
1388 if (aLastKeyMSG.wParam == VK_PACKET) {
1389 // If the last message was VK_PACKET, that means that a keyboard
1390 // utility tried to insert a character. So, current message is
1391 // not repeated key event of the previous event.
1392 return;
1394 // Let's check whether current message and previous message are
1395 // caused by same physical key.
1396 mIsSkippableInRemoteProcess =
1397 mScanCode == WinUtils::GetScanCode(aLastKeyMSG.lParam) &&
1398 mIsExtended == WinUtils::IsExtendedScanCode(aLastKeyMSG.lParam);
1399 return;
1400 default:
1401 // If previous message is not a keydown, this must not be generated
1402 // by the auto-repeat feature.
1403 return;
1405 case WM_APPCOMMAND:
1406 MOZ_ASSERT_UNREACHABLE(
1407 "WM_APPCOMMAND should be handled in "
1408 "InitWithAppCommand()");
1409 return;
1410 default:
1411 // keyup message shouldn't be repeated by the auto-repeat feature.
1412 return;
1416 void NativeKey::InitWithKeyOrChar() {
1417 MSG lastKeyMSG = sLastKeyMSG;
1418 char16_t pendingHighSurrogate = sPendingHighSurrogate;
1419 mScanCode = WinUtils::GetScanCode(mMsg.lParam);
1420 mIsExtended = WinUtils::IsExtendedScanCode(mMsg.lParam);
1421 switch (mMsg.message) {
1422 case WM_KEYDOWN:
1423 case WM_SYSKEYDOWN:
1424 sPendingHighSurrogate = 0;
1425 [[fallthrough]];
1426 case WM_KEYUP:
1427 case WM_SYSKEYUP: {
1428 // Modify sLastKeyMSG now since retrieving following char messages may
1429 // cause sending another key message if odd tool hooks GetMessage(),
1430 // PeekMessage().
1431 sLastKeyMSG = mMsg;
1433 // Note that we don't need to compute raw virtual keycode here even when
1434 // it's VK_PROCESS (i.e., already handled by IME) because we need to
1435 // export it as DOM_VK_PROCESS and KEY_NAME_INDEX_Process.
1436 mOriginalVirtualKeyCode = static_cast<uint8_t>(mMsg.wParam);
1438 // If the key message is sent from other application like a11y tools, the
1439 // scancode value might not be set proper value. Then, probably the value
1440 // is 0.
1441 // NOTE: If the virtual keycode can be caused by both non-extended key
1442 // and extended key, the API returns the non-extended key's
1443 // scancode. E.g., VK_LEFT causes "4" key on numpad.
1444 if (!mScanCode && mOriginalVirtualKeyCode != VK_PACKET) {
1445 uint16_t scanCodeEx = ComputeScanCodeExFromVirtualKeyCode(mMsg.wParam);
1446 if (scanCodeEx) {
1447 mScanCode = static_cast<uint8_t>(scanCodeEx & 0xFF);
1448 uint8_t extended = static_cast<uint8_t>((scanCodeEx & 0xFF00) >> 8);
1449 mIsExtended = (extended == 0xE0) || (extended == 0xE1);
1453 // Most keys are not distinguished as left or right keys.
1454 bool isLeftRightDistinguishedKey = false;
1456 // mOriginalVirtualKeyCode must not distinguish left or right of
1457 // Shift, Control or Alt.
1458 switch (mOriginalVirtualKeyCode) {
1459 case VK_SHIFT:
1460 case VK_CONTROL:
1461 case VK_MENU:
1462 isLeftRightDistinguishedKey = true;
1463 break;
1464 case VK_LSHIFT:
1465 case VK_RSHIFT:
1466 mVirtualKeyCode = mOriginalVirtualKeyCode;
1467 mOriginalVirtualKeyCode = VK_SHIFT;
1468 isLeftRightDistinguishedKey = true;
1469 break;
1470 case VK_LCONTROL:
1471 case VK_RCONTROL:
1472 mVirtualKeyCode = mOriginalVirtualKeyCode;
1473 mOriginalVirtualKeyCode = VK_CONTROL;
1474 isLeftRightDistinguishedKey = true;
1475 break;
1476 case VK_LMENU:
1477 case VK_RMENU:
1478 mVirtualKeyCode = mOriginalVirtualKeyCode;
1479 mOriginalVirtualKeyCode = VK_MENU;
1480 isLeftRightDistinguishedKey = true;
1481 break;
1484 // If virtual keycode (left-right distinguished keycode) is already
1485 // computed, we don't need to do anymore.
1486 if (mVirtualKeyCode) {
1487 break;
1490 // If the keycode doesn't have LR distinguished keycode, we just set
1491 // mOriginalVirtualKeyCode to mVirtualKeyCode. Note that don't compute
1492 // it from MapVirtualKeyEx() because the scan code might be wrong if
1493 // the message is sent/posted by other application. Then, we will compute
1494 // unexpected keycode from the scan code.
1495 if (!isLeftRightDistinguishedKey) {
1496 break;
1499 NS_ASSERTION(!mVirtualKeyCode,
1500 "mVirtualKeyCode has been computed already");
1502 // Otherwise, compute the virtual keycode with MapVirtualKeyEx().
1503 mVirtualKeyCode = ComputeVirtualKeyCodeFromScanCodeEx();
1505 // Following code shouldn't be used now because we compute scancode value
1506 // if we detect that the sender doesn't set proper scancode.
1507 // However, the detection might fail. Therefore, let's keep using this.
1508 switch (mOriginalVirtualKeyCode) {
1509 case VK_CONTROL:
1510 if (mVirtualKeyCode != VK_LCONTROL &&
1511 mVirtualKeyCode != VK_RCONTROL) {
1512 mVirtualKeyCode = mIsExtended ? VK_RCONTROL : VK_LCONTROL;
1514 break;
1515 case VK_MENU:
1516 if (mVirtualKeyCode != VK_LMENU && mVirtualKeyCode != VK_RMENU) {
1517 mVirtualKeyCode = mIsExtended ? VK_RMENU : VK_LMENU;
1519 break;
1520 case VK_SHIFT:
1521 if (mVirtualKeyCode != VK_LSHIFT && mVirtualKeyCode != VK_RSHIFT) {
1522 // Neither left shift nor right shift is an extended key,
1523 // let's use VK_LSHIFT for unknown mapping.
1524 mVirtualKeyCode = VK_LSHIFT;
1526 break;
1527 default:
1528 MOZ_CRASH("Unsupported mOriginalVirtualKeyCode");
1530 break;
1532 case WM_CHAR:
1533 case WM_UNICHAR:
1534 case WM_SYSCHAR:
1535 sPendingHighSurrogate = 0;
1536 // If there is another instance and it is trying to remove a char message
1537 // from the queue, this message should be handled in the old instance.
1538 if (IsAnotherInstanceRemovingCharMessage()) {
1539 // XXX Do we need to make mReceivedMsg an array?
1540 MOZ_ASSERT(IsEmptyMSG(mLastInstance->mReceivedMsg));
1541 MOZ_LOG(
1542 gKeyLog, LogLevel::Warning,
1543 ("%p NativeKey::InitWithKeyOrChar(), WARNING, detecting another "
1544 "instance is trying to remove a char message, so, this instance "
1545 "should do nothing, mLastInstance=0x%p, mRemovingMsg=%s, "
1546 "mReceivedMsg=%s",
1547 this, mLastInstance, ToString(mLastInstance->mRemovingMsg).get(),
1548 ToString(mLastInstance->mReceivedMsg).get()));
1549 mLastInstance->mReceivedMsg = mMsg;
1550 return;
1553 // NOTE: If other applications like a11y tools sends WM_*CHAR without
1554 // scancode, we cannot compute virtual keycode. I.e., with such
1555 // applications, we cannot generate proper KeyboardEvent.code value.
1557 mVirtualKeyCode = mOriginalVirtualKeyCode =
1558 ComputeVirtualKeyCodeFromScanCodeEx();
1559 NS_ASSERTION(mVirtualKeyCode, "Failed to compute virtual keycode");
1560 break;
1561 default: {
1562 MOZ_CRASH_UNSAFE_PRINTF("Unsupported message: 0x%04X", mMsg.message);
1563 break;
1567 if (!mVirtualKeyCode) {
1568 mVirtualKeyCode = mOriginalVirtualKeyCode;
1571 KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
1572 mDOMKeyCode =
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.
1578 mKeyNameIndex =
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
1589 // WM_CHAR message.
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()) {
1598 MSG charMsg;
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) {
1603 continue;
1605 MOZ_LOG(gKeyLog, LogLevel::Info,
1606 ("%p NativeKey::InitWithKeyOrChar(), removed char message, %s",
1607 this, ToString(charMsg).get()));
1608 Unused << NS_WARN_IF(charMsg.hwnd != mMsg.hwnd);
1609 mFollowingCharMsgs.AppendElement(charMsg);
1611 if (mFollowingCharMsgs.Length() == 1) {
1612 // If we receive a keydown message for a high-surrogate, a low-surrogate
1613 // keydown message **will** and should follow it. We cannot translate the
1614 // following WM_KEYDOWN message for the low-surrogate right now since
1615 // it's not yet queued into the message queue yet. Therefore, we need to
1616 // wait next one to dispatch keypress event with setting its `.key` value
1617 // to a surrogate pair rather than setting it to a lone surrogate.
1618 // FYI: This may happen with typing a non-BMP character on the touch
1619 // keyboard on Windows 10 or later except when an IME is installed. (If
1620 // IME is installed, composition is used instead.)
1621 if (IS_HIGH_SURROGATE(mFollowingCharMsgs[0].wParam)) {
1622 if (pendingHighSurrogate) {
1623 MOZ_LOG(gKeyLog, LogLevel::Warning,
1624 ("%p NativeKey::InitWithKeyOrChar(), there is pending "
1625 "high surrogate input, but received another high surrogate "
1626 "input. The previous one is discarded",
1627 this));
1629 sPendingHighSurrogate = mFollowingCharMsgs[0].wParam;
1630 mFollowingCharMsgs.Clear();
1631 } else if (IS_LOW_SURROGATE(mFollowingCharMsgs[0].wParam)) {
1632 // If we stopped dispathing a keypress event for a preceding
1633 // high-surrogate, treat this keydown (for a low-surrogate) as
1634 // introducing both the high surrogate and the low surrogate.
1635 if (pendingHighSurrogate) {
1636 MSG charMsg = mFollowingCharMsgs[0];
1637 mFollowingCharMsgs[0].wParam = pendingHighSurrogate;
1638 mFollowingCharMsgs.AppendElement(std::move(charMsg));
1639 } else {
1640 MOZ_LOG(
1641 gKeyLog, LogLevel::Warning,
1642 ("%p NativeKey::InitWithKeyOrChar(), there is no pending high "
1643 "surrogate input, but received lone low surrogate input",
1644 this));
1646 } else {
1647 MOZ_LOG(gKeyLog, LogLevel::Warning,
1648 ("%p NativeKey::InitWithKeyOrChar(), there is pending "
1649 "high surrogate input, but received non-surrogate input. "
1650 "The high surrogate input is discarded",
1651 this));
1653 } else {
1654 MOZ_LOG(gKeyLog, LogLevel::Warning,
1655 ("%p NativeKey::InitWithKeyOrChar(), there is pending "
1656 "high surrogate input, but received 2 or more character input. "
1657 "The high surrogate input is discarded",
1658 this));
1662 keyboardLayout->InitNativeKey(*this);
1664 // Now, we can know if the key produces character(s) or a dead key with
1665 // AltGraph modifier. When user emulates AltGr key press with pressing
1666 // both Ctrl and Alt and the key produces character(s) or a dead key, we
1667 // need to replace Control and Alt state with AltGraph if the keyboard
1668 // layout has AltGr key.
1669 // Note that if Ctrl and/or Alt are pressed (not to emulate to press AltGr),
1670 // we need to set actual modifiers to eKeyDown and eKeyUp.
1671 if (MaybeEmulatingAltGraph() &&
1672 (mCommittedCharsAndModifiers.IsProducingCharsWithAltGr() ||
1673 mKeyNameIndex == KEY_NAME_INDEX_Dead)) {
1674 mModKeyState.Unset(MODIFIER_CONTROL | MODIFIER_ALT);
1675 mModKeyState.Set(MODIFIER_ALTGRAPH);
1678 mIsDeadKey =
1679 (IsFollowedByDeadCharMessage() ||
1680 keyboardLayout->IsDeadKey(mOriginalVirtualKeyCode, mModKeyState));
1681 mIsPrintableKey = mKeyNameIndex == KEY_NAME_INDEX_USE_STRING ||
1682 KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode);
1683 // The repeat count in mMsg.lParam isn't useful to check whether the event
1684 // is caused by the auto-repeat feature because it's not incremented even
1685 // if it's repeated twice or more (i.e., always 1). Therefore, we need to
1686 // check previous key state (31th bit) instead. If it's 1, the key was down
1687 // before the message was sent.
1688 mIsRepeat = (mMsg.lParam & (1 << 30)) != 0;
1689 InitIsSkippableForKeyOrChar(lastKeyMSG);
1691 if (IsKeyDownMessage()) {
1692 // Compute some strings which may be inputted by the key with various
1693 // modifier state if this key event won't cause text input actually.
1694 // They will be used for setting mAlternativeCharCodes in the callback
1695 // method which will be called by TextEventDispatcher.
1696 if (!IsFollowedByPrintableCharMessage()) {
1697 ComputeInputtingStringWithKeyboardLayout();
1699 // Remove odd char messages if there are.
1700 RemoveFollowingOddCharMessages();
1704 void NativeKey::InitCommittedCharsAndModifiersWithFollowingCharMessages() {
1705 mCommittedCharsAndModifiers.Clear();
1706 // This should cause inputting text in focused editor. However, it
1707 // ignores keypress events whose altKey or ctrlKey is true.
1708 // Therefore, we need to remove these modifier state here.
1709 Modifiers modifiers = mModKeyState.GetModifiers();
1710 if (IsFollowedByPrintableCharMessage()) {
1711 modifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
1712 if (MaybeEmulatingAltGraph()) {
1713 modifiers |= MODIFIER_ALTGRAPH;
1716 // NOTE: This method assumes that WM_CHAR and WM_SYSCHAR are never retrieved
1717 // at same time.
1718 for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
1719 // Ignore non-printable char messages.
1720 if (!IsPrintableCharOrSysCharMessage(mFollowingCharMsgs[i])) {
1721 continue;
1723 char16_t ch = static_cast<char16_t>(mFollowingCharMsgs[i].wParam);
1724 mCommittedCharsAndModifiers.Append(ch, modifiers);
1728 NativeKey::~NativeKey() {
1729 MOZ_LOG(gKeyLog, LogLevel::Debug,
1730 ("%p NativeKey::~NativeKey(), destroyed", this));
1731 if (mIsOverridingKeyboardLayout) {
1732 KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
1733 keyboardLayout->RestoreLayout();
1735 sLatestInstance = mLastInstance;
1738 void NativeKey::InitWithAppCommand() {
1739 if (GET_DEVICE_LPARAM(mMsg.lParam) != FAPPCOMMAND_KEY) {
1740 return;
1743 uint32_t appCommand = GET_APPCOMMAND_LPARAM(mMsg.lParam);
1744 switch (GET_APPCOMMAND_LPARAM(mMsg.lParam)) {
1745 #undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX
1746 #define NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX(aAppCommand, aKeyNameIndex) \
1747 case aAppCommand: \
1748 mKeyNameIndex = aKeyNameIndex; \
1749 break;
1751 #include "NativeKeyToDOMKeyName.h"
1753 #undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX
1755 default:
1756 mKeyNameIndex = KEY_NAME_INDEX_Unidentified;
1759 // Guess the virtual keycode which caused this message.
1760 switch (appCommand) {
1761 case APPCOMMAND_BROWSER_BACKWARD:
1762 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_BACK;
1763 break;
1764 case APPCOMMAND_BROWSER_FORWARD:
1765 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_FORWARD;
1766 break;
1767 case APPCOMMAND_BROWSER_REFRESH:
1768 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_REFRESH;
1769 break;
1770 case APPCOMMAND_BROWSER_STOP:
1771 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_STOP;
1772 break;
1773 case APPCOMMAND_BROWSER_SEARCH:
1774 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_SEARCH;
1775 break;
1776 case APPCOMMAND_BROWSER_FAVORITES:
1777 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_FAVORITES;
1778 break;
1779 case APPCOMMAND_BROWSER_HOME:
1780 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_HOME;
1781 break;
1782 case APPCOMMAND_VOLUME_MUTE:
1783 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_VOLUME_MUTE;
1784 break;
1785 case APPCOMMAND_VOLUME_DOWN:
1786 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_VOLUME_DOWN;
1787 break;
1788 case APPCOMMAND_VOLUME_UP:
1789 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_VOLUME_UP;
1790 break;
1791 case APPCOMMAND_MEDIA_NEXTTRACK:
1792 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_NEXT_TRACK;
1793 break;
1794 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
1795 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_PREV_TRACK;
1796 break;
1797 case APPCOMMAND_MEDIA_STOP:
1798 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_STOP;
1799 break;
1800 case APPCOMMAND_MEDIA_PLAY_PAUSE:
1801 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_PLAY_PAUSE;
1802 break;
1803 case APPCOMMAND_LAUNCH_MAIL:
1804 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_MAIL;
1805 break;
1806 case APPCOMMAND_LAUNCH_MEDIA_SELECT:
1807 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_MEDIA_SELECT;
1808 break;
1809 case APPCOMMAND_LAUNCH_APP1:
1810 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_APP1;
1811 break;
1812 case APPCOMMAND_LAUNCH_APP2:
1813 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_APP2;
1814 break;
1815 default:
1816 return;
1819 uint16_t scanCodeEx = ComputeScanCodeExFromVirtualKeyCode(mVirtualKeyCode);
1820 mScanCode = static_cast<uint8_t>(scanCodeEx & 0xFF);
1821 uint8_t extended = static_cast<uint8_t>((scanCodeEx & 0xFF00) >> 8);
1822 mIsExtended = (extended == 0xE0) || (extended == 0xE1);
1823 mDOMKeyCode = KeyboardLayout::GetInstance()->ConvertNativeKeyCodeToDOMKeyCode(
1824 mOriginalVirtualKeyCode);
1825 mCodeNameIndex = KeyboardLayout::ConvertScanCodeToCodeNameIndex(
1826 GetScanCodeWithExtendedFlag());
1827 // If we can map the WM_APPCOMMAND to a virtual keycode, we can trust
1828 // the result of GetKeyboardState(). Otherwise, we dispatch both
1829 // keydown and keyup events from WM_APPCOMMAND handler. Therefore,
1830 // even if WM_APPCOMMAND is caused by auto key repeat, web apps receive
1831 // a pair of DOM keydown and keyup events. I.e., KeyboardEvent.repeat
1832 // should be never true of such keys.
1833 // XXX Isn't the key state always true? If the key press caused this
1834 // WM_APPCOMMAND, that means it's pressed at that time.
1835 if (mVirtualKeyCode) {
1836 BYTE kbdState[256];
1837 memset(kbdState, 0, sizeof(kbdState));
1838 ::GetKeyboardState(kbdState);
1839 mIsSkippableInRemoteProcess = mIsRepeat = !!kbdState[mVirtualKeyCode];
1843 bool NativeKey::MaybeEmulatingAltGraph() const {
1844 return IsControl() && IsAlt() && KeyboardLayout::GetInstance()->HasAltGr();
1847 // static
1848 bool NativeKey::IsControlChar(char16_t aChar) {
1849 static const char16_t U_SPACE = 0x20;
1850 static const char16_t U_DELETE = 0x7F;
1851 return aChar < U_SPACE || aChar == U_DELETE;
1854 bool NativeKey::IsFollowedByDeadCharMessage() const {
1855 if (mFollowingCharMsgs.IsEmpty()) {
1856 return false;
1858 return IsDeadCharMessage(mFollowingCharMsgs[0]);
1861 bool NativeKey::IsFollowedByPrintableCharMessage() const {
1862 for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
1863 if (IsPrintableCharMessage(mFollowingCharMsgs[i])) {
1864 return true;
1867 return false;
1870 bool NativeKey::IsFollowedByPrintableCharOrSysCharMessage() const {
1871 for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
1872 if (IsPrintableCharOrSysCharMessage(mFollowingCharMsgs[i])) {
1873 return true;
1876 return false;
1879 bool NativeKey::IsReservedBySystem() const {
1880 // Alt+Space key is handled by OS, we shouldn't touch it.
1881 if (mModKeyState.IsAlt() && !mModKeyState.IsControl() &&
1882 mVirtualKeyCode == VK_SPACE) {
1883 return true;
1886 // XXX How about Alt+F4? We receive WM_SYSKEYDOWN for F4 before closing the
1887 // window. Although, we don't prevent to close the window but the key
1888 // event shouldn't be exposed to the web.
1890 return false;
1893 bool NativeKey::IsIMEDoingKakuteiUndo() const {
1894 // Following message pattern is caused by "Kakutei-Undo" of ATOK or WXG:
1895 // ---------------------------------------------------------------------------
1896 // WM_KEYDOWN * n (wParam = VK_BACK, lParam = 0x1)
1897 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC0000001) # ATOK
1898 // WM_IME_STARTCOMPOSITION * 1 (wParam = 0x0, lParam = 0x0)
1899 // WM_IME_COMPOSITION * 1 (wParam = 0x0, lParam = 0x1BF)
1900 // WM_CHAR * n (wParam = VK_BACK, lParam = 0x1)
1901 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC00E0001)
1902 // ---------------------------------------------------------------------------
1903 // This doesn't match usual key message pattern such as:
1904 // WM_KEYDOWN -> WM_CHAR -> WM_KEYDOWN -> WM_CHAR -> ... -> WM_KEYUP
1905 // See following bugs for the detail.
1906 // https://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese)
1907 // https://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English)
1908 MSG startCompositionMsg, compositionMsg, charMsg;
1909 return WinUtils::PeekMessage(&startCompositionMsg, mMsg.hwnd,
1910 WM_IME_STARTCOMPOSITION, WM_IME_STARTCOMPOSITION,
1911 PM_NOREMOVE | PM_NOYIELD) &&
1912 WinUtils::PeekMessage(&compositionMsg, mMsg.hwnd, WM_IME_COMPOSITION,
1913 WM_IME_COMPOSITION, PM_NOREMOVE | PM_NOYIELD) &&
1914 WinUtils::PeekMessage(&charMsg, mMsg.hwnd, WM_CHAR, WM_CHAR,
1915 PM_NOREMOVE | PM_NOYIELD) &&
1916 startCompositionMsg.wParam == 0x0 &&
1917 startCompositionMsg.lParam == 0x0 && compositionMsg.wParam == 0x0 &&
1918 compositionMsg.lParam == 0x1BF && charMsg.wParam == VK_BACK &&
1919 charMsg.lParam == 0x1 &&
1920 startCompositionMsg.time <= compositionMsg.time &&
1921 compositionMsg.time <= charMsg.time;
1924 void NativeKey::RemoveFollowingOddCharMessages() {
1925 MOZ_ASSERT(IsKeyDownMessage());
1927 // If the keydown message is synthesized for automated tests, there is
1928 // nothing to do here.
1929 if (mFakeCharMsgs) {
1930 return;
1933 // If there are some following char messages before another key message,
1934 // there is nothing to do here.
1935 if (!mFollowingCharMsgs.IsEmpty()) {
1936 return;
1939 // If the handling key isn't Backspace, there is nothing to do here.
1940 if (mOriginalVirtualKeyCode != VK_BACK) {
1941 return;
1944 // If we don't see the odd message pattern, there is nothing to do here.
1945 if (!IsIMEDoingKakuteiUndo()) {
1946 return;
1949 // Otherwise, we need to remove odd WM_CHAR messages for ATOK or WXG (both
1950 // of them are Japanese IME).
1951 MSG msg;
1952 while (WinUtils::PeekMessage(&msg, mMsg.hwnd, WM_CHAR, WM_CHAR,
1953 PM_REMOVE | PM_NOYIELD)) {
1954 if (msg.message != WM_CHAR) {
1955 MOZ_RELEASE_ASSERT(msg.message == WM_NULL,
1956 "Unexpected message was removed");
1957 continue;
1959 MOZ_LOG(
1960 gKeyLog, LogLevel::Info,
1961 ("%p NativeKey::RemoveFollowingOddCharMessages(), removed odd char "
1962 "message, %s",
1963 this, ToString(msg).get()));
1964 mRemovedOddCharMsgs.AppendElement(msg);
1968 UINT NativeKey::GetScanCodeWithExtendedFlag() const {
1969 if (!mIsExtended) {
1970 return mScanCode;
1972 return (0xE000 | mScanCode);
1975 uint32_t NativeKey::GetKeyLocation() const {
1976 switch (mVirtualKeyCode) {
1977 case VK_LSHIFT:
1978 case VK_LCONTROL:
1979 case VK_LMENU:
1980 case VK_LWIN:
1981 return eKeyLocationLeft;
1983 case VK_RSHIFT:
1984 case VK_RCONTROL:
1985 case VK_RMENU:
1986 case VK_RWIN:
1987 return eKeyLocationRight;
1989 case VK_RETURN:
1990 // XXX This code assumes that all keyboard drivers use same mapping.
1991 return !mIsExtended ? eKeyLocationStandard : eKeyLocationNumpad;
1993 case VK_INSERT:
1994 case VK_DELETE:
1995 case VK_END:
1996 case VK_DOWN:
1997 case VK_NEXT:
1998 case VK_LEFT:
1999 case VK_CLEAR:
2000 case VK_RIGHT:
2001 case VK_HOME:
2002 case VK_UP:
2003 case VK_PRIOR:
2004 // XXX This code assumes that all keyboard drivers use same mapping.
2005 return mIsExtended ? eKeyLocationStandard : eKeyLocationNumpad;
2007 // NumLock key isn't included due to IE9's behavior.
2008 case VK_NUMPAD0:
2009 case VK_NUMPAD1:
2010 case VK_NUMPAD2:
2011 case VK_NUMPAD3:
2012 case VK_NUMPAD4:
2013 case VK_NUMPAD5:
2014 case VK_NUMPAD6:
2015 case VK_NUMPAD7:
2016 case VK_NUMPAD8:
2017 case VK_NUMPAD9:
2018 case VK_DECIMAL:
2019 case VK_DIVIDE:
2020 case VK_MULTIPLY:
2021 case VK_SUBTRACT:
2022 case VK_ADD:
2023 // Separator key of Brazilian keyboard or JIS keyboard for Mac
2024 case VK_ABNT_C2:
2025 return eKeyLocationNumpad;
2027 case VK_SHIFT:
2028 case VK_CONTROL:
2029 case VK_MENU:
2030 NS_WARNING("Failed to decide the key location?");
2031 [[fallthrough]];
2033 default:
2034 return eKeyLocationStandard;
2038 uint8_t NativeKey::ComputeVirtualKeyCodeFromScanCode() const {
2039 return static_cast<uint8_t>(
2040 ::MapVirtualKeyEx(mScanCode, MAPVK_VSC_TO_VK, mKeyboardLayout));
2043 uint8_t NativeKey::ComputeVirtualKeyCodeFromScanCodeEx() const {
2044 // MapVirtualKeyEx() has been improved for supporting extended keys since
2045 // Vista. When we call it for mapping a scancode of an extended key and
2046 // a virtual keycode, we need to add 0xE000 to the scancode.
2047 return static_cast<uint8_t>(::MapVirtualKeyEx(
2048 GetScanCodeWithExtendedFlag(), MAPVK_VSC_TO_VK_EX, mKeyboardLayout));
2051 uint16_t NativeKey::ComputeScanCodeExFromVirtualKeyCode(
2052 UINT aVirtualKeyCode) const {
2053 return static_cast<uint16_t>(
2054 ::MapVirtualKeyEx(aVirtualKeyCode, MAPVK_VK_TO_VSC_EX, mKeyboardLayout));
2057 char16_t NativeKey::ComputeUnicharFromScanCode() const {
2058 return static_cast<char16_t>(::MapVirtualKeyEx(
2059 ComputeVirtualKeyCodeFromScanCode(), MAPVK_VK_TO_CHAR, mKeyboardLayout));
2062 nsEventStatus NativeKey::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const {
2063 return InitKeyEvent(aKeyEvent, mModKeyState);
2066 nsEventStatus NativeKey::InitKeyEvent(
2067 WidgetKeyboardEvent& aKeyEvent,
2068 const ModifierKeyState& aModKeyState) const {
2069 if (mWidget->Destroyed()) {
2070 MOZ_CRASH("NativeKey tries to dispatch a key event on destroyed widget");
2073 LayoutDeviceIntPoint point(0, 0);
2074 mWidget->InitEvent(aKeyEvent, &point);
2076 switch (aKeyEvent.mMessage) {
2077 case eKeyDown:
2078 // If it was followed by a char message but it was consumed by somebody,
2079 // we should mark it as consumed because somebody must have handled it
2080 // and we should prevent to do "double action" for the key operation.
2081 // However, for compatibility with older version and other browsers,
2082 // we should dispatch the events even in the web content.
2083 if (mCharMessageHasGone) {
2084 aKeyEvent.PreventDefaultBeforeDispatch(CrossProcessForwarding::eAllow);
2086 aKeyEvent.mKeyCode = mDOMKeyCode;
2087 // Unique id for this keydown event and its associated keypress.
2088 sUniqueKeyEventId++;
2089 aKeyEvent.mUniqueId = sUniqueKeyEventId;
2090 break;
2091 case eKeyUp:
2092 aKeyEvent.mKeyCode = mDOMKeyCode;
2093 // Set defaultPrevented of the key event if the VK_MENU is not a system
2094 // key release, so that the menu bar does not trigger. This helps avoid
2095 // triggering the menu bar for ALT key accelerators used in assistive
2096 // technologies such as Window-Eyes and ZoomText or for switching open
2097 // state of IME. On the other hand, we should dispatch the events even
2098 // in the web content for compatibility with older version and other
2099 // browsers.
2100 if (mOriginalVirtualKeyCode == VK_MENU && mMsg.message != WM_SYSKEYUP) {
2101 aKeyEvent.PreventDefaultBeforeDispatch(CrossProcessForwarding::eAllow);
2103 break;
2104 case eKeyPress:
2105 MOZ_ASSERT(!mCharMessageHasGone,
2106 "If following char message was consumed by somebody, "
2107 "keydown event should be consumed above");
2108 aKeyEvent.mUniqueId = sUniqueKeyEventId;
2109 break;
2110 default:
2111 MOZ_CRASH("Invalid event message");
2114 aKeyEvent.mIsRepeat = mIsRepeat;
2115 aKeyEvent.mMaybeSkippableInRemoteProcess = mIsSkippableInRemoteProcess;
2116 aKeyEvent.mKeyNameIndex = mKeyNameIndex;
2117 if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
2118 aKeyEvent.mKeyValue = mCommittedCharsAndModifiers.ToString();
2120 aKeyEvent.mCodeNameIndex = mCodeNameIndex;
2121 MOZ_ASSERT(mCodeNameIndex != CODE_NAME_INDEX_USE_STRING);
2122 aKeyEvent.mLocation = GetKeyLocation();
2123 aModKeyState.InitInputEvent(aKeyEvent);
2125 KeyboardLayout::NotifyIdleServiceOfUserActivity();
2127 MOZ_LOG(
2128 gKeyLog, LogLevel::Info,
2129 ("%p NativeKey::InitKeyEvent(), initialized, aKeyEvent={ "
2130 "mMessage=%s, mKeyNameIndex=%s, mKeyValue=\"%s\", mCodeNameIndex=%s, "
2131 "mKeyCode=%s, mLocation=%s, mModifiers=%s, DefaultPrevented()=%s }",
2132 this, ToChar(aKeyEvent.mMessage),
2133 ToString(aKeyEvent.mKeyNameIndex).get(),
2134 NS_ConvertUTF16toUTF8(aKeyEvent.mKeyValue).get(),
2135 ToString(aKeyEvent.mCodeNameIndex).get(),
2136 GetDOMKeyCodeName(aKeyEvent.mKeyCode).get(),
2137 GetKeyLocationName(aKeyEvent.mLocation).get(),
2138 GetModifiersName(aKeyEvent.mModifiers).get(),
2139 GetBoolName(aKeyEvent.DefaultPrevented())));
2141 return aKeyEvent.DefaultPrevented() ? nsEventStatus_eConsumeNoDefault
2142 : nsEventStatus_eIgnore;
2145 bool NativeKey::DispatchCommandEvent(uint32_t aEventCommand) const {
2146 RefPtr<nsAtom> command;
2147 switch (aEventCommand) {
2148 case APPCOMMAND_BROWSER_BACKWARD:
2149 command = nsGkAtoms::Back;
2150 break;
2151 case APPCOMMAND_BROWSER_FORWARD:
2152 command = nsGkAtoms::Forward;
2153 break;
2154 case APPCOMMAND_BROWSER_REFRESH:
2155 command = nsGkAtoms::Reload;
2156 break;
2157 case APPCOMMAND_BROWSER_STOP:
2158 command = nsGkAtoms::Stop;
2159 break;
2160 case APPCOMMAND_BROWSER_SEARCH:
2161 command = nsGkAtoms::Search;
2162 break;
2163 case APPCOMMAND_BROWSER_FAVORITES:
2164 command = nsGkAtoms::Bookmarks;
2165 break;
2166 case APPCOMMAND_BROWSER_HOME:
2167 command = nsGkAtoms::Home;
2168 break;
2169 case APPCOMMAND_CLOSE:
2170 command = nsGkAtoms::Close;
2171 break;
2172 case APPCOMMAND_FIND:
2173 command = nsGkAtoms::Find;
2174 break;
2175 case APPCOMMAND_HELP:
2176 command = nsGkAtoms::Help;
2177 break;
2178 case APPCOMMAND_NEW:
2179 command = nsGkAtoms::New;
2180 break;
2181 case APPCOMMAND_OPEN:
2182 command = nsGkAtoms::Open;
2183 break;
2184 case APPCOMMAND_PRINT:
2185 command = nsGkAtoms::Print;
2186 break;
2187 case APPCOMMAND_SAVE:
2188 command = nsGkAtoms::Save;
2189 break;
2190 case APPCOMMAND_FORWARD_MAIL:
2191 command = nsGkAtoms::ForwardMail;
2192 break;
2193 case APPCOMMAND_REPLY_TO_MAIL:
2194 command = nsGkAtoms::ReplyToMail;
2195 break;
2196 case APPCOMMAND_SEND_MAIL:
2197 command = nsGkAtoms::SendMail;
2198 break;
2199 case APPCOMMAND_MEDIA_NEXTTRACK:
2200 command = nsGkAtoms::NextTrack;
2201 break;
2202 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
2203 command = nsGkAtoms::PreviousTrack;
2204 break;
2205 case APPCOMMAND_MEDIA_STOP:
2206 command = nsGkAtoms::MediaStop;
2207 break;
2208 case APPCOMMAND_MEDIA_PLAY_PAUSE:
2209 command = nsGkAtoms::PlayPause;
2210 break;
2211 default:
2212 MOZ_LOG(
2213 gKeyLog, LogLevel::Info,
2214 ("%p NativeKey::DispatchCommandEvent(), doesn't dispatch command "
2215 "event",
2216 this));
2217 return false;
2219 WidgetCommandEvent appCommandEvent(true, command, mWidget);
2221 mWidget->InitEvent(appCommandEvent);
2222 MOZ_LOG(gKeyLog, LogLevel::Info,
2223 ("%p NativeKey::DispatchCommandEvent(), dispatching "
2224 "%s app command event...",
2225 this, nsAtomCString(command).get()));
2226 bool ok =
2227 mWidget->DispatchWindowEvent(appCommandEvent) || mWidget->Destroyed();
2228 MOZ_LOG(
2229 gKeyLog, LogLevel::Info,
2230 ("%p NativeKey::DispatchCommandEvent(), dispatched app command event, "
2231 "result=%s, mWidget->Destroyed()=%s",
2232 this, GetBoolName(ok), GetBoolName(mWidget->Destroyed())));
2233 return ok;
2236 bool NativeKey::HandleAppCommandMessage() const {
2237 // If the widget has gone, we should do nothing.
2238 if (mWidget->Destroyed()) {
2239 MOZ_LOG(gKeyLog, LogLevel::Warning,
2240 ("%p NativeKey::HandleAppCommandMessage(), WARNING, not handled "
2241 "due to "
2242 "destroyed the widget",
2243 this));
2244 return false;
2247 // NOTE: Typical behavior of WM_APPCOMMAND caused by key is, WM_APPCOMMAND
2248 // message is _sent_ first. Then, the DefaultWndProc will _post_
2249 // WM_KEYDOWN message and WM_KEYUP message if the keycode for the
2250 // command is available (i.e., mVirtualKeyCode is not 0).
2252 // NOTE: IntelliType (Microsoft's keyboard utility software) always consumes
2253 // WM_KEYDOWN and WM_KEYUP.
2255 // Let's dispatch keydown message before our chrome handles the command
2256 // when the message is caused by a keypress. This behavior makes handling
2257 // WM_APPCOMMAND be a default action of the keydown event. This means that
2258 // web applications can handle multimedia keys and prevent our default action.
2259 // This allow web applications to provide better UX for multimedia keyboard
2260 // users.
2261 bool dispatchKeyEvent = (GET_DEVICE_LPARAM(mMsg.lParam) == FAPPCOMMAND_KEY);
2262 if (dispatchKeyEvent) {
2263 // If a plug-in window has focus but it didn't consume the message, our
2264 // window receive WM_APPCOMMAND message. In this case, we shouldn't
2265 // dispatch KeyboardEvents because an event handler may access the
2266 // plug-in process synchronously.
2267 dispatchKeyEvent =
2268 WinUtils::IsOurProcessWindow(reinterpret_cast<HWND>(mMsg.wParam));
2271 bool consumed = false;
2273 if (dispatchKeyEvent) {
2274 nsresult rv = mDispatcher->BeginNativeInputTransaction();
2275 if (NS_WARN_IF(NS_FAILED(rv))) {
2276 MOZ_LOG(gKeyLog, LogLevel::Error,
2277 ("%p NativeKey::HandleAppCommandMessage(), FAILED due to "
2278 "BeginNativeInputTransaction() failure",
2279 this));
2280 return true;
2282 MOZ_LOG(gKeyLog, LogLevel::Info,
2283 ("%p NativeKey::HandleAppCommandMessage(), initializing keydown "
2284 "event...",
2285 this));
2286 WidgetKeyboardEvent keydownEvent(true, eKeyDown, mWidget);
2287 nsEventStatus status = InitKeyEvent(keydownEvent, mModKeyState);
2288 MOZ_LOG(gKeyLog, LogLevel::Info,
2289 ("%p NativeKey::HandleAppCommandMessage(), tries to dispatch "
2290 "keydown event...",
2291 this));
2292 // NOTE: If the keydown event is consumed by web contents, we shouldn't
2293 // continue to handle the command.
2294 if (!mDispatcher->DispatchKeyboardEvent(eKeyDown, keydownEvent, status,
2295 const_cast<NativeKey*>(this))) {
2296 MOZ_LOG(gKeyLog, LogLevel::Info,
2297 ("%p NativeKey::HandleAppCommandMessage(), keydown event isn't "
2298 "dispatched",
2299 this));
2300 // If keyboard event wasn't fired, there must be composition.
2301 // So, we don't need to dispatch a command event.
2302 return true;
2304 consumed = status == nsEventStatus_eConsumeNoDefault;
2305 MOZ_LOG(gKeyLog, LogLevel::Info,
2306 ("%p NativeKey::HandleAppCommandMessage(), keydown event was "
2307 "dispatched, consumed=%s",
2308 this, GetBoolName(consumed)));
2309 sDispatchedKeyOfAppCommand = mVirtualKeyCode;
2310 if (mWidget->Destroyed()) {
2311 MOZ_LOG(
2312 gKeyLog, LogLevel::Info,
2313 ("%p NativeKey::HandleAppCommandMessage(), keydown event caused "
2314 "destroying the widget",
2315 this));
2316 return true;
2320 // Dispatch a command event or a content command event if the command is
2321 // supported.
2322 if (!consumed) {
2323 uint32_t appCommand = GET_APPCOMMAND_LPARAM(mMsg.lParam);
2324 EventMessage contentCommandMessage = eVoidEvent;
2325 switch (appCommand) {
2326 case APPCOMMAND_BROWSER_BACKWARD:
2327 case APPCOMMAND_BROWSER_FORWARD:
2328 case APPCOMMAND_BROWSER_REFRESH:
2329 case APPCOMMAND_BROWSER_STOP:
2330 case APPCOMMAND_BROWSER_SEARCH:
2331 case APPCOMMAND_BROWSER_FAVORITES:
2332 case APPCOMMAND_BROWSER_HOME:
2333 case APPCOMMAND_CLOSE:
2334 case APPCOMMAND_FIND:
2335 case APPCOMMAND_HELP:
2336 case APPCOMMAND_NEW:
2337 case APPCOMMAND_OPEN:
2338 case APPCOMMAND_PRINT:
2339 case APPCOMMAND_SAVE:
2340 case APPCOMMAND_FORWARD_MAIL:
2341 case APPCOMMAND_REPLY_TO_MAIL:
2342 case APPCOMMAND_SEND_MAIL:
2343 case APPCOMMAND_MEDIA_NEXTTRACK:
2344 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
2345 case APPCOMMAND_MEDIA_STOP:
2346 case APPCOMMAND_MEDIA_PLAY_PAUSE:
2347 // We shouldn't consume the message always because if we don't handle
2348 // the message, the sender (typically, utility of keyboard or mouse)
2349 // may send other key messages which indicate well known shortcut key.
2350 consumed = DispatchCommandEvent(appCommand);
2351 break;
2353 // Use content command for following commands:
2354 case APPCOMMAND_COPY:
2355 contentCommandMessage = eContentCommandCopy;
2356 break;
2357 case APPCOMMAND_CUT:
2358 contentCommandMessage = eContentCommandCut;
2359 break;
2360 case APPCOMMAND_PASTE:
2361 contentCommandMessage = eContentCommandPaste;
2362 break;
2363 case APPCOMMAND_REDO:
2364 contentCommandMessage = eContentCommandRedo;
2365 break;
2366 case APPCOMMAND_UNDO:
2367 contentCommandMessage = eContentCommandUndo;
2368 break;
2371 if (contentCommandMessage) {
2372 MOZ_ASSERT(!mWidget->Destroyed());
2373 WidgetContentCommandEvent contentCommandEvent(true, contentCommandMessage,
2374 mWidget);
2375 MOZ_LOG(
2376 gKeyLog, LogLevel::Info,
2377 ("%p NativeKey::HandleAppCommandMessage(), dispatching %s event...",
2378 this, ToChar(contentCommandMessage)));
2379 mWidget->DispatchWindowEvent(contentCommandEvent);
2380 MOZ_LOG(gKeyLog, LogLevel::Info,
2381 ("%p NativeKey::HandleAppCommandMessage(), dispatched %s event",
2382 this, ToChar(contentCommandMessage)));
2383 consumed = true;
2385 if (mWidget->Destroyed()) {
2386 MOZ_LOG(gKeyLog, LogLevel::Info,
2387 ("%p NativeKey::HandleAppCommandMessage(), %s event caused "
2388 "destroying the widget",
2389 this, ToChar(contentCommandMessage)));
2390 return true;
2392 } else {
2393 MOZ_LOG(gKeyLog, LogLevel::Info,
2394 ("%p NativeKey::HandleAppCommandMessage(), doesn't dispatch "
2395 "content "
2396 "command event",
2397 this));
2401 // Dispatch a keyup event if the command is caused by pressing a key and
2402 // the key isn't mapped to a virtual keycode.
2403 if (dispatchKeyEvent && !mVirtualKeyCode) {
2404 MOZ_ASSERT(!mWidget->Destroyed());
2405 nsresult rv = mDispatcher->BeginNativeInputTransaction();
2406 if (NS_WARN_IF(NS_FAILED(rv))) {
2407 MOZ_LOG(gKeyLog, LogLevel::Error,
2408 ("%p NativeKey::HandleAppCommandMessage(), FAILED due to "
2409 "BeginNativeInputTransaction() failure",
2410 this));
2411 return true;
2413 MOZ_LOG(gKeyLog, LogLevel::Info,
2414 ("%p NativeKey::HandleAppCommandMessage(), initializing keyup "
2415 "event...",
2416 this));
2417 WidgetKeyboardEvent keyupEvent(true, eKeyUp, mWidget);
2418 nsEventStatus status = InitKeyEvent(keyupEvent, mModKeyState);
2419 MOZ_LOG(gKeyLog, LogLevel::Info,
2420 ("%p NativeKey::HandleAppCommandMessage(), dispatching keyup "
2421 "event...",
2422 this));
2423 // NOTE: Ignore if the keyup event is consumed because keyup event
2424 // represents just a physical key event state change.
2425 mDispatcher->DispatchKeyboardEvent(eKeyUp, keyupEvent, status,
2426 const_cast<NativeKey*>(this));
2427 MOZ_LOG(
2428 gKeyLog, LogLevel::Info,
2429 ("%p NativeKey::HandleAppCommandMessage(), dispatched keyup event",
2430 this));
2431 if (mWidget->Destroyed()) {
2432 MOZ_LOG(gKeyLog, LogLevel::Info,
2433 ("%p NativeKey::HandleAppCommandMessage(), keyup event caused "
2434 "destroying the widget",
2435 this));
2436 return true;
2440 return consumed;
2443 bool NativeKey::HandleKeyDownMessage(bool* aEventDispatched) const {
2444 MOZ_ASSERT(IsKeyDownMessage());
2446 if (aEventDispatched) {
2447 *aEventDispatched = false;
2450 if (sDispatchedKeyOfAppCommand &&
2451 sDispatchedKeyOfAppCommand == mOriginalVirtualKeyCode) {
2452 // The multimedia key event has already been dispatch from
2453 // HandleAppCommandMessage().
2454 sDispatchedKeyOfAppCommand = 0;
2455 MOZ_LOG(gKeyLog, LogLevel::Info,
2456 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2457 "event due to already dispatched from HandleAppCommandMessage(), ",
2458 this));
2459 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
2460 RedirectedKeyDownMessageManager::Forget();
2462 return true;
2465 if (IsReservedBySystem()) {
2466 MOZ_LOG(gKeyLog, LogLevel::Info,
2467 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2468 "event because the key combination is reserved by the system",
2469 this));
2470 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
2471 RedirectedKeyDownMessageManager::Forget();
2473 return false;
2476 if (sPendingHighSurrogate) {
2477 MOZ_LOG(gKeyLog, LogLevel::Info,
2478 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2479 "event because the key introduced only a high surrotate, so we "
2480 "should wait the following low surrogate input",
2481 this));
2482 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
2483 RedirectedKeyDownMessageManager::Forget();
2485 return false;
2488 // If the widget has gone, we should do nothing.
2489 if (mWidget->Destroyed()) {
2490 MOZ_LOG(
2491 gKeyLog, LogLevel::Warning,
2492 ("%p NativeKey::HandleKeyDownMessage(), WARNING, not handled due to "
2493 "destroyed the widget",
2494 this));
2495 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
2496 RedirectedKeyDownMessageManager::Forget();
2498 return false;
2501 bool defaultPrevented = false;
2502 if (mFakeCharMsgs ||
2503 !RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
2504 nsresult rv = mDispatcher->BeginNativeInputTransaction();
2505 if (NS_WARN_IF(NS_FAILED(rv))) {
2506 MOZ_LOG(gKeyLog, LogLevel::Error,
2507 ("%p NativeKey::HandleKeyDownMessage(), FAILED due to "
2508 "BeginNativeInputTransaction() failure",
2509 this));
2510 return true;
2513 bool isIMEEnabled = WinUtils::IsIMEEnabled(mWidget->GetInputContext());
2515 MOZ_LOG(gKeyLog, LogLevel::Debug,
2516 ("%p NativeKey::HandleKeyDownMessage(), initializing keydown "
2517 "event...",
2518 this));
2520 WidgetKeyboardEvent keydownEvent(true, eKeyDown, mWidget);
2521 nsEventStatus status = InitKeyEvent(keydownEvent, mModKeyState);
2522 MOZ_LOG(
2523 gKeyLog, LogLevel::Info,
2524 ("%p NativeKey::HandleKeyDownMessage(), dispatching keydown event...",
2525 this));
2526 bool dispatched = mDispatcher->DispatchKeyboardEvent(
2527 eKeyDown, keydownEvent, status, const_cast<NativeKey*>(this));
2528 if (aEventDispatched) {
2529 *aEventDispatched = dispatched;
2531 if (!dispatched) {
2532 // If the keydown event wasn't fired, there must be composition.
2533 // we don't need to do anything anymore.
2534 MOZ_LOG(
2535 gKeyLog, LogLevel::Info,
2536 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keypress "
2537 "event(s) because keydown event isn't dispatched actually",
2538 this));
2539 return false;
2541 defaultPrevented = status == nsEventStatus_eConsumeNoDefault;
2543 if (mWidget->Destroyed() || IsFocusedWindowChanged()) {
2544 MOZ_LOG(gKeyLog, LogLevel::Info,
2545 ("%p NativeKey::HandleKeyDownMessage(), keydown event caused "
2546 "destroying the widget",
2547 this));
2548 return true;
2551 MOZ_LOG(
2552 gKeyLog, LogLevel::Info,
2553 ("%p NativeKey::HandleKeyDownMessage(), dispatched keydown event, "
2554 "dispatched=%s, defaultPrevented=%s",
2555 this, GetBoolName(dispatched), GetBoolName(defaultPrevented)));
2557 // If IMC wasn't associated to the window but is associated it now (i.e.,
2558 // focus is moved from a non-editable editor to an editor by keydown
2559 // event handler), WM_CHAR and WM_SYSCHAR shouldn't cause first character
2560 // inputting if IME is opened. But then, we should redirect the native
2561 // keydown message to IME.
2562 // However, note that if focus has been already moved to another
2563 // application, we shouldn't redirect the message to it because the keydown
2564 // message is processed by us, so, nobody shouldn't process it.
2565 HWND focusedWnd = ::GetFocus();
2566 if (!defaultPrevented && !mFakeCharMsgs && focusedWnd && !isIMEEnabled &&
2567 WinUtils::IsIMEEnabled(mWidget->GetInputContext())) {
2568 RedirectedKeyDownMessageManager::RemoveNextCharMessage(focusedWnd);
2570 INPUT keyinput;
2571 keyinput.type = INPUT_KEYBOARD;
2572 keyinput.ki.wVk = mOriginalVirtualKeyCode;
2573 keyinput.ki.wScan = mScanCode;
2574 keyinput.ki.dwFlags = KEYEVENTF_SCANCODE;
2575 if (mIsExtended) {
2576 keyinput.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
2578 keyinput.ki.time = 0;
2579 keyinput.ki.dwExtraInfo = 0;
2581 RedirectedKeyDownMessageManager::WillRedirect(mMsg, defaultPrevented);
2583 MOZ_LOG(gKeyLog, LogLevel::Info,
2584 ("%p NativeKey::HandleKeyDownMessage(), redirecting %s...",
2585 this, ToString(mMsg).get()));
2587 ::SendInput(1, &keyinput, sizeof(keyinput));
2589 MOZ_LOG(gKeyLog, LogLevel::Info,
2590 ("%p NativeKey::HandleKeyDownMessage(), redirected %s", this,
2591 ToString(mMsg).get()));
2593 // Return here. We shouldn't dispatch keypress event for this WM_KEYDOWN.
2594 // If it's needed, it will be dispatched after next (redirected)
2595 // WM_KEYDOWN.
2596 return true;
2598 } else {
2599 MOZ_LOG(gKeyLog, LogLevel::Info,
2600 ("%p NativeKey::HandleKeyDownMessage(), received a redirected %s",
2601 this, ToString(mMsg).get()));
2603 defaultPrevented = RedirectedKeyDownMessageManager::DefaultPrevented();
2604 // If this is redirected keydown message, we have dispatched the keydown
2605 // event already.
2606 if (aEventDispatched) {
2607 *aEventDispatched = true;
2611 RedirectedKeyDownMessageManager::Forget();
2613 MOZ_ASSERT(!mWidget->Destroyed());
2615 // If the key was processed by IME and didn't cause WM_(SYS)CHAR messages, we
2616 // shouldn't dispatch keypress event.
2617 if (mOriginalVirtualKeyCode == VK_PROCESSKEY &&
2618 !IsFollowedByPrintableCharOrSysCharMessage()) {
2619 MOZ_LOG(gKeyLog, LogLevel::Info,
2620 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2621 "event because the key was already handled by IME, "
2622 "defaultPrevented=%s",
2623 this, GetBoolName(defaultPrevented)));
2624 return defaultPrevented;
2627 if (defaultPrevented) {
2628 MOZ_LOG(gKeyLog, LogLevel::Info,
2629 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2630 "event because preceding keydown event was consumed",
2631 this));
2632 return true;
2635 MOZ_ASSERT(!mCharMessageHasGone,
2636 "If following char message was consumed by somebody, "
2637 "keydown event should have been consumed before dispatch");
2639 // If mCommittedCharsAndModifiers was initialized with following char
2640 // messages, we should dispatch keypress events with its information.
2641 if (IsFollowedByPrintableCharOrSysCharMessage()) {
2642 MOZ_LOG(gKeyLog, LogLevel::Info,
2643 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2644 "keypress events with retrieved char messages...",
2645 this));
2646 return DispatchKeyPressEventsWithRetrievedCharMessages();
2649 // If we won't be getting a WM_CHAR, WM_SYSCHAR or WM_DEADCHAR, synthesize a
2650 // keypress for almost all keys
2651 if (NeedsToHandleWithoutFollowingCharMessages()) {
2652 MOZ_LOG(gKeyLog, LogLevel::Info,
2653 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2654 "keypress events...",
2655 this));
2656 return DispatchKeyPressEventsWithoutCharMessage();
2659 // If WM_KEYDOWN of VK_PACKET isn't followed by WM_CHAR, we don't need to
2660 // dispatch keypress events.
2661 if (mVirtualKeyCode == VK_PACKET) {
2662 MOZ_LOG(gKeyLog, LogLevel::Info,
2663 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2664 "event "
2665 "because the key is VK_PACKET and there are no char messages",
2666 this));
2667 return false;
2670 if (!mModKeyState.IsControl() && !mModKeyState.IsAlt() &&
2671 !mModKeyState.IsWin() && mIsPrintableKey) {
2672 // If this is simple KeyDown event but next message is not WM_CHAR,
2673 // this event may not input text, so we should ignore this event.
2674 // See bug 314130.
2675 MOZ_LOG(gKeyLog, LogLevel::Info,
2676 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2677 "event "
2678 "because the key event is simple printable key's event but not "
2679 "followed "
2680 "by char messages",
2681 this));
2682 return false;
2685 if (mIsDeadKey) {
2686 MOZ_LOG(gKeyLog, LogLevel::Info,
2687 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2688 "event "
2689 "because the key is a dead key and not followed by char messages",
2690 this));
2691 return false;
2694 MOZ_LOG(gKeyLog, LogLevel::Info,
2695 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2696 "keypress events due to no following char messages...",
2697 this));
2698 return DispatchKeyPressEventsWithoutCharMessage();
2701 bool NativeKey::HandleCharMessage(bool* aEventDispatched) const {
2702 MOZ_ASSERT(IsCharOrSysCharMessage(mMsg));
2703 return HandleCharMessage(mMsg, aEventDispatched);
2706 bool NativeKey::HandleCharMessage(const MSG& aCharMsg,
2707 bool* aEventDispatched) const {
2708 MOZ_ASSERT(IsKeyDownMessage() || IsCharOrSysCharMessage(mMsg));
2709 MOZ_ASSERT(IsCharOrSysCharMessage(aCharMsg.message));
2711 if (aEventDispatched) {
2712 *aEventDispatched = false;
2715 if ((IsCharOrSysCharMessage(mMsg) || IsEnterKeyPressCharMessage(mMsg)) &&
2716 IsAnotherInstanceRemovingCharMessage()) {
2717 MOZ_LOG(
2718 gKeyLog, LogLevel::Warning,
2719 ("%p NativeKey::HandleCharMessage(), WARNING, does nothing because "
2720 "the message should be handled in another instance removing this "
2721 "message",
2722 this));
2723 // Consume this for now because it will be handled by another instance.
2724 return true;
2727 // If the key combinations is reserved by the system, we shouldn't dispatch
2728 // eKeyPress event for it and passes the message to next wndproc.
2729 if (IsReservedBySystem()) {
2730 MOZ_LOG(gKeyLog, LogLevel::Info,
2731 ("%p NativeKey::HandleCharMessage(), doesn't dispatch keypress "
2732 "event because the key combination is reserved by the system",
2733 this));
2734 return false;
2737 // If the widget has gone, we should do nothing.
2738 if (mWidget->Destroyed()) {
2739 MOZ_LOG(gKeyLog, LogLevel::Warning,
2740 ("%p NativeKey::HandleCharMessage(), WARNING, not handled due to "
2741 "destroyed the widget",
2742 this));
2743 return false;
2746 // When a control key is inputted by a key, it should be handled without
2747 // WM_*CHAR messages at receiving WM_*KEYDOWN message. So, when we receive
2748 // WM_*CHAR message directly, we see a control character here.
2749 // Note that when the char is '\r', it means that the char message should
2750 // cause "Enter" keypress event for inserting a line break.
2751 if (IsControlCharMessage(aCharMsg) && !IsEnterKeyPressCharMessage(aCharMsg)) {
2752 // In this case, we don't need to dispatch eKeyPress event because:
2753 // 1. We're the only browser which dispatches "keypress" event for
2754 // non-printable characters (Although, both Chrome and Edge dispatch
2755 // "keypress" event for some keys accidentally. For example, "IntlRo"
2756 // key with Ctrl of Japanese keyboard layout).
2757 // 2. Currently, we may handle shortcut keys with "keydown" event if
2758 // it's reserved or something. So, we shouldn't dispatch "keypress"
2759 // event without it.
2760 // Note that this does NOT mean we stop dispatching eKeyPress event for
2761 // key presses causes a control character when Ctrl is pressed. In such
2762 // case, DispatchKeyPressEventsWithoutCharMessage() dispatches eKeyPress
2763 // instead of this method.
2764 MOZ_LOG(
2765 gKeyLog, LogLevel::Info,
2766 ("%p NativeKey::HandleCharMessage(), doesn't dispatch keypress "
2767 "event because received a control character input without WM_KEYDOWN",
2768 this));
2769 return false;
2772 // XXXmnakano I think that if mMsg is WM_CHAR, i.e., it comes without
2773 // preceding WM_KEYDOWN, we should should dispatch composition
2774 // events instead of eKeyPress because they are not caused by
2775 // actual keyboard operation.
2777 // First, handle normal text input or non-printable key case here.
2778 WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget);
2779 if (IsEnterKeyPressCharMessage(aCharMsg)) {
2780 keypressEvent.mKeyCode = NS_VK_RETURN;
2781 } else {
2782 keypressEvent.mCharCode = static_cast<uint32_t>(aCharMsg.wParam);
2784 nsresult rv = mDispatcher->BeginNativeInputTransaction();
2785 if (NS_WARN_IF(NS_FAILED(rv))) {
2786 MOZ_LOG(gKeyLog, LogLevel::Error,
2787 ("%p NativeKey::HandleCharMessage(), FAILED due to "
2788 "BeginNativeInputTransaction() failure",
2789 this));
2790 return true;
2793 MOZ_LOG(gKeyLog, LogLevel::Debug,
2794 ("%p NativeKey::HandleCharMessage(), initializing keypress "
2795 "event...",
2796 this));
2798 ModifierKeyState modKeyState(mModKeyState);
2799 // When AltGr is pressed, both Alt and Ctrl are active. However, when they
2800 // are active, TextEditor won't treat the keypress event as inputting a
2801 // character. Therefore, when AltGr is pressed and the key tries to input
2802 // a character, let's set them to false.
2803 if (modKeyState.IsControl() && modKeyState.IsAlt() &&
2804 IsPrintableCharMessage(aCharMsg)) {
2805 modKeyState.Unset(MODIFIER_ALT | MODIFIER_CONTROL);
2807 nsEventStatus status = InitKeyEvent(keypressEvent, modKeyState);
2808 MOZ_LOG(gKeyLog, LogLevel::Info,
2809 ("%p NativeKey::HandleCharMessage(), dispatching keypress event...",
2810 this));
2811 bool dispatched = mDispatcher->MaybeDispatchKeypressEvents(
2812 keypressEvent, status, const_cast<NativeKey*>(this));
2813 if (aEventDispatched) {
2814 *aEventDispatched = dispatched;
2816 if (mWidget->Destroyed()) {
2817 MOZ_LOG(gKeyLog, LogLevel::Info,
2818 ("%p NativeKey::HandleCharMessage(), keypress event caused "
2819 "destroying the widget",
2820 this));
2821 return true;
2823 bool consumed = status == nsEventStatus_eConsumeNoDefault;
2824 MOZ_LOG(gKeyLog, LogLevel::Info,
2825 ("%p NativeKey::HandleCharMessage(), dispatched keypress event, "
2826 "dispatched=%s, consumed=%s",
2827 this, GetBoolName(dispatched), GetBoolName(consumed)));
2828 return consumed;
2831 bool NativeKey::HandleKeyUpMessage(bool* aEventDispatched) const {
2832 MOZ_ASSERT(IsKeyUpMessage());
2834 if (aEventDispatched) {
2835 *aEventDispatched = false;
2838 // If the key combinations is reserved by the system, we shouldn't dispatch
2839 // eKeyUp event for it and passes the message to next wndproc.
2840 if (IsReservedBySystem()) {
2841 MOZ_LOG(gKeyLog, LogLevel::Info,
2842 ("%p NativeKey::HandleKeyUpMessage(), doesn't dispatch keyup "
2843 "event because the key combination is reserved by the system",
2844 this));
2845 return false;
2848 if (sPendingHighSurrogate) {
2849 MOZ_LOG(gKeyLog, LogLevel::Info,
2850 ("%p NativeKey::HandleKeyUpMessage(), doesn't dispatch keyup "
2851 "event because the key introduced only a high surrotate, so we "
2852 "should wait the following low surrogate input",
2853 this));
2854 return false;
2857 // If the widget has gone, we should do nothing.
2858 if (mWidget->Destroyed()) {
2859 MOZ_LOG(
2860 gKeyLog, LogLevel::Warning,
2861 ("%p NativeKey::HandleKeyUpMessage(), WARNING, not handled due to "
2862 "destroyed the widget",
2863 this));
2864 return false;
2867 nsresult rv = mDispatcher->BeginNativeInputTransaction();
2868 if (NS_WARN_IF(NS_FAILED(rv))) {
2869 MOZ_LOG(gKeyLog, LogLevel::Error,
2870 ("%p NativeKey::HandleKeyUpMessage(), FAILED due to "
2871 "BeginNativeInputTransaction() failure",
2872 this));
2873 return true;
2876 MOZ_LOG(gKeyLog, LogLevel::Debug,
2877 ("%p NativeKey::HandleKeyUpMessage(), initializing keyup event...",
2878 this));
2879 WidgetKeyboardEvent keyupEvent(true, eKeyUp, mWidget);
2880 nsEventStatus status = InitKeyEvent(keyupEvent, mModKeyState);
2881 MOZ_LOG(gKeyLog, LogLevel::Info,
2882 ("%p NativeKey::HandleKeyUpMessage(), dispatching keyup event...",
2883 this));
2884 bool dispatched = mDispatcher->DispatchKeyboardEvent(
2885 eKeyUp, keyupEvent, status, const_cast<NativeKey*>(this));
2886 if (aEventDispatched) {
2887 *aEventDispatched = dispatched;
2889 if (mWidget->Destroyed()) {
2890 MOZ_LOG(gKeyLog, LogLevel::Info,
2891 ("%p NativeKey::HandleKeyUpMessage(), keyup event caused "
2892 "destroying the widget",
2893 this));
2894 return true;
2896 bool consumed = status == nsEventStatus_eConsumeNoDefault;
2897 MOZ_LOG(gKeyLog, LogLevel::Info,
2898 ("%p NativeKey::HandleKeyUpMessage(), dispatched keyup event, "
2899 "dispatched=%s, consumed=%s",
2900 this, GetBoolName(dispatched), GetBoolName(consumed)));
2901 return consumed;
2904 bool NativeKey::NeedsToHandleWithoutFollowingCharMessages() const {
2905 MOZ_ASSERT(IsKeyDownMessage());
2907 // If the key combination is reserved by the system, the caller shouldn't
2908 // do anything with following WM_*CHAR messages. So, let's return true here.
2909 if (IsReservedBySystem()) {
2910 return true;
2913 // If the keydown message is generated for inputting some Unicode characters
2914 // via SendInput() API, we need to handle it only with WM_*CHAR messages.
2915 if (mVirtualKeyCode == VK_PACKET) {
2916 return false;
2919 // If following char message is for a control character, it should be handled
2920 // without WM_CHAR message. This is typically Ctrl + [a-z].
2921 if (mFollowingCharMsgs.Length() == 1 &&
2922 IsControlCharMessage(mFollowingCharMsgs[0])) {
2923 return true;
2926 // If keydown message is followed by WM_CHAR or WM_SYSCHAR whose wParam isn't
2927 // a control character, we should dispatch keypress event with the char
2928 // message even with any modifier state.
2929 if (IsFollowedByPrintableCharOrSysCharMessage()) {
2930 return false;
2933 // If any modifier keys which may cause printable keys becoming non-printable
2934 // are not pressed, we don't need special handling for the key.
2935 // Note that if the key does not produce a character with AltGr and when
2936 // AltGr key is pressed, we don't need to dispatch eKeyPress event for it
2937 // because AltGr shouldn't be used for a modifier for a shortcut without
2938 // Ctrl, Alt or Win. That means that we should treat it in same path for
2939 // Shift key.
2940 if (!mModKeyState.IsControl() && !mModKeyState.IsAlt() &&
2941 !mModKeyState.IsWin()) {
2942 return false;
2945 // If the key event causes dead key event, we don't need to dispatch keypress
2946 // event.
2947 if (mIsDeadKey && mCommittedCharsAndModifiers.IsEmpty()) {
2948 return false;
2951 // Even if the key is a printable key, it might cause non-printable character
2952 // input with modifier key(s).
2953 return mIsPrintableKey;
2956 static nsCString GetResultOfInSendMessageEx() {
2957 DWORD ret = ::InSendMessageEx(nullptr);
2958 if (!ret) {
2959 return "ISMEX_NOSEND"_ns;
2961 nsCString result;
2962 if (ret & ISMEX_CALLBACK) {
2963 result = "ISMEX_CALLBACK";
2965 if (ret & ISMEX_NOTIFY) {
2966 if (!result.IsEmpty()) {
2967 result += " | ";
2969 result += "ISMEX_NOTIFY";
2971 if (ret & ISMEX_REPLIED) {
2972 if (!result.IsEmpty()) {
2973 result += " | ";
2975 result += "ISMEX_REPLIED";
2977 if (ret & ISMEX_SEND) {
2978 if (!result.IsEmpty()) {
2979 result += " | ";
2981 result += "ISMEX_SEND";
2983 return result;
2986 bool NativeKey::MayBeSameCharMessage(const MSG& aCharMsg1,
2987 const MSG& aCharMsg2) const {
2988 // NOTE: Although, we don't know when this case occurs, the scan code value
2989 // in lParam may be changed from 0 to something. The changed value
2990 // is different from the scan code of handling keydown message.
2991 static const LPARAM kScanCodeMask = 0x00FF0000;
2992 return aCharMsg1.message == aCharMsg2.message &&
2993 aCharMsg1.wParam == aCharMsg2.wParam &&
2994 (aCharMsg1.lParam & ~kScanCodeMask) ==
2995 (aCharMsg2.lParam & ~kScanCodeMask);
2998 bool NativeKey::IsSamePhysicalKeyMessage(const MSG& aKeyOrCharMsg1,
2999 const MSG& aKeyOrCharMsg2) const {
3000 if (NS_WARN_IF(aKeyOrCharMsg1.message < WM_KEYFIRST) ||
3001 NS_WARN_IF(aKeyOrCharMsg1.message > WM_KEYLAST) ||
3002 NS_WARN_IF(aKeyOrCharMsg2.message < WM_KEYFIRST) ||
3003 NS_WARN_IF(aKeyOrCharMsg2.message > WM_KEYLAST)) {
3004 return false;
3006 return WinUtils::GetScanCode(aKeyOrCharMsg1.lParam) ==
3007 WinUtils::GetScanCode(aKeyOrCharMsg2.lParam) &&
3008 WinUtils::IsExtendedScanCode(aKeyOrCharMsg1.lParam) ==
3009 WinUtils::IsExtendedScanCode(aKeyOrCharMsg2.lParam);
3012 bool NativeKey::GetFollowingCharMessage(MSG& aCharMsg) {
3013 MOZ_ASSERT(IsKeyDownMessage());
3015 aCharMsg.message = WM_NULL;
3017 if (mFakeCharMsgs) {
3018 for (size_t i = 0; i < mFakeCharMsgs->Length(); i++) {
3019 FakeCharMsg& fakeCharMsg = mFakeCharMsgs->ElementAt(i);
3020 if (fakeCharMsg.mConsumed) {
3021 continue;
3023 MSG charMsg = fakeCharMsg.GetCharMsg(mMsg.hwnd);
3024 fakeCharMsg.mConsumed = true;
3025 if (!IsCharMessage(charMsg)) {
3026 return false;
3028 aCharMsg = charMsg;
3029 return true;
3031 return false;
3034 // If next key message is not char message, we should give up to find a
3035 // related char message for the handling keydown event for now.
3036 // Note that it's possible other applications may send other key message
3037 // after we call TranslateMessage(). That may cause PeekMessage() failing
3038 // to get char message for the handling keydown message.
3039 MSG nextKeyMsg;
3040 if (!WinUtils::PeekMessage(&nextKeyMsg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST,
3041 PM_NOREMOVE | PM_NOYIELD) ||
3042 !IsCharMessage(nextKeyMsg)) {
3043 MOZ_LOG(gKeyLog, LogLevel::Debug,
3044 ("%p NativeKey::GetFollowingCharMessage(), there are no char "
3045 "messages",
3046 this));
3047 return false;
3049 const MSG kFoundCharMsg = nextKeyMsg;
3051 AutoRestore<MSG> saveLastRemovingMsg(mRemovingMsg);
3052 mRemovingMsg = nextKeyMsg;
3054 mReceivedMsg = sEmptyMSG;
3055 AutoRestore<MSG> ensureToClearRecivedMsg(mReceivedMsg);
3057 // On Metrofox, PeekMessage() sometimes returns WM_NULL even if we specify
3058 // the message range. So, if it returns WM_NULL, we should retry to get
3059 // the following char message it was found above.
3060 for (uint32_t i = 0; i < 50; i++) {
3061 MSG removedMsg, nextKeyMsgInAllWindows;
3062 bool doCrash = false;
3063 if (!WinUtils::PeekMessage(&removedMsg, mMsg.hwnd, nextKeyMsg.message,
3064 nextKeyMsg.message, PM_REMOVE | PM_NOYIELD)) {
3065 // We meets unexpected case. We should collect the message queue state
3066 // and crash for reporting the bug.
3067 doCrash = true;
3069 // If another instance was created for the removing message during trying
3070 // to remove a char message, the instance didn't handle it for preventing
3071 // recursive handling. So, let's handle it in this instance.
3072 if (!IsEmptyMSG(mReceivedMsg)) {
3073 // If focus is moved to different window, we shouldn't handle it on
3074 // the widget. Let's discard it for now.
3075 if (mReceivedMsg.hwnd != nextKeyMsg.hwnd) {
3076 MOZ_LOG(
3077 gKeyLog, LogLevel::Warning,
3078 ("%p NativeKey::GetFollowingCharMessage(), WARNING, received a "
3079 "char message during removing it from the queue, but it's for "
3080 "different window, mReceivedMsg=%s, nextKeyMsg=%s, "
3081 "kFoundCharMsg=%s",
3082 this, ToString(mReceivedMsg).get(), ToString(nextKeyMsg).get(),
3083 ToString(kFoundCharMsg).get()));
3084 // There might still exist char messages, the loop of calling
3085 // this method should be continued.
3086 aCharMsg.message = WM_NULL;
3087 return true;
3089 // Even if the received message is different from what we tried to
3090 // remove from the queue, let's take the received message as a part of
3091 // the result of this key sequence.
3092 if (mReceivedMsg.message != nextKeyMsg.message ||
3093 mReceivedMsg.wParam != nextKeyMsg.wParam ||
3094 mReceivedMsg.lParam != nextKeyMsg.lParam) {
3095 MOZ_LOG(
3096 gKeyLog, LogLevel::Warning,
3097 ("%p NativeKey::GetFollowingCharMessage(), WARNING, received a "
3098 "char message during removing it from the queue, but it's "
3099 "differnt from what trying to remove from the queue, "
3100 "aCharMsg=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3101 this, ToString(mReceivedMsg).get(), ToString(nextKeyMsg).get(),
3102 ToString(kFoundCharMsg).get()));
3103 } else {
3104 MOZ_LOG(
3105 gKeyLog, LogLevel::Debug,
3106 ("%p NativeKey::GetFollowingCharMessage(), succeeded to "
3107 "retrieve next char message via another instance, aCharMsg=%s, "
3108 "kFoundCharMsg=%s",
3109 this, ToString(mReceivedMsg).get(),
3110 ToString(kFoundCharMsg).get()));
3112 aCharMsg = mReceivedMsg;
3113 return true;
3116 // The char message is redirected to different thread's window by focus
3117 // move or something or just cancelled by external application.
3118 if (!WinUtils::PeekMessage(&nextKeyMsgInAllWindows, 0, WM_KEYFIRST,
3119 WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD)) {
3120 MOZ_LOG(
3121 gKeyLog, LogLevel::Warning,
3122 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3123 "remove a char message, but it's already gone from all message "
3124 "queues, nextKeyMsg=%s, kFoundCharMsg=%s",
3125 this, ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3126 return true; // XXX should return false in this case
3128 // The next key message is redirected to different window created by our
3129 // thread, we should do nothing because we must not have focus.
3130 if (nextKeyMsgInAllWindows.hwnd != mMsg.hwnd) {
3131 aCharMsg = nextKeyMsgInAllWindows;
3132 MOZ_LOG(
3133 gKeyLog, LogLevel::Warning,
3134 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3135 "remove a char message, but found in another message queue, "
3136 "nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3137 this, ToString(nextKeyMsgInAllWindows).get(),
3138 ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3139 return true;
3141 // If next key message becomes non-char message, this key operation
3142 // may have already been consumed or canceled.
3143 if (!IsCharMessage(nextKeyMsgInAllWindows)) {
3144 MOZ_LOG(
3145 gKeyLog, LogLevel::Warning,
3146 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3147 "remove a char message and next key message becomes non-char "
3148 "message, nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, "
3149 "kFoundCharMsg=%s",
3150 this, ToString(nextKeyMsgInAllWindows).get(),
3151 ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3152 MOZ_ASSERT(!mCharMessageHasGone);
3153 mFollowingCharMsgs.Clear();
3154 mCharMessageHasGone = true;
3155 return false;
3157 // If next key message is still a char message but different key message,
3158 // we should treat current key operation is consumed or canceled and
3159 // next char message should be handled as an orphan char message later.
3160 if (!IsSamePhysicalKeyMessage(nextKeyMsgInAllWindows, kFoundCharMsg)) {
3161 MOZ_LOG(
3162 gKeyLog, LogLevel::Warning,
3163 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3164 "remove a char message and next key message becomes differnt "
3165 "key's "
3166 "char message, nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, "
3167 "kFoundCharMsg=%s",
3168 this, ToString(nextKeyMsgInAllWindows).get(),
3169 ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3170 MOZ_ASSERT(!mCharMessageHasGone);
3171 mFollowingCharMsgs.Clear();
3172 mCharMessageHasGone = true;
3173 return false;
3175 // If next key message is still a char message but the message is changed,
3176 // we should retry to remove the new message with PeekMessage() again.
3177 if (nextKeyMsgInAllWindows.message != nextKeyMsg.message) {
3178 MOZ_LOG(
3179 gKeyLog, LogLevel::Warning,
3180 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3181 "remove a char message due to message change, let's retry to "
3182 "remove the message with newly found char message, "
3183 "nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3184 this, ToString(nextKeyMsgInAllWindows).get(),
3185 ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3186 nextKeyMsg = nextKeyMsgInAllWindows;
3187 continue;
3189 // If there is still existing a char message caused by same physical key
3190 // in the queue but PeekMessage(PM_REMOVE) failed to remove it from the
3191 // queue, it might be possible that the odd keyboard layout or utility
3192 // hooks only PeekMessage(PM_NOREMOVE) and GetMessage(). So, let's try
3193 // remove the char message with GetMessage() again.
3194 // FYI: The wParam might be different from the found message, but it's
3195 // okay because we assume that odd keyboard layouts return actual
3196 // inputting character at removing the char message.
3197 if (WinUtils::GetMessage(&removedMsg, mMsg.hwnd, nextKeyMsg.message,
3198 nextKeyMsg.message)) {
3199 MOZ_LOG(
3200 gKeyLog, LogLevel::Warning,
3201 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3202 "remove a char message, but succeeded with GetMessage(), "
3203 "removedMsg=%s, kFoundCharMsg=%s",
3204 this, ToString(removedMsg).get(), ToString(kFoundCharMsg).get()));
3205 // Cancel to crash, but we need to check the removed message value.
3206 doCrash = false;
3208 // If we've already removed some WM_NULL messages from the queue and
3209 // the found message has already gone from the queue, let's treat the key
3210 // as inputting no characters and already consumed.
3211 else if (i > 0) {
3212 MOZ_LOG(
3213 gKeyLog, LogLevel::Warning,
3214 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3215 "remove a char message, but removed %d WM_NULL messages",
3216 this, i));
3217 // If the key is a printable key or a control key but tried to input
3218 // a character, mark mCharMessageHasGone true for handling the keydown
3219 // event as inputting empty string.
3220 MOZ_ASSERT(!mCharMessageHasGone);
3221 mFollowingCharMsgs.Clear();
3222 mCharMessageHasGone = true;
3223 return false;
3225 MOZ_LOG(gKeyLog, LogLevel::Error,
3226 ("%p NativeKey::GetFollowingCharMessage(), FAILED, lost target "
3227 "message to remove, nextKeyMsg=%s",
3228 this, ToString(nextKeyMsg).get()));
3231 if (doCrash) {
3232 nsPrintfCString info(
3233 "\nPeekMessage() failed to remove char message! "
3234 "\nActive keyboard layout=0x%p (%s), "
3235 "\nHandling message: %s, InSendMessageEx()=%s, "
3236 "\nFound message: %s, "
3237 "\nWM_NULL has been removed: %d, "
3238 "\nNext key message in all windows: %s, "
3239 "time=%ld, ",
3240 KeyboardLayout::GetActiveLayout(),
3241 KeyboardLayout::GetActiveLayoutName().get(), ToString(mMsg).get(),
3242 GetResultOfInSendMessageEx().get(), ToString(kFoundCharMsg).get(), i,
3243 ToString(nextKeyMsgInAllWindows).get(), nextKeyMsgInAllWindows.time);
3244 CrashReporter::AppendAppNotesToCrashReport(info);
3245 MSG nextMsg;
3246 if (WinUtils::PeekMessage(&nextMsg, 0, 0, 0, PM_NOREMOVE | PM_NOYIELD)) {
3247 nsPrintfCString info("\nNext message in all windows: %s, time=%ld",
3248 ToString(nextMsg).get(), nextMsg.time);
3249 CrashReporter::AppendAppNotesToCrashReport(info);
3250 } else {
3251 CrashReporter::AppendAppNotesToCrashReport(
3252 "\nThere is no message in any window"_ns);
3255 MOZ_CRASH("We lost the following char message");
3258 // We're still not sure why ::PeekMessage() may return WM_NULL even with
3259 // its first message and its last message are same message. However,
3260 // at developing Metrofox, we met this case even with usual keyboard
3261 // layouts. So, it might be possible in desktop application or it really
3262 // occurs with some odd keyboard layouts which perhaps hook API.
3263 if (removedMsg.message == WM_NULL) {
3264 MOZ_LOG(gKeyLog, LogLevel::Warning,
3265 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3266 "remove a char message, instead, removed WM_NULL message, "
3267 "removedMsg=%s",
3268 this, ToString(removedMsg).get()));
3269 // Check if there is the message which we're trying to remove.
3270 MSG newNextKeyMsg;
3271 if (!WinUtils::PeekMessage(&newNextKeyMsg, mMsg.hwnd, WM_KEYFIRST,
3272 WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD)) {
3273 // If there is no key message, we should mark this keydown as consumed
3274 // because the key operation may have already been handled or canceled.
3275 MOZ_LOG(
3276 gKeyLog, LogLevel::Warning,
3277 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3278 "remove a char message because it's gone during removing it from "
3279 "the queue, nextKeyMsg=%s, kFoundCharMsg=%s",
3280 this, ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3281 MOZ_ASSERT(!mCharMessageHasGone);
3282 mFollowingCharMsgs.Clear();
3283 mCharMessageHasGone = true;
3284 return false;
3286 if (!IsCharMessage(newNextKeyMsg)) {
3287 // If next key message becomes a non-char message, we should mark this
3288 // keydown as consumed because the key operation may have already been
3289 // handled or canceled.
3290 MOZ_LOG(
3291 gKeyLog, LogLevel::Warning,
3292 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3293 "remove a char message because it's gone during removing it from "
3294 "the queue, nextKeyMsg=%s, newNextKeyMsg=%s, kFoundCharMsg=%s",
3295 this, ToString(nextKeyMsg).get(), ToString(newNextKeyMsg).get(),
3296 ToString(kFoundCharMsg).get()));
3297 MOZ_ASSERT(!mCharMessageHasGone);
3298 mFollowingCharMsgs.Clear();
3299 mCharMessageHasGone = true;
3300 return false;
3302 MOZ_LOG(
3303 gKeyLog, LogLevel::Debug,
3304 ("%p NativeKey::GetFollowingCharMessage(), there is the message "
3305 "which is being tried to be removed from the queue, trying again...",
3306 this));
3307 continue;
3310 // Typically, this case occurs with WM_DEADCHAR. If the removed message's
3311 // wParam becomes 0, that means that the key event shouldn't cause text
3312 // input. So, let's ignore the strange char message.
3313 if (removedMsg.message == nextKeyMsg.message && !removedMsg.wParam) {
3314 MOZ_LOG(
3315 gKeyLog, LogLevel::Warning,
3316 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3317 "remove a char message, but the removed message's wParam is 0, "
3318 "removedMsg=%s",
3319 this, ToString(removedMsg).get()));
3320 return false;
3323 // This is normal case.
3324 if (MayBeSameCharMessage(removedMsg, nextKeyMsg)) {
3325 aCharMsg = removedMsg;
3326 MOZ_LOG(
3327 gKeyLog, LogLevel::Debug,
3328 ("%p NativeKey::GetFollowingCharMessage(), succeeded to retrieve "
3329 "next char message, aCharMsg=%s",
3330 this, ToString(aCharMsg).get()));
3331 return true;
3334 // Even if removed message is different char message from the found char
3335 // message, when the scan code is same, we can assume that the message
3336 // is overwritten by somebody who hooks API. See bug 1336028 comment 0 for
3337 // the possible scenarios.
3338 if (IsCharMessage(removedMsg) &&
3339 IsSamePhysicalKeyMessage(removedMsg, nextKeyMsg)) {
3340 aCharMsg = removedMsg;
3341 MOZ_LOG(
3342 gKeyLog, LogLevel::Warning,
3343 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3344 "remove a char message, but the removed message was changed from "
3345 "the found message except their scancode, aCharMsg=%s, "
3346 "nextKeyMsg=%s, kFoundCharMsg=%s",
3347 this, ToString(aCharMsg).get(), ToString(nextKeyMsg).get(),
3348 ToString(kFoundCharMsg).get()));
3349 return true;
3352 // When found message's wParam is 0 and its scancode is 0xFF, we may remove
3353 // usual char message actually. In such case, we should use the removed
3354 // char message.
3355 if (IsCharMessage(removedMsg) && !nextKeyMsg.wParam &&
3356 WinUtils::GetScanCode(nextKeyMsg.lParam) == 0xFF) {
3357 aCharMsg = removedMsg;
3358 MOZ_LOG(
3359 gKeyLog, LogLevel::Warning,
3360 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3361 "remove a char message, but the removed message was changed from "
3362 "the found message but the found message was odd, so, ignoring the "
3363 "odd found message and respecting the removed message, aCharMsg=%s, "
3364 "nextKeyMsg=%s, kFoundCharMsg=%s",
3365 this, ToString(aCharMsg).get(), ToString(nextKeyMsg).get(),
3366 ToString(kFoundCharMsg).get()));
3367 return true;
3370 // NOTE: Although, we don't know when this case occurs, the scan code value
3371 // in lParam may be changed from 0 to something. The changed value
3372 // is different from the scan code of handling keydown message.
3373 MOZ_LOG(
3374 gKeyLog, LogLevel::Error,
3375 ("%p NativeKey::GetFollowingCharMessage(), FAILED, removed message "
3376 "is really different from what we have already found, removedMsg=%s, "
3377 "nextKeyMsg=%s, kFoundCharMsg=%s",
3378 this, ToString(removedMsg).get(), ToString(nextKeyMsg).get(),
3379 ToString(kFoundCharMsg).get()));
3380 nsPrintfCString info(
3381 "\nPeekMessage() removed unexpcted char message! "
3382 "\nActive keyboard layout=0x%p (%s), "
3383 "\nHandling message: %s, InSendMessageEx()=%s, "
3384 "\nFound message: %s, "
3385 "\nRemoved message: %s, ",
3386 KeyboardLayout::GetActiveLayout(),
3387 KeyboardLayout::GetActiveLayoutName().get(), ToString(mMsg).get(),
3388 GetResultOfInSendMessageEx().get(), ToString(kFoundCharMsg).get(),
3389 ToString(removedMsg).get());
3390 CrashReporter::AppendAppNotesToCrashReport(info);
3391 // What's the next key message?
3392 MSG nextKeyMsgAfter;
3393 if (WinUtils::PeekMessage(&nextKeyMsgAfter, mMsg.hwnd, WM_KEYFIRST,
3394 WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD)) {
3395 nsPrintfCString info(
3396 "\nNext key message after unexpected char message "
3397 "removed: %s, ",
3398 ToString(nextKeyMsgAfter).get());
3399 CrashReporter::AppendAppNotesToCrashReport(info);
3400 } else {
3401 CrashReporter::AppendAppNotesToCrashReport(
3402 nsLiteralCString("\nThere is no key message after unexpected char "
3403 "message removed, "));
3405 // Another window has a key message?
3406 if (WinUtils::PeekMessage(&nextKeyMsgInAllWindows, 0, WM_KEYFIRST,
3407 WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD)) {
3408 nsPrintfCString info("\nNext key message in all windows: %s.",
3409 ToString(nextKeyMsgInAllWindows).get());
3410 CrashReporter::AppendAppNotesToCrashReport(info);
3411 } else {
3412 CrashReporter::AppendAppNotesToCrashReport(
3413 "\nThere is no key message in any windows."_ns);
3416 MOZ_CRASH("PeekMessage() removed unexpected message");
3418 MOZ_LOG(
3419 gKeyLog, LogLevel::Error,
3420 ("%p NativeKey::GetFollowingCharMessage(), FAILED, removed messages "
3421 "are all WM_NULL, nextKeyMsg=%s",
3422 this, ToString(nextKeyMsg).get()));
3423 nsPrintfCString info(
3424 "\nWe lost following char message! "
3425 "\nActive keyboard layout=0x%p (%s), "
3426 "\nHandling message: %s, InSendMessageEx()=%s, \n"
3427 "Found message: %s, removed a lot of WM_NULL",
3428 KeyboardLayout::GetActiveLayout(),
3429 KeyboardLayout::GetActiveLayoutName().get(), ToString(mMsg).get(),
3430 GetResultOfInSendMessageEx().get(), ToString(kFoundCharMsg).get());
3431 CrashReporter::AppendAppNotesToCrashReport(info);
3432 MOZ_CRASH("We lost the following char message");
3433 return false;
3436 void NativeKey::ComputeInputtingStringWithKeyboardLayout() {
3437 KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
3439 if (KeyboardLayout::IsPrintableCharKey(mVirtualKeyCode) ||
3440 mCharMessageHasGone) {
3441 mInputtingStringAndModifiers = mCommittedCharsAndModifiers;
3442 } else {
3443 mInputtingStringAndModifiers.Clear();
3445 mShiftedString.Clear();
3446 mUnshiftedString.Clear();
3447 mShiftedLatinChar = mUnshiftedLatinChar = 0;
3449 // XXX How about when Win key is pressed?
3450 if (mModKeyState.IsControl() == mModKeyState.IsAlt()) {
3451 return;
3454 // If user is inputting a Unicode character with typing Alt + Numpad
3455 // keys, we shouldn't set alternative key codes because the key event
3456 // shouldn't match with a mnemonic. However, we should set only
3457 // mUnshiftedString for keeping traditional behavior at WM_SYSKEYDOWN.
3458 // FYI: I guess that it's okay that mUnshiftedString stays empty. So,
3459 // if its value breaks something, you must be able to just return here.
3460 if (MaybeTypingUnicodeScalarValue()) {
3461 if (!mCommittedCharsAndModifiers.IsEmpty()) {
3462 MOZ_ASSERT(mMsg.message == WM_SYSKEYDOWN);
3463 char16_t num = mCommittedCharsAndModifiers.CharAt(0);
3464 MOZ_ASSERT(num >= '0' && num <= '9');
3465 mUnshiftedString.Append(num, MODIFIER_NONE);
3466 return;
3468 // If user presses a function key without NumLock or 3rd party utility
3469 // synthesizes a function key on numpad, we should handle it as-is because
3470 // the user's intention may be performing `Alt` + foo.
3471 MOZ_ASSERT(!KeyboardLayout::IsPrintableCharKey(mVirtualKeyCode));
3472 return;
3475 ModifierKeyState capsLockState(mModKeyState.GetModifiers() &
3476 MODIFIER_CAPSLOCK);
3478 mUnshiftedString =
3479 keyboardLayout->GetUniCharsAndModifiers(mVirtualKeyCode, capsLockState);
3480 capsLockState.Set(MODIFIER_SHIFT);
3481 mShiftedString =
3482 keyboardLayout->GetUniCharsAndModifiers(mVirtualKeyCode, capsLockState);
3484 // The current keyboard cannot input alphabets or numerics,
3485 // we should append them for Shortcut/Access keys.
3486 // E.g., for Cyrillic keyboard layout.
3487 capsLockState.Unset(MODIFIER_SHIFT);
3488 WidgetUtils::GetLatinCharCodeForKeyCode(
3489 mDOMKeyCode, capsLockState.GetModifiers(), &mUnshiftedLatinChar,
3490 &mShiftedLatinChar);
3492 // If the mShiftedLatinChar isn't 0, the key code is NS_VK_[A-Z].
3493 if (mShiftedLatinChar) {
3494 // If the produced characters of the key on current keyboard layout
3495 // are same as computed Latin characters, we shouldn't append the
3496 // Latin characters to alternativeCharCode.
3497 if (mUnshiftedLatinChar == mUnshiftedString.CharAt(0) &&
3498 mShiftedLatinChar == mShiftedString.CharAt(0)) {
3499 mShiftedLatinChar = mUnshiftedLatinChar = 0;
3501 } else if (mUnshiftedLatinChar) {
3502 // If the mShiftedLatinChar is 0, the mKeyCode doesn't produce
3503 // alphabet character. At that time, the character may be produced
3504 // with Shift key. E.g., on French keyboard layout, NS_VK_PERCENT
3505 // key produces LATIN SMALL LETTER U WITH GRAVE (U+00F9) without
3506 // Shift key but with Shift key, it produces '%'.
3507 // If the mUnshiftedLatinChar is produced by the key on current
3508 // keyboard layout, we shouldn't append it to alternativeCharCode.
3509 if (mUnshiftedLatinChar == mUnshiftedString.CharAt(0) ||
3510 mUnshiftedLatinChar == mShiftedString.CharAt(0)) {
3511 mUnshiftedLatinChar = 0;
3515 if (!mModKeyState.IsControl()) {
3516 return;
3519 // If the mCharCode is not ASCII character, we should replace the
3520 // mCharCode with ASCII character only when Ctrl is pressed.
3521 // But don't replace the mCharCode when the mCharCode is not same as
3522 // unmodified characters. In such case, Ctrl is sometimes used for a
3523 // part of character inputting key combination like Shift.
3524 uint32_t ch =
3525 mModKeyState.IsShift() ? mShiftedLatinChar : mUnshiftedLatinChar;
3526 if (!ch) {
3527 return;
3529 if (mInputtingStringAndModifiers.IsEmpty() ||
3530 mInputtingStringAndModifiers.UniCharsCaseInsensitiveEqual(
3531 mModKeyState.IsShift() ? mShiftedString : mUnshiftedString)) {
3532 mInputtingStringAndModifiers.Clear();
3533 mInputtingStringAndModifiers.Append(ch, mModKeyState.GetModifiers());
3537 bool NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages() const {
3538 MOZ_ASSERT(IsKeyDownMessage());
3539 MOZ_ASSERT(IsFollowedByPrintableCharOrSysCharMessage());
3540 MOZ_ASSERT(!mWidget->Destroyed());
3542 nsresult rv = mDispatcher->BeginNativeInputTransaction();
3543 if (NS_WARN_IF(NS_FAILED(rv))) {
3544 MOZ_LOG(
3545 gKeyLog, LogLevel::Error,
3546 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3547 "FAILED due to BeginNativeInputTransaction() failure",
3548 this));
3549 return true;
3551 WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget);
3552 MOZ_LOG(gKeyLog, LogLevel::Debug,
3553 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3554 "initializing keypress event...",
3555 this));
3556 ModifierKeyState modKeyState(mModKeyState);
3557 if (mCanIgnoreModifierStateAtKeyPress && IsFollowedByPrintableCharMessage()) {
3558 // If eKeyPress event should cause inputting text in focused editor,
3559 // we need to remove Alt and Ctrl state.
3560 modKeyState.Unset(MODIFIER_ALT | MODIFIER_CONTROL);
3562 // We don't need to send char message here if there are two or more retrieved
3563 // messages because we need to set each message to each eKeyPress event.
3564 bool needsCallback = mFollowingCharMsgs.Length() > 1;
3565 nsEventStatus status = InitKeyEvent(keypressEvent, modKeyState);
3566 MOZ_LOG(gKeyLog, LogLevel::Info,
3567 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3568 "dispatching keypress event(s)...",
3569 this));
3570 bool dispatched = mDispatcher->MaybeDispatchKeypressEvents(
3571 keypressEvent, status, const_cast<NativeKey*>(this), needsCallback);
3572 if (mWidget->Destroyed()) {
3573 MOZ_LOG(
3574 gKeyLog, LogLevel::Info,
3575 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3576 "keypress event(s) caused destroying the widget",
3577 this));
3578 return true;
3580 bool consumed = status == nsEventStatus_eConsumeNoDefault;
3581 MOZ_LOG(gKeyLog, LogLevel::Info,
3582 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3583 "dispatched keypress event(s), dispatched=%s, consumed=%s",
3584 this, GetBoolName(dispatched), GetBoolName(consumed)));
3585 return consumed;
3588 bool NativeKey::DispatchKeyPressEventsWithoutCharMessage() const {
3589 MOZ_ASSERT(IsKeyDownMessage());
3590 MOZ_ASSERT(!mIsDeadKey || !mCommittedCharsAndModifiers.IsEmpty());
3591 MOZ_ASSERT(!mWidget->Destroyed());
3593 nsresult rv = mDispatcher->BeginNativeInputTransaction();
3594 if (NS_WARN_IF(NS_FAILED(rv))) {
3595 MOZ_LOG(gKeyLog, LogLevel::Error,
3596 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3597 "FAILED due "
3598 "to BeginNativeInputTransaction() failure",
3599 this));
3600 return true;
3603 WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget);
3604 if (mInputtingStringAndModifiers.IsEmpty() && mShiftedString.IsEmpty() &&
3605 mUnshiftedString.IsEmpty()) {
3606 keypressEvent.mKeyCode = mDOMKeyCode;
3608 MOZ_LOG(gKeyLog, LogLevel::Debug,
3609 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3610 "initializing "
3611 "keypress event...",
3612 this));
3613 nsEventStatus status = InitKeyEvent(keypressEvent, mModKeyState);
3614 MOZ_LOG(gKeyLog, LogLevel::Info,
3615 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3616 "dispatching "
3617 "keypress event(s)...",
3618 this));
3619 bool dispatched = mDispatcher->MaybeDispatchKeypressEvents(
3620 keypressEvent, status, const_cast<NativeKey*>(this));
3621 if (mWidget->Destroyed()) {
3622 MOZ_LOG(gKeyLog, LogLevel::Info,
3623 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3624 "keypress event(s) caused destroying the widget",
3625 this));
3626 return true;
3628 bool consumed = status == nsEventStatus_eConsumeNoDefault;
3629 MOZ_LOG(
3630 gKeyLog, LogLevel::Info,
3631 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), dispatched "
3632 "keypress event(s), dispatched=%s, consumed=%s",
3633 this, GetBoolName(dispatched), GetBoolName(consumed)));
3634 return consumed;
3637 void NativeKey::WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyboardEvent,
3638 uint32_t aIndex) {
3639 // If it's an eKeyPress event and it's generated from retrieved char message,
3640 // we need to set raw message information for plugins.
3641 if (aKeyboardEvent.mMessage == eKeyPress &&
3642 IsFollowedByPrintableCharOrSysCharMessage()) {
3643 MOZ_RELEASE_ASSERT(aIndex < mCommittedCharsAndModifiers.Length());
3644 uint32_t foundPrintableCharMessages = 0;
3645 for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
3646 if (!IsPrintableCharOrSysCharMessage(mFollowingCharMsgs[i])) {
3647 // XXX Should we dispatch a plugin event for WM_*DEADCHAR messages and
3648 // WM_CHAR with a control character here? But we're not sure
3649 // how can we create such message queue (i.e., WM_CHAR or
3650 // WM_SYSCHAR with a printable character and such message are
3651 // generated by a keydown). So, let's ignore such case until
3652 // we'd get some bug reports.
3653 MOZ_LOG(gKeyLog, LogLevel::Warning,
3654 ("%p NativeKey::WillDispatchKeyboardEvent(), WARNING, "
3655 "ignoring %zuth message due to non-printable char message, %s",
3656 this, i + 1, ToString(mFollowingCharMsgs[i]).get()));
3657 continue;
3659 if (foundPrintableCharMessages++ == aIndex) {
3660 // Found message which caused the eKeyPress event.
3661 break;
3664 // Set modifier state from mCommittedCharsAndModifiers because some of them
3665 // might be different. For example, Shift key was pressed at inputting
3666 // dead char but Shift key was released before inputting next character.
3667 if (mCanIgnoreModifierStateAtKeyPress) {
3668 ModifierKeyState modKeyState(mModKeyState);
3669 modKeyState.Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT |
3670 MODIFIER_ALTGRAPH | MODIFIER_CAPSLOCK);
3671 modKeyState.Set(mCommittedCharsAndModifiers.ModifiersAt(aIndex));
3672 modKeyState.InitInputEvent(aKeyboardEvent);
3673 MOZ_LOG(gKeyLog, LogLevel::Info,
3674 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3675 "setting %uth modifier state to %s",
3676 this, aIndex + 1, ToString(modKeyState).get()));
3679 size_t longestLength =
3680 std::max(mInputtingStringAndModifiers.Length(),
3681 std::max(mShiftedString.Length(), mUnshiftedString.Length()));
3682 size_t skipUniChars = longestLength - mInputtingStringAndModifiers.Length();
3683 size_t skipShiftedChars = longestLength - mShiftedString.Length();
3684 size_t skipUnshiftedChars = longestLength - mUnshiftedString.Length();
3685 if (aIndex >= longestLength) {
3686 MOZ_LOG(
3687 gKeyLog, LogLevel::Info,
3688 ("%p NativeKey::WillDispatchKeyboardEvent(), does nothing for %uth "
3689 "%s event",
3690 this, aIndex + 1, ToChar(aKeyboardEvent.mMessage)));
3691 return;
3694 // Check if aKeyboardEvent is the last event for a key press.
3695 // So, if it's not an eKeyPress event, it's always the last event.
3696 // Otherwise, check if the index is the last character of
3697 // mCommittedCharsAndModifiers.
3698 bool isLastIndex = aKeyboardEvent.mMessage != eKeyPress ||
3699 mCommittedCharsAndModifiers.IsEmpty() ||
3700 mCommittedCharsAndModifiers.Length() - 1 == aIndex;
3702 nsTArray<AlternativeCharCode>& altArray =
3703 aKeyboardEvent.mAlternativeCharCodes;
3705 // Set charCode and adjust modifier state for every eKeyPress event.
3706 // This is not necessary for the other keyboard events because the other
3707 // keyboard events shouldn't have non-zero charCode value and should have
3708 // current modifier state.
3709 if (aKeyboardEvent.mMessage == eKeyPress && skipUniChars <= aIndex) {
3710 // XXX Modifying modifier state of aKeyboardEvent is illegal, but no way
3711 // to set different modifier state per keypress event except this
3712 // hack. Note that ideally, dead key should cause composition events
3713 // instead of keypress events, though.
3714 if (aIndex - skipUniChars < mInputtingStringAndModifiers.Length()) {
3715 ModifierKeyState modKeyState(mModKeyState);
3716 // If key in combination with Alt and/or Ctrl produces a different
3717 // character than without them then do not report these flags
3718 // because it is separate keyboard layout shift state. If dead-key
3719 // and base character does not produce a valid composite character
3720 // then both produced dead-key character and following base
3721 // character may have different modifier flags, too.
3722 modKeyState.Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT |
3723 MODIFIER_ALTGRAPH | MODIFIER_CAPSLOCK);
3724 modKeyState.Set(
3725 mInputtingStringAndModifiers.ModifiersAt(aIndex - skipUniChars));
3726 modKeyState.InitInputEvent(aKeyboardEvent);
3727 MOZ_LOG(gKeyLog, LogLevel::Info,
3728 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3729 "setting %uth modifier state to %s",
3730 this, aIndex + 1, ToString(modKeyState).get()));
3732 uint16_t uniChar =
3733 mInputtingStringAndModifiers.CharAt(aIndex - skipUniChars);
3735 // The mCharCode was set from mKeyValue. However, for example, when Ctrl key
3736 // is pressed, its value should indicate an ASCII character for backward
3737 // compatibility rather than inputting character without the modifiers.
3738 // Therefore, we need to modify mCharCode value here.
3739 aKeyboardEvent.SetCharCode(uniChar);
3740 MOZ_LOG(gKeyLog, LogLevel::Info,
3741 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3742 "setting %uth charCode to %s",
3743 this, aIndex + 1, GetCharacterCodeName(uniChar).get()));
3746 // We need to append alterntaive charCode values:
3747 // - if the event is eKeyPress, we need to append for the index because
3748 // eKeyPress event is dispatched for every character inputted by a
3749 // key press.
3750 // - if the event is not eKeyPress, we need to append for all characters
3751 // inputted by the key press because the other keyboard events (e.g.,
3752 // eKeyDown are eKeyUp) are fired only once for a key press.
3753 size_t count;
3754 if (aKeyboardEvent.mMessage == eKeyPress) {
3755 // Basically, append alternative charCode values only for the index.
3756 count = 1;
3757 // However, if it's the last eKeyPress event but different shift state
3758 // can input longer string, the last eKeyPress event should have all
3759 // remaining alternative charCode values.
3760 if (isLastIndex) {
3761 count = longestLength - aIndex;
3763 } else {
3764 count = longestLength;
3766 for (size_t i = 0; i < count; ++i) {
3767 uint16_t shiftedChar = 0, unshiftedChar = 0;
3768 if (skipShiftedChars <= aIndex + i) {
3769 shiftedChar = mShiftedString.CharAt(aIndex + i - skipShiftedChars);
3771 if (skipUnshiftedChars <= aIndex + i) {
3772 unshiftedChar = mUnshiftedString.CharAt(aIndex + i - skipUnshiftedChars);
3775 if (shiftedChar || unshiftedChar) {
3776 AlternativeCharCode chars(unshiftedChar, shiftedChar);
3777 altArray.AppendElement(chars);
3780 if (!isLastIndex) {
3781 continue;
3784 if (mUnshiftedLatinChar || mShiftedLatinChar) {
3785 AlternativeCharCode chars(mUnshiftedLatinChar, mShiftedLatinChar);
3786 altArray.AppendElement(chars);
3789 // Typically, following virtual keycodes are used for a key which can
3790 // input the character. However, these keycodes are also used for
3791 // other keys on some keyboard layout. E.g., in spite of Shift+'1'
3792 // inputs '+' on Thai keyboard layout, a key which is at '=/+'
3793 // key on ANSI keyboard layout is VK_OEM_PLUS. Native applications
3794 // handle it as '+' key if Ctrl key is pressed.
3795 char16_t charForOEMKeyCode = 0;
3796 switch (mVirtualKeyCode) {
3797 case VK_OEM_PLUS:
3798 charForOEMKeyCode = '+';
3799 break;
3800 case VK_OEM_COMMA:
3801 charForOEMKeyCode = ',';
3802 break;
3803 case VK_OEM_MINUS:
3804 charForOEMKeyCode = '-';
3805 break;
3806 case VK_OEM_PERIOD:
3807 charForOEMKeyCode = '.';
3808 break;
3810 if (charForOEMKeyCode && charForOEMKeyCode != mUnshiftedString.CharAt(0) &&
3811 charForOEMKeyCode != mShiftedString.CharAt(0) &&
3812 charForOEMKeyCode != mUnshiftedLatinChar &&
3813 charForOEMKeyCode != mShiftedLatinChar) {
3814 AlternativeCharCode OEMChars(charForOEMKeyCode, charForOEMKeyCode);
3815 altArray.AppendElement(OEMChars);
3820 /*****************************************************************************
3821 * mozilla::widget::KeyboardLayout
3822 *****************************************************************************/
3824 KeyboardLayout* KeyboardLayout::sInstance = nullptr;
3825 nsIUserIdleServiceInternal* KeyboardLayout::sIdleService = nullptr;
3827 // static
3828 KeyboardLayout* KeyboardLayout::GetInstance() {
3829 if (!sInstance) {
3830 sInstance = new KeyboardLayout();
3831 nsCOMPtr<nsIUserIdleServiceInternal> idleService =
3832 do_GetService("@mozilla.org/widget/useridleservice;1");
3833 // The refcount will be decreased at shut down.
3834 sIdleService = idleService.forget().take();
3836 return sInstance;
3839 // static
3840 void KeyboardLayout::Shutdown() {
3841 delete sInstance;
3842 sInstance = nullptr;
3843 NS_IF_RELEASE(sIdleService);
3846 // static
3847 void KeyboardLayout::NotifyIdleServiceOfUserActivity() {
3848 sIdleService->ResetIdleTimeOut(0);
3851 KeyboardLayout::KeyboardLayout()
3852 : mKeyboardLayout(0),
3853 mIsOverridden(false),
3854 mIsPendingToRestoreKeyboardLayout(false),
3855 mHasAltGr(false) {
3856 mDeadKeyTableListHead = nullptr;
3857 // A dead key sequence should be made from up to 5 keys. Therefore, 4 is
3858 // enough and makes sense because the item is uint8_t.
3859 // (Although, even if it's possible to be 6 keys or more in a sequence,
3860 // this array will be re-allocated).
3861 mActiveDeadKeys.SetCapacity(4);
3862 mDeadKeyShiftStates.SetCapacity(4);
3864 // NOTE: LoadLayout() should be called via OnLayoutChange().
3867 KeyboardLayout::~KeyboardLayout() { ReleaseDeadKeyTables(); }
3869 bool KeyboardLayout::IsPrintableCharKey(uint8_t aVirtualKey) {
3870 return GetKeyIndex(aVirtualKey) >= 0;
3873 WORD KeyboardLayout::ComputeScanCodeForVirtualKeyCode(
3874 uint8_t aVirtualKeyCode) const {
3875 return static_cast<WORD>(
3876 ::MapVirtualKeyEx(aVirtualKeyCode, MAPVK_VK_TO_VSC, GetLayout()));
3879 bool KeyboardLayout::IsDeadKey(uint8_t aVirtualKey,
3880 const ModifierKeyState& aModKeyState) const {
3881 int32_t virtualKeyIndex = GetKeyIndex(aVirtualKey);
3883 // XXX KeyboardLayout class doesn't support unusual keyboard layout which
3884 // maps some function keys as dead keys.
3885 if (virtualKeyIndex < 0) {
3886 return false;
3889 return mVirtualKeys[virtualKeyIndex].IsDeadKey(
3890 VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()));
3893 bool KeyboardLayout::IsSysKey(uint8_t aVirtualKey,
3894 const ModifierKeyState& aModKeyState) const {
3895 // If Alt key is not pressed, it's never a system key combination.
3896 // Additionally, if Ctrl key is pressed, it's never a system key combination
3897 // too.
3898 // FYI: Windows logo key state won't affect if it's a system key.
3899 if (!aModKeyState.IsAlt() || aModKeyState.IsControl()) {
3900 return false;
3903 int32_t virtualKeyIndex = GetKeyIndex(aVirtualKey);
3904 if (virtualKeyIndex < 0) {
3905 return true;
3908 UniCharsAndModifiers inputCharsAndModifiers =
3909 GetUniCharsAndModifiers(aVirtualKey, aModKeyState);
3910 if (inputCharsAndModifiers.IsEmpty()) {
3911 return true;
3914 // If the Alt key state isn't consumed, that means that the key with Alt
3915 // doesn't cause text input. So, the combination is a system key.
3916 return !!(inputCharsAndModifiers.ModifiersAt(0) & MODIFIER_ALT);
3919 void KeyboardLayout::InitNativeKey(NativeKey& aNativeKey) {
3920 if (mIsPendingToRestoreKeyboardLayout) {
3921 LoadLayout(::GetKeyboardLayout(0));
3924 // If the aNativeKey is initialized with WM_CHAR, the key information
3925 // should be discarded because mKeyValue should have the string to be
3926 // inputted.
3927 if (aNativeKey.mMsg.message == WM_CHAR) {
3928 char16_t ch = static_cast<char16_t>(aNativeKey.mMsg.wParam);
3929 // But don't set key value as printable key if the character is a control
3930 // character such as 0x0D at pressing Enter key.
3931 if (!NativeKey::IsControlChar(ch)) {
3932 aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
3933 Modifiers modifiers =
3934 aNativeKey.GetModifiers() & ~(MODIFIER_ALT | MODIFIER_CONTROL);
3935 aNativeKey.mCommittedCharsAndModifiers.Append(ch, modifiers);
3936 return;
3940 // If the aNativeKey is in a sequence to input a Unicode character with
3941 // Alt + numpad keys, we should just set the number as the inputting charcter.
3942 // Note that we should compute the key value from the virtual key code
3943 // because they may be mapped to alphabets, but they should be treated as
3944 // Alt + [0-9] even by web apps.
3945 // However, we shouldn't touch the key value if given virtual key code is
3946 // not a printable key because it may be synthesized by 3rd party utility
3947 // or just NumLock is unlocked and user tries to use shortcut key. In the
3948 // latter case, we cannot solve the conflict issue with Alt+foo shortcut key
3949 // and inputting a Unicode scalar value like reported to bug 1606655, though,
3950 // I have no better idea. Perhaps, `Alt` shouldn't be used for shortcut key
3951 // combination on Windows.
3952 if (aNativeKey.MaybeTypingUnicodeScalarValue() &&
3953 KeyboardLayout::IsPrintableCharKey(aNativeKey.mVirtualKeyCode)) {
3954 // If the key code value is mapped to a Numpad key, let's compute the key
3955 // value with it. This is same behavior as Chrome. In strictly speaking,
3956 // I think that the else block's computation is better because it seems
3957 // that Windows does not refer virtual key code value, but we should avoid
3958 // web-compat issues.
3959 char16_t num = '0';
3960 if (aNativeKey.mVirtualKeyCode >= VK_NUMPAD0 &&
3961 aNativeKey.mVirtualKeyCode <= VK_NUMPAD9) {
3962 num = '0' + aNativeKey.mVirtualKeyCode - VK_NUMPAD0;
3964 // Otherwise, let's use fake key value for making never match with
3965 // mnemonic.
3966 else {
3967 switch (aNativeKey.mScanCode) {
3968 case 0x0052: // Numpad0
3969 num = '0';
3970 break;
3971 case 0x004F: // Numpad1
3972 num = '1';
3973 break;
3974 case 0x0050: // Numpad2
3975 num = '2';
3976 break;
3977 case 0x0051: // Numpad3
3978 num = '3';
3979 break;
3980 case 0x004B: // Numpad4
3981 num = '4';
3982 break;
3983 case 0x004C: // Numpad5
3984 num = '5';
3985 break;
3986 case 0x004D: // Numpad6
3987 num = '6';
3988 break;
3989 case 0x0047: // Numpad7
3990 num = '7';
3991 break;
3992 case 0x0048: // Numpad8
3993 num = '8';
3994 break;
3995 case 0x0049: // Numpad9
3996 num = '9';
3997 break;
3998 default:
3999 MOZ_ASSERT_UNREACHABLE(
4000 "IsTypingUnicodeScalarValue() must have returned true for wrong "
4001 "scancode");
4002 break;
4005 aNativeKey.mCommittedCharsAndModifiers.Append(num,
4006 aNativeKey.GetModifiers());
4007 aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
4008 return;
4011 // When it's followed by non-dead char message(s) for printable character(s),
4012 // aNativeKey should dispatch eKeyPress events for them rather than
4013 // information from keyboard layout because respecting WM_(SYS)CHAR messages
4014 // guarantees that we can always input characters which is expected by
4015 // the user even if the user uses odd keyboard layout.
4016 // Or, when it was followed by non-dead char message for a printable character
4017 // but it's gone at removing the message from the queue, let's treat it
4018 // as a key inputting empty string.
4019 if (aNativeKey.IsFollowedByPrintableCharOrSysCharMessage() ||
4020 aNativeKey.mCharMessageHasGone) {
4021 MOZ_ASSERT(!aNativeKey.IsCharMessage(aNativeKey.mMsg));
4022 if (aNativeKey.IsFollowedByPrintableCharOrSysCharMessage()) {
4023 // Initialize mCommittedCharsAndModifiers with following char messages.
4024 aNativeKey.InitCommittedCharsAndModifiersWithFollowingCharMessages();
4025 MOZ_ASSERT(!aNativeKey.mCommittedCharsAndModifiers.IsEmpty());
4027 // Currently, we are doing a ugly hack to keypress events to cause
4028 // inputting character even if Ctrl or Alt key is pressed, that is, we
4029 // remove Ctrl and Alt modifier state from keypress event. However, for
4030 // example, Ctrl+Space which causes ' ' of WM_CHAR message never causes
4031 // keypress event whose ctrlKey is true. For preventing this problem,
4032 // we should mark as not removable if Ctrl or Alt key does not cause
4033 // changing inputting character.
4034 if (IsPrintableCharKey(aNativeKey.mOriginalVirtualKeyCode) &&
4035 (aNativeKey.IsControl() ^ aNativeKey.IsAlt())) {
4036 ModifierKeyState state = aNativeKey.ModifierKeyStateRef();
4037 state.Unset(MODIFIER_ALT | MODIFIER_CONTROL);
4038 UniCharsAndModifiers charsWithoutModifier =
4039 GetUniCharsAndModifiers(aNativeKey.GenericVirtualKeyCode(), state);
4040 aNativeKey.mCanIgnoreModifierStateAtKeyPress =
4041 !charsWithoutModifier.UniCharsEqual(
4042 aNativeKey.mCommittedCharsAndModifiers);
4044 } else {
4045 aNativeKey.mCommittedCharsAndModifiers.Clear();
4047 aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
4049 // If it's not in dead key sequence, we don't need to do anymore here.
4050 if (!IsInDeadKeySequence()) {
4051 return;
4054 // If it's in dead key sequence and dead char is inputted as is, we need to
4055 // set the previous modifier state which is stored when preceding dead key
4056 // is pressed.
4057 UniCharsAndModifiers deadChars = GetDeadUniCharsAndModifiers();
4058 aNativeKey.mCommittedCharsAndModifiers.OverwriteModifiersIfBeginsWith(
4059 deadChars);
4060 // Finish the dead key sequence.
4061 DeactivateDeadKeyState();
4062 return;
4065 // If it's a dead key, aNativeKey will be initialized by
4066 // MaybeInitNativeKeyAsDeadKey().
4067 if (MaybeInitNativeKeyAsDeadKey(aNativeKey)) {
4068 return;
4071 // If the key is not a usual printable key, KeyboardLayout class assume that
4072 // it's not cause dead char nor printable char. Therefore, there are nothing
4073 // to do here fore such keys (e.g., function keys).
4074 // However, this should keep dead key state even if non-printable key is
4075 // pressed during a dead key sequence.
4076 if (!IsPrintableCharKey(aNativeKey.mOriginalVirtualKeyCode)) {
4077 return;
4080 MOZ_ASSERT(aNativeKey.mOriginalVirtualKeyCode != VK_PACKET,
4081 "At handling VK_PACKET, we shouldn't refer keyboard layout");
4082 MOZ_ASSERT(
4083 aNativeKey.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING,
4084 "Printable key's key name index must be KEY_NAME_INDEX_USE_STRING");
4086 // If it's in dead key handling and the pressed key causes a composite
4087 // character, aNativeKey will be initialized by
4088 // MaybeInitNativeKeyWithCompositeChar().
4089 if (MaybeInitNativeKeyWithCompositeChar(aNativeKey)) {
4090 return;
4093 UniCharsAndModifiers baseChars = GetUniCharsAndModifiers(aNativeKey);
4095 // If the key press isn't related to any dead keys, initialize aNativeKey
4096 // with the characters which should be caused by the key.
4097 if (!IsInDeadKeySequence()) {
4098 aNativeKey.mCommittedCharsAndModifiers = baseChars;
4099 return;
4102 // If the key doesn't cause a composite character with preceding dead key,
4103 // initialize aNativeKey with the dead-key character followed by current
4104 // key's character.
4105 UniCharsAndModifiers deadChars = GetDeadUniCharsAndModifiers();
4106 aNativeKey.mCommittedCharsAndModifiers = deadChars + baseChars;
4107 if (aNativeKey.IsKeyDownMessage()) {
4108 DeactivateDeadKeyState();
4112 bool KeyboardLayout::MaybeInitNativeKeyAsDeadKey(NativeKey& aNativeKey) {
4113 // Only when it's not in dead key sequence, we can trust IsDeadKey() result.
4114 if (!IsInDeadKeySequence() && !IsDeadKey(aNativeKey)) {
4115 return false;
4118 // When keydown message is followed by a dead char message, it should be
4119 // initialized as dead key.
4120 bool isDeadKeyDownEvent =
4121 aNativeKey.IsKeyDownMessage() && aNativeKey.IsFollowedByDeadCharMessage();
4123 // When keyup message is received, let's check if it's one of preceding
4124 // dead keys because keydown message order and keyup message order may be
4125 // different.
4126 bool isDeadKeyUpEvent =
4127 !aNativeKey.IsKeyDownMessage() &&
4128 mActiveDeadKeys.Contains(aNativeKey.GenericVirtualKeyCode());
4130 if (isDeadKeyDownEvent || isDeadKeyUpEvent) {
4131 ActivateDeadKeyState(aNativeKey);
4132 // Any dead key events don't generate characters. So, a dead key should
4133 // cause only keydown event and keyup event whose KeyboardEvent.key
4134 // values are "Dead".
4135 aNativeKey.mCommittedCharsAndModifiers.Clear();
4136 aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_Dead;
4137 return true;
4140 // At keydown message handling, we need to forget the first dead key
4141 // because there is no guarantee coming WM_KEYUP for the second dead
4142 // key before next WM_KEYDOWN. E.g., due to auto key repeat or pressing
4143 // another dead key before releasing current key. Therefore, we can
4144 // set only a character for current key for keyup event.
4145 if (!IsInDeadKeySequence()) {
4146 aNativeKey.mCommittedCharsAndModifiers =
4147 GetUniCharsAndModifiers(aNativeKey);
4148 return true;
4151 // When non-printable key event comes during a dead key sequence, that must
4152 // be a modifier key event. So, such events shouldn't be handled as a part
4153 // of the dead key sequence.
4154 if (!IsDeadKey(aNativeKey)) {
4155 return false;
4158 // FYI: Following code may run when the user doesn't input text actually
4159 // but the key sequence is a dead key sequence. For example,
4160 // ` -> Ctrl+` with Spanish keyboard layout. Let's keep using this
4161 // complicated code for now because this runs really rarely.
4163 // Dead key followed by another dead key may cause a composed character
4164 // (e.g., "Russian - Mnemonic" keyboard layout's 's' -> 'c').
4165 if (MaybeInitNativeKeyWithCompositeChar(aNativeKey)) {
4166 return true;
4169 // Otherwise, dead key followed by another dead key causes inputting both
4170 // character.
4171 UniCharsAndModifiers prevDeadChars = GetDeadUniCharsAndModifiers();
4172 UniCharsAndModifiers newChars = GetUniCharsAndModifiers(aNativeKey);
4173 // But keypress events should be fired for each committed character.
4174 aNativeKey.mCommittedCharsAndModifiers = prevDeadChars + newChars;
4175 if (aNativeKey.IsKeyDownMessage()) {
4176 DeactivateDeadKeyState();
4178 return true;
4181 bool KeyboardLayout::MaybeInitNativeKeyWithCompositeChar(
4182 NativeKey& aNativeKey) {
4183 if (!IsInDeadKeySequence()) {
4184 return false;
4187 if (NS_WARN_IF(!IsPrintableCharKey(aNativeKey.mOriginalVirtualKeyCode))) {
4188 return false;
4191 UniCharsAndModifiers baseChars = GetUniCharsAndModifiers(aNativeKey);
4192 if (baseChars.IsEmpty() || !baseChars.CharAt(0)) {
4193 return false;
4196 char16_t compositeChar = GetCompositeChar(baseChars.CharAt(0));
4197 if (!compositeChar) {
4198 return false;
4201 // Active dead-key and base character does produce exactly one composite
4202 // character.
4203 aNativeKey.mCommittedCharsAndModifiers.Append(compositeChar,
4204 baseChars.ModifiersAt(0));
4205 if (aNativeKey.IsKeyDownMessage()) {
4206 DeactivateDeadKeyState();
4208 return true;
4211 UniCharsAndModifiers KeyboardLayout::GetUniCharsAndModifiers(
4212 uint8_t aVirtualKey, VirtualKey::ShiftState aShiftState) const {
4213 UniCharsAndModifiers result;
4214 int32_t key = GetKeyIndex(aVirtualKey);
4215 if (key < 0) {
4216 return result;
4218 return mVirtualKeys[key].GetUniChars(aShiftState);
4221 UniCharsAndModifiers KeyboardLayout::GetDeadUniCharsAndModifiers() const {
4222 MOZ_RELEASE_ASSERT(mActiveDeadKeys.Length() == mDeadKeyShiftStates.Length());
4224 if (NS_WARN_IF(mActiveDeadKeys.IsEmpty())) {
4225 return UniCharsAndModifiers();
4228 UniCharsAndModifiers result;
4229 for (size_t i = 0; i < mActiveDeadKeys.Length(); ++i) {
4230 result +=
4231 GetUniCharsAndModifiers(mActiveDeadKeys[i], mDeadKeyShiftStates[i]);
4233 return result;
4236 char16_t KeyboardLayout::GetCompositeChar(char16_t aBaseChar) const {
4237 if (NS_WARN_IF(mActiveDeadKeys.IsEmpty())) {
4238 return 0;
4240 // XXX Currently, we don't support computing a composite character with
4241 // two or more dead keys since it needs big table for supporting
4242 // long chained dead keys. However, this should be a minor bug
4243 // because this runs only when the latest keydown event does not cause
4244 // WM_(SYS)CHAR messages. So, when user wants to input a character,
4245 // this path never runs.
4246 if (mActiveDeadKeys.Length() > 1) {
4247 return 0;
4249 int32_t key = GetKeyIndex(mActiveDeadKeys[0]);
4250 if (key < 0) {
4251 return 0;
4253 return mVirtualKeys[key].GetCompositeChar(mDeadKeyShiftStates[0], aBaseChar);
4256 // static
4257 HKL KeyboardLayout::GetActiveLayout() { return GetInstance()->mKeyboardLayout; }
4259 // static
4260 nsCString KeyboardLayout::GetActiveLayoutName() {
4261 return GetInstance()->GetLayoutName(GetActiveLayout());
4264 static bool IsValidKeyboardLayoutsChild(const nsAString& aChildName) {
4265 if (aChildName.Length() != 8) {
4266 return false;
4268 for (size_t i = 0; i < aChildName.Length(); i++) {
4269 if ((aChildName[i] >= '0' && aChildName[i] <= '9') ||
4270 (aChildName[i] >= 'a' && aChildName[i] <= 'f') ||
4271 (aChildName[i] >= 'A' && aChildName[i] <= 'F')) {
4272 continue;
4274 return false;
4276 return true;
4279 nsCString KeyboardLayout::GetLayoutName(HKL aLayout) const {
4280 const wchar_t kKeyboardLayouts[] =
4281 L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\";
4282 uint16_t language = reinterpret_cast<uintptr_t>(aLayout) & 0xFFFF;
4283 uint16_t layout = (reinterpret_cast<uintptr_t>(aLayout) >> 16) & 0xFFFF;
4284 // If the layout is less than 0xA000XXXX (normal keyboard layout for the
4285 // language) or 0xEYYYXXXX (IMM-IME), we can retrieve its name simply.
4286 if (layout < 0xA000 || (layout & 0xF000) == 0xE000) {
4287 nsAutoString key(kKeyboardLayouts);
4288 key.AppendPrintf("%08" PRIXPTR, layout < 0xA000
4289 ? layout
4290 : reinterpret_cast<uintptr_t>(aLayout));
4291 wchar_t buf[256];
4292 if (NS_WARN_IF(!WinUtils::GetRegistryKey(
4293 HKEY_LOCAL_MACHINE, key.get(), L"Layout Text", buf, sizeof(buf)))) {
4294 return "No name or too long name"_ns;
4296 return NS_ConvertUTF16toUTF8(buf);
4299 if (NS_WARN_IF((layout & 0xF000) != 0xF000)) {
4300 nsCString result;
4301 result.AppendPrintf("Odd HKL: 0x%08" PRIXPTR,
4302 reinterpret_cast<uintptr_t>(aLayout));
4303 return result;
4306 // Otherwise, we need to walk the registry under "Keyboard Layouts".
4307 nsCOMPtr<nsIWindowsRegKey> regKey =
4308 do_CreateInstance("@mozilla.org/windows-registry-key;1");
4309 if (NS_WARN_IF(!regKey)) {
4310 return ""_ns;
4312 nsresult rv =
4313 regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
4314 nsString(kKeyboardLayouts), nsIWindowsRegKey::ACCESS_READ);
4315 if (NS_WARN_IF(NS_FAILED(rv))) {
4316 return ""_ns;
4318 uint32_t childCount = 0;
4319 if (NS_WARN_IF(NS_FAILED(regKey->GetChildCount(&childCount))) ||
4320 NS_WARN_IF(!childCount)) {
4321 return ""_ns;
4323 for (uint32_t i = 0; i < childCount; i++) {
4324 nsAutoString childName;
4325 if (NS_WARN_IF(NS_FAILED(regKey->GetChildName(i, childName))) ||
4326 !IsValidKeyboardLayoutsChild(childName)) {
4327 continue;
4329 uint32_t childNum = static_cast<uint32_t>(childName.ToInteger64(&rv, 16));
4330 if (NS_WARN_IF(NS_FAILED(rv))) {
4331 continue;
4333 // Ignore normal keyboard layouts for each language.
4334 if (childNum <= 0xFFFF) {
4335 continue;
4337 // If it doesn't start with 'A' nor 'a', language should be matched.
4338 if ((childNum & 0xFFFF) != language &&
4339 (childNum & 0xF0000000) != 0xA0000000) {
4340 continue;
4342 // Then, the child should have "Layout Id" which is "YYY" of 0xFYYYXXXX.
4343 nsAutoString key(kKeyboardLayouts);
4344 key += childName;
4345 wchar_t buf[256];
4346 if (NS_WARN_IF(!WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE, key.get(),
4347 L"Layout Id", buf, sizeof(buf)))) {
4348 continue;
4350 uint16_t layoutId = wcstol(buf, nullptr, 16);
4351 if (layoutId != (layout & 0x0FFF)) {
4352 continue;
4354 if (NS_WARN_IF(!WinUtils::GetRegistryKey(
4355 HKEY_LOCAL_MACHINE, key.get(), L"Layout Text", buf, sizeof(buf)))) {
4356 continue;
4358 return NS_ConvertUTF16toUTF8(buf);
4360 return ""_ns;
4363 void KeyboardLayout::LoadLayout(HKL aLayout) {
4364 mIsPendingToRestoreKeyboardLayout = false;
4366 if (mKeyboardLayout == aLayout) {
4367 return;
4370 mKeyboardLayout = aLayout;
4371 mHasAltGr = false;
4373 MOZ_LOG(gKeyLog, LogLevel::Info,
4374 ("KeyboardLayout::LoadLayout(aLayout=0x%p (%s))", aLayout,
4375 GetLayoutName(aLayout).get()));
4377 BYTE kbdState[256];
4378 memset(kbdState, 0, sizeof(kbdState));
4380 BYTE originalKbdState[256];
4381 // Bitfield with all shift states that have at least one dead-key.
4382 uint16_t shiftStatesWithDeadKeys = 0;
4383 // Bitfield with all shift states that produce any possible dead-key base
4384 // characters.
4385 uint16_t shiftStatesWithBaseChars = 0;
4387 mActiveDeadKeys.Clear();
4388 mDeadKeyShiftStates.Clear();
4390 ReleaseDeadKeyTables();
4392 ::GetKeyboardState(originalKbdState);
4394 // For each shift state gather all printable characters that are produced
4395 // for normal case when no any dead-key is active.
4397 for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) {
4398 VirtualKey::FillKbdState(kbdState, shiftState);
4399 bool isAltGr = VirtualKey::IsAltGrIndex(shiftState);
4400 for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
4401 int32_t vki = GetKeyIndex(virtualKey);
4402 if (vki < 0) {
4403 continue;
4405 NS_ASSERTION(uint32_t(vki) < ArrayLength(mVirtualKeys), "invalid index");
4406 char16_t uniChars[5];
4407 int32_t ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)uniChars,
4408 ArrayLength(uniChars), 0, mKeyboardLayout);
4409 // dead-key
4410 if (ret < 0) {
4411 shiftStatesWithDeadKeys |= (1 << shiftState);
4412 // Repeat dead-key to deactivate it and get its character
4413 // representation.
4414 char16_t deadChar[2];
4415 ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)deadChar,
4416 ArrayLength(deadChar), 0, mKeyboardLayout);
4417 NS_ASSERTION(ret == 2, "Expecting twice repeated dead-key character");
4418 mVirtualKeys[vki].SetDeadChar(shiftState, deadChar[0]);
4420 MOZ_LOG(gKeyLog, LogLevel::Verbose,
4421 (" %s (%d): DeadChar(%s, %s) (ret=%d)",
4422 kVirtualKeyName[virtualKey], vki,
4423 GetShiftStateName(shiftState).get(),
4424 GetCharacterCodeNames(deadChar, 1).get(), ret));
4425 } else {
4426 if (ret == 1) {
4427 // dead-key can pair only with exactly one base character.
4428 shiftStatesWithBaseChars |= (1 << shiftState);
4430 mVirtualKeys[vki].SetNormalChars(shiftState, uniChars, ret);
4431 MOZ_LOG(gKeyLog, LogLevel::Verbose,
4432 (" %s (%d): NormalChar(%s, %s) (ret=%d)",
4433 kVirtualKeyName[virtualKey], vki,
4434 GetShiftStateName(shiftState).get(),
4435 GetCharacterCodeNames(uniChars, ret).get(), ret));
4438 // If the key inputs at least one character with AltGr modifier,
4439 // check if AltGr changes inputting character. If it does, mark
4440 // this keyboard layout has AltGr modifier actually.
4441 if (!mHasAltGr && ret > 0 && isAltGr &&
4442 mVirtualKeys[vki].IsChangedByAltGr(shiftState)) {
4443 mHasAltGr = true;
4444 MOZ_LOG(gKeyLog, LogLevel::Info,
4445 (" Found a key (%s) changed by AltGr: %s -> %s (%s) (ret=%d)",
4446 kVirtualKeyName[virtualKey],
4447 GetCharacterCodeNames(
4448 mVirtualKeys[vki].GetNativeUniChars(
4449 shiftState - VirtualKey::ShiftStateIndex::eAltGr))
4450 .get(),
4451 GetCharacterCodeNames(
4452 mVirtualKeys[vki].GetNativeUniChars(shiftState))
4453 .get(),
4454 GetShiftStateName(shiftState).get(), ret));
4459 // Now process each dead-key to find all its base characters and resulting
4460 // composite characters.
4461 for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) {
4462 if (!(shiftStatesWithDeadKeys & (1 << shiftState))) {
4463 continue;
4466 VirtualKey::FillKbdState(kbdState, shiftState);
4468 for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
4469 int32_t vki = GetKeyIndex(virtualKey);
4470 if (vki >= 0 && mVirtualKeys[vki].IsDeadKey(shiftState)) {
4471 DeadKeyEntry deadKeyArray[256];
4472 int32_t n = GetDeadKeyCombinations(
4473 virtualKey, kbdState, shiftStatesWithBaseChars, deadKeyArray,
4474 ArrayLength(deadKeyArray));
4475 const DeadKeyTable* dkt =
4476 mVirtualKeys[vki].MatchingDeadKeyTable(deadKeyArray, n);
4477 if (!dkt) {
4478 dkt = AddDeadKeyTable(deadKeyArray, n);
4480 mVirtualKeys[vki].AttachDeadKeyTable(shiftState, dkt);
4485 ::SetKeyboardState(originalKbdState);
4487 if (MOZ_LOG_TEST(gKeyLog, LogLevel::Verbose)) {
4488 static const UINT kExtendedScanCode[] = {0x0000, 0xE000};
4489 static const UINT kMapType = MAPVK_VSC_TO_VK_EX;
4490 MOZ_LOG(gKeyLog, LogLevel::Verbose,
4491 ("Logging virtual keycode values for scancode (0x%p)...",
4492 mKeyboardLayout));
4493 for (uint32_t i = 0; i < ArrayLength(kExtendedScanCode); i++) {
4494 for (uint32_t j = 1; j <= 0xFF; j++) {
4495 UINT scanCode = kExtendedScanCode[i] + j;
4496 UINT virtualKeyCode =
4497 ::MapVirtualKeyEx(scanCode, kMapType, mKeyboardLayout);
4498 MOZ_LOG(gKeyLog, LogLevel::Verbose,
4499 ("0x%04X, %s", scanCode, kVirtualKeyName[virtualKeyCode]));
4504 MOZ_LOG(gKeyLog, LogLevel::Info,
4505 (" AltGr key is %s in %s", mHasAltGr ? "found" : "not found",
4506 GetLayoutName(aLayout).get()));
4509 inline int32_t KeyboardLayout::GetKeyIndex(uint8_t aVirtualKey) {
4510 // Currently these 68 (NS_NUM_OF_KEYS) virtual keys are assumed
4511 // to produce visible representation:
4512 // 0x20 - VK_SPACE ' '
4513 // 0x30..0x39 '0'..'9'
4514 // 0x41..0x5A 'A'..'Z'
4515 // 0x60..0x69 '0'..'9' on numpad
4516 // 0x6A - VK_MULTIPLY '*' on numpad
4517 // 0x6B - VK_ADD '+' on numpad
4518 // 0x6D - VK_SUBTRACT '-' on numpad
4519 // 0x6E - VK_DECIMAL '.' on numpad
4520 // 0x6F - VK_DIVIDE '/' on numpad
4521 // 0x6E - VK_DECIMAL '.'
4522 // 0xBA - VK_OEM_1 ';:' for US
4523 // 0xBB - VK_OEM_PLUS '+' any country
4524 // 0xBC - VK_OEM_COMMA ',' any country
4525 // 0xBD - VK_OEM_MINUS '-' any country
4526 // 0xBE - VK_OEM_PERIOD '.' any country
4527 // 0xBF - VK_OEM_2 '/?' for US
4528 // 0xC0 - VK_OEM_3 '`~' for US
4529 // 0xC1 - VK_ABNT_C1 '/?' for Brazilian
4530 // 0xC2 - VK_ABNT_C2 separator key on numpad (Brazilian or JIS for Mac)
4531 // 0xDB - VK_OEM_4 '[{' for US
4532 // 0xDC - VK_OEM_5 '\|' for US
4533 // 0xDD - VK_OEM_6 ']}' for US
4534 // 0xDE - VK_OEM_7 ''"' for US
4535 // 0xDF - VK_OEM_8
4536 // 0xE1 - no name
4537 // 0xE2 - VK_OEM_102 '\_' for JIS
4538 // 0xE3 - no name
4539 // 0xE4 - no name
4541 static const int8_t xlat[256] = {
4542 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
4543 //-----------------------------------------------------------------------
4544 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00
4545 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10
4546 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20
4547 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, // 30
4548 -1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 40
4549 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, -1, -1, // 50
4550 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, -1, 49, 50, 51, // 60
4551 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 70
4552 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
4553 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90
4554 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0
4555 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 53, 54, 55, 56, 57, // B0
4556 58, 59, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0
4557 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 61, 62, 63, 64, 65, // D0
4558 -1, 66, 67, 68, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0
4559 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // F0
4562 return xlat[aVirtualKey];
4565 int KeyboardLayout::CompareDeadKeyEntries(const void* aArg1, const void* aArg2,
4566 void*) {
4567 const DeadKeyEntry* arg1 = static_cast<const DeadKeyEntry*>(aArg1);
4568 const DeadKeyEntry* arg2 = static_cast<const DeadKeyEntry*>(aArg2);
4570 return arg1->BaseChar - arg2->BaseChar;
4573 const DeadKeyTable* KeyboardLayout::AddDeadKeyTable(
4574 const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries) {
4575 DeadKeyTableListEntry* next = mDeadKeyTableListHead;
4577 const size_t bytes = offsetof(DeadKeyTableListEntry, data) +
4578 DeadKeyTable::SizeInBytes(aEntries);
4579 uint8_t* p = new uint8_t[bytes];
4581 mDeadKeyTableListHead = reinterpret_cast<DeadKeyTableListEntry*>(p);
4582 mDeadKeyTableListHead->next = next;
4584 DeadKeyTable* dkt =
4585 reinterpret_cast<DeadKeyTable*>(mDeadKeyTableListHead->data);
4587 dkt->Init(aDeadKeyArray, aEntries);
4589 return dkt;
4592 void KeyboardLayout::ReleaseDeadKeyTables() {
4593 while (mDeadKeyTableListHead) {
4594 uint8_t* p = reinterpret_cast<uint8_t*>(mDeadKeyTableListHead);
4595 mDeadKeyTableListHead = mDeadKeyTableListHead->next;
4597 delete[] p;
4601 bool KeyboardLayout::EnsureDeadKeyActive(bool aIsActive, uint8_t aDeadKey,
4602 const PBYTE aDeadKeyKbdState) {
4603 int32_t ret;
4604 do {
4605 char16_t dummyChars[5];
4606 ret =
4607 ::ToUnicodeEx(aDeadKey, 0, (PBYTE)aDeadKeyKbdState, (LPWSTR)dummyChars,
4608 ArrayLength(dummyChars), 0, mKeyboardLayout);
4609 // returned values:
4610 // <0 - Dead key state is active. The keyboard driver will wait for next
4611 // character.
4612 // 1 - Previous pressed key was a valid base character that produced
4613 // exactly one composite character.
4614 // >1 - Previous pressed key does not produce any composite characters.
4615 // Return dead-key character followed by base character(s).
4616 } while ((ret < 0) != aIsActive);
4618 return (ret < 0);
4621 void KeyboardLayout::ActivateDeadKeyState(const NativeKey& aNativeKey) {
4622 // Dead-key state should be activated at keydown.
4623 if (!aNativeKey.IsKeyDownMessage()) {
4624 return;
4627 mActiveDeadKeys.AppendElement(aNativeKey.mOriginalVirtualKeyCode);
4628 mDeadKeyShiftStates.AppendElement(aNativeKey.GetShiftState());
4631 void KeyboardLayout::DeactivateDeadKeyState() {
4632 if (mActiveDeadKeys.IsEmpty()) {
4633 return;
4636 BYTE kbdState[256];
4637 memset(kbdState, 0, sizeof(kbdState));
4639 // Assume that the last dead key can finish dead key sequence.
4640 VirtualKey::FillKbdState(kbdState, mDeadKeyShiftStates.LastElement());
4641 EnsureDeadKeyActive(false, mActiveDeadKeys.LastElement(), kbdState);
4642 mActiveDeadKeys.Clear();
4643 mDeadKeyShiftStates.Clear();
4646 bool KeyboardLayout::AddDeadKeyEntry(char16_t aBaseChar,
4647 char16_t aCompositeChar,
4648 DeadKeyEntry* aDeadKeyArray,
4649 uint32_t aEntries) {
4650 for (uint32_t index = 0; index < aEntries; index++) {
4651 if (aDeadKeyArray[index].BaseChar == aBaseChar) {
4652 return false;
4656 aDeadKeyArray[aEntries].BaseChar = aBaseChar;
4657 aDeadKeyArray[aEntries].CompositeChar = aCompositeChar;
4659 return true;
4662 uint32_t KeyboardLayout::GetDeadKeyCombinations(
4663 uint8_t aDeadKey, const PBYTE aDeadKeyKbdState,
4664 uint16_t aShiftStatesWithBaseChars, DeadKeyEntry* aDeadKeyArray,
4665 uint32_t aMaxEntries) {
4666 bool deadKeyActive = false;
4667 uint32_t entries = 0;
4668 BYTE kbdState[256];
4669 memset(kbdState, 0, sizeof(kbdState));
4671 for (uint32_t shiftState = 0; shiftState < 16; shiftState++) {
4672 if (!(aShiftStatesWithBaseChars & (1 << shiftState))) {
4673 continue;
4676 VirtualKey::FillKbdState(kbdState, shiftState);
4678 for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
4679 int32_t vki = GetKeyIndex(virtualKey);
4680 // Dead-key can pair only with such key that produces exactly one base
4681 // character.
4682 if (vki >= 0 &&
4683 mVirtualKeys[vki].GetNativeUniChars(shiftState).Length() == 1) {
4684 // Ensure dead-key is in active state, when it swallows entered
4685 // character and waits for the next pressed key.
4686 if (!deadKeyActive) {
4687 deadKeyActive = EnsureDeadKeyActive(true, aDeadKey, aDeadKeyKbdState);
4690 // Depending on the character the followed the dead-key, the keyboard
4691 // driver can produce one composite character, or a dead-key character
4692 // followed by a second character.
4693 char16_t compositeChars[5];
4694 int32_t ret =
4695 ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)compositeChars,
4696 ArrayLength(compositeChars), 0, mKeyboardLayout);
4697 switch (ret) {
4698 case 0:
4699 // This key combination does not produce any characters. The
4700 // dead-key is still in active state.
4701 break;
4702 case 1: {
4703 char16_t baseChars[5];
4704 ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)baseChars,
4705 ArrayLength(baseChars), 0, mKeyboardLayout);
4706 if (entries < aMaxEntries) {
4707 switch (ret) {
4708 case 1:
4709 // Exactly one composite character produced. Now, when
4710 // dead-key is not active, repeat the last character one more
4711 // time to determine the base character.
4712 if (AddDeadKeyEntry(baseChars[0], compositeChars[0],
4713 aDeadKeyArray, entries)) {
4714 entries++;
4716 deadKeyActive = false;
4717 break;
4718 case -1: {
4719 // If pressing another dead-key produces different character,
4720 // we should register the dead-key entry with first character
4721 // produced by current key.
4723 // First inactivate the dead-key state completely.
4724 deadKeyActive =
4725 EnsureDeadKeyActive(false, aDeadKey, aDeadKeyKbdState);
4726 if (NS_WARN_IF(deadKeyActive)) {
4727 MOZ_LOG(gKeyLog, LogLevel::Error,
4728 (" failed to deactivating the dead-key state..."));
4729 break;
4731 for (int32_t i = 0; i < 5; ++i) {
4732 ret = ::ToUnicodeEx(
4733 virtualKey, 0, kbdState, (LPWSTR)baseChars,
4734 ArrayLength(baseChars), 0, mKeyboardLayout);
4735 if (ret >= 0) {
4736 break;
4739 if (ret > 0 &&
4740 AddDeadKeyEntry(baseChars[0], compositeChars[0],
4741 aDeadKeyArray, entries)) {
4742 entries++;
4744 // Inactivate dead-key state for current virtual keycode.
4745 EnsureDeadKeyActive(false, virtualKey, kbdState);
4746 break;
4748 default:
4749 NS_WARNING("File a bug for this dead-key handling!");
4750 deadKeyActive = false;
4751 break;
4754 MOZ_LOG(
4755 gKeyLog, LogLevel::Verbose,
4756 (" %s -> %s (%d): DeadKeyEntry(%s, %s) (ret=%d)",
4757 kVirtualKeyName[aDeadKey], kVirtualKeyName[virtualKey], vki,
4758 GetCharacterCodeNames(compositeChars, 1).get(),
4759 ret <= 0
4760 ? "''"
4761 : GetCharacterCodeNames(baseChars, std::min(ret, 5)).get(),
4762 ret));
4763 break;
4765 default:
4766 // 1. Unexpected dead-key. Dead-key chaining is not supported.
4767 // 2. More than one character generated. This is not a valid
4768 // dead-key and base character combination.
4769 deadKeyActive = false;
4770 MOZ_LOG(
4771 gKeyLog, LogLevel::Verbose,
4772 (" %s -> %s (%d): Unsupport dead key type(%s) (ret=%d)",
4773 kVirtualKeyName[aDeadKey], kVirtualKeyName[virtualKey], vki,
4774 ret <= 0
4775 ? "''"
4776 : GetCharacterCodeNames(compositeChars, std::min(ret, 5))
4777 .get(),
4778 ret));
4779 break;
4785 if (deadKeyActive) {
4786 deadKeyActive = EnsureDeadKeyActive(false, aDeadKey, aDeadKeyKbdState);
4789 NS_QuickSort(aDeadKeyArray, entries, sizeof(DeadKeyEntry),
4790 CompareDeadKeyEntries, nullptr);
4791 return entries;
4794 uint32_t KeyboardLayout::ConvertNativeKeyCodeToDOMKeyCode(
4795 UINT aNativeKeyCode) const {
4796 // Alphabet or Numeric or Numpad or Function keys
4797 if ((aNativeKeyCode >= 0x30 && aNativeKeyCode <= 0x39) ||
4798 (aNativeKeyCode >= 0x41 && aNativeKeyCode <= 0x5A) ||
4799 (aNativeKeyCode >= 0x60 && aNativeKeyCode <= 0x87)) {
4800 return static_cast<uint32_t>(aNativeKeyCode);
4802 switch (aNativeKeyCode) {
4803 // Following keycodes are same as our DOM keycodes
4804 case VK_CANCEL:
4805 case VK_BACK:
4806 case VK_TAB:
4807 case VK_CLEAR:
4808 case VK_RETURN:
4809 case VK_SHIFT:
4810 case VK_CONTROL:
4811 case VK_MENU: // Alt
4812 case VK_PAUSE:
4813 case VK_CAPITAL: // CAPS LOCK
4814 case VK_KANA: // same as VK_HANGUL
4815 case VK_JUNJA:
4816 case VK_FINAL:
4817 case VK_HANJA: // same as VK_KANJI
4818 case VK_ESCAPE:
4819 case VK_CONVERT:
4820 case VK_NONCONVERT:
4821 case VK_ACCEPT:
4822 case VK_MODECHANGE:
4823 case VK_SPACE:
4824 case VK_PRIOR: // PAGE UP
4825 case VK_NEXT: // PAGE DOWN
4826 case VK_END:
4827 case VK_HOME:
4828 case VK_LEFT:
4829 case VK_UP:
4830 case VK_RIGHT:
4831 case VK_DOWN:
4832 case VK_SELECT:
4833 case VK_PRINT:
4834 case VK_EXECUTE:
4835 case VK_SNAPSHOT:
4836 case VK_INSERT:
4837 case VK_DELETE:
4838 case VK_APPS: // Context Menu
4839 case VK_SLEEP:
4840 case VK_NUMLOCK:
4841 case VK_SCROLL: // SCROLL LOCK
4842 case VK_ATTN: // Attension key of IBM midrange computers, e.g., AS/400
4843 case VK_CRSEL: // Cursor Selection
4844 case VK_EXSEL: // Extend Selection
4845 case VK_EREOF: // Erase EOF key of IBM 3270 keyboard layout
4846 case VK_PLAY:
4847 case VK_ZOOM:
4848 case VK_PA1: // PA1 key of IBM 3270 keyboard layout
4849 return uint32_t(aNativeKeyCode);
4851 case VK_HELP:
4852 return NS_VK_HELP;
4854 // Windows key should be mapped to a Win keycode
4855 // They should be able to be distinguished by DOM3 KeyboardEvent.location
4856 case VK_LWIN:
4857 case VK_RWIN:
4858 return NS_VK_WIN;
4860 case VK_VOLUME_MUTE:
4861 return NS_VK_VOLUME_MUTE;
4862 case VK_VOLUME_DOWN:
4863 return NS_VK_VOLUME_DOWN;
4864 case VK_VOLUME_UP:
4865 return NS_VK_VOLUME_UP;
4867 case VK_LSHIFT:
4868 case VK_RSHIFT:
4869 return NS_VK_SHIFT;
4871 case VK_LCONTROL:
4872 case VK_RCONTROL:
4873 return NS_VK_CONTROL;
4875 // Note that even if the key is AltGr, we should return NS_VK_ALT for
4876 // compatibility with both older Gecko and the other browsers.
4877 case VK_LMENU:
4878 case VK_RMENU:
4879 return NS_VK_ALT;
4881 // Following keycodes are not defined in our DOM keycodes.
4882 case VK_BROWSER_BACK:
4883 case VK_BROWSER_FORWARD:
4884 case VK_BROWSER_REFRESH:
4885 case VK_BROWSER_STOP:
4886 case VK_BROWSER_SEARCH:
4887 case VK_BROWSER_FAVORITES:
4888 case VK_BROWSER_HOME:
4889 case VK_MEDIA_NEXT_TRACK:
4890 case VK_MEDIA_PREV_TRACK:
4891 case VK_MEDIA_STOP:
4892 case VK_MEDIA_PLAY_PAUSE:
4893 case VK_LAUNCH_MAIL:
4894 case VK_LAUNCH_MEDIA_SELECT:
4895 case VK_LAUNCH_APP1:
4896 case VK_LAUNCH_APP2:
4897 return 0;
4899 // Following OEM specific virtual keycodes should pass through DOM keyCode
4900 // for compatibility with the other browsers on Windows.
4902 // Following OEM specific virtual keycodes are defined for Fujitsu/OASYS.
4903 case VK_OEM_FJ_JISHO:
4904 case VK_OEM_FJ_MASSHOU:
4905 case VK_OEM_FJ_TOUROKU:
4906 case VK_OEM_FJ_LOYA:
4907 case VK_OEM_FJ_ROYA:
4908 // Not sure what means "ICO".
4909 case VK_ICO_HELP:
4910 case VK_ICO_00:
4911 case VK_ICO_CLEAR:
4912 // Following OEM specific virtual keycodes are defined for Nokia/Ericsson.
4913 case VK_OEM_RESET:
4914 case VK_OEM_JUMP:
4915 case VK_OEM_PA1:
4916 case VK_OEM_PA2:
4917 case VK_OEM_PA3:
4918 case VK_OEM_WSCTRL:
4919 case VK_OEM_CUSEL:
4920 case VK_OEM_ATTN:
4921 case VK_OEM_FINISH:
4922 case VK_OEM_COPY:
4923 case VK_OEM_AUTO:
4924 case VK_OEM_ENLW:
4925 case VK_OEM_BACKTAB:
4926 // VK_OEM_CLEAR is defined as not OEM specific, but let's pass though
4927 // DOM keyCode like other OEM specific virtual keycodes.
4928 case VK_OEM_CLEAR:
4929 return uint32_t(aNativeKeyCode);
4931 // 0xE1 is an OEM specific virtual keycode. However, the value is already
4932 // used in our DOM keyCode for AltGr on Linux. So, this virtual keycode
4933 // cannot pass through DOM keyCode.
4934 case 0xE1:
4935 return 0;
4937 // Following keycodes are OEM keys which are keycodes for non-alphabet and
4938 // non-numeric keys, we should compute each keycode of them from unshifted
4939 // character which is inputted by each key. But if the unshifted character
4940 // is not an ASCII character but shifted character is an ASCII character,
4941 // we should refer it.
4942 case VK_OEM_1:
4943 case VK_OEM_PLUS:
4944 case VK_OEM_COMMA:
4945 case VK_OEM_MINUS:
4946 case VK_OEM_PERIOD:
4947 case VK_OEM_2:
4948 case VK_OEM_3:
4949 case VK_OEM_4:
4950 case VK_OEM_5:
4951 case VK_OEM_6:
4952 case VK_OEM_7:
4953 case VK_OEM_8:
4954 case VK_OEM_102:
4955 case VK_ABNT_C1: {
4956 NS_ASSERTION(IsPrintableCharKey(aNativeKeyCode),
4957 "The key must be printable");
4958 ModifierKeyState modKeyState(0);
4959 UniCharsAndModifiers uniChars =
4960 GetUniCharsAndModifiers(aNativeKeyCode, modKeyState);
4961 if (uniChars.Length() != 1 || uniChars.CharAt(0) < ' ' ||
4962 uniChars.CharAt(0) > 0x7F) {
4963 modKeyState.Set(MODIFIER_SHIFT);
4964 uniChars = GetUniCharsAndModifiers(aNativeKeyCode, modKeyState);
4965 if (uniChars.Length() != 1 || uniChars.CharAt(0) < ' ' ||
4966 uniChars.CharAt(0) > 0x7F) {
4967 // In this case, we've returned 0 in this case for long time because
4968 // we decided that we should avoid setting same keyCode value to 2 or
4969 // more keys since active keyboard layout may have a key to input the
4970 // punctuation with different key. However, setting keyCode to 0
4971 // makes some web applications which are aware of neither
4972 // KeyboardEvent.key nor KeyboardEvent.code not work with Firefox
4973 // when user selects non-ASCII capable keyboard layout such as
4974 // Russian and Thai layout. So, let's decide keyCode value with
4975 // major keyboard layout's key which causes the OEM keycode.
4976 // Actually, this maps same keyCode value to 2 keys on Russian
4977 // keyboard layout. "Period" key causes VK_OEM_PERIOD but inputs
4978 // Yu of Cyrillic and "Slash" key causes VK_OEM_2 (same as US
4979 // keyboard layout) but inputs "." (period of ASCII). Therefore,
4980 // we return DOM_VK_PERIOD which is same as VK_OEM_PERIOD for
4981 // "Period" key. On the other hand, we use same keyCode value for
4982 // "Slash" key too because it inputs ".".
4983 CodeNameIndex code;
4984 switch (aNativeKeyCode) {
4985 case VK_OEM_1:
4986 code = CODE_NAME_INDEX_Semicolon;
4987 break;
4988 case VK_OEM_PLUS:
4989 code = CODE_NAME_INDEX_Equal;
4990 break;
4991 case VK_OEM_COMMA:
4992 code = CODE_NAME_INDEX_Comma;
4993 break;
4994 case VK_OEM_MINUS:
4995 code = CODE_NAME_INDEX_Minus;
4996 break;
4997 case VK_OEM_PERIOD:
4998 code = CODE_NAME_INDEX_Period;
4999 break;
5000 case VK_OEM_2:
5001 code = CODE_NAME_INDEX_Slash;
5002 break;
5003 case VK_OEM_3:
5004 code = CODE_NAME_INDEX_Backquote;
5005 break;
5006 case VK_OEM_4:
5007 code = CODE_NAME_INDEX_BracketLeft;
5008 break;
5009 case VK_OEM_5:
5010 code = CODE_NAME_INDEX_Backslash;
5011 break;
5012 case VK_OEM_6:
5013 code = CODE_NAME_INDEX_BracketRight;
5014 break;
5015 case VK_OEM_7:
5016 code = CODE_NAME_INDEX_Quote;
5017 break;
5018 case VK_OEM_8:
5019 // Use keyCode value for "Backquote" key on UK keyboard layout.
5020 code = CODE_NAME_INDEX_Backquote;
5021 break;
5022 case VK_OEM_102:
5023 // Use keyCode value for "IntlBackslash" key.
5024 code = CODE_NAME_INDEX_IntlBackslash;
5025 break;
5026 case VK_ABNT_C1: // "/" of ABNT.
5027 // Use keyCode value for "IntlBackslash" key on ABNT keyboard
5028 // layout.
5029 code = CODE_NAME_INDEX_IntlBackslash;
5030 break;
5031 default:
5032 MOZ_ASSERT_UNREACHABLE("Handle all OEM keycode values");
5033 return 0;
5035 return WidgetKeyboardEvent::GetFallbackKeyCodeOfPunctuationKey(code);
5038 return WidgetUtils::ComputeKeyCodeFromChar(uniChars.CharAt(0));
5041 // IE sets 0xC2 to the DOM keyCode for VK_ABNT_C2. However, we're already
5042 // using NS_VK_SEPARATOR for the separator key on Mac and Linux. Therefore,
5043 // We should keep consistency between Gecko on all platforms rather than
5044 // with other browsers since a lot of keyCode values are already different
5045 // between browsers.
5046 case VK_ABNT_C2:
5047 return NS_VK_SEPARATOR;
5049 // VK_PROCESSKEY means IME already consumed the key event.
5050 case VK_PROCESSKEY:
5051 return NS_VK_PROCESSKEY;
5052 // VK_PACKET is generated by SendInput() API, we don't need to
5053 // care this message as key event.
5054 case VK_PACKET:
5055 return 0;
5056 // If a key is not mapped to a virtual keycode, 0xFF is used.
5057 case 0xFF:
5058 NS_WARNING("The key is failed to be converted to a virtual keycode");
5059 return 0;
5061 #ifdef DEBUG
5062 nsPrintfCString warning(
5063 "Unknown virtual keycode (0x%08X), please check the "
5064 "latest MSDN document, there may be some new "
5065 "keycodes we've never known.",
5066 aNativeKeyCode);
5067 NS_WARNING(warning.get());
5068 #endif
5069 return 0;
5072 KeyNameIndex KeyboardLayout::ConvertNativeKeyCodeToKeyNameIndex(
5073 uint8_t aVirtualKey) const {
5074 if (IsPrintableCharKey(aVirtualKey) || aVirtualKey == VK_PACKET) {
5075 return KEY_NAME_INDEX_USE_STRING;
5078 // If the keyboard layout has AltGr and AltRight key is pressed,
5079 // return AltGraph.
5080 if (aVirtualKey == VK_RMENU && HasAltGr()) {
5081 return KEY_NAME_INDEX_AltGraph;
5084 switch (aVirtualKey) {
5085 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5086 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5087 case aNativeKey: \
5088 return aKeyNameIndex;
5090 #include "NativeKeyToDOMKeyName.h"
5092 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5094 default:
5095 break;
5098 HKL layout = GetLayout();
5099 WORD langID = LOWORD(static_cast<HKL>(layout));
5100 WORD primaryLangID = PRIMARYLANGID(langID);
5102 if (primaryLangID == LANG_JAPANESE) {
5103 switch (aVirtualKey) {
5104 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5105 #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, \
5106 aKeyNameIndex) \
5107 case aNativeKey: \
5108 return aKeyNameIndex;
5110 #include "NativeKeyToDOMKeyName.h"
5112 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5114 default:
5115 break;
5117 } else if (primaryLangID == LANG_KOREAN) {
5118 switch (aVirtualKey) {
5119 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5120 #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5121 case aNativeKey: \
5122 return aKeyNameIndex;
5124 #include "NativeKeyToDOMKeyName.h"
5126 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5128 default:
5129 return KEY_NAME_INDEX_Unidentified;
5133 switch (aVirtualKey) {
5134 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5135 #define NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5136 case aNativeKey: \
5137 return aKeyNameIndex;
5139 #include "NativeKeyToDOMKeyName.h"
5141 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5143 default:
5144 return KEY_NAME_INDEX_Unidentified;
5148 // static
5149 CodeNameIndex KeyboardLayout::ConvertScanCodeToCodeNameIndex(UINT aScanCode) {
5150 switch (aScanCode) {
5151 #define NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX(aNativeKey, aCodeNameIndex) \
5152 case aNativeKey: \
5153 return aCodeNameIndex;
5155 #include "NativeKeyToDOMCodeName.h"
5157 #undef NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX
5159 default:
5160 return CODE_NAME_INDEX_UNKNOWN;
5164 nsresult KeyboardLayout::SynthesizeNativeKeyEvent(
5165 nsWindow* aWidget, int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode,
5166 uint32_t aModifierFlags, const nsAString& aCharacters,
5167 const nsAString& aUnmodifiedCharacters) {
5168 UINT keyboardLayoutListCount = ::GetKeyboardLayoutList(0, nullptr);
5169 NS_ASSERTION(keyboardLayoutListCount > 0,
5170 "One keyboard layout must be installed at least");
5171 HKL keyboardLayoutListBuff[50];
5172 HKL* keyboardLayoutList = keyboardLayoutListCount < 50
5173 ? keyboardLayoutListBuff
5174 : new HKL[keyboardLayoutListCount];
5175 keyboardLayoutListCount =
5176 ::GetKeyboardLayoutList(keyboardLayoutListCount, keyboardLayoutList);
5177 NS_ASSERTION(keyboardLayoutListCount > 0,
5178 "Failed to get all keyboard layouts installed on the system");
5180 nsPrintfCString layoutName("%08x", aNativeKeyboardLayout);
5181 HKL loadedLayout = LoadKeyboardLayoutA(layoutName.get(), KLF_NOTELLSHELL);
5182 if (loadedLayout == nullptr) {
5183 if (keyboardLayoutListBuff != keyboardLayoutList) {
5184 delete[] keyboardLayoutList;
5186 return NS_ERROR_NOT_AVAILABLE;
5189 // Setup clean key state and load desired layout
5190 BYTE originalKbdState[256];
5191 ::GetKeyboardState(originalKbdState);
5192 BYTE kbdState[256];
5193 memset(kbdState, 0, sizeof(kbdState));
5194 // This changes the state of the keyboard for the current thread only,
5195 // and we'll restore it soon, so this should be OK.
5196 ::SetKeyboardState(kbdState);
5198 OverrideLayout(loadedLayout);
5200 bool isAltGrKeyPress = false;
5201 if (aModifierFlags & nsIWidget::ALTGRAPH) {
5202 if (!HasAltGr()) {
5203 return NS_ERROR_INVALID_ARG;
5205 // AltGr emulates ControlLeft key press and AltRight key press.
5206 // So, we should remove those flags from aModifierFlags before
5207 // calling WinUtils::SetupKeyModifiersSequence() to create correct
5208 // key sequence.
5209 // FYI: We don't support both ControlLeft and AltRight (AltGr) are
5210 // pressed at the same time unless synthesizing key is
5211 // VK_LCONTROL.
5212 aModifierFlags &= ~(nsIWidget::CTRL_L | nsIWidget::ALT_R);
5215 uint8_t argumentKeySpecific = 0;
5216 switch (aNativeKeyCode & 0xFF) {
5217 case VK_SHIFT:
5218 aModifierFlags &= ~(nsIWidget::SHIFT_L | nsIWidget::SHIFT_R);
5219 argumentKeySpecific = VK_LSHIFT;
5220 break;
5221 case VK_LSHIFT:
5222 aModifierFlags &= ~nsIWidget::SHIFT_L;
5223 argumentKeySpecific = aNativeKeyCode & 0xFF;
5224 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_SHIFT;
5225 break;
5226 case VK_RSHIFT:
5227 aModifierFlags &= ~nsIWidget::SHIFT_R;
5228 argumentKeySpecific = aNativeKeyCode & 0xFF;
5229 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_SHIFT;
5230 break;
5231 case VK_CONTROL:
5232 aModifierFlags &= ~(nsIWidget::CTRL_L | nsIWidget::CTRL_R);
5233 argumentKeySpecific = VK_LCONTROL;
5234 break;
5235 case VK_LCONTROL:
5236 aModifierFlags &= ~nsIWidget::CTRL_L;
5237 argumentKeySpecific = aNativeKeyCode & 0xFF;
5238 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_CONTROL;
5239 break;
5240 case VK_RCONTROL:
5241 aModifierFlags &= ~nsIWidget::CTRL_R;
5242 argumentKeySpecific = aNativeKeyCode & 0xFF;
5243 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_CONTROL;
5244 break;
5245 case VK_MENU:
5246 aModifierFlags &= ~(nsIWidget::ALT_L | nsIWidget::ALT_R);
5247 argumentKeySpecific = VK_LMENU;
5248 break;
5249 case VK_LMENU:
5250 aModifierFlags &= ~nsIWidget::ALT_L;
5251 argumentKeySpecific = aNativeKeyCode & 0xFF;
5252 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_MENU;
5253 break;
5254 case VK_RMENU:
5255 aModifierFlags &= ~(nsIWidget::ALT_R | nsIWidget::ALTGRAPH);
5256 argumentKeySpecific = aNativeKeyCode & 0xFF;
5257 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_MENU;
5258 // If AltRight key is AltGr in the keyboard layout, let's use
5259 // SetupKeyModifiersSequence() to emulate the native behavior
5260 // since the same event order between keydown and keyup makes
5261 // the following code complicated.
5262 if (HasAltGr()) {
5263 isAltGrKeyPress = true;
5264 aModifierFlags &= ~nsIWidget::CTRL_L;
5265 aModifierFlags |= nsIWidget::ALTGRAPH;
5267 break;
5268 case VK_CAPITAL:
5269 aModifierFlags &= ~nsIWidget::CAPS_LOCK;
5270 argumentKeySpecific = VK_CAPITAL;
5271 break;
5272 case VK_NUMLOCK:
5273 aModifierFlags &= ~nsIWidget::NUM_LOCK;
5274 argumentKeySpecific = VK_NUMLOCK;
5275 break;
5278 AutoTArray<KeyPair, 10> keySequence;
5279 WinUtils::SetupKeyModifiersSequence(&keySequence, aModifierFlags, WM_KEYDOWN);
5280 if (!isAltGrKeyPress) {
5281 keySequence.AppendElement(KeyPair(aNativeKeyCode, argumentKeySpecific));
5284 // Simulate the pressing of each modifier key and then the real key
5285 // FYI: Each NativeKey instance here doesn't need to override keyboard layout
5286 // since this method overrides and restores the keyboard layout.
5287 for (uint32_t i = 0; i < keySequence.Length(); ++i) {
5288 uint8_t key = keySequence[i].mGeneral;
5289 uint8_t keySpecific = keySequence[i].mSpecific;
5290 uint16_t scanCode = keySequence[i].mScanCode;
5291 kbdState[key] = 0x81; // key is down and toggled on if appropriate
5292 if (keySpecific) {
5293 kbdState[keySpecific] = 0x81;
5295 ::SetKeyboardState(kbdState);
5296 ModifierKeyState modKeyState;
5297 // If scan code isn't specified explicitly, let's compute it with current
5298 // keyboard layout.
5299 if (!scanCode) {
5300 scanCode =
5301 ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key);
5303 LPARAM lParam = static_cast<LPARAM>(scanCode << 16);
5304 // If the scan code is for an extended key, set extended key flag.
5305 if ((scanCode & 0xFF00) == 0xE000) {
5306 lParam |= 0x1000000;
5308 // When AltGr key is pressed, both ControlLeft and AltRight cause
5309 // WM_KEYDOWN messages.
5310 bool makeSysKeyMsg =
5311 !(aModifierFlags & nsIWidget::ALTGRAPH) && IsSysKey(key, modKeyState);
5312 MSG keyDownMsg =
5313 WinUtils::InitMSG(makeSysKeyMsg ? WM_SYSKEYDOWN : WM_KEYDOWN, key,
5314 lParam, aWidget->GetWindowHandle());
5315 if (i == keySequence.Length() - 1) {
5316 bool makeDeadCharMsg =
5317 (IsDeadKey(key, modKeyState) && aCharacters.IsEmpty());
5318 nsAutoString chars(aCharacters);
5319 if (makeDeadCharMsg) {
5320 UniCharsAndModifiers deadChars =
5321 GetUniCharsAndModifiers(key, modKeyState);
5322 chars = deadChars.ToString();
5323 NS_ASSERTION(chars.Length() == 1,
5324 "Dead char must be only one character");
5326 if (chars.IsEmpty()) {
5327 NativeKey nativeKey(aWidget, keyDownMsg, modKeyState);
5328 nativeKey.HandleKeyDownMessage();
5329 } else {
5330 AutoTArray<NativeKey::FakeCharMsg, 10> fakeCharMsgs;
5331 for (uint32_t j = 0; j < chars.Length(); j++) {
5332 NativeKey::FakeCharMsg* fakeCharMsg = fakeCharMsgs.AppendElement();
5333 fakeCharMsg->mCharCode = chars.CharAt(j);
5334 fakeCharMsg->mScanCode = scanCode;
5335 fakeCharMsg->mIsSysKey = makeSysKeyMsg;
5336 fakeCharMsg->mIsDeadKey = makeDeadCharMsg;
5338 NativeKey nativeKey(aWidget, keyDownMsg, modKeyState, 0, &fakeCharMsgs);
5339 bool dispatched;
5340 nativeKey.HandleKeyDownMessage(&dispatched);
5341 // If some char messages are not consumed, let's emulate the widget
5342 // receiving the message directly.
5343 for (uint32_t j = 1; j < fakeCharMsgs.Length(); j++) {
5344 if (fakeCharMsgs[j].mConsumed) {
5345 continue;
5347 MSG charMsg = fakeCharMsgs[j].GetCharMsg(aWidget->GetWindowHandle());
5348 NativeKey nativeKey(aWidget, charMsg, modKeyState);
5349 nativeKey.HandleCharMessage(charMsg);
5352 } else {
5353 NativeKey nativeKey(aWidget, keyDownMsg, modKeyState);
5354 nativeKey.HandleKeyDownMessage();
5358 keySequence.Clear();
5359 if (!isAltGrKeyPress) {
5360 keySequence.AppendElement(KeyPair(aNativeKeyCode, argumentKeySpecific));
5362 WinUtils::SetupKeyModifiersSequence(&keySequence, aModifierFlags, WM_KEYUP);
5363 for (uint32_t i = 0; i < keySequence.Length(); ++i) {
5364 uint8_t key = keySequence[i].mGeneral;
5365 uint8_t keySpecific = keySequence[i].mSpecific;
5366 uint16_t scanCode = keySequence[i].mScanCode;
5367 kbdState[key] = 0; // key is up and toggled off if appropriate
5368 if (keySpecific) {
5369 kbdState[keySpecific] = 0;
5371 ::SetKeyboardState(kbdState);
5372 ModifierKeyState modKeyState;
5373 // If scan code isn't specified explicitly, let's compute it with current
5374 // keyboard layout.
5375 if (!scanCode) {
5376 scanCode =
5377 ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key);
5379 LPARAM lParam = static_cast<LPARAM>(scanCode << 16);
5380 // If the scan code is for an extended key, set extended key flag.
5381 if ((scanCode & 0xFF00) == 0xE000) {
5382 lParam |= 0x1000000;
5384 // Don't use WM_SYSKEYUP for Alt keyup.
5385 // NOTE: When AltGr was pressed, ControlLeft causes WM_SYSKEYUP normally.
5386 bool makeSysKeyMsg = IsSysKey(key, modKeyState) && key != VK_MENU;
5387 MSG keyUpMsg = WinUtils::InitMSG(makeSysKeyMsg ? WM_SYSKEYUP : WM_KEYUP,
5388 key, lParam, aWidget->GetWindowHandle());
5389 NativeKey nativeKey(aWidget, keyUpMsg, modKeyState);
5390 nativeKey.HandleKeyUpMessage();
5393 // Restore old key state and layout
5394 ::SetKeyboardState(originalKbdState);
5395 RestoreLayout();
5397 // Don't unload the layout if it's installed actually.
5398 for (uint32_t i = 0; i < keyboardLayoutListCount; i++) {
5399 if (keyboardLayoutList[i] == loadedLayout) {
5400 loadedLayout = 0;
5401 break;
5404 if (keyboardLayoutListBuff != keyboardLayoutList) {
5405 delete[] keyboardLayoutList;
5407 if (loadedLayout) {
5408 ::UnloadKeyboardLayout(loadedLayout);
5410 return NS_OK;
5413 /*****************************************************************************
5414 * mozilla::widget::DeadKeyTable
5415 *****************************************************************************/
5417 char16_t DeadKeyTable::GetCompositeChar(char16_t aBaseChar) const {
5418 // Dead-key table is sorted by BaseChar in ascending order.
5419 // Usually they are too small to use binary search.
5421 for (uint32_t index = 0; index < mEntries; index++) {
5422 if (mTable[index].BaseChar == aBaseChar) {
5423 return mTable[index].CompositeChar;
5425 if (mTable[index].BaseChar > aBaseChar) {
5426 break;
5430 return 0;
5433 /*****************************************************************************
5434 * mozilla::widget::RedirectedKeyDownMessage
5435 *****************************************************************************/
5437 MSG RedirectedKeyDownMessageManager::sRedirectedKeyDownMsg;
5438 bool RedirectedKeyDownMessageManager::sDefaultPreventedOfRedirectedMsg = false;
5440 // static
5441 bool RedirectedKeyDownMessageManager::IsRedirectedMessage(const MSG& aMsg) {
5442 return (aMsg.message == WM_KEYDOWN || aMsg.message == WM_SYSKEYDOWN) &&
5443 (sRedirectedKeyDownMsg.message == aMsg.message &&
5444 WinUtils::GetScanCode(sRedirectedKeyDownMsg.lParam) ==
5445 WinUtils::GetScanCode(aMsg.lParam));
5448 // static
5449 void RedirectedKeyDownMessageManager::RemoveNextCharMessage(HWND aWnd) {
5450 MSG msg;
5451 if (WinUtils::PeekMessage(&msg, aWnd, WM_KEYFIRST, WM_KEYLAST,
5452 PM_NOREMOVE | PM_NOYIELD) &&
5453 (msg.message == WM_CHAR || msg.message == WM_SYSCHAR)) {
5454 WinUtils::PeekMessage(&msg, aWnd, msg.message, msg.message,
5455 PM_REMOVE | PM_NOYIELD);
5459 } // namespace widget
5460 } // namespace mozilla