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