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