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