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