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