Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / widget / windows / KeyboardLayout.cpp
blob88445add25c9320d71c9351fcbf0c32738e86f52
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/Logging.h"
8 #include "mozilla/ArrayUtils.h"
9 #include "mozilla/AutoRestore.h"
10 #include "mozilla/MouseEvents.h"
11 #include "mozilla/MiscEvents.h"
12 #include "mozilla/StaticPrefs_ui.h"
13 #include "mozilla/TextEvents.h"
14 #include "mozilla/widget/WinRegistry.h"
16 #include "nsExceptionHandler.h"
17 #include "nsGkAtoms.h"
18 #include "nsIUserIdleServiceInternal.h"
19 #include "nsIWindowsRegKey.h"
20 #include "nsPrintfCString.h"
21 #include "nsReadableUtils.h"
22 #include "nsServiceManagerUtils.h"
23 #include "nsTArray.h"
24 #include "nsUnicharUtils.h"
25 #include "nsWindowDbg.h"
27 #include "KeyboardLayout.h"
28 #include "WidgetUtils.h"
29 #include "WinUtils.h"
31 #include "npapi.h"
33 #include <windows.h>
34 #include <winnls.h>
35 #include <winuser.h>
36 #include <algorithm>
38 #ifndef WINABLEAPI
39 # include <winable.h>
40 #endif
42 // For collecting other people's log, tell them `MOZ_LOG=KeyboardHandler:4,sync`
43 // rather than `MOZ_LOG=KeyboardHandler:5,sync` since using `5` may create too
44 // big file.
45 // Therefore you shouldn't use `LogLevel::Verbose` for logging usual behavior.
46 mozilla::LazyLogModule gKeyLog("KeyboardHandler");
48 namespace mozilla {
49 namespace widget {
51 static const char* const kVirtualKeyName[] = {
52 "NULL",
53 "VK_LBUTTON",
54 "VK_RBUTTON",
55 "VK_CANCEL",
56 "VK_MBUTTON",
57 "VK_XBUTTON1",
58 "VK_XBUTTON2",
59 "0x07",
60 "VK_BACK",
61 "VK_TAB",
62 "0x0A",
63 "0x0B",
64 "VK_CLEAR",
65 "VK_RETURN",
66 "0x0E",
67 "0x0F",
69 "VK_SHIFT",
70 "VK_CONTROL",
71 "VK_MENU",
72 "VK_PAUSE",
73 "VK_CAPITAL",
74 "VK_KANA, VK_HANGUL",
75 "0x16",
76 "VK_JUNJA",
77 "VK_FINAL",
78 "VK_HANJA, VK_KANJI",
79 "0x1A",
80 "VK_ESCAPE",
81 "VK_CONVERT",
82 "VK_NONCONVERT",
83 "VK_ACCEPT",
84 "VK_MODECHANGE",
86 "VK_SPACE",
87 "VK_PRIOR",
88 "VK_NEXT",
89 "VK_END",
90 "VK_HOME",
91 "VK_LEFT",
92 "VK_UP",
93 "VK_RIGHT",
94 "VK_DOWN",
95 "VK_SELECT",
96 "VK_PRINT",
97 "VK_EXECUTE",
98 "VK_SNAPSHOT",
99 "VK_INSERT",
100 "VK_DELETE",
101 "VK_HELP",
103 "VK_0",
104 "VK_1",
105 "VK_2",
106 "VK_3",
107 "VK_4",
108 "VK_5",
109 "VK_6",
110 "VK_7",
111 "VK_8",
112 "VK_9",
113 "0x3A",
114 "0x3B",
115 "0x3C",
116 "0x3D",
117 "0x3E",
118 "0x3F",
120 "0x40",
121 "VK_A",
122 "VK_B",
123 "VK_C",
124 "VK_D",
125 "VK_E",
126 "VK_F",
127 "VK_G",
128 "VK_H",
129 "VK_I",
130 "VK_J",
131 "VK_K",
132 "VK_L",
133 "VK_M",
134 "VK_N",
135 "VK_O",
137 "VK_P",
138 "VK_Q",
139 "VK_R",
140 "VK_S",
141 "VK_T",
142 "VK_U",
143 "VK_V",
144 "VK_W",
145 "VK_X",
146 "VK_Y",
147 "VK_Z",
148 "VK_LWIN",
149 "VK_RWIN",
150 "VK_APPS",
151 "0x5E",
152 "VK_SLEEP",
154 "VK_NUMPAD0",
155 "VK_NUMPAD1",
156 "VK_NUMPAD2",
157 "VK_NUMPAD3",
158 "VK_NUMPAD4",
159 "VK_NUMPAD5",
160 "VK_NUMPAD6",
161 "VK_NUMPAD7",
162 "VK_NUMPAD8",
163 "VK_NUMPAD9",
164 "VK_MULTIPLY",
165 "VK_ADD",
166 "VK_SEPARATOR",
167 "VK_SUBTRACT",
168 "VK_DECIMAL",
169 "VK_DIVIDE",
171 "VK_F1",
172 "VK_F2",
173 "VK_F3",
174 "VK_F4",
175 "VK_F5",
176 "VK_F6",
177 "VK_F7",
178 "VK_F8",
179 "VK_F9",
180 "VK_F10",
181 "VK_F11",
182 "VK_F12",
183 "VK_F13",
184 "VK_F14",
185 "VK_F15",
186 "VK_F16",
188 "VK_F17",
189 "VK_F18",
190 "VK_F19",
191 "VK_F20",
192 "VK_F21",
193 "VK_F22",
194 "VK_F23",
195 "VK_F24",
196 "0x88",
197 "0x89",
198 "0x8A",
199 "0x8B",
200 "0x8C",
201 "0x8D",
202 "0x8E",
203 "0x8F",
205 "VK_NUMLOCK",
206 "VK_SCROLL",
207 "VK_OEM_NEC_EQUAL, VK_OEM_FJ_JISHO",
208 "VK_OEM_FJ_MASSHOU",
209 "VK_OEM_FJ_TOUROKU",
210 "VK_OEM_FJ_LOYA",
211 "VK_OEM_FJ_ROYA",
212 "0x97",
213 "0x98",
214 "0x99",
215 "0x9A",
216 "0x9B",
217 "0x9C",
218 "0x9D",
219 "0x9E",
220 "0x9F",
222 "VK_LSHIFT",
223 "VK_RSHIFT",
224 "VK_LCONTROL",
225 "VK_RCONTROL",
226 "VK_LMENU",
227 "VK_RMENU",
228 "VK_BROWSER_BACK",
229 "VK_BROWSER_FORWARD",
230 "VK_BROWSER_REFRESH",
231 "VK_BROWSER_STOP",
232 "VK_BROWSER_SEARCH",
233 "VK_BROWSER_FAVORITES",
234 "VK_BROWSER_HOME",
235 "VK_VOLUME_MUTE",
236 "VK_VOLUME_DOWN",
237 "VK_VOLUME_UP",
239 "VK_MEDIA_NEXT_TRACK",
240 "VK_MEDIA_PREV_TRACK",
241 "VK_MEDIA_STOP",
242 "VK_MEDIA_PLAY_PAUSE",
243 "VK_LAUNCH_MAIL",
244 "VK_LAUNCH_MEDIA_SELECT",
245 "VK_LAUNCH_APP1",
246 "VK_LAUNCH_APP2",
247 "0xB8",
248 "0xB9",
249 "VK_OEM_1",
250 "VK_OEM_PLUS",
251 "VK_OEM_COMMA",
252 "VK_OEM_MINUS",
253 "VK_OEM_PERIOD",
254 "VK_OEM_2",
256 "VK_OEM_3",
257 "VK_ABNT_C1",
258 "VK_ABNT_C2",
259 "0xC3",
260 "0xC4",
261 "0xC5",
262 "0xC6",
263 "0xC7",
264 "0xC8",
265 "0xC9",
266 "0xCA",
267 "0xCB",
268 "0xCC",
269 "0xCD",
270 "0xCE",
271 "0xCF",
273 "0xD0",
274 "0xD1",
275 "0xD2",
276 "0xD3",
277 "0xD4",
278 "0xD5",
279 "0xD6",
280 "0xD7",
281 "0xD8",
282 "0xD9",
283 "0xDA",
284 "VK_OEM_4",
285 "VK_OEM_5",
286 "VK_OEM_6",
287 "VK_OEM_7",
288 "VK_OEM_8",
290 "0xE0",
291 "VK_OEM_AX",
292 "VK_OEM_102",
293 "VK_ICO_HELP",
294 "VK_ICO_00",
295 "VK_PROCESSKEY",
296 "VK_ICO_CLEAR",
297 "VK_PACKET",
298 "0xE8",
299 "VK_OEM_RESET",
300 "VK_OEM_JUMP",
301 "VK_OEM_PA1",
302 "VK_OEM_PA2",
303 "VK_OEM_PA3",
304 "VK_OEM_WSCTRL",
305 "VK_OEM_CUSEL",
307 "VK_OEM_ATTN",
308 "VK_OEM_FINISH",
309 "VK_OEM_COPY",
310 "VK_OEM_AUTO",
311 "VK_OEM_ENLW",
312 "VK_OEM_BACKTAB",
313 "VK_ATTN",
314 "VK_CRSEL",
315 "VK_EXSEL",
316 "VK_EREOF",
317 "VK_PLAY",
318 "VK_ZOOM",
319 "VK_NONAME",
320 "VK_PA1",
321 "VK_OEM_CLEAR",
322 "0xFF"};
324 static_assert(sizeof(kVirtualKeyName) / sizeof(const char*) == 0x100,
325 "The virtual key name must be defined just 256 keys");
327 static const char* GetBoolName(bool aBool) { return aBool ? "true" : "false"; }
329 static const nsCString GetCharacterCodeName(WPARAM aCharCode) {
330 switch (aCharCode) {
331 case 0x0000:
332 return "NULL (0x0000)"_ns;
333 case 0x0008:
334 return "BACKSPACE (0x0008)"_ns;
335 case 0x0009:
336 return "CHARACTER TABULATION (0x0009)"_ns;
337 case 0x000A:
338 return "LINE FEED (0x000A)"_ns;
339 case 0x000B:
340 return "LINE TABULATION (0x000B)"_ns;
341 case 0x000C:
342 return "FORM FEED (0x000C)"_ns;
343 case 0x000D:
344 return "CARRIAGE RETURN (0x000D)"_ns;
345 case 0x0018:
346 return "CANCEL (0x0018)"_ns;
347 case 0x001B:
348 return "ESCAPE (0x001B)"_ns;
349 case 0x0020:
350 return "SPACE (0x0020)"_ns;
351 case 0x007F:
352 return "DELETE (0x007F)"_ns;
353 case 0x00A0:
354 return "NO-BREAK SPACE (0x00A0)"_ns;
355 case 0x00AD:
356 return "SOFT HYPHEN (0x00AD)"_ns;
357 case 0x2000:
358 return "EN QUAD (0x2000)"_ns;
359 case 0x2001:
360 return "EM QUAD (0x2001)"_ns;
361 case 0x2002:
362 return "EN SPACE (0x2002)"_ns;
363 case 0x2003:
364 return "EM SPACE (0x2003)"_ns;
365 case 0x2004:
366 return "THREE-PER-EM SPACE (0x2004)"_ns;
367 case 0x2005:
368 return "FOUR-PER-EM SPACE (0x2005)"_ns;
369 case 0x2006:
370 return "SIX-PER-EM SPACE (0x2006)"_ns;
371 case 0x2007:
372 return "FIGURE SPACE (0x2007)"_ns;
373 case 0x2008:
374 return "PUNCTUATION SPACE (0x2008)"_ns;
375 case 0x2009:
376 return "THIN SPACE (0x2009)"_ns;
377 case 0x200A:
378 return "HAIR SPACE (0x200A)"_ns;
379 case 0x200B:
380 return "ZERO WIDTH SPACE (0x200B)"_ns;
381 case 0x200C:
382 return "ZERO WIDTH NON-JOINER (0x200C)"_ns;
383 case 0x200D:
384 return "ZERO WIDTH JOINER (0x200D)"_ns;
385 case 0x200E:
386 return "LEFT-TO-RIGHT MARK (0x200E)"_ns;
387 case 0x200F:
388 return "RIGHT-TO-LEFT MARK (0x200F)"_ns;
389 case 0x2029:
390 return "PARAGRAPH SEPARATOR (0x2029)"_ns;
391 case 0x202A:
392 return "LEFT-TO-RIGHT EMBEDDING (0x202A)"_ns;
393 case 0x202B:
394 return "RIGHT-TO-LEFT EMBEDDING (0x202B)"_ns;
395 case 0x202D:
396 return "LEFT-TO-RIGHT OVERRIDE (0x202D)"_ns;
397 case 0x202E:
398 return "RIGHT-TO-LEFT OVERRIDE (0x202E)"_ns;
399 case 0x202F:
400 return "NARROW NO-BREAK SPACE (0x202F)"_ns;
401 case 0x205F:
402 return "MEDIUM MATHEMATICAL SPACE (0x205F)"_ns;
403 case 0x2060:
404 return "WORD JOINER (0x2060)"_ns;
405 case 0x2066:
406 return "LEFT-TO-RIGHT ISOLATE (0x2066)"_ns;
407 case 0x2067:
408 return "RIGHT-TO-LEFT ISOLATE (0x2067)"_ns;
409 case 0x3000:
410 return "IDEOGRAPHIC SPACE (0x3000)"_ns;
411 case 0xFEFF:
412 return "ZERO WIDTH NO-BREAK SPACE (0xFEFF)"_ns;
413 default: {
414 if (aCharCode < ' ' || (aCharCode >= 0x80 && aCharCode < 0xA0)) {
415 return nsPrintfCString("control (0x%04zX)", aCharCode);
417 if (NS_IS_HIGH_SURROGATE(aCharCode)) {
418 return nsPrintfCString("high surrogate (0x%04zX)", aCharCode);
420 if (NS_IS_LOW_SURROGATE(aCharCode)) {
421 return nsPrintfCString("low surrogate (0x%04zX)", aCharCode);
423 return IS_IN_BMP(aCharCode)
424 ? nsPrintfCString(
425 "'%s' (0x%04zX)",
426 NS_ConvertUTF16toUTF8(nsAutoString(aCharCode)).get(),
427 aCharCode)
428 : nsPrintfCString(
429 "'%s' (0x%08zX)",
430 NS_ConvertUTF16toUTF8(nsAutoString(aCharCode)).get(),
431 aCharCode);
436 static const nsCString GetKeyLocationName(uint32_t aLocation) {
437 switch (aLocation) {
438 case eKeyLocationLeft:
439 return "KEY_LOCATION_LEFT"_ns;
440 case eKeyLocationRight:
441 return "KEY_LOCATION_RIGHT"_ns;
442 case eKeyLocationStandard:
443 return "KEY_LOCATION_STANDARD"_ns;
444 case eKeyLocationNumpad:
445 return "KEY_LOCATION_NUMPAD"_ns;
446 default:
447 return nsPrintfCString("Unknown (0x%04X)", aLocation);
451 static const nsCString GetCharacterCodeNames(const char16_t* aChars,
452 uint32_t aLength) {
453 if (!aLength) {
454 return ""_ns;
456 nsCString result;
457 result.AssignLiteral("\"");
458 StringJoinAppend(result, ", "_ns, Span{aChars, aLength},
459 [](nsACString& dest, const char16_t charValue) {
460 dest.Append(GetCharacterCodeName(charValue));
462 result.AppendLiteral("\"");
463 return result;
466 static const nsCString GetCharacterCodeNames(
467 const UniCharsAndModifiers& aUniCharsAndModifiers) {
468 if (aUniCharsAndModifiers.IsEmpty()) {
469 return ""_ns;
471 nsCString result;
472 result.AssignLiteral("\"");
473 StringJoinAppend(result, ", "_ns, Span{aUniCharsAndModifiers.ToString()},
474 [](nsACString& dest, const char16_t charValue) {
475 dest.Append(GetCharacterCodeName(charValue));
477 result.AppendLiteral("\"");
478 return result;
481 class MOZ_STACK_CLASS GetShiftStateName final : public nsAutoCString {
482 public:
483 explicit GetShiftStateName(VirtualKey::ShiftState aShiftState) {
484 if (!aShiftState) {
485 AssignLiteral("none");
486 return;
488 if (aShiftState & VirtualKey::STATE_SHIFT) {
489 AssignLiteral("Shift");
490 aShiftState &= ~VirtualKey::STATE_SHIFT;
492 if (aShiftState & VirtualKey::STATE_CONTROL) {
493 MaybeAppendSeparator();
494 AssignLiteral("Ctrl");
495 aShiftState &= ~VirtualKey::STATE_CONTROL;
497 if (aShiftState & VirtualKey::STATE_ALT) {
498 MaybeAppendSeparator();
499 AssignLiteral("Alt");
500 aShiftState &= ~VirtualKey::STATE_ALT;
502 if (aShiftState & VirtualKey::STATE_CAPSLOCK) {
503 MaybeAppendSeparator();
504 AssignLiteral("CapsLock");
505 aShiftState &= ~VirtualKey::STATE_CAPSLOCK;
507 MOZ_ASSERT(!aShiftState);
510 private:
511 void MaybeAppendSeparator() {
512 if (!IsEmpty()) {
513 AppendLiteral(" | ");
518 static const nsCString GetMessageName(UINT aMessage) {
519 switch (aMessage) {
520 case WM_NULL:
521 return "WM_NULL"_ns;
522 case WM_KEYDOWN:
523 return "WM_KEYDOWN"_ns;
524 case WM_KEYUP:
525 return "WM_KEYUP"_ns;
526 case WM_SYSKEYDOWN:
527 return "WM_SYSKEYDOWN"_ns;
528 case WM_SYSKEYUP:
529 return "WM_SYSKEYUP"_ns;
530 case WM_CHAR:
531 return "WM_CHAR"_ns;
532 case WM_UNICHAR:
533 return "WM_UNICHAR"_ns;
534 case WM_SYSCHAR:
535 return "WM_SYSCHAR"_ns;
536 case WM_DEADCHAR:
537 return "WM_DEADCHAR"_ns;
538 case WM_SYSDEADCHAR:
539 return "WM_SYSDEADCHAR"_ns;
540 case WM_APPCOMMAND:
541 return "WM_APPCOMMAND"_ns;
542 case WM_QUIT:
543 return "WM_QUIT"_ns;
544 default:
545 return nsPrintfCString("Unknown Message (0x%04X)", aMessage);
549 static const nsCString GetVirtualKeyCodeName(WPARAM aVK) {
550 if (aVK >= std::size(kVirtualKeyName)) {
551 return nsPrintfCString("Invalid (0x%08zX)", aVK);
553 return nsCString(kVirtualKeyName[aVK]);
556 static const nsCString GetAppCommandName(WPARAM aCommand) {
557 switch (aCommand) {
558 case APPCOMMAND_BASS_BOOST:
559 return "APPCOMMAND_BASS_BOOST"_ns;
560 case APPCOMMAND_BASS_DOWN:
561 return "APPCOMMAND_BASS_DOWN"_ns;
562 case APPCOMMAND_BASS_UP:
563 return "APPCOMMAND_BASS_UP"_ns;
564 case APPCOMMAND_BROWSER_BACKWARD:
565 return "APPCOMMAND_BROWSER_BACKWARD"_ns;
566 case APPCOMMAND_BROWSER_FAVORITES:
567 return "APPCOMMAND_BROWSER_FAVORITES"_ns;
568 case APPCOMMAND_BROWSER_FORWARD:
569 return "APPCOMMAND_BROWSER_FORWARD"_ns;
570 case APPCOMMAND_BROWSER_HOME:
571 return "APPCOMMAND_BROWSER_HOME"_ns;
572 case APPCOMMAND_BROWSER_REFRESH:
573 return "APPCOMMAND_BROWSER_REFRESH"_ns;
574 case APPCOMMAND_BROWSER_SEARCH:
575 return "APPCOMMAND_BROWSER_SEARCH"_ns;
576 case APPCOMMAND_BROWSER_STOP:
577 return "APPCOMMAND_BROWSER_STOP"_ns;
578 case APPCOMMAND_CLOSE:
579 return "APPCOMMAND_CLOSE"_ns;
580 case APPCOMMAND_COPY:
581 return "APPCOMMAND_COPY"_ns;
582 case APPCOMMAND_CORRECTION_LIST:
583 return "APPCOMMAND_CORRECTION_LIST"_ns;
584 case APPCOMMAND_CUT:
585 return "APPCOMMAND_CUT"_ns;
586 case APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE:
587 return "APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE"_ns;
588 case APPCOMMAND_FIND:
589 return "APPCOMMAND_FIND"_ns;
590 case APPCOMMAND_FORWARD_MAIL:
591 return "APPCOMMAND_FORWARD_MAIL"_ns;
592 case APPCOMMAND_HELP:
593 return "APPCOMMAND_HELP"_ns;
594 case APPCOMMAND_LAUNCH_APP1:
595 return "APPCOMMAND_LAUNCH_APP1"_ns;
596 case APPCOMMAND_LAUNCH_APP2:
597 return "APPCOMMAND_LAUNCH_APP2"_ns;
598 case APPCOMMAND_LAUNCH_MAIL:
599 return "APPCOMMAND_LAUNCH_MAIL"_ns;
600 case APPCOMMAND_LAUNCH_MEDIA_SELECT:
601 return "APPCOMMAND_LAUNCH_MEDIA_SELECT"_ns;
602 case APPCOMMAND_MEDIA_CHANNEL_DOWN:
603 return "APPCOMMAND_MEDIA_CHANNEL_DOWN"_ns;
604 case APPCOMMAND_MEDIA_CHANNEL_UP:
605 return "APPCOMMAND_MEDIA_CHANNEL_UP"_ns;
606 case APPCOMMAND_MEDIA_FAST_FORWARD:
607 return "APPCOMMAND_MEDIA_FAST_FORWARD"_ns;
608 case APPCOMMAND_MEDIA_NEXTTRACK:
609 return "APPCOMMAND_MEDIA_NEXTTRACK"_ns;
610 case APPCOMMAND_MEDIA_PAUSE:
611 return "APPCOMMAND_MEDIA_PAUSE"_ns;
612 case APPCOMMAND_MEDIA_PLAY:
613 return "APPCOMMAND_MEDIA_PLAY"_ns;
614 case APPCOMMAND_MEDIA_PLAY_PAUSE:
615 return "APPCOMMAND_MEDIA_PLAY_PAUSE"_ns;
616 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
617 return "APPCOMMAND_MEDIA_PREVIOUSTRACK"_ns;
618 case APPCOMMAND_MEDIA_RECORD:
619 return "APPCOMMAND_MEDIA_RECORD"_ns;
620 case APPCOMMAND_MEDIA_REWIND:
621 return "APPCOMMAND_MEDIA_REWIND"_ns;
622 case APPCOMMAND_MEDIA_STOP:
623 return "APPCOMMAND_MEDIA_STOP"_ns;
624 case APPCOMMAND_MIC_ON_OFF_TOGGLE:
625 return "APPCOMMAND_MIC_ON_OFF_TOGGLE"_ns;
626 case APPCOMMAND_MICROPHONE_VOLUME_DOWN:
627 return "APPCOMMAND_MICROPHONE_VOLUME_DOWN"_ns;
628 case APPCOMMAND_MICROPHONE_VOLUME_MUTE:
629 return "APPCOMMAND_MICROPHONE_VOLUME_MUTE"_ns;
630 case APPCOMMAND_MICROPHONE_VOLUME_UP:
631 return "APPCOMMAND_MICROPHONE_VOLUME_UP"_ns;
632 case APPCOMMAND_NEW:
633 return "APPCOMMAND_NEW"_ns;
634 case APPCOMMAND_OPEN:
635 return "APPCOMMAND_OPEN"_ns;
636 case APPCOMMAND_PASTE:
637 return "APPCOMMAND_PASTE"_ns;
638 case APPCOMMAND_PRINT:
639 return "APPCOMMAND_PRINT"_ns;
640 case APPCOMMAND_REDO:
641 return "APPCOMMAND_REDO"_ns;
642 case APPCOMMAND_REPLY_TO_MAIL:
643 return "APPCOMMAND_REPLY_TO_MAIL"_ns;
644 case APPCOMMAND_SAVE:
645 return "APPCOMMAND_SAVE"_ns;
646 case APPCOMMAND_SEND_MAIL:
647 return "APPCOMMAND_SEND_MAIL"_ns;
648 case APPCOMMAND_SPELL_CHECK:
649 return "APPCOMMAND_SPELL_CHECK"_ns;
650 case APPCOMMAND_TREBLE_DOWN:
651 return "APPCOMMAND_TREBLE_DOWN"_ns;
652 case APPCOMMAND_TREBLE_UP:
653 return "APPCOMMAND_TREBLE_UP"_ns;
654 case APPCOMMAND_UNDO:
655 return "APPCOMMAND_UNDO"_ns;
656 case APPCOMMAND_VOLUME_DOWN:
657 return "APPCOMMAND_VOLUME_DOWN"_ns;
658 case APPCOMMAND_VOLUME_MUTE:
659 return "APPCOMMAND_VOLUME_MUTE"_ns;
660 case APPCOMMAND_VOLUME_UP:
661 return "APPCOMMAND_VOLUME_UP"_ns;
662 default:
663 return nsPrintfCString("Unknown app command (0x%08zX)", aCommand);
667 static const nsCString GetAppCommandDeviceName(LPARAM aDevice) {
668 switch (aDevice) {
669 case FAPPCOMMAND_KEY:
670 return "FAPPCOMMAND_KEY"_ns;
671 case FAPPCOMMAND_MOUSE:
672 return "FAPPCOMMAND_MOUSE"_ns;
673 case FAPPCOMMAND_OEM:
674 return "FAPPCOMMAND_OEM"_ns;
675 default:
676 return nsPrintfCString("Unknown app command device (0x%04" PRIXLPTR ")",
677 aDevice);
681 class MOZ_STACK_CLASS GetAppCommandKeysName final : public nsAutoCString {
682 public:
683 explicit GetAppCommandKeysName(WPARAM aKeys) {
684 if (aKeys & MK_CONTROL) {
685 AppendLiteral("MK_CONTROL");
686 aKeys &= ~MK_CONTROL;
688 if (aKeys & MK_LBUTTON) {
689 MaybeAppendSeparator();
690 AppendLiteral("MK_LBUTTON");
691 aKeys &= ~MK_LBUTTON;
693 if (aKeys & MK_MBUTTON) {
694 MaybeAppendSeparator();
695 AppendLiteral("MK_MBUTTON");
696 aKeys &= ~MK_MBUTTON;
698 if (aKeys & MK_RBUTTON) {
699 MaybeAppendSeparator();
700 AppendLiteral("MK_RBUTTON");
701 aKeys &= ~MK_RBUTTON;
703 if (aKeys & MK_SHIFT) {
704 MaybeAppendSeparator();
705 AppendLiteral("MK_SHIFT");
706 aKeys &= ~MK_SHIFT;
708 if (aKeys & MK_XBUTTON1) {
709 MaybeAppendSeparator();
710 AppendLiteral("MK_XBUTTON1");
711 aKeys &= ~MK_XBUTTON1;
713 if (aKeys & MK_XBUTTON2) {
714 MaybeAppendSeparator();
715 AppendLiteral("MK_XBUTTON2");
716 aKeys &= ~MK_XBUTTON2;
718 if (aKeys) {
719 MaybeAppendSeparator();
720 AppendPrintf("Unknown Flags (0x%04zX)", aKeys);
722 if (IsEmpty()) {
723 AssignLiteral("none (0x0000)");
727 private:
728 void MaybeAppendSeparator() {
729 if (!IsEmpty()) {
730 AppendLiteral(" | ");
735 static const nsCString ToString(const MSG& aMSG) {
736 nsCString result;
737 result.AssignLiteral("{ message=");
738 result.Append(GetMessageName(aMSG.message).get());
739 result.AppendLiteral(", ");
740 switch (aMSG.message) {
741 case WM_KEYDOWN:
742 case WM_KEYUP:
743 case WM_SYSKEYDOWN:
744 case WM_SYSKEYUP:
745 result.AppendPrintf(
746 "virtual keycode=%s, repeat count=%" PRIdLPTR
747 ", "
748 "scancode=0x%02X, extended key=%s, "
749 "context code=%s, previous key state=%s, "
750 "transition state=%s",
751 GetVirtualKeyCodeName(aMSG.wParam).get(), aMSG.lParam & 0xFFFF,
752 WinUtils::GetScanCode(aMSG.lParam),
753 GetBoolName(WinUtils::IsExtendedScanCode(aMSG.lParam)),
754 GetBoolName((aMSG.lParam & (1 << 29)) != 0),
755 GetBoolName((aMSG.lParam & (1 << 30)) != 0),
756 GetBoolName((aMSG.lParam & (1 << 31)) != 0));
757 break;
758 case WM_CHAR:
759 case WM_DEADCHAR:
760 case WM_SYSCHAR:
761 case WM_SYSDEADCHAR:
762 result.AppendPrintf(
763 "character code=%s, repeat count=%" PRIdLPTR
764 ", "
765 "scancode=0x%02X, extended key=%s, "
766 "context code=%s, previous key state=%s, "
767 "transition state=%s",
768 GetCharacterCodeName(aMSG.wParam).get(), aMSG.lParam & 0xFFFF,
769 WinUtils::GetScanCode(aMSG.lParam),
770 GetBoolName(WinUtils::IsExtendedScanCode(aMSG.lParam)),
771 GetBoolName((aMSG.lParam & (1 << 29)) != 0),
772 GetBoolName((aMSG.lParam & (1 << 30)) != 0),
773 GetBoolName((aMSG.lParam & (1 << 31)) != 0));
774 break;
775 case WM_APPCOMMAND:
776 result.AppendPrintf(
777 "window handle=0x%zx, app command=%s, device=%s, dwKeys=%s",
778 aMSG.wParam,
779 GetAppCommandName(GET_APPCOMMAND_LPARAM(aMSG.lParam)).get(),
780 GetAppCommandDeviceName(GET_DEVICE_LPARAM(aMSG.lParam)).get(),
781 GetAppCommandKeysName(GET_KEYSTATE_LPARAM(aMSG.lParam)).get());
782 break;
783 default:
784 result.AppendPrintf("wParam=%zu, lParam=%" PRIdLPTR, aMSG.wParam,
785 aMSG.lParam);
786 break;
788 result.AppendPrintf(", hwnd=0x%p", aMSG.hwnd);
789 return result;
792 static const nsCString ToString(
793 const UniCharsAndModifiers& aUniCharsAndModifiers) {
794 if (aUniCharsAndModifiers.IsEmpty()) {
795 return "{}"_ns;
797 nsCString result;
798 result.AssignLiteral("{ ");
799 result.Append(GetCharacterCodeName(aUniCharsAndModifiers.CharAt(0)));
800 for (size_t i = 1; i < aUniCharsAndModifiers.Length(); ++i) {
801 if (aUniCharsAndModifiers.ModifiersAt(i - 1) !=
802 aUniCharsAndModifiers.ModifiersAt(i)) {
803 result.AppendLiteral(" [");
804 result.Append(GetModifiersName(aUniCharsAndModifiers.ModifiersAt(0)));
805 result.AppendLiteral("]");
807 result.AppendLiteral(", ");
808 result.Append(GetCharacterCodeName(aUniCharsAndModifiers.CharAt(i)));
810 result.AppendLiteral(" [");
811 uint32_t lastIndex = aUniCharsAndModifiers.Length() - 1;
812 result.Append(GetModifiersName(aUniCharsAndModifiers.ModifiersAt(lastIndex)));
813 result.AppendLiteral("] }");
814 return result;
817 const nsCString ToString(const ModifierKeyState& aModifierKeyState) {
818 nsCString result;
819 result.AssignLiteral("{ ");
820 result.Append(GetModifiersName(aModifierKeyState.GetModifiers()).get());
821 result.AppendLiteral(" }");
822 return result;
825 // Unique id counter associated with a keydown / keypress events. Used in
826 // identifing keypress events for removal from async event dispatch queue
827 // in metrofx after preventDefault is called on keydown events.
828 static uint32_t sUniqueKeyEventId = 0;
830 /*****************************************************************************
831 * mozilla::widget::ModifierKeyState
832 *****************************************************************************/
834 ModifierKeyState::ModifierKeyState() { Update(); }
836 ModifierKeyState::ModifierKeyState(Modifiers aModifiers)
837 : mModifiers(aModifiers) {
838 MOZ_ASSERT(!(mModifiers & MODIFIER_ALTGRAPH) || (!IsControl() && !IsAlt()),
839 "Neither MODIFIER_CONTROL nor MODIFIER_ALT should be set "
840 "if MODIFIER_ALTGRAPH is set");
843 void ModifierKeyState::Update() {
844 mModifiers = 0;
845 if (IS_VK_DOWN(VK_SHIFT)) {
846 mModifiers |= MODIFIER_SHIFT;
848 // If AltGr key (i.e., VK_RMENU on some keyboard layout) is pressed, only
849 // MODIFIER_ALTGRAPH should be set. Otherwise, i.e., if both Ctrl and Alt
850 // keys are pressed to emulate AltGr key, MODIFIER_CONTROL and MODIFIER_ALT
851 // keys should be set separately.
852 if (IS_VK_DOWN(VK_RMENU) && KeyboardLayout::GetInstance()->HasAltGr()) {
853 mModifiers |= MODIFIER_ALTGRAPH;
854 } else {
855 if (IS_VK_DOWN(VK_CONTROL)) {
856 mModifiers |= MODIFIER_CONTROL;
858 if (IS_VK_DOWN(VK_MENU)) {
859 mModifiers |= MODIFIER_ALT;
862 if (IS_VK_DOWN(VK_LWIN) || IS_VK_DOWN(VK_RWIN)) {
863 mModifiers |= MODIFIER_META;
865 if (::GetKeyState(VK_CAPITAL) & 1) {
866 mModifiers |= MODIFIER_CAPSLOCK;
868 if (::GetKeyState(VK_NUMLOCK) & 1) {
869 mModifiers |= MODIFIER_NUMLOCK;
871 if (::GetKeyState(VK_SCROLL) & 1) {
872 mModifiers |= MODIFIER_SCROLLLOCK;
876 void ModifierKeyState::Unset(Modifiers aRemovingModifiers) {
877 mModifiers &= ~aRemovingModifiers;
880 void ModifierKeyState::Set(Modifiers aAddingModifiers) {
881 mModifiers |= aAddingModifiers;
882 MOZ_ASSERT(!(mModifiers & MODIFIER_ALTGRAPH) || (!IsControl() && !IsAlt()),
883 "Neither MODIFIER_CONTROL nor MODIFIER_ALT should be set "
884 "if MODIFIER_ALTGRAPH is set");
887 void ModifierKeyState::InitInputEvent(WidgetInputEvent& aInputEvent) const {
888 aInputEvent.mModifiers = mModifiers;
890 switch (aInputEvent.mClass) {
891 case eMouseEventClass:
892 case ePointerEventClass:
893 case eMouseScrollEventClass:
894 case eWheelEventClass:
895 case eDragEventClass:
896 case eSimpleGestureEventClass:
897 InitMouseEvent(aInputEvent);
898 break;
899 default:
900 break;
904 void ModifierKeyState::InitMouseEvent(WidgetInputEvent& aMouseEvent) const {
905 NS_ASSERTION(aMouseEvent.mClass == eMouseEventClass ||
906 aMouseEvent.mClass == ePointerEventClass ||
907 aMouseEvent.mClass == eWheelEventClass ||
908 aMouseEvent.mClass == eDragEventClass ||
909 aMouseEvent.mClass == eSimpleGestureEventClass,
910 "called with non-mouse event");
912 WidgetMouseEventBase& mouseEvent = *aMouseEvent.AsMouseEventBase();
913 mouseEvent.mButtons = 0;
914 if (::GetKeyState(VK_LBUTTON) < 0) {
915 mouseEvent.mButtons |= MouseButtonsFlag::ePrimaryFlag;
917 if (::GetKeyState(VK_RBUTTON) < 0) {
918 mouseEvent.mButtons |= MouseButtonsFlag::eSecondaryFlag;
920 if (::GetKeyState(VK_MBUTTON) < 0) {
921 mouseEvent.mButtons |= MouseButtonsFlag::eMiddleFlag;
923 if (::GetKeyState(VK_XBUTTON1) < 0) {
924 mouseEvent.mButtons |= MouseButtonsFlag::e4thFlag;
926 if (::GetKeyState(VK_XBUTTON2) < 0) {
927 mouseEvent.mButtons |= MouseButtonsFlag::e5thFlag;
931 bool ModifierKeyState::IsShift() const {
932 return (mModifiers & MODIFIER_SHIFT) != 0;
935 bool ModifierKeyState::IsControl() const {
936 return (mModifiers & MODIFIER_CONTROL) != 0;
939 bool ModifierKeyState::IsAlt() const {
940 return (mModifiers & MODIFIER_ALT) != 0;
943 bool ModifierKeyState::IsWin() const {
944 return (mModifiers & MODIFIER_META) != 0;
947 bool ModifierKeyState::MaybeMatchShortcutKey() const {
948 // If Windows key is pressed, even if both Ctrl key and Alt key are pressed,
949 // it's possible to match a shortcut key.
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 = std::size(mShiftStates[aShiftState].Normal.Chars);
1111 for (uint32_t index = aNumOfChars; index < len; index++) {
1112 mShiftStates[aShiftState].Normal.Chars[index] = 0;
1116 void VirtualKey::SetDeadChar(ShiftState aShiftState, char16_t aDeadChar) {
1117 MOZ_ASSERT(aShiftState == ToShiftStateIndex(aShiftState));
1119 SetDeadKey(aShiftState, true);
1121 mShiftStates[aShiftState].DeadKey.DeadChar = aDeadChar;
1122 mShiftStates[aShiftState].DeadKey.Table = nullptr;
1125 UniCharsAndModifiers VirtualKey::GetUniChars(ShiftState aShiftState) const {
1126 UniCharsAndModifiers result = GetNativeUniChars(aShiftState);
1128 const uint8_t kShiftStateIndex = ToShiftStateIndex(aShiftState);
1129 if (!(kShiftStateIndex & STATE_CONTROL_ALT)) {
1130 // If neither Alt nor Ctrl key is pressed, just return stored data
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 = std::size(mShiftStates[kShiftStateIndex].Normal.Chars);
1185 for (uint32_t i = 0;
1186 i < len && mShiftStates[kShiftStateIndex].Normal.Chars[i]; i++) {
1187 result.Append(mShiftStates[kShiftStateIndex].Normal.Chars[i], modifiers);
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->GetLoadedLayout();
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 NS_WARNING_ASSERTION(
1604 charMsg.hwnd == mMsg.hwnd,
1605 "The retrieved char message was targeted to differnet window");
1606 mFollowingCharMsgs.AppendElement(charMsg);
1608 if (mFollowingCharMsgs.Length() == 1) {
1609 // If we receive a keydown message for a high-surrogate, a low-surrogate
1610 // keydown message **will** and should follow it. We cannot translate the
1611 // following WM_KEYDOWN message for the low-surrogate right now since
1612 // it's not yet queued into the message queue yet. Therefore, we need to
1613 // wait next one to dispatch keypress event with setting its `.key` value
1614 // to a surrogate pair rather than setting it to a lone surrogate.
1615 // FYI: This may happen with typing a non-BMP character on the touch
1616 // keyboard on Windows 10 or later except when an IME is installed. (If
1617 // IME is installed, composition is used instead.)
1618 if (IS_HIGH_SURROGATE(mFollowingCharMsgs[0].wParam)) {
1619 if (pendingHighSurrogate) {
1620 MOZ_LOG(gKeyLog, LogLevel::Warning,
1621 ("%p NativeKey::InitWithKeyOrChar(), there is pending "
1622 "high surrogate input, but received another high surrogate "
1623 "input. The previous one is discarded",
1624 this));
1626 sPendingHighSurrogate = mFollowingCharMsgs[0].wParam;
1627 mFollowingCharMsgs.Clear();
1628 } else if (IS_LOW_SURROGATE(mFollowingCharMsgs[0].wParam)) {
1629 // If we stopped dispathing a keypress event for a preceding
1630 // high-surrogate, treat this keydown (for a low-surrogate) as
1631 // introducing both the high surrogate and the low surrogate.
1632 if (pendingHighSurrogate) {
1633 MSG charMsg = mFollowingCharMsgs[0];
1634 mFollowingCharMsgs[0].wParam = pendingHighSurrogate;
1635 mFollowingCharMsgs.AppendElement(std::move(charMsg));
1636 } else {
1637 MOZ_LOG(
1638 gKeyLog, LogLevel::Warning,
1639 ("%p NativeKey::InitWithKeyOrChar(), there is no pending high "
1640 "surrogate input, but received lone low surrogate input",
1641 this));
1643 } else if (MOZ_UNLIKELY(pendingHighSurrogate)) {
1644 MOZ_LOG(gKeyLog, LogLevel::Warning,
1645 ("%p NativeKey::InitWithKeyOrChar(), there is pending "
1646 "high surrogate input, but received non-surrogate input. "
1647 "The high surrogate input is discarded",
1648 this));
1650 } else if (MOZ_UNLIKELY(pendingHighSurrogate &&
1651 !mFollowingCharMsgs.IsEmpty())) {
1652 MOZ_LOG(gKeyLog, LogLevel::Warning,
1653 ("%p NativeKey::InitWithKeyOrChar(), there is pending "
1654 "high surrogate input, but received 2 or more character input. "
1655 "The high surrogate input is discarded",
1656 this));
1660 keyboardLayout->InitNativeKey(*this);
1662 // Now, we can know if the key produces character(s) or a dead key with
1663 // AltGraph modifier. When user emulates AltGr key press with pressing
1664 // both Ctrl and Alt and the key produces character(s) or a dead key, we
1665 // need to replace Control and Alt state with AltGraph if the keyboard
1666 // layout has AltGr key.
1667 // Note that if Ctrl and/or Alt are pressed (not to emulate to press AltGr),
1668 // we need to set actual modifiers to eKeyDown and eKeyUp.
1669 if (MaybeEmulatingAltGraph() &&
1670 (mCommittedCharsAndModifiers.IsProducingCharsWithAltGr() ||
1671 mKeyNameIndex == KEY_NAME_INDEX_Dead)) {
1672 mModKeyState.Unset(MODIFIER_CONTROL | MODIFIER_ALT);
1673 mModKeyState.Set(MODIFIER_ALTGRAPH);
1676 mIsDeadKey =
1677 (IsFollowedByDeadCharMessage() ||
1678 keyboardLayout->IsDeadKey(mOriginalVirtualKeyCode, mModKeyState));
1679 mIsPrintableKey = mKeyNameIndex == KEY_NAME_INDEX_USE_STRING ||
1680 KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode);
1681 // The repeat count in mMsg.lParam isn't useful to check whether the event
1682 // is caused by the auto-repeat feature because it's not incremented even
1683 // if it's repeated twice or more (i.e., always 1). Therefore, we need to
1684 // check previous key state (31th bit) instead. If it's 1, the key was down
1685 // before the message was sent.
1686 mIsRepeat = (mMsg.lParam & (1 << 30)) != 0;
1687 InitIsSkippableForKeyOrChar(lastKeyMSG);
1689 if (IsKeyDownMessage()) {
1690 // Compute some strings which may be inputted by the key with various
1691 // modifier state if this key event won't cause text input actually.
1692 // They will be used for setting mAlternativeCharCodes in the callback
1693 // method which will be called by TextEventDispatcher.
1694 if (!IsFollowedByPrintableCharMessage()) {
1695 ComputeInputtingStringWithKeyboardLayout();
1697 // Remove odd char messages if there are.
1698 RemoveFollowingOddCharMessages();
1702 void NativeKey::InitCommittedCharsAndModifiersWithFollowingCharMessages() {
1703 mCommittedCharsAndModifiers.Clear();
1704 // This should cause inputting text in focused editor. However, it
1705 // ignores keypress events whose altKey or ctrlKey is true.
1706 // Therefore, we need to remove these modifier state here.
1707 Modifiers modifiers = mModKeyState.GetModifiers();
1708 if (IsFollowedByPrintableCharMessage()) {
1709 modifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
1710 if (MaybeEmulatingAltGraph()) {
1711 modifiers |= MODIFIER_ALTGRAPH;
1714 // NOTE: This method assumes that WM_CHAR and WM_SYSCHAR are never retrieved
1715 // at same time.
1716 for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
1717 // Ignore non-printable char messages.
1718 if (!IsPrintableCharOrSysCharMessage(mFollowingCharMsgs[i])) {
1719 continue;
1721 char16_t ch = static_cast<char16_t>(mFollowingCharMsgs[i].wParam);
1722 mCommittedCharsAndModifiers.Append(ch, modifiers);
1726 NativeKey::~NativeKey() {
1727 MOZ_LOG(gKeyLog, LogLevel::Debug,
1728 ("%p NativeKey::~NativeKey(), destroyed", this));
1729 if (mIsOverridingKeyboardLayout) {
1730 KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
1731 keyboardLayout->RestoreLayout();
1733 sLatestInstance = mLastInstance;
1736 void NativeKey::InitWithAppCommand() {
1737 if (GET_DEVICE_LPARAM(mMsg.lParam) != FAPPCOMMAND_KEY) {
1738 return;
1741 uint32_t appCommand = GET_APPCOMMAND_LPARAM(mMsg.lParam);
1742 switch (GET_APPCOMMAND_LPARAM(mMsg.lParam)) {
1743 #undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX
1744 #define NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX(aAppCommand, aKeyNameIndex) \
1745 case aAppCommand: \
1746 mKeyNameIndex = aKeyNameIndex; \
1747 break;
1749 #include "NativeKeyToDOMKeyName.h"
1751 #undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX
1753 default:
1754 mKeyNameIndex = KEY_NAME_INDEX_Unidentified;
1757 // Guess the virtual keycode which caused this message.
1758 switch (appCommand) {
1759 case APPCOMMAND_BROWSER_BACKWARD:
1760 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_BACK;
1761 break;
1762 case APPCOMMAND_BROWSER_FORWARD:
1763 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_FORWARD;
1764 break;
1765 case APPCOMMAND_BROWSER_REFRESH:
1766 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_REFRESH;
1767 break;
1768 case APPCOMMAND_BROWSER_STOP:
1769 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_STOP;
1770 break;
1771 case APPCOMMAND_BROWSER_SEARCH:
1772 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_SEARCH;
1773 break;
1774 case APPCOMMAND_BROWSER_FAVORITES:
1775 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_FAVORITES;
1776 break;
1777 case APPCOMMAND_BROWSER_HOME:
1778 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_HOME;
1779 break;
1780 case APPCOMMAND_VOLUME_MUTE:
1781 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_VOLUME_MUTE;
1782 break;
1783 case APPCOMMAND_VOLUME_DOWN:
1784 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_VOLUME_DOWN;
1785 break;
1786 case APPCOMMAND_VOLUME_UP:
1787 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_VOLUME_UP;
1788 break;
1789 case APPCOMMAND_MEDIA_NEXTTRACK:
1790 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_NEXT_TRACK;
1791 break;
1792 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
1793 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_PREV_TRACK;
1794 break;
1795 case APPCOMMAND_MEDIA_STOP:
1796 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_STOP;
1797 break;
1798 case APPCOMMAND_MEDIA_PLAY_PAUSE:
1799 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_PLAY_PAUSE;
1800 break;
1801 case APPCOMMAND_LAUNCH_MAIL:
1802 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_MAIL;
1803 break;
1804 case APPCOMMAND_LAUNCH_MEDIA_SELECT:
1805 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_MEDIA_SELECT;
1806 break;
1807 case APPCOMMAND_LAUNCH_APP1:
1808 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_APP1;
1809 break;
1810 case APPCOMMAND_LAUNCH_APP2:
1811 mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_APP2;
1812 break;
1813 default:
1814 return;
1817 uint16_t scanCodeEx = ComputeScanCodeExFromVirtualKeyCode(mVirtualKeyCode);
1818 mScanCode = static_cast<uint8_t>(scanCodeEx & 0xFF);
1819 uint8_t extended = static_cast<uint8_t>((scanCodeEx & 0xFF00) >> 8);
1820 mIsExtended = (extended == 0xE0) || (extended == 0xE1);
1821 mDOMKeyCode = KeyboardLayout::GetInstance()->ConvertNativeKeyCodeToDOMKeyCode(
1822 mOriginalVirtualKeyCode);
1823 mCodeNameIndex = KeyboardLayout::ConvertScanCodeToCodeNameIndex(
1824 GetScanCodeWithExtendedFlag());
1825 // If we can map the WM_APPCOMMAND to a virtual keycode, we can trust
1826 // the result of GetKeyboardState(). Otherwise, we dispatch both
1827 // keydown and keyup events from WM_APPCOMMAND handler. Therefore,
1828 // even if WM_APPCOMMAND is caused by auto key repeat, web apps receive
1829 // a pair of DOM keydown and keyup events. I.e., KeyboardEvent.repeat
1830 // should be never true of such keys.
1831 // XXX Isn't the key state always true? If the key press caused this
1832 // WM_APPCOMMAND, that means it's pressed at that time.
1833 if (mVirtualKeyCode) {
1834 BYTE kbdState[256];
1835 memset(kbdState, 0, sizeof(kbdState));
1836 ::GetKeyboardState(kbdState);
1837 mIsSkippableInRemoteProcess = mIsRepeat = !!kbdState[mVirtualKeyCode];
1841 bool NativeKey::MaybeEmulatingAltGraph() const {
1842 return IsControl() && IsAlt() && KeyboardLayout::GetInstance()->HasAltGr();
1845 // static
1846 bool NativeKey::IsControlChar(char16_t aChar) {
1847 static const char16_t U_SPACE = 0x20;
1848 static const char16_t U_DELETE = 0x7F;
1849 return aChar < U_SPACE || aChar == U_DELETE;
1852 bool NativeKey::IsFollowedByDeadCharMessage() const {
1853 if (mFollowingCharMsgs.IsEmpty()) {
1854 return false;
1856 return IsDeadCharMessage(mFollowingCharMsgs[0]);
1859 bool NativeKey::IsFollowedByPrintableCharMessage() const {
1860 for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
1861 if (IsPrintableCharMessage(mFollowingCharMsgs[i])) {
1862 return true;
1865 return false;
1868 bool NativeKey::IsFollowedByPrintableCharOrSysCharMessage() const {
1869 for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
1870 if (IsPrintableCharOrSysCharMessage(mFollowingCharMsgs[i])) {
1871 return true;
1874 return false;
1877 bool NativeKey::IsReservedBySystem() const {
1878 // Alt+Space key is handled by OS, we shouldn't touch it.
1879 if (mModKeyState.IsAlt() && !mModKeyState.IsControl() &&
1880 mVirtualKeyCode == VK_SPACE) {
1881 return true;
1884 // XXX How about Alt+F4? We receive WM_SYSKEYDOWN for F4 before closing the
1885 // window. Although, we don't prevent to close the window but the key
1886 // event shouldn't be exposed to the web.
1888 return false;
1891 bool NativeKey::IsIMEDoingKakuteiUndo() const {
1892 // Following message pattern is caused by "Kakutei-Undo" of ATOK or WXG:
1893 // ---------------------------------------------------------------------------
1894 // WM_KEYDOWN * n (wParam = VK_BACK, lParam = 0x1)
1895 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC0000001) # ATOK
1896 // WM_IME_STARTCOMPOSITION * 1 (wParam = 0x0, lParam = 0x0)
1897 // WM_IME_COMPOSITION * 1 (wParam = 0x0, lParam = 0x1BF)
1898 // WM_CHAR * n (wParam = VK_BACK, lParam = 0x1)
1899 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC00E0001)
1900 // ---------------------------------------------------------------------------
1901 // This doesn't match usual key message pattern such as:
1902 // WM_KEYDOWN -> WM_CHAR -> WM_KEYDOWN -> WM_CHAR -> ... -> WM_KEYUP
1903 // See following bugs for the detail.
1904 // https://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese)
1905 // https://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English)
1906 MSG startCompositionMsg, compositionMsg, charMsg;
1907 return WinUtils::PeekMessage(&startCompositionMsg, mMsg.hwnd,
1908 WM_IME_STARTCOMPOSITION, WM_IME_STARTCOMPOSITION,
1909 PM_NOREMOVE | PM_NOYIELD) &&
1910 WinUtils::PeekMessage(&compositionMsg, mMsg.hwnd, WM_IME_COMPOSITION,
1911 WM_IME_COMPOSITION, PM_NOREMOVE | PM_NOYIELD) &&
1912 WinUtils::PeekMessage(&charMsg, mMsg.hwnd, WM_CHAR, WM_CHAR,
1913 PM_NOREMOVE | PM_NOYIELD) &&
1914 startCompositionMsg.wParam == 0x0 &&
1915 startCompositionMsg.lParam == 0x0 && compositionMsg.wParam == 0x0 &&
1916 compositionMsg.lParam == 0x1BF && charMsg.wParam == VK_BACK &&
1917 charMsg.lParam == 0x1 &&
1918 startCompositionMsg.time <= compositionMsg.time &&
1919 compositionMsg.time <= charMsg.time;
1922 void NativeKey::RemoveFollowingOddCharMessages() {
1923 MOZ_ASSERT(IsKeyDownMessage());
1925 // If the keydown message is synthesized for automated tests, there is
1926 // nothing to do here.
1927 if (mFakeCharMsgs) {
1928 return;
1931 // If there are some following char messages before another key message,
1932 // there is nothing to do here.
1933 if (!mFollowingCharMsgs.IsEmpty()) {
1934 return;
1937 // If the handling key isn't Backspace, there is nothing to do here.
1938 if (mOriginalVirtualKeyCode != VK_BACK) {
1939 return;
1942 // If we don't see the odd message pattern, there is nothing to do here.
1943 if (!IsIMEDoingKakuteiUndo()) {
1944 return;
1947 // Otherwise, we need to remove odd WM_CHAR messages for ATOK or WXG (both
1948 // of them are Japanese IME).
1949 MSG msg;
1950 while (WinUtils::PeekMessage(&msg, mMsg.hwnd, WM_CHAR, WM_CHAR,
1951 PM_REMOVE | PM_NOYIELD)) {
1952 if (msg.message != WM_CHAR) {
1953 MOZ_RELEASE_ASSERT(msg.message == WM_NULL,
1954 "Unexpected message was removed");
1955 continue;
1957 MOZ_LOG(
1958 gKeyLog, LogLevel::Info,
1959 ("%p NativeKey::RemoveFollowingOddCharMessages(), removed odd char "
1960 "message, %s",
1961 this, ToString(msg).get()));
1962 mRemovedOddCharMsgs.AppendElement(msg);
1966 UINT NativeKey::GetScanCodeWithExtendedFlag() const {
1967 if (!mIsExtended) {
1968 return mScanCode;
1970 return (0xE000 | mScanCode);
1973 uint32_t NativeKey::GetKeyLocation() const {
1974 switch (mVirtualKeyCode) {
1975 case VK_LSHIFT:
1976 case VK_LCONTROL:
1977 case VK_LMENU:
1978 case VK_LWIN:
1979 return eKeyLocationLeft;
1981 case VK_RSHIFT:
1982 case VK_RCONTROL:
1983 case VK_RMENU:
1984 case VK_RWIN:
1985 return eKeyLocationRight;
1987 case VK_RETURN:
1988 // XXX This code assumes that all keyboard drivers use same mapping.
1989 return !mIsExtended ? eKeyLocationStandard : eKeyLocationNumpad;
1991 case VK_INSERT:
1992 case VK_DELETE:
1993 case VK_END:
1994 case VK_DOWN:
1995 case VK_NEXT:
1996 case VK_LEFT:
1997 case VK_CLEAR:
1998 case VK_RIGHT:
1999 case VK_HOME:
2000 case VK_UP:
2001 case VK_PRIOR:
2002 // XXX This code assumes that all keyboard drivers use same mapping.
2003 return mIsExtended ? eKeyLocationStandard : eKeyLocationNumpad;
2005 // NumLock key isn't included due to IE9's behavior.
2006 case VK_NUMPAD0:
2007 case VK_NUMPAD1:
2008 case VK_NUMPAD2:
2009 case VK_NUMPAD3:
2010 case VK_NUMPAD4:
2011 case VK_NUMPAD5:
2012 case VK_NUMPAD6:
2013 case VK_NUMPAD7:
2014 case VK_NUMPAD8:
2015 case VK_NUMPAD9:
2016 case VK_DECIMAL:
2017 case VK_DIVIDE:
2018 case VK_MULTIPLY:
2019 case VK_SUBTRACT:
2020 case VK_ADD:
2021 // Separator key of Brazilian keyboard or JIS keyboard for Mac
2022 case VK_ABNT_C2:
2023 return eKeyLocationNumpad;
2025 case VK_SHIFT:
2026 case VK_CONTROL:
2027 case VK_MENU:
2028 NS_WARNING("Failed to decide the key location?");
2029 [[fallthrough]];
2031 default:
2032 return eKeyLocationStandard;
2036 uint8_t NativeKey::ComputeVirtualKeyCodeFromScanCode() const {
2037 return static_cast<uint8_t>(
2038 ::MapVirtualKeyEx(mScanCode, MAPVK_VSC_TO_VK, mKeyboardLayout));
2041 uint8_t NativeKey::ComputeVirtualKeyCodeFromScanCodeEx() const {
2042 // MapVirtualKeyEx() has been improved for supporting extended keys since
2043 // Vista. When we call it for mapping a scancode of an extended key and
2044 // a virtual keycode, we need to add 0xE000 to the scancode.
2045 return static_cast<uint8_t>(::MapVirtualKeyEx(
2046 GetScanCodeWithExtendedFlag(), MAPVK_VSC_TO_VK_EX, mKeyboardLayout));
2049 uint16_t NativeKey::ComputeScanCodeExFromVirtualKeyCode(
2050 UINT aVirtualKeyCode) const {
2051 return static_cast<uint16_t>(
2052 ::MapVirtualKeyEx(aVirtualKeyCode, MAPVK_VK_TO_VSC_EX, mKeyboardLayout));
2055 char16_t NativeKey::ComputeUnicharFromScanCode() const {
2056 return static_cast<char16_t>(::MapVirtualKeyEx(
2057 ComputeVirtualKeyCodeFromScanCode(), MAPVK_VK_TO_CHAR, mKeyboardLayout));
2060 nsEventStatus NativeKey::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const {
2061 return InitKeyEvent(aKeyEvent, mModKeyState);
2064 nsEventStatus NativeKey::InitKeyEvent(
2065 WidgetKeyboardEvent& aKeyEvent,
2066 const ModifierKeyState& aModKeyState) const {
2067 if (mWidget->Destroyed()) {
2068 MOZ_CRASH("NativeKey tries to dispatch a key event on destroyed widget");
2071 LayoutDeviceIntPoint point(0, 0);
2072 mWidget->InitEvent(aKeyEvent, &point);
2074 switch (aKeyEvent.mMessage) {
2075 case eKeyDown:
2076 // If it was followed by a char message but it was consumed by somebody,
2077 // we should mark it as consumed because somebody must have handled it
2078 // and we should prevent to do "double action" for the key operation.
2079 // However, for compatibility with older version and other browsers,
2080 // we should dispatch the events even in the web content.
2081 // And also if it's a WM_SYSKEYDOWN which is not followed by WM_SYSCHAR,
2082 // the input may be consumed by the builtin IME to input a Unicode
2083 // character from the code point.
2084 if (mCharMessageHasGone || (IsSysKeyDownMessage() && mIsPrintableKey &&
2085 mFollowingCharMsgs.IsEmpty())) {
2086 aKeyEvent.PreventDefaultBeforeDispatch(CrossProcessForwarding::eAllow);
2088 aKeyEvent.mKeyCode = mDOMKeyCode;
2089 // Unique id for this keydown event and its associated keypress.
2090 sUniqueKeyEventId++;
2091 aKeyEvent.mUniqueId = sUniqueKeyEventId;
2092 break;
2093 case eKeyUp:
2094 aKeyEvent.mKeyCode = mDOMKeyCode;
2095 // Set defaultPrevented of the key event if the VK_MENU is not a system
2096 // key release, so that the menu bar does not trigger. This helps avoid
2097 // triggering the menu bar for ALT key accelerators used in assistive
2098 // technologies such as Window-Eyes and ZoomText or for switching open
2099 // state of IME. On the other hand, we should dispatch the events even
2100 // in the web content for compatibility with older version and other
2101 // browsers.
2102 if (mOriginalVirtualKeyCode == VK_MENU && mMsg.message != WM_SYSKEYUP) {
2103 aKeyEvent.PreventDefaultBeforeDispatch(CrossProcessForwarding::eAllow);
2105 break;
2106 case eKeyPress:
2107 MOZ_ASSERT(!mCharMessageHasGone,
2108 "If following char message was consumed by somebody, "
2109 "keydown event should be consumed above");
2110 aKeyEvent.mUniqueId = sUniqueKeyEventId;
2111 break;
2112 default:
2113 MOZ_CRASH("Invalid event message");
2116 aKeyEvent.mIsRepeat = mIsRepeat;
2117 aKeyEvent.mMaybeSkippableInRemoteProcess = mIsSkippableInRemoteProcess;
2118 aKeyEvent.mKeyNameIndex = mKeyNameIndex;
2119 if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
2120 aKeyEvent.mKeyValue = mCommittedCharsAndModifiers.ToString();
2122 aKeyEvent.mCodeNameIndex = mCodeNameIndex;
2123 MOZ_ASSERT(mCodeNameIndex != CODE_NAME_INDEX_USE_STRING);
2124 aKeyEvent.mLocation = GetKeyLocation();
2125 aModKeyState.InitInputEvent(aKeyEvent);
2127 KeyboardLayout::NotifyIdleServiceOfUserActivity();
2129 MOZ_LOG(
2130 gKeyLog, LogLevel::Info,
2131 ("%p NativeKey::InitKeyEvent(), initialized, aKeyEvent={ "
2132 "mMessage=%s, mKeyNameIndex=%s, mKeyValue=\"%s\", mCodeNameIndex=%s, "
2133 "mKeyCode=%s, mLocation=%s, mModifiers=%s, DefaultPrevented()=%s }",
2134 this, ToChar(aKeyEvent.mMessage),
2135 ToString(aKeyEvent.mKeyNameIndex).get(),
2136 NS_ConvertUTF16toUTF8(aKeyEvent.mKeyValue).get(),
2137 ToString(aKeyEvent.mCodeNameIndex).get(),
2138 GetDOMKeyCodeName(aKeyEvent.mKeyCode).get(),
2139 GetKeyLocationName(aKeyEvent.mLocation).get(),
2140 GetModifiersName(aKeyEvent.mModifiers).get(),
2141 GetBoolName(aKeyEvent.DefaultPrevented())));
2143 return aKeyEvent.DefaultPrevented() ? nsEventStatus_eConsumeNoDefault
2144 : nsEventStatus_eIgnore;
2147 bool NativeKey::DispatchCommandEvent(uint32_t aEventCommand) const {
2148 RefPtr<nsAtom> command;
2149 switch (aEventCommand) {
2150 case APPCOMMAND_BROWSER_BACKWARD:
2151 command = nsGkAtoms::Back;
2152 break;
2153 case APPCOMMAND_BROWSER_FORWARD:
2154 command = nsGkAtoms::Forward;
2155 break;
2156 case APPCOMMAND_BROWSER_REFRESH:
2157 command = nsGkAtoms::Reload;
2158 break;
2159 case APPCOMMAND_BROWSER_STOP:
2160 command = nsGkAtoms::Stop;
2161 break;
2162 case APPCOMMAND_BROWSER_SEARCH:
2163 command = nsGkAtoms::Search;
2164 break;
2165 case APPCOMMAND_BROWSER_FAVORITES:
2166 command = nsGkAtoms::Bookmarks;
2167 break;
2168 case APPCOMMAND_BROWSER_HOME:
2169 command = nsGkAtoms::Home;
2170 break;
2171 case APPCOMMAND_CLOSE:
2172 command = nsGkAtoms::Close;
2173 break;
2174 case APPCOMMAND_FIND:
2175 command = nsGkAtoms::Find;
2176 break;
2177 case APPCOMMAND_HELP:
2178 command = nsGkAtoms::Help;
2179 break;
2180 case APPCOMMAND_NEW:
2181 command = nsGkAtoms::New;
2182 break;
2183 case APPCOMMAND_OPEN:
2184 command = nsGkAtoms::Open;
2185 break;
2186 case APPCOMMAND_PRINT:
2187 command = nsGkAtoms::Print;
2188 break;
2189 case APPCOMMAND_SAVE:
2190 command = nsGkAtoms::Save;
2191 break;
2192 case APPCOMMAND_FORWARD_MAIL:
2193 command = nsGkAtoms::ForwardMail;
2194 break;
2195 case APPCOMMAND_REPLY_TO_MAIL:
2196 command = nsGkAtoms::ReplyToMail;
2197 break;
2198 case APPCOMMAND_SEND_MAIL:
2199 command = nsGkAtoms::SendMail;
2200 break;
2201 case APPCOMMAND_MEDIA_NEXTTRACK:
2202 command = nsGkAtoms::NextTrack;
2203 break;
2204 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
2205 command = nsGkAtoms::PreviousTrack;
2206 break;
2207 case APPCOMMAND_MEDIA_STOP:
2208 command = nsGkAtoms::MediaStop;
2209 break;
2210 case APPCOMMAND_MEDIA_PLAY_PAUSE:
2211 command = nsGkAtoms::PlayPause;
2212 break;
2213 default:
2214 MOZ_LOG(
2215 gKeyLog, LogLevel::Info,
2216 ("%p NativeKey::DispatchCommandEvent(), doesn't dispatch command "
2217 "event",
2218 this));
2219 return false;
2221 WidgetCommandEvent appCommandEvent(true, command, mWidget);
2223 mWidget->InitEvent(appCommandEvent);
2224 MOZ_LOG(gKeyLog, LogLevel::Info,
2225 ("%p NativeKey::DispatchCommandEvent(), dispatching "
2226 "%s app command event...",
2227 this, nsAtomCString(command).get()));
2228 bool ok =
2229 mWidget->DispatchWindowEvent(appCommandEvent) || mWidget->Destroyed();
2230 MOZ_LOG(
2231 gKeyLog, LogLevel::Info,
2232 ("%p NativeKey::DispatchCommandEvent(), dispatched app command event, "
2233 "result=%s, mWidget->Destroyed()=%s",
2234 this, GetBoolName(ok), GetBoolName(mWidget->Destroyed())));
2235 return ok;
2238 bool NativeKey::HandleAppCommandMessage() const {
2239 // If the widget has gone, we should do nothing.
2240 if (mWidget->Destroyed()) {
2241 MOZ_LOG(gKeyLog, LogLevel::Warning,
2242 ("%p NativeKey::HandleAppCommandMessage(), WARNING, not handled "
2243 "due to "
2244 "destroyed the widget",
2245 this));
2246 return false;
2249 // NOTE: Typical behavior of WM_APPCOMMAND caused by key is, WM_APPCOMMAND
2250 // message is _sent_ first. Then, the DefaultWndProc will _post_
2251 // WM_KEYDOWN message and WM_KEYUP message if the keycode for the
2252 // command is available (i.e., mVirtualKeyCode is not 0).
2254 // NOTE: IntelliType (Microsoft's keyboard utility software) always consumes
2255 // WM_KEYDOWN and WM_KEYUP.
2257 // Let's dispatch keydown message before our chrome handles the command
2258 // when the message is caused by a keypress. This behavior makes handling
2259 // WM_APPCOMMAND be a default action of the keydown event. This means that
2260 // web applications can handle multimedia keys and prevent our default action.
2261 // This allow web applications to provide better UX for multimedia keyboard
2262 // users.
2263 bool dispatchKeyEvent = (GET_DEVICE_LPARAM(mMsg.lParam) == FAPPCOMMAND_KEY);
2264 if (dispatchKeyEvent) {
2265 // If a plug-in window has focus but it didn't consume the message, our
2266 // window receive WM_APPCOMMAND message. In this case, we shouldn't
2267 // dispatch KeyboardEvents because an event handler may access the
2268 // plug-in process synchronously.
2269 dispatchKeyEvent =
2270 WinUtils::IsOurProcessWindow(reinterpret_cast<HWND>(mMsg.wParam));
2273 bool consumed = false;
2275 if (dispatchKeyEvent) {
2276 nsresult rv = mDispatcher->BeginNativeInputTransaction();
2277 if (NS_WARN_IF(NS_FAILED(rv))) {
2278 MOZ_LOG(gKeyLog, LogLevel::Error,
2279 ("%p NativeKey::HandleAppCommandMessage(), FAILED due to "
2280 "BeginNativeInputTransaction() failure",
2281 this));
2282 return true;
2284 MOZ_LOG(gKeyLog, LogLevel::Info,
2285 ("%p NativeKey::HandleAppCommandMessage(), initializing keydown "
2286 "event...",
2287 this));
2288 WidgetKeyboardEvent keydownEvent(true, eKeyDown, mWidget);
2289 nsEventStatus status = InitKeyEvent(keydownEvent, mModKeyState);
2290 MOZ_LOG(gKeyLog, LogLevel::Info,
2291 ("%p NativeKey::HandleAppCommandMessage(), tries to dispatch "
2292 "keydown event...",
2293 this));
2294 // NOTE: If the keydown event is consumed by web contents, we shouldn't
2295 // continue to handle the command.
2296 if (!mDispatcher->DispatchKeyboardEvent(eKeyDown, keydownEvent, status,
2297 const_cast<NativeKey*>(this))) {
2298 MOZ_LOG(gKeyLog, LogLevel::Info,
2299 ("%p NativeKey::HandleAppCommandMessage(), keydown event isn't "
2300 "dispatched",
2301 this));
2302 // If keyboard event wasn't fired, there must be composition.
2303 // So, we don't need to dispatch a command event.
2304 return true;
2306 consumed = status == nsEventStatus_eConsumeNoDefault;
2307 MOZ_LOG(gKeyLog, LogLevel::Info,
2308 ("%p NativeKey::HandleAppCommandMessage(), keydown event was "
2309 "dispatched, consumed=%s",
2310 this, GetBoolName(consumed)));
2311 sDispatchedKeyOfAppCommand = mVirtualKeyCode;
2312 if (mWidget->Destroyed()) {
2313 MOZ_LOG(
2314 gKeyLog, LogLevel::Info,
2315 ("%p NativeKey::HandleAppCommandMessage(), keydown event caused "
2316 "destroying the widget",
2317 this));
2318 return true;
2322 // Dispatch a command event or a content command event if the command is
2323 // supported.
2324 if (!consumed) {
2325 uint32_t appCommand = GET_APPCOMMAND_LPARAM(mMsg.lParam);
2326 EventMessage contentCommandMessage = eVoidEvent;
2327 switch (appCommand) {
2328 case APPCOMMAND_BROWSER_BACKWARD:
2329 case APPCOMMAND_BROWSER_FORWARD:
2330 case APPCOMMAND_BROWSER_REFRESH:
2331 case APPCOMMAND_BROWSER_STOP:
2332 case APPCOMMAND_BROWSER_SEARCH:
2333 case APPCOMMAND_BROWSER_FAVORITES:
2334 case APPCOMMAND_BROWSER_HOME:
2335 case APPCOMMAND_CLOSE:
2336 case APPCOMMAND_FIND:
2337 case APPCOMMAND_HELP:
2338 case APPCOMMAND_NEW:
2339 case APPCOMMAND_OPEN:
2340 case APPCOMMAND_PRINT:
2341 case APPCOMMAND_SAVE:
2342 case APPCOMMAND_FORWARD_MAIL:
2343 case APPCOMMAND_REPLY_TO_MAIL:
2344 case APPCOMMAND_SEND_MAIL:
2345 case APPCOMMAND_MEDIA_NEXTTRACK:
2346 case APPCOMMAND_MEDIA_PREVIOUSTRACK:
2347 case APPCOMMAND_MEDIA_STOP:
2348 case APPCOMMAND_MEDIA_PLAY_PAUSE:
2349 // We shouldn't consume the message always because if we don't handle
2350 // the message, the sender (typically, utility of keyboard or mouse)
2351 // may send other key messages which indicate well known shortcut key.
2352 consumed = DispatchCommandEvent(appCommand);
2353 break;
2355 // Use content command for following commands:
2356 case APPCOMMAND_COPY:
2357 contentCommandMessage = eContentCommandCopy;
2358 break;
2359 case APPCOMMAND_CUT:
2360 contentCommandMessage = eContentCommandCut;
2361 break;
2362 case APPCOMMAND_PASTE:
2363 contentCommandMessage = eContentCommandPaste;
2364 break;
2365 case APPCOMMAND_REDO:
2366 contentCommandMessage = eContentCommandRedo;
2367 break;
2368 case APPCOMMAND_UNDO:
2369 contentCommandMessage = eContentCommandUndo;
2370 break;
2373 if (contentCommandMessage) {
2374 MOZ_ASSERT(!mWidget->Destroyed());
2375 WidgetContentCommandEvent contentCommandEvent(true, contentCommandMessage,
2376 mWidget);
2377 MOZ_LOG(
2378 gKeyLog, LogLevel::Info,
2379 ("%p NativeKey::HandleAppCommandMessage(), dispatching %s event...",
2380 this, ToChar(contentCommandMessage)));
2381 mWidget->DispatchWindowEvent(contentCommandEvent);
2382 MOZ_LOG(gKeyLog, LogLevel::Info,
2383 ("%p NativeKey::HandleAppCommandMessage(), dispatched %s event",
2384 this, ToChar(contentCommandMessage)));
2385 consumed = true;
2387 if (mWidget->Destroyed()) {
2388 MOZ_LOG(gKeyLog, LogLevel::Info,
2389 ("%p NativeKey::HandleAppCommandMessage(), %s event caused "
2390 "destroying the widget",
2391 this, ToChar(contentCommandMessage)));
2392 return true;
2394 } else {
2395 MOZ_LOG(gKeyLog, LogLevel::Info,
2396 ("%p NativeKey::HandleAppCommandMessage(), doesn't dispatch "
2397 "content "
2398 "command event",
2399 this));
2403 // Dispatch a keyup event if the command is caused by pressing a key and
2404 // the key isn't mapped to a virtual keycode.
2405 if (dispatchKeyEvent && !mVirtualKeyCode) {
2406 MOZ_ASSERT(!mWidget->Destroyed());
2407 nsresult rv = mDispatcher->BeginNativeInputTransaction();
2408 if (NS_WARN_IF(NS_FAILED(rv))) {
2409 MOZ_LOG(gKeyLog, LogLevel::Error,
2410 ("%p NativeKey::HandleAppCommandMessage(), FAILED due to "
2411 "BeginNativeInputTransaction() failure",
2412 this));
2413 return true;
2415 MOZ_LOG(gKeyLog, LogLevel::Info,
2416 ("%p NativeKey::HandleAppCommandMessage(), initializing keyup "
2417 "event...",
2418 this));
2419 WidgetKeyboardEvent keyupEvent(true, eKeyUp, mWidget);
2420 nsEventStatus status = InitKeyEvent(keyupEvent, mModKeyState);
2421 MOZ_LOG(gKeyLog, LogLevel::Info,
2422 ("%p NativeKey::HandleAppCommandMessage(), dispatching keyup "
2423 "event...",
2424 this));
2425 // NOTE: Ignore if the keyup event is consumed because keyup event
2426 // represents just a physical key event state change.
2427 mDispatcher->DispatchKeyboardEvent(eKeyUp, keyupEvent, status,
2428 const_cast<NativeKey*>(this));
2429 MOZ_LOG(
2430 gKeyLog, LogLevel::Info,
2431 ("%p NativeKey::HandleAppCommandMessage(), dispatched keyup event",
2432 this));
2433 if (mWidget->Destroyed()) {
2434 MOZ_LOG(gKeyLog, LogLevel::Info,
2435 ("%p NativeKey::HandleAppCommandMessage(), keyup event caused "
2436 "destroying the widget",
2437 this));
2438 return true;
2442 return consumed;
2445 bool NativeKey::HandleKeyDownMessage(bool* aEventDispatched) const {
2446 MOZ_ASSERT(IsKeyDownMessage());
2448 if (aEventDispatched) {
2449 *aEventDispatched = false;
2452 if (sDispatchedKeyOfAppCommand &&
2453 sDispatchedKeyOfAppCommand == mOriginalVirtualKeyCode) {
2454 // The multimedia key event has already been dispatch from
2455 // HandleAppCommandMessage().
2456 sDispatchedKeyOfAppCommand = 0;
2457 MOZ_LOG(gKeyLog, LogLevel::Info,
2458 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2459 "event due to already dispatched from HandleAppCommandMessage(), ",
2460 this));
2461 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
2462 RedirectedKeyDownMessageManager::Forget();
2464 return true;
2467 if (IsReservedBySystem()) {
2468 MOZ_LOG(gKeyLog, LogLevel::Info,
2469 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2470 "event because the key combination is reserved by the system",
2471 this));
2472 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
2473 RedirectedKeyDownMessageManager::Forget();
2475 return false;
2478 if (sPendingHighSurrogate) {
2479 MOZ_LOG(gKeyLog, LogLevel::Info,
2480 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keydown "
2481 "event because the key introduced only a high surrotate, so we "
2482 "should wait the following low surrogate input",
2483 this));
2484 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
2485 RedirectedKeyDownMessageManager::Forget();
2487 return false;
2490 // If the widget has gone, we should do nothing.
2491 if (mWidget->Destroyed()) {
2492 MOZ_LOG(
2493 gKeyLog, LogLevel::Warning,
2494 ("%p NativeKey::HandleKeyDownMessage(), WARNING, not handled due to "
2495 "destroyed the widget",
2496 this));
2497 if (RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
2498 RedirectedKeyDownMessageManager::Forget();
2500 return false;
2503 bool defaultPrevented = false;
2504 if (mFakeCharMsgs ||
2505 !RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
2506 nsresult rv = mDispatcher->BeginNativeInputTransaction();
2507 if (NS_WARN_IF(NS_FAILED(rv))) {
2508 MOZ_LOG(gKeyLog, LogLevel::Error,
2509 ("%p NativeKey::HandleKeyDownMessage(), FAILED due to "
2510 "BeginNativeInputTransaction() failure",
2511 this));
2512 return true;
2515 bool isIMEEnabled = WinUtils::IsIMEEnabled(mWidget->GetInputContext());
2517 MOZ_LOG(gKeyLog, LogLevel::Debug,
2518 ("%p NativeKey::HandleKeyDownMessage(), initializing keydown "
2519 "event...",
2520 this));
2522 WidgetKeyboardEvent keydownEvent(true, eKeyDown, mWidget);
2523 nsEventStatus status = InitKeyEvent(keydownEvent, mModKeyState);
2524 MOZ_LOG(
2525 gKeyLog, LogLevel::Info,
2526 ("%p NativeKey::HandleKeyDownMessage(), dispatching keydown event...",
2527 this));
2528 bool dispatched = mDispatcher->DispatchKeyboardEvent(
2529 eKeyDown, keydownEvent, status, const_cast<NativeKey*>(this));
2530 if (aEventDispatched) {
2531 *aEventDispatched = dispatched;
2533 if (!dispatched) {
2534 // If the keydown event wasn't fired, there must be composition.
2535 // we don't need to do anything anymore.
2536 MOZ_LOG(
2537 gKeyLog, LogLevel::Info,
2538 ("%p NativeKey::HandleKeyDownMessage(), doesn't dispatch keypress "
2539 "event(s) because keydown event isn't dispatched actually",
2540 this));
2541 return false;
2543 defaultPrevented = status == nsEventStatus_eConsumeNoDefault;
2545 if (mWidget->Destroyed() || IsFocusedWindowChanged()) {
2546 MOZ_LOG(gKeyLog, LogLevel::Info,
2547 ("%p NativeKey::HandleKeyDownMessage(), keydown event caused "
2548 "destroying the widget",
2549 this));
2550 return true;
2553 MOZ_LOG(
2554 gKeyLog, LogLevel::Info,
2555 ("%p NativeKey::HandleKeyDownMessage(), dispatched keydown event, "
2556 "dispatched=%s, defaultPrevented=%s",
2557 this, GetBoolName(dispatched), GetBoolName(defaultPrevented)));
2559 // If IMC wasn't associated to the window but is associated it now (i.e.,
2560 // focus is moved from a non-editable editor to an editor by keydown
2561 // event handler), WM_CHAR and WM_SYSCHAR shouldn't cause first character
2562 // inputting if IME is opened. But then, we should redirect the native
2563 // keydown message to IME.
2564 // However, note that if focus has been already moved to another
2565 // application, we shouldn't redirect the message to it because the keydown
2566 // message is processed by us, so, nobody shouldn't process it.
2567 HWND focusedWnd = ::GetFocus();
2568 if (!defaultPrevented && !mFakeCharMsgs && focusedWnd && !isIMEEnabled &&
2569 WinUtils::IsIMEEnabled(mWidget->GetInputContext())) {
2570 RedirectedKeyDownMessageManager::RemoveNextCharMessage(focusedWnd);
2572 INPUT keyinput;
2573 keyinput.type = INPUT_KEYBOARD;
2574 keyinput.ki.wVk = mOriginalVirtualKeyCode;
2575 keyinput.ki.wScan = mScanCode;
2576 keyinput.ki.dwFlags = KEYEVENTF_SCANCODE;
2577 if (mIsExtended) {
2578 keyinput.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
2580 keyinput.ki.time = 0;
2581 keyinput.ki.dwExtraInfo = 0;
2583 RedirectedKeyDownMessageManager::WillRedirect(mMsg, defaultPrevented);
2585 MOZ_LOG(gKeyLog, LogLevel::Info,
2586 ("%p NativeKey::HandleKeyDownMessage(), redirecting %s...",
2587 this, ToString(mMsg).get()));
2589 ::SendInput(1, &keyinput, sizeof(keyinput));
2591 MOZ_LOG(gKeyLog, LogLevel::Info,
2592 ("%p NativeKey::HandleKeyDownMessage(), redirected %s", this,
2593 ToString(mMsg).get()));
2595 // Return here. We shouldn't dispatch keypress event for this WM_KEYDOWN.
2596 // If it's needed, it will be dispatched after next (redirected)
2597 // WM_KEYDOWN.
2598 return true;
2600 } else {
2601 MOZ_LOG(gKeyLog, LogLevel::Info,
2602 ("%p NativeKey::HandleKeyDownMessage(), received a redirected %s",
2603 this, ToString(mMsg).get()));
2605 defaultPrevented = RedirectedKeyDownMessageManager::DefaultPrevented();
2606 // If this is redirected keydown message, we have dispatched the keydown
2607 // event already.
2608 if (aEventDispatched) {
2609 *aEventDispatched = true;
2613 RedirectedKeyDownMessageManager::Forget();
2615 MOZ_ASSERT(!mWidget->Destroyed());
2617 // If the key was processed by IME and didn't cause WM_(SYS)CHAR messages, we
2618 // shouldn't dispatch keypress event.
2619 if (mOriginalVirtualKeyCode == VK_PROCESSKEY &&
2620 !IsFollowedByPrintableCharOrSysCharMessage()) {
2621 MOZ_LOG(gKeyLog, LogLevel::Info,
2622 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2623 "event because the key was already handled by IME, "
2624 "defaultPrevented=%s",
2625 this, GetBoolName(defaultPrevented)));
2626 return defaultPrevented;
2629 if (defaultPrevented) {
2630 MOZ_LOG(gKeyLog, LogLevel::Info,
2631 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2632 "event because preceding keydown event was consumed",
2633 this));
2634 return true;
2637 MOZ_ASSERT(!mCharMessageHasGone,
2638 "If following char message was consumed by somebody, "
2639 "keydown event should have been consumed before dispatch");
2641 // If mCommittedCharsAndModifiers was initialized with following char
2642 // messages, we should dispatch keypress events with its information.
2643 if (IsFollowedByPrintableCharOrSysCharMessage()) {
2644 MOZ_LOG(gKeyLog, LogLevel::Info,
2645 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2646 "keypress events with retrieved char messages...",
2647 this));
2648 return DispatchKeyPressEventsWithRetrievedCharMessages();
2651 // If we won't be getting a WM_CHAR, WM_SYSCHAR or WM_DEADCHAR, synthesize a
2652 // keypress for almost all keys
2653 if (NeedsToHandleWithoutFollowingCharMessages()) {
2654 MOZ_LOG(gKeyLog, LogLevel::Info,
2655 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2656 "keypress events...",
2657 this));
2658 return DispatchKeyPressEventsWithoutCharMessage();
2661 // If WM_KEYDOWN of VK_PACKET isn't followed by WM_CHAR, we don't need to
2662 // dispatch keypress events.
2663 if (mVirtualKeyCode == VK_PACKET) {
2664 MOZ_LOG(gKeyLog, LogLevel::Info,
2665 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2666 "event "
2667 "because the key is VK_PACKET and there are no char messages",
2668 this));
2669 return false;
2672 if (!mModKeyState.IsControl() && !mModKeyState.IsAlt() &&
2673 !mModKeyState.IsWin() && mIsPrintableKey) {
2674 // If this is simple KeyDown event but next message is not WM_CHAR,
2675 // this event may not input text, so we should ignore this event.
2676 // See bug 314130.
2677 MOZ_LOG(gKeyLog, LogLevel::Info,
2678 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2679 "event "
2680 "because the key event is simple printable key's event but not "
2681 "followed "
2682 "by char messages",
2683 this));
2684 return false;
2687 if (mIsDeadKey) {
2688 MOZ_LOG(gKeyLog, LogLevel::Info,
2689 ("%p NativeKey::HandleKeyDownMessage(), not dispatching keypress "
2690 "event "
2691 "because the key is a dead key and not followed by char messages",
2692 this));
2693 return false;
2696 MOZ_LOG(gKeyLog, LogLevel::Info,
2697 ("%p NativeKey::HandleKeyDownMessage(), tries to be dispatching "
2698 "keypress events due to no following char messages...",
2699 this));
2700 return DispatchKeyPressEventsWithoutCharMessage();
2703 bool NativeKey::HandleCharMessage(bool* aEventDispatched) const {
2704 MOZ_ASSERT(IsCharOrSysCharMessage(mMsg));
2705 return HandleCharMessage(mMsg, aEventDispatched);
2708 bool NativeKey::HandleCharMessage(const MSG& aCharMsg,
2709 bool* aEventDispatched) const {
2710 MOZ_ASSERT(IsKeyDownMessage() || IsCharOrSysCharMessage(mMsg));
2711 MOZ_ASSERT(IsCharOrSysCharMessage(aCharMsg.message));
2713 if (aEventDispatched) {
2714 *aEventDispatched = false;
2717 if ((IsCharOrSysCharMessage(mMsg) || IsEnterKeyPressCharMessage(mMsg)) &&
2718 IsAnotherInstanceRemovingCharMessage()) {
2719 MOZ_LOG(
2720 gKeyLog, LogLevel::Warning,
2721 ("%p NativeKey::HandleCharMessage(), WARNING, does nothing because "
2722 "the message should be handled in another instance removing this "
2723 "message",
2724 this));
2725 // Consume this for now because it will be handled by another instance.
2726 return true;
2729 // If the key combinations is reserved by the system, we shouldn't dispatch
2730 // eKeyPress event for it and passes the message to next wndproc.
2731 if (IsReservedBySystem()) {
2732 MOZ_LOG(gKeyLog, LogLevel::Info,
2733 ("%p NativeKey::HandleCharMessage(), doesn't dispatch keypress "
2734 "event because the key combination is reserved by the system",
2735 this));
2736 return false;
2739 // If the widget has gone, we should do nothing.
2740 if (mWidget->Destroyed()) {
2741 MOZ_LOG(gKeyLog, LogLevel::Warning,
2742 ("%p NativeKey::HandleCharMessage(), WARNING, not handled due to "
2743 "destroyed the widget",
2744 this));
2745 return false;
2748 // When a control key is inputted by a key, it should be handled without
2749 // WM_*CHAR messages at receiving WM_*KEYDOWN message. So, when we receive
2750 // WM_*CHAR message directly, we see a control character here.
2751 // Note that when the char is '\r', it means that the char message should
2752 // cause "Enter" keypress event for inserting a line break.
2753 if (IsControlCharMessage(aCharMsg) && !IsEnterKeyPressCharMessage(aCharMsg)) {
2754 // In this case, we don't need to dispatch eKeyPress event because:
2755 // 1. We're the only browser which dispatches "keypress" event for
2756 // non-printable characters (Although, both Chrome and Edge dispatch
2757 // "keypress" event for some keys accidentally. For example, "IntlRo"
2758 // key with Ctrl of Japanese keyboard layout).
2759 // 2. Currently, we may handle shortcut keys with "keydown" event if
2760 // it's reserved or something. So, we shouldn't dispatch "keypress"
2761 // event without it.
2762 // Note that this does NOT mean we stop dispatching eKeyPress event for
2763 // key presses causes a control character when Ctrl is pressed. In such
2764 // case, DispatchKeyPressEventsWithoutCharMessage() dispatches eKeyPress
2765 // instead of this method.
2766 MOZ_LOG(
2767 gKeyLog, LogLevel::Info,
2768 ("%p NativeKey::HandleCharMessage(), doesn't dispatch keypress "
2769 "event because received a control character input without WM_KEYDOWN",
2770 this));
2771 return false;
2774 // XXXmnakano I think that if mMsg is WM_CHAR, i.e., it comes without
2775 // preceding WM_KEYDOWN, we should should dispatch composition
2776 // events instead of eKeyPress because they are not caused by
2777 // actual keyboard operation.
2779 // First, handle normal text input or non-printable key case here.
2780 WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget);
2781 if (IsEnterKeyPressCharMessage(aCharMsg)) {
2782 keypressEvent.mKeyCode = NS_VK_RETURN;
2783 } else {
2784 keypressEvent.mCharCode = static_cast<uint32_t>(aCharMsg.wParam);
2786 nsresult rv = mDispatcher->BeginNativeInputTransaction();
2787 if (NS_WARN_IF(NS_FAILED(rv))) {
2788 MOZ_LOG(gKeyLog, LogLevel::Error,
2789 ("%p NativeKey::HandleCharMessage(), FAILED due to "
2790 "BeginNativeInputTransaction() failure",
2791 this));
2792 return true;
2795 MOZ_LOG(gKeyLog, LogLevel::Debug,
2796 ("%p NativeKey::HandleCharMessage(), initializing keypress "
2797 "event...",
2798 this));
2800 ModifierKeyState modKeyState(mModKeyState);
2801 // When AltGr is pressed, both Alt and Ctrl are active. However, when they
2802 // are active, TextEditor won't treat the keypress event as inputting a
2803 // character. Therefore, when AltGr is pressed and the key tries to input
2804 // a character, let's set them to false.
2805 if (modKeyState.IsControl() && modKeyState.IsAlt() &&
2806 IsPrintableCharMessage(aCharMsg)) {
2807 modKeyState.Unset(MODIFIER_ALT | MODIFIER_CONTROL);
2809 nsEventStatus status = InitKeyEvent(keypressEvent, modKeyState);
2810 MOZ_LOG(gKeyLog, LogLevel::Info,
2811 ("%p NativeKey::HandleCharMessage(), dispatching keypress event...",
2812 this));
2813 bool dispatched = mDispatcher->MaybeDispatchKeypressEvents(
2814 keypressEvent, status, const_cast<NativeKey*>(this));
2815 if (aEventDispatched) {
2816 *aEventDispatched = dispatched;
2818 if (mWidget->Destroyed()) {
2819 MOZ_LOG(gKeyLog, LogLevel::Info,
2820 ("%p NativeKey::HandleCharMessage(), keypress event caused "
2821 "destroying the widget",
2822 this));
2823 return true;
2825 bool consumed = status == nsEventStatus_eConsumeNoDefault;
2826 MOZ_LOG(gKeyLog, LogLevel::Info,
2827 ("%p NativeKey::HandleCharMessage(), dispatched keypress event, "
2828 "dispatched=%s, consumed=%s",
2829 this, GetBoolName(dispatched), GetBoolName(consumed)));
2830 return consumed;
2833 bool NativeKey::HandleKeyUpMessage(bool* aEventDispatched) const {
2834 MOZ_ASSERT(IsKeyUpMessage());
2836 if (aEventDispatched) {
2837 *aEventDispatched = false;
2840 // If the key combinations is reserved by the system, we shouldn't dispatch
2841 // eKeyUp event for it and passes the message to next wndproc.
2842 if (IsReservedBySystem()) {
2843 MOZ_LOG(gKeyLog, LogLevel::Info,
2844 ("%p NativeKey::HandleKeyUpMessage(), doesn't dispatch keyup "
2845 "event because the key combination is reserved by the system",
2846 this));
2847 return false;
2850 if (sPendingHighSurrogate) {
2851 MOZ_LOG(gKeyLog, LogLevel::Info,
2852 ("%p NativeKey::HandleKeyUpMessage(), doesn't dispatch keyup "
2853 "event because the key introduced only a high surrotate, so we "
2854 "should wait the following low surrogate input",
2855 this));
2856 return false;
2859 // If the widget has gone, we should do nothing.
2860 if (mWidget->Destroyed()) {
2861 MOZ_LOG(
2862 gKeyLog, LogLevel::Warning,
2863 ("%p NativeKey::HandleKeyUpMessage(), WARNING, not handled due to "
2864 "destroyed the widget",
2865 this));
2866 return false;
2869 nsresult rv = mDispatcher->BeginNativeInputTransaction();
2870 if (NS_WARN_IF(NS_FAILED(rv))) {
2871 MOZ_LOG(gKeyLog, LogLevel::Error,
2872 ("%p NativeKey::HandleKeyUpMessage(), FAILED due to "
2873 "BeginNativeInputTransaction() failure",
2874 this));
2875 return true;
2878 MOZ_LOG(gKeyLog, LogLevel::Debug,
2879 ("%p NativeKey::HandleKeyUpMessage(), initializing keyup event...",
2880 this));
2881 WidgetKeyboardEvent keyupEvent(true, eKeyUp, mWidget);
2882 nsEventStatus status = InitKeyEvent(keyupEvent, mModKeyState);
2883 MOZ_LOG(gKeyLog, LogLevel::Info,
2884 ("%p NativeKey::HandleKeyUpMessage(), dispatching keyup event...",
2885 this));
2886 bool dispatched = mDispatcher->DispatchKeyboardEvent(
2887 eKeyUp, keyupEvent, status, const_cast<NativeKey*>(this));
2888 if (aEventDispatched) {
2889 *aEventDispatched = dispatched;
2891 if (mWidget->Destroyed()) {
2892 MOZ_LOG(gKeyLog, LogLevel::Info,
2893 ("%p NativeKey::HandleKeyUpMessage(), keyup event caused "
2894 "destroying the widget",
2895 this));
2896 return true;
2898 bool consumed = status == nsEventStatus_eConsumeNoDefault;
2899 MOZ_LOG(gKeyLog, LogLevel::Info,
2900 ("%p NativeKey::HandleKeyUpMessage(), dispatched keyup event, "
2901 "dispatched=%s, consumed=%s",
2902 this, GetBoolName(dispatched), GetBoolName(consumed)));
2903 return consumed;
2906 bool NativeKey::NeedsToHandleWithoutFollowingCharMessages() const {
2907 MOZ_ASSERT(IsKeyDownMessage());
2909 // If the key combination is reserved by the system, the caller shouldn't
2910 // do anything with following WM_*CHAR messages. So, let's return true here.
2911 if (IsReservedBySystem()) {
2912 return true;
2915 // If the keydown message is generated for inputting some Unicode characters
2916 // via SendInput() API, we need to handle it only with WM_*CHAR messages.
2917 if (mVirtualKeyCode == VK_PACKET) {
2918 return false;
2921 // If following char message is for a control character, it should be handled
2922 // without WM_CHAR message. This is typically Ctrl + [a-z].
2923 if (mFollowingCharMsgs.Length() == 1 &&
2924 IsControlCharMessage(mFollowingCharMsgs[0])) {
2925 return true;
2928 // If keydown message is followed by WM_CHAR or WM_SYSCHAR whose wParam isn't
2929 // a control character, we should dispatch keypress event with the char
2930 // message even with any modifier state.
2931 if (IsFollowedByPrintableCharOrSysCharMessage()) {
2932 return false;
2935 // If any modifier keys which may cause printable keys becoming non-printable
2936 // are not pressed, we don't need special handling for the key.
2937 // Note that if the key does not produce a character with AltGr and when
2938 // AltGr key is pressed, we don't need to dispatch eKeyPress event for it
2939 // because AltGr shouldn't be used for a modifier for a shortcut without
2940 // Ctrl, Alt or Win. That means that we should treat it in same path for
2941 // Shift key.
2942 if (!mModKeyState.IsControl() && !mModKeyState.IsAlt() &&
2943 !mModKeyState.IsWin()) {
2944 return false;
2947 // If the key event causes dead key event, we don't need to dispatch keypress
2948 // event.
2949 if (mIsDeadKey && mCommittedCharsAndModifiers.IsEmpty()) {
2950 return false;
2953 // Even if the key is a printable key, it might cause non-printable character
2954 // input with modifier key(s).
2955 return mIsPrintableKey;
2958 static nsCString GetResultOfInSendMessageEx() {
2959 DWORD ret = ::InSendMessageEx(nullptr);
2960 if (!ret) {
2961 return "ISMEX_NOSEND"_ns;
2963 nsCString result;
2964 if (ret & ISMEX_CALLBACK) {
2965 result = "ISMEX_CALLBACK";
2967 if (ret & ISMEX_NOTIFY) {
2968 if (!result.IsEmpty()) {
2969 result += " | ";
2971 result += "ISMEX_NOTIFY";
2973 if (ret & ISMEX_REPLIED) {
2974 if (!result.IsEmpty()) {
2975 result += " | ";
2977 result += "ISMEX_REPLIED";
2979 if (ret & ISMEX_SEND) {
2980 if (!result.IsEmpty()) {
2981 result += " | ";
2983 result += "ISMEX_SEND";
2985 return result;
2988 bool NativeKey::MayBeSameCharMessage(const MSG& aCharMsg1,
2989 const MSG& aCharMsg2) const {
2990 // NOTE: Although, we don't know when this case occurs, the scan code value
2991 // in lParam may be changed from 0 to something. The changed value
2992 // is different from the scan code of handling keydown message.
2993 static const LPARAM kScanCodeMask = 0x00FF0000;
2994 return aCharMsg1.message == aCharMsg2.message &&
2995 aCharMsg1.wParam == aCharMsg2.wParam &&
2996 (aCharMsg1.lParam & ~kScanCodeMask) ==
2997 (aCharMsg2.lParam & ~kScanCodeMask);
3000 bool NativeKey::IsSamePhysicalKeyMessage(const MSG& aKeyOrCharMsg1,
3001 const MSG& aKeyOrCharMsg2) const {
3002 if (NS_WARN_IF(aKeyOrCharMsg1.message < WM_KEYFIRST) ||
3003 NS_WARN_IF(aKeyOrCharMsg1.message > WM_KEYLAST) ||
3004 NS_WARN_IF(aKeyOrCharMsg2.message < WM_KEYFIRST) ||
3005 NS_WARN_IF(aKeyOrCharMsg2.message > WM_KEYLAST)) {
3006 return false;
3008 return WinUtils::GetScanCode(aKeyOrCharMsg1.lParam) ==
3009 WinUtils::GetScanCode(aKeyOrCharMsg2.lParam) &&
3010 WinUtils::IsExtendedScanCode(aKeyOrCharMsg1.lParam) ==
3011 WinUtils::IsExtendedScanCode(aKeyOrCharMsg2.lParam);
3014 bool NativeKey::GetFollowingCharMessage(MSG& aCharMsg) {
3015 MOZ_ASSERT(IsKeyDownMessage());
3017 aCharMsg.message = WM_NULL;
3019 if (mFakeCharMsgs) {
3020 for (size_t i = 0; i < mFakeCharMsgs->Length(); i++) {
3021 FakeCharMsg& fakeCharMsg = mFakeCharMsgs->ElementAt(i);
3022 if (fakeCharMsg.mConsumed) {
3023 continue;
3025 MSG charMsg = fakeCharMsg.GetCharMsg(mMsg.hwnd);
3026 fakeCharMsg.mConsumed = true;
3027 if (!IsCharMessage(charMsg)) {
3028 return false;
3030 aCharMsg = charMsg;
3031 return true;
3033 return false;
3036 // If next key message is not char message, we should give up to find a
3037 // related char message for the handling keydown event for now.
3038 // Note that it's possible other applications may send other key message
3039 // after we call TranslateMessage(). That may cause PeekMessage() failing
3040 // to get char message for the handling keydown message.
3041 MSG nextKeyMsg;
3042 if (!WinUtils::PeekMessage(&nextKeyMsg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST,
3043 PM_NOREMOVE | PM_NOYIELD) ||
3044 !IsCharMessage(nextKeyMsg)) {
3045 MOZ_LOG(gKeyLog, LogLevel::Debug,
3046 ("%p NativeKey::GetFollowingCharMessage(), there are no char "
3047 "messages",
3048 this));
3049 return false;
3051 const MSG kFoundCharMsg = nextKeyMsg;
3053 AutoRestore<MSG> saveLastRemovingMsg(mRemovingMsg);
3054 mRemovingMsg = nextKeyMsg;
3056 mReceivedMsg = sEmptyMSG;
3057 AutoRestore<MSG> ensureToClearRecivedMsg(mReceivedMsg);
3059 // On Metrofox, PeekMessage() sometimes returns WM_NULL even if we specify
3060 // the message range. So, if it returns WM_NULL, we should retry to get
3061 // the following char message it was found above.
3062 for (uint32_t i = 0; i < 50; i++) {
3063 MSG removedMsg, nextKeyMsgInAllWindows;
3064 bool doCrash = false;
3065 if (!WinUtils::PeekMessage(&removedMsg, mMsg.hwnd, nextKeyMsg.message,
3066 nextKeyMsg.message, PM_REMOVE | PM_NOYIELD)) {
3067 // We meets unexpected case. We should collect the message queue state
3068 // and crash for reporting the bug.
3069 doCrash = true;
3071 // If another instance was created for the removing message during trying
3072 // to remove a char message, the instance didn't handle it for preventing
3073 // recursive handling. So, let's handle it in this instance.
3074 if (!IsEmptyMSG(mReceivedMsg)) {
3075 // If focus is moved to different window, we shouldn't handle it on
3076 // the widget. Let's discard it for now.
3077 if (mReceivedMsg.hwnd != nextKeyMsg.hwnd) {
3078 MOZ_LOG(
3079 gKeyLog, LogLevel::Warning,
3080 ("%p NativeKey::GetFollowingCharMessage(), WARNING, received a "
3081 "char message during removing it from the queue, but it's for "
3082 "different window, mReceivedMsg=%s, nextKeyMsg=%s, "
3083 "kFoundCharMsg=%s",
3084 this, ToString(mReceivedMsg).get(), ToString(nextKeyMsg).get(),
3085 ToString(kFoundCharMsg).get()));
3086 // There might still exist char messages, the loop of calling
3087 // this method should be continued.
3088 aCharMsg.message = WM_NULL;
3089 return true;
3091 // Even if the received message is different from what we tried to
3092 // remove from the queue, let's take the received message as a part of
3093 // the result of this key sequence.
3094 if (mReceivedMsg.message != nextKeyMsg.message ||
3095 mReceivedMsg.wParam != nextKeyMsg.wParam ||
3096 mReceivedMsg.lParam != nextKeyMsg.lParam) {
3097 MOZ_LOG(
3098 gKeyLog, LogLevel::Warning,
3099 ("%p NativeKey::GetFollowingCharMessage(), WARNING, received a "
3100 "char message during removing it from the queue, but it's "
3101 "differnt from what trying to remove from the queue, "
3102 "aCharMsg=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3103 this, ToString(mReceivedMsg).get(), ToString(nextKeyMsg).get(),
3104 ToString(kFoundCharMsg).get()));
3105 } else {
3106 MOZ_LOG(
3107 gKeyLog, LogLevel::Debug,
3108 ("%p NativeKey::GetFollowingCharMessage(), succeeded to "
3109 "retrieve next char message via another instance, aCharMsg=%s, "
3110 "kFoundCharMsg=%s",
3111 this, ToString(mReceivedMsg).get(),
3112 ToString(kFoundCharMsg).get()));
3114 aCharMsg = mReceivedMsg;
3115 return true;
3118 // The char message is redirected to different thread's window by focus
3119 // move or something or just cancelled by external application.
3120 if (!WinUtils::PeekMessage(&nextKeyMsgInAllWindows, 0, WM_KEYFIRST,
3121 WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD)) {
3122 MOZ_LOG(
3123 gKeyLog, LogLevel::Warning,
3124 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3125 "remove a char message, but it's already gone from all message "
3126 "queues, nextKeyMsg=%s, kFoundCharMsg=%s",
3127 this, ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3128 return true; // XXX should return false in this case
3130 // The next key message is redirected to different window created by our
3131 // thread, we should do nothing because we must not have focus.
3132 if (nextKeyMsgInAllWindows.hwnd != mMsg.hwnd) {
3133 aCharMsg = nextKeyMsgInAllWindows;
3134 MOZ_LOG(
3135 gKeyLog, LogLevel::Warning,
3136 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3137 "remove a char message, but found in another message queue, "
3138 "nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3139 this, ToString(nextKeyMsgInAllWindows).get(),
3140 ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3141 return true;
3143 // If next key message becomes non-char message, this key operation
3144 // may have already been consumed or canceled.
3145 if (!IsCharMessage(nextKeyMsgInAllWindows)) {
3146 MOZ_LOG(
3147 gKeyLog, LogLevel::Warning,
3148 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3149 "remove a char message and next key message becomes non-char "
3150 "message, nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, "
3151 "kFoundCharMsg=%s",
3152 this, ToString(nextKeyMsgInAllWindows).get(),
3153 ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3154 MOZ_ASSERT(!mCharMessageHasGone);
3155 mFollowingCharMsgs.Clear();
3156 mCharMessageHasGone = true;
3157 return false;
3159 // If next key message is still a char message but different key message,
3160 // we should treat current key operation is consumed or canceled and
3161 // next char message should be handled as an orphan char message later.
3162 if (!IsSamePhysicalKeyMessage(nextKeyMsgInAllWindows, kFoundCharMsg)) {
3163 MOZ_LOG(
3164 gKeyLog, LogLevel::Warning,
3165 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3166 "remove a char message and next key message becomes differnt "
3167 "key's char message, nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, "
3168 "kFoundCharMsg=%s",
3169 this, ToString(nextKeyMsgInAllWindows).get(),
3170 ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3171 MOZ_ASSERT(!mCharMessageHasGone);
3172 mFollowingCharMsgs.Clear();
3173 mCharMessageHasGone = true;
3174 return false;
3176 // If next key message is still a char message but the message is changed,
3177 // we should retry to remove the new message with PeekMessage() again.
3178 if (nextKeyMsgInAllWindows.message != nextKeyMsg.message) {
3179 MOZ_LOG(
3180 gKeyLog, LogLevel::Warning,
3181 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3182 "remove a char message due to message change, let's retry to "
3183 "remove the message with newly found char message, "
3184 "nextKeyMsgInAllWindows=%s, nextKeyMsg=%s, kFoundCharMsg=%s",
3185 this, ToString(nextKeyMsgInAllWindows).get(),
3186 ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3187 nextKeyMsg = nextKeyMsgInAllWindows;
3188 continue;
3190 // If there is still existing a char message caused by same physical key
3191 // in the queue but PeekMessage(PM_REMOVE) failed to remove it from the
3192 // queue, it might be possible that the odd keyboard layout or utility
3193 // hooks only PeekMessage(PM_NOREMOVE) and GetMessage(). So, let's try
3194 // remove the char message with GetMessage() again.
3195 // FYI: The wParam might be different from the found message, but it's
3196 // okay because we assume that odd keyboard layouts return actual
3197 // inputting character at removing the char message.
3198 if (WinUtils::GetMessage(&removedMsg, mMsg.hwnd, nextKeyMsg.message,
3199 nextKeyMsg.message)) {
3200 MOZ_LOG(
3201 gKeyLog, LogLevel::Warning,
3202 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3203 "remove a char message, but succeeded with GetMessage(), "
3204 "removedMsg=%s, kFoundCharMsg=%s",
3205 this, ToString(removedMsg).get(), ToString(kFoundCharMsg).get()));
3206 // Cancel to crash, but we need to check the removed message value.
3207 doCrash = false;
3209 // If we've already removed some WM_NULL messages from the queue and
3210 // the found message has already gone from the queue, let's treat the key
3211 // as inputting no characters and already consumed.
3212 else if (i > 0) {
3213 MOZ_LOG(
3214 gKeyLog, LogLevel::Warning,
3215 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3216 "remove a char message, but removed %d WM_NULL messages",
3217 this, i));
3218 // If the key is a printable key or a control key but tried to input
3219 // a character, mark mCharMessageHasGone true for handling the keydown
3220 // event as inputting empty string.
3221 MOZ_ASSERT(!mCharMessageHasGone);
3222 mFollowingCharMsgs.Clear();
3223 mCharMessageHasGone = true;
3224 return false;
3226 MOZ_LOG(gKeyLog, LogLevel::Error,
3227 ("%p NativeKey::GetFollowingCharMessage(), FAILED, lost target "
3228 "message to remove, nextKeyMsg=%s",
3229 this, ToString(nextKeyMsg).get()));
3232 if (doCrash) {
3233 nsPrintfCString info(
3234 "\nPeekMessage() failed to remove char message! "
3235 "\nActive keyboard layout=0x%p (%s), "
3236 "\nHandling message: %s, InSendMessageEx()=%s, "
3237 "\nFound message: %s, "
3238 "\nWM_NULL has been removed: %d, "
3239 "\nNext key message in all windows: %s, "
3240 "time=%ld, ",
3241 KeyboardLayout::GetInstance()->GetLoadedLayout(),
3242 KeyboardLayout::GetInstance()->GetLoadedLayoutName().get(),
3243 ToString(mMsg).get(), GetResultOfInSendMessageEx().get(),
3244 ToString(kFoundCharMsg).get(), i,
3245 ToString(nextKeyMsgInAllWindows).get(), nextKeyMsgInAllWindows.time);
3246 CrashReporter::AppendAppNotesToCrashReport(info);
3247 MSG nextMsg;
3248 if (WinUtils::PeekMessage(&nextMsg, 0, 0, 0, PM_NOREMOVE | PM_NOYIELD)) {
3249 nsPrintfCString info("\nNext message in all windows: %s, time=%ld",
3250 ToString(nextMsg).get(), nextMsg.time);
3251 CrashReporter::AppendAppNotesToCrashReport(info);
3252 } else {
3253 CrashReporter::AppendAppNotesToCrashReport(
3254 "\nThere is no message in any window"_ns);
3257 MOZ_CRASH("We lost the following char message");
3260 // We're still not sure why ::PeekMessage() may return WM_NULL even with
3261 // its first message and its last message are same message. However,
3262 // at developing Metrofox, we met this case even with usual keyboard
3263 // layouts. So, it might be possible in desktop application or it really
3264 // occurs with some odd keyboard layouts which perhaps hook API.
3265 if (removedMsg.message == WM_NULL) {
3266 MOZ_LOG(gKeyLog, LogLevel::Warning,
3267 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3268 "remove a char message, instead, removed WM_NULL message, "
3269 "removedMsg=%s",
3270 this, ToString(removedMsg).get()));
3271 // Check if there is the message which we're trying to remove.
3272 MSG newNextKeyMsg;
3273 if (!WinUtils::PeekMessage(&newNextKeyMsg, mMsg.hwnd, WM_KEYFIRST,
3274 WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD)) {
3275 // If there is no key message, we should mark this keydown as consumed
3276 // because the key operation may have already been handled or canceled.
3277 MOZ_LOG(
3278 gKeyLog, LogLevel::Warning,
3279 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3280 "remove a char message because it's gone during removing it from "
3281 "the queue, nextKeyMsg=%s, kFoundCharMsg=%s",
3282 this, ToString(nextKeyMsg).get(), ToString(kFoundCharMsg).get()));
3283 MOZ_ASSERT(!mCharMessageHasGone);
3284 mFollowingCharMsgs.Clear();
3285 mCharMessageHasGone = true;
3286 return false;
3288 if (!IsCharMessage(newNextKeyMsg)) {
3289 // If next key message becomes a non-char message, we should mark this
3290 // keydown as consumed because the key operation may have already been
3291 // handled or canceled.
3292 MOZ_LOG(
3293 gKeyLog, LogLevel::Warning,
3294 ("%p NativeKey::GetFollowingCharMessage(), WARNING, failed to "
3295 "remove a char message because it's gone during removing it from "
3296 "the queue, nextKeyMsg=%s, newNextKeyMsg=%s, kFoundCharMsg=%s",
3297 this, ToString(nextKeyMsg).get(), ToString(newNextKeyMsg).get(),
3298 ToString(kFoundCharMsg).get()));
3299 MOZ_ASSERT(!mCharMessageHasGone);
3300 mFollowingCharMsgs.Clear();
3301 mCharMessageHasGone = true;
3302 return false;
3304 MOZ_LOG(
3305 gKeyLog, LogLevel::Debug,
3306 ("%p NativeKey::GetFollowingCharMessage(), there is the message "
3307 "which is being tried to be removed from the queue, trying again...",
3308 this));
3309 continue;
3312 // wParam of WM_DEADCHAR may be 0, but for the other char messages, this is
3313 // an odd case because it means that the key event shouldn't cause text
3314 // input. So, let's ignore the strange char message.
3315 if (removedMsg.message != WM_DEADCHAR &&
3316 removedMsg.message == nextKeyMsg.message && !removedMsg.wParam) {
3317 MOZ_LOG(
3318 gKeyLog, LogLevel::Warning,
3319 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3320 "remove a char message, but the removed message's wParam is 0, "
3321 "removedMsg=%s",
3322 this, ToString(removedMsg).get()));
3323 return false;
3326 // This is normal case.
3327 if (MayBeSameCharMessage(removedMsg, nextKeyMsg)) {
3328 aCharMsg = removedMsg;
3329 MOZ_LOG(
3330 gKeyLog, LogLevel::Debug,
3331 ("%p NativeKey::GetFollowingCharMessage(), succeeded to retrieve "
3332 "next char message, aCharMsg=%s",
3333 this, ToString(aCharMsg).get()));
3334 return true;
3337 // Even if removed message is different char message from the found char
3338 // message, when the scan code is same, we can assume that the message
3339 // is overwritten by somebody who hooks API. See bug 1336028 comment 0 for
3340 // the possible scenarios.
3341 if (IsCharMessage(removedMsg) &&
3342 IsSamePhysicalKeyMessage(removedMsg, nextKeyMsg)) {
3343 aCharMsg = removedMsg;
3344 MOZ_LOG(
3345 gKeyLog, LogLevel::Warning,
3346 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3347 "remove a char message, but the removed message was changed from "
3348 "the found message except their scancode, aCharMsg=%s, "
3349 "nextKeyMsg=%s, kFoundCharMsg=%s",
3350 this, ToString(aCharMsg).get(), ToString(nextKeyMsg).get(),
3351 ToString(kFoundCharMsg).get()));
3352 return true;
3355 // When found message's wParam is 0 and its scancode is 0xFF, we may remove
3356 // usual char message actually. In such case, we should use the removed
3357 // char message.
3358 if (IsCharMessage(removedMsg) && !nextKeyMsg.wParam &&
3359 WinUtils::GetScanCode(nextKeyMsg.lParam) == 0xFF) {
3360 aCharMsg = removedMsg;
3361 MOZ_LOG(
3362 gKeyLog, LogLevel::Warning,
3363 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3364 "remove a char message, but the removed message was changed from "
3365 "the found message but the found message was odd, so, ignoring the "
3366 "odd found message and respecting the removed message, aCharMsg=%s, "
3367 "nextKeyMsg=%s, kFoundCharMsg=%s",
3368 this, ToString(aCharMsg).get(), ToString(nextKeyMsg).get(),
3369 ToString(kFoundCharMsg).get()));
3370 return true;
3373 const auto nextKeyMsgAfter = [&]() -> Maybe<MSG> {
3374 MSG nextKeyMsgAfter;
3375 if (WinUtils::PeekMessage(&nextKeyMsgAfter, mMsg.hwnd, WM_KEYFIRST,
3376 WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD)) {
3377 return Some(nextKeyMsgAfter);
3379 return Nothing();
3380 }();
3382 // We have some crash reports for Ethiopic Syllable. In the case, we found
3383 // a WM_DEADCHAR whose wParam may be not NULL, first. Then, we remove the
3384 // message from the queue. However, the keyboard or IME makes us to remove
3385 // a dummy message whose wParam (inputting character) is NULL and scancode
3386 // is 0. Finally, the found message keeps staying in the queue. The crash
3387 // report comes after we started accepting WM_DEADCHAR with NULL character
3388 // above. However, we haven't handled only a part of dead key sequence but
3389 // the user tried to input Ethiopic Syllable. Therefore, it's hard to guess
3390 // that the user starts using Firefox with the keyboard/IME at the timing
3391 // and the the user keeps using Firefox with unfunctional keyboard/IME.
3392 // Perhaps the keyboard/IME does something different hack for us to make
3393 // itself available on Firefox. Therefore, we should just return `false` to
3394 // make the caller give up removing the following messages.
3395 if (IsDeadCharMessage(removedMsg.message) && !removedMsg.wParam &&
3396 !WinUtils::GetScanCode(removedMsg.lParam) && nextKeyMsgAfter.isSome() &&
3397 MayBeSameCharMessage(nextKeyMsgAfter.ref(), nextKeyMsg)) {
3398 MOZ_LOG(
3399 gKeyLog, LogLevel::Warning,
3400 ("%p NativeKey::GetFollowingCharMessage(), WARNING, succeeded to "
3401 "remove a dead char message, but the removed message's wParam is 0 "
3402 "and the found message still in the queue, nextKeyMsg=%s but "
3403 "removedMsg=%s",
3404 this, ToString(nextKeyMsg).get(), ToString(removedMsg).get()));
3405 return false;
3408 // NOTE: Although, we don't know when this case occurs, the scan code value
3409 // in lParam may be changed from 0 to something. The changed value
3410 // is different from the scan code of handling keydown message.
3411 MOZ_LOG(
3412 gKeyLog, LogLevel::Error,
3413 ("%p NativeKey::GetFollowingCharMessage(), FAILED, removed message "
3414 "is really different from what we have already found, removedMsg=%s, "
3415 "nextKeyMsg=%s, kFoundCharMsg=%s",
3416 this, ToString(removedMsg).get(), ToString(nextKeyMsg).get(),
3417 ToString(kFoundCharMsg).get()));
3418 nsPrintfCString info(
3419 "\nPeekMessage() removed unexpcted char message! "
3420 "\nActive keyboard layout=0x%p (%s), "
3421 "\nHandling message: %s, InSendMessageEx()=%s, "
3422 "\nFound message: %s, "
3423 "\nRemoved message: %s, ",
3424 KeyboardLayout::GetInstance()->GetLoadedLayout(),
3425 KeyboardLayout::GetInstance()->GetLoadedLayoutName().get(),
3426 ToString(mMsg).get(), GetResultOfInSendMessageEx().get(),
3427 ToString(kFoundCharMsg).get(), ToString(removedMsg).get());
3428 CrashReporter::AppendAppNotesToCrashReport(info);
3429 // What's the next key message?
3430 if (nextKeyMsgAfter.isSome()) {
3431 nsPrintfCString info(
3432 "\nNext key message after unexpected char message "
3433 "removed: %s, ",
3434 ToString(nextKeyMsgAfter.ref()).get());
3435 CrashReporter::AppendAppNotesToCrashReport(info);
3436 } else {
3437 CrashReporter::AppendAppNotesToCrashReport(
3438 nsLiteralCString("\nThere is no key message after unexpected char "
3439 "message removed, "));
3441 // Another window has a key message?
3442 if (WinUtils::PeekMessage(&nextKeyMsgInAllWindows, 0, WM_KEYFIRST,
3443 WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD)) {
3444 nsPrintfCString info("\nNext key message in all windows: %s.",
3445 ToString(nextKeyMsgInAllWindows).get());
3446 CrashReporter::AppendAppNotesToCrashReport(info);
3447 } else {
3448 CrashReporter::AppendAppNotesToCrashReport(
3449 "\nThere is no key message in any windows."_ns);
3452 MOZ_CRASH("PeekMessage() removed unexpected message");
3454 MOZ_LOG(
3455 gKeyLog, LogLevel::Error,
3456 ("%p NativeKey::GetFollowingCharMessage(), FAILED, removed messages "
3457 "are all WM_NULL, nextKeyMsg=%s",
3458 this, ToString(nextKeyMsg).get()));
3459 nsPrintfCString info(
3460 "\nWe lost following char message! "
3461 "\nActive keyboard layout=0x%p (%s), "
3462 "\nHandling message: %s, InSendMessageEx()=%s, \n"
3463 "Found message: %s, removed a lot of WM_NULL",
3464 KeyboardLayout::GetInstance()->GetLoadedLayout(),
3465 KeyboardLayout::GetInstance()->GetLoadedLayoutName().get(),
3466 ToString(mMsg).get(), GetResultOfInSendMessageEx().get(),
3467 ToString(kFoundCharMsg).get());
3468 CrashReporter::AppendAppNotesToCrashReport(info);
3469 MOZ_CRASH("We lost the following char message");
3470 return false;
3473 void NativeKey::ComputeInputtingStringWithKeyboardLayout() {
3474 KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
3476 if (KeyboardLayout::IsPrintableCharKey(mVirtualKeyCode) ||
3477 mCharMessageHasGone) {
3478 mInputtingStringAndModifiers = mCommittedCharsAndModifiers;
3479 } else {
3480 mInputtingStringAndModifiers.Clear();
3482 mShiftedString.Clear();
3483 mUnshiftedString.Clear();
3484 mShiftedLatinChar = mUnshiftedLatinChar = 0;
3486 // XXX How about when Win key is pressed?
3487 if (!mModKeyState.IsControl() && !mModKeyState.IsAlt()) {
3488 return;
3491 // If user is inputting a Unicode character with typing Alt + Numpad
3492 // keys, we shouldn't set alternative key codes because the key event
3493 // shouldn't match with a mnemonic. However, we should set only
3494 // mUnshiftedString for keeping traditional behavior at WM_SYSKEYDOWN.
3495 // FYI: I guess that it's okay that mUnshiftedString stays empty. So,
3496 // if its value breaks something, you must be able to just return here.
3497 if (MaybeTypingUnicodeScalarValue()) {
3498 if (!mCommittedCharsAndModifiers.IsEmpty()) {
3499 MOZ_ASSERT(mMsg.message == WM_SYSKEYDOWN);
3500 char16_t num = mCommittedCharsAndModifiers.CharAt(0);
3501 MOZ_ASSERT(num >= '0' && num <= '9');
3502 mUnshiftedString.Append(num, MODIFIER_NONE);
3503 return;
3505 // If user presses a function key without NumLock or 3rd party utility
3506 // synthesizes a function key on numpad, we should handle it as-is because
3507 // the user's intention may be performing `Alt` + foo.
3508 MOZ_ASSERT(!KeyboardLayout::IsPrintableCharKey(mVirtualKeyCode));
3509 return;
3512 ModifierKeyState capsLockState(mModKeyState.GetModifiers() &
3513 MODIFIER_CAPSLOCK);
3515 mUnshiftedString =
3516 keyboardLayout->GetUniCharsAndModifiers(mVirtualKeyCode, capsLockState);
3517 capsLockState.Set(MODIFIER_SHIFT);
3518 mShiftedString =
3519 keyboardLayout->GetUniCharsAndModifiers(mVirtualKeyCode, capsLockState);
3521 // The current keyboard cannot input alphabets or numerics,
3522 // we should append them for Shortcut/Access keys.
3523 // E.g., for Cyrillic keyboard layout.
3524 capsLockState.Unset(MODIFIER_SHIFT);
3525 WidgetUtils::GetLatinCharCodeForKeyCode(
3526 mDOMKeyCode, capsLockState.GetModifiers(), &mUnshiftedLatinChar,
3527 &mShiftedLatinChar);
3529 // If the mShiftedLatinChar isn't 0, the key code is NS_VK_[A-Z].
3530 if (mShiftedLatinChar) {
3531 // If the produced characters of the key on current keyboard layout
3532 // are same as computed Latin characters, we shouldn't append the
3533 // Latin characters to alternativeCharCode.
3534 if (mUnshiftedLatinChar == mUnshiftedString.CharAt(0) &&
3535 mShiftedLatinChar == mShiftedString.CharAt(0)) {
3536 mShiftedLatinChar = mUnshiftedLatinChar = 0;
3538 } else if (mUnshiftedLatinChar) {
3539 // If the mShiftedLatinChar is 0, the mKeyCode doesn't produce
3540 // alphabet character. At that time, the character may be produced
3541 // with Shift key. E.g., on French keyboard layout, NS_VK_PERCENT
3542 // key produces LATIN SMALL LETTER U WITH GRAVE (U+00F9) without
3543 // Shift key but with Shift key, it produces '%'.
3544 // If the mUnshiftedLatinChar is produced by the key on current
3545 // keyboard layout, we shouldn't append it to alternativeCharCode.
3546 if (mUnshiftedLatinChar == mUnshiftedString.CharAt(0) ||
3547 mUnshiftedLatinChar == mShiftedString.CharAt(0)) {
3548 mUnshiftedLatinChar = 0;
3552 if (!mModKeyState.IsControl()) {
3553 return;
3556 // If the mCharCode is not ASCII character, we should replace the
3557 // mCharCode with ASCII character only when Ctrl is pressed.
3558 // But don't replace the mCharCode when the mCharCode is not same as
3559 // unmodified characters. In such case, Ctrl is sometimes used for a
3560 // part of character inputting key combination like Shift.
3561 uint32_t ch =
3562 mModKeyState.IsShift() ? mShiftedLatinChar : mUnshiftedLatinChar;
3563 if (!ch) {
3564 return;
3566 if (mInputtingStringAndModifiers.IsEmpty() ||
3567 mInputtingStringAndModifiers.UniCharsCaseInsensitiveEqual(
3568 mModKeyState.IsShift() ? mShiftedString : mUnshiftedString)) {
3569 mInputtingStringAndModifiers.Clear();
3570 mInputtingStringAndModifiers.Append(ch, mModKeyState.GetModifiers());
3574 bool NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages() const {
3575 MOZ_ASSERT(IsKeyDownMessage());
3576 MOZ_ASSERT(IsFollowedByPrintableCharOrSysCharMessage());
3577 MOZ_ASSERT(!mWidget->Destroyed());
3579 nsresult rv = mDispatcher->BeginNativeInputTransaction();
3580 if (NS_WARN_IF(NS_FAILED(rv))) {
3581 MOZ_LOG(
3582 gKeyLog, LogLevel::Error,
3583 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3584 "FAILED due to BeginNativeInputTransaction() failure",
3585 this));
3586 return true;
3588 WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget);
3589 MOZ_LOG(gKeyLog, LogLevel::Debug,
3590 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3591 "initializing keypress event...",
3592 this));
3593 ModifierKeyState modKeyState(mModKeyState);
3594 if (mCanIgnoreModifierStateAtKeyPress && IsFollowedByPrintableCharMessage()) {
3595 // If eKeyPress event should cause inputting text in focused editor,
3596 // we need to remove Alt and Ctrl state.
3597 modKeyState.Unset(MODIFIER_ALT | MODIFIER_CONTROL);
3599 // We don't need to send char message here if there are two or more retrieved
3600 // messages because we need to set each message to each eKeyPress event.
3601 bool needsCallback = mFollowingCharMsgs.Length() > 1;
3602 nsEventStatus status = InitKeyEvent(keypressEvent, modKeyState);
3603 MOZ_LOG(gKeyLog, LogLevel::Info,
3604 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3605 "dispatching keypress event(s)...",
3606 this));
3607 bool dispatched = mDispatcher->MaybeDispatchKeypressEvents(
3608 keypressEvent, status, const_cast<NativeKey*>(this), needsCallback);
3609 if (mWidget->Destroyed()) {
3610 MOZ_LOG(
3611 gKeyLog, LogLevel::Info,
3612 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3613 "keypress event(s) caused destroying the widget",
3614 this));
3615 return true;
3617 bool consumed = status == nsEventStatus_eConsumeNoDefault;
3618 MOZ_LOG(gKeyLog, LogLevel::Info,
3619 ("%p NativeKey::DispatchKeyPressEventsWithRetrievedCharMessages(), "
3620 "dispatched keypress event(s), dispatched=%s, consumed=%s",
3621 this, GetBoolName(dispatched), GetBoolName(consumed)));
3622 return consumed;
3625 bool NativeKey::DispatchKeyPressEventsWithoutCharMessage() const {
3626 MOZ_ASSERT(IsKeyDownMessage());
3627 MOZ_ASSERT(!mIsDeadKey || !mCommittedCharsAndModifiers.IsEmpty());
3628 MOZ_ASSERT(!mWidget->Destroyed());
3630 nsresult rv = mDispatcher->BeginNativeInputTransaction();
3631 if (NS_WARN_IF(NS_FAILED(rv))) {
3632 MOZ_LOG(gKeyLog, LogLevel::Error,
3633 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3634 "FAILED due "
3635 "to BeginNativeInputTransaction() failure",
3636 this));
3637 return true;
3640 WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget);
3641 if (mInputtingStringAndModifiers.IsEmpty() && mShiftedString.IsEmpty() &&
3642 mUnshiftedString.IsEmpty()) {
3643 keypressEvent.mKeyCode = mDOMKeyCode;
3645 MOZ_LOG(gKeyLog, LogLevel::Debug,
3646 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3647 "initializing "
3648 "keypress event...",
3649 this));
3650 nsEventStatus status = InitKeyEvent(keypressEvent, mModKeyState);
3651 MOZ_LOG(gKeyLog, LogLevel::Info,
3652 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3653 "dispatching "
3654 "keypress event(s)...",
3655 this));
3656 bool dispatched = mDispatcher->MaybeDispatchKeypressEvents(
3657 keypressEvent, status, const_cast<NativeKey*>(this));
3658 if (mWidget->Destroyed()) {
3659 MOZ_LOG(gKeyLog, LogLevel::Info,
3660 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), "
3661 "keypress event(s) caused destroying the widget",
3662 this));
3663 return true;
3665 bool consumed = status == nsEventStatus_eConsumeNoDefault;
3666 MOZ_LOG(
3667 gKeyLog, LogLevel::Info,
3668 ("%p NativeKey::DispatchKeyPressEventsWithoutCharMessage(), dispatched "
3669 "keypress event(s), dispatched=%s, consumed=%s",
3670 this, GetBoolName(dispatched), GetBoolName(consumed)));
3671 return consumed;
3674 void NativeKey::WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyboardEvent,
3675 uint32_t aIndex) {
3676 // If it's an eKeyPress event and it's generated from retrieved char message,
3677 // we need to set raw message information for plugins.
3678 if (aKeyboardEvent.mMessage == eKeyPress &&
3679 IsFollowedByPrintableCharOrSysCharMessage()) {
3680 MOZ_RELEASE_ASSERT(aIndex < mCommittedCharsAndModifiers.Length());
3681 uint32_t foundPrintableCharMessages = 0;
3682 for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
3683 if (!IsPrintableCharOrSysCharMessage(mFollowingCharMsgs[i])) {
3684 // XXX Should we dispatch a plugin event for WM_*DEADCHAR messages and
3685 // WM_CHAR with a control character here? But we're not sure
3686 // how can we create such message queue (i.e., WM_CHAR or
3687 // WM_SYSCHAR with a printable character and such message are
3688 // generated by a keydown). So, let's ignore such case until
3689 // we'd get some bug reports.
3690 MOZ_LOG(gKeyLog, LogLevel::Warning,
3691 ("%p NativeKey::WillDispatchKeyboardEvent(), WARNING, "
3692 "ignoring %zuth message due to non-printable char message, %s",
3693 this, i + 1, ToString(mFollowingCharMsgs[i]).get()));
3694 continue;
3696 if (foundPrintableCharMessages++ == aIndex) {
3697 // Found message which caused the eKeyPress event.
3698 break;
3701 // Set modifier state from mCommittedCharsAndModifiers because some of them
3702 // might be different. For example, Shift key was pressed at inputting
3703 // dead char but Shift key was released before inputting next character.
3704 if (mCanIgnoreModifierStateAtKeyPress) {
3705 ModifierKeyState modKeyState(mModKeyState);
3706 modKeyState.Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT |
3707 MODIFIER_ALTGRAPH | MODIFIER_CAPSLOCK);
3708 modKeyState.Set(mCommittedCharsAndModifiers.ModifiersAt(aIndex));
3709 modKeyState.InitInputEvent(aKeyboardEvent);
3710 MOZ_LOG(gKeyLog, LogLevel::Info,
3711 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3712 "setting %uth modifier state to %s",
3713 this, aIndex + 1, ToString(modKeyState).get()));
3716 size_t longestLength =
3717 std::max(mInputtingStringAndModifiers.Length(),
3718 std::max(mShiftedString.Length(), mUnshiftedString.Length()));
3719 size_t skipUniChars = longestLength - mInputtingStringAndModifiers.Length();
3720 size_t skipShiftedChars = longestLength - mShiftedString.Length();
3721 size_t skipUnshiftedChars = longestLength - mUnshiftedString.Length();
3722 if (aIndex >= longestLength) {
3723 MOZ_LOG(
3724 gKeyLog, LogLevel::Info,
3725 ("%p NativeKey::WillDispatchKeyboardEvent(), does nothing for %uth "
3726 "%s event",
3727 this, aIndex + 1, ToChar(aKeyboardEvent.mMessage)));
3728 return;
3731 // Check if aKeyboardEvent is the last event for a key press.
3732 // So, if it's not an eKeyPress event, it's always the last event.
3733 // Otherwise, check if the index is the last character of
3734 // mCommittedCharsAndModifiers.
3735 bool isLastIndex = aKeyboardEvent.mMessage != eKeyPress ||
3736 mCommittedCharsAndModifiers.IsEmpty() ||
3737 mCommittedCharsAndModifiers.Length() - 1 == aIndex;
3739 nsTArray<AlternativeCharCode>& altArray =
3740 aKeyboardEvent.mAlternativeCharCodes;
3742 // Set charCode and adjust modifier state for every eKeyPress event.
3743 // This is not necessary for the other keyboard events because the other
3744 // keyboard events shouldn't have non-zero charCode value and should have
3745 // current modifier state.
3746 if (aKeyboardEvent.mMessage == eKeyPress && skipUniChars <= aIndex) {
3747 // XXX Modifying modifier state of aKeyboardEvent is illegal, but no way
3748 // to set different modifier state per keypress event except this
3749 // hack. Note that ideally, dead key should cause composition events
3750 // instead of keypress events, though.
3751 if (aIndex - skipUniChars < mInputtingStringAndModifiers.Length()) {
3752 ModifierKeyState modKeyState(mModKeyState);
3753 // If key in combination with Alt and/or Ctrl produces a different
3754 // character than without them then do not report these flags
3755 // because it is separate keyboard layout shift state. If dead-key
3756 // and base character does not produce a valid composite character
3757 // then both produced dead-key character and following base
3758 // character may have different modifier flags, too.
3759 modKeyState.Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT |
3760 MODIFIER_ALTGRAPH | MODIFIER_CAPSLOCK);
3761 modKeyState.Set(
3762 mInputtingStringAndModifiers.ModifiersAt(aIndex - skipUniChars));
3763 modKeyState.InitInputEvent(aKeyboardEvent);
3764 MOZ_LOG(gKeyLog, LogLevel::Info,
3765 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3766 "setting %uth modifier state to %s",
3767 this, aIndex + 1, ToString(modKeyState).get()));
3769 uint16_t uniChar =
3770 mInputtingStringAndModifiers.CharAt(aIndex - skipUniChars);
3772 // The mCharCode was set from mKeyValue. However, for example, when Ctrl key
3773 // is pressed, its value should indicate an ASCII character for backward
3774 // compatibility rather than inputting character without the modifiers.
3775 // Therefore, we need to modify mCharCode value here.
3776 aKeyboardEvent.SetCharCode(uniChar);
3777 MOZ_LOG(gKeyLog, LogLevel::Info,
3778 ("%p NativeKey::WillDispatchKeyboardEvent(), "
3779 "setting %uth charCode to %s",
3780 this, aIndex + 1, GetCharacterCodeName(uniChar).get()));
3783 // We need to append alterntaive charCode values:
3784 // - if the event is eKeyPress, we need to append for the index because
3785 // eKeyPress event is dispatched for every character inputted by a
3786 // key press.
3787 // - if the event is not eKeyPress, we need to append for all characters
3788 // inputted by the key press because the other keyboard events (e.g.,
3789 // eKeyDown are eKeyUp) are fired only once for a key press.
3790 size_t count;
3791 if (aKeyboardEvent.mMessage == eKeyPress) {
3792 // Basically, append alternative charCode values only for the index.
3793 count = 1;
3794 // However, if it's the last eKeyPress event but different shift state
3795 // can input longer string, the last eKeyPress event should have all
3796 // remaining alternative charCode values.
3797 if (isLastIndex) {
3798 count = longestLength - aIndex;
3800 } else {
3801 count = longestLength;
3803 for (size_t i = 0; i < count; ++i) {
3804 uint16_t shiftedChar = 0, unshiftedChar = 0;
3805 if (skipShiftedChars <= aIndex + i) {
3806 shiftedChar = mShiftedString.CharAt(aIndex + i - skipShiftedChars);
3808 if (skipUnshiftedChars <= aIndex + i) {
3809 unshiftedChar = mUnshiftedString.CharAt(aIndex + i - skipUnshiftedChars);
3812 if (shiftedChar || unshiftedChar) {
3813 AlternativeCharCode chars(unshiftedChar, shiftedChar);
3814 altArray.AppendElement(chars);
3817 if (!isLastIndex) {
3818 continue;
3821 if (mUnshiftedLatinChar || mShiftedLatinChar) {
3822 AlternativeCharCode chars(mUnshiftedLatinChar, mShiftedLatinChar);
3823 altArray.AppendElement(chars);
3826 // Typically, following virtual keycodes are used for a key which can
3827 // input the character. However, these keycodes are also used for
3828 // other keys on some keyboard layout. E.g., in spite of Shift+'1'
3829 // inputs '+' on Thai keyboard layout, a key which is at '=/+'
3830 // key on ANSI keyboard layout is VK_OEM_PLUS. Native applications
3831 // handle it as '+' key if Ctrl key is pressed.
3832 char16_t charForOEMKeyCode = 0;
3833 switch (mVirtualKeyCode) {
3834 case VK_OEM_PLUS:
3835 charForOEMKeyCode = '+';
3836 break;
3837 case VK_OEM_COMMA:
3838 charForOEMKeyCode = ',';
3839 break;
3840 case VK_OEM_MINUS:
3841 charForOEMKeyCode = '-';
3842 break;
3843 case VK_OEM_PERIOD:
3844 charForOEMKeyCode = '.';
3845 break;
3847 if (charForOEMKeyCode && charForOEMKeyCode != mUnshiftedString.CharAt(0) &&
3848 charForOEMKeyCode != mShiftedString.CharAt(0) &&
3849 charForOEMKeyCode != mUnshiftedLatinChar &&
3850 charForOEMKeyCode != mShiftedLatinChar) {
3851 AlternativeCharCode OEMChars(charForOEMKeyCode, charForOEMKeyCode);
3852 altArray.AppendElement(OEMChars);
3857 /*****************************************************************************
3858 * mozilla::widget::KeyboardLayout
3859 *****************************************************************************/
3861 KeyboardLayout* KeyboardLayout::sInstance = nullptr;
3862 StaticRefPtr<nsIUserIdleServiceInternal> KeyboardLayout::sIdleService;
3864 // static
3865 KeyboardLayout* KeyboardLayout::GetInstance() {
3866 if (!sInstance) {
3867 sInstance = new KeyboardLayout();
3869 return sInstance;
3872 // static
3873 void KeyboardLayout::Shutdown() {
3874 delete sInstance;
3875 sInstance = nullptr;
3876 sIdleService = nullptr;
3879 // static
3880 void KeyboardLayout::NotifyIdleServiceOfUserActivity() {
3881 if (!sIdleService) {
3882 sIdleService = nsCOMPtr<nsIUserIdleServiceInternal>(
3883 do_GetService("@mozilla.org/widget/useridleservice;1"))
3884 .forget();
3885 if (NS_WARN_IF(!sIdleService)) {
3886 return;
3889 sIdleService->ResetIdleTimeOut(0);
3892 KeyboardLayout::KeyboardLayout() {
3893 mDeadKeyTableListHead = nullptr;
3894 // A dead key sequence should be made from up to 5 keys. Therefore, 4 is
3895 // enough and makes sense because the item is uint8_t.
3896 // (Although, even if it's possible to be 6 keys or more in a sequence,
3897 // this array will be re-allocated).
3898 mActiveDeadKeys.SetCapacity(4);
3899 mDeadKeyShiftStates.SetCapacity(4);
3901 // If we put it off to load active keyboard layout when first needed, we need
3902 // to load it at instanciation. That makes us save the cost of a call of
3903 // GetKeyboardLayout() API.
3904 if (StaticPrefs::ui_key_layout_load_when_first_needed()) {
3905 OnLayoutChange(::GetKeyboardLayout(0));
3909 KeyboardLayout::~KeyboardLayout() { ReleaseDeadKeyTables(); }
3911 bool KeyboardLayout::IsPrintableCharKey(uint8_t aVirtualKey) {
3912 return GetKeyIndex(aVirtualKey) >= 0;
3915 WORD KeyboardLayout::ComputeScanCodeForVirtualKeyCode(
3916 uint8_t aVirtualKeyCode) const {
3917 return static_cast<WORD>(::MapVirtualKeyEx(aVirtualKeyCode, MAPVK_VK_TO_VSC,
3918 KeyboardLayout::GetLayout()));
3921 bool KeyboardLayout::IsDeadKey(uint8_t aVirtualKey,
3922 const ModifierKeyState& aModKeyState) const {
3923 int32_t virtualKeyIndex = GetKeyIndex(aVirtualKey);
3925 // XXX KeyboardLayout class doesn't support unusual keyboard layout which
3926 // maps some function keys as dead keys.
3927 if (virtualKeyIndex < 0) {
3928 return false;
3931 return mVirtualKeys[virtualKeyIndex].IsDeadKey(
3932 VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()));
3935 bool KeyboardLayout::IsSysKey(uint8_t aVirtualKey,
3936 const ModifierKeyState& aModKeyState) const {
3937 // If Alt key is not pressed, it's never a system key combination.
3938 // Additionally, if Ctrl key is pressed, it's never a system key combination
3939 // too.
3940 // FYI: Windows logo key state won't affect if it's a system key.
3941 if (!aModKeyState.IsAlt() || aModKeyState.IsControl()) {
3942 return false;
3945 int32_t virtualKeyIndex = GetKeyIndex(aVirtualKey);
3946 if (virtualKeyIndex < 0) {
3947 return true;
3950 UniCharsAndModifiers inputCharsAndModifiers =
3951 GetUniCharsAndModifiers(aVirtualKey, aModKeyState);
3952 if (inputCharsAndModifiers.IsEmpty()) {
3953 return true;
3956 // If the Alt key state isn't consumed, that means that the key with Alt
3957 // doesn't cause text input. So, the combination is a system key.
3958 return !!(inputCharsAndModifiers.ModifiersAt(0) & MODIFIER_ALT);
3961 void KeyboardLayout::InitNativeKey(NativeKey& aNativeKey) {
3962 if (mIsPendingToRestoreKeyboardLayout) {
3963 LoadLayout(::GetKeyboardLayout(0));
3966 // If the aNativeKey is initialized with WM_CHAR, the key information
3967 // should be discarded because mKeyValue should have the string to be
3968 // inputted.
3969 if (aNativeKey.mMsg.message == WM_CHAR) {
3970 char16_t ch = static_cast<char16_t>(aNativeKey.mMsg.wParam);
3971 // But don't set key value as printable key if the character is a control
3972 // character such as 0x0D at pressing Enter key.
3973 if (!NativeKey::IsControlChar(ch)) {
3974 aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
3975 Modifiers modifiers =
3976 aNativeKey.GetModifiers() & ~(MODIFIER_ALT | MODIFIER_CONTROL);
3977 aNativeKey.mCommittedCharsAndModifiers.Append(ch, modifiers);
3978 return;
3982 // If the aNativeKey is in a sequence to input a Unicode character with
3983 // Alt + numpad keys, we should just set the number as the inputting charcter.
3984 // Note that we should compute the key value from the virtual key code
3985 // because they may be mapped to alphabets, but they should be treated as
3986 // Alt + [0-9] even by web apps.
3987 // However, we shouldn't touch the key value if given virtual key code is
3988 // not a printable key because it may be synthesized by 3rd party utility
3989 // or just NumLock is unlocked and user tries to use shortcut key. In the
3990 // latter case, we cannot solve the conflict issue with Alt+foo shortcut key
3991 // and inputting a Unicode scalar value like reported to bug 1606655, though,
3992 // I have no better idea. Perhaps, `Alt` shouldn't be used for shortcut key
3993 // combination on Windows.
3994 if (aNativeKey.MaybeTypingUnicodeScalarValue() &&
3995 KeyboardLayout::IsPrintableCharKey(aNativeKey.mVirtualKeyCode)) {
3996 // If the key code value is mapped to a Numpad key, let's compute the key
3997 // value with it. This is same behavior as Chrome. In strictly speaking,
3998 // I think that the else block's computation is better because it seems
3999 // that Windows does not refer virtual key code value, but we should avoid
4000 // web-compat issues.
4001 char16_t num = '0';
4002 if (aNativeKey.mVirtualKeyCode >= VK_NUMPAD0 &&
4003 aNativeKey.mVirtualKeyCode <= VK_NUMPAD9) {
4004 num = '0' + aNativeKey.mVirtualKeyCode - VK_NUMPAD0;
4006 // Otherwise, let's use fake key value for making never match with
4007 // mnemonic.
4008 else {
4009 switch (aNativeKey.mScanCode) {
4010 case 0x0052: // Numpad0
4011 num = '0';
4012 break;
4013 case 0x004F: // Numpad1
4014 num = '1';
4015 break;
4016 case 0x0050: // Numpad2
4017 num = '2';
4018 break;
4019 case 0x0051: // Numpad3
4020 num = '3';
4021 break;
4022 case 0x004B: // Numpad4
4023 num = '4';
4024 break;
4025 case 0x004C: // Numpad5
4026 num = '5';
4027 break;
4028 case 0x004D: // Numpad6
4029 num = '6';
4030 break;
4031 case 0x0047: // Numpad7
4032 num = '7';
4033 break;
4034 case 0x0048: // Numpad8
4035 num = '8';
4036 break;
4037 case 0x0049: // Numpad9
4038 num = '9';
4039 break;
4040 default:
4041 MOZ_ASSERT_UNREACHABLE(
4042 "IsTypingUnicodeScalarValue() must have returned true for wrong "
4043 "scancode");
4044 break;
4047 aNativeKey.mCommittedCharsAndModifiers.Append(num,
4048 aNativeKey.GetModifiers());
4049 aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
4050 return;
4053 // When it's followed by non-dead char message(s) for printable character(s),
4054 // aNativeKey should dispatch eKeyPress events for them rather than
4055 // information from keyboard layout because respecting WM_(SYS)CHAR messages
4056 // guarantees that we can always input characters which is expected by
4057 // the user even if the user uses odd keyboard layout.
4058 // Or, when it was followed by non-dead char message for a printable character
4059 // but it's gone at removing the message from the queue, let's treat it
4060 // as a key inputting empty string.
4061 if (aNativeKey.IsFollowedByPrintableCharOrSysCharMessage() ||
4062 aNativeKey.mCharMessageHasGone) {
4063 MOZ_ASSERT(!aNativeKey.IsCharMessage(aNativeKey.mMsg));
4064 if (aNativeKey.IsFollowedByPrintableCharOrSysCharMessage()) {
4065 // Initialize mCommittedCharsAndModifiers with following char messages.
4066 aNativeKey.InitCommittedCharsAndModifiersWithFollowingCharMessages();
4067 MOZ_ASSERT(!aNativeKey.mCommittedCharsAndModifiers.IsEmpty());
4069 // Currently, we are doing a ugly hack to keypress events to cause
4070 // inputting character even if Ctrl or Alt key is pressed, that is, we
4071 // remove Ctrl and Alt modifier state from keypress event. However, for
4072 // example, Ctrl+Space which causes ' ' of WM_CHAR message never causes
4073 // keypress event whose ctrlKey is true. For preventing this problem,
4074 // we should mark as not removable if Ctrl or Alt key does not cause
4075 // changing inputting character.
4076 if (IsPrintableCharKey(aNativeKey.mOriginalVirtualKeyCode) &&
4077 (aNativeKey.IsControl() ^ aNativeKey.IsAlt())) {
4078 ModifierKeyState state = aNativeKey.ModifierKeyStateRef();
4079 state.Unset(MODIFIER_ALT | MODIFIER_CONTROL);
4080 UniCharsAndModifiers charsWithoutModifier =
4081 GetUniCharsAndModifiers(aNativeKey.GenericVirtualKeyCode(), state);
4082 aNativeKey.mCanIgnoreModifierStateAtKeyPress =
4083 !charsWithoutModifier.UniCharsEqual(
4084 aNativeKey.mCommittedCharsAndModifiers);
4086 } else {
4087 aNativeKey.mCommittedCharsAndModifiers.Clear();
4089 aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
4091 // If it's not in dead key sequence, we don't need to do anymore here.
4092 if (!IsInDeadKeySequence()) {
4093 return;
4096 // If it's in dead key sequence and dead char is inputted as is, we need to
4097 // set the previous modifier state which is stored when preceding dead key
4098 // is pressed.
4099 UniCharsAndModifiers deadChars = GetDeadUniCharsAndModifiers();
4100 aNativeKey.mCommittedCharsAndModifiers.OverwriteModifiersIfBeginsWith(
4101 deadChars);
4102 // Finish the dead key sequence.
4103 DeactivateDeadKeyState();
4104 return;
4107 // If it's a dead key, aNativeKey will be initialized by
4108 // MaybeInitNativeKeyAsDeadKey().
4109 if (MaybeInitNativeKeyAsDeadKey(aNativeKey)) {
4110 return;
4113 // If the key is not a usual printable key, KeyboardLayout class assume that
4114 // it's not cause dead char nor printable char. Therefore, there are nothing
4115 // to do here fore such keys (e.g., function keys).
4116 // However, this should keep dead key state even if non-printable key is
4117 // pressed during a dead key sequence.
4118 if (!IsPrintableCharKey(aNativeKey.mOriginalVirtualKeyCode)) {
4119 return;
4122 MOZ_ASSERT(aNativeKey.mOriginalVirtualKeyCode != VK_PACKET,
4123 "At handling VK_PACKET, we shouldn't refer keyboard layout");
4124 MOZ_ASSERT(
4125 aNativeKey.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING,
4126 "Printable key's key name index must be KEY_NAME_INDEX_USE_STRING");
4128 // If it's in dead key handling and the pressed key causes a composite
4129 // character, aNativeKey will be initialized by
4130 // MaybeInitNativeKeyWithCompositeChar().
4131 if (MaybeInitNativeKeyWithCompositeChar(aNativeKey)) {
4132 return;
4135 UniCharsAndModifiers baseChars = GetUniCharsAndModifiers(aNativeKey);
4137 // If the key press isn't related to any dead keys, initialize aNativeKey
4138 // with the characters which should be caused by the key.
4139 if (!IsInDeadKeySequence()) {
4140 aNativeKey.mCommittedCharsAndModifiers = baseChars;
4141 return;
4144 // If the key doesn't cause a composite character with preceding dead key,
4145 // initialize aNativeKey with the dead-key character followed by current
4146 // key's character.
4147 UniCharsAndModifiers deadChars = GetDeadUniCharsAndModifiers();
4148 aNativeKey.mCommittedCharsAndModifiers = deadChars + baseChars;
4149 if (aNativeKey.IsKeyDownMessage()) {
4150 DeactivateDeadKeyState();
4154 bool KeyboardLayout::MaybeInitNativeKeyAsDeadKey(NativeKey& aNativeKey) {
4155 // Only when it's not in dead key sequence, we can trust IsDeadKey() result.
4156 if (!IsInDeadKeySequence() && !IsDeadKey(aNativeKey)) {
4157 return false;
4160 // When keydown message is followed by a dead char message, it should be
4161 // initialized as dead key.
4162 const bool isDeadKeyDownEvent =
4163 aNativeKey.IsKeyDownMessage() && aNativeKey.IsFollowedByDeadCharMessage();
4165 // When keyup message is received, let's check if it's one of preceding
4166 // dead keys because keydown message order and keyup message order may be
4167 // different.
4168 const bool isDeadKeyUpEvent =
4169 !aNativeKey.IsKeyDownMessage() &&
4170 mActiveDeadKeys.Contains(aNativeKey.GenericVirtualKeyCode());
4172 if (isDeadKeyDownEvent || isDeadKeyUpEvent) {
4173 ActivateDeadKeyState(aNativeKey);
4174 // Any dead key events don't generate characters. So, a dead key should
4175 // cause only keydown event and keyup event whose KeyboardEvent.key
4176 // values are "Dead".
4177 aNativeKey.mCommittedCharsAndModifiers.Clear();
4178 aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_Dead;
4179 return true;
4182 // At keydown message handling, we need to forget the first dead key
4183 // because there is no guarantee coming WM_KEYUP for the second dead
4184 // key before next WM_KEYDOWN. E.g., due to auto key repeat or pressing
4185 // another dead key before releasing current key. Therefore, we can
4186 // set only a character for current key for keyup event.
4187 if (!IsInDeadKeySequence()) {
4188 aNativeKey.mCommittedCharsAndModifiers =
4189 GetUniCharsAndModifiers(aNativeKey);
4190 return true;
4193 // When non-printable key event comes during a dead key sequence, that must
4194 // be a modifier key event. So, such events shouldn't be handled as a part
4195 // of the dead key sequence.
4196 if (!IsDeadKey(aNativeKey)) {
4197 return false;
4200 // FYI: Following code may run when the user doesn't input text actually
4201 // but the key sequence is a dead key sequence. For example,
4202 // ` -> Ctrl+` with Spanish keyboard layout. Let's keep using this
4203 // complicated code for now because this runs really rarely.
4205 // Dead key followed by another dead key may cause a composed character
4206 // (e.g., "Russian - Mnemonic" keyboard layout's 's' -> 'c').
4207 if (MaybeInitNativeKeyWithCompositeChar(aNativeKey)) {
4208 return true;
4211 // Otherwise, dead key followed by another dead key causes inputting both
4212 // character.
4213 UniCharsAndModifiers prevDeadChars = GetDeadUniCharsAndModifiers();
4214 UniCharsAndModifiers newChars = GetUniCharsAndModifiers(aNativeKey);
4215 // But keypress events should be fired for each committed character.
4216 aNativeKey.mCommittedCharsAndModifiers = prevDeadChars + newChars;
4217 if (aNativeKey.IsKeyDownMessage()) {
4218 DeactivateDeadKeyState();
4220 return true;
4223 bool KeyboardLayout::MaybeInitNativeKeyWithCompositeChar(
4224 NativeKey& aNativeKey) {
4225 if (!IsInDeadKeySequence()) {
4226 return false;
4229 if (NS_WARN_IF(!IsPrintableCharKey(aNativeKey.mOriginalVirtualKeyCode))) {
4230 return false;
4233 UniCharsAndModifiers baseChars = GetUniCharsAndModifiers(aNativeKey);
4234 if (baseChars.IsEmpty() || !baseChars.CharAt(0)) {
4235 return false;
4238 char16_t compositeChar = GetCompositeChar(baseChars.CharAt(0));
4239 if (!compositeChar) {
4240 return false;
4243 // Active dead-key and base character does produce exactly one composite
4244 // character.
4245 aNativeKey.mCommittedCharsAndModifiers.Append(compositeChar,
4246 baseChars.ModifiersAt(0));
4247 if (aNativeKey.IsKeyDownMessage()) {
4248 DeactivateDeadKeyState();
4250 return true;
4253 UniCharsAndModifiers KeyboardLayout::GetUniCharsAndModifiers(
4254 uint8_t aVirtualKey, VirtualKey::ShiftState aShiftState) const {
4255 UniCharsAndModifiers result;
4256 int32_t key = GetKeyIndex(aVirtualKey);
4257 if (key < 0) {
4258 return result;
4260 return mVirtualKeys[key].GetUniChars(aShiftState);
4263 UniCharsAndModifiers KeyboardLayout::GetDeadUniCharsAndModifiers() const {
4264 MOZ_RELEASE_ASSERT(mActiveDeadKeys.Length() == mDeadKeyShiftStates.Length());
4266 if (NS_WARN_IF(mActiveDeadKeys.IsEmpty())) {
4267 return UniCharsAndModifiers();
4270 UniCharsAndModifiers result;
4271 for (size_t i = 0; i < mActiveDeadKeys.Length(); ++i) {
4272 result +=
4273 GetUniCharsAndModifiers(mActiveDeadKeys[i], mDeadKeyShiftStates[i]);
4275 return result;
4278 char16_t KeyboardLayout::GetCompositeChar(char16_t aBaseChar) const {
4279 if (NS_WARN_IF(mActiveDeadKeys.IsEmpty())) {
4280 return 0;
4282 // XXX Currently, we don't support computing a composite character with
4283 // two or more dead keys since it needs big table for supporting
4284 // long chained dead keys. However, this should be a minor bug
4285 // because this runs only when the latest keydown event does not cause
4286 // WM_(SYS)CHAR messages. So, when user wants to input a character,
4287 // this path never runs.
4288 if (mActiveDeadKeys.Length() > 1) {
4289 return 0;
4291 int32_t key = GetKeyIndex(mActiveDeadKeys[0]);
4292 if (key < 0) {
4293 return 0;
4295 return mVirtualKeys[key].GetCompositeChar(mDeadKeyShiftStates[0], aBaseChar);
4298 static bool IsValidKeyboardLayoutsChild(const nsAString& aChildName) {
4299 if (aChildName.Length() != 8) {
4300 return false;
4302 for (size_t i = 0; i < aChildName.Length(); i++) {
4303 if ((aChildName[i] >= '0' && aChildName[i] <= '9') ||
4304 (aChildName[i] >= 'a' && aChildName[i] <= 'f') ||
4305 (aChildName[i] >= 'A' && aChildName[i] <= 'F')) {
4306 continue;
4308 return false;
4310 return true;
4313 // static
4314 nsCString KeyboardLayout::GetLayoutName(HKL aLayout) {
4315 constexpr auto kKeyboardLayouts =
4316 u"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\"_ns;
4317 uint16_t language = reinterpret_cast<uintptr_t>(aLayout) & 0xFFFF;
4318 uint16_t layout = (reinterpret_cast<uintptr_t>(aLayout) >> 16) & 0xFFFF;
4319 // If the layout is less than 0xA000XXXX (normal keyboard layout for the
4320 // language) or 0xEYYYXXXX (IMM-IME), we can retrieve its name simply.
4321 if (layout < 0xA000 || (layout & 0xF000) == 0xE000) {
4322 nsAutoString key(kKeyboardLayouts);
4323 key.AppendPrintf("%08" PRIXPTR, layout < 0xA000
4324 ? layout
4325 : reinterpret_cast<uintptr_t>(aLayout));
4326 wchar_t buf[256];
4327 if (NS_WARN_IF(!WinRegistry::GetString(
4328 HKEY_LOCAL_MACHINE, key, u"Layout Text"_ns, buf,
4329 WinRegistry::kLegacyWinUtilsStringFlags))) {
4330 return "No name or too long name"_ns;
4332 return NS_ConvertUTF16toUTF8(buf);
4335 if (NS_WARN_IF((layout & 0xF000) != 0xF000)) {
4336 nsCString result;
4337 result.AppendPrintf("Odd HKL: 0x%08" PRIXPTR,
4338 reinterpret_cast<uintptr_t>(aLayout));
4339 return result;
4342 // Otherwise, we need to walk the registry under "Keyboard Layouts".
4343 WinRegistry::Key regKey(HKEY_LOCAL_MACHINE, kKeyboardLayouts,
4344 WinRegistry::KeyMode::Read);
4345 if (NS_WARN_IF(!regKey)) {
4346 return ""_ns;
4348 uint32_t childCount = regKey.GetChildCount();
4349 if (NS_WARN_IF(!childCount)) {
4350 return ""_ns;
4352 for (uint32_t i = 0; i < childCount; i++) {
4353 nsAutoString childName;
4354 if (NS_WARN_IF(!regKey.GetChildName(i, childName)) ||
4355 !IsValidKeyboardLayoutsChild(childName)) {
4356 continue;
4358 nsresult rv = NS_OK;
4359 uint32_t childNum = static_cast<uint32_t>(childName.ToInteger64(&rv, 16));
4360 if (NS_WARN_IF(NS_FAILED(rv))) {
4361 continue;
4363 // Ignore normal keyboard layouts for each language.
4364 if (childNum <= 0xFFFF) {
4365 continue;
4367 // If it doesn't start with 'A' nor 'a', language should be matched.
4368 if ((childNum & 0xFFFF) != language &&
4369 (childNum & 0xF0000000) != 0xA0000000) {
4370 continue;
4372 // Then, the child should have "Layout Id" which is "YYY" of 0xFYYYXXXX.
4373 nsAutoString key(kKeyboardLayouts);
4374 key += childName;
4375 WinRegistry::Key regKey(HKEY_LOCAL_MACHINE, key,
4376 WinRegistry::KeyMode::QueryValue);
4377 if (NS_WARN_IF(!regKey)) {
4378 continue;
4380 wchar_t buf[256];
4381 if (NS_WARN_IF(!regKey.GetValueAsString(
4382 u"Layout Id"_ns, buf, WinRegistry::kLegacyWinUtilsStringFlags))) {
4383 continue;
4385 uint16_t layoutId = wcstol(buf, nullptr, 16);
4386 if (layoutId != (layout & 0x0FFF)) {
4387 continue;
4389 if (NS_WARN_IF(!regKey.GetValueAsString(
4390 u"Layout Text"_ns, buf, WinRegistry::kLegacyWinUtilsStringFlags))) {
4391 continue;
4393 return NS_ConvertUTF16toUTF8(buf);
4395 return ""_ns;
4398 void KeyboardLayout::LoadLayout(HKL aLayout) {
4399 mIsPendingToRestoreKeyboardLayout = false;
4401 if (mKeyboardLayout == aLayout) {
4402 return;
4405 mKeyboardLayout = aLayout;
4406 mHasAltGr = false;
4408 MOZ_LOG(gKeyLog, LogLevel::Info,
4409 ("KeyboardLayout::LoadLayout(aLayout=0x%p (%s))", aLayout,
4410 GetLayoutName(aLayout).get()));
4412 BYTE kbdState[256];
4413 memset(kbdState, 0, sizeof(kbdState));
4415 BYTE originalKbdState[256];
4416 // Bitfield with all shift states that have at least one dead-key.
4417 uint16_t shiftStatesWithDeadKeys = 0;
4418 // Bitfield with all shift states that produce any possible dead-key base
4419 // characters.
4420 uint16_t shiftStatesWithBaseChars = 0;
4422 mActiveDeadKeys.Clear();
4423 mDeadKeyShiftStates.Clear();
4425 ReleaseDeadKeyTables();
4427 ::GetKeyboardState(originalKbdState);
4429 // For each shift state gather all printable characters that are produced
4430 // for normal case when no any dead-key is active.
4432 for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) {
4433 VirtualKey::FillKbdState(kbdState, shiftState);
4434 bool isAltGr = VirtualKey::IsAltGrIndex(shiftState);
4435 for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
4436 int32_t vki = GetKeyIndex(virtualKey);
4437 if (vki < 0) {
4438 continue;
4440 NS_ASSERTION(uint32_t(vki) < std::size(mVirtualKeys), "invalid index");
4441 char16_t uniChars[5];
4442 int32_t ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)uniChars,
4443 std::size(uniChars), 0, mKeyboardLayout);
4444 // dead-key
4445 if (ret < 0) {
4446 shiftStatesWithDeadKeys |= (1 << shiftState);
4447 // Repeat dead-key to deactivate it and get its character
4448 // representation.
4449 char16_t deadChar[2];
4450 ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)deadChar,
4451 std::size(deadChar), 0, mKeyboardLayout);
4452 NS_ASSERTION(ret == 2, "Expecting twice repeated dead-key character");
4453 mVirtualKeys[vki].SetDeadChar(shiftState, deadChar[0]);
4455 MOZ_LOG(gKeyLog, LogLevel::Verbose,
4456 (" %s (%d): DeadChar(%s, %s) (ret=%d)",
4457 kVirtualKeyName[virtualKey], vki,
4458 GetShiftStateName(shiftState).get(),
4459 GetCharacterCodeNames(deadChar, 1).get(), ret));
4460 } else {
4461 if (ret == 1) {
4462 // dead-key can pair only with exactly one base character.
4463 shiftStatesWithBaseChars |= (1 << shiftState);
4465 mVirtualKeys[vki].SetNormalChars(shiftState, uniChars, ret);
4466 MOZ_LOG(gKeyLog, LogLevel::Verbose,
4467 (" %s (%d): NormalChar(%s, %s) (ret=%d)",
4468 kVirtualKeyName[virtualKey], vki,
4469 GetShiftStateName(shiftState).get(),
4470 GetCharacterCodeNames(uniChars, ret).get(), ret));
4473 // If the key inputs at least one character with AltGr modifier,
4474 // check if AltGr changes inputting character. If it does, mark
4475 // this keyboard layout has AltGr modifier actually.
4476 if (!mHasAltGr && ret > 0 && isAltGr &&
4477 mVirtualKeys[vki].IsChangedByAltGr(shiftState)) {
4478 mHasAltGr = true;
4479 MOZ_LOG(gKeyLog, LogLevel::Info,
4480 (" Found a key (%s) changed by AltGr: %s -> %s (%s) (ret=%d)",
4481 kVirtualKeyName[virtualKey],
4482 GetCharacterCodeNames(
4483 mVirtualKeys[vki].GetNativeUniChars(
4484 shiftState - VirtualKey::ShiftStateIndex::eAltGr))
4485 .get(),
4486 GetCharacterCodeNames(
4487 mVirtualKeys[vki].GetNativeUniChars(shiftState))
4488 .get(),
4489 GetShiftStateName(shiftState).get(), ret));
4494 // Now process each dead-key to find all its base characters and resulting
4495 // composite characters.
4496 for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) {
4497 if (!(shiftStatesWithDeadKeys & (1 << shiftState))) {
4498 continue;
4501 VirtualKey::FillKbdState(kbdState, shiftState);
4503 for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
4504 int32_t vki = GetKeyIndex(virtualKey);
4505 if (vki >= 0 && mVirtualKeys[vki].IsDeadKey(shiftState)) {
4506 AutoTArray<DeadKeyEntry, 256> deadKeyArray;
4507 uint32_t n = GetDeadKeyCombinations(
4508 virtualKey, kbdState, shiftStatesWithBaseChars, deadKeyArray);
4509 const DeadKeyTable* dkt =
4510 mVirtualKeys[vki].MatchingDeadKeyTable(deadKeyArray.Elements(), n);
4511 if (!dkt) {
4512 dkt = AddDeadKeyTable(deadKeyArray.Elements(), n);
4514 mVirtualKeys[vki].AttachDeadKeyTable(shiftState, dkt);
4519 ::SetKeyboardState(originalKbdState);
4521 if (MOZ_LOG_TEST(gKeyLog, LogLevel::Verbose)) {
4522 static const UINT kExtendedScanCode[] = {0x0000, 0xE000};
4523 static const UINT kMapType = MAPVK_VSC_TO_VK_EX;
4524 MOZ_LOG(gKeyLog, LogLevel::Verbose,
4525 ("Logging virtual keycode values for scancode (0x%p)...",
4526 mKeyboardLayout));
4527 for (uint32_t i = 0; i < std::size(kExtendedScanCode); i++) {
4528 for (uint32_t j = 1; j <= 0xFF; j++) {
4529 UINT scanCode = kExtendedScanCode[i] + j;
4530 UINT virtualKeyCode =
4531 ::MapVirtualKeyEx(scanCode, kMapType, mKeyboardLayout);
4532 MOZ_LOG(gKeyLog, LogLevel::Verbose,
4533 ("0x%04X, %s", scanCode, kVirtualKeyName[virtualKeyCode]));
4538 MOZ_LOG(gKeyLog, LogLevel::Info,
4539 (" AltGr key is %s in %s", mHasAltGr ? "found" : "not found",
4540 GetLayoutName(aLayout).get()));
4543 inline int32_t KeyboardLayout::GetKeyIndex(uint8_t aVirtualKey) {
4544 // Currently these 68 (NS_NUM_OF_KEYS) virtual keys are assumed
4545 // to produce visible representation:
4546 // 0x20 - VK_SPACE ' '
4547 // 0x30..0x39 '0'..'9'
4548 // 0x41..0x5A 'A'..'Z'
4549 // 0x60..0x69 '0'..'9' on numpad
4550 // 0x6A - VK_MULTIPLY '*' on numpad
4551 // 0x6B - VK_ADD '+' on numpad
4552 // 0x6D - VK_SUBTRACT '-' on numpad
4553 // 0x6E - VK_DECIMAL '.' on numpad
4554 // 0x6F - VK_DIVIDE '/' on numpad
4555 // 0x6E - VK_DECIMAL '.'
4556 // 0xBA - VK_OEM_1 ';:' for US
4557 // 0xBB - VK_OEM_PLUS '+' any country
4558 // 0xBC - VK_OEM_COMMA ',' any country
4559 // 0xBD - VK_OEM_MINUS '-' any country
4560 // 0xBE - VK_OEM_PERIOD '.' any country
4561 // 0xBF - VK_OEM_2 '/?' for US
4562 // 0xC0 - VK_OEM_3 '`~' for US
4563 // 0xC1 - VK_ABNT_C1 '/?' for Brazilian
4564 // 0xC2 - VK_ABNT_C2 separator key on numpad (Brazilian or JIS for Mac)
4565 // 0xDB - VK_OEM_4 '[{' for US
4566 // 0xDC - VK_OEM_5 '\|' for US
4567 // 0xDD - VK_OEM_6 ']}' for US
4568 // 0xDE - VK_OEM_7 ''"' for US
4569 // 0xDF - VK_OEM_8
4570 // 0xE1 - no name
4571 // 0xE2 - VK_OEM_102 '\_' for JIS
4572 // 0xE3 - no name
4573 // 0xE4 - no name
4575 static const int8_t xlat[256] = {
4576 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
4577 //-----------------------------------------------------------------------
4578 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00
4579 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10
4580 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20
4581 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, // 30
4582 -1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 40
4583 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, -1, -1, // 50
4584 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, -1, 49, 50, 51, // 60
4585 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 70
4586 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
4587 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90
4588 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0
4589 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 53, 54, 55, 56, 57, // B0
4590 58, 59, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0
4591 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 61, 62, 63, 64, 65, // D0
4592 -1, 66, 67, 68, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0
4593 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // F0
4596 return xlat[aVirtualKey];
4599 const DeadKeyTable* KeyboardLayout::AddDeadKeyTable(
4600 const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries) {
4601 DeadKeyTableListEntry* next = mDeadKeyTableListHead;
4603 const size_t bytes = offsetof(DeadKeyTableListEntry, data) +
4604 DeadKeyTable::SizeInBytes(aEntries);
4605 uint8_t* p = new uint8_t[bytes];
4607 mDeadKeyTableListHead = reinterpret_cast<DeadKeyTableListEntry*>(p);
4608 mDeadKeyTableListHead->next = next;
4610 DeadKeyTable* dkt =
4611 reinterpret_cast<DeadKeyTable*>(mDeadKeyTableListHead->data);
4613 dkt->Init(aDeadKeyArray, aEntries);
4615 return dkt;
4618 void KeyboardLayout::ReleaseDeadKeyTables() {
4619 while (mDeadKeyTableListHead) {
4620 uint8_t* p = reinterpret_cast<uint8_t*>(mDeadKeyTableListHead);
4621 mDeadKeyTableListHead = mDeadKeyTableListHead->next;
4623 delete[] p;
4627 bool KeyboardLayout::EnsureDeadKeyActive(bool aIsActive, uint8_t aDeadKey,
4628 const PBYTE aDeadKeyKbdState) {
4629 int32_t ret;
4630 do {
4631 char16_t dummyChars[5];
4632 ret =
4633 ::ToUnicodeEx(aDeadKey, 0, (PBYTE)aDeadKeyKbdState, (LPWSTR)dummyChars,
4634 std::size(dummyChars), 0, mKeyboardLayout);
4635 // returned values:
4636 // <0 - Dead key state is active. The keyboard driver will wait for next
4637 // character.
4638 // 1 - Previous pressed key was a valid base character that produced
4639 // exactly one composite character.
4640 // >1 - Previous pressed key does not produce any composite characters.
4641 // Return dead-key character followed by base character(s).
4642 } while ((ret < 0) != aIsActive);
4644 return (ret < 0);
4647 void KeyboardLayout::ActivateDeadKeyState(const NativeKey& aNativeKey) {
4648 // Dead-key state should be activated at keydown.
4649 if (!aNativeKey.IsKeyDownMessage()) {
4650 return;
4653 mActiveDeadKeys.AppendElement(aNativeKey.mOriginalVirtualKeyCode);
4654 mDeadKeyShiftStates.AppendElement(aNativeKey.GetShiftState());
4657 void KeyboardLayout::DeactivateDeadKeyState() {
4658 if (mActiveDeadKeys.IsEmpty()) {
4659 return;
4662 BYTE kbdState[256];
4663 memset(kbdState, 0, sizeof(kbdState));
4665 // Assume that the last dead key can finish dead key sequence.
4666 VirtualKey::FillKbdState(kbdState, mDeadKeyShiftStates.LastElement());
4667 EnsureDeadKeyActive(false, mActiveDeadKeys.LastElement(), kbdState);
4668 mActiveDeadKeys.Clear();
4669 mDeadKeyShiftStates.Clear();
4672 bool KeyboardLayout::AddDeadKeyEntry(char16_t aBaseChar,
4673 char16_t aCompositeChar,
4674 nsTArray<DeadKeyEntry>& aDeadKeyArray) {
4675 auto dke = DeadKeyEntry(aBaseChar, aCompositeChar);
4676 for (uint32_t index = 0; index < aDeadKeyArray.Length(); index++) {
4677 if (aDeadKeyArray[index] == dke) {
4678 return false;
4682 aDeadKeyArray.AppendElement(dke);
4684 return true;
4687 uint32_t KeyboardLayout::GetDeadKeyCombinations(
4688 uint8_t aDeadKey, const PBYTE aDeadKeyKbdState,
4689 uint16_t aShiftStatesWithBaseChars, nsTArray<DeadKeyEntry>& aDeadKeyArray) {
4690 bool deadKeyActive = false;
4691 uint32_t entries = 0;
4692 BYTE kbdState[256];
4693 memset(kbdState, 0, sizeof(kbdState));
4695 for (uint32_t shiftState = 0; shiftState < 16; shiftState++) {
4696 if (!(aShiftStatesWithBaseChars & (1 << shiftState))) {
4697 continue;
4700 VirtualKey::FillKbdState(kbdState, shiftState);
4702 for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
4703 int32_t vki = GetKeyIndex(virtualKey);
4704 // Dead-key can pair only with such key that produces exactly one base
4705 // character.
4706 if (vki >= 0 &&
4707 mVirtualKeys[vki].GetNativeUniChars(shiftState).Length() == 1) {
4708 // Ensure dead-key is in active state, when it swallows entered
4709 // character and waits for the next pressed key.
4710 if (!deadKeyActive) {
4711 deadKeyActive = EnsureDeadKeyActive(true, aDeadKey, aDeadKeyKbdState);
4714 // Depending on the character the followed the dead-key, the keyboard
4715 // driver can produce one composite character, or a dead-key character
4716 // followed by a second character.
4717 char16_t compositeChars[5];
4718 int32_t ret =
4719 ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)compositeChars,
4720 std::size(compositeChars), 0, mKeyboardLayout);
4721 switch (ret) {
4722 case 0:
4723 // This key combination does not produce any characters. The
4724 // dead-key is still in active state.
4725 break;
4726 case 1: {
4727 char16_t baseChars[5];
4728 ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)baseChars,
4729 std::size(baseChars), 0, mKeyboardLayout);
4730 if (entries < aDeadKeyArray.Capacity()) {
4731 switch (ret) {
4732 case 1:
4733 // Exactly one composite character produced. Now, when
4734 // dead-key is not active, repeat the last character one more
4735 // time to determine the base character.
4736 if (AddDeadKeyEntry(baseChars[0], compositeChars[0],
4737 aDeadKeyArray)) {
4738 entries++;
4740 deadKeyActive = false;
4741 break;
4742 case -1: {
4743 // If pressing another dead-key produces different character,
4744 // we should register the dead-key entry with first character
4745 // produced by current key.
4747 // First inactivate the dead-key state completely.
4748 deadKeyActive =
4749 EnsureDeadKeyActive(false, aDeadKey, aDeadKeyKbdState);
4750 if (NS_WARN_IF(deadKeyActive)) {
4751 MOZ_LOG(gKeyLog, LogLevel::Error,
4752 (" failed to deactivating the dead-key state..."));
4753 break;
4755 for (int32_t i = 0; i < 5; ++i) {
4756 ret = ::ToUnicodeEx(virtualKey, 0, kbdState,
4757 (LPWSTR)baseChars, std::size(baseChars),
4758 0, mKeyboardLayout);
4759 if (ret >= 0) {
4760 break;
4763 if (ret > 0 &&
4764 AddDeadKeyEntry(baseChars[0], compositeChars[0],
4765 aDeadKeyArray)) {
4766 entries++;
4768 // Inactivate dead-key state for current virtual keycode.
4769 EnsureDeadKeyActive(false, virtualKey, kbdState);
4770 break;
4772 default:
4773 NS_WARNING("File a bug for this dead-key handling!");
4774 deadKeyActive = false;
4775 break;
4778 MOZ_LOG(
4779 gKeyLog, LogLevel::Verbose,
4780 (" %s -> %s (%d): DeadKeyEntry(%s, %s) (ret=%d)",
4781 kVirtualKeyName[aDeadKey], kVirtualKeyName[virtualKey], vki,
4782 GetCharacterCodeNames(compositeChars, 1).get(),
4783 ret <= 0
4784 ? "''"
4785 : GetCharacterCodeNames(baseChars, std::min(ret, 5)).get(),
4786 ret));
4787 break;
4789 default:
4790 // 1. Unexpected dead-key. Dead-key chaining is not supported.
4791 // 2. More than one character generated. This is not a valid
4792 // dead-key and base character combination.
4793 deadKeyActive = false;
4794 MOZ_LOG(
4795 gKeyLog, LogLevel::Verbose,
4796 (" %s -> %s (%d): Unsupport dead key type(%s) (ret=%d)",
4797 kVirtualKeyName[aDeadKey], kVirtualKeyName[virtualKey], vki,
4798 ret <= 0
4799 ? "''"
4800 : GetCharacterCodeNames(compositeChars, std::min(ret, 5))
4801 .get(),
4802 ret));
4803 break;
4809 if (deadKeyActive) {
4810 deadKeyActive = EnsureDeadKeyActive(false, aDeadKey, aDeadKeyKbdState);
4813 aDeadKeyArray.Sort();
4815 return entries;
4818 uint32_t KeyboardLayout::ConvertNativeKeyCodeToDOMKeyCode(
4819 UINT aNativeKeyCode) const {
4820 // Alphabet or Numeric or Numpad or Function keys
4821 if ((aNativeKeyCode >= 0x30 && aNativeKeyCode <= 0x39) ||
4822 (aNativeKeyCode >= 0x41 && aNativeKeyCode <= 0x5A) ||
4823 (aNativeKeyCode >= 0x60 && aNativeKeyCode <= 0x87)) {
4824 return static_cast<uint32_t>(aNativeKeyCode);
4826 switch (aNativeKeyCode) {
4827 // Following keycodes are same as our DOM keycodes
4828 case VK_CANCEL:
4829 case VK_BACK:
4830 case VK_TAB:
4831 case VK_CLEAR:
4832 case VK_RETURN:
4833 case VK_SHIFT:
4834 case VK_CONTROL:
4835 case VK_MENU: // Alt
4836 case VK_PAUSE:
4837 case VK_CAPITAL: // CAPS LOCK
4838 case VK_KANA: // same as VK_HANGUL
4839 case VK_JUNJA:
4840 case VK_FINAL:
4841 case VK_HANJA: // same as VK_KANJI
4842 case VK_ESCAPE:
4843 case VK_CONVERT:
4844 case VK_NONCONVERT:
4845 case VK_ACCEPT:
4846 case VK_MODECHANGE:
4847 case VK_SPACE:
4848 case VK_PRIOR: // PAGE UP
4849 case VK_NEXT: // PAGE DOWN
4850 case VK_END:
4851 case VK_HOME:
4852 case VK_LEFT:
4853 case VK_UP:
4854 case VK_RIGHT:
4855 case VK_DOWN:
4856 case VK_SELECT:
4857 case VK_PRINT:
4858 case VK_EXECUTE:
4859 case VK_SNAPSHOT:
4860 case VK_INSERT:
4861 case VK_DELETE:
4862 case VK_APPS: // Context Menu
4863 case VK_SLEEP:
4864 case VK_NUMLOCK:
4865 case VK_SCROLL: // SCROLL LOCK
4866 case VK_ATTN: // Attension key of IBM midrange computers, e.g., AS/400
4867 case VK_CRSEL: // Cursor Selection
4868 case VK_EXSEL: // Extend Selection
4869 case VK_EREOF: // Erase EOF key of IBM 3270 keyboard layout
4870 case VK_PLAY:
4871 case VK_ZOOM:
4872 case VK_PA1: // PA1 key of IBM 3270 keyboard layout
4873 return uint32_t(aNativeKeyCode);
4875 case VK_HELP:
4876 return NS_VK_HELP;
4878 // Windows key should be mapped to a Win keycode
4879 // They should be able to be distinguished by DOM3 KeyboardEvent.location
4880 case VK_LWIN:
4881 case VK_RWIN:
4882 return NS_VK_WIN;
4884 case VK_VOLUME_MUTE:
4885 return NS_VK_VOLUME_MUTE;
4886 case VK_VOLUME_DOWN:
4887 return NS_VK_VOLUME_DOWN;
4888 case VK_VOLUME_UP:
4889 return NS_VK_VOLUME_UP;
4891 case VK_LSHIFT:
4892 case VK_RSHIFT:
4893 return NS_VK_SHIFT;
4895 case VK_LCONTROL:
4896 case VK_RCONTROL:
4897 return NS_VK_CONTROL;
4899 // Note that even if the key is AltGr, we should return NS_VK_ALT for
4900 // compatibility with both older Gecko and the other browsers.
4901 case VK_LMENU:
4902 case VK_RMENU:
4903 return NS_VK_ALT;
4905 // Following keycodes are not defined in our DOM keycodes.
4906 case VK_BROWSER_BACK:
4907 case VK_BROWSER_FORWARD:
4908 case VK_BROWSER_REFRESH:
4909 case VK_BROWSER_STOP:
4910 case VK_BROWSER_SEARCH:
4911 case VK_BROWSER_FAVORITES:
4912 case VK_BROWSER_HOME:
4913 case VK_MEDIA_NEXT_TRACK:
4914 case VK_MEDIA_PREV_TRACK:
4915 case VK_MEDIA_STOP:
4916 case VK_MEDIA_PLAY_PAUSE:
4917 case VK_LAUNCH_MAIL:
4918 case VK_LAUNCH_MEDIA_SELECT:
4919 case VK_LAUNCH_APP1:
4920 case VK_LAUNCH_APP2:
4921 return 0;
4923 // Following OEM specific virtual keycodes should pass through DOM keyCode
4924 // for compatibility with the other browsers on Windows.
4926 // Following OEM specific virtual keycodes are defined for Fujitsu/OASYS.
4927 case VK_OEM_FJ_JISHO:
4928 case VK_OEM_FJ_MASSHOU:
4929 case VK_OEM_FJ_TOUROKU:
4930 case VK_OEM_FJ_LOYA:
4931 case VK_OEM_FJ_ROYA:
4932 // Not sure what means "ICO".
4933 case VK_ICO_HELP:
4934 case VK_ICO_00:
4935 case VK_ICO_CLEAR:
4936 // Following OEM specific virtual keycodes are defined for Nokia/Ericsson.
4937 case VK_OEM_RESET:
4938 case VK_OEM_JUMP:
4939 case VK_OEM_PA1:
4940 case VK_OEM_PA2:
4941 case VK_OEM_PA3:
4942 case VK_OEM_WSCTRL:
4943 case VK_OEM_CUSEL:
4944 case VK_OEM_ATTN:
4945 case VK_OEM_FINISH:
4946 case VK_OEM_COPY:
4947 case VK_OEM_AUTO:
4948 case VK_OEM_ENLW:
4949 case VK_OEM_BACKTAB:
4950 // VK_OEM_CLEAR is defined as not OEM specific, but let's pass though
4951 // DOM keyCode like other OEM specific virtual keycodes.
4952 case VK_OEM_CLEAR:
4953 return uint32_t(aNativeKeyCode);
4955 // 0xE1 is an OEM specific virtual keycode. However, the value is already
4956 // used in our DOM keyCode for AltGr on Linux. So, this virtual keycode
4957 // cannot pass through DOM keyCode.
4958 case 0xE1:
4959 return 0;
4961 // Following keycodes are OEM keys which are keycodes for non-alphabet and
4962 // non-numeric keys, we should compute each keycode of them from unshifted
4963 // character which is inputted by each key. But if the unshifted character
4964 // is not an ASCII character but shifted character is an ASCII character,
4965 // we should refer it.
4966 case VK_OEM_1:
4967 case VK_OEM_PLUS:
4968 case VK_OEM_COMMA:
4969 case VK_OEM_MINUS:
4970 case VK_OEM_PERIOD:
4971 case VK_OEM_2:
4972 case VK_OEM_3:
4973 case VK_OEM_4:
4974 case VK_OEM_5:
4975 case VK_OEM_6:
4976 case VK_OEM_7:
4977 case VK_OEM_8:
4978 case VK_OEM_102:
4979 case VK_ABNT_C1: {
4980 NS_ASSERTION(IsPrintableCharKey(aNativeKeyCode),
4981 "The key must be printable");
4982 ModifierKeyState modKeyState(0);
4983 UniCharsAndModifiers uniChars =
4984 GetUniCharsAndModifiers(aNativeKeyCode, modKeyState);
4985 if (uniChars.Length() != 1 || uniChars.CharAt(0) < ' ' ||
4986 uniChars.CharAt(0) > 0x7F) {
4987 modKeyState.Set(MODIFIER_SHIFT);
4988 uniChars = GetUniCharsAndModifiers(aNativeKeyCode, modKeyState);
4989 if (uniChars.Length() != 1 || uniChars.CharAt(0) < ' ' ||
4990 uniChars.CharAt(0) > 0x7F) {
4991 // In this case, we've returned 0 in this case for long time because
4992 // we decided that we should avoid setting same keyCode value to 2 or
4993 // more keys since active keyboard layout may have a key to input the
4994 // punctuation with different key. However, setting keyCode to 0
4995 // makes some web applications which are aware of neither
4996 // KeyboardEvent.key nor KeyboardEvent.code not work with Firefox
4997 // when user selects non-ASCII capable keyboard layout such as
4998 // Russian and Thai layout. So, let's decide keyCode value with
4999 // major keyboard layout's key which causes the OEM keycode.
5000 // Actually, this maps same keyCode value to 2 keys on Russian
5001 // keyboard layout. "Period" key causes VK_OEM_PERIOD but inputs
5002 // Yu of Cyrillic and "Slash" key causes VK_OEM_2 (same as US
5003 // keyboard layout) but inputs "." (period of ASCII). Therefore,
5004 // we return DOM_VK_PERIOD which is same as VK_OEM_PERIOD for
5005 // "Period" key. On the other hand, we use same keyCode value for
5006 // "Slash" key too because it inputs ".".
5007 CodeNameIndex code;
5008 switch (aNativeKeyCode) {
5009 case VK_OEM_1:
5010 code = CODE_NAME_INDEX_Semicolon;
5011 break;
5012 case VK_OEM_PLUS:
5013 code = CODE_NAME_INDEX_Equal;
5014 break;
5015 case VK_OEM_COMMA:
5016 code = CODE_NAME_INDEX_Comma;
5017 break;
5018 case VK_OEM_MINUS:
5019 code = CODE_NAME_INDEX_Minus;
5020 break;
5021 case VK_OEM_PERIOD:
5022 code = CODE_NAME_INDEX_Period;
5023 break;
5024 case VK_OEM_2:
5025 code = CODE_NAME_INDEX_Slash;
5026 break;
5027 case VK_OEM_3:
5028 code = CODE_NAME_INDEX_Backquote;
5029 break;
5030 case VK_OEM_4:
5031 code = CODE_NAME_INDEX_BracketLeft;
5032 break;
5033 case VK_OEM_5:
5034 code = CODE_NAME_INDEX_Backslash;
5035 break;
5036 case VK_OEM_6:
5037 code = CODE_NAME_INDEX_BracketRight;
5038 break;
5039 case VK_OEM_7:
5040 code = CODE_NAME_INDEX_Quote;
5041 break;
5042 case VK_OEM_8:
5043 // Use keyCode value for "Backquote" key on UK keyboard layout.
5044 code = CODE_NAME_INDEX_Backquote;
5045 break;
5046 case VK_OEM_102:
5047 // Use keyCode value for "IntlBackslash" key.
5048 code = CODE_NAME_INDEX_IntlBackslash;
5049 break;
5050 case VK_ABNT_C1: // "/" of ABNT.
5051 // Use keyCode value for "IntlBackslash" key on ABNT keyboard
5052 // layout.
5053 code = CODE_NAME_INDEX_IntlBackslash;
5054 break;
5055 default:
5056 MOZ_ASSERT_UNREACHABLE("Handle all OEM keycode values");
5057 return 0;
5059 return WidgetKeyboardEvent::GetFallbackKeyCodeOfPunctuationKey(code);
5062 return WidgetUtils::ComputeKeyCodeFromChar(uniChars.CharAt(0));
5065 // IE sets 0xC2 to the DOM keyCode for VK_ABNT_C2. However, we're already
5066 // using NS_VK_SEPARATOR for the separator key on Mac and Linux. Therefore,
5067 // We should keep consistency between Gecko on all platforms rather than
5068 // with other browsers since a lot of keyCode values are already different
5069 // between browsers.
5070 case VK_ABNT_C2:
5071 return NS_VK_SEPARATOR;
5073 // VK_PROCESSKEY means IME already consumed the key event.
5074 case VK_PROCESSKEY:
5075 return NS_VK_PROCESSKEY;
5076 // VK_PACKET is generated by SendInput() API, we don't need to
5077 // care this message as key event.
5078 case VK_PACKET:
5079 return 0;
5080 // If a key is not mapped to a virtual keycode, 0xFF is used.
5081 case 0xFF:
5082 NS_WARNING("The key is failed to be converted to a virtual keycode");
5083 return 0;
5085 #ifdef DEBUG
5086 nsPrintfCString warning(
5087 "Unknown virtual keycode (0x%08X), please check the "
5088 "latest MSDN document, there may be some new "
5089 "keycodes we've never known.",
5090 aNativeKeyCode);
5091 NS_WARNING(warning.get());
5092 #endif
5093 return 0;
5096 KeyNameIndex KeyboardLayout::ConvertNativeKeyCodeToKeyNameIndex(
5097 uint8_t aVirtualKey) const {
5098 if (IsPrintableCharKey(aVirtualKey) || aVirtualKey == VK_PACKET) {
5099 return KEY_NAME_INDEX_USE_STRING;
5102 // If the keyboard layout has AltGr and AltRight key is pressed,
5103 // return AltGraph.
5104 if (aVirtualKey == VK_RMENU && HasAltGr()) {
5105 return KEY_NAME_INDEX_AltGraph;
5108 switch (aVirtualKey) {
5109 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5110 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5111 case aNativeKey: \
5112 return aKeyNameIndex;
5114 #include "NativeKeyToDOMKeyName.h"
5116 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5118 default:
5119 break;
5122 HKL layout = KeyboardLayout::GetLayout();
5123 WORD langID = LOWORD(static_cast<HKL>(layout));
5124 WORD primaryLangID = PRIMARYLANGID(langID);
5126 if (primaryLangID == LANG_JAPANESE) {
5127 switch (aVirtualKey) {
5128 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5129 #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, \
5130 aKeyNameIndex) \
5131 case aNativeKey: \
5132 return aKeyNameIndex;
5134 #include "NativeKeyToDOMKeyName.h"
5136 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5138 default:
5139 break;
5141 } else if (primaryLangID == LANG_KOREAN) {
5142 switch (aVirtualKey) {
5143 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5144 #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5145 case aNativeKey: \
5146 return aKeyNameIndex;
5148 #include "NativeKeyToDOMKeyName.h"
5150 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5152 default:
5153 return KEY_NAME_INDEX_Unidentified;
5157 switch (aVirtualKey) {
5158 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5159 #define NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
5160 case aNativeKey: \
5161 return aKeyNameIndex;
5163 #include "NativeKeyToDOMKeyName.h"
5165 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
5167 default:
5168 return KEY_NAME_INDEX_Unidentified;
5172 // static
5173 CodeNameIndex KeyboardLayout::ConvertScanCodeToCodeNameIndex(UINT aScanCode) {
5174 switch (aScanCode) {
5175 #define NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX(aNativeKey, aCodeNameIndex) \
5176 case aNativeKey: \
5177 return aCodeNameIndex;
5179 #include "NativeKeyToDOMCodeName.h"
5181 #undef NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX
5183 default:
5184 return CODE_NAME_INDEX_UNKNOWN;
5188 nsresult KeyboardLayout::SynthesizeNativeKeyEvent(
5189 nsWindow* aWidget, int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode,
5190 uint32_t aModifierFlags, const nsAString& aCharacters,
5191 const nsAString& aUnmodifiedCharacters) {
5192 UINT keyboardLayoutListCount = ::GetKeyboardLayoutList(0, nullptr);
5193 NS_ASSERTION(keyboardLayoutListCount > 0,
5194 "One keyboard layout must be installed at least");
5195 HKL keyboardLayoutListBuff[50];
5196 HKL* keyboardLayoutList = keyboardLayoutListCount < 50
5197 ? keyboardLayoutListBuff
5198 : new HKL[keyboardLayoutListCount];
5199 keyboardLayoutListCount =
5200 ::GetKeyboardLayoutList(keyboardLayoutListCount, keyboardLayoutList);
5201 NS_ASSERTION(keyboardLayoutListCount > 0,
5202 "Failed to get all keyboard layouts installed on the system");
5204 nsPrintfCString layoutName("%08x", aNativeKeyboardLayout);
5205 HKL loadedLayout = LoadKeyboardLayoutA(layoutName.get(), KLF_NOTELLSHELL);
5206 if (loadedLayout == nullptr) {
5207 if (keyboardLayoutListBuff != keyboardLayoutList) {
5208 delete[] keyboardLayoutList;
5210 return NS_ERROR_NOT_AVAILABLE;
5213 // Setup clean key state and load desired layout
5214 BYTE originalKbdState[256];
5215 ::GetKeyboardState(originalKbdState);
5216 BYTE kbdState[256];
5217 memset(kbdState, 0, sizeof(kbdState));
5218 // This changes the state of the keyboard for the current thread only,
5219 // and we'll restore it soon, so this should be OK.
5220 ::SetKeyboardState(kbdState);
5222 OverrideLayout(loadedLayout);
5224 bool isAltGrKeyPress = false;
5225 if (aModifierFlags & nsIWidget::ALTGRAPH) {
5226 if (!HasAltGr()) {
5227 return NS_ERROR_INVALID_ARG;
5229 // AltGr emulates ControlLeft key press and AltRight key press.
5230 // So, we should remove those flags from aModifierFlags before
5231 // calling WinUtils::SetupKeyModifiersSequence() to create correct
5232 // key sequence.
5233 // FYI: We don't support both ControlLeft and AltRight (AltGr) are
5234 // pressed at the same time unless synthesizing key is
5235 // VK_LCONTROL.
5236 aModifierFlags &= ~(nsIWidget::CTRL_L | nsIWidget::ALT_R);
5239 uint8_t argumentKeySpecific = 0;
5240 switch (aNativeKeyCode & 0xFF) {
5241 case VK_SHIFT:
5242 aModifierFlags &= ~(nsIWidget::SHIFT_L | nsIWidget::SHIFT_R);
5243 argumentKeySpecific = VK_LSHIFT;
5244 break;
5245 case VK_LSHIFT:
5246 aModifierFlags &= ~nsIWidget::SHIFT_L;
5247 argumentKeySpecific = aNativeKeyCode & 0xFF;
5248 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_SHIFT;
5249 break;
5250 case VK_RSHIFT:
5251 aModifierFlags &= ~nsIWidget::SHIFT_R;
5252 argumentKeySpecific = aNativeKeyCode & 0xFF;
5253 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_SHIFT;
5254 break;
5255 case VK_CONTROL:
5256 aModifierFlags &= ~(nsIWidget::CTRL_L | nsIWidget::CTRL_R);
5257 argumentKeySpecific = VK_LCONTROL;
5258 break;
5259 case VK_LCONTROL:
5260 aModifierFlags &= ~nsIWidget::CTRL_L;
5261 argumentKeySpecific = aNativeKeyCode & 0xFF;
5262 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_CONTROL;
5263 break;
5264 case VK_RCONTROL:
5265 aModifierFlags &= ~nsIWidget::CTRL_R;
5266 argumentKeySpecific = aNativeKeyCode & 0xFF;
5267 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_CONTROL;
5268 break;
5269 case VK_MENU:
5270 aModifierFlags &= ~(nsIWidget::ALT_L | nsIWidget::ALT_R);
5271 argumentKeySpecific = VK_LMENU;
5272 break;
5273 case VK_LMENU:
5274 aModifierFlags &= ~nsIWidget::ALT_L;
5275 argumentKeySpecific = aNativeKeyCode & 0xFF;
5276 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_MENU;
5277 break;
5278 case VK_RMENU:
5279 aModifierFlags &= ~(nsIWidget::ALT_R | nsIWidget::ALTGRAPH);
5280 argumentKeySpecific = aNativeKeyCode & 0xFF;
5281 aNativeKeyCode = (aNativeKeyCode & 0xFFFF0000) | VK_MENU;
5282 // If AltRight key is AltGr in the keyboard layout, let's use
5283 // SetupKeyModifiersSequence() to emulate the native behavior
5284 // since the same event order between keydown and keyup makes
5285 // the following code complicated.
5286 if (HasAltGr()) {
5287 isAltGrKeyPress = true;
5288 aModifierFlags &= ~nsIWidget::CTRL_L;
5289 aModifierFlags |= nsIWidget::ALTGRAPH;
5291 break;
5292 case VK_CAPITAL:
5293 aModifierFlags &= ~nsIWidget::CAPS_LOCK;
5294 argumentKeySpecific = VK_CAPITAL;
5295 break;
5296 case VK_NUMLOCK:
5297 aModifierFlags &= ~nsIWidget::NUM_LOCK;
5298 argumentKeySpecific = VK_NUMLOCK;
5299 break;
5302 AutoTArray<KeyPair, 10> keySequence;
5303 WinUtils::SetupKeyModifiersSequence(&keySequence, aModifierFlags, WM_KEYDOWN);
5304 if (!isAltGrKeyPress) {
5305 keySequence.AppendElement(KeyPair(aNativeKeyCode, argumentKeySpecific));
5308 // Simulate the pressing of each modifier key and then the real key
5309 // FYI: Each NativeKey instance here doesn't need to override keyboard layout
5310 // since this method overrides and restores the keyboard layout.
5311 for (uint32_t i = 0; i < keySequence.Length(); ++i) {
5312 uint8_t key = keySequence[i].mGeneral;
5313 uint8_t keySpecific = keySequence[i].mSpecific;
5314 uint16_t scanCode = keySequence[i].mScanCode;
5315 kbdState[key] = 0x81; // key is down and toggled on if appropriate
5316 if (keySpecific) {
5317 kbdState[keySpecific] = 0x81;
5319 ::SetKeyboardState(kbdState);
5320 ModifierKeyState modKeyState;
5321 // If scan code isn't specified explicitly, let's compute it with current
5322 // keyboard layout.
5323 if (!scanCode) {
5324 scanCode =
5325 ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key);
5327 LPARAM lParam = static_cast<LPARAM>(scanCode << 16);
5328 // If the scan code is for an extended key, set extended key flag.
5329 if ((scanCode & 0xFF00) == 0xE000) {
5330 lParam |= 0x1000000;
5332 // When AltGr key is pressed, both ControlLeft and AltRight cause
5333 // WM_KEYDOWN messages.
5334 bool makeSysKeyMsg =
5335 !(aModifierFlags & nsIWidget::ALTGRAPH) && IsSysKey(key, modKeyState);
5336 MSG keyDownMsg =
5337 WinUtils::InitMSG(makeSysKeyMsg ? WM_SYSKEYDOWN : WM_KEYDOWN, key,
5338 lParam, aWidget->GetWindowHandle());
5339 if (i == keySequence.Length() - 1) {
5340 bool makeDeadCharMsg =
5341 (IsDeadKey(key, modKeyState) && aCharacters.IsEmpty());
5342 nsAutoString chars(aCharacters);
5343 if (makeDeadCharMsg) {
5344 UniCharsAndModifiers deadChars =
5345 GetUniCharsAndModifiers(key, modKeyState);
5346 chars = deadChars.ToString();
5347 NS_ASSERTION(chars.Length() == 1,
5348 "Dead char must be only one character");
5350 if (chars.IsEmpty()) {
5351 NativeKey nativeKey(aWidget, keyDownMsg, modKeyState);
5352 nativeKey.HandleKeyDownMessage();
5353 } else {
5354 AutoTArray<NativeKey::FakeCharMsg, 10> fakeCharMsgs;
5355 for (uint32_t j = 0; j < chars.Length(); j++) {
5356 NativeKey::FakeCharMsg* fakeCharMsg = fakeCharMsgs.AppendElement();
5357 fakeCharMsg->mCharCode = chars.CharAt(j);
5358 fakeCharMsg->mScanCode = scanCode;
5359 fakeCharMsg->mIsSysKey = makeSysKeyMsg;
5360 fakeCharMsg->mIsDeadKey = makeDeadCharMsg;
5362 NativeKey nativeKey(aWidget, keyDownMsg, modKeyState, 0, &fakeCharMsgs);
5363 bool dispatched;
5364 nativeKey.HandleKeyDownMessage(&dispatched);
5365 // If some char messages are not consumed, let's emulate the widget
5366 // receiving the message directly.
5367 for (uint32_t j = 1; j < fakeCharMsgs.Length(); j++) {
5368 if (fakeCharMsgs[j].mConsumed) {
5369 continue;
5371 MSG charMsg = fakeCharMsgs[j].GetCharMsg(aWidget->GetWindowHandle());
5372 NativeKey nativeKey(aWidget, charMsg, modKeyState);
5373 nativeKey.HandleCharMessage(charMsg);
5376 } else {
5377 NativeKey nativeKey(aWidget, keyDownMsg, modKeyState);
5378 nativeKey.HandleKeyDownMessage();
5382 keySequence.Clear();
5383 if (!isAltGrKeyPress) {
5384 keySequence.AppendElement(KeyPair(aNativeKeyCode, argumentKeySpecific));
5386 WinUtils::SetupKeyModifiersSequence(&keySequence, aModifierFlags, WM_KEYUP);
5387 for (uint32_t i = 0; i < keySequence.Length(); ++i) {
5388 uint8_t key = keySequence[i].mGeneral;
5389 uint8_t keySpecific = keySequence[i].mSpecific;
5390 uint16_t scanCode = keySequence[i].mScanCode;
5391 kbdState[key] = 0; // key is up and toggled off if appropriate
5392 if (keySpecific) {
5393 kbdState[keySpecific] = 0;
5395 ::SetKeyboardState(kbdState);
5396 ModifierKeyState modKeyState;
5397 // If scan code isn't specified explicitly, let's compute it with current
5398 // keyboard layout.
5399 if (!scanCode) {
5400 scanCode =
5401 ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key);
5403 LPARAM lParam = static_cast<LPARAM>(scanCode << 16);
5404 // If the scan code is for an extended key, set extended key flag.
5405 if ((scanCode & 0xFF00) == 0xE000) {
5406 lParam |= 0x1000000;
5408 // Don't use WM_SYSKEYUP for Alt keyup.
5409 // NOTE: When AltGr was pressed, ControlLeft causes WM_SYSKEYUP normally.
5410 bool makeSysKeyMsg = IsSysKey(key, modKeyState) && key != VK_MENU;
5411 MSG keyUpMsg = WinUtils::InitMSG(makeSysKeyMsg ? WM_SYSKEYUP : WM_KEYUP,
5412 key, lParam, aWidget->GetWindowHandle());
5413 NativeKey nativeKey(aWidget, keyUpMsg, modKeyState);
5414 nativeKey.HandleKeyUpMessage();
5417 // Restore old key state and layout
5418 ::SetKeyboardState(originalKbdState);
5419 RestoreLayout();
5421 // Don't unload the layout if it's installed actually.
5422 for (uint32_t i = 0; i < keyboardLayoutListCount; i++) {
5423 if (keyboardLayoutList[i] == loadedLayout) {
5424 loadedLayout = 0;
5425 break;
5428 if (keyboardLayoutListBuff != keyboardLayoutList) {
5429 delete[] keyboardLayoutList;
5431 if (loadedLayout) {
5432 ::UnloadKeyboardLayout(loadedLayout);
5434 return NS_OK;
5437 /*****************************************************************************
5438 * mozilla::widget::DeadKeyTable
5439 *****************************************************************************/
5441 char16_t DeadKeyTable::GetCompositeChar(char16_t aBaseChar) const {
5442 // Dead-key table is sorted by BaseChar in ascending order.
5443 // Usually they are too small to use binary search.
5445 for (uint32_t index = 0; index < mEntries; index++) {
5446 if (mTable[index].BaseChar == aBaseChar) {
5447 return mTable[index].CompositeChar;
5449 if (mTable[index].BaseChar > aBaseChar) {
5450 break;
5454 return 0;
5457 /*****************************************************************************
5458 * mozilla::widget::RedirectedKeyDownMessage
5459 *****************************************************************************/
5461 MSG RedirectedKeyDownMessageManager::sRedirectedKeyDownMsg;
5462 bool RedirectedKeyDownMessageManager::sDefaultPreventedOfRedirectedMsg = false;
5464 // static
5465 bool RedirectedKeyDownMessageManager::IsRedirectedMessage(const MSG& aMsg) {
5466 return (aMsg.message == WM_KEYDOWN || aMsg.message == WM_SYSKEYDOWN) &&
5467 (sRedirectedKeyDownMsg.message == aMsg.message &&
5468 WinUtils::GetScanCode(sRedirectedKeyDownMsg.lParam) ==
5469 WinUtils::GetScanCode(aMsg.lParam));
5472 // static
5473 void RedirectedKeyDownMessageManager::RemoveNextCharMessage(HWND aWnd) {
5474 MSG msg;
5475 if (WinUtils::PeekMessage(&msg, aWnd, WM_KEYFIRST, WM_KEYLAST,
5476 PM_NOREMOVE | PM_NOYIELD) &&
5477 (msg.message == WM_CHAR || msg.message == WM_SYSCHAR)) {
5478 WinUtils::PeekMessage(&msg, aWnd, msg.message, msg.message,
5479 PM_REMOVE | PM_NOYIELD);
5483 } // namespace widget
5484 } // namespace mozilla