Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / widget / windows / KeyboardLayout.cpp
blob475046c06c2ff52d54d4552242a24776106fbefc
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 // For collecting other people's log, tell them `MOZ_LOG=KeyboardHandler:4,sync`
45 // rather than `MOZ_LOG=KeyboardHandler:5,sync` since using `5` may create too
46 // big file.
47 // Therefore you shouldn't use `LogLevel::Verbose` for logging usual behavior.
48 mozilla::LazyLogModule gKeyLog("KeyboardHandler");
50 namespace mozilla {
51 namespace widget {
53 static const char* const kVirtualKeyName[] = {
54 "NULL",
55 "VK_LBUTTON",
56 "VK_RBUTTON",
57 "VK_CANCEL",
58 "VK_MBUTTON",
59 "VK_XBUTTON1",
60 "VK_XBUTTON2",
61 "0x07",
62 "VK_BACK",
63 "VK_TAB",
64 "0x0A",
65 "0x0B",
66 "VK_CLEAR",
67 "VK_RETURN",
68 "0x0E",
69 "0x0F",
71 "VK_SHIFT",
72 "VK_CONTROL",
73 "VK_MENU",
74 "VK_PAUSE",
75 "VK_CAPITAL",
76 "VK_KANA, VK_HANGUL",
77 "0x16",
78 "VK_JUNJA",
79 "VK_FINAL",
80 "VK_HANJA, VK_KANJI",
81 "0x1A",
82 "VK_ESCAPE",
83 "VK_CONVERT",
84 "VK_NONCONVERT",
85 "VK_ACCEPT",
86 "VK_MODECHANGE",
88 "VK_SPACE",
89 "VK_PRIOR",
90 "VK_NEXT",
91 "VK_END",
92 "VK_HOME",
93 "VK_LEFT",
94 "VK_UP",
95 "VK_RIGHT",
96 "VK_DOWN",
97 "VK_SELECT",
98 "VK_PRINT",
99 "VK_EXECUTE",
100 "VK_SNAPSHOT",
101 "VK_INSERT",
102 "VK_DELETE",
103 "VK_HELP",
105 "VK_0",
106 "VK_1",
107 "VK_2",
108 "VK_3",
109 "VK_4",
110 "VK_5",
111 "VK_6",
112 "VK_7",
113 "VK_8",
114 "VK_9",
115 "0x3A",
116 "0x3B",
117 "0x3C",
118 "0x3D",
119 "0x3E",
120 "0x3F",
122 "0x40",
123 "VK_A",
124 "VK_B",
125 "VK_C",
126 "VK_D",
127 "VK_E",
128 "VK_F",
129 "VK_G",
130 "VK_H",
131 "VK_I",
132 "VK_J",
133 "VK_K",
134 "VK_L",
135 "VK_M",
136 "VK_N",
137 "VK_O",
139 "VK_P",
140 "VK_Q",
141 "VK_R",
142 "VK_S",
143 "VK_T",
144 "VK_U",
145 "VK_V",
146 "VK_W",
147 "VK_X",
148 "VK_Y",
149 "VK_Z",
150 "VK_LWIN",
151 "VK_RWIN",
152 "VK_APPS",
153 "0x5E",
154 "VK_SLEEP",
156 "VK_NUMPAD0",
157 "VK_NUMPAD1",
158 "VK_NUMPAD2",
159 "VK_NUMPAD3",
160 "VK_NUMPAD4",
161 "VK_NUMPAD5",
162 "VK_NUMPAD6",
163 "VK_NUMPAD7",
164 "VK_NUMPAD8",
165 "VK_NUMPAD9",
166 "VK_MULTIPLY",
167 "VK_ADD",
168 "VK_SEPARATOR",
169 "VK_SUBTRACT",
170 "VK_DECIMAL",
171 "VK_DIVIDE",
173 "VK_F1",
174 "VK_F2",
175 "VK_F3",
176 "VK_F4",
177 "VK_F5",
178 "VK_F6",
179 "VK_F7",
180 "VK_F8",
181 "VK_F9",
182 "VK_F10",
183 "VK_F11",
184 "VK_F12",
185 "VK_F13",
186 "VK_F14",
187 "VK_F15",
188 "VK_F16",
190 "VK_F17",
191 "VK_F18",
192 "VK_F19",
193 "VK_F20",
194 "VK_F21",
195 "VK_F22",
196 "VK_F23",
197 "VK_F24",
198 "0x88",
199 "0x89",
200 "0x8A",
201 "0x8B",
202 "0x8C",
203 "0x8D",
204 "0x8E",
205 "0x8F",
207 "VK_NUMLOCK",
208 "VK_SCROLL",
209 "VK_OEM_NEC_EQUAL, VK_OEM_FJ_JISHO",
210 "VK_OEM_FJ_MASSHOU",
211 "VK_OEM_FJ_TOUROKU",
212 "VK_OEM_FJ_LOYA",
213 "VK_OEM_FJ_ROYA",
214 "0x97",
215 "0x98",
216 "0x99",
217 "0x9A",
218 "0x9B",
219 "0x9C",
220 "0x9D",
221 "0x9E",
222 "0x9F",
224 "VK_LSHIFT",
225 "VK_RSHIFT",
226 "VK_LCONTROL",
227 "VK_RCONTROL",
228 "VK_LMENU",
229 "VK_RMENU",
230 "VK_BROWSER_BACK",
231 "VK_BROWSER_FORWARD",
232 "VK_BROWSER_REFRESH",
233 "VK_BROWSER_STOP",
234 "VK_BROWSER_SEARCH",
235 "VK_BROWSER_FAVORITES",
236 "VK_BROWSER_HOME",
237 "VK_VOLUME_MUTE",
238 "VK_VOLUME_DOWN",
239 "VK_VOLUME_UP",
241 "VK_MEDIA_NEXT_TRACK",
242 "VK_MEDIA_PREV_TRACK",
243 "VK_MEDIA_STOP",
244 "VK_MEDIA_PLAY_PAUSE",
245 "VK_LAUNCH_MAIL",
246 "VK_LAUNCH_MEDIA_SELECT",
247 "VK_LAUNCH_APP1",
248 "VK_LAUNCH_APP2",
249 "0xB8",
250 "0xB9",
251 "VK_OEM_1",
252 "VK_OEM_PLUS",
253 "VK_OEM_COMMA",
254 "VK_OEM_MINUS",
255 "VK_OEM_PERIOD",
256 "VK_OEM_2",
258 "VK_OEM_3",
259 "VK_ABNT_C1",
260 "VK_ABNT_C2",
261 "0xC3",
262 "0xC4",
263 "0xC5",
264 "0xC6",
265 "0xC7",
266 "0xC8",
267 "0xC9",
268 "0xCA",
269 "0xCB",
270 "0xCC",
271 "0xCD",
272 "0xCE",
273 "0xCF",
275 "0xD0",
276 "0xD1",
277 "0xD2",
278 "0xD3",
279 "0xD4",
280 "0xD5",
281 "0xD6",
282 "0xD7",
283 "0xD8",
284 "0xD9",
285 "0xDA",
286 "VK_OEM_4",
287 "VK_OEM_5",
288 "VK_OEM_6",
289 "VK_OEM_7",
290 "VK_OEM_8",
292 "0xE0",
293 "VK_OEM_AX",
294 "VK_OEM_102",
295 "VK_ICO_HELP",
296 "VK_ICO_00",
297 "VK_PROCESSKEY",
298 "VK_ICO_CLEAR",
299 "VK_PACKET",
300 "0xE8",
301 "VK_OEM_RESET",
302 "VK_OEM_JUMP",
303 "VK_OEM_PA1",
304 "VK_OEM_PA2",
305 "VK_OEM_PA3",
306 "VK_OEM_WSCTRL",
307 "VK_OEM_CUSEL",
309 "VK_OEM_ATTN",
310 "VK_OEM_FINISH",
311 "VK_OEM_COPY",
312 "VK_OEM_AUTO",
313 "VK_OEM_ENLW",
314 "VK_OEM_BACKTAB",
315 "VK_ATTN",
316 "VK_CRSEL",
317 "VK_EXSEL",
318 "VK_EREOF",
319 "VK_PLAY",
320 "VK_ZOOM",
321 "VK_NONAME",
322 "VK_PA1",
323 "VK_OEM_CLEAR",
324 "0xFF"};
326 static_assert(sizeof(kVirtualKeyName) / sizeof(const char*) == 0x100,
327 "The virtual key name must be defined just 256 keys");
329 static const char* GetBoolName(bool aBool) { return aBool ? "true" : "false"; }
331 static const nsCString GetCharacterCodeName(WPARAM aCharCode) {
332 switch (aCharCode) {
333 case 0x0000:
334 return "NULL (0x0000)"_ns;
335 case 0x0008:
336 return "BACKSPACE (0x0008)"_ns;
337 case 0x0009:
338 return "CHARACTER TABULATION (0x0009)"_ns;
339 case 0x000A:
340 return "LINE FEED (0x000A)"_ns;
341 case 0x000B:
342 return "LINE TABULATION (0x000B)"_ns;
343 case 0x000C:
344 return "FORM FEED (0x000C)"_ns;
345 case 0x000D:
346 return "CARRIAGE RETURN (0x000D)"_ns;
347 case 0x0018:
348 return "CANCEL (0x0018)"_ns;
349 case 0x001B:
350 return "ESCAPE (0x001B)"_ns;
351 case 0x0020:
352 return "SPACE (0x0020)"_ns;
353 case 0x007F:
354 return "DELETE (0x007F)"_ns;
355 case 0x00A0:
356 return "NO-BREAK SPACE (0x00A0)"_ns;
357 case 0x00AD:
358 return "SOFT HYPHEN (0x00AD)"_ns;
359 case 0x2000:
360 return "EN QUAD (0x2000)"_ns;
361 case 0x2001:
362 return "EM QUAD (0x2001)"_ns;
363 case 0x2002:
364 return "EN SPACE (0x2002)"_ns;
365 case 0x2003:
366 return "EM SPACE (0x2003)"_ns;
367 case 0x2004:
368 return "THREE-PER-EM SPACE (0x2004)"_ns;
369 case 0x2005:
370 return "FOUR-PER-EM SPACE (0x2005)"_ns;
371 case 0x2006:
372 return "SIX-PER-EM SPACE (0x2006)"_ns;
373 case 0x2007:
374 return "FIGURE SPACE (0x2007)"_ns;
375 case 0x2008:
376 return "PUNCTUATION SPACE (0x2008)"_ns;
377 case 0x2009:
378 return "THIN SPACE (0x2009)"_ns;
379 case 0x200A:
380 return "HAIR SPACE (0x200A)"_ns;
381 case 0x200B:
382 return "ZERO WIDTH SPACE (0x200B)"_ns;
383 case 0x200C:
384 return "ZERO WIDTH NON-JOINER (0x200C)"_ns;
385 case 0x200D:
386 return "ZERO WIDTH JOINER (0x200D)"_ns;
387 case 0x200E:
388 return "LEFT-TO-RIGHT MARK (0x200E)"_ns;
389 case 0x200F:
390 return "RIGHT-TO-LEFT MARK (0x200F)"_ns;
391 case 0x2029:
392 return "PARAGRAPH SEPARATOR (0x2029)"_ns;
393 case 0x202A:
394 return "LEFT-TO-RIGHT EMBEDDING (0x202A)"_ns;
395 case 0x202B:
396 return "RIGHT-TO-LEFT EMBEDDING (0x202B)"_ns;
397 case 0x202D:
398 return "LEFT-TO-RIGHT OVERRIDE (0x202D)"_ns;
399 case 0x202E:
400 return "RIGHT-TO-LEFT OVERRIDE (0x202E)"_ns;
401 case 0x202F:
402 return "NARROW NO-BREAK SPACE (0x202F)"_ns;
403 case 0x205F:
404 return "MEDIUM MATHEMATICAL SPACE (0x205F)"_ns;
405 case 0x2060:
406 return "WORD JOINER (0x2060)"_ns;
407 case 0x2066:
408 return "LEFT-TO-RIGHT ISOLATE (0x2066)"_ns;
409 case 0x2067:
410 return "RIGHT-TO-LEFT ISOLATE (0x2067)"_ns;
411 case 0x3000:
412 return "IDEOGRAPHIC SPACE (0x3000)"_ns;
413 case 0xFEFF:
414 return "ZERO WIDTH NO-BREAK SPACE (0xFEFF)"_ns;
415 default: {
416 if (aCharCode < ' ' || (aCharCode >= 0x80 && aCharCode < 0xA0)) {
417 return nsPrintfCString("control (0x%04zX)", aCharCode);
419 if (NS_IS_HIGH_SURROGATE(aCharCode)) {
420 return nsPrintfCString("high surrogate (0x%04zX)", aCharCode);
422 if (NS_IS_LOW_SURROGATE(aCharCode)) {
423 return nsPrintfCString("low surrogate (0x%04zX)", aCharCode);
425 return IS_IN_BMP(aCharCode)
426 ? nsPrintfCString(
427 "'%s' (0x%04zX)",
428 NS_ConvertUTF16toUTF8(nsAutoString(aCharCode)).get(),
429 aCharCode)
430 : nsPrintfCString(
431 "'%s' (0x%08zX)",
432 NS_ConvertUTF16toUTF8(nsAutoString(aCharCode)).get(),
433 aCharCode);
438 static const nsCString GetKeyLocationName(uint32_t aLocation) {
439 switch (aLocation) {
440 case eKeyLocationLeft:
441 return "KEY_LOCATION_LEFT"_ns;
442 case eKeyLocationRight:
443 return "KEY_LOCATION_RIGHT"_ns;
444 case eKeyLocationStandard:
445 return "KEY_LOCATION_STANDARD"_ns;
446 case eKeyLocationNumpad:
447 return "KEY_LOCATION_NUMPAD"_ns;
448 default:
449 return nsPrintfCString("Unknown (0x%04X)", aLocation);
453 static const nsCString GetCharacterCodeNames(const char16_t* aChars,
454 uint32_t aLength) {
455 if (!aLength) {
456 return ""_ns;
458 nsCString result;
459 result.AssignLiteral("\"");
460 StringJoinAppend(result, ", "_ns, Span{aChars, aLength},
461 [](nsACString& dest, const char16_t charValue) {
462 dest.Append(GetCharacterCodeName(charValue));
464 result.AppendLiteral("\"");
465 return result;
468 static const nsCString GetCharacterCodeNames(
469 const UniCharsAndModifiers& aUniCharsAndModifiers) {
470 if (aUniCharsAndModifiers.IsEmpty()) {
471 return ""_ns;
473 nsCString result;
474 result.AssignLiteral("\"");
475 StringJoinAppend(result, ", "_ns, Span{aUniCharsAndModifiers.ToString()},
476 [](nsACString& dest, const char16_t charValue) {
477 dest.Append(GetCharacterCodeName(charValue));
479 result.AppendLiteral("\"");
480 return result;
483 class MOZ_STACK_CLASS GetShiftStateName final : public nsAutoCString {
484 public:
485 explicit GetShiftStateName(VirtualKey::ShiftState aShiftState) {
486 if (!aShiftState) {
487 AssignLiteral("none");
488 return;
490 if (aShiftState & VirtualKey::STATE_SHIFT) {
491 AssignLiteral("Shift");
492 aShiftState &= ~VirtualKey::STATE_SHIFT;
494 if (aShiftState & VirtualKey::STATE_CONTROL) {
495 MaybeAppendSeparator();
496 AssignLiteral("Ctrl");
497 aShiftState &= ~VirtualKey::STATE_CONTROL;
499 if (aShiftState & VirtualKey::STATE_ALT) {
500 MaybeAppendSeparator();
501 AssignLiteral("Alt");
502 aShiftState &= ~VirtualKey::STATE_ALT;
504 if (aShiftState & VirtualKey::STATE_CAPSLOCK) {
505 MaybeAppendSeparator();
506 AssignLiteral("CapsLock");
507 aShiftState &= ~VirtualKey::STATE_CAPSLOCK;
509 MOZ_ASSERT(!aShiftState);
512 private:
513 void MaybeAppendSeparator() {
514 if (!IsEmpty()) {
515 AppendLiteral(" | ");
520 static const nsCString GetMessageName(UINT aMessage) {
521 switch (aMessage) {
522 case WM_NULL:
523 return "WM_NULL"_ns;
524 case WM_KEYDOWN:
525 return "WM_KEYDOWN"_ns;
526 case WM_KEYUP:
527 return "WM_KEYUP"_ns;
528 case WM_SYSKEYDOWN:
529 return "WM_SYSKEYDOWN"_ns;
530 case WM_SYSKEYUP:
531 return "WM_SYSKEYUP"_ns;
532 case WM_CHAR:
533 return "WM_CHAR"_ns;
534 case WM_UNICHAR:
535 return "WM_UNICHAR"_ns;
536 case WM_SYSCHAR:
537 return "WM_SYSCHAR"_ns;
538 case WM_DEADCHAR:
539 return "WM_DEADCHAR"_ns;
540 case WM_SYSDEADCHAR:
541 return "WM_SYSDEADCHAR"_ns;
542 case WM_APPCOMMAND:
543 return "WM_APPCOMMAND"_ns;
544 case WM_QUIT:
545 return "WM_QUIT"_ns;
546 default:
547 return nsPrintfCString("Unknown Message (0x%04X)", aMessage);
551 static const nsCString GetVirtualKeyCodeName(WPARAM aVK) {
552 if (aVK >= ArrayLength(kVirtualKeyName)) {
553 return nsPrintfCString("Invalid (0x%08zX)", aVK);
555 return nsCString(kVirtualKeyName[aVK]);
558 static const nsCString GetAppCommandName(WPARAM aCommand) {
559 switch (aCommand) {
560 case APPCOMMAND_BASS_BOOST:
561 return "APPCOMMAND_BASS_BOOST"_ns;
562 case APPCOMMAND_BASS_DOWN:
563 return "APPCOMMAND_BASS_DOWN"_ns;
564 case APPCOMMAND_BASS_UP:
565 return "APPCOMMAND_BASS_UP"_ns;
566 case APPCOMMAND_BROWSER_BACKWARD:
567 return "APPCOMMAND_BROWSER_BACKWARD"_ns;
568 case APPCOMMAND_BROWSER_FAVORITES:
569 return "APPCOMMAND_BROWSER_FAVORITES"_ns;
570 case APPCOMMAND_BROWSER_FORWARD:
571 return "APPCOMMAND_BROWSER_FORWARD"_ns;
572 case APPCOMMAND_BROWSER_HOME:
573 return "APPCOMMAND_BROWSER_HOME"_ns;
574 case APPCOMMAND_BROWSER_REFRESH:
575 return "APPCOMMAND_BROWSER_REFRESH"_ns;
576 case APPCOMMAND_BROWSER_SEARCH:
577 return "APPCOMMAND_BROWSER_SEARCH"_ns;
578 case APPCOMMAND_BROWSER_STOP:
579 return "APPCOMMAND_BROWSER_STOP"_ns;
580 case APPCOMMAND_CLOSE:
581 return "APPCOMMAND_CLOSE"_ns;
582 case APPCOMMAND_COPY:
583 return "APPCOMMAND_COPY"_ns;
584 case APPCOMMAND_CORRECTION_LIST:
585 return "APPCOMMAND_CORRECTION_LIST"_ns;
586 case APPCOMMAND_CUT:
587 return "APPCOMMAND_CUT"_ns;
588 case APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE:
589 return "APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE"_ns;
590 case APPCOMMAND_FIND:
591 return "APPCOMMAND_FIND"_ns;
592 case APPCOMMAND_FORWARD_MAIL:
593 return "APPCOMMAND_FORWARD_MAIL"_ns;
594 case APPCOMMAND_HELP:
595 return "APPCOMMAND_HELP"_ns;
596 case APPCOMMAND_LAUNCH_APP1:
597 return "APPCOMMAND_LAUNCH_APP1"_ns;
598 case APPCOMMAND_LAUNCH_APP2:
599 return "APPCOMMAND_LAUNCH_APP2"_ns;
600 case APPCOMMAND_LAUNCH_MAIL:
601 return "APPCOMMAND_LAUNCH_MAIL"_ns;
602 case APPCOMMAND_LAUNCH_MEDIA_SELECT:
603 return "APPCOMMAND_LAUNCH_MEDIA_SELECT"_ns;
604 case APPCOMMAND_MEDIA_CHANNEL_DOWN:
605 return "APPCOMMAND_MEDIA_CHANNEL_DOWN"_ns;
606 case APPCOMMAND_MEDIA_CHANNEL_UP:
607 return "APPCOMMAND_MEDIA_CHANNEL_UP"_ns;
608 case APPCOMMAND_MEDIA_FAST_FORWARD:
609 return "APPCOMMAND_MEDIA_FAST_FORWARD"_ns;
610 case APPCOMMAND_MEDIA_NEXTTRACK:
611 return "APPCOMMAND_MEDIA_NEXTTRACK"_ns;
612 case APPCOMMAND_MEDIA_PAUSE:
613 return "APPCOMMAND_MEDIA_PAUSE"_ns;
614 case APPCOMMAND_MEDIA_PLAY:
615 return "APPCOMMAND_MEDIA_PLAY"_ns;
616 case APPCOMMAND_MEDIA_PLAY_PAUSE:
617 return "APPCOMMAND_MEDIA_PLAY_PAUSE"_ns;
618 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
619 return "APPCOMMAND_MEDIA_PREVIOUSTRACK"_ns;
620 case APPCOMMAND_MEDIA_RECORD:
621 return "APPCOMMAND_MEDIA_RECORD"_ns;
622 case APPCOMMAND_MEDIA_REWIND:
623 return "APPCOMMAND_MEDIA_REWIND"_ns;
624 case APPCOMMAND_MEDIA_STOP:
625 return "APPCOMMAND_MEDIA_STOP"_ns;
626 case APPCOMMAND_MIC_ON_OFF_TOGGLE:
627 return "APPCOMMAND_MIC_ON_OFF_TOGGLE"_ns;
628 case APPCOMMAND_MICROPHONE_VOLUME_DOWN:
629 return "APPCOMMAND_MICROPHONE_VOLUME_DOWN"_ns;
630 case APPCOMMAND_MICROPHONE_VOLUME_MUTE:
631 return "APPCOMMAND_MICROPHONE_VOLUME_MUTE"_ns;
632 case APPCOMMAND_MICROPHONE_VOLUME_UP:
633 return "APPCOMMAND_MICROPHONE_VOLUME_UP"_ns;
634 case APPCOMMAND_NEW:
635 return "APPCOMMAND_NEW"_ns;
636 case APPCOMMAND_OPEN:
637 return "APPCOMMAND_OPEN"_ns;
638 case APPCOMMAND_PASTE:
639 return "APPCOMMAND_PASTE"_ns;
640 case APPCOMMAND_PRINT:
641 return "APPCOMMAND_PRINT"_ns;
642 case APPCOMMAND_REDO:
643 return "APPCOMMAND_REDO"_ns;
644 case APPCOMMAND_REPLY_TO_MAIL:
645 return "APPCOMMAND_REPLY_TO_MAIL"_ns;
646 case APPCOMMAND_SAVE:
647 return "APPCOMMAND_SAVE"_ns;
648 case APPCOMMAND_SEND_MAIL:
649 return "APPCOMMAND_SEND_MAIL"_ns;
650 case APPCOMMAND_SPELL_CHECK:
651 return "APPCOMMAND_SPELL_CHECK"_ns;
652 case APPCOMMAND_TREBLE_DOWN:
653 return "APPCOMMAND_TREBLE_DOWN"_ns;
654 case APPCOMMAND_TREBLE_UP:
655 return "APPCOMMAND_TREBLE_UP"_ns;
656 case APPCOMMAND_UNDO:
657 return "APPCOMMAND_UNDO"_ns;
658 case APPCOMMAND_VOLUME_DOWN:
659 return "APPCOMMAND_VOLUME_DOWN"_ns;
660 case APPCOMMAND_VOLUME_MUTE:
661 return "APPCOMMAND_VOLUME_MUTE"_ns;
662 case APPCOMMAND_VOLUME_UP:
663 return "APPCOMMAND_VOLUME_UP"_ns;
664 default:
665 return nsPrintfCString("Unknown app command (0x%08zX)", aCommand);
669 static const nsCString GetAppCommandDeviceName(LPARAM aDevice) {
670 switch (aDevice) {
671 case FAPPCOMMAND_KEY:
672 return "FAPPCOMMAND_KEY"_ns;
673 case FAPPCOMMAND_MOUSE:
674 return "FAPPCOMMAND_MOUSE"_ns;
675 case FAPPCOMMAND_OEM:
676 return "FAPPCOMMAND_OEM"_ns;
677 default:
678 return nsPrintfCString("Unknown app command device (0x%04" PRIXLPTR ")",
679 aDevice);
683 class MOZ_STACK_CLASS GetAppCommandKeysName final : public nsAutoCString {
684 public:
685 explicit GetAppCommandKeysName(WPARAM aKeys) {
686 if (aKeys & MK_CONTROL) {
687 AppendLiteral("MK_CONTROL");
688 aKeys &= ~MK_CONTROL;
690 if (aKeys & MK_LBUTTON) {
691 MaybeAppendSeparator();
692 AppendLiteral("MK_LBUTTON");
693 aKeys &= ~MK_LBUTTON;
695 if (aKeys & MK_MBUTTON) {
696 MaybeAppendSeparator();
697 AppendLiteral("MK_MBUTTON");
698 aKeys &= ~MK_MBUTTON;
700 if (aKeys & MK_RBUTTON) {
701 MaybeAppendSeparator();
702 AppendLiteral("MK_RBUTTON");
703 aKeys &= ~MK_RBUTTON;
705 if (aKeys & MK_SHIFT) {
706 MaybeAppendSeparator();
707 AppendLiteral("MK_SHIFT");
708 aKeys &= ~MK_SHIFT;
710 if (aKeys & MK_XBUTTON1) {
711 MaybeAppendSeparator();
712 AppendLiteral("MK_XBUTTON1");
713 aKeys &= ~MK_XBUTTON1;
715 if (aKeys & MK_XBUTTON2) {
716 MaybeAppendSeparator();
717 AppendLiteral("MK_XBUTTON2");
718 aKeys &= ~MK_XBUTTON2;
720 if (aKeys) {
721 MaybeAppendSeparator();
722 AppendPrintf("Unknown Flags (0x%04zX)", aKeys);
724 if (IsEmpty()) {
725 AssignLiteral("none (0x0000)");
729 private:
730 void MaybeAppendSeparator() {
731 if (!IsEmpty()) {
732 AppendLiteral(" | ");
737 static const nsCString ToString(const MSG& aMSG) {
738 nsCString result;
739 result.AssignLiteral("{ message=");
740 result.Append(GetMessageName(aMSG.message).get());
741 result.AppendLiteral(", ");
742 switch (aMSG.message) {
743 case WM_KEYDOWN:
744 case WM_KEYUP:
745 case WM_SYSKEYDOWN:
746 case WM_SYSKEYUP:
747 result.AppendPrintf(
748 "virtual keycode=%s, repeat count=%" PRIdLPTR
749 ", "
750 "scancode=0x%02X, extended key=%s, "
751 "context code=%s, previous key state=%s, "
752 "transition state=%s",
753 GetVirtualKeyCodeName(aMSG.wParam).get(), aMSG.lParam & 0xFFFF,
754 WinUtils::GetScanCode(aMSG.lParam),
755 GetBoolName(WinUtils::IsExtendedScanCode(aMSG.lParam)),
756 GetBoolName((aMSG.lParam & (1 << 29)) != 0),
757 GetBoolName((aMSG.lParam & (1 << 30)) != 0),
758 GetBoolName((aMSG.lParam & (1 << 31)) != 0));
759 break;
760 case WM_CHAR:
761 case WM_DEADCHAR:
762 case WM_SYSCHAR:
763 case WM_SYSDEADCHAR:
764 result.AppendPrintf(
765 "character code=%s, repeat count=%" PRIdLPTR
766 ", "
767 "scancode=0x%02X, extended key=%s, "
768 "context code=%s, previous key state=%s, "
769 "transition state=%s",
770 GetCharacterCodeName(aMSG.wParam).get(), aMSG.lParam & 0xFFFF,
771 WinUtils::GetScanCode(aMSG.lParam),
772 GetBoolName(WinUtils::IsExtendedScanCode(aMSG.lParam)),
773 GetBoolName((aMSG.lParam & (1 << 29)) != 0),
774 GetBoolName((aMSG.lParam & (1 << 30)) != 0),
775 GetBoolName((aMSG.lParam & (1 << 31)) != 0));
776 break;
777 case WM_APPCOMMAND:
778 result.AppendPrintf(
779 "window handle=0x%zx, app command=%s, device=%s, dwKeys=%s",
780 aMSG.wParam,
781 GetAppCommandName(GET_APPCOMMAND_LPARAM(aMSG.lParam)).get(),
782 GetAppCommandDeviceName(GET_DEVICE_LPARAM(aMSG.lParam)).get(),
783 GetAppCommandKeysName(GET_KEYSTATE_LPARAM(aMSG.lParam)).get());
784 break;
785 default:
786 result.AppendPrintf("wParam=%zu, lParam=%" PRIdLPTR, aMSG.wParam,
787 aMSG.lParam);
788 break;
790 result.AppendPrintf(", hwnd=0x%p", aMSG.hwnd);
791 return result;
794 static const nsCString ToString(
795 const UniCharsAndModifiers& aUniCharsAndModifiers) {
796 if (aUniCharsAndModifiers.IsEmpty()) {
797 return "{}"_ns;
799 nsCString result;
800 result.AssignLiteral("{ ");
801 result.Append(GetCharacterCodeName(aUniCharsAndModifiers.CharAt(0)));
802 for (size_t i = 1; i < aUniCharsAndModifiers.Length(); ++i) {
803 if (aUniCharsAndModifiers.ModifiersAt(i - 1) !=
804 aUniCharsAndModifiers.ModifiersAt(i)) {
805 result.AppendLiteral(" [");
806 result.Append(GetModifiersName(aUniCharsAndModifiers.ModifiersAt(0)));
807 result.AppendLiteral("]");
809 result.AppendLiteral(", ");
810 result.Append(GetCharacterCodeName(aUniCharsAndModifiers.CharAt(i)));
812 result.AppendLiteral(" [");
813 uint32_t lastIndex = aUniCharsAndModifiers.Length() - 1;
814 result.Append(GetModifiersName(aUniCharsAndModifiers.ModifiersAt(lastIndex)));
815 result.AppendLiteral("] }");
816 return result;
819 const nsCString ToString(const ModifierKeyState& aModifierKeyState) {
820 nsCString result;
821 result.AssignLiteral("{ ");
822 result.Append(GetModifiersName(aModifierKeyState.GetModifiers()).get());
823 result.AppendLiteral(" }");
824 return result;
827 // Unique id counter associated with a keydown / keypress events. Used in
828 // identifing keypress events for removal from async event dispatch queue
829 // in metrofx after preventDefault is called on keydown events.
830 static uint32_t sUniqueKeyEventId = 0;
832 /*****************************************************************************
833 * mozilla::widget::ModifierKeyState
834 *****************************************************************************/
836 ModifierKeyState::ModifierKeyState() { Update(); }
838 ModifierKeyState::ModifierKeyState(Modifiers aModifiers)
839 : mModifiers(aModifiers) {
840 MOZ_ASSERT(!(mModifiers & MODIFIER_ALTGRAPH) || (!IsControl() && !IsAlt()),
841 "Neither MODIFIER_CONTROL nor MODIFIER_ALT should be set "
842 "if MODIFIER_ALTGRAPH is set");
845 void ModifierKeyState::Update() {
846 mModifiers = 0;
847 if (IS_VK_DOWN(VK_SHIFT)) {
848 mModifiers |= MODIFIER_SHIFT;
850 // If AltGr key (i.e., VK_RMENU on some keyboard layout) is pressed, only
851 // MODIFIER_ALTGRAPH should be set. Otherwise, i.e., if both Ctrl and Alt
852 // keys are pressed to emulate AltGr key, MODIFIER_CONTROL and MODIFIER_ALT
853 // keys should be set separately.
854 if (KeyboardLayout::GetInstance()->HasAltGr() && IS_VK_DOWN(VK_RMENU)) {
855 mModifiers |= MODIFIER_ALTGRAPH;
856 } else {
857 if (IS_VK_DOWN(VK_CONTROL)) {
858 mModifiers |= MODIFIER_CONTROL;
860 if (IS_VK_DOWN(VK_MENU)) {
861 mModifiers |= MODIFIER_ALT;
864 if (IS_VK_DOWN(VK_LWIN) || IS_VK_DOWN(VK_RWIN)) {
865 mModifiers |= MODIFIER_META;
867 if (::GetKeyState(VK_CAPITAL) & 1) {
868 mModifiers |= MODIFIER_CAPSLOCK;
870 if (::GetKeyState(VK_NUMLOCK) & 1) {
871 mModifiers |= MODIFIER_NUMLOCK;
873 if (::GetKeyState(VK_SCROLL) & 1) {
874 mModifiers |= MODIFIER_SCROLLLOCK;
878 void ModifierKeyState::Unset(Modifiers aRemovingModifiers) {
879 mModifiers &= ~aRemovingModifiers;
882 void ModifierKeyState::Set(Modifiers aAddingModifiers) {
883 mModifiers |= aAddingModifiers;
884 MOZ_ASSERT(!(mModifiers & MODIFIER_ALTGRAPH) || (!IsControl() && !IsAlt()),
885 "Neither MODIFIER_CONTROL nor MODIFIER_ALT should be set "
886 "if MODIFIER_ALTGRAPH is set");
889 void ModifierKeyState::InitInputEvent(WidgetInputEvent& aInputEvent) const {
890 aInputEvent.mModifiers = mModifiers;
892 switch (aInputEvent.mClass) {
893 case eMouseEventClass:
894 case eMouseScrollEventClass:
895 case eWheelEventClass:
896 case eDragEventClass:
897 case eSimpleGestureEventClass:
898 InitMouseEvent(aInputEvent);
899 break;
900 default:
901 break;
905 void ModifierKeyState::InitMouseEvent(WidgetInputEvent& aMouseEvent) const {
906 NS_ASSERTION(aMouseEvent.mClass == eMouseEventClass ||
907 aMouseEvent.mClass == eWheelEventClass ||
908 aMouseEvent.mClass == eDragEventClass ||
909 aMouseEvent.mClass == eSimpleGestureEventClass,
910 "called with non-mouse event");
912 WidgetMouseEventBase& mouseEvent = *aMouseEvent.AsMouseEventBase();
913 mouseEvent.mButtons = 0;
914 if (::GetKeyState(VK_LBUTTON) < 0) {
915 mouseEvent.mButtons |= MouseButtonsFlag::ePrimaryFlag;
917 if (::GetKeyState(VK_RBUTTON) < 0) {
918 mouseEvent.mButtons |= MouseButtonsFlag::eSecondaryFlag;
920 if (::GetKeyState(VK_MBUTTON) < 0) {
921 mouseEvent.mButtons |= MouseButtonsFlag::eMiddleFlag;
923 if (::GetKeyState(VK_XBUTTON1) < 0) {
924 mouseEvent.mButtons |= MouseButtonsFlag::e4thFlag;
926 if (::GetKeyState(VK_XBUTTON2) < 0) {
927 mouseEvent.mButtons |= MouseButtonsFlag::e5thFlag;
931 bool ModifierKeyState::IsShift() const {
932 return (mModifiers & MODIFIER_SHIFT) != 0;
935 bool ModifierKeyState::IsControl() const {
936 return (mModifiers & MODIFIER_CONTROL) != 0;
939 bool ModifierKeyState::IsAlt() const {
940 return (mModifiers & MODIFIER_ALT) != 0;
943 bool ModifierKeyState::IsWin() const {
944 return (mModifiers & MODIFIER_META) != 0;
947 bool ModifierKeyState::MaybeMatchShortcutKey() const {
948 // If Windows key is pressed, even if both Ctrl key and Alt key are pressed,
949 // it's possible to match a shortcut key.
950 if (IsWin()) {
951 return true;
953 // Otherwise, when both Ctrl key and Alt key are pressed, it shouldn't be
954 // a shortcut key for Windows since it means pressing AltGr key on
955 // some keyboard layouts.
956 if (IsControl() ^ IsAlt()) {
957 return true;
959 // If no modifier key is active except a lockable modifier nor Shift key,
960 // the key shouldn't match any shortcut keys (there are Space and
961 // Shift+Space, though, let's ignore these special case...).
962 return false;
965 bool ModifierKeyState::IsCapsLocked() const {
966 return (mModifiers & MODIFIER_CAPSLOCK) != 0;
969 bool ModifierKeyState::IsNumLocked() const {
970 return (mModifiers & MODIFIER_NUMLOCK) != 0;
973 bool ModifierKeyState::IsScrollLocked() const {
974 return (mModifiers & MODIFIER_SCROLLLOCK) != 0;
977 /*****************************************************************************
978 * mozilla::widget::UniCharsAndModifiers
979 *****************************************************************************/
981 void UniCharsAndModifiers::Append(char16_t aUniChar, Modifiers aModifiers) {
982 mChars.Append(aUniChar);
983 mModifiers.AppendElement(aModifiers);
986 void UniCharsAndModifiers::FillModifiers(Modifiers aModifiers) {
987 for (size_t i = 0; i < Length(); i++) {
988 mModifiers[i] = aModifiers;
992 void UniCharsAndModifiers::OverwriteModifiersIfBeginsWith(
993 const UniCharsAndModifiers& aOther) {
994 if (!BeginsWith(aOther)) {
995 return;
997 for (size_t i = 0; i < aOther.Length(); ++i) {
998 mModifiers[i] = aOther.mModifiers[i];
1002 bool UniCharsAndModifiers::UniCharsEqual(
1003 const UniCharsAndModifiers& aOther) const {
1004 return mChars.Equals(aOther.mChars);
1007 bool UniCharsAndModifiers::UniCharsCaseInsensitiveEqual(
1008 const UniCharsAndModifiers& aOther) const {
1009 return mChars.Equals(aOther.mChars, nsCaseInsensitiveStringComparator);
1012 bool UniCharsAndModifiers::BeginsWith(
1013 const UniCharsAndModifiers& aOther) const {
1014 return StringBeginsWith(mChars, aOther.mChars);
1017 UniCharsAndModifiers& UniCharsAndModifiers::operator+=(
1018 const UniCharsAndModifiers& aOther) {
1019 mChars.Append(aOther.mChars);
1020 mModifiers.AppendElements(aOther.mModifiers);
1021 return *this;
1024 UniCharsAndModifiers UniCharsAndModifiers::operator+(
1025 const UniCharsAndModifiers& aOther) const {
1026 UniCharsAndModifiers result(*this);
1027 result += aOther;
1028 return result;
1031 /*****************************************************************************
1032 * mozilla::widget::VirtualKey
1033 *****************************************************************************/
1035 // static
1036 VirtualKey::ShiftState VirtualKey::ModifiersToShiftState(Modifiers aModifiers) {
1037 ShiftState state = 0;
1038 if (aModifiers & MODIFIER_SHIFT) {
1039 state |= STATE_SHIFT;
1041 if (aModifiers & MODIFIER_ALTGRAPH) {
1042 state |= STATE_ALTGRAPH;
1043 } else {
1044 if (aModifiers & MODIFIER_CONTROL) {
1045 state |= STATE_CONTROL;
1047 if (aModifiers & MODIFIER_ALT) {
1048 state |= STATE_ALT;
1051 if (aModifiers & MODIFIER_CAPSLOCK) {
1052 state |= STATE_CAPSLOCK;
1054 return state;
1057 // static
1058 Modifiers VirtualKey::ShiftStateToModifiers(ShiftState aShiftState) {
1059 Modifiers modifiers = 0;
1060 if (aShiftState & STATE_SHIFT) {
1061 modifiers |= MODIFIER_SHIFT;
1063 if (aShiftState & STATE_ALTGRAPH) {
1064 modifiers |= MODIFIER_ALTGRAPH;
1065 } else {
1066 if (aShiftState & STATE_CONTROL) {
1067 modifiers |= MODIFIER_CONTROL;
1069 if (aShiftState & STATE_ALT) {
1070 modifiers |= MODIFIER_ALT;
1073 if (aShiftState & STATE_CAPSLOCK) {
1074 modifiers |= MODIFIER_CAPSLOCK;
1076 return modifiers;
1079 const DeadKeyTable* VirtualKey::MatchingDeadKeyTable(
1080 const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries) const {
1081 if (!mIsDeadKey) {
1082 return nullptr;
1085 for (ShiftState shiftState = 0; shiftState < 16; shiftState++) {
1086 if (!IsDeadKey(shiftState)) {
1087 continue;
1089 const DeadKeyTable* dkt = mShiftStates[shiftState].DeadKey.Table;
1090 if (dkt && dkt->IsEqual(aDeadKeyArray, aEntries)) {
1091 return dkt;
1095 return nullptr;
1098 void VirtualKey::SetNormalChars(ShiftState aShiftState, const char16_t* aChars,
1099 uint32_t aNumOfChars) {
1100 MOZ_ASSERT(aShiftState == ToShiftStateIndex(aShiftState));
1102 SetDeadKey(aShiftState, false);
1104 for (uint32_t index = 0; index < aNumOfChars; index++) {
1105 // Ignore legacy non-printable control characters
1106 mShiftStates[aShiftState].Normal.Chars[index] =
1107 (aChars[index] >= 0x20) ? aChars[index] : 0;
1110 uint32_t len = ArrayLength(mShiftStates[aShiftState].Normal.Chars);
1111 for (uint32_t index = aNumOfChars; index < len; index++) {
1112 mShiftStates[aShiftState].Normal.Chars[index] = 0;
1116 void VirtualKey::SetDeadChar(ShiftState aShiftState, char16_t aDeadChar) {
1117 MOZ_ASSERT(aShiftState == ToShiftStateIndex(aShiftState));
1119 SetDeadKey(aShiftState, true);
1121 mShiftStates[aShiftState].DeadKey.DeadChar = aDeadChar;
1122 mShiftStates[aShiftState].DeadKey.Table = nullptr;
1125 UniCharsAndModifiers VirtualKey::GetUniChars(ShiftState aShiftState) const {
1126 UniCharsAndModifiers result = GetNativeUniChars(aShiftState);
1128 const uint8_t kShiftStateIndex = ToShiftStateIndex(aShiftState);
1129 if (!(kShiftStateIndex & STATE_CONTROL_ALT)) {
1130 // If neither Alt nor Ctrl key is pressed, just return stored data
1131 // for the key.
1132 return result;
1135 if (result.IsEmpty()) {
1136 // If Alt and/or Control are pressed and the key produces no
1137 // character, return characters which is produced by the key without
1138 // Alt and Control, and return given modifiers as is.
1139 result = GetNativeUniChars(kShiftStateIndex & ~STATE_CONTROL_ALT);
1140 result.FillModifiers(ShiftStateToModifiers(aShiftState));
1141 return result;
1144 if (IsAltGrIndex(kShiftStateIndex)) {
1145 // If AltGr or both Ctrl and Alt are pressed and the key produces
1146 // character(s), we need to clear MODIFIER_ALT and MODIFIER_CONTROL
1147 // since TextEditor won't handle eKeyPress event whose mModifiers
1148 // has MODIFIER_ALT or MODIFIER_CONTROL. Additionally, we need to
1149 // use MODIFIER_ALTGRAPH when a key produces character(s) with
1150 // AltGr or both Ctrl and Alt on Windows. See following spec issue:
1151 // <https://github.com/w3c/uievents/issues/147>
1152 Modifiers finalModifiers = ShiftStateToModifiers(aShiftState);
1153 finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
1154 finalModifiers |= MODIFIER_ALTGRAPH;
1155 result.FillModifiers(finalModifiers);
1156 return result;
1159 // Otherwise, i.e., Alt or Ctrl is pressed and it produces character(s),
1160 // check if different character(s) is produced by the key without Alt/Ctrl.
1161 // If it produces different character, we need to consume the Alt and
1162 // Ctrl modifier for TextEditor. Otherwise, the key does not produces the
1163 // character actually. So, keep setting Alt and Ctrl modifiers.
1164 UniCharsAndModifiers unmodifiedReslt =
1165 GetNativeUniChars(kShiftStateIndex & ~STATE_CONTROL_ALT);
1166 if (!result.UniCharsEqual(unmodifiedReslt)) {
1167 Modifiers finalModifiers = ShiftStateToModifiers(aShiftState);
1168 finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
1169 result.FillModifiers(finalModifiers);
1171 return result;
1174 UniCharsAndModifiers VirtualKey::GetNativeUniChars(
1175 ShiftState aShiftState) const {
1176 const uint8_t kShiftStateIndex = ToShiftStateIndex(aShiftState);
1177 UniCharsAndModifiers result;
1178 Modifiers modifiers = ShiftStateToModifiers(aShiftState);
1179 if (IsDeadKey(aShiftState)) {
1180 result.Append(mShiftStates[kShiftStateIndex].DeadKey.DeadChar, modifiers);
1181 return result;
1184 uint32_t len = ArrayLength(mShiftStates[kShiftStateIndex].Normal.Chars);
1185 for (uint32_t i = 0;
1186 i < len && mShiftStates[kShiftStateIndex].Normal.Chars[i]; i++) {
1187 result.Append(mShiftStates[kShiftStateIndex].Normal.Chars[i], modifiers);
1189 return result;
1192 // static
1193 void VirtualKey::FillKbdState(PBYTE aKbdState, const ShiftState aShiftState) {
1194 if (aShiftState & STATE_SHIFT) {
1195 aKbdState[VK_SHIFT] |= 0x80;
1196 } else {
1197 aKbdState[VK_SHIFT] &= ~0x80;
1198 aKbdState[VK_LSHIFT] &= ~0x80;
1199 aKbdState[VK_RSHIFT] &= ~0x80;
1202 if (aShiftState & STATE_ALTGRAPH) {
1203 aKbdState[VK_CONTROL] |= 0x80;
1204 aKbdState[VK_LCONTROL] |= 0x80;
1205 aKbdState[VK_RCONTROL] &= ~0x80;
1206 aKbdState[VK_MENU] |= 0x80;
1207 aKbdState[VK_LMENU] &= ~0x80;
1208 aKbdState[VK_RMENU] |= 0x80;
1209 } else {
1210 if (aShiftState & STATE_CONTROL) {
1211 aKbdState[VK_CONTROL] |= 0x80;
1212 } else {
1213 aKbdState[VK_CONTROL] &= ~0x80;
1214 aKbdState[VK_LCONTROL] &= ~0x80;
1215 aKbdState[VK_RCONTROL] &= ~0x80;
1218 if (aShiftState & STATE_ALT) {
1219 aKbdState[VK_MENU] |= 0x80;
1220 } else {
1221 aKbdState[VK_MENU] &= ~0x80;
1222 aKbdState[VK_LMENU] &= ~0x80;
1223 aKbdState[VK_RMENU] &= ~0x80;
1227 if (aShiftState & STATE_CAPSLOCK) {
1228 aKbdState[VK_CAPITAL] |= 0x01;
1229 } else {
1230 aKbdState[VK_CAPITAL] &= ~0x01;
1234 /*****************************************************************************
1235 * mozilla::widget::NativeKey
1236 *****************************************************************************/
1238 uint8_t NativeKey::sDispatchedKeyOfAppCommand = 0;
1239 NativeKey* NativeKey::sLatestInstance = nullptr;
1240 const MSG NativeKey::sEmptyMSG = {};
1241 MSG NativeKey::sLastKeyOrCharMSG = {};
1242 MSG NativeKey::sLastKeyMSG = {};
1243 char16_t NativeKey::sPendingHighSurrogate = 0;
1245 NativeKey::NativeKey(nsWindow* aWidget, const MSG& aMessage,
1246 const ModifierKeyState& aModKeyState,
1247 HKL aOverrideKeyboardLayout,
1248 nsTArray<FakeCharMsg>* aFakeCharMsgs)
1249 : mLastInstance(sLatestInstance),
1250 mRemovingMsg(sEmptyMSG),
1251 mReceivedMsg(sEmptyMSG),
1252 mWidget(aWidget),
1253 mDispatcher(aWidget->GetTextEventDispatcher()),
1254 mMsg(aMessage),
1255 mFocusedWndBeforeDispatch(::GetFocus()),
1256 mDOMKeyCode(0),
1257 mKeyNameIndex(KEY_NAME_INDEX_Unidentified),
1258 mCodeNameIndex(CODE_NAME_INDEX_UNKNOWN),
1259 mModKeyState(aModKeyState),
1260 mVirtualKeyCode(0),
1261 mOriginalVirtualKeyCode(0),
1262 mShiftedLatinChar(0),
1263 mUnshiftedLatinChar(0),
1264 mScanCode(0),
1265 mIsExtended(false),
1266 mIsRepeat(false),
1267 mIsDeadKey(false),
1268 mIsPrintableKey(false),
1269 mIsSkippableInRemoteProcess(false),
1270 mCharMessageHasGone(false),
1271 mCanIgnoreModifierStateAtKeyPress(true),
1272 mFakeCharMsgs(aFakeCharMsgs && aFakeCharMsgs->Length() ? aFakeCharMsgs
1273 : nullptr) {
1274 MOZ_LOG(gKeyLog, LogLevel::Info,
1275 ("%p NativeKey::NativeKey(aWidget=0x%p { GetWindowHandle()=0x%p }, "
1276 "aMessage=%s, aModKeyState=%s), sLatestInstance=0x%p",
1277 this, aWidget, aWidget->GetWindowHandle(), ToString(aMessage).get(),
1278 ToString(aModKeyState).get(), sLatestInstance));
1280 MOZ_ASSERT(aWidget);
1281 MOZ_ASSERT(mDispatcher);
1282 sLatestInstance = this;
1283 KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
1284 mKeyboardLayout = keyboardLayout->GetLayout();
1285 if (aOverrideKeyboardLayout && mKeyboardLayout != aOverrideKeyboardLayout) {
1286 keyboardLayout->OverrideLayout(aOverrideKeyboardLayout);
1287 mKeyboardLayout = keyboardLayout->GetLayout();
1288 MOZ_ASSERT(mKeyboardLayout == aOverrideKeyboardLayout);
1289 mIsOverridingKeyboardLayout = true;
1290 } else {
1291 mIsOverridingKeyboardLayout = false;
1292 sLastKeyOrCharMSG = aMessage;
1295 if (mMsg.message == WM_APPCOMMAND) {
1296 InitWithAppCommand();
1297 } else {
1298 InitWithKeyOrChar();
1301 MOZ_LOG(gKeyLog, LogLevel::Info,
1302 ("%p NativeKey::NativeKey(), mKeyboardLayout=0x%p, "
1303 "mFocusedWndBeforeDispatch=0x%p, mDOMKeyCode=%s, "
1304 "mKeyNameIndex=%s, mCodeNameIndex=%s, mModKeyState=%s, "
1305 "mVirtualKeyCode=%s, mOriginalVirtualKeyCode=%s, "
1306 "mCommittedCharsAndModifiers=%s, mInputtingStringAndModifiers=%s, "
1307 "mShiftedString=%s, mUnshiftedString=%s, mShiftedLatinChar=%s, "
1308 "mUnshiftedLatinChar=%s, mScanCode=0x%04X, mIsExtended=%s, "
1309 "mIsRepeat=%s, mIsDeadKey=%s, mIsPrintableKey=%s, "
1310 "mIsSkippableInRemoteProcess=%s, mCharMessageHasGone=%s, "
1311 "mIsOverridingKeyboardLayout=%s",
1312 this, mKeyboardLayout, mFocusedWndBeforeDispatch,
1313 GetDOMKeyCodeName(mDOMKeyCode).get(), ToString(mKeyNameIndex).get(),
1314 ToString(mCodeNameIndex).get(), ToString(mModKeyState).get(),
1315 GetVirtualKeyCodeName(mVirtualKeyCode).get(),
1316 GetVirtualKeyCodeName(mOriginalVirtualKeyCode).get(),
1317 ToString(mCommittedCharsAndModifiers).get(),
1318 ToString(mInputtingStringAndModifiers).get(),
1319 ToString(mShiftedString).get(), ToString(mUnshiftedString).get(),
1320 GetCharacterCodeName(mShiftedLatinChar).get(),
1321 GetCharacterCodeName(mUnshiftedLatinChar).get(), mScanCode,
1322 GetBoolName(mIsExtended), GetBoolName(mIsRepeat),
1323 GetBoolName(mIsDeadKey), GetBoolName(mIsPrintableKey),
1324 GetBoolName(mIsSkippableInRemoteProcess),
1325 GetBoolName(mCharMessageHasGone),
1326 GetBoolName(mIsOverridingKeyboardLayout)));
1329 void NativeKey::InitIsSkippableForKeyOrChar(const MSG& aLastKeyMSG) {
1330 mIsSkippableInRemoteProcess = false;
1332 if (!mIsRepeat) {
1333 // If the message is not repeated key message, the event should be always
1334 // handled in remote process even if it's too old.
1335 return;
1338 // Keyboard utilities may send us some generated messages and such messages
1339 // may be marked as "repeated", e.g., SendInput() calls with
1340 // KEYEVENTF_UNICODE but without KEYEVENTF_KEYUP. However, key sequence
1341 // comes from such utilities may be really important. For example, utilities
1342 // may send WM_KEYDOWN for VK_BACK to remove previous character and send
1343 // WM_KEYDOWN for VK_PACKET to insert a composite character. Therefore, we
1344 // should check if current message and previous key message are caused by
1345 // same physical key. If not, the message may be generated by such
1346 // utility.
1347 // XXX With this approach, if VK_BACK messages are generated with known
1348 // scancode, we cannot distinguish whether coming VK_BACK message is
1349 // actually repeated by the auto-repeat feature. Currently, we need
1350 // this hack only for "SinhalaTamil IME" and fortunately, it generates
1351 // VK_BACK messages with odd scancode. So, we don't need to handle
1352 // VK_BACK specially at least for now.
1354 if (mCodeNameIndex == CODE_NAME_INDEX_UNKNOWN) {
1355 // If current event is not caused by physical key operation, it may be
1356 // caused by a keyboard utility. If so, the event shouldn't be ignored by
1357 // BrowserChild since it want to insert the character, delete a character or
1358 // move caret.
1359 return;
1362 if (mOriginalVirtualKeyCode == VK_PACKET) {
1363 // If the message is VK_PACKET, that means that a keyboard utility
1364 // tries to insert a character.
1365 return;
1368 switch (mMsg.message) {
1369 case WM_KEYDOWN:
1370 case WM_SYSKEYDOWN:
1371 case WM_CHAR:
1372 case WM_SYSCHAR:
1373 case WM_DEADCHAR:
1374 case WM_SYSDEADCHAR:
1375 // However, some keyboard layouts may send some keyboard messages with
1376 // activating the bit. If we dispatch repeated keyboard events, they
1377 // may be ignored by BrowserChild due to performance reason. So, we need
1378 // to check if actually a physical key is repeated by the auto-repeat
1379 // feature.
1380 switch (aLastKeyMSG.message) {
1381 case WM_KEYDOWN:
1382 case WM_SYSKEYDOWN:
1383 if (aLastKeyMSG.wParam == VK_PACKET) {
1384 // If the last message was VK_PACKET, that means that a keyboard
1385 // utility tried to insert a character. So, current message is
1386 // not repeated key event of the previous event.
1387 return;
1389 // Let's check whether current message and previous message are
1390 // caused by same physical key.
1391 mIsSkippableInRemoteProcess =
1392 mScanCode == WinUtils::GetScanCode(aLastKeyMSG.lParam) &&
1393 mIsExtended == WinUtils::IsExtendedScanCode(aLastKeyMSG.lParam);
1394 return;
1395 default:
1396 // If previous message is not a keydown, this must not be generated
1397 // by the auto-repeat feature.
1398 return;
1400 case WM_APPCOMMAND:
1401 MOZ_ASSERT_UNREACHABLE(
1402 "WM_APPCOMMAND should be handled in "
1403 "InitWithAppCommand()");
1404 return;
1405 default:
1406 // keyup message shouldn't be repeated by the auto-repeat feature.
1407 return;
1411 void NativeKey::InitWithKeyOrChar() {
1412 MSG lastKeyMSG = sLastKeyMSG;
1413 char16_t pendingHighSurrogate = sPendingHighSurrogate;
1414 mScanCode = WinUtils::GetScanCode(mMsg.lParam);
1415 mIsExtended = WinUtils::IsExtendedScanCode(mMsg.lParam);
1416 switch (mMsg.message) {
1417 case WM_KEYDOWN:
1418 case WM_SYSKEYDOWN:
1419 sPendingHighSurrogate = 0;
1420 [[fallthrough]];
1421 case WM_KEYUP:
1422 case WM_SYSKEYUP: {
1423 // Modify sLastKeyMSG now since retrieving following char messages may
1424 // cause sending another key message if odd tool hooks GetMessage(),
1425 // PeekMessage().
1426 sLastKeyMSG = mMsg;
1428 // Note that we don't need to compute raw virtual keycode here even when
1429 // it's VK_PROCESS (i.e., already handled by IME) because we need to
1430 // export it as DOM_VK_PROCESS and KEY_NAME_INDEX_Process.
1431 mOriginalVirtualKeyCode = static_cast<uint8_t>(mMsg.wParam);
1433 // If the key message is sent from other application like a11y tools, the
1434 // scancode value might not be set proper value. Then, probably the value
1435 // is 0.
1436 // NOTE: If the virtual keycode can be caused by both non-extended key
1437 // and extended key, the API returns the non-extended key's
1438 // scancode. E.g., VK_LEFT causes "4" key on numpad.
1439 if (!mScanCode && mOriginalVirtualKeyCode != VK_PACKET) {
1440 uint16_t scanCodeEx = ComputeScanCodeExFromVirtualKeyCode(mMsg.wParam);
1441 if (scanCodeEx) {
1442 mScanCode = static_cast<uint8_t>(scanCodeEx & 0xFF);
1443 uint8_t extended = static_cast<uint8_t>((scanCodeEx & 0xFF00) >> 8);
1444 mIsExtended = (extended == 0xE0) || (extended == 0xE1);
1448 // Most keys are not distinguished as left or right keys.
1449 bool isLeftRightDistinguishedKey = false;
1451 // mOriginalVirtualKeyCode must not distinguish left or right of
1452 // Shift, Control or Alt.
1453 switch (mOriginalVirtualKeyCode) {
1454 case VK_SHIFT:
1455 case VK_CONTROL:
1456 case VK_MENU:
1457 isLeftRightDistinguishedKey = true;
1458 break;
1459 case VK_LSHIFT:
1460 case VK_RSHIFT:
1461 mVirtualKeyCode = mOriginalVirtualKeyCode;
1462 mOriginalVirtualKeyCode = VK_SHIFT;
1463 isLeftRightDistinguishedKey = true;
1464 break;
1465 case VK_LCONTROL:
1466 case VK_RCONTROL:
1467 mVirtualKeyCode = mOriginalVirtualKeyCode;
1468 mOriginalVirtualKeyCode = VK_CONTROL;
1469 isLeftRightDistinguishedKey = true;
1470 break;
1471 case VK_LMENU:
1472 case VK_RMENU:
1473 mVirtualKeyCode = mOriginalVirtualKeyCode;
1474 mOriginalVirtualKeyCode = VK_MENU;
1475 isLeftRightDistinguishedKey = true;
1476 break;
1479 // If virtual keycode (left-right distinguished keycode) is already
1480 // computed, we don't need to do anymore.
1481 if (mVirtualKeyCode) {
1482 break;
1485 // If the keycode doesn't have LR distinguished keycode, we just set
1486 // mOriginalVirtualKeyCode to mVirtualKeyCode. Note that don't compute
1487 // it from MapVirtualKeyEx() because the scan code might be wrong if
1488 // the message is sent/posted by other application. Then, we will compute
1489 // unexpected keycode from the scan code.
1490 if (!isLeftRightDistinguishedKey) {
1491 break;
1494 NS_ASSERTION(!mVirtualKeyCode,
1495 "mVirtualKeyCode has been computed already");
1497 // Otherwise, compute the virtual keycode with MapVirtualKeyEx().
1498 mVirtualKeyCode = ComputeVirtualKeyCodeFromScanCodeEx();
1500 // Following code shouldn't be used now because we compute scancode value
1501 // if we detect that the sender doesn't set proper scancode.
1502 // However, the detection might fail. Therefore, let's keep using this.
1503 switch (mOriginalVirtualKeyCode) {
1504 case VK_CONTROL:
1505 if (mVirtualKeyCode != VK_LCONTROL &&
1506 mVirtualKeyCode != VK_RCONTROL) {
1507 mVirtualKeyCode = mIsExtended ? VK_RCONTROL : VK_LCONTROL;
1509 break;
1510 case VK_MENU:
1511 if (mVirtualKeyCode != VK_LMENU && mVirtualKeyCode != VK_RMENU) {
1512 mVirtualKeyCode = mIsExtended ? VK_RMENU : VK_LMENU;
1514 break;
1515 case VK_SHIFT:
1516 if (mVirtualKeyCode != VK_LSHIFT && mVirtualKeyCode != VK_RSHIFT) {
1517 // Neither left shift nor right shift is an extended key,
1518 // let's use VK_LSHIFT for unknown mapping.
1519 mVirtualKeyCode = VK_LSHIFT;
1521 break;
1522 default:
1523 MOZ_CRASH("Unsupported mOriginalVirtualKeyCode");
1525 break;
1527 case WM_CHAR:
1528 case WM_UNICHAR:
1529 case WM_SYSCHAR:
1530 sPendingHighSurrogate = 0;
1531 // If there is another instance and it is trying to remove a char message
1532 // from the queue, this message should be handled in the old instance.
1533 if (IsAnotherInstanceRemovingCharMessage()) {
1534 // XXX Do we need to make mReceivedMsg an array?
1535 MOZ_ASSERT(IsEmptyMSG(mLastInstance->mReceivedMsg));
1536 MOZ_LOG(
1537 gKeyLog, LogLevel::Warning,
1538 ("%p NativeKey::InitWithKeyOrChar(), WARNING, detecting another "
1539 "instance is trying to remove a char message, so, this instance "
1540 "should do nothing, mLastInstance=0x%p, mRemovingMsg=%s, "
1541 "mReceivedMsg=%s",
1542 this, mLastInstance, ToString(mLastInstance->mRemovingMsg).get(),
1543 ToString(mLastInstance->mReceivedMsg).get()));
1544 mLastInstance->mReceivedMsg = mMsg;
1545 return;
1548 // NOTE: If other applications like a11y tools sends WM_*CHAR without
1549 // scancode, we cannot compute virtual keycode. I.e., with such
1550 // applications, we cannot generate proper KeyboardEvent.code value.
1552 mVirtualKeyCode = mOriginalVirtualKeyCode =
1553 ComputeVirtualKeyCodeFromScanCodeEx();
1554 NS_ASSERTION(mVirtualKeyCode, "Failed to compute virtual keycode");
1555 break;
1556 default: {
1557 MOZ_CRASH_UNSAFE_PRINTF("Unsupported message: 0x%04X", mMsg.message);
1558 break;
1562 if (!mVirtualKeyCode) {
1563 mVirtualKeyCode = mOriginalVirtualKeyCode;
1566 KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
1567 mDOMKeyCode =
1568 keyboardLayout->ConvertNativeKeyCodeToDOMKeyCode(mVirtualKeyCode);
1569 // Be aware, keyboard utilities can change non-printable keys to printable
1570 // keys. In such case, we should make the key value as a printable key.
1571 // FYI: IsFollowedByPrintableCharMessage() returns true only when it's
1572 // handling a keydown message.
1573 mKeyNameIndex =
1574 IsFollowedByPrintableCharMessage()
1575 ? KEY_NAME_INDEX_USE_STRING
1576 : keyboardLayout->ConvertNativeKeyCodeToKeyNameIndex(mVirtualKeyCode);
1577 mCodeNameIndex = KeyboardLayout::ConvertScanCodeToCodeNameIndex(
1578 GetScanCodeWithExtendedFlag());
1580 // If next message of WM_(SYS)KEYDOWN is WM_*CHAR message and the key
1581 // combination is not reserved by the system, let's consume it now.
1582 // TODO: We cannot initialize mCommittedCharsAndModifiers for VK_PACKET
1583 // if the message is WM_KEYUP because we don't have preceding
1584 // WM_CHAR message.
1585 // TODO: Like Edge, we shouldn't dispatch two sets of keyboard events
1586 // for a Unicode character in non-BMP because its key value looks
1587 // broken and not good thing for our editor if only one keydown or
1588 // keypress event's default is prevented. I guess, we should store
1589 // key message information globally and we should wait following
1590 // WM_KEYDOWN if following WM_CHAR is a part of a Unicode character.
1591 if ((mMsg.message == WM_KEYDOWN || mMsg.message == WM_SYSKEYDOWN) &&
1592 !IsReservedBySystem()) {
1593 MSG charMsg;
1594 while (GetFollowingCharMessage(charMsg)) {
1595 // Although, got message shouldn't be WM_NULL in desktop apps,
1596 // we should keep checking this. FYI: This was added for Metrofox.
1597 if (charMsg.message == WM_NULL) {
1598 continue;
1600 MOZ_LOG(gKeyLog, LogLevel::Info,
1601 ("%p NativeKey::InitWithKeyOrChar(), removed char message, %s",
1602 this, ToString(charMsg).get()));
1603 Unused << NS_WARN_IF(charMsg.hwnd != mMsg.hwnd);
1604 mFollowingCharMsgs.AppendElement(charMsg);
1606 if (mFollowingCharMsgs.Length() == 1) {
1607 // If we receive a keydown message for a high-surrogate, a low-surrogate
1608 // keydown message **will** and should follow it. We cannot translate the
1609 // following WM_KEYDOWN message for the low-surrogate right now since
1610 // it's not yet queued into the message queue yet. Therefore, we need to
1611 // wait next one to dispatch keypress event with setting its `.key` value
1612 // to a surrogate pair rather than setting it to a lone surrogate.
1613 // FYI: This may happen with typing a non-BMP character on the touch
1614 // keyboard on Windows 10 or later except when an IME is installed. (If
1615 // IME is installed, composition is used instead.)
1616 if (IS_HIGH_SURROGATE(mFollowingCharMsgs[0].wParam)) {
1617 if (pendingHighSurrogate) {
1618 MOZ_LOG(gKeyLog, LogLevel::Warning,
1619 ("%p NativeKey::InitWithKeyOrChar(), there is pending "
1620 "high surrogate input, but received another high surrogate "
1621 "input. The previous one is discarded",
1622 this));
1624 sPendingHighSurrogate = mFollowingCharMsgs[0].wParam;
1625 mFollowingCharMsgs.Clear();
1626 } else if (IS_LOW_SURROGATE(mFollowingCharMsgs[0].wParam)) {
1627 // If we stopped dispathing a keypress event for a preceding
1628 // high-surrogate, treat this keydown (for a low-surrogate) as
1629 // introducing both the high surrogate and the low surrogate.
1630 if (pendingHighSurrogate) {
1631 MSG charMsg = mFollowingCharMsgs[0];
1632 mFollowingCharMsgs[0].wParam = pendingHighSurrogate;
1633 mFollowingCharMsgs.AppendElement(std::move(charMsg));
1634 } else {
1635 MOZ_LOG(
1636 gKeyLog, LogLevel::Warning,
1637 ("%p NativeKey::InitWithKeyOrChar(), there is no pending high "
1638 "surrogate input, but received lone low surrogate input",
1639 this));
1641 } else {
1642 MOZ_LOG(gKeyLog, LogLevel::Warning,
1643 ("%p NativeKey::InitWithKeyOrChar(), there is pending "
1644 "high surrogate input, but received non-surrogate input. "
1645 "The high surrogate input is discarded",
1646 this));
1648 } else {
1649 MOZ_LOG(gKeyLog, LogLevel::Warning,
1650 ("%p NativeKey::InitWithKeyOrChar(), there is pending "
1651 "high surrogate input, but received 2 or more character input. "
1652 "The high surrogate input is discarded",
1653 this));
1657 keyboardLayout->InitNativeKey(*this);
1659 // Now, we can know if the key produces character(s) or a dead key with
1660 // AltGraph modifier. When user emulates AltGr key press with pressing
1661 // both Ctrl and Alt and the key produces character(s) or a dead key, we
1662 // need to replace Control and Alt state with AltGraph if the keyboard
1663 // layout has AltGr key.
1664 // Note that if Ctrl and/or Alt are pressed (not to emulate to press AltGr),
1665 // we need to set actual modifiers to eKeyDown and eKeyUp.
1666 if (MaybeEmulatingAltGraph() &&
1667 (mCommittedCharsAndModifiers.IsProducingCharsWithAltGr() ||
1668 mKeyNameIndex == KEY_NAME_INDEX_Dead)) {
1669 mModKeyState.Unset(MODIFIER_CONTROL | MODIFIER_ALT);
1670 mModKeyState.Set(MODIFIER_ALTGRAPH);
1673 mIsDeadKey =
1674 (IsFollowedByDeadCharMessage() ||
1675 keyboardLayout->IsDeadKey(mOriginalVirtualKeyCode, mModKeyState));
1676 mIsPrintableKey = mKeyNameIndex == KEY_NAME_INDEX_USE_STRING ||
1677 KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode);
1678 // The repeat count in mMsg.lParam isn't useful to check whether the event
1679 // is caused by the auto-repeat feature because it's not incremented even
1680 // if it's repeated twice or more (i.e., always 1). Therefore, we need to
1681 // check previous key state (31th bit) instead. If it's 1, the key was down
1682 // before the message was sent.
1683 mIsRepeat = (mMsg.lParam & (1 << 30)) != 0;
1684 InitIsSkippableForKeyOrChar(lastKeyMSG);
1686 if (IsKeyDownMessage()) {
1687 // Compute some strings which may be inputted by the key with various
1688 // modifier state if this key event won't cause text input actually.
1689 // They will be used for setting mAlternativeCharCodes in the callback
1690 // method which will be called by TextEventDispatcher.
1691 if (!IsFollowedByPrintableCharMessage()) {
1692 ComputeInputtingStringWithKeyboardLayout();
1694 // Remove odd char messages if there are.
1695 RemoveFollowingOddCharMessages();
1699 void NativeKey::InitCommittedCharsAndModifiersWithFollowingCharMessages() {
1700 mCommittedCharsAndModifiers.Clear();
1701 // This should cause inputting text in focused editor. However, it
1702 // ignores keypress events whose altKey or ctrlKey is true.
1703 // Therefore, we need to remove these modifier state here.
1704 Modifiers modifiers = mModKeyState.GetModifiers();
1705 if (IsFollowedByPrintableCharMessage()) {
1706 modifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
1707 if (MaybeEmulatingAltGraph()) {
1708 modifiers |= MODIFIER_ALTGRAPH;
1711 // NOTE: This method assumes that WM_CHAR and WM_SYSCHAR are never retrieved
1712 // at same time.
1713 for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
1714 // Ignore non-printable char messages.
1715 if (!IsPrintableCharOrSysCharMessage(mFollowingCharMsgs[i])) {
1716 continue;
1718 char16_t ch = static_cast<char16_t>(mFollowingCharMsgs[i].wParam);
1719 mCommittedCharsAndModifiers.Append(ch, modifiers);
1723 NativeKey::~NativeKey() {
1724 MOZ_LOG(gKeyLog, LogLevel::Debug,
1725 ("%p NativeKey::~NativeKey(), destroyed", this));
1726 if (mIsOverridingKeyboardLayout) {
1727 KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
1728 keyboardLayout->RestoreLayout();
1730 sLatestInstance = mLastInstance;
1733 void NativeKey::InitWithAppCommand() {
1734 if (GET_DEVICE_LPARAM(mMsg.lParam) != FAPPCOMMAND_KEY) {
1735 return;
1738 uint32_t appCommand = GET_APPCOMMAND_LPARAM(mMsg.lParam);
1739 switch (GET_APPCOMMAND_LPARAM(mMsg.lParam)) {
1740 #undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX
1741 #define NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX(aAppCommand, aKeyNameIndex) \
1742 case aAppCommand: \
1743 mKeyNameIndex = aKeyNameIndex; \
1744 break;
1746 #include "NativeKeyToDOMKeyName.h"
1748 #undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX
1750 default:
1751 mKeyNameIndex = KEY_NAME_INDEX_Unidentified;
1754 // Guess the virtual keycode which caused this message.
1755 switch (appCommand) {
1756 case APPCOMMAND_BROWSER_BACKWARD:
1757 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_BACK;
1758 break;
1759 case APPCOMMAND_BROWSER_FORWARD:
1760 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_FORWARD;
1761 break;
1762 case APPCOMMAND_BROWSER_REFRESH:
1763 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_REFRESH;
1764 break;
1765 case APPCOMMAND_BROWSER_STOP:
1766 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_STOP;
1767 break;
1768 case APPCOMMAND_BROWSER_SEARCH:
1769 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_SEARCH;
1770 break;
1771 case APPCOMMAND_BROWSER_FAVORITES:
1772 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_FAVORITES;
1773 break;
1774 case APPCOMMAND_BROWSER_HOME:
1775 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_HOME;
1776 break;
1777 case APPCOMMAND_VOLUME_MUTE:
1778 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_VOLUME_MUTE;
1779 break;
1780 case APPCOMMAND_VOLUME_DOWN:
1781 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_VOLUME_DOWN;
1782 break;
1783 case APPCOMMAND_VOLUME_UP:
1784 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_VOLUME_UP;
1785 break;
1786 case APPCOMMAND_MEDIA_NEXTTRACK:
1787 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_NEXT_TRACK;
1788 break;
1789 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
1790 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_PREV_TRACK;
1791 break;
1792 case APPCOMMAND_MEDIA_STOP:
1793 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_STOP;
1794 break;
1795 case APPCOMMAND_MEDIA_PLAY_PAUSE:
1796 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_PLAY_PAUSE;
1797 break;
1798 case APPCOMMAND_LAUNCH_MAIL:
1799 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_MAIL;
1800 break;
1801 case APPCOMMAND_LAUNCH_MEDIA_SELECT:
1802 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_MEDIA_SELECT;
1803 break;
1804 case APPCOMMAND_LAUNCH_APP1:
1805 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_APP1;
1806 break;
1807 case APPCOMMAND_LAUNCH_APP2:
1808 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_APP2;
1809 break;
1810 default:
1811 return;
1814 uint16_t scanCodeEx = ComputeScanCodeExFromVirtualKeyCode(mVirtualKeyCode);
1815 mScanCode = static_cast<uint8_t>(scanCodeEx & 0xFF);
1816 uint8_t extended = static_cast<uint8_t>((scanCodeEx & 0xFF00) >> 8);
1817 mIsExtended = (extended == 0xE0) || (extended == 0xE1);
1818 mDOMKeyCode = KeyboardLayout::GetInstance()->ConvertNativeKeyCodeToDOMKeyCode(
1819 mOriginalVirtualKeyCode);
1820 mCodeNameIndex = KeyboardLayout::ConvertScanCodeToCodeNameIndex(
1821 GetScanCodeWithExtendedFlag());
1822 // If we can map the WM_APPCOMMAND to a virtual keycode, we can trust
1823 // the result of GetKeyboardState(). Otherwise, we dispatch both
1824 // keydown and keyup events from WM_APPCOMMAND handler. Therefore,
1825 // even if WM_APPCOMMAND is caused by auto key repeat, web apps receive
1826 // a pair of DOM keydown and keyup events. I.e., KeyboardEvent.repeat
1827 // should be never true of such keys.
1828 // XXX Isn't the key state always true? If the key press caused this
1829 // WM_APPCOMMAND, that means it's pressed at that time.
1830 if (mVirtualKeyCode) {
1831 BYTE kbdState[256];
1832 memset(kbdState, 0, sizeof(kbdState));
1833 ::GetKeyboardState(kbdState);
1834 mIsSkippableInRemoteProcess = mIsRepeat = !!kbdState[mVirtualKeyCode];
1838 bool NativeKey::MaybeEmulatingAltGraph() const {
1839 return IsControl() && IsAlt() && KeyboardLayout::GetInstance()->HasAltGr();
1842 // static
1843 bool NativeKey::IsControlChar(char16_t aChar) {
1844 static const char16_t U_SPACE = 0x20;
1845 static const char16_t U_DELETE = 0x7F;
1846 return aChar < U_SPACE || aChar == U_DELETE;
1849 bool NativeKey::IsFollowedByDeadCharMessage() const {
1850 if (mFollowingCharMsgs.IsEmpty()) {
1851 return false;
1853 return IsDeadCharMessage(mFollowingCharMsgs[0]);
1856 bool NativeKey::IsFollowedByPrintableCharMessage() const {
1857 for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
1858 if (IsPrintableCharMessage(mFollowingCharMsgs[i])) {
1859 return true;
1862 return false;
1865 bool NativeKey::IsFollowedByPrintableCharOrSysCharMessage() const {
1866 for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
1867 if (IsPrintableCharOrSysCharMessage(mFollowingCharMsgs[i])) {
1868 return true;
1871 return false;
1874 bool NativeKey::IsReservedBySystem() const {
1875 // Alt+Space key is handled by OS, we shouldn't touch it.
1876 if (mModKeyState.IsAlt() && !mModKeyState.IsControl() &&
1877 mVirtualKeyCode == VK_SPACE) {
1878 return true;
1881 // XXX How about Alt+F4? We receive WM_SYSKEYDOWN for F4 before closing the
1882 // window. Although, we don't prevent to close the window but the key
1883 // event shouldn't be exposed to the web.
1885 return false;
1888 bool NativeKey::IsIMEDoingKakuteiUndo() const {
1889 // Following message pattern is caused by "Kakutei-Undo" of ATOK or WXG:
1890 // ---------------------------------------------------------------------------
1891 // WM_KEYDOWN * n (wParam = VK_BACK, lParam = 0x1)
1892 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC0000001) # ATOK
1893 // WM_IME_STARTCOMPOSITION * 1 (wParam = 0x0, lParam = 0x0)
1894 // WM_IME_COMPOSITION * 1 (wParam = 0x0, lParam = 0x1BF)
1895 // WM_CHAR * n (wParam = VK_BACK, lParam = 0x1)
1896 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC00E0001)
1897 // ---------------------------------------------------------------------------
1898 // This doesn't match usual key message pattern such as:
1899 // WM_KEYDOWN -> WM_CHAR -> WM_KEYDOWN -> WM_CHAR -> ... -> WM_KEYUP
1900 // See following bugs for the detail.
1901 // https://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese)
1902 // https://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English)
1903 MSG startCompositionMsg, compositionMsg, charMsg;
1904 return WinUtils::PeekMessage(&startCompositionMsg, mMsg.hwnd,
1905 WM_IME_STARTCOMPOSITION, WM_IME_STARTCOMPOSITION,
1906 PM_NOREMOVE | PM_NOYIELD) &&
1907 WinUtils::PeekMessage(&compositionMsg, mMsg.hwnd, WM_IME_COMPOSITION,
1908 WM_IME_COMPOSITION, PM_NOREMOVE | PM_NOYIELD) &&
1909 WinUtils::PeekMessage(&charMsg, mMsg.hwnd, WM_CHAR, WM_CHAR,
1910 PM_NOREMOVE | PM_NOYIELD) &&
1911 startCompositionMsg.wParam == 0x0 &&
1912 startCompositionMsg.lParam == 0x0 && compositionMsg.wParam == 0x0 &&
1913 compositionMsg.lParam == 0x1BF && charMsg.wParam == VK_BACK &&
1914 charMsg.lParam == 0x1 &&
1915 startCompositionMsg.time <= compositionMsg.time &&
1916 compositionMsg.time <= charMsg.time;
1919 void NativeKey::RemoveFollowingOddCharMessages() {
1920 MOZ_ASSERT(IsKeyDownMessage());
1922 // If the keydown message is synthesized for automated tests, there is
1923 // nothing to do here.
1924 if (mFakeCharMsgs) {
1925 return;
1928 // If there are some following char messages before another key message,
1929 // there is nothing to do here.
1930 if (!mFollowingCharMsgs.IsEmpty()) {
1931 return;
1934 // If the handling key isn't Backspace, there is nothing to do here.
1935 if (mOriginalVirtualKeyCode != VK_BACK) {
1936 return;
1939 // If we don't see the odd message pattern, there is nothing to do here.
1940 if (!IsIMEDoingKakuteiUndo()) {
1941 return;
1944 // Otherwise, we need to remove odd WM_CHAR messages for ATOK or WXG (both
1945 // of them are Japanese IME).
1946 MSG msg;
1947 while (WinUtils::PeekMessage(&msg, mMsg.hwnd, WM_CHAR, WM_CHAR,
1948 PM_REMOVE | PM_NOYIELD)) {
1949 if (msg.message != WM_CHAR) {
1950 MOZ_RELEASE_ASSERT(msg.message == WM_NULL,
1951 "Unexpected message was removed");
1952 continue;
1954 MOZ_LOG(
1955 gKeyLog, LogLevel::Info,
1956 ("%p NativeKey::RemoveFollowingOddCharMessages(), removed odd char "
1957 "message, %s",
1958 this, ToString(msg).get()));
1959 mRemovedOddCharMsgs.AppendElement(msg);
1963 UINT NativeKey::GetScanCodeWithExtendedFlag() const {
1964 if (!mIsExtended) {
1965 return mScanCode;
1967 return (0xE000 | mScanCode);
1970 uint32_t NativeKey::GetKeyLocation() const {
1971 switch (mVirtualKeyCode) {
1972 case VK_LSHIFT:
1973 case VK_LCONTROL:
1974 case VK_LMENU:
1975 case VK_LWIN:
1976 return eKeyLocationLeft;
1978 case VK_RSHIFT:
1979 case VK_RCONTROL:
1980 case VK_RMENU:
1981 case VK_RWIN:
1982 return eKeyLocationRight;
1984 case VK_RETURN:
1985 // XXX This code assumes that all keyboard drivers use same mapping.
1986 return !mIsExtended ? eKeyLocationStandard : eKeyLocationNumpad;
1988 case VK_INSERT:
1989 case VK_DELETE:
1990 case VK_END:
1991 case VK_DOWN:
1992 case VK_NEXT:
1993 case VK_LEFT:
1994 case VK_CLEAR:
1995 case VK_RIGHT:
1996 case VK_HOME:
1997 case VK_UP:
1998 case VK_PRIOR:
1999 // XXX This code assumes that all keyboard drivers use same mapping.
2000 return mIsExtended ? eKeyLocationStandard : eKeyLocationNumpad;
2002 // NumLock key isn't included due to IE9's behavior.
2003 case VK_NUMPAD0:
2004 case VK_NUMPAD1:
2005 case VK_NUMPAD2:
2006 case VK_NUMPAD3:
2007 case VK_NUMPAD4:
2008 case VK_NUMPAD5:
2009 case VK_NUMPAD6:
2010 case VK_NUMPAD7:
2011 case VK_NUMPAD8:
2012 case VK_NUMPAD9:
2013 case VK_DECIMAL:
2014 case VK_DIVIDE:
2015 case VK_MULTIPLY:
2016 case VK_SUBTRACT:
2017 case VK_ADD:
2018 // Separator key of Brazilian keyboard or JIS keyboard for Mac
2019 case VK_ABNT_C2:
2020 return eKeyLocationNumpad;
2022 case VK_SHIFT:
2023 case VK_CONTROL:
2024 case VK_MENU:
2025 NS_WARNING("Failed to decide the key location?");
2026 [[fallthrough]];
2028 default:
2029 return eKeyLocationStandard;
2033 uint8_t NativeKey::ComputeVirtualKeyCodeFromScanCode() const {
2034 return static_cast<uint8_t>(
2035 ::MapVirtualKeyEx(mScanCode, MAPVK_VSC_TO_VK, mKeyboardLayout));
2038 uint8_t NativeKey::ComputeVirtualKeyCodeFromScanCodeEx() const {
2039 // MapVirtualKeyEx() has been improved for supporting extended keys since
2040 // Vista. When we call it for mapping a scancode of an extended key and
2041 // a virtual keycode, we need to add 0xE000 to the scancode.
2042 return static_cast<uint8_t>(::MapVirtualKeyEx(
2043 GetScanCodeWithExtendedFlag(), MAPVK_VSC_TO_VK_EX, mKeyboardLayout));
2046 uint16_t NativeKey::ComputeScanCodeExFromVirtualKeyCode(
2047 UINT aVirtualKeyCode) const {
2048 return static_cast<uint16_t>(
2049 ::MapVirtualKeyEx(aVirtualKeyCode, MAPVK_VK_TO_VSC_EX, mKeyboardLayout));
2052 char16_t NativeKey::ComputeUnicharFromScanCode() const {
2053 return static_cast<char16_t>(::MapVirtualKeyEx(
2054 ComputeVirtualKeyCodeFromScanCode(), MAPVK_VK_TO_CHAR, mKeyboardLayout));
2057 nsEventStatus NativeKey::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const {
2058 return InitKeyEvent(aKeyEvent, mModKeyState);
2061 nsEventStatus NativeKey::InitKeyEvent(
2062 WidgetKeyboardEvent& aKeyEvent,
2063 const ModifierKeyState& aModKeyState) const {
2064 if (mWidget->Destroyed()) {
2065 MOZ_CRASH("NativeKey tries to dispatch a key event on destroyed widget");
2068 LayoutDeviceIntPoint point(0, 0);
2069 mWidget->InitEvent(aKeyEvent, &point);
2071 switch (aKeyEvent.mMessage) {
2072 case eKeyDown:
2073 // If it was followed by a char message but it was consumed by somebody,
2074 // we should mark it as consumed because somebody must have handled it
2075 // and we should prevent to do "double action" for the key operation.
2076 // However, for compatibility with older version and other browsers,
2077 // we should dispatch the events even in the web content.
2078 if (mCharMessageHasGone) {
2079 aKeyEvent.PreventDefaultBeforeDispatch(CrossProcessForwarding::eAllow);
2081 aKeyEvent.mKeyCode = mDOMKeyCode;
2082 // Unique id for this keydown event and its associated keypress.
2083 sUniqueKeyEventId++;
2084 aKeyEvent.mUniqueId = sUniqueKeyEventId;
2085 break;
2086 case eKeyUp:
2087 aKeyEvent.mKeyCode = mDOMKeyCode;
2088 // Set defaultPrevented of the key event if the VK_MENU is not a system
2089 // key release, so that the menu bar does not trigger. This helps avoid
2090 // triggering the menu bar for ALT key accelerators used in assistive
2091 // technologies such as Window-Eyes and ZoomText or for switching open
2092 // state of IME. On the other hand, we should dispatch the events even
2093 // in the web content for compatibility with older version and other
2094 // browsers.
2095 if (mOriginalVirtualKeyCode == VK_MENU && mMsg.message != WM_SYSKEYUP) {
2096 aKeyEvent.PreventDefaultBeforeDispatch(CrossProcessForwarding::eAllow);
2098 break;
2099 case eKeyPress:
2100 MOZ_ASSERT(!mCharMessageHasGone,
2101 "If following char message was consumed by somebody, "
2102 "keydown event should be consumed above");
2103 aKeyEvent.mUniqueId = sUniqueKeyEventId;
2104 break;
2105 default:
2106 MOZ_CRASH("Invalid event message");
2109 aKeyEvent.mIsRepeat = mIsRepeat;
2110 aKeyEvent.mMaybeSkippableInRemoteProcess = mIsSkippableInRemoteProcess;
2111 aKeyEvent.mKeyNameIndex = mKeyNameIndex;
2112 if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
2113 aKeyEvent.mKeyValue = mCommittedCharsAndModifiers.ToString();
2115 aKeyEvent.mCodeNameIndex = mCodeNameIndex;
2116 MOZ_ASSERT(mCodeNameIndex != CODE_NAME_INDEX_USE_STRING);
2117 aKeyEvent.mLocation = GetKeyLocation();
2118 aModKeyState.InitInputEvent(aKeyEvent);
2120 KeyboardLayout::NotifyIdleServiceOfUserActivity();
2122 MOZ_LOG(
2123 gKeyLog, LogLevel::Info,
2124 ("%p NativeKey::InitKeyEvent(), initialized, aKeyEvent={ "
2125 "mMessage=%s, mKeyNameIndex=%s, mKeyValue=\"%s\", mCodeNameIndex=%s, "
2126 "mKeyCode=%s, mLocation=%s, mModifiers=%s, DefaultPrevented()=%s }",
2127 this, ToChar(aKeyEvent.mMessage),
2128 ToString(aKeyEvent.mKeyNameIndex).get(),
2129 NS_ConvertUTF16toUTF8(aKeyEvent.mKeyValue).get(),
2130 ToString(aKeyEvent.mCodeNameIndex).get(),
2131 GetDOMKeyCodeName(aKeyEvent.mKeyCode).get(),
2132 GetKeyLocationName(aKeyEvent.mLocation).get(),
2133 GetModifiersName(aKeyEvent.mModifiers).get(),
2134 GetBoolName(aKeyEvent.DefaultPrevented())));
2136 return aKeyEvent.DefaultPrevented() ? nsEventStatus_eConsumeNoDefault
2137 : nsEventStatus_eIgnore;
2140 bool NativeKey::DispatchCommandEvent(uint32_t aEventCommand) const {
2141 RefPtr<nsAtom> command;
2142 switch (aEventCommand) {
2143 case APPCOMMAND_BROWSER_BACKWARD:
2144 command = nsGkAtoms::Back;
2145 break;
2146 case APPCOMMAND_BROWSER_FORWARD:
2147 command = nsGkAtoms::Forward;
2148 break;
2149 case APPCOMMAND_BROWSER_REFRESH:
2150 command = nsGkAtoms::Reload;
2151 break;
2152 case APPCOMMAND_BROWSER_STOP:
2153 command = nsGkAtoms::Stop;
2154 break;
2155 case APPCOMMAND_BROWSER_SEARCH:
2156 command = nsGkAtoms::Search;
2157 break;
2158 case APPCOMMAND_BROWSER_FAVORITES:
2159 command = nsGkAtoms::Bookmarks;
2160 break;
2161 case APPCOMMAND_BROWSER_HOME:
2162 command = nsGkAtoms::Home;
2163 break;
2164 case APPCOMMAND_CLOSE:
2165 command = nsGkAtoms::Close;
2166 break;
2167 case APPCOMMAND_FIND:
2168 command = nsGkAtoms::Find;
2169 break;
2170 case APPCOMMAND_HELP:
2171 command = nsGkAtoms::Help;
2172 break;
2173 case APPCOMMAND_NEW:
2174 command = nsGkAtoms::New;
2175 break;
2176 case APPCOMMAND_OPEN:
2177 command = nsGkAtoms::Open;
2178 break;
2179 case APPCOMMAND_PRINT:
2180 command = nsGkAtoms::Print;
2181 break;
2182 case APPCOMMAND_SAVE:
2183 command = nsGkAtoms::Save;
2184 break;
2185 case APPCOMMAND_FORWARD_MAIL:
2186 command = nsGkAtoms::ForwardMail;
2187 break;
2188 case APPCOMMAND_REPLY_TO_MAIL:
2189 command = nsGkAtoms::ReplyToMail;
2190 break;
2191 case APPCOMMAND_SEND_MAIL:
2192 command = nsGkAtoms::SendMail;
2193 break;
2194 case APPCOMMAND_MEDIA_NEXTTRACK:
2195 command = nsGkAtoms::NextTrack;
2196 break;
2197 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
2198 command = nsGkAtoms::PreviousTrack;
2199 break;
2200 case APPCOMMAND_MEDIA_STOP:
2201 command = nsGkAtoms::MediaStop;
2202 break;
2203 case APPCOMMAND_MEDIA_PLAY_PAUSE:
2204 command = nsGkAtoms::PlayPause;
2205 break;
2206 default:
2207 MOZ_LOG(
2208 gKeyLog, LogLevel::Info,
2209 ("%p NativeKey::DispatchCommandEvent(), doesn't dispatch command "
2210 "event",
2211 this));
2212 return false;
2214 WidgetCommandEvent appCommandEvent(true, command, mWidget);
2216 mWidget->InitEvent(appCommandEvent);
2217 MOZ_LOG(gKeyLog, LogLevel::Info,
2218 ("%p NativeKey::DispatchCommandEvent(), dispatching "
2219 "%s app command event...",
2220 this, nsAtomCString(command).get()));
2221 bool ok =
2222 mWidget->DispatchWindowEvent(appCommandEvent) || mWidget->Destroyed();
2223 MOZ_LOG(
2224 gKeyLog, LogLevel::Info,
2225 ("%p NativeKey::DispatchCommandEvent(), dispatched app command event, "
2226 "result=%s, mWidget->Destroyed()=%s",
2227 this, GetBoolName(ok), GetBoolName(mWidget->Destroyed())));
2228 return ok;
2231 bool NativeKey::HandleAppCommandMessage() const {
2232 // If the widget has gone, we should do nothing.
2233 if (mWidget->Destroyed()) {
2234 MOZ_LOG(gKeyLog, LogLevel::Warning,
2235 ("%p NativeKey::HandleAppCommandMessage(), WARNING, not handled "
2236 "due to "
2237 "destroyed the widget",
2238 this));
2239 return false;
2242 // NOTE: Typical behavior of WM_APPCOMMAND caused by key is, WM_APPCOMMAND
2243 // message is _sent_ first. Then, the DefaultWndProc will _post_
2244 // WM_KEYDOWN message and WM_KEYUP message if the keycode for the
2245 // command is available (i.e., mVirtualKeyCode is not 0).
2247 // NOTE: IntelliType (Microsoft's keyboard utility software) always consumes
2248 // WM_KEYDOWN and WM_KEYUP.
2250 // Let's dispatch keydown message before our chrome handles the command
2251 // when the message is caused by a keypress. This behavior makes handling
2252 // WM_APPCOMMAND be a default action of the keydown event. This means that
2253 // web applications can handle multimedia keys and prevent our default action.
2254 // This allow web applications to provide better UX for multimedia keyboard
2255 // users.
2256 bool dispatchKeyEvent = (GET_DEVICE_LPARAM(mMsg.lParam) == FAPPCOMMAND_KEY);
2257 if (dispatchKeyEvent) {
2258 // If a plug-in window has focus but it didn't consume the message, our
2259 // window receive WM_APPCOMMAND message. In this case, we shouldn't
2260 // dispatch KeyboardEvents because an event handler may access the
2261 // plug-in process synchronously.
2262 dispatchKeyEvent =
2263 WinUtils::IsOurProcessWindow(reinterpret_cast<HWND>(mMsg.wParam));
2266 bool consumed = false;
2268 if (dispatchKeyEvent) {
2269 nsresult rv = mDispatcher->BeginNativeInputTransaction();
2270 if (NS_WARN_IF(NS_FAILED(rv))) {
2271 MOZ_LOG(gKeyLog, LogLevel::Error,
2272 ("%p NativeKey::HandleAppCommandMessage(), FAILED due to "
2273 "BeginNativeInputTransaction() failure",
2274 this));
2275 return true;
2277 MOZ_LOG(gKeyLog, LogLevel::Info,
2278 ("%p NativeKey::HandleAppCommandMessage(), initializing keydown "
2279 "event...",
2280 this));
2281 WidgetKeyboardEvent keydownEvent(true, eKeyDown, mWidget);
2282 nsEventStatus status = InitKeyEvent(keydownEvent, mModKeyState);
2283 MOZ_LOG(gKeyLog, LogLevel::Info,
2284 ("%p NativeKey::HandleAppCommandMessage(), tries to dispatch "
2285 "keydown event...",
2286 this));
2287 // NOTE: If the keydown event is consumed by web contents, we shouldn't
2288 // continue to handle the command.
2289 if (!mDispatcher->DispatchKeyboardEvent(eKeyDown, keydownEvent, status,
2290 const_cast<NativeKey*>(this))) {
2291 MOZ_LOG(gKeyLog, LogLevel::Info,
2292 ("%p NativeKey::HandleAppCommandMessage(), keydown event isn't "
2293 "dispatched",
2294 this));
2295 // If keyboard event wasn't fired, there must be composition.
2296 // So, we don't need to dispatch a command event.
2297 return true;
2299 consumed = status == nsEventStatus_eConsumeNoDefault;
2300 MOZ_LOG(gKeyLog, LogLevel::Info,
2301 ("%p NativeKey::HandleAppCommandMessage(), keydown event was "
2302 "dispatched, consumed=%s",
2303 this, GetBoolName(consumed)));
2304 sDispatchedKeyOfAppCommand = mVirtualKeyCode;
2305 if (mWidget->Destroyed()) {
2306 MOZ_LOG(
2307 gKeyLog, LogLevel::Info,
2308 ("%p NativeKey::HandleAppCommandMessage(), keydown event caused "
2309 "destroying the widget",
2310 this));
2311 return true;
2315 // Dispatch a command event or a content command event if the command is
2316 // supported.
2317 if (!consumed) {
2318 uint32_t appCommand = GET_APPCOMMAND_LPARAM(mMsg.lParam);
2319 EventMessage contentCommandMessage = eVoidEvent;
2320 switch (appCommand) {
2321 case APPCOMMAND_BROWSER_BACKWARD:
2322 case APPCOMMAND_BROWSER_FORWARD:
2323 case APPCOMMAND_BROWSER_REFRESH:
2324 case APPCOMMAND_BROWSER_STOP:
2325 case APPCOMMAND_BROWSER_SEARCH:
2326 case APPCOMMAND_BROWSER_FAVORITES:
2327 case APPCOMMAND_BROWSER_HOME:
2328 case APPCOMMAND_CLOSE:
2329 case APPCOMMAND_FIND:
2330 case APPCOMMAND_HELP:
2331 case APPCOMMAND_NEW:
2332 case APPCOMMAND_OPEN:
2333 case APPCOMMAND_PRINT:
2334 case APPCOMMAND_SAVE:
2335 case APPCOMMAND_FORWARD_MAIL:
2336 case APPCOMMAND_REPLY_TO_MAIL:
2337 case APPCOMMAND_SEND_MAIL:
2338 case APPCOMMAND_MEDIA_NEXTTRACK:
2339 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
2340 case APPCOMMAND_MEDIA_STOP:
2341 case APPCOMMAND_MEDIA_PLAY_PAUSE:
2342 // We shouldn't consume the message always because if we don't handle
2343 // the message, the sender (typically, utility of keyboard or mouse)
2344 // may send other key messages which indicate well known shortcut key.
2345 consumed = DispatchCommandEvent(appCommand);
2346 break;
2348 // Use content command for following commands:
2349 case APPCOMMAND_COPY:
2350 contentCommandMessage = eContentCommandCopy;
2351 break;
2352 case APPCOMMAND_CUT:
2353 contentCommandMessage = eContentCommandCut;
2354 break;
2355 case APPCOMMAND_PASTE:
2356 contentCommandMessage = eContentCommandPaste;
2357 break;
2358 case APPCOMMAND_REDO:
2359 contentCommandMessage = eContentCommandRedo;
2360 break;
2361 case APPCOMMAND_UNDO:
2362 contentCommandMessage = eContentCommandUndo;
2363 break;
2366 if (contentCommandMessage) {
2367 MOZ_ASSERT(!mWidget->Destroyed());
2368 WidgetContentCommandEvent contentCommandEvent(true, contentCommandMessage,
2369 mWidget);
2370 MOZ_LOG(
2371 gKeyLog, LogLevel::Info,
2372 ("%p NativeKey::HandleAppCommandMessage(), dispatching %s event...",
2373 this, ToChar(contentCommandMessage)));
2374 mWidget->DispatchWindowEvent(contentCommandEvent);
2375 MOZ_LOG(gKeyLog, LogLevel::Info,
2376 ("%p NativeKey::HandleAppCommandMessage(), dispatched %s event",
2377 this, ToChar(contentCommandMessage)));
2378 consumed = true;
2380 if (mWidget->Destroyed()) {
2381 MOZ_LOG(gKeyLog, LogLevel::Info,
2382 ("%p NativeKey::HandleAppCommandMessage(), %s event caused "
2383 "destroying the widget",
2384 this, ToChar(contentCommandMessage)));
2385 return true;
2387 } else {
2388 MOZ_LOG(gKeyLog, LogLevel::Info,
2389 ("%p NativeKey::HandleAppCommandMessage(), doesn't dispatch "
2390 "content "
2391 "command event",
2392 this));
2396 // Dispatch a keyup event if the command is caused by pressing a key and
2397 // the key isn't mapped to a virtual keycode.
2398 if (dispatchKeyEvent && !mVirtualKeyCode) {
2399 MOZ_ASSERT(!mWidget->Destroyed());
2400 nsresult rv = mDispatcher->BeginNativeInputTransaction();
2401 if (NS_WARN_IF(NS_FAILED(rv))) {
2402 MOZ_LOG(gKeyLog, LogLevel::Error,
2403 ("%p NativeKey::HandleAppCommandMessage(), FAILED due to "
2404 "BeginNativeInputTransaction() failure",
2405 this));
2406 return true;
2408 MOZ_LOG(gKeyLog, LogLevel::Info,
2409 ("%p NativeKey::HandleAppCommandMessage(), initializing keyup "
2410 "event...",
2411 this));
2412 WidgetKeyboardEvent keyupEvent(true, eKeyUp, mWidget);
2413 nsEventStatus status = InitKeyEvent(keyupEvent, mModKeyState);
2414 MOZ_LOG(gKeyLog, LogLevel::Info,
2415 ("%p NativeKey::HandleAppCommandMessage(), dispatching keyup "
2416 "event...",
2417 this));
2418 // NOTE: Ignore if the keyup event is consumed because keyup event
2419 // represents just a physical key event state change.
2420 mDispatcher->DispatchKeyboardEvent(eKeyUp, keyupEvent, status,
2421 const_cast<NativeKey*>(this));
2422 MOZ_LOG(
2423 gKeyLog, LogLevel::Info,
2424 ("%p NativeKey::HandleAppCommandMessage(), dispatched keyup event",
2425 this));
2426 if (mWidget->Destroyed()) {
2427 MOZ_LOG(gKeyLog, LogLevel::Info,
2428 ("%p NativeKey::HandleAppCommandMessage(), keyup event caused "
2429 "destroying the widget",
2430 this));
2431 return true;
2435 return consumed;
2438 bool NativeKey::HandleKeyDownMessage(bool* aEventDispatched) const {
2439 MOZ_ASSERT(IsKeyDownMessage());
2441 if (aEventDispatched) {
2442 *aEventDispatched = false;
2445 if (sDispatchedKeyOfAppCommand &&
2446 sDispatchedKeyOfAppCommand == mOriginalVirtualKeyCode) {
2447 // The multimedia key event has already been dispatch from
2448 // HandleAppCommandMessage().
2449 sDispatchedKeyOfAppCommand = 0;
2450 MOZ_LOG(gKeyLog, LogLevel::Info,
2451 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2452 "event due to already dispatched from HandleAppCommandMessage(), ",
2453 this));
2454 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
2455 RedirectedKeyDownMessageManager::Forget();
2457 return true;
2460 if (IsReservedBySystem()) {
2461 MOZ_LOG(gKeyLog, LogLevel::Info,
2462 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2463 "event because the key combination is reserved by the system",
2464 this));
2465 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
2466 RedirectedKeyDownMessageManager::Forget();
2468 return false;
2471 if (sPendingHighSurrogate) {
2472 MOZ_LOG(gKeyLog, LogLevel::Info,
2473 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2474 "event because the key introduced only a high surrotate, so we "
2475 "should wait the following low surrogate input",
2476 this));
2477 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
2478 RedirectedKeyDownMessageManager::Forget();
2480 return false;
2483 // If the widget has gone, we should do nothing.
2484 if (mWidget->Destroyed()) {
2485 MOZ_LOG(
2486 gKeyLog, LogLevel::Warning,
2487 ("%p NativeKey::HandleKeyDownMessage(), WARNING, not handled due to "
2488 "destroyed the widget",
2489 this));
2490 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
2491 RedirectedKeyDownMessageManager::Forget();
2493 return false;
2496 bool defaultPrevented = false;
2497 if (mFakeCharMsgs ||
2498 !RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
2499 nsresult rv = mDispatcher->BeginNativeInputTransaction();
2500 if (NS_WARN_IF(NS_FAILED(rv))) {
2501 MOZ_LOG(gKeyLog, LogLevel::Error,
2502 ("%p NativeKey::HandleKeyDownMessage(), FAILED due to "
2503 "BeginNativeInputTransaction() failure",
2504 this));
2505 return true;
2508 bool isIMEEnabled = WinUtils::IsIMEEnabled(mWidget->GetInputContext());
2510 MOZ_LOG(gKeyLog, LogLevel::Debug,
2511 ("%p NativeKey::HandleKeyDownMessage(), initializing keydown "
2512 "event...",
2513 this));
2515 WidgetKeyboardEvent keydownEvent(true, eKeyDown, mWidget);
2516 nsEventStatus status = InitKeyEvent(keydownEvent, mModKeyState);
2517 MOZ_LOG(
2518 gKeyLog, LogLevel::Info,
2519 ("%p NativeKey::HandleKeyDownMessage(), dispatching keydown event...",
2520 this));
2521 bool dispatched = mDispatcher->DispatchKeyboardEvent(
2522 eKeyDown, keydownEvent, status, const_cast<NativeKey*>(this));
2523 if (aEventDispatched) {
2524 *aEventDispatched = dispatched;
2526 if (!dispatched) {
2527 // If the keydown event wasn't fired, there must be composition.
2528 // we don't need to do anything anymore.
2529 MOZ_LOG(
2530 gKeyLog, LogLevel::Info,
2531 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keypress "
2532 "event(s) because keydown event isn't dispatched actually",
2533 this));
2534 return false;
2536 defaultPrevented = status == nsEventStatus_eConsumeNoDefault;
2538 if (mWidget->Destroyed() || IsFocusedWindowChanged()) {
2539 MOZ_LOG(gKeyLog, LogLevel::Info,
2540 ("%p NativeKey::HandleKeyDownMessage(), keydown event caused "
2541 "destroying the widget",
2542 this));
2543 return true;
2546 MOZ_LOG(
2547 gKeyLog, LogLevel::Info,
2548 ("%p NativeKey::HandleKeyDownMessage(), dispatched keydown event, "
2549 "dispatched=%s, defaultPrevented=%s",
2550 this, GetBoolName(dispatched), GetBoolName(defaultPrevented)));
2552 // If IMC wasn't associated to the window but is associated it now (i.e.,
2553 // focus is moved from a non-editable editor to an editor by keydown
2554 // event handler), WM_CHAR and WM_SYSCHAR shouldn't cause first character
2555 // inputting if IME is opened. But then, we should redirect the native
2556 // keydown message to IME.
2557 // However, note that if focus has been already moved to another
2558 // application, we shouldn't redirect the message to it because the keydown
2559 // message is processed by us, so, nobody shouldn't process it.
2560 HWND focusedWnd = ::GetFocus();
2561 if (!defaultPrevented && !mFakeCharMsgs && focusedWnd && !isIMEEnabled &&
2562 WinUtils::IsIMEEnabled(mWidget->GetInputContext())) {
2563 RedirectedKeyDownMessageManager::RemoveNextCharMessage(focusedWnd);
2565 INPUT keyinput;
2566 keyinput.type = INPUT_KEYBOARD;
2567 keyinput.ki.wVk = mOriginalVirtualKeyCode;
2568 keyinput.ki.wScan = mScanCode;
2569 keyinput.ki.dwFlags = KEYEVENTF_SCANCODE;
2570 if (mIsExtended) {
2571 keyinput.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
2573 keyinput.ki.time = 0;
2574 keyinput.ki.dwExtraInfo = 0;
2576 RedirectedKeyDownMessageManager::WillRedirect(mMsg, defaultPrevented);
2578 MOZ_LOG(gKeyLog, LogLevel::Info,
2579 ("%p NativeKey::HandleKeyDownMessage(), redirecting %s...",
2580 this, ToString(mMsg).get()));
2582 ::SendInput(1, &keyinput, sizeof(keyinput));
2584 MOZ_LOG(gKeyLog, LogLevel::Info,
2585 ("%p NativeKey::HandleKeyDownMessage(), redirected %s", this,
2586 ToString(mMsg).get()));
2588 // Return here. We shouldn't dispatch keypress event for this WM_KEYDOWN.
2589 // If it's needed, it will be dispatched after next (redirected)
2590 // WM_KEYDOWN.
2591 return true;
2593 } else {
2594 MOZ_LOG(gKeyLog, LogLevel::Info,
2595 ("%p NativeKey::HandleKeyDownMessage(), received a redirected %s",
2596 this, ToString(mMsg).get()));
2598 defaultPrevented = RedirectedKeyDownMessageManager::DefaultPrevented();
2599 // If this is redirected keydown message, we have dispatched the keydown
2600 // event already.
2601 if (aEventDispatched) {
2602 *aEventDispatched = true;
2606 RedirectedKeyDownMessageManager::Forget();
2608 MOZ_ASSERT(!mWidget->Destroyed());
2610 // If the key was processed by IME and didn't cause WM_(SYS)CHAR messages, we
2611 // shouldn't dispatch keypress event.
2612 if (mOriginalVirtualKeyCode == VK_PROCESSKEY &&
2613 !IsFollowedByPrintableCharOrSysCharMessage()) {
2614 MOZ_LOG(gKeyLog, LogLevel::Info,
2615 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2616 "event because the key was already handled by IME, "
2617 "defaultPrevented=%s",
2618 this, GetBoolName(defaultPrevented)));
2619 return defaultPrevented;
2622 if (defaultPrevented) {
2623 MOZ_LOG(gKeyLog, LogLevel::Info,
2624 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2625 "event because preceding keydown event was consumed",
2626 this));
2627 return true;
2630 MOZ_ASSERT(!mCharMessageHasGone,
2631 "If following char message was consumed by somebody, "
2632 "keydown event should have been consumed before dispatch");
2634 // If mCommittedCharsAndModifiers was initialized with following char
2635 // messages, we should dispatch keypress events with its information.
2636 if (IsFollowedByPrintableCharOrSysCharMessage()) {
2637 MOZ_LOG(gKeyLog, LogLevel::Info,
2638 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2639 "keypress events with retrieved char messages...",
2640 this));
2641 return DispatchKeyPressEventsWithRetrievedCharMessages();
2644 // If we won't be getting a WM_CHAR, WM_SYSCHAR or WM_DEADCHAR, synthesize a
2645 // keypress for almost all keys
2646 if (NeedsToHandleWithoutFollowingCharMessages()) {
2647 MOZ_LOG(gKeyLog, LogLevel::Info,
2648 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2649 "keypress events...",
2650 this));
2651 return DispatchKeyPressEventsWithoutCharMessage();
2654 // If WM_KEYDOWN of VK_PACKET isn't followed by WM_CHAR, we don't need to
2655 // dispatch keypress events.
2656 if (mVirtualKeyCode == VK_PACKET) {
2657 MOZ_LOG(gKeyLog, LogLevel::Info,
2658 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2659 "event "
2660 "because the key is VK_PACKET and there are no char messages",
2661 this));
2662 return false;
2665 if (!mModKeyState.IsControl() && !mModKeyState.IsAlt() &&
2666 !mModKeyState.IsWin() && mIsPrintableKey) {
2667 // If this is simple KeyDown event but next message is not WM_CHAR,
2668 // this event may not input text, so we should ignore this event.
2669 // See bug 314130.
2670 MOZ_LOG(gKeyLog, LogLevel::Info,
2671 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2672 "event "
2673 "because the key event is simple printable key's event but not "
2674 "followed "
2675 "by char messages",
2676 this));
2677 return false;
2680 if (mIsDeadKey) {
2681 MOZ_LOG(gKeyLog, LogLevel::Info,
2682 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2683 "event "
2684 "because the key is a dead key and not followed by char messages",
2685 this));
2686 return false;
2689 MOZ_LOG(gKeyLog, LogLevel::Info,
2690 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2691 "keypress events due to no following char messages...",
2692 this));
2693 return DispatchKeyPressEventsWithoutCharMessage();
2696 bool NativeKey::HandleCharMessage(bool* aEventDispatched) const {
2697 MOZ_ASSERT(IsCharOrSysCharMessage(mMsg));
2698 return HandleCharMessage(mMsg, aEventDispatched);
2701 bool NativeKey::HandleCharMessage(const MSG& aCharMsg,
2702 bool* aEventDispatched) const {
2703 MOZ_ASSERT(IsKeyDownMessage() || IsCharOrSysCharMessage(mMsg));
2704 MOZ_ASSERT(IsCharOrSysCharMessage(aCharMsg.message));
2706 if (aEventDispatched) {
2707 *aEventDispatched = false;
2710 if ((IsCharOrSysCharMessage(mMsg) || IsEnterKeyPressCharMessage(mMsg)) &&
2711 IsAnotherInstanceRemovingCharMessage()) {
2712 MOZ_LOG(
2713 gKeyLog, LogLevel::Warning,
2714 ("%p NativeKey::HandleCharMessage(), WARNING, does nothing because "
2715 "the message should be handled in another instance removing this "
2716 "message",
2717 this));
2718 // Consume this for now because it will be handled by another instance.
2719 return true;
2722 // If the key combinations is reserved by the system, we shouldn't dispatch
2723 // eKeyPress event for it and passes the message to next wndproc.
2724 if (IsReservedBySystem()) {
2725 MOZ_LOG(gKeyLog, LogLevel::Info,
2726 ("%p NativeKey::HandleCharMessage(), doesn't dispatch keypress "
2727 "event because the key combination is reserved by the system",
2728 this));
2729 return false;
2732 // If the widget has gone, we should do nothing.
2733 if (mWidget->Destroyed()) {
2734 MOZ_LOG(gKeyLog, LogLevel::Warning,
2735 ("%p NativeKey::HandleCharMessage(), WARNING, not handled due to "
2736 "destroyed the widget",
2737 this));
2738 return false;
2741 // When a control key is inputted by a key, it should be handled without
2742 // WM_*CHAR messages at receiving WM_*KEYDOWN message. So, when we receive
2743 // WM_*CHAR message directly, we see a control character here.
2744 // Note that when the char is '\r', it means that the char message should
2745 // cause "Enter" keypress event for inserting a line break.
2746 if (IsControlCharMessage(aCharMsg) && !IsEnterKeyPressCharMessage(aCharMsg)) {
2747 // In this case, we don't need to dispatch eKeyPress event because:
2748 // 1. We're the only browser which dispatches "keypress" event for
2749 // non-printable characters (Although, both Chrome and Edge dispatch
2750 // "keypress" event for some keys accidentally. For example, "IntlRo"
2751 // key with Ctrl of Japanese keyboard layout).
2752 // 2. Currently, we may handle shortcut keys with "keydown" event if
2753 // it's reserved or something. So, we shouldn't dispatch "keypress"
2754 // event without it.
2755 // Note that this does NOT mean we stop dispatching eKeyPress event for
2756 // key presses causes a control character when Ctrl is pressed. In such
2757 // case, DispatchKeyPressEventsWithoutCharMessage() dispatches eKeyPress
2758 // instead of this method.
2759 MOZ_LOG(
2760 gKeyLog, LogLevel::Info,
2761 ("%p NativeKey::HandleCharMessage(), doesn't dispatch keypress "
2762 "event because received a control character input without WM_KEYDOWN",
2763 this));
2764 return false;
2767 // XXXmnakano I think that if mMsg is WM_CHAR, i.e., it comes without
2768 // preceding WM_KEYDOWN, we should should dispatch composition
2769 // events instead of eKeyPress because they are not caused by
2770 // actual keyboard operation.
2772 // First, handle normal text input or non-printable key case here.
2773 WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget);
2774 if (IsEnterKeyPressCharMessage(aCharMsg)) {
2775 keypressEvent.mKeyCode = NS_VK_RETURN;
2776 } else {
2777 keypressEvent.mCharCode = static_cast<uint32_t>(aCharMsg.wParam);
2779 nsresult rv = mDispatcher->BeginNativeInputTransaction();
2780 if (NS_WARN_IF(NS_FAILED(rv))) {
2781 MOZ_LOG(gKeyLog, LogLevel::Error,
2782 ("%p NativeKey::HandleCharMessage(), FAILED due to "
2783 "BeginNativeInputTransaction() failure",
2784 this));
2785 return true;
2788 MOZ_LOG(gKeyLog, LogLevel::Debug,
2789 ("%p NativeKey::HandleCharMessage(), initializing keypress "
2790 "event...",
2791 this));
2793 ModifierKeyState modKeyState(mModKeyState);
2794 // When AltGr is pressed, both Alt and Ctrl are active. However, when they
2795 // are active, TextEditor won't treat the keypress event as inputting a
2796 // character. Therefore, when AltGr is pressed and the key tries to input
2797 // a character, let's set them to false.
2798 if (modKeyState.IsControl() && modKeyState.IsAlt() &&
2799 IsPrintableCharMessage(aCharMsg)) {
2800 modKeyState.Unset(MODIFIER_ALT | MODIFIER_CONTROL);
2802 nsEventStatus status = InitKeyEvent(keypressEvent, modKeyState);
2803 MOZ_LOG(gKeyLog, LogLevel::Info,
2804 ("%p NativeKey::HandleCharMessage(), dispatching keypress event...",
2805 this));
2806 bool dispatched = mDispatcher->MaybeDispatchKeypressEvents(
2807 keypressEvent, status, const_cast<NativeKey*>(this));
2808 if (aEventDispatched) {
2809 *aEventDispatched = dispatched;
2811 if (mWidget->Destroyed()) {
2812 MOZ_LOG(gKeyLog, LogLevel::Info,
2813 ("%p NativeKey::HandleCharMessage(), keypress event caused "
2814 "destroying the widget",
2815 this));
2816 return true;
2818 bool consumed = status == nsEventStatus_eConsumeNoDefault;
2819 MOZ_LOG(gKeyLog, LogLevel::Info,
2820 ("%p NativeKey::HandleCharMessage(), dispatched keypress event, "
2821 "dispatched=%s, consumed=%s",
2822 this, GetBoolName(dispatched), GetBoolName(consumed)));
2823 return consumed;
2826 bool NativeKey::HandleKeyUpMessage(bool* aEventDispatched) const {
2827 MOZ_ASSERT(IsKeyUpMessage());
2829 if (aEventDispatched) {
2830 *aEventDispatched = false;
2833 // If the key combinations is reserved by the system, we shouldn't dispatch
2834 // eKeyUp event for it and passes the message to next wndproc.
2835 if (IsReservedBySystem()) {
2836 MOZ_LOG(gKeyLog, LogLevel::Info,
2837 ("%p NativeKey::HandleKeyUpMessage(), doesn't dispatch keyup "
2838 "event because the key combination is reserved by the system",
2839 this));
2840 return false;
2843 if (sPendingHighSurrogate) {
2844 MOZ_LOG(gKeyLog, LogLevel::Info,
2845 ("%p NativeKey::HandleKeyUpMessage(), doesn't dispatch keyup "
2846 "event because the key introduced only a high surrotate, so we "
2847 "should wait the following low surrogate input",
2848 this));
2849 return false;
2852 // If the widget has gone, we should do nothing.
2853 if (mWidget->Destroyed()) {
2854 MOZ_LOG(
2855 gKeyLog, LogLevel::Warning,
2856 ("%p NativeKey::HandleKeyUpMessage(), WARNING, not handled due to "
2857 "destroyed the widget",
2858 this));
2859 return false;
2862 nsresult rv = mDispatcher->BeginNativeInputTransaction();
2863 if (NS_WARN_IF(NS_FAILED(rv))) {
2864 MOZ_LOG(gKeyLog, LogLevel::Error,
2865 ("%p NativeKey::HandleKeyUpMessage(), FAILED due to "
2866 "BeginNativeInputTransaction() failure",
2867 this));
2868 return true;
2871 MOZ_LOG(gKeyLog, LogLevel::Debug,
2872 ("%p NativeKey::HandleKeyUpMessage(), initializing keyup event...",
2873 this));
2874 WidgetKeyboardEvent keyupEvent(true, eKeyUp, mWidget);
2875 nsEventStatus status = InitKeyEvent(keyupEvent, mModKeyState);
2876 MOZ_LOG(gKeyLog, LogLevel::Info,
2877 ("%p NativeKey::HandleKeyUpMessage(), dispatching keyup event...",
2878 this));
2879 bool dispatched = mDispatcher->DispatchKeyboardEvent(
2880 eKeyUp, keyupEvent, status, const_cast<NativeKey*>(this));
2881 if (aEventDispatched) {
2882 *aEventDispatched = dispatched;
2884 if (mWidget->Destroyed()) {
2885 MOZ_LOG(gKeyLog, LogLevel::Info,
2886 ("%p NativeKey::HandleKeyUpMessage(), keyup event caused "
2887 "destroying the widget",
2888 this));
2889 return true;
2891 bool consumed = status == nsEventStatus_eConsumeNoDefault;
2892 MOZ_LOG(gKeyLog, LogLevel::Info,
2893 ("%p NativeKey::HandleKeyUpMessage(), dispatched keyup event, "
2894 "dispatched=%s, consumed=%s",
2895 this, GetBoolName(dispatched), GetBoolName(consumed)));
2896 return consumed;
2899 bool NativeKey::NeedsToHandleWithoutFollowingCharMessages() const {
2900 MOZ_ASSERT(IsKeyDownMessage());
2902 // If the key combination is reserved by the system, the caller shouldn't
2903 // do anything with following WM_*CHAR messages. So, let's return true here.
2904 if (IsReservedBySystem()) {
2905 return true;
2908 // If the keydown message is generated for inputting some Unicode characters
2909 // via SendInput() API, we need to handle it only with WM_*CHAR messages.
2910 if (mVirtualKeyCode == VK_PACKET) {
2911 return false;
2914 // If following char message is for a control character, it should be handled
2915 // without WM_CHAR message. This is typically Ctrl + [a-z].
2916 if (mFollowingCharMsgs.Length() == 1 &&
2917 IsControlCharMessage(mFollowingCharMsgs[0])) {
2918 return true;
2921 // If keydown message is followed by WM_CHAR or WM_SYSCHAR whose wParam isn't
2922 // a control character, we should dispatch keypress event with the char
2923 // message even with any modifier state.
2924 if (IsFollowedByPrintableCharOrSysCharMessage()) {
2925 return false;
2928 // If any modifier keys which may cause printable keys becoming non-printable
2929 // are not pressed, we don't need special handling for the key.
2930 // Note that if the key does not produce a character with AltGr and when
2931 // AltGr key is pressed, we don't need to dispatch eKeyPress event for it
2932 // because AltGr shouldn't be used for a modifier for a shortcut without
2933 // Ctrl, Alt or Win. That means that we should treat it in same path for
2934 // Shift key.
2935 if (!mModKeyState.IsControl() && !mModKeyState.IsAlt() &&
2936 !mModKeyState.IsWin()) {
2937 return false;
2940 // If the key event causes dead key event, we don't need to dispatch keypress
2941 // event.
2942 if (mIsDeadKey && mCommittedCharsAndModifiers.IsEmpty()) {
2943 return false;
2946 // Even if the key is a printable key, it might cause non-printable character
2947 // input with modifier key(s).
2948 return mIsPrintableKey;
2951 static nsCString GetResultOfInSendMessageEx() {
2952 DWORD ret = ::InSendMessageEx(nullptr);
2953 if (!ret) {
2954 return "ISMEX_NOSEND"_ns;
2956 nsCString result;
2957 if (ret & ISMEX_CALLBACK) {
2958 result = "ISMEX_CALLBACK";
2960 if (ret & ISMEX_NOTIFY) {
2961 if (!result.IsEmpty()) {
2962 result += " | ";
2964 result += "ISMEX_NOTIFY";
2966 if (ret & ISMEX_REPLIED) {
2967 if (!result.IsEmpty()) {
2968 result += " | ";
2970 result += "ISMEX_REPLIED";
2972 if (ret & ISMEX_SEND) {
2973 if (!result.IsEmpty()) {
2974 result += " | ";
2976 result += "ISMEX_SEND";
2978 return result;
2981 bool NativeKey::MayBeSameCharMessage(const MSG& aCharMsg1,
2982 const MSG& aCharMsg2) const {
2983 // NOTE: Although, we don't know when this case occurs, the scan code value
2984 // in lParam may be changed from 0 to something. The changed value
2985 // is different from the scan code of handling keydown message.
2986 static const LPARAM kScanCodeMask = 0x00FF0000;
2987 return aCharMsg1.message == aCharMsg2.message &&
2988 aCharMsg1.wParam == aCharMsg2.wParam &&
2989 (aCharMsg1.lParam & ~kScanCodeMask) ==
2990 (aCharMsg2.lParam & ~kScanCodeMask);
2993 bool NativeKey::IsSamePhysicalKeyMessage(const MSG& aKeyOrCharMsg1,
2994 const MSG& aKeyOrCharMsg2) const {
2995 if (NS_WARN_IF(aKeyOrCharMsg1.message < WM_KEYFIRST) ||
2996 NS_WARN_IF(aKeyOrCharMsg1.message > WM_KEYLAST) ||
2997 NS_WARN_IF(aKeyOrCharMsg2.message < WM_KEYFIRST) ||
2998 NS_WARN_IF(aKeyOrCharMsg2.message > WM_KEYLAST)) {
2999 return false;
3001 return WinUtils::GetScanCode(aKeyOrCharMsg1.lParam) ==
3002 WinUtils::GetScanCode(aKeyOrCharMsg2.lParam) &&
3003 WinUtils::IsExtendedScanCode(aKeyOrCharMsg1.lParam) ==
3004 WinUtils::IsExtendedScanCode(aKeyOrCharMsg2.lParam);
3007 bool NativeKey::GetFollowingCharMessage(MSG& aCharMsg) {
3008 MOZ_ASSERT(IsKeyDownMessage());
3010 aCharMsg.message = WM_NULL;
3012 if (mFakeCharMsgs) {
3013 for (size_t i = 0; i < mFakeCharMsgs->Length(); i++) {
3014 FakeCharMsg& fakeCharMsg = mFakeCharMsgs->ElementAt(i);
3015 if (fakeCharMsg.mConsumed) {
3016 continue;
3018 MSG charMsg = fakeCharMsg.GetCharMsg(mMsg.hwnd);
3019 fakeCharMsg.mConsumed = true;
3020 if (!IsCharMessage(charMsg)) {
3021 return false;
3023 aCharMsg = charMsg;
3024 return true;
3026 return false;
3029 // If next key message is not char message, we should give up to find a
3030 // related char message for the handling keydown event for now.
3031 // Note that it's possible other applications may send other key message
3032 // after we call TranslateMessage(). That may cause PeekMessage() failing
3033 // to get char message for the handling keydown message.
3034 MSG nextKeyMsg;
3035 if (!WinUtils::PeekMessage(&nextKeyMsg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST,
3036 PM_NOREMOVE | PM_NOYIELD) ||
3037 !IsCharMessage(nextKeyMsg)) {
3038 MOZ_LOG(gKeyLog, LogLevel::Debug,
3039 ("%p NativeKey::GetFollowingCharMessage(), there are no char "
3040 "messages",
3041 this));
3042 return false;
3044 const MSG kFoundCharMsg = nextKeyMsg;
3046 AutoRestore<MSG> saveLastRemovingMsg(mRemovingMsg);
3047 mRemovingMsg = nextKeyMsg;
3049 mReceivedMsg = sEmptyMSG;
3050 AutoRestore<MSG> ensureToClearRecivedMsg(mReceivedMsg);
3052 // On Metrofox, PeekMessage() sometimes returns WM_NULL even if we specify
3053 // the message range. So, if it returns WM_NULL, we should retry to get
3054 // the following char message it was found above.
3055 for (uint32_t i = 0; i < 50; i++) {
3056 MSG removedMsg, nextKeyMsgInAllWindows;
3057 bool doCrash = false;
3058 if (!WinUtils::PeekMessage(&removedMsg, mMsg.hwnd, nextKeyMsg.message,
3059 nextKeyMsg.message, PM_REMOVE | PM_NOYIELD)) {
3060 // We meets unexpected case. We should collect the message queue state
3061 // and crash for reporting the bug.
3062 doCrash = true;
3064 // If another instance was created for the removing message during trying
3065 // to remove a char message, the instance didn't handle it for preventing
3066 // recursive handling. So, let's handle it in this instance.
3067 if (!IsEmptyMSG(mReceivedMsg)) {
3068 // If focus is moved to different window, we shouldn't handle it on
3069 // the widget. Let's discard it for now.
3070 if (mReceivedMsg.hwnd != nextKeyMsg.hwnd) {
3071 MOZ_LOG(
3072 gKeyLog, LogLevel::Warning,
3073 ("%p NativeKey::GetFollowingCharMessage(), WARNING, received a "
3074 "char message during removing it from the queue, but it's for "
3075 "different window, mReceivedMsg=%s, nextKeyMsg=%s, "
3076 "kFoundCharMsg=%s",
3077 this, ToString(mReceivedMsg).get(), ToString(nextKeyMsg).get(),
3078 ToString(kFoundCharMsg).get()));
3079 // There might still exist char messages, the loop of calling
3080 // this method should be continued.
3081 aCharMsg.message = WM_NULL;
3082 return true;
3084 // Even if the received message is different from what we tried to
3085 // remove from the queue, let's take the received message as a part of
3086 // the result of this key sequence.
3087 if (mReceivedMsg.message != nextKeyMsg.message ||
3088 mReceivedMsg.wParam != nextKeyMsg.wParam ||
3089 mReceivedMsg.lParam != nextKeyMsg.lParam) {
3090 MOZ_LOG(
3091 gKeyLog, LogLevel::Warning,
3092 ("%p NativeKey::GetFollowingCharMessage(), WARNING, received a "
3093 "char message during removing it from the queue, but it's "
3094 "differnt from what trying to remove from the queue, "
3095 "aCharMsg=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3096 this, ToString(mReceivedMsg).get(), ToString(nextKeyMsg).get(),
3097 ToString(kFoundCharMsg).get()));
3098 } else {
3099 MOZ_LOG(
3100 gKeyLog, LogLevel::Debug,
3101 ("%p NativeKey::GetFollowingCharMessage(), succeeded to "
3102 "retrieve next char message via another instance, aCharMsg=%s, "
3103 "kFoundCharMsg=%s",
3104 this, ToString(mReceivedMsg).get(),
3105 ToString(kFoundCharMsg).get()));
3107 aCharMsg = mReceivedMsg;
3108 return true;
3111 // The char message is redirected to different thread's window by focus
3112 // move or something or just cancelled by external application.
3113 if (!WinUtils::PeekMessage(&nextKeyMsgInAllWindows, 0, WM_KEYFIRST,
3114 WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD)) {
3115 MOZ_LOG(
3116 gKeyLog, LogLevel::Warning,
3117 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3118 "remove a char message, but it's already gone from all message "
3119 "queues, nextKeyMsg=%s, kFoundCharMsg=%s",
3120 this, ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3121 return true; // XXX should return false in this case
3123 // The next key message is redirected to different window created by our
3124 // thread, we should do nothing because we must not have focus.
3125 if (nextKeyMsgInAllWindows.hwnd != mMsg.hwnd) {
3126 aCharMsg = nextKeyMsgInAllWindows;
3127 MOZ_LOG(
3128 gKeyLog, LogLevel::Warning,
3129 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3130 "remove a char message, but found in another message queue, "
3131 "nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3132 this, ToString(nextKeyMsgInAllWindows).get(),
3133 ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3134 return true;
3136 // If next key message becomes non-char message, this key operation
3137 // may have already been consumed or canceled.
3138 if (!IsCharMessage(nextKeyMsgInAllWindows)) {
3139 MOZ_LOG(
3140 gKeyLog, LogLevel::Warning,
3141 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3142 "remove a char message and next key message becomes non-char "
3143 "message, nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, "
3144 "kFoundCharMsg=%s",
3145 this, ToString(nextKeyMsgInAllWindows).get(),
3146 ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3147 MOZ_ASSERT(!mCharMessageHasGone);
3148 mFollowingCharMsgs.Clear();
3149 mCharMessageHasGone = true;
3150 return false;
3152 // If next key message is still a char message but different key message,
3153 // we should treat current key operation is consumed or canceled and
3154 // next char message should be handled as an orphan char message later.
3155 if (!IsSamePhysicalKeyMessage(nextKeyMsgInAllWindows, kFoundCharMsg)) {
3156 MOZ_LOG(
3157 gKeyLog, LogLevel::Warning,
3158 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3159 "remove a char message and next key message becomes differnt "
3160 "key's "
3161 "char message, nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, "
3162 "kFoundCharMsg=%s",
3163 this, ToString(nextKeyMsgInAllWindows).get(),
3164 ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3165 MOZ_ASSERT(!mCharMessageHasGone);
3166 mFollowingCharMsgs.Clear();
3167 mCharMessageHasGone = true;
3168 return false;
3170 // If next key message is still a char message but the message is changed,
3171 // we should retry to remove the new message with PeekMessage() again.
3172 if (nextKeyMsgInAllWindows.message != nextKeyMsg.message) {
3173 MOZ_LOG(
3174 gKeyLog, LogLevel::Warning,
3175 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3176 "remove a char message due to message change, let's retry to "
3177 "remove the message with newly found char message, "
3178 "nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3179 this, ToString(nextKeyMsgInAllWindows).get(),
3180 ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3181 nextKeyMsg = nextKeyMsgInAllWindows;
3182 continue;
3184 // If there is still existing a char message caused by same physical key
3185 // in the queue but PeekMessage(PM_REMOVE) failed to remove it from the
3186 // queue, it might be possible that the odd keyboard layout or utility
3187 // hooks only PeekMessage(PM_NOREMOVE) and GetMessage(). So, let's try
3188 // remove the char message with GetMessage() again.
3189 // FYI: The wParam might be different from the found message, but it's
3190 // okay because we assume that odd keyboard layouts return actual
3191 // inputting character at removing the char message.
3192 if (WinUtils::GetMessage(&removedMsg, mMsg.hwnd, nextKeyMsg.message,
3193 nextKeyMsg.message)) {
3194 MOZ_LOG(
3195 gKeyLog, LogLevel::Warning,
3196 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3197 "remove a char message, but succeeded with GetMessage(), "
3198 "removedMsg=%s, kFoundCharMsg=%s",
3199 this, ToString(removedMsg).get(), ToString(kFoundCharMsg).get()));
3200 // Cancel to crash, but we need to check the removed message value.
3201 doCrash = false;
3203 // If we've already removed some WM_NULL messages from the queue and
3204 // the found message has already gone from the queue, let's treat the key
3205 // as inputting no characters and already consumed.
3206 else if (i > 0) {
3207 MOZ_LOG(
3208 gKeyLog, LogLevel::Warning,
3209 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3210 "remove a char message, but removed %d WM_NULL messages",
3211 this, i));
3212 // If the key is a printable key or a control key but tried to input
3213 // a character, mark mCharMessageHasGone true for handling the keydown
3214 // event as inputting empty string.
3215 MOZ_ASSERT(!mCharMessageHasGone);
3216 mFollowingCharMsgs.Clear();
3217 mCharMessageHasGone = true;
3218 return false;
3220 MOZ_LOG(gKeyLog, LogLevel::Error,
3221 ("%p NativeKey::GetFollowingCharMessage(), FAILED, lost target "
3222 "message to remove, nextKeyMsg=%s",
3223 this, ToString(nextKeyMsg).get()));
3226 if (doCrash) {
3227 nsPrintfCString info(
3228 "\nPeekMessage() failed to remove char message! "
3229 "\nActive keyboard layout=0x%p (%s), "
3230 "\nHandling message: %s, InSendMessageEx()=%s, "
3231 "\nFound message: %s, "
3232 "\nWM_NULL has been removed: %d, "
3233 "\nNext key message in all windows: %s, "
3234 "time=%ld, ",
3235 KeyboardLayout::GetActiveLayout(),
3236 KeyboardLayout::GetActiveLayoutName().get(), ToString(mMsg).get(),
3237 GetResultOfInSendMessageEx().get(), ToString(kFoundCharMsg).get(), i,
3238 ToString(nextKeyMsgInAllWindows).get(), nextKeyMsgInAllWindows.time);
3239 CrashReporter::AppendAppNotesToCrashReport(info);
3240 MSG nextMsg;
3241 if (WinUtils::PeekMessage(&nextMsg, 0, 0, 0, PM_NOREMOVE | PM_NOYIELD)) {
3242 nsPrintfCString info("\nNext message in all windows: %s, time=%ld",
3243 ToString(nextMsg).get(), nextMsg.time);
3244 CrashReporter::AppendAppNotesToCrashReport(info);
3245 } else {
3246 CrashReporter::AppendAppNotesToCrashReport(
3247 "\nThere is no message in any window"_ns);
3250 MOZ_CRASH("We lost the following char message");
3253 // We're still not sure why ::PeekMessage() may return WM_NULL even with
3254 // its first message and its last message are same message. However,
3255 // at developing Metrofox, we met this case even with usual keyboard
3256 // layouts. So, it might be possible in desktop application or it really
3257 // occurs with some odd keyboard layouts which perhaps hook API.
3258 if (removedMsg.message == WM_NULL) {
3259 MOZ_LOG(gKeyLog, LogLevel::Warning,
3260 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3261 "remove a char message, instead, removed WM_NULL message, "
3262 "removedMsg=%s",
3263 this, ToString(removedMsg).get()));
3264 // Check if there is the message which we're trying to remove.
3265 MSG newNextKeyMsg;
3266 if (!WinUtils::PeekMessage(&newNextKeyMsg, mMsg.hwnd, WM_KEYFIRST,
3267 WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD)) {
3268 // If there is no key message, we should mark this keydown as consumed
3269 // because the key operation may have already been handled or canceled.
3270 MOZ_LOG(
3271 gKeyLog, LogLevel::Warning,
3272 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3273 "remove a char message because it's gone during removing it from "
3274 "the queue, nextKeyMsg=%s, kFoundCharMsg=%s",
3275 this, ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3276 MOZ_ASSERT(!mCharMessageHasGone);
3277 mFollowingCharMsgs.Clear();
3278 mCharMessageHasGone = true;
3279 return false;
3281 if (!IsCharMessage(newNextKeyMsg)) {
3282 // If next key message becomes a non-char message, we should mark this
3283 // keydown as consumed because the key operation may have already been
3284 // handled or canceled.
3285 MOZ_LOG(
3286 gKeyLog, LogLevel::Warning,
3287 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3288 "remove a char message because it's gone during removing it from "
3289 "the queue, nextKeyMsg=%s, newNextKeyMsg=%s, kFoundCharMsg=%s",
3290 this, ToString(nextKeyMsg).get(), ToString(newNextKeyMsg).get(),
3291 ToString(kFoundCharMsg).get()));
3292 MOZ_ASSERT(!mCharMessageHasGone);
3293 mFollowingCharMsgs.Clear();
3294 mCharMessageHasGone = true;
3295 return false;
3297 MOZ_LOG(
3298 gKeyLog, LogLevel::Debug,
3299 ("%p NativeKey::GetFollowingCharMessage(), there is the message "
3300 "which is being tried to be removed from the queue, trying again...",
3301 this));
3302 continue;
3305 // Typically, this case occurs with WM_DEADCHAR. If the removed message's
3306 // wParam becomes 0, that means that the key event shouldn't cause text
3307 // input. So, let's ignore the strange char message.
3308 if (removedMsg.message == nextKeyMsg.message && !removedMsg.wParam) {
3309 MOZ_LOG(
3310 gKeyLog, LogLevel::Warning,
3311 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3312 "remove a char message, but the removed message's wParam is 0, "
3313 "removedMsg=%s",
3314 this, ToString(removedMsg).get()));
3315 return false;
3318 // This is normal case.
3319 if (MayBeSameCharMessage(removedMsg, nextKeyMsg)) {
3320 aCharMsg = removedMsg;
3321 MOZ_LOG(
3322 gKeyLog, LogLevel::Debug,
3323 ("%p NativeKey::GetFollowingCharMessage(), succeeded to retrieve "
3324 "next char message, aCharMsg=%s",
3325 this, ToString(aCharMsg).get()));
3326 return true;
3329 // Even if removed message is different char message from the found char
3330 // message, when the scan code is same, we can assume that the message
3331 // is overwritten by somebody who hooks API. See bug 1336028 comment 0 for
3332 // the possible scenarios.
3333 if (IsCharMessage(removedMsg) &&
3334 IsSamePhysicalKeyMessage(removedMsg, nextKeyMsg)) {
3335 aCharMsg = removedMsg;
3336 MOZ_LOG(
3337 gKeyLog, LogLevel::Warning,
3338 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3339 "remove a char message, but the removed message was changed from "
3340 "the found message except their scancode, aCharMsg=%s, "
3341 "nextKeyMsg=%s, kFoundCharMsg=%s",
3342 this, ToString(aCharMsg).get(), ToString(nextKeyMsg).get(),
3343 ToString(kFoundCharMsg).get()));
3344 return true;
3347 // When found message's wParam is 0 and its scancode is 0xFF, we may remove
3348 // usual char message actually. In such case, we should use the removed
3349 // char message.
3350 if (IsCharMessage(removedMsg) && !nextKeyMsg.wParam &&
3351 WinUtils::GetScanCode(nextKeyMsg.lParam) == 0xFF) {
3352 aCharMsg = removedMsg;
3353 MOZ_LOG(
3354 gKeyLog, LogLevel::Warning,
3355 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3356 "remove a char message, but the removed message was changed from "
3357 "the found message but the found message was odd, so, ignoring the "
3358 "odd found message and respecting the removed message, aCharMsg=%s, "
3359 "nextKeyMsg=%s, kFoundCharMsg=%s",
3360 this, ToString(aCharMsg).get(), ToString(nextKeyMsg).get(),
3361 ToString(kFoundCharMsg).get()));
3362 return true;
3365 // NOTE: Although, we don't know when this case occurs, the scan code value
3366 // in lParam may be changed from 0 to something. The changed value
3367 // is different from the scan code of handling keydown message.
3368 MOZ_LOG(
3369 gKeyLog, LogLevel::Error,
3370 ("%p NativeKey::GetFollowingCharMessage(), FAILED, removed message "
3371 "is really different from what we have already found, removedMsg=%s, "
3372 "nextKeyMsg=%s, kFoundCharMsg=%s",
3373 this, ToString(removedMsg).get(), ToString(nextKeyMsg).get(),
3374 ToString(kFoundCharMsg).get()));
3375 nsPrintfCString info(
3376 "\nPeekMessage() removed unexpcted char message! "
3377 "\nActive keyboard layout=0x%p (%s), "
3378 "\nHandling message: %s, InSendMessageEx()=%s, "
3379 "\nFound message: %s, "
3380 "\nRemoved message: %s, ",
3381 KeyboardLayout::GetActiveLayout(),
3382 KeyboardLayout::GetActiveLayoutName().get(), ToString(mMsg).get(),
3383 GetResultOfInSendMessageEx().get(), ToString(kFoundCharMsg).get(),
3384 ToString(removedMsg).get());
3385 CrashReporter::AppendAppNotesToCrashReport(info);
3386 // What's the next key message?
3387 MSG nextKeyMsgAfter;
3388 if (WinUtils::PeekMessage(&nextKeyMsgAfter, mMsg.hwnd, WM_KEYFIRST,
3389 WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD)) {
3390 nsPrintfCString info(
3391 "\nNext key message after unexpected char message "
3392 "removed: %s, ",
3393 ToString(nextKeyMsgAfter).get());
3394 CrashReporter::AppendAppNotesToCrashReport(info);
3395 } else {
3396 CrashReporter::AppendAppNotesToCrashReport(
3397 nsLiteralCString("\nThere is no key message after unexpected char "
3398 "message removed, "));
3400 // Another window has a key message?
3401 if (WinUtils::PeekMessage(&nextKeyMsgInAllWindows, 0, WM_KEYFIRST,
3402 WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD)) {
3403 nsPrintfCString info("\nNext key message in all windows: %s.",
3404 ToString(nextKeyMsgInAllWindows).get());
3405 CrashReporter::AppendAppNotesToCrashReport(info);
3406 } else {
3407 CrashReporter::AppendAppNotesToCrashReport(
3408 "\nThere is no key message in any windows."_ns);
3411 MOZ_CRASH("PeekMessage() removed unexpected message");
3413 MOZ_LOG(
3414 gKeyLog, LogLevel::Error,
3415 ("%p NativeKey::GetFollowingCharMessage(), FAILED, removed messages "
3416 "are all WM_NULL, nextKeyMsg=%s",
3417 this, ToString(nextKeyMsg).get()));
3418 nsPrintfCString info(
3419 "\nWe lost following char message! "
3420 "\nActive keyboard layout=0x%p (%s), "
3421 "\nHandling message: %s, InSendMessageEx()=%s, \n"
3422 "Found message: %s, removed a lot of WM_NULL",
3423 KeyboardLayout::GetActiveLayout(),
3424 KeyboardLayout::GetActiveLayoutName().get(), ToString(mMsg).get(),
3425 GetResultOfInSendMessageEx().get(), ToString(kFoundCharMsg).get());
3426 CrashReporter::AppendAppNotesToCrashReport(info);
3427 MOZ_CRASH("We lost the following char message");
3428 return false;
3431 void NativeKey::ComputeInputtingStringWithKeyboardLayout() {
3432 KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
3434 if (KeyboardLayout::IsPrintableCharKey(mVirtualKeyCode) ||
3435 mCharMessageHasGone) {
3436 mInputtingStringAndModifiers = mCommittedCharsAndModifiers;
3437 } else {
3438 mInputtingStringAndModifiers.Clear();
3440 mShiftedString.Clear();
3441 mUnshiftedString.Clear();
3442 mShiftedLatinChar = mUnshiftedLatinChar = 0;
3444 // XXX How about when Win key is pressed?
3445 if (mModKeyState.IsControl() == mModKeyState.IsAlt()) {
3446 return;
3449 // If user is inputting a Unicode character with typing Alt + Numpad
3450 // keys, we shouldn't set alternative key codes because the key event
3451 // shouldn't match with a mnemonic. However, we should set only
3452 // mUnshiftedString for keeping traditional behavior at WM_SYSKEYDOWN.
3453 // FYI: I guess that it's okay that mUnshiftedString stays empty. So,
3454 // if its value breaks something, you must be able to just return here.
3455 if (MaybeTypingUnicodeScalarValue()) {
3456 if (!mCommittedCharsAndModifiers.IsEmpty()) {
3457 MOZ_ASSERT(mMsg.message == WM_SYSKEYDOWN);
3458 char16_t num = mCommittedCharsAndModifiers.CharAt(0);
3459 MOZ_ASSERT(num >= '0' && num <= '9');
3460 mUnshiftedString.Append(num, MODIFIER_NONE);
3461 return;
3463 // If user presses a function key without NumLock or 3rd party utility
3464 // synthesizes a function key on numpad, we should handle it as-is because
3465 // the user's intention may be performing `Alt` + foo.
3466 MOZ_ASSERT(!KeyboardLayout::IsPrintableCharKey(mVirtualKeyCode));
3467 return;
3470 ModifierKeyState capsLockState(mModKeyState.GetModifiers() &
3471 MODIFIER_CAPSLOCK);
3473 mUnshiftedString =
3474 keyboardLayout->GetUniCharsAndModifiers(mVirtualKeyCode, capsLockState);
3475 capsLockState.Set(MODIFIER_SHIFT);
3476 mShiftedString =
3477 keyboardLayout->GetUniCharsAndModifiers(mVirtualKeyCode, capsLockState);
3479 // The current keyboard cannot input alphabets or numerics,
3480 // we should append them for Shortcut/Access keys.
3481 // E.g., for Cyrillic keyboard layout.
3482 capsLockState.Unset(MODIFIER_SHIFT);
3483 WidgetUtils::GetLatinCharCodeForKeyCode(
3484 mDOMKeyCode, capsLockState.GetModifiers(), &mUnshiftedLatinChar,
3485 &mShiftedLatinChar);
3487 // If the mShiftedLatinChar isn't 0, the key code is NS_VK_[A-Z].
3488 if (mShiftedLatinChar) {
3489 // If the produced characters of the key on current keyboard layout
3490 // are same as computed Latin characters, we shouldn't append the
3491 // Latin characters to alternativeCharCode.
3492 if (mUnshiftedLatinChar == mUnshiftedString.CharAt(0) &&
3493 mShiftedLatinChar == mShiftedString.CharAt(0)) {
3494 mShiftedLatinChar = mUnshiftedLatinChar = 0;
3496 } else if (mUnshiftedLatinChar) {
3497 // If the mShiftedLatinChar is 0, the mKeyCode doesn't produce
3498 // alphabet character. At that time, the character may be produced
3499 // with Shift key. E.g., on French keyboard layout, NS_VK_PERCENT
3500 // key produces LATIN SMALL LETTER U WITH GRAVE (U+00F9) without
3501 // Shift key but with Shift key, it produces '%'.
3502 // If the mUnshiftedLatinChar is produced by the key on current
3503 // keyboard layout, we shouldn't append it to alternativeCharCode.
3504 if (mUnshiftedLatinChar == mUnshiftedString.CharAt(0) ||
3505 mUnshiftedLatinChar == mShiftedString.CharAt(0)) {
3506 mUnshiftedLatinChar = 0;
3510 if (!mModKeyState.IsControl()) {
3511 return;
3514 // If the mCharCode is not ASCII character, we should replace the
3515 // mCharCode with ASCII character only when Ctrl is pressed.
3516 // But don't replace the mCharCode when the mCharCode is not same as
3517 // unmodified characters. In such case, Ctrl is sometimes used for a
3518 // part of character inputting key combination like Shift.
3519 uint32_t ch =
3520 mModKeyState.IsShift() ? mShiftedLatinChar : mUnshiftedLatinChar;
3521 if (!ch) {
3522 return;
3524 if (mInputtingStringAndModifiers.IsEmpty() ||
3525 mInputtingStringAndModifiers.UniCharsCaseInsensitiveEqual(
3526 mModKeyState.IsShift() ? mShiftedString : mUnshiftedString)) {
3527 mInputtingStringAndModifiers.Clear();
3528 mInputtingStringAndModifiers.Append(ch, mModKeyState.GetModifiers());
3532 bool NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages() const {
3533 MOZ_ASSERT(IsKeyDownMessage());
3534 MOZ_ASSERT(IsFollowedByPrintableCharOrSysCharMessage());
3535 MOZ_ASSERT(!mWidget->Destroyed());
3537 nsresult rv = mDispatcher->BeginNativeInputTransaction();
3538 if (NS_WARN_IF(NS_FAILED(rv))) {
3539 MOZ_LOG(
3540 gKeyLog, LogLevel::Error,
3541 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3542 "FAILED due to BeginNativeInputTransaction() failure",
3543 this));
3544 return true;
3546 WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget);
3547 MOZ_LOG(gKeyLog, LogLevel::Debug,
3548 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3549 "initializing keypress event...",
3550 this));
3551 ModifierKeyState modKeyState(mModKeyState);
3552 if (mCanIgnoreModifierStateAtKeyPress && IsFollowedByPrintableCharMessage()) {
3553 // If eKeyPress event should cause inputting text in focused editor,
3554 // we need to remove Alt and Ctrl state.
3555 modKeyState.Unset(MODIFIER_ALT | MODIFIER_CONTROL);
3557 // We don't need to send char message here if there are two or more retrieved
3558 // messages because we need to set each message to each eKeyPress event.
3559 bool needsCallback = mFollowingCharMsgs.Length() > 1;
3560 nsEventStatus status = InitKeyEvent(keypressEvent, modKeyState);
3561 MOZ_LOG(gKeyLog, LogLevel::Info,
3562 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3563 "dispatching keypress event(s)...",
3564 this));
3565 bool dispatched = mDispatcher->MaybeDispatchKeypressEvents(
3566 keypressEvent, status, const_cast<NativeKey*>(this), needsCallback);
3567 if (mWidget->Destroyed()) {
3568 MOZ_LOG(
3569 gKeyLog, LogLevel::Info,
3570 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3571 "keypress event(s) caused destroying the widget",
3572 this));
3573 return true;
3575 bool consumed = status == nsEventStatus_eConsumeNoDefault;
3576 MOZ_LOG(gKeyLog, LogLevel::Info,
3577 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3578 "dispatched keypress event(s), dispatched=%s, consumed=%s",
3579 this, GetBoolName(dispatched), GetBoolName(consumed)));
3580 return consumed;
3583 bool NativeKey::DispatchKeyPressEventsWithoutCharMessage() const {
3584 MOZ_ASSERT(IsKeyDownMessage());
3585 MOZ_ASSERT(!mIsDeadKey || !mCommittedCharsAndModifiers.IsEmpty());
3586 MOZ_ASSERT(!mWidget->Destroyed());
3588 nsresult rv = mDispatcher->BeginNativeInputTransaction();
3589 if (NS_WARN_IF(NS_FAILED(rv))) {
3590 MOZ_LOG(gKeyLog, LogLevel::Error,
3591 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3592 "FAILED due "
3593 "to BeginNativeInputTransaction() failure",
3594 this));
3595 return true;
3598 WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget);
3599 if (mInputtingStringAndModifiers.IsEmpty() && mShiftedString.IsEmpty() &&
3600 mUnshiftedString.IsEmpty()) {
3601 keypressEvent.mKeyCode = mDOMKeyCode;
3603 MOZ_LOG(gKeyLog, LogLevel::Debug,
3604 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3605 "initializing "
3606 "keypress event...",
3607 this));
3608 nsEventStatus status = InitKeyEvent(keypressEvent, mModKeyState);
3609 MOZ_LOG(gKeyLog, LogLevel::Info,
3610 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3611 "dispatching "
3612 "keypress event(s)...",
3613 this));
3614 bool dispatched = mDispatcher->MaybeDispatchKeypressEvents(
3615 keypressEvent, status, const_cast<NativeKey*>(this));
3616 if (mWidget->Destroyed()) {
3617 MOZ_LOG(gKeyLog, LogLevel::Info,
3618 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3619 "keypress event(s) caused destroying the widget",
3620 this));
3621 return true;
3623 bool consumed = status == nsEventStatus_eConsumeNoDefault;
3624 MOZ_LOG(
3625 gKeyLog, LogLevel::Info,
3626 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), dispatched "
3627 "keypress event(s), dispatched=%s, consumed=%s",
3628 this, GetBoolName(dispatched), GetBoolName(consumed)));
3629 return consumed;
3632 void NativeKey::WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyboardEvent,
3633 uint32_t aIndex) {
3634 // If it's an eKeyPress event and it's generated from retrieved char message,
3635 // we need to set raw message information for plugins.
3636 if (aKeyboardEvent.mMessage == eKeyPress &&
3637 IsFollowedByPrintableCharOrSysCharMessage()) {
3638 MOZ_RELEASE_ASSERT(aIndex < mCommittedCharsAndModifiers.Length());
3639 uint32_t foundPrintableCharMessages = 0;
3640 for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
3641 if (!IsPrintableCharOrSysCharMessage(mFollowingCharMsgs[i])) {
3642 // XXX Should we dispatch a plugin event for WM_*DEADCHAR messages and
3643 // WM_CHAR with a control character here? But we're not sure
3644 // how can we create such message queue (i.e., WM_CHAR or
3645 // WM_SYSCHAR with a printable character and such message are
3646 // generated by a keydown). So, let's ignore such case until
3647 // we'd get some bug reports.
3648 MOZ_LOG(gKeyLog, LogLevel::Warning,
3649 ("%p NativeKey::WillDispatchKeyboardEvent(), WARNING, "
3650 "ignoring %zuth message due to non-printable char message, %s",
3651 this, i + 1, ToString(mFollowingCharMsgs[i]).get()));
3652 continue;
3654 if (foundPrintableCharMessages++ == aIndex) {
3655 // Found message which caused the eKeyPress event.
3656 break;
3659 // Set modifier state from mCommittedCharsAndModifiers because some of them
3660 // might be different. For example, Shift key was pressed at inputting
3661 // dead char but Shift key was released before inputting next character.
3662 if (mCanIgnoreModifierStateAtKeyPress) {
3663 ModifierKeyState modKeyState(mModKeyState);
3664 modKeyState.Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT |
3665 MODIFIER_ALTGRAPH | MODIFIER_CAPSLOCK);
3666 modKeyState.Set(mCommittedCharsAndModifiers.ModifiersAt(aIndex));
3667 modKeyState.InitInputEvent(aKeyboardEvent);
3668 MOZ_LOG(gKeyLog, LogLevel::Info,
3669 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3670 "setting %uth modifier state to %s",
3671 this, aIndex + 1, ToString(modKeyState).get()));
3674 size_t longestLength =
3675 std::max(mInputtingStringAndModifiers.Length(),
3676 std::max(mShiftedString.Length(), mUnshiftedString.Length()));
3677 size_t skipUniChars = longestLength - mInputtingStringAndModifiers.Length();
3678 size_t skipShiftedChars = longestLength - mShiftedString.Length();
3679 size_t skipUnshiftedChars = longestLength - mUnshiftedString.Length();
3680 if (aIndex >= longestLength) {
3681 MOZ_LOG(
3682 gKeyLog, LogLevel::Info,
3683 ("%p NativeKey::WillDispatchKeyboardEvent(), does nothing for %uth "
3684 "%s event",
3685 this, aIndex + 1, ToChar(aKeyboardEvent.mMessage)));
3686 return;
3689 // Check if aKeyboardEvent is the last event for a key press.
3690 // So, if it's not an eKeyPress event, it's always the last event.
3691 // Otherwise, check if the index is the last character of
3692 // mCommittedCharsAndModifiers.
3693 bool isLastIndex = aKeyboardEvent.mMessage != eKeyPress ||
3694 mCommittedCharsAndModifiers.IsEmpty() ||
3695 mCommittedCharsAndModifiers.Length() - 1 == aIndex;
3697 nsTArray<AlternativeCharCode>& altArray =
3698 aKeyboardEvent.mAlternativeCharCodes;
3700 // Set charCode and adjust modifier state for every eKeyPress event.
3701 // This is not necessary for the other keyboard events because the other
3702 // keyboard events shouldn't have non-zero charCode value and should have
3703 // current modifier state.
3704 if (aKeyboardEvent.mMessage == eKeyPress && skipUniChars <= aIndex) {
3705 // XXX Modifying modifier state of aKeyboardEvent is illegal, but no way
3706 // to set different modifier state per keypress event except this
3707 // hack. Note that ideally, dead key should cause composition events
3708 // instead of keypress events, though.
3709 if (aIndex - skipUniChars < mInputtingStringAndModifiers.Length()) {
3710 ModifierKeyState modKeyState(mModKeyState);
3711 // If key in combination with Alt and/or Ctrl produces a different
3712 // character than without them then do not report these flags
3713 // because it is separate keyboard layout shift state. If dead-key
3714 // and base character does not produce a valid composite character
3715 // then both produced dead-key character and following base
3716 // character may have different modifier flags, too.
3717 modKeyState.Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT |
3718 MODIFIER_ALTGRAPH | MODIFIER_CAPSLOCK);
3719 modKeyState.Set(
3720 mInputtingStringAndModifiers.ModifiersAt(aIndex - skipUniChars));
3721 modKeyState.InitInputEvent(aKeyboardEvent);
3722 MOZ_LOG(gKeyLog, LogLevel::Info,
3723 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3724 "setting %uth modifier state to %s",
3725 this, aIndex + 1, ToString(modKeyState).get()));
3727 uint16_t uniChar =
3728 mInputtingStringAndModifiers.CharAt(aIndex - skipUniChars);
3730 // The mCharCode was set from mKeyValue. However, for example, when Ctrl key
3731 // is pressed, its value should indicate an ASCII character for backward
3732 // compatibility rather than inputting character without the modifiers.
3733 // Therefore, we need to modify mCharCode value here.
3734 aKeyboardEvent.SetCharCode(uniChar);
3735 MOZ_LOG(gKeyLog, LogLevel::Info,
3736 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3737 "setting %uth charCode to %s",
3738 this, aIndex + 1, GetCharacterCodeName(uniChar).get()));
3741 // We need to append alterntaive charCode values:
3742 // - if the event is eKeyPress, we need to append for the index because
3743 // eKeyPress event is dispatched for every character inputted by a
3744 // key press.
3745 // - if the event is not eKeyPress, we need to append for all characters
3746 // inputted by the key press because the other keyboard events (e.g.,
3747 // eKeyDown are eKeyUp) are fired only once for a key press.
3748 size_t count;
3749 if (aKeyboardEvent.mMessage == eKeyPress) {
3750 // Basically, append alternative charCode values only for the index.
3751 count = 1;
3752 // However, if it's the last eKeyPress event but different shift state
3753 // can input longer string, the last eKeyPress event should have all
3754 // remaining alternative charCode values.
3755 if (isLastIndex) {
3756 count = longestLength - aIndex;
3758 } else {
3759 count = longestLength;
3761 for (size_t i = 0; i < count; ++i) {
3762 uint16_t shiftedChar = 0, unshiftedChar = 0;
3763 if (skipShiftedChars <= aIndex + i) {
3764 shiftedChar = mShiftedString.CharAt(aIndex + i - skipShiftedChars);
3766 if (skipUnshiftedChars <= aIndex + i) {
3767 unshiftedChar = mUnshiftedString.CharAt(aIndex + i - skipUnshiftedChars);
3770 if (shiftedChar || unshiftedChar) {
3771 AlternativeCharCode chars(unshiftedChar, shiftedChar);
3772 altArray.AppendElement(chars);
3775 if (!isLastIndex) {
3776 continue;
3779 if (mUnshiftedLatinChar || mShiftedLatinChar) {
3780 AlternativeCharCode chars(mUnshiftedLatinChar, mShiftedLatinChar);
3781 altArray.AppendElement(chars);
3784 // Typically, following virtual keycodes are used for a key which can
3785 // input the character. However, these keycodes are also used for
3786 // other keys on some keyboard layout. E.g., in spite of Shift+'1'
3787 // inputs '+' on Thai keyboard layout, a key which is at '=/+'
3788 // key on ANSI keyboard layout is VK_OEM_PLUS. Native applications
3789 // handle it as '+' key if Ctrl key is pressed.
3790 char16_t charForOEMKeyCode = 0;
3791 switch (mVirtualKeyCode) {
3792 case VK_OEM_PLUS:
3793 charForOEMKeyCode = '+';
3794 break;
3795 case VK_OEM_COMMA:
3796 charForOEMKeyCode = ',';
3797 break;
3798 case VK_OEM_MINUS:
3799 charForOEMKeyCode = '-';
3800 break;
3801 case VK_OEM_PERIOD:
3802 charForOEMKeyCode = '.';
3803 break;
3805 if (charForOEMKeyCode && charForOEMKeyCode != mUnshiftedString.CharAt(0) &&
3806 charForOEMKeyCode != mShiftedString.CharAt(0) &&
3807 charForOEMKeyCode != mUnshiftedLatinChar &&
3808 charForOEMKeyCode != mShiftedLatinChar) {
3809 AlternativeCharCode OEMChars(charForOEMKeyCode, charForOEMKeyCode);
3810 altArray.AppendElement(OEMChars);
3815 /*****************************************************************************
3816 * mozilla::widget::KeyboardLayout
3817 *****************************************************************************/
3819 KeyboardLayout* KeyboardLayout::sInstance = nullptr;
3820 nsIUserIdleServiceInternal* KeyboardLayout::sIdleService = nullptr;
3822 // static
3823 KeyboardLayout* KeyboardLayout::GetInstance() {
3824 if (!sInstance) {
3825 sInstance = new KeyboardLayout();
3826 nsCOMPtr<nsIUserIdleServiceInternal> idleService =
3827 do_GetService("@mozilla.org/widget/useridleservice;1");
3828 // The refcount will be decreased at shut down.
3829 sIdleService = idleService.forget().take();
3831 return sInstance;
3834 // static
3835 void KeyboardLayout::Shutdown() {
3836 delete sInstance;
3837 sInstance = nullptr;
3838 NS_IF_RELEASE(sIdleService);
3841 // static
3842 void KeyboardLayout::NotifyIdleServiceOfUserActivity() {
3843 sIdleService->ResetIdleTimeOut(0);
3846 KeyboardLayout::KeyboardLayout()
3847 : mKeyboardLayout(0),
3848 mIsOverridden(false),
3849 mIsPendingToRestoreKeyboardLayout(false),
3850 mHasAltGr(false) {
3851 mDeadKeyTableListHead = nullptr;
3852 // A dead key sequence should be made from up to 5 keys. Therefore, 4 is
3853 // enough and makes sense because the item is uint8_t.
3854 // (Although, even if it's possible to be 6 keys or more in a sequence,
3855 // this array will be re-allocated).
3856 mActiveDeadKeys.SetCapacity(4);
3857 mDeadKeyShiftStates.SetCapacity(4);
3859 // NOTE: LoadLayout() should be called via OnLayoutChange().
3862 KeyboardLayout::~KeyboardLayout() { ReleaseDeadKeyTables(); }
3864 bool KeyboardLayout::IsPrintableCharKey(uint8_t aVirtualKey) {
3865 return GetKeyIndex(aVirtualKey) >= 0;
3868 WORD KeyboardLayout::ComputeScanCodeForVirtualKeyCode(
3869 uint8_t aVirtualKeyCode) const {
3870 return static_cast<WORD>(
3871 ::MapVirtualKeyEx(aVirtualKeyCode, MAPVK_VK_TO_VSC, GetLayout()));
3874 bool KeyboardLayout::IsDeadKey(uint8_t aVirtualKey,
3875 const ModifierKeyState& aModKeyState) const {
3876 int32_t virtualKeyIndex = GetKeyIndex(aVirtualKey);
3878 // XXX KeyboardLayout class doesn't support unusual keyboard layout which
3879 // maps some function keys as dead keys.
3880 if (virtualKeyIndex < 0) {
3881 return false;
3884 return mVirtualKeys[virtualKeyIndex].IsDeadKey(
3885 VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()));
3888 bool KeyboardLayout::IsSysKey(uint8_t aVirtualKey,
3889 const ModifierKeyState& aModKeyState) const {
3890 // If Alt key is not pressed, it's never a system key combination.
3891 // Additionally, if Ctrl key is pressed, it's never a system key combination
3892 // too.
3893 // FYI: Windows logo key state won't affect if it's a system key.
3894 if (!aModKeyState.IsAlt() || aModKeyState.IsControl()) {
3895 return false;
3898 int32_t virtualKeyIndex = GetKeyIndex(aVirtualKey);
3899 if (virtualKeyIndex < 0) {
3900 return true;
3903 UniCharsAndModifiers inputCharsAndModifiers =
3904 GetUniCharsAndModifiers(aVirtualKey, aModKeyState);
3905 if (inputCharsAndModifiers.IsEmpty()) {
3906 return true;
3909 // If the Alt key state isn't consumed, that means that the key with Alt
3910 // doesn't cause text input. So, the combination is a system key.
3911 return !!(inputCharsAndModifiers.ModifiersAt(0) & MODIFIER_ALT);
3914 void KeyboardLayout::InitNativeKey(NativeKey& aNativeKey) {
3915 if (mIsPendingToRestoreKeyboardLayout) {
3916 LoadLayout(::GetKeyboardLayout(0));
3919 // If the aNativeKey is initialized with WM_CHAR, the key information
3920 // should be discarded because mKeyValue should have the string to be
3921 // inputted.
3922 if (aNativeKey.mMsg.message == WM_CHAR) {
3923 char16_t ch = static_cast<char16_t>(aNativeKey.mMsg.wParam);
3924 // But don't set key value as printable key if the character is a control
3925 // character such as 0x0D at pressing Enter key.
3926 if (!NativeKey::IsControlChar(ch)) {
3927 aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
3928 Modifiers modifiers =
3929 aNativeKey.GetModifiers() & ~(MODIFIER_ALT | MODIFIER_CONTROL);
3930 aNativeKey.mCommittedCharsAndModifiers.Append(ch, modifiers);
3931 return;
3935 // If the aNativeKey is in a sequence to input a Unicode character with
3936 // Alt + numpad keys, we should just set the number as the inputting charcter.
3937 // Note that we should compute the key value from the virtual key code
3938 // because they may be mapped to alphabets, but they should be treated as
3939 // Alt + [0-9] even by web apps.
3940 // However, we shouldn't touch the key value if given virtual key code is
3941 // not a printable key because it may be synthesized by 3rd party utility
3942 // or just NumLock is unlocked and user tries to use shortcut key. In the
3943 // latter case, we cannot solve the conflict issue with Alt+foo shortcut key
3944 // and inputting a Unicode scalar value like reported to bug 1606655, though,
3945 // I have no better idea. Perhaps, `Alt` shouldn't be used for shortcut key
3946 // combination on Windows.
3947 if (aNativeKey.MaybeTypingUnicodeScalarValue() &&
3948 KeyboardLayout::IsPrintableCharKey(aNativeKey.mVirtualKeyCode)) {
3949 // If the key code value is mapped to a Numpad key, let's compute the key
3950 // value with it. This is same behavior as Chrome. In strictly speaking,
3951 // I think that the else block's computation is better because it seems
3952 // that Windows does not refer virtual key code value, but we should avoid
3953 // web-compat issues.
3954 char16_t num = '0';
3955 if (aNativeKey.mVirtualKeyCode >= VK_NUMPAD0 &&
3956 aNativeKey.mVirtualKeyCode <= VK_NUMPAD9) {
3957 num = '0' + aNativeKey.mVirtualKeyCode - VK_NUMPAD0;
3959 // Otherwise, let's use fake key value for making never match with
3960 // mnemonic.
3961 else {
3962 switch (aNativeKey.mScanCode) {
3963 case 0x0052: // Numpad0
3964 num = '0';
3965 break;
3966 case 0x004F: // Numpad1
3967 num = '1';
3968 break;
3969 case 0x0050: // Numpad2
3970 num = '2';
3971 break;
3972 case 0x0051: // Numpad3
3973 num = '3';
3974 break;
3975 case 0x004B: // Numpad4
3976 num = '4';
3977 break;
3978 case 0x004C: // Numpad5
3979 num = '5';
3980 break;
3981 case 0x004D: // Numpad6
3982 num = '6';
3983 break;
3984 case 0x0047: // Numpad7
3985 num = '7';
3986 break;
3987 case 0x0048: // Numpad8
3988 num = '8';
3989 break;
3990 case 0x0049: // Numpad9
3991 num = '9';
3992 break;
3993 default:
3994 MOZ_ASSERT_UNREACHABLE(
3995 "IsTypingUnicodeScalarValue() must have returned true for wrong "
3996 "scancode");
3997 break;
4000 aNativeKey.mCommittedCharsAndModifiers.Append(num,
4001 aNativeKey.GetModifiers());
4002 aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
4003 return;
4006 // When it's followed by non-dead char message(s) for printable character(s),
4007 // aNativeKey should dispatch eKeyPress events for them rather than
4008 // information from keyboard layout because respecting WM_(SYS)CHAR messages
4009 // guarantees that we can always input characters which is expected by
4010 // the user even if the user uses odd keyboard layout.
4011 // Or, when it was followed by non-dead char message for a printable character
4012 // but it's gone at removing the message from the queue, let's treat it
4013 // as a key inputting empty string.
4014 if (aNativeKey.IsFollowedByPrintableCharOrSysCharMessage() ||
4015 aNativeKey.mCharMessageHasGone) {
4016 MOZ_ASSERT(!aNativeKey.IsCharMessage(aNativeKey.mMsg));
4017 if (aNativeKey.IsFollowedByPrintableCharOrSysCharMessage()) {
4018 // Initialize mCommittedCharsAndModifiers with following char messages.
4019 aNativeKey.InitCommittedCharsAndModifiersWithFollowingCharMessages();
4020 MOZ_ASSERT(!aNativeKey.mCommittedCharsAndModifiers.IsEmpty());
4022 // Currently, we are doing a ugly hack to keypress events to cause
4023 // inputting character even if Ctrl or Alt key is pressed, that is, we
4024 // remove Ctrl and Alt modifier state from keypress event. However, for
4025 // example, Ctrl+Space which causes ' ' of WM_CHAR message never causes
4026 // keypress event whose ctrlKey is true. For preventing this problem,
4027 // we should mark as not removable if Ctrl or Alt key does not cause
4028 // changing inputting character.
4029 if (IsPrintableCharKey(aNativeKey.mOriginalVirtualKeyCode) &&
4030 (aNativeKey.IsControl() ^ aNativeKey.IsAlt())) {
4031 ModifierKeyState state = aNativeKey.ModifierKeyStateRef();
4032 state.Unset(MODIFIER_ALT | MODIFIER_CONTROL);
4033 UniCharsAndModifiers charsWithoutModifier =
4034 GetUniCharsAndModifiers(aNativeKey.GenericVirtualKeyCode(), state);
4035 aNativeKey.mCanIgnoreModifierStateAtKeyPress =
4036 !charsWithoutModifier.UniCharsEqual(
4037 aNativeKey.mCommittedCharsAndModifiers);
4039 } else {
4040 aNativeKey.mCommittedCharsAndModifiers.Clear();
4042 aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
4044 // If it's not in dead key sequence, we don't need to do anymore here.
4045 if (!IsInDeadKeySequence()) {
4046 return;
4049 // If it's in dead key sequence and dead char is inputted as is, we need to
4050 // set the previous modifier state which is stored when preceding dead key
4051 // is pressed.
4052 UniCharsAndModifiers deadChars = GetDeadUniCharsAndModifiers();
4053 aNativeKey.mCommittedCharsAndModifiers.OverwriteModifiersIfBeginsWith(
4054 deadChars);
4055 // Finish the dead key sequence.
4056 DeactivateDeadKeyState();
4057 return;
4060 // If it's a dead key, aNativeKey will be initialized by
4061 // MaybeInitNativeKeyAsDeadKey().
4062 if (MaybeInitNativeKeyAsDeadKey(aNativeKey)) {
4063 return;
4066 // If the key is not a usual printable key, KeyboardLayout class assume that
4067 // it's not cause dead char nor printable char. Therefore, there are nothing
4068 // to do here fore such keys (e.g., function keys).
4069 // However, this should keep dead key state even if non-printable key is
4070 // pressed during a dead key sequence.
4071 if (!IsPrintableCharKey(aNativeKey.mOriginalVirtualKeyCode)) {
4072 return;
4075 MOZ_ASSERT(aNativeKey.mOriginalVirtualKeyCode != VK_PACKET,
4076 "At handling VK_PACKET, we shouldn't refer keyboard layout");
4077 MOZ_ASSERT(
4078 aNativeKey.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING,
4079 "Printable key's key name index must be KEY_NAME_INDEX_USE_STRING");
4081 // If it's in dead key handling and the pressed key causes a composite
4082 // character, aNativeKey will be initialized by
4083 // MaybeInitNativeKeyWithCompositeChar().
4084 if (MaybeInitNativeKeyWithCompositeChar(aNativeKey)) {
4085 return;
4088 UniCharsAndModifiers baseChars = GetUniCharsAndModifiers(aNativeKey);
4090 // If the key press isn't related to any dead keys, initialize aNativeKey
4091 // with the characters which should be caused by the key.
4092 if (!IsInDeadKeySequence()) {
4093 aNativeKey.mCommittedCharsAndModifiers = baseChars;
4094 return;
4097 // If the key doesn't cause a composite character with preceding dead key,
4098 // initialize aNativeKey with the dead-key character followed by current
4099 // key's character.
4100 UniCharsAndModifiers deadChars = GetDeadUniCharsAndModifiers();
4101 aNativeKey.mCommittedCharsAndModifiers = deadChars + baseChars;
4102 if (aNativeKey.IsKeyDownMessage()) {
4103 DeactivateDeadKeyState();
4107 bool KeyboardLayout::MaybeInitNativeKeyAsDeadKey(NativeKey& aNativeKey) {
4108 // Only when it's not in dead key sequence, we can trust IsDeadKey() result.
4109 if (!IsInDeadKeySequence() && !IsDeadKey(aNativeKey)) {
4110 return false;
4113 // When keydown message is followed by a dead char message, it should be
4114 // initialized as dead key.
4115 bool isDeadKeyDownEvent =
4116 aNativeKey.IsKeyDownMessage() && aNativeKey.IsFollowedByDeadCharMessage();
4118 // When keyup message is received, let's check if it's one of preceding
4119 // dead keys because keydown message order and keyup message order may be
4120 // different.
4121 bool isDeadKeyUpEvent =
4122 !aNativeKey.IsKeyDownMessage() &&
4123 mActiveDeadKeys.Contains(aNativeKey.GenericVirtualKeyCode());
4125 if (isDeadKeyDownEvent || isDeadKeyUpEvent) {
4126 ActivateDeadKeyState(aNativeKey);
4127 // Any dead key events don't generate characters. So, a dead key should
4128 // cause only keydown event and keyup event whose KeyboardEvent.key
4129 // values are "Dead".
4130 aNativeKey.mCommittedCharsAndModifiers.Clear();
4131 aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_Dead;
4132 return true;
4135 // At keydown message handling, we need to forget the first dead key
4136 // because there is no guarantee coming WM_KEYUP for the second dead
4137 // key before next WM_KEYDOWN. E.g., due to auto key repeat or pressing
4138 // another dead key before releasing current key. Therefore, we can
4139 // set only a character for current key for keyup event.
4140 if (!IsInDeadKeySequence()) {
4141 aNativeKey.mCommittedCharsAndModifiers =
4142 GetUniCharsAndModifiers(aNativeKey);
4143 return true;
4146 // When non-printable key event comes during a dead key sequence, that must
4147 // be a modifier key event. So, such events shouldn't be handled as a part
4148 // of the dead key sequence.
4149 if (!IsDeadKey(aNativeKey)) {
4150 return false;
4153 // FYI: Following code may run when the user doesn't input text actually
4154 // but the key sequence is a dead key sequence. For example,
4155 // ` -> Ctrl+` with Spanish keyboard layout. Let's keep using this
4156 // complicated code for now because this runs really rarely.
4158 // Dead key followed by another dead key may cause a composed character
4159 // (e.g., "Russian - Mnemonic" keyboard layout's 's' -> 'c').
4160 if (MaybeInitNativeKeyWithCompositeChar(aNativeKey)) {
4161 return true;
4164 // Otherwise, dead key followed by another dead key causes inputting both
4165 // character.
4166 UniCharsAndModifiers prevDeadChars = GetDeadUniCharsAndModifiers();
4167 UniCharsAndModifiers newChars = GetUniCharsAndModifiers(aNativeKey);
4168 // But keypress events should be fired for each committed character.
4169 aNativeKey.mCommittedCharsAndModifiers = prevDeadChars + newChars;
4170 if (aNativeKey.IsKeyDownMessage()) {
4171 DeactivateDeadKeyState();
4173 return true;
4176 bool KeyboardLayout::MaybeInitNativeKeyWithCompositeChar(
4177 NativeKey& aNativeKey) {
4178 if (!IsInDeadKeySequence()) {
4179 return false;
4182 if (NS_WARN_IF(!IsPrintableCharKey(aNativeKey.mOriginalVirtualKeyCode))) {
4183 return false;
4186 UniCharsAndModifiers baseChars = GetUniCharsAndModifiers(aNativeKey);
4187 if (baseChars.IsEmpty() || !baseChars.CharAt(0)) {
4188 return false;
4191 char16_t compositeChar = GetCompositeChar(baseChars.CharAt(0));
4192 if (!compositeChar) {
4193 return false;
4196 // Active dead-key and base character does produce exactly one composite
4197 // character.
4198 aNativeKey.mCommittedCharsAndModifiers.Append(compositeChar,
4199 baseChars.ModifiersAt(0));
4200 if (aNativeKey.IsKeyDownMessage()) {
4201 DeactivateDeadKeyState();
4203 return true;
4206 UniCharsAndModifiers KeyboardLayout::GetUniCharsAndModifiers(
4207 uint8_t aVirtualKey, VirtualKey::ShiftState aShiftState) const {
4208 UniCharsAndModifiers result;
4209 int32_t key = GetKeyIndex(aVirtualKey);
4210 if (key < 0) {
4211 return result;
4213 return mVirtualKeys[key].GetUniChars(aShiftState);
4216 UniCharsAndModifiers KeyboardLayout::GetDeadUniCharsAndModifiers() const {
4217 MOZ_RELEASE_ASSERT(mActiveDeadKeys.Length() == mDeadKeyShiftStates.Length());
4219 if (NS_WARN_IF(mActiveDeadKeys.IsEmpty())) {
4220 return UniCharsAndModifiers();
4223 UniCharsAndModifiers result;
4224 for (size_t i = 0; i < mActiveDeadKeys.Length(); ++i) {
4225 result +=
4226 GetUniCharsAndModifiers(mActiveDeadKeys[i], mDeadKeyShiftStates[i]);
4228 return result;
4231 char16_t KeyboardLayout::GetCompositeChar(char16_t aBaseChar) const {
4232 if (NS_WARN_IF(mActiveDeadKeys.IsEmpty())) {
4233 return 0;
4235 // XXX Currently, we don't support computing a composite character with
4236 // two or more dead keys since it needs big table for supporting
4237 // long chained dead keys. However, this should be a minor bug
4238 // because this runs only when the latest keydown event does not cause
4239 // WM_(SYS)CHAR messages. So, when user wants to input a character,
4240 // this path never runs.
4241 if (mActiveDeadKeys.Length() > 1) {
4242 return 0;
4244 int32_t key = GetKeyIndex(mActiveDeadKeys[0]);
4245 if (key < 0) {
4246 return 0;
4248 return mVirtualKeys[key].GetCompositeChar(mDeadKeyShiftStates[0], aBaseChar);
4251 // static
4252 HKL KeyboardLayout::GetActiveLayout() { return GetInstance()->mKeyboardLayout; }
4254 // static
4255 nsCString KeyboardLayout::GetActiveLayoutName() {
4256 return GetInstance()->GetLayoutName(GetActiveLayout());
4259 static bool IsValidKeyboardLayoutsChild(const nsAString& aChildName) {
4260 if (aChildName.Length() != 8) {
4261 return false;
4263 for (size_t i = 0; i < aChildName.Length(); i++) {
4264 if ((aChildName[i] >= '0' && aChildName[i] <= '9') ||
4265 (aChildName[i] >= 'a' && aChildName[i] <= 'f') ||
4266 (aChildName[i] >= 'A' && aChildName[i] <= 'F')) {
4267 continue;
4269 return false;
4271 return true;
4274 nsCString KeyboardLayout::GetLayoutName(HKL aLayout) const {
4275 const wchar_t kKeyboardLayouts[] =
4276 L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\";
4277 uint16_t language = reinterpret_cast<uintptr_t>(aLayout) & 0xFFFF;
4278 uint16_t layout = (reinterpret_cast<uintptr_t>(aLayout) >> 16) & 0xFFFF;
4279 // If the layout is less than 0xA000XXXX (normal keyboard layout for the
4280 // language) or 0xEYYYXXXX (IMM-IME), we can retrieve its name simply.
4281 if (layout < 0xA000 || (layout & 0xF000) == 0xE000) {
4282 nsAutoString key(kKeyboardLayouts);
4283 key.AppendPrintf("%08" PRIXPTR, layout < 0xA000
4284 ? layout
4285 : reinterpret_cast<uintptr_t>(aLayout));
4286 wchar_t buf[256];
4287 if (NS_WARN_IF(!WinUtils::GetRegistryKey(
4288 HKEY_LOCAL_MACHINE, key.get(), L"Layout Text", buf, sizeof(buf)))) {
4289 return "No name or too long name"_ns;
4291 return NS_ConvertUTF16toUTF8(buf);
4294 if (NS_WARN_IF((layout & 0xF000) != 0xF000)) {
4295 nsCString result;
4296 result.AppendPrintf("Odd HKL: 0x%08" PRIXPTR,
4297 reinterpret_cast<uintptr_t>(aLayout));
4298 return result;
4301 // Otherwise, we need to walk the registry under "Keyboard Layouts".
4302 nsCOMPtr<nsIWindowsRegKey> regKey =
4303 do_CreateInstance("@mozilla.org/windows-registry-key;1");
4304 if (NS_WARN_IF(!regKey)) {
4305 return ""_ns;
4307 nsresult rv =
4308 regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
4309 nsString(kKeyboardLayouts), nsIWindowsRegKey::ACCESS_READ);
4310 if (NS_WARN_IF(NS_FAILED(rv))) {
4311 return ""_ns;
4313 uint32_t childCount = 0;
4314 if (NS_WARN_IF(NS_FAILED(regKey->GetChildCount(&childCount))) ||
4315 NS_WARN_IF(!childCount)) {
4316 return ""_ns;
4318 for (uint32_t i = 0; i < childCount; i++) {
4319 nsAutoString childName;
4320 if (NS_WARN_IF(NS_FAILED(regKey->GetChildName(i, childName))) ||
4321 !IsValidKeyboardLayoutsChild(childName)) {
4322 continue;
4324 uint32_t childNum = static_cast<uint32_t>(childName.ToInteger64(&rv, 16));
4325 if (NS_WARN_IF(NS_FAILED(rv))) {
4326 continue;
4328 // Ignore normal keyboard layouts for each language.
4329 if (childNum <= 0xFFFF) {
4330 continue;
4332 // If it doesn't start with 'A' nor 'a', language should be matched.
4333 if ((childNum & 0xFFFF) != language &&
4334 (childNum & 0xF0000000) != 0xA0000000) {
4335 continue;
4337 // Then, the child should have "Layout Id" which is "YYY" of 0xFYYYXXXX.
4338 nsAutoString key(kKeyboardLayouts);
4339 key += childName;
4340 wchar_t buf[256];
4341 if (NS_WARN_IF(!WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE, key.get(),
4342 L"Layout Id", buf, sizeof(buf)))) {
4343 continue;
4345 uint16_t layoutId = wcstol(buf, nullptr, 16);
4346 if (layoutId != (layout & 0x0FFF)) {
4347 continue;
4349 if (NS_WARN_IF(!WinUtils::GetRegistryKey(
4350 HKEY_LOCAL_MACHINE, key.get(), L"Layout Text", buf, sizeof(buf)))) {
4351 continue;
4353 return NS_ConvertUTF16toUTF8(buf);
4355 return ""_ns;
4358 void KeyboardLayout::LoadLayout(HKL aLayout) {
4359 mIsPendingToRestoreKeyboardLayout = false;
4361 if (mKeyboardLayout == aLayout) {
4362 return;
4365 mKeyboardLayout = aLayout;
4366 mHasAltGr = false;
4368 MOZ_LOG(gKeyLog, LogLevel::Info,
4369 ("KeyboardLayout::LoadLayout(aLayout=0x%p (%s))", aLayout,
4370 GetLayoutName(aLayout).get()));
4372 BYTE kbdState[256];
4373 memset(kbdState, 0, sizeof(kbdState));
4375 BYTE originalKbdState[256];
4376 // Bitfield with all shift states that have at least one dead-key.
4377 uint16_t shiftStatesWithDeadKeys = 0;
4378 // Bitfield with all shift states that produce any possible dead-key base
4379 // characters.
4380 uint16_t shiftStatesWithBaseChars = 0;
4382 mActiveDeadKeys.Clear();
4383 mDeadKeyShiftStates.Clear();
4385 ReleaseDeadKeyTables();
4387 ::GetKeyboardState(originalKbdState);
4389 // For each shift state gather all printable characters that are produced
4390 // for normal case when no any dead-key is active.
4392 for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) {
4393 VirtualKey::FillKbdState(kbdState, shiftState);
4394 bool isAltGr = VirtualKey::IsAltGrIndex(shiftState);
4395 for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
4396 int32_t vki = GetKeyIndex(virtualKey);
4397 if (vki < 0) {
4398 continue;
4400 NS_ASSERTION(uint32_t(vki) < ArrayLength(mVirtualKeys), "invalid index");
4401 char16_t uniChars[5];
4402 int32_t ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)uniChars,
4403 ArrayLength(uniChars), 0, mKeyboardLayout);
4404 // dead-key
4405 if (ret < 0) {
4406 shiftStatesWithDeadKeys |= (1 << shiftState);
4407 // Repeat dead-key to deactivate it and get its character
4408 // representation.
4409 char16_t deadChar[2];
4410 ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)deadChar,
4411 ArrayLength(deadChar), 0, mKeyboardLayout);
4412 NS_ASSERTION(ret == 2, "Expecting twice repeated dead-key character");
4413 mVirtualKeys[vki].SetDeadChar(shiftState, deadChar[0]);
4415 MOZ_LOG(gKeyLog, LogLevel::Verbose,
4416 (" %s (%d): DeadChar(%s, %s) (ret=%d)",
4417 kVirtualKeyName[virtualKey], vki,
4418 GetShiftStateName(shiftState).get(),
4419 GetCharacterCodeNames(deadChar, 1).get(), ret));
4420 } else {
4421 if (ret == 1) {
4422 // dead-key can pair only with exactly one base character.
4423 shiftStatesWithBaseChars |= (1 << shiftState);
4425 mVirtualKeys[vki].SetNormalChars(shiftState, uniChars, ret);
4426 MOZ_LOG(gKeyLog, LogLevel::Verbose,
4427 (" %s (%d): NormalChar(%s, %s) (ret=%d)",
4428 kVirtualKeyName[virtualKey], vki,
4429 GetShiftStateName(shiftState).get(),
4430 GetCharacterCodeNames(uniChars, ret).get(), ret));
4433 // If the key inputs at least one character with AltGr modifier,
4434 // check if AltGr changes inputting character. If it does, mark
4435 // this keyboard layout has AltGr modifier actually.
4436 if (!mHasAltGr && ret > 0 && isAltGr &&
4437 mVirtualKeys[vki].IsChangedByAltGr(shiftState)) {
4438 mHasAltGr = true;
4439 MOZ_LOG(gKeyLog, LogLevel::Info,
4440 (" Found a key (%s) changed by AltGr: %s -> %s (%s) (ret=%d)",
4441 kVirtualKeyName[virtualKey],
4442 GetCharacterCodeNames(
4443 mVirtualKeys[vki].GetNativeUniChars(
4444 shiftState - VirtualKey::ShiftStateIndex::eAltGr))
4445 .get(),
4446 GetCharacterCodeNames(
4447 mVirtualKeys[vki].GetNativeUniChars(shiftState))
4448 .get(),
4449 GetShiftStateName(shiftState).get(), ret));
4454 // Now process each dead-key to find all its base characters and resulting
4455 // composite characters.
4456 for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) {
4457 if (!(shiftStatesWithDeadKeys & (1 << shiftState))) {
4458 continue;
4461 VirtualKey::FillKbdState(kbdState, shiftState);
4463 for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
4464 int32_t vki = GetKeyIndex(virtualKey);
4465 if (vki >= 0 && mVirtualKeys[vki].IsDeadKey(shiftState)) {
4466 DeadKeyEntry deadKeyArray[256];
4467 int32_t n = GetDeadKeyCombinations(
4468 virtualKey, kbdState, shiftStatesWithBaseChars, deadKeyArray,
4469 ArrayLength(deadKeyArray));
4470 const DeadKeyTable* dkt =
4471 mVirtualKeys[vki].MatchingDeadKeyTable(deadKeyArray, n);
4472 if (!dkt) {
4473 dkt = AddDeadKeyTable(deadKeyArray, n);
4475 mVirtualKeys[vki].AttachDeadKeyTable(shiftState, dkt);
4480 ::SetKeyboardState(originalKbdState);
4482 if (MOZ_LOG_TEST(gKeyLog, LogLevel::Verbose)) {
4483 static const UINT kExtendedScanCode[] = {0x0000, 0xE000};
4484 static const UINT kMapType = MAPVK_VSC_TO_VK_EX;
4485 MOZ_LOG(gKeyLog, LogLevel::Verbose,
4486 ("Logging virtual keycode values for scancode (0x%p)...",
4487 mKeyboardLayout));
4488 for (uint32_t i = 0; i < ArrayLength(kExtendedScanCode); i++) {
4489 for (uint32_t j = 1; j <= 0xFF; j++) {
4490 UINT scanCode = kExtendedScanCode[i] + j;
4491 UINT virtualKeyCode =
4492 ::MapVirtualKeyEx(scanCode, kMapType, mKeyboardLayout);
4493 MOZ_LOG(gKeyLog, LogLevel::Verbose,
4494 ("0x%04X, %s", scanCode, kVirtualKeyName[virtualKeyCode]));
4499 MOZ_LOG(gKeyLog, LogLevel::Info,
4500 (" AltGr key is %s in %s", mHasAltGr ? "found" : "not found",
4501 GetLayoutName(aLayout).get()));
4504 inline int32_t KeyboardLayout::GetKeyIndex(uint8_t aVirtualKey) {
4505 // Currently these 68 (NS_NUM_OF_KEYS) virtual keys are assumed
4506 // to produce visible representation:
4507 // 0x20 - VK_SPACE ' '
4508 // 0x30..0x39 '0'..'9'
4509 // 0x41..0x5A 'A'..'Z'
4510 // 0x60..0x69 '0'..'9' on numpad
4511 // 0x6A - VK_MULTIPLY '*' on numpad
4512 // 0x6B - VK_ADD '+' on numpad
4513 // 0x6D - VK_SUBTRACT '-' on numpad
4514 // 0x6E - VK_DECIMAL '.' on numpad
4515 // 0x6F - VK_DIVIDE '/' on numpad
4516 // 0x6E - VK_DECIMAL '.'
4517 // 0xBA - VK_OEM_1 ';:' for US
4518 // 0xBB - VK_OEM_PLUS '+' any country
4519 // 0xBC - VK_OEM_COMMA ',' any country
4520 // 0xBD - VK_OEM_MINUS '-' any country
4521 // 0xBE - VK_OEM_PERIOD '.' any country
4522 // 0xBF - VK_OEM_2 '/?' for US
4523 // 0xC0 - VK_OEM_3 '`~' for US
4524 // 0xC1 - VK_ABNT_C1 '/?' for Brazilian
4525 // 0xC2 - VK_ABNT_C2 separator key on numpad (Brazilian or JIS for Mac)
4526 // 0xDB - VK_OEM_4 '[{' for US
4527 // 0xDC - VK_OEM_5 '\|' for US
4528 // 0xDD - VK_OEM_6 ']}' for US
4529 // 0xDE - VK_OEM_7 ''"' for US
4530 // 0xDF - VK_OEM_8
4531 // 0xE1 - no name
4532 // 0xE2 - VK_OEM_102 '\_' for JIS
4533 // 0xE3 - no name
4534 // 0xE4 - no name
4536 static const int8_t xlat[256] = {
4537 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
4538 //-----------------------------------------------------------------------
4539 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00
4540 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10
4541 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20
4542 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, // 30
4543 -1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 40
4544 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, -1, -1, // 50
4545 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, -1, 49, 50, 51, // 60
4546 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 70
4547 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
4548 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90
4549 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0
4550 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 53, 54, 55, 56, 57, // B0
4551 58, 59, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0
4552 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 61, 62, 63, 64, 65, // D0
4553 -1, 66, 67, 68, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0
4554 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // F0
4557 return xlat[aVirtualKey];
4560 int KeyboardLayout::CompareDeadKeyEntries(const void* aArg1, const void* aArg2,
4561 void*) {
4562 const DeadKeyEntry* arg1 = static_cast<const DeadKeyEntry*>(aArg1);
4563 const DeadKeyEntry* arg2 = static_cast<const DeadKeyEntry*>(aArg2);
4565 return arg1->BaseChar - arg2->BaseChar;
4568 const DeadKeyTable* KeyboardLayout::AddDeadKeyTable(
4569 const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries) {
4570 DeadKeyTableListEntry* next = mDeadKeyTableListHead;
4572 const size_t bytes = offsetof(DeadKeyTableListEntry, data) +
4573 DeadKeyTable::SizeInBytes(aEntries);
4574 uint8_t* p = new uint8_t[bytes];
4576 mDeadKeyTableListHead = reinterpret_cast<DeadKeyTableListEntry*>(p);
4577 mDeadKeyTableListHead->next = next;
4579 DeadKeyTable* dkt =
4580 reinterpret_cast<DeadKeyTable*>(mDeadKeyTableListHead->data);
4582 dkt->Init(aDeadKeyArray, aEntries);
4584 return dkt;
4587 void KeyboardLayout::ReleaseDeadKeyTables() {
4588 while (mDeadKeyTableListHead) {
4589 uint8_t* p = reinterpret_cast<uint8_t*>(mDeadKeyTableListHead);
4590 mDeadKeyTableListHead = mDeadKeyTableListHead->next;
4592 delete[] p;
4596 bool KeyboardLayout::EnsureDeadKeyActive(bool aIsActive, uint8_t aDeadKey,
4597 const PBYTE aDeadKeyKbdState) {
4598 int32_t ret;
4599 do {
4600 char16_t dummyChars[5];
4601 ret =
4602 ::ToUnicodeEx(aDeadKey, 0, (PBYTE)aDeadKeyKbdState, (LPWSTR)dummyChars,
4603 ArrayLength(dummyChars), 0, mKeyboardLayout);
4604 // returned values:
4605 // <0 - Dead key state is active. The keyboard driver will wait for next
4606 // character.
4607 // 1 - Previous pressed key was a valid base character that produced
4608 // exactly one composite character.
4609 // >1 - Previous pressed key does not produce any composite characters.
4610 // Return dead-key character followed by base character(s).
4611 } while ((ret < 0) != aIsActive);
4613 return (ret < 0);
4616 void KeyboardLayout::ActivateDeadKeyState(const NativeKey& aNativeKey) {
4617 // Dead-key state should be activated at keydown.
4618 if (!aNativeKey.IsKeyDownMessage()) {
4619 return;
4622 mActiveDeadKeys.AppendElement(aNativeKey.mOriginalVirtualKeyCode);
4623 mDeadKeyShiftStates.AppendElement(aNativeKey.GetShiftState());
4626 void KeyboardLayout::DeactivateDeadKeyState() {
4627 if (mActiveDeadKeys.IsEmpty()) {
4628 return;
4631 BYTE kbdState[256];
4632 memset(kbdState, 0, sizeof(kbdState));
4634 // Assume that the last dead key can finish dead key sequence.
4635 VirtualKey::FillKbdState(kbdState, mDeadKeyShiftStates.LastElement());
4636 EnsureDeadKeyActive(false, mActiveDeadKeys.LastElement(), kbdState);
4637 mActiveDeadKeys.Clear();
4638 mDeadKeyShiftStates.Clear();
4641 bool KeyboardLayout::AddDeadKeyEntry(char16_t aBaseChar,
4642 char16_t aCompositeChar,
4643 DeadKeyEntry* aDeadKeyArray,
4644 uint32_t aEntries) {
4645 for (uint32_t index = 0; index < aEntries; index++) {
4646 if (aDeadKeyArray[index].BaseChar == aBaseChar) {
4647 return false;
4651 aDeadKeyArray[aEntries].BaseChar = aBaseChar;
4652 aDeadKeyArray[aEntries].CompositeChar = aCompositeChar;
4654 return true;
4657 uint32_t KeyboardLayout::GetDeadKeyCombinations(
4658 uint8_t aDeadKey, const PBYTE aDeadKeyKbdState,
4659 uint16_t aShiftStatesWithBaseChars, DeadKeyEntry* aDeadKeyArray,
4660 uint32_t aMaxEntries) {
4661 bool deadKeyActive = false;
4662 uint32_t entries = 0;
4663 BYTE kbdState[256];
4664 memset(kbdState, 0, sizeof(kbdState));
4666 for (uint32_t shiftState = 0; shiftState < 16; shiftState++) {
4667 if (!(aShiftStatesWithBaseChars & (1 << shiftState))) {
4668 continue;
4671 VirtualKey::FillKbdState(kbdState, shiftState);
4673 for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
4674 int32_t vki = GetKeyIndex(virtualKey);
4675 // Dead-key can pair only with such key that produces exactly one base
4676 // character.
4677 if (vki >= 0 &&
4678 mVirtualKeys[vki].GetNativeUniChars(shiftState).Length() == 1) {
4679 // Ensure dead-key is in active state, when it swallows entered
4680 // character and waits for the next pressed key.
4681 if (!deadKeyActive) {
4682 deadKeyActive = EnsureDeadKeyActive(true, aDeadKey, aDeadKeyKbdState);
4685 // Depending on the character the followed the dead-key, the keyboard
4686 // driver can produce one composite character, or a dead-key character
4687 // followed by a second character.
4688 char16_t compositeChars[5];
4689 int32_t ret =
4690 ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)compositeChars,
4691 ArrayLength(compositeChars), 0, mKeyboardLayout);
4692 switch (ret) {
4693 case 0:
4694 // This key combination does not produce any characters. The
4695 // dead-key is still in active state.
4696 break;
4697 case 1: {
4698 char16_t baseChars[5];
4699 ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)baseChars,
4700 ArrayLength(baseChars), 0, mKeyboardLayout);
4701 if (entries < aMaxEntries) {
4702 switch (ret) {
4703 case 1:
4704 // Exactly one composite character produced. Now, when
4705 // dead-key is not active, repeat the last character one more
4706 // time to determine the base character.
4707 if (AddDeadKeyEntry(baseChars[0], compositeChars[0],
4708 aDeadKeyArray, entries)) {
4709 entries++;
4711 deadKeyActive = false;
4712 break;
4713 case -1: {
4714 // If pressing another dead-key produces different character,
4715 // we should register the dead-key entry with first character
4716 // produced by current key.
4718 // First inactivate the dead-key state completely.
4719 deadKeyActive =
4720 EnsureDeadKeyActive(false, aDeadKey, aDeadKeyKbdState);
4721 if (NS_WARN_IF(deadKeyActive)) {
4722 MOZ_LOG(gKeyLog, LogLevel::Error,
4723 (" failed to deactivating the dead-key state..."));
4724 break;
4726 for (int32_t i = 0; i < 5; ++i) {
4727 ret = ::ToUnicodeEx(
4728 virtualKey, 0, kbdState, (LPWSTR)baseChars,
4729 ArrayLength(baseChars), 0, mKeyboardLayout);
4730 if (ret >= 0) {
4731 break;
4734 if (ret > 0 &&
4735 AddDeadKeyEntry(baseChars[0], compositeChars[0],
4736 aDeadKeyArray, entries)) {
4737 entries++;
4739 // Inactivate dead-key state for current virtual keycode.
4740 EnsureDeadKeyActive(false, virtualKey, kbdState);
4741 break;
4743 default:
4744 NS_WARNING("File a bug for this dead-key handling!");
4745 deadKeyActive = false;
4746 break;
4749 MOZ_LOG(
4750 gKeyLog, LogLevel::Verbose,
4751 (" %s -> %s (%d): DeadKeyEntry(%s, %s) (ret=%d)",
4752 kVirtualKeyName[aDeadKey], kVirtualKeyName[virtualKey], vki,
4753 GetCharacterCodeNames(compositeChars, 1).get(),
4754 ret <= 0
4755 ? "''"
4756 : GetCharacterCodeNames(baseChars, std::min(ret, 5)).get(),
4757 ret));
4758 break;
4760 default:
4761 // 1. Unexpected dead-key. Dead-key chaining is not supported.
4762 // 2. More than one character generated. This is not a valid
4763 // dead-key and base character combination.
4764 deadKeyActive = false;
4765 MOZ_LOG(
4766 gKeyLog, LogLevel::Verbose,
4767 (" %s -> %s (%d): Unsupport dead key type(%s) (ret=%d)",
4768 kVirtualKeyName[aDeadKey], kVirtualKeyName[virtualKey], vki,
4769 ret <= 0
4770 ? "''"
4771 : GetCharacterCodeNames(compositeChars, std::min(ret, 5))
4772 .get(),
4773 ret));
4774 break;
4780 if (deadKeyActive) {
4781 deadKeyActive = EnsureDeadKeyActive(false, aDeadKey, aDeadKeyKbdState);
4784 NS_QuickSort(aDeadKeyArray, entries, sizeof(DeadKeyEntry),
4785 CompareDeadKeyEntries, nullptr);
4786 return entries;
4789 uint32_t KeyboardLayout::ConvertNativeKeyCodeToDOMKeyCode(
4790 UINT aNativeKeyCode) const {
4791 // Alphabet or Numeric or Numpad or Function keys
4792 if ((aNativeKeyCode >= 0x30 && aNativeKeyCode <= 0x39) ||
4793 (aNativeKeyCode >= 0x41 && aNativeKeyCode <= 0x5A) ||
4794 (aNativeKeyCode >= 0x60 && aNativeKeyCode <= 0x87)) {
4795 return static_cast<uint32_t>(aNativeKeyCode);
4797 switch (aNativeKeyCode) {
4798 // Following keycodes are same as our DOM keycodes
4799 case VK_CANCEL:
4800 case VK_BACK:
4801 case VK_TAB:
4802 case VK_CLEAR:
4803 case VK_RETURN:
4804 case VK_SHIFT:
4805 case VK_CONTROL:
4806 case VK_MENU: // Alt
4807 case VK_PAUSE:
4808 case VK_CAPITAL: // CAPS LOCK
4809 case VK_KANA: // same as VK_HANGUL
4810 case VK_JUNJA:
4811 case VK_FINAL:
4812 case VK_HANJA: // same as VK_KANJI
4813 case VK_ESCAPE:
4814 case VK_CONVERT:
4815 case VK_NONCONVERT:
4816 case VK_ACCEPT:
4817 case VK_MODECHANGE:
4818 case VK_SPACE:
4819 case VK_PRIOR: // PAGE UP
4820 case VK_NEXT: // PAGE DOWN
4821 case VK_END:
4822 case VK_HOME:
4823 case VK_LEFT:
4824 case VK_UP:
4825 case VK_RIGHT:
4826 case VK_DOWN:
4827 case VK_SELECT:
4828 case VK_PRINT:
4829 case VK_EXECUTE:
4830 case VK_SNAPSHOT:
4831 case VK_INSERT:
4832 case VK_DELETE:
4833 case VK_APPS: // Context Menu
4834 case VK_SLEEP:
4835 case VK_NUMLOCK:
4836 case VK_SCROLL: // SCROLL LOCK
4837 case VK_ATTN: // Attension key of IBM midrange computers, e.g., AS/400
4838 case VK_CRSEL: // Cursor Selection
4839 case VK_EXSEL: // Extend Selection
4840 case VK_EREOF: // Erase EOF key of IBM 3270 keyboard layout
4841 case VK_PLAY:
4842 case VK_ZOOM:
4843 case VK_PA1: // PA1 key of IBM 3270 keyboard layout
4844 return uint32_t(aNativeKeyCode);
4846 case VK_HELP:
4847 return NS_VK_HELP;
4849 // Windows key should be mapped to a Win keycode
4850 // They should be able to be distinguished by DOM3 KeyboardEvent.location
4851 case VK_LWIN:
4852 case VK_RWIN:
4853 return NS_VK_WIN;
4855 case VK_VOLUME_MUTE:
4856 return NS_VK_VOLUME_MUTE;
4857 case VK_VOLUME_DOWN:
4858 return NS_VK_VOLUME_DOWN;
4859 case VK_VOLUME_UP:
4860 return NS_VK_VOLUME_UP;
4862 case VK_LSHIFT:
4863 case VK_RSHIFT:
4864 return NS_VK_SHIFT;
4866 case VK_LCONTROL:
4867 case VK_RCONTROL:
4868 return NS_VK_CONTROL;
4870 // Note that even if the key is AltGr, we should return NS_VK_ALT for
4871 // compatibility with both older Gecko and the other browsers.
4872 case VK_LMENU:
4873 case VK_RMENU:
4874 return NS_VK_ALT;
4876 // Following keycodes are not defined in our DOM keycodes.
4877 case VK_BROWSER_BACK:
4878 case VK_BROWSER_FORWARD:
4879 case VK_BROWSER_REFRESH:
4880 case VK_BROWSER_STOP:
4881 case VK_BROWSER_SEARCH:
4882 case VK_BROWSER_FAVORITES:
4883 case VK_BROWSER_HOME:
4884 case VK_MEDIA_NEXT_TRACK:
4885 case VK_MEDIA_PREV_TRACK:
4886 case VK_MEDIA_STOP:
4887 case VK_MEDIA_PLAY_PAUSE:
4888 case VK_LAUNCH_MAIL:
4889 case VK_LAUNCH_MEDIA_SELECT:
4890 case VK_LAUNCH_APP1:
4891 case VK_LAUNCH_APP2:
4892 return 0;
4894 // Following OEM specific virtual keycodes should pass through DOM keyCode
4895 // for compatibility with the other browsers on Windows.
4897 // Following OEM specific virtual keycodes are defined for Fujitsu/OASYS.
4898 case VK_OEM_FJ_JISHO:
4899 case VK_OEM_FJ_MASSHOU:
4900 case VK_OEM_FJ_TOUROKU:
4901 case VK_OEM_FJ_LOYA:
4902 case VK_OEM_FJ_ROYA:
4903 // Not sure what means "ICO".
4904 case VK_ICO_HELP:
4905 case VK_ICO_00:
4906 case VK_ICO_CLEAR:
4907 // Following OEM specific virtual keycodes are defined for Nokia/Ericsson.
4908 case VK_OEM_RESET:
4909 case VK_OEM_JUMP:
4910 case VK_OEM_PA1:
4911 case VK_OEM_PA2:
4912 case VK_OEM_PA3:
4913 case VK_OEM_WSCTRL:
4914 case VK_OEM_CUSEL:
4915 case VK_OEM_ATTN:
4916 case VK_OEM_FINISH:
4917 case VK_OEM_COPY:
4918 case VK_OEM_AUTO:
4919 case VK_OEM_ENLW:
4920 case VK_OEM_BACKTAB:
4921 // VK_OEM_CLEAR is defined as not OEM specific, but let's pass though
4922 // DOM keyCode like other OEM specific virtual keycodes.
4923 case VK_OEM_CLEAR:
4924 return uint32_t(aNativeKeyCode);
4926 // 0xE1 is an OEM specific virtual keycode. However, the value is already
4927 // used in our DOM keyCode for AltGr on Linux. So, this virtual keycode
4928 // cannot pass through DOM keyCode.
4929 case 0xE1:
4930 return 0;
4932 // Following keycodes are OEM keys which are keycodes for non-alphabet and
4933 // non-numeric keys, we should compute each keycode of them from unshifted
4934 // character which is inputted by each key. But if the unshifted character
4935 // is not an ASCII character but shifted character is an ASCII character,
4936 // we should refer it.
4937 case VK_OEM_1:
4938 case VK_OEM_PLUS:
4939 case VK_OEM_COMMA:
4940 case VK_OEM_MINUS:
4941 case VK_OEM_PERIOD:
4942 case VK_OEM_2:
4943 case VK_OEM_3:
4944 case VK_OEM_4:
4945 case VK_OEM_5:
4946 case VK_OEM_6:
4947 case VK_OEM_7:
4948 case VK_OEM_8:
4949 case VK_OEM_102:
4950 case VK_ABNT_C1: {
4951 NS_ASSERTION(IsPrintableCharKey(aNativeKeyCode),
4952 "The key must be printable");
4953 ModifierKeyState modKeyState(0);
4954 UniCharsAndModifiers uniChars =
4955 GetUniCharsAndModifiers(aNativeKeyCode, modKeyState);
4956 if (uniChars.Length() != 1 || uniChars.CharAt(0) < ' ' ||
4957 uniChars.CharAt(0) > 0x7F) {
4958 modKeyState.Set(MODIFIER_SHIFT);
4959 uniChars = GetUniCharsAndModifiers(aNativeKeyCode, modKeyState);
4960 if (uniChars.Length() != 1 || uniChars.CharAt(0) < ' ' ||
4961 uniChars.CharAt(0) > 0x7F) {
4962 // In this case, we've returned 0 in this case for long time because
4963 // we decided that we should avoid setting same keyCode value to 2 or
4964 // more keys since active keyboard layout may have a key to input the
4965 // punctuation with different key. However, setting keyCode to 0
4966 // makes some web applications which are aware of neither
4967 // KeyboardEvent.key nor KeyboardEvent.code not work with Firefox
4968 // when user selects non-ASCII capable keyboard layout such as
4969 // Russian and Thai layout. So, let's decide keyCode value with
4970 // major keyboard layout's key which causes the OEM keycode.
4971 // Actually, this maps same keyCode value to 2 keys on Russian
4972 // keyboard layout. "Period" key causes VK_OEM_PERIOD but inputs
4973 // Yu of Cyrillic and "Slash" key causes VK_OEM_2 (same as US
4974 // keyboard layout) but inputs "." (period of ASCII). Therefore,
4975 // we return DOM_VK_PERIOD which is same as VK_OEM_PERIOD for
4976 // "Period" key. On the other hand, we use same keyCode value for
4977 // "Slash" key too because it inputs ".".
4978 CodeNameIndex code;
4979 switch (aNativeKeyCode) {
4980 case VK_OEM_1:
4981 code = CODE_NAME_INDEX_Semicolon;
4982 break;
4983 case VK_OEM_PLUS:
4984 code = CODE_NAME_INDEX_Equal;
4985 break;
4986 case VK_OEM_COMMA:
4987 code = CODE_NAME_INDEX_Comma;
4988 break;
4989 case VK_OEM_MINUS:
4990 code = CODE_NAME_INDEX_Minus;
4991 break;
4992 case VK_OEM_PERIOD:
4993 code = CODE_NAME_INDEX_Period;
4994 break;
4995 case VK_OEM_2:
4996 code = CODE_NAME_INDEX_Slash;
4997 break;
4998 case VK_OEM_3:
4999 code = CODE_NAME_INDEX_Backquote;
5000 break;
5001 case VK_OEM_4:
5002 code = CODE_NAME_INDEX_BracketLeft;
5003 break;
5004 case VK_OEM_5:
5005 code = CODE_NAME_INDEX_Backslash;
5006 break;
5007 case VK_OEM_6:
5008 code = CODE_NAME_INDEX_BracketRight;
5009 break;
5010 case VK_OEM_7:
5011 code = CODE_NAME_INDEX_Quote;
5012 break;
5013 case VK_OEM_8:
5014 // Use keyCode value for "Backquote" key on UK keyboard layout.
5015 code = CODE_NAME_INDEX_Backquote;
5016 break;
5017 case VK_OEM_102:
5018 // Use keyCode value for "IntlBackslash" key.
5019 code = CODE_NAME_INDEX_IntlBackslash;
5020 break;
5021 case VK_ABNT_C1: // "/" of ABNT.
5022 // Use keyCode value for "IntlBackslash" key on ABNT keyboard
5023 // layout.
5024 code = CODE_NAME_INDEX_IntlBackslash;
5025 break;
5026 default:
5027 MOZ_ASSERT_UNREACHABLE("Handle all OEM keycode values");
5028 return 0;
5030 return WidgetKeyboardEvent::GetFallbackKeyCodeOfPunctuationKey(code);
5033 return WidgetUtils::ComputeKeyCodeFromChar(uniChars.CharAt(0));
5036 // IE sets 0xC2 to the DOM keyCode for VK_ABNT_C2. However, we're already
5037 // using NS_VK_SEPARATOR for the separator key on Mac and Linux. Therefore,
5038 // We should keep consistency between Gecko on all platforms rather than
5039 // with other browsers since a lot of keyCode values are already different
5040 // between browsers.
5041 case VK_ABNT_C2:
5042 return NS_VK_SEPARATOR;
5044 // VK_PROCESSKEY means IME already consumed the key event.
5045 case VK_PROCESSKEY:
5046 return NS_VK_PROCESSKEY;
5047 // VK_PACKET is generated by SendInput() API, we don't need to
5048 // care this message as key event.
5049 case VK_PACKET:
5050 return 0;
5051 // If a key is not mapped to a virtual keycode, 0xFF is used.
5052 case 0xFF:
5053 NS_WARNING("The key is failed to be converted to a virtual keycode");
5054 return 0;
5056 #ifdef DEBUG
5057 nsPrintfCString warning(
5058 "Unknown virtual keycode (0x%08X), please check the "
5059 "latest MSDN document, there may be some new "
5060 "keycodes we've never known.",
5061 aNativeKeyCode);
5062 NS_WARNING(warning.get());
5063 #endif
5064 return 0;
5067 KeyNameIndex KeyboardLayout::ConvertNativeKeyCodeToKeyNameIndex(
5068 uint8_t aVirtualKey) const {
5069 if (IsPrintableCharKey(aVirtualKey) || aVirtualKey == VK_PACKET) {
5070 return KEY_NAME_INDEX_USE_STRING;
5073 // If the keyboard layout has AltGr and AltRight key is pressed,
5074 // return AltGraph.
5075 if (aVirtualKey == VK_RMENU && HasAltGr()) {
5076 return KEY_NAME_INDEX_AltGraph;
5079 switch (aVirtualKey) {
5080 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5081 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5082 case aNativeKey: \
5083 return aKeyNameIndex;
5085 #include "NativeKeyToDOMKeyName.h"
5087 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5089 default:
5090 break;
5093 HKL layout = GetLayout();
5094 WORD langID = LOWORD(static_cast<HKL>(layout));
5095 WORD primaryLangID = PRIMARYLANGID(langID);
5097 if (primaryLangID == LANG_JAPANESE) {
5098 switch (aVirtualKey) {
5099 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5100 #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, \
5101 aKeyNameIndex) \
5102 case aNativeKey: \
5103 return aKeyNameIndex;
5105 #include "NativeKeyToDOMKeyName.h"
5107 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5109 default:
5110 break;
5112 } else if (primaryLangID == LANG_KOREAN) {
5113 switch (aVirtualKey) {
5114 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5115 #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5116 case aNativeKey: \
5117 return aKeyNameIndex;
5119 #include "NativeKeyToDOMKeyName.h"
5121 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5123 default:
5124 return KEY_NAME_INDEX_Unidentified;
5128 switch (aVirtualKey) {
5129 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5130 #define NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5131 case aNativeKey: \
5132 return aKeyNameIndex;
5134 #include "NativeKeyToDOMKeyName.h"
5136 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5138 default:
5139 return KEY_NAME_INDEX_Unidentified;
5143 // static
5144 CodeNameIndex KeyboardLayout::ConvertScanCodeToCodeNameIndex(UINT aScanCode) {
5145 switch (aScanCode) {
5146 #define NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX(aNativeKey, aCodeNameIndex) \
5147 case aNativeKey: \
5148 return aCodeNameIndex;
5150 #include "NativeKeyToDOMCodeName.h"
5152 #undef NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX
5154 default:
5155 return CODE_NAME_INDEX_UNKNOWN;
5159 nsresult KeyboardLayout::SynthesizeNativeKeyEvent(
5160 nsWindow* aWidget, int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode,
5161 uint32_t aModifierFlags, const nsAString& aCharacters,
5162 const nsAString& aUnmodifiedCharacters) {
5163 UINT keyboardLayoutListCount = ::GetKeyboardLayoutList(0, nullptr);
5164 NS_ASSERTION(keyboardLayoutListCount > 0,
5165 "One keyboard layout must be installed at least");
5166 HKL keyboardLayoutListBuff[50];
5167 HKL* keyboardLayoutList = keyboardLayoutListCount < 50
5168 ? keyboardLayoutListBuff
5169 : new HKL[keyboardLayoutListCount];
5170 keyboardLayoutListCount =
5171 ::GetKeyboardLayoutList(keyboardLayoutListCount, keyboardLayoutList);
5172 NS_ASSERTION(keyboardLayoutListCount > 0,
5173 "Failed to get all keyboard layouts installed on the system");
5175 nsPrintfCString layoutName("%08x", aNativeKeyboardLayout);
5176 HKL loadedLayout = LoadKeyboardLayoutA(layoutName.get(), KLF_NOTELLSHELL);
5177 if (loadedLayout == nullptr) {
5178 if (keyboardLayoutListBuff != keyboardLayoutList) {
5179 delete[] keyboardLayoutList;
5181 return NS_ERROR_NOT_AVAILABLE;
5184 // Setup clean key state and load desired layout
5185 BYTE originalKbdState[256];
5186 ::GetKeyboardState(originalKbdState);
5187 BYTE kbdState[256];
5188 memset(kbdState, 0, sizeof(kbdState));
5189 // This changes the state of the keyboard for the current thread only,
5190 // and we'll restore it soon, so this should be OK.
5191 ::SetKeyboardState(kbdState);
5193 OverrideLayout(loadedLayout);
5195 bool isAltGrKeyPress = false;
5196 if (aModifierFlags & nsIWidget::ALTGRAPH) {
5197 if (!HasAltGr()) {
5198 return NS_ERROR_INVALID_ARG;
5200 // AltGr emulates ControlLeft key press and AltRight key press.
5201 // So, we should remove those flags from aModifierFlags before
5202 // calling WinUtils::SetupKeyModifiersSequence() to create correct
5203 // key sequence.
5204 // FYI: We don't support both ControlLeft and AltRight (AltGr) are
5205 // pressed at the same time unless synthesizing key is
5206 // VK_LCONTROL.
5207 aModifierFlags &= ~(nsIWidget::CTRL_L | nsIWidget::ALT_R);
5210 uint8_t argumentKeySpecific = 0;
5211 switch (aNativeKeyCode & 0xFF) {
5212 case VK_SHIFT:
5213 aModifierFlags &= ~(nsIWidget::SHIFT_L | nsIWidget::SHIFT_R);
5214 argumentKeySpecific = VK_LSHIFT;
5215 break;
5216 case VK_LSHIFT:
5217 aModifierFlags &= ~nsIWidget::SHIFT_L;
5218 argumentKeySpecific = aNativeKeyCode & 0xFF;
5219 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_SHIFT;
5220 break;
5221 case VK_RSHIFT:
5222 aModifierFlags &= ~nsIWidget::SHIFT_R;
5223 argumentKeySpecific = aNativeKeyCode & 0xFF;
5224 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_SHIFT;
5225 break;
5226 case VK_CONTROL:
5227 aModifierFlags &= ~(nsIWidget::CTRL_L | nsIWidget::CTRL_R);
5228 argumentKeySpecific = VK_LCONTROL;
5229 break;
5230 case VK_LCONTROL:
5231 aModifierFlags &= ~nsIWidget::CTRL_L;
5232 argumentKeySpecific = aNativeKeyCode & 0xFF;
5233 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_CONTROL;
5234 break;
5235 case VK_RCONTROL:
5236 aModifierFlags &= ~nsIWidget::CTRL_R;
5237 argumentKeySpecific = aNativeKeyCode & 0xFF;
5238 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_CONTROL;
5239 break;
5240 case VK_MENU:
5241 aModifierFlags &= ~(nsIWidget::ALT_L | nsIWidget::ALT_R);
5242 argumentKeySpecific = VK_LMENU;
5243 break;
5244 case VK_LMENU:
5245 aModifierFlags &= ~nsIWidget::ALT_L;
5246 argumentKeySpecific = aNativeKeyCode & 0xFF;
5247 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_MENU;
5248 break;
5249 case VK_RMENU:
5250 aModifierFlags &= ~(nsIWidget::ALT_R | nsIWidget::ALTGRAPH);
5251 argumentKeySpecific = aNativeKeyCode & 0xFF;
5252 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_MENU;
5253 // If AltRight key is AltGr in the keyboard layout, let's use
5254 // SetupKeyModifiersSequence() to emulate the native behavior
5255 // since the same event order between keydown and keyup makes
5256 // the following code complicated.
5257 if (HasAltGr()) {
5258 isAltGrKeyPress = true;
5259 aModifierFlags &= ~nsIWidget::CTRL_L;
5260 aModifierFlags |= nsIWidget::ALTGRAPH;
5262 break;
5263 case VK_CAPITAL:
5264 aModifierFlags &= ~nsIWidget::CAPS_LOCK;
5265 argumentKeySpecific = VK_CAPITAL;
5266 break;
5267 case VK_NUMLOCK:
5268 aModifierFlags &= ~nsIWidget::NUM_LOCK;
5269 argumentKeySpecific = VK_NUMLOCK;
5270 break;
5273 AutoTArray<KeyPair, 10> keySequence;
5274 WinUtils::SetupKeyModifiersSequence(&keySequence, aModifierFlags, WM_KEYDOWN);
5275 if (!isAltGrKeyPress) {
5276 keySequence.AppendElement(KeyPair(aNativeKeyCode, argumentKeySpecific));
5279 // Simulate the pressing of each modifier key and then the real key
5280 // FYI: Each NativeKey instance here doesn't need to override keyboard layout
5281 // since this method overrides and restores the keyboard layout.
5282 for (uint32_t i = 0; i < keySequence.Length(); ++i) {
5283 uint8_t key = keySequence[i].mGeneral;
5284 uint8_t keySpecific = keySequence[i].mSpecific;
5285 uint16_t scanCode = keySequence[i].mScanCode;
5286 kbdState[key] = 0x81; // key is down and toggled on if appropriate
5287 if (keySpecific) {
5288 kbdState[keySpecific] = 0x81;
5290 ::SetKeyboardState(kbdState);
5291 ModifierKeyState modKeyState;
5292 // If scan code isn't specified explicitly, let's compute it with current
5293 // keyboard layout.
5294 if (!scanCode) {
5295 scanCode =
5296 ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key);
5298 LPARAM lParam = static_cast<LPARAM>(scanCode << 16);
5299 // If the scan code is for an extended key, set extended key flag.
5300 if ((scanCode & 0xFF00) == 0xE000) {
5301 lParam |= 0x1000000;
5303 // When AltGr key is pressed, both ControlLeft and AltRight cause
5304 // WM_KEYDOWN messages.
5305 bool makeSysKeyMsg =
5306 !(aModifierFlags & nsIWidget::ALTGRAPH) && IsSysKey(key, modKeyState);
5307 MSG keyDownMsg =
5308 WinUtils::InitMSG(makeSysKeyMsg ? WM_SYSKEYDOWN : WM_KEYDOWN, key,
5309 lParam, aWidget->GetWindowHandle());
5310 if (i == keySequence.Length() - 1) {
5311 bool makeDeadCharMsg =
5312 (IsDeadKey(key, modKeyState) && aCharacters.IsEmpty());
5313 nsAutoString chars(aCharacters);
5314 if (makeDeadCharMsg) {
5315 UniCharsAndModifiers deadChars =
5316 GetUniCharsAndModifiers(key, modKeyState);
5317 chars = deadChars.ToString();
5318 NS_ASSERTION(chars.Length() == 1,
5319 "Dead char must be only one character");
5321 if (chars.IsEmpty()) {
5322 NativeKey nativeKey(aWidget, keyDownMsg, modKeyState);
5323 nativeKey.HandleKeyDownMessage();
5324 } else {
5325 AutoTArray<NativeKey::FakeCharMsg, 10> fakeCharMsgs;
5326 for (uint32_t j = 0; j < chars.Length(); j++) {
5327 NativeKey::FakeCharMsg* fakeCharMsg = fakeCharMsgs.AppendElement();
5328 fakeCharMsg->mCharCode = chars.CharAt(j);
5329 fakeCharMsg->mScanCode = scanCode;
5330 fakeCharMsg->mIsSysKey = makeSysKeyMsg;
5331 fakeCharMsg->mIsDeadKey = makeDeadCharMsg;
5333 NativeKey nativeKey(aWidget, keyDownMsg, modKeyState, 0, &fakeCharMsgs);
5334 bool dispatched;
5335 nativeKey.HandleKeyDownMessage(&dispatched);
5336 // If some char messages are not consumed, let's emulate the widget
5337 // receiving the message directly.
5338 for (uint32_t j = 1; j < fakeCharMsgs.Length(); j++) {
5339 if (fakeCharMsgs[j].mConsumed) {
5340 continue;
5342 MSG charMsg = fakeCharMsgs[j].GetCharMsg(aWidget->GetWindowHandle());
5343 NativeKey nativeKey(aWidget, charMsg, modKeyState);
5344 nativeKey.HandleCharMessage(charMsg);
5347 } else {
5348 NativeKey nativeKey(aWidget, keyDownMsg, modKeyState);
5349 nativeKey.HandleKeyDownMessage();
5353 keySequence.Clear();
5354 if (!isAltGrKeyPress) {
5355 keySequence.AppendElement(KeyPair(aNativeKeyCode, argumentKeySpecific));
5357 WinUtils::SetupKeyModifiersSequence(&keySequence, aModifierFlags, WM_KEYUP);
5358 for (uint32_t i = 0; i < keySequence.Length(); ++i) {
5359 uint8_t key = keySequence[i].mGeneral;
5360 uint8_t keySpecific = keySequence[i].mSpecific;
5361 uint16_t scanCode = keySequence[i].mScanCode;
5362 kbdState[key] = 0; // key is up and toggled off if appropriate
5363 if (keySpecific) {
5364 kbdState[keySpecific] = 0;
5366 ::SetKeyboardState(kbdState);
5367 ModifierKeyState modKeyState;
5368 // If scan code isn't specified explicitly, let's compute it with current
5369 // keyboard layout.
5370 if (!scanCode) {
5371 scanCode =
5372 ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key);
5374 LPARAM lParam = static_cast<LPARAM>(scanCode << 16);
5375 // If the scan code is for an extended key, set extended key flag.
5376 if ((scanCode & 0xFF00) == 0xE000) {
5377 lParam |= 0x1000000;
5379 // Don't use WM_SYSKEYUP for Alt keyup.
5380 // NOTE: When AltGr was pressed, ControlLeft causes WM_SYSKEYUP normally.
5381 bool makeSysKeyMsg = IsSysKey(key, modKeyState) && key != VK_MENU;
5382 MSG keyUpMsg = WinUtils::InitMSG(makeSysKeyMsg ? WM_SYSKEYUP : WM_KEYUP,
5383 key, lParam, aWidget->GetWindowHandle());
5384 NativeKey nativeKey(aWidget, keyUpMsg, modKeyState);
5385 nativeKey.HandleKeyUpMessage();
5388 // Restore old key state and layout
5389 ::SetKeyboardState(originalKbdState);
5390 RestoreLayout();
5392 // Don't unload the layout if it's installed actually.
5393 for (uint32_t i = 0; i < keyboardLayoutListCount; i++) {
5394 if (keyboardLayoutList[i] == loadedLayout) {
5395 loadedLayout = 0;
5396 break;
5399 if (keyboardLayoutListBuff != keyboardLayoutList) {
5400 delete[] keyboardLayoutList;
5402 if (loadedLayout) {
5403 ::UnloadKeyboardLayout(loadedLayout);
5405 return NS_OK;
5408 /*****************************************************************************
5409 * mozilla::widget::DeadKeyTable
5410 *****************************************************************************/
5412 char16_t DeadKeyTable::GetCompositeChar(char16_t aBaseChar) const {
5413 // Dead-key table is sorted by BaseChar in ascending order.
5414 // Usually they are too small to use binary search.
5416 for (uint32_t index = 0; index < mEntries; index++) {
5417 if (mTable[index].BaseChar == aBaseChar) {
5418 return mTable[index].CompositeChar;
5420 if (mTable[index].BaseChar > aBaseChar) {
5421 break;
5425 return 0;
5428 /*****************************************************************************
5429 * mozilla::widget::RedirectedKeyDownMessage
5430 *****************************************************************************/
5432 MSG RedirectedKeyDownMessageManager::sRedirectedKeyDownMsg;
5433 bool RedirectedKeyDownMessageManager::sDefaultPreventedOfRedirectedMsg = false;
5435 // static
5436 bool RedirectedKeyDownMessageManager::IsRedirectedMessage(const MSG& aMsg) {
5437 return (aMsg.message == WM_KEYDOWN || aMsg.message == WM_SYSKEYDOWN) &&
5438 (sRedirectedKeyDownMsg.message == aMsg.message &&
5439 WinUtils::GetScanCode(sRedirectedKeyDownMsg.lParam) ==
5440 WinUtils::GetScanCode(aMsg.lParam));
5443 // static
5444 void RedirectedKeyDownMessageManager::RemoveNextCharMessage(HWND aWnd) {
5445 MSG msg;
5446 if (WinUtils::PeekMessage(&msg, aWnd, WM_KEYFIRST, WM_KEYLAST,
5447 PM_NOREMOVE | PM_NOYIELD) &&
5448 (msg.message == WM_CHAR || msg.message == WM_SYSCHAR)) {
5449 WinUtils::PeekMessage(&msg, aWnd, msg.message, msg.message,
5450 PM_REMOVE | PM_NOYIELD);
5454 } // namespace widget
5455 } // namespace mozilla