1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stddef.h>
6 
7 #include <string>
8 
9 #include "base/stl_util.h"
10 #include "base/strings/string16.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "build/build_config.h"
13 #include "chrome/test/chromedriver/chrome/status.h"
14 #include "chrome/test/chromedriver/chrome/ui_events.h"
15 #include "chrome/test/chromedriver/key_converter.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "ui/events/test/keyboard_layout.h"
18 
19 namespace {
20 
CheckEvents(const base::string16 & keys,const std::vector<KeyEvent> & expected_events,bool release_modifiers,int expected_modifiers)21 void CheckEvents(const base::string16& keys,
22                  const std::vector<KeyEvent>& expected_events,
23                  bool release_modifiers,
24                  int expected_modifiers) {
25   int modifiers = 0;
26   std::vector<KeyEvent> events;
27   EXPECT_EQ(kOk, ConvertKeysToKeyEvents(keys, release_modifiers,
28                                         &modifiers, &events).code());
29   EXPECT_EQ(expected_events.size(), events.size());
30   auto expected = expected_events.begin();
31   std::vector<KeyEvent>::const_iterator actual = events.begin();
32   while (expected != expected_events.end() && actual != events.end()) {
33     EXPECT_EQ(expected->type, actual->type);
34     EXPECT_EQ(expected->modifiers, actual->modifiers);
35     EXPECT_EQ(expected->modified_text, actual->modified_text);
36     EXPECT_EQ(expected->unmodified_text, actual->unmodified_text);
37     EXPECT_EQ(expected->key_code, actual->key_code);
38 
39     ++expected;
40     ++actual;
41   }
42   EXPECT_EQ(expected_modifiers, modifiers);
43 }
44 
CheckEventsReleaseModifiers(const base::string16 & keys,const std::vector<KeyEvent> & expected_events)45 void CheckEventsReleaseModifiers(const base::string16& keys,
46                                  const std::vector<KeyEvent>& expected_events) {
47   CheckEvents(keys, expected_events, true /* release_modifier */,
48       0 /* expected_modifiers */);
49 }
50 
CheckEventsReleaseModifiers(const std::string & keys,std::vector<KeyEvent> & expected_events)51 void CheckEventsReleaseModifiers(const std::string& keys,
52                                  std::vector<KeyEvent>& expected_events) {
53   CheckEventsReleaseModifiers(base::UTF8ToUTF16(keys), expected_events);
54 }
55 
CheckNonShiftChar(ui::KeyboardCode key_code,char character)56 void CheckNonShiftChar(ui::KeyboardCode key_code, char character) {
57   int modifiers = 0;
58   std::string char_string;
59   char_string.push_back(character);
60   std::vector<KeyEvent> events;
61   EXPECT_EQ(kOk, ConvertKeysToKeyEvents(base::ASCIIToUTF16(char_string),
62                                         true /* release_modifiers*/,
63                                         &modifiers, &events).code());
64   ASSERT_EQ(3u, events.size()) << "Char: " << character;
65   std::vector<KeyEvent>::const_iterator it = events.begin();
66   EXPECT_EQ(key_code, it->key_code) << "Char: " << character;
67   ++it;  // Move to the second event.
68   ASSERT_EQ(1u, it->modified_text.length()) << "Char: " << character;
69   ASSERT_EQ(1u, it->unmodified_text.length()) << "Char: " << character;
70   EXPECT_EQ(character, it->modified_text[0]) << "Char: " << character;
71   EXPECT_EQ(character, it->unmodified_text[0]) << "Char: " << character;
72   ++it;  // Move to the third event.
73   EXPECT_EQ(key_code, it->key_code) << "Char: " << character;
74 }
75 
CheckShiftChar(ui::KeyboardCode key_code,char character,char lower)76 void CheckShiftChar(ui::KeyboardCode key_code, char character, char lower) {
77   int modifiers = 0;
78   std::string char_string;
79   char_string.push_back(character);
80   std::vector<KeyEvent> events;
81   EXPECT_EQ(kOk, ConvertKeysToKeyEvents(base::ASCIIToUTF16(char_string),
82                                         true /* release_modifiers*/,
83                                         &modifiers, &events).code());
84   ASSERT_EQ(5u, events.size()) << "Char: " << character;
85   std::vector<KeyEvent>::const_iterator it = events.begin();
86   EXPECT_EQ(ui::VKEY_SHIFT, it->key_code) << "Char: " << character;
87   ++it;  // Move to second event.
88   EXPECT_EQ(key_code, it->key_code) << "Char: " << character;
89   ++it;  // Move to third event.
90   ASSERT_EQ(1u, it->modified_text.length()) << "Char: " << character;
91   ASSERT_EQ(1u, it->unmodified_text.length()) << "Char: " << character;
92   EXPECT_EQ(character, it->modified_text[0]) << "Char: " << character;
93   EXPECT_EQ(lower, it->unmodified_text[0]) << "Char: " << character;
94   ++it;  // Move to fourth event.
95   EXPECT_EQ(key_code, it->key_code) << "Char: " << character;
96   ++it;  // Move to fifth event.
97   EXPECT_EQ(ui::VKEY_SHIFT, it->key_code) << "Char: " << character;
98 }
99 
100 }  // namespace
101 
TEST(KeyConverter,SingleChar)102 TEST(KeyConverter, SingleChar) {
103   ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
104   KeyEventBuilder builder;
105   std::vector<KeyEvent> key_events;
106   builder.SetText("h", "h")->SetKeyCode(ui::VKEY_H)->Generate(&key_events);
107   CheckEventsReleaseModifiers("h", key_events);
108 }
109 
TEST(KeyConverter,SingleNumber)110 TEST(KeyConverter, SingleNumber) {
111   ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
112   KeyEventBuilder builder;
113   std::vector<KeyEvent> key_events;
114   builder.SetText("1", "1")->SetKeyCode(ui::VKEY_1)->Generate(&key_events);
115   CheckEventsReleaseModifiers("1", key_events);
116 }
117 
TEST(KeyConverter,MultipleChars)118 TEST(KeyConverter, MultipleChars) {
119   ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
120   KeyEventBuilder builder;
121   std::vector<KeyEvent> key_events;
122   builder.SetText("h", "h")->SetKeyCode(ui::VKEY_H)->Generate(&key_events);
123   builder.SetText("e", "e")->SetKeyCode(ui::VKEY_E)->Generate(&key_events);
124   builder.SetText("y", "y")->SetKeyCode(ui::VKEY_Y)->Generate(&key_events);
125   CheckEventsReleaseModifiers("hey", key_events);
126 }
127 
TEST(KeyConverter,WebDriverSpecialChar)128 TEST(KeyConverter, WebDriverSpecialChar) {
129   ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
130   KeyEventBuilder builder;
131   std::vector<KeyEvent> key_events;
132   builder.SetKeyCode(ui::VKEY_SPACE)->SetText(" ", " ")->Generate(&key_events);
133   base::string16 keys;
134   keys.push_back(static_cast<base::char16>(0xE00DU));
135   CheckEventsReleaseModifiers(keys, key_events);
136 }
137 
TEST(KeyConverter,WebDriverSpecialNonCharKey)138 TEST(KeyConverter, WebDriverSpecialNonCharKey) {
139   KeyEventBuilder builder;
140   std::vector<KeyEvent> key_events;
141   builder.SetKeyCode(ui::VKEY_F1)->Generate(&key_events);
142   base::string16 keys;
143   keys.push_back(static_cast<base::char16>(0xE031U));
144   CheckEventsReleaseModifiers(keys, key_events);
145 }
146 
TEST(KeyConverter,FrenchKeyOnEnglishLayout)147 TEST(KeyConverter, FrenchKeyOnEnglishLayout) {
148   ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
149   KeyEventBuilder builder;
150   std::string e_acute = base::WideToUTF8(L"\u00E9");
151   std::vector<KeyEvent> key_events;
152   builder.SetText(e_acute, e_acute)->Generate(&key_events);
153   CheckEventsReleaseModifiers(base::WideToUTF16(L"\u00E9"), key_events);
154 }
155 
156 #if defined(OS_WIN)
TEST(KeyConverter,NeedsCtrlAndAlt)157 TEST(KeyConverter, NeedsCtrlAndAlt) {
158   KeyEventBuilder ctrl_builder;
159   ctrl_builder.SetKeyCode(ui::VKEY_CONTROL);
160 
161   KeyEventBuilder alt_builder;
162   alt_builder.SetKeyCode(ui::VKEY_MENU);
163 
164   KeyEventBuilder q_builder;
165   q_builder.SetModifiers(kControlKeyModifierMask | kAltKeyModifierMask)
166       ->SetKeyCode(ui::VKEY_Q)
167       ->SetText("q", "@");
168 
169   std::vector<KeyEvent> key_events;
170   key_events.push_back(ctrl_builder.SetType(kRawKeyDownEventType)->Build());
171   key_events.push_back(alt_builder.SetType(kRawKeyDownEventType)->Build());
172   q_builder.Generate(&key_events);
173   key_events.push_back(alt_builder.SetType(kKeyUpEventType)->Build());
174   key_events.push_back(ctrl_builder.SetType(kKeyUpEventType)->Build());
175 
176   ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_GERMAN);
177   CheckEventsReleaseModifiers("@", key_events);
178 }
179 #endif
180 
TEST(KeyConverter,UppercaseCharDoesShift)181 TEST(KeyConverter, UppercaseCharDoesShift) {
182   ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
183   KeyEventBuilder shift_builder;
184   shift_builder.SetKeyCode(ui::VKEY_SHIFT);
185   KeyEventBuilder a_builder;
186   a_builder.SetKeyCode(ui::VKEY_A)
187       ->SetModifiers(kShiftKeyModifierMask)
188       ->SetText("a", "A");
189   std::vector<KeyEvent> key_events;
190   key_events.push_back(shift_builder.SetType(kRawKeyDownEventType)->Build());
191   a_builder.Generate(&key_events);
192   key_events.push_back(shift_builder.SetType(kKeyUpEventType)->Build());
193   CheckEventsReleaseModifiers("A", key_events);
194 }
195 
TEST(KeyConverter,UppercaseSymbolCharDoesShift)196 TEST(KeyConverter, UppercaseSymbolCharDoesShift) {
197   ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
198   KeyEventBuilder shift_builder;
199   shift_builder.SetKeyCode(ui::VKEY_SHIFT);
200   KeyEventBuilder one_builder;
201   one_builder.SetModifiers(kShiftKeyModifierMask)
202       ->SetKeyCode(ui::VKEY_1)
203       ->SetText("1", "!");
204   std::vector<KeyEvent> key_events;
205   key_events.push_back(shift_builder.SetType(kRawKeyDownEventType)->Build());
206   one_builder.Generate(&key_events);
207   key_events.push_back(shift_builder.SetType(kKeyUpEventType)->Build());
208   CheckEventsReleaseModifiers("!", key_events);
209 }
210 
TEST(KeyConverter,UppercaseCharUsesShiftOnlyIfNecessary)211 TEST(KeyConverter, UppercaseCharUsesShiftOnlyIfNecessary) {
212   ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
213   std::vector<KeyEvent> key_events;
214   KeyEventBuilder shift_builder;
215   key_events.push_back(shift_builder.SetType(kRawKeyDownEventType)
216                            ->SetKeyCode(ui::VKEY_SHIFT)
217                            ->SetModifiers(kShiftKeyModifierMask)
218                            ->Build());
219   KeyEventBuilder builder;
220   builder.SetModifiers(kShiftKeyModifierMask);
221   builder.SetKeyCode(ui::VKEY_A)->SetText("a", "A")->Generate(&key_events);
222   builder.SetKeyCode(ui::VKEY_B)->SetText("b", "B")->Generate(&key_events);
223   builder.SetKeyCode(ui::VKEY_C)->SetText("c", "C")->Generate(&key_events);
224   key_events.push_back(
225       shift_builder.SetType(kKeyUpEventType)->SetModifiers(0)->Build());
226   base::string16 keys;
227   keys.push_back(static_cast<base::char16>(0xE008U));
228   keys.append(base::UTF8ToUTF16("aBc"));
229   CheckEventsReleaseModifiers(keys, key_events);
230 }
231 
TEST(KeyConverter,ToggleModifiers)232 TEST(KeyConverter, ToggleModifiers) {
233   std::vector<KeyEvent> key_events;
234   KeyEventBuilder builder;
235   key_events.push_back(builder.SetType(kRawKeyDownEventType)
236                            ->SetKeyCode(ui::VKEY_SHIFT)
237                            ->SetModifiers(kShiftKeyModifierMask)
238                            ->Build());
239   key_events.push_back(
240       builder.SetType(kKeyUpEventType)->SetModifiers(0)->Build());
241   key_events.push_back(builder.SetType(kRawKeyDownEventType)
242                            ->SetKeyCode(ui::VKEY_CONTROL)
243                            ->SetModifiers(kControlKeyModifierMask)
244                            ->Build());
245   key_events.push_back(
246       builder.SetType(kKeyUpEventType)->SetModifiers(0)->Build());
247   key_events.push_back(builder.SetType(kRawKeyDownEventType)
248                            ->SetKeyCode(ui::VKEY_MENU)
249                            ->SetModifiers(kAltKeyModifierMask)
250                            ->Build());
251   key_events.push_back(
252       builder.SetType(kKeyUpEventType)->SetModifiers(0)->Build());
253   key_events.push_back(builder.SetType(kRawKeyDownEventType)
254                            ->SetKeyCode(ui::VKEY_COMMAND)
255                            ->SetModifiers(kMetaKeyModifierMask)
256                            ->Build());
257   key_events.push_back(
258       builder.SetType(kKeyUpEventType)->SetModifiers(0)->Build());
259   base::string16 keys;
260   keys.push_back(static_cast<base::char16>(0xE008U));
261   keys.push_back(static_cast<base::char16>(0xE008U));
262   keys.push_back(static_cast<base::char16>(0xE009U));
263   keys.push_back(static_cast<base::char16>(0xE009U));
264   keys.push_back(static_cast<base::char16>(0xE00AU));
265   keys.push_back(static_cast<base::char16>(0xE00AU));
266   keys.push_back(static_cast<base::char16>(0xE03DU));
267   keys.push_back(static_cast<base::char16>(0xE03DU));
268   CheckEventsReleaseModifiers(keys, key_events);
269 }
270 
TEST(KeyConverter,AllShorthandKeys)271 TEST(KeyConverter, AllShorthandKeys) {
272   ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
273   KeyEventBuilder builder;
274   std::vector<KeyEvent> key_events;
275   builder.SetKeyCode(ui::VKEY_RETURN)
276       ->SetText("\r", "\r")
277       ->Generate(&key_events);
278   builder.Generate(&key_events);
279   builder.SetKeyCode(ui::VKEY_TAB);
280 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
281   builder.SetText("\t", "\t")->Generate(&key_events);
282 #else
283   builder.SetText(std::string(), std::string());
284   key_events.push_back(builder.SetType(kRawKeyDownEventType)->Build());
285   key_events.push_back(builder.SetType(kKeyUpEventType)->Build());
286 #endif
287   builder.SetKeyCode(ui::VKEY_BACK);
288 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
289   builder.SetText("\b", "\b")->Generate(&key_events);
290 #else
291   builder.SetText(std::string(), std::string());
292   key_events.push_back(builder.SetType(kRawKeyDownEventType)->Build());
293   key_events.push_back(builder.SetType(kKeyUpEventType)->Build());
294 #endif
295   builder.SetKeyCode(ui::VKEY_SPACE)->SetText(" ", " ")->Generate(&key_events);
296   CheckEventsReleaseModifiers("\n\r\n\t\b ", key_events);
297 }
298 
299 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
300 // Fails on bots: crbug.com/174962
301 #define MAYBE_AllEnglishKeyboardSymbols DISABLED_AllEnglishKeyboardSymbols
302 #else
303 #define MAYBE_AllEnglishKeyboardSymbols AllEnglishKeyboardSymbols
304 #endif
305 
TEST(KeyConverter,MAYBE_AllEnglishKeyboardSymbols)306 TEST(KeyConverter, MAYBE_AllEnglishKeyboardSymbols) {
307   ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
308   base::string16 keys;
309   const ui::KeyboardCode kSymbolKeyCodes[] = {
310       ui::VKEY_OEM_3,
311       ui::VKEY_OEM_MINUS,
312       ui::VKEY_OEM_PLUS,
313       ui::VKEY_OEM_4,
314       ui::VKEY_OEM_6,
315       ui::VKEY_OEM_5,
316       ui::VKEY_OEM_1,
317       ui::VKEY_OEM_7,
318       ui::VKEY_OEM_COMMA,
319       ui::VKEY_OEM_PERIOD,
320       ui::VKEY_OEM_2};
321   std::string kLowerSymbols = "`-=[]\\;',./";
322   std::string kUpperSymbols = "~_+{}|:\"<>?";
323   for (size_t i = 0; i < kLowerSymbols.length(); ++i)
324     CheckNonShiftChar(kSymbolKeyCodes[i], kLowerSymbols[i]);
325   for (size_t i = 0; i < kUpperSymbols.length(); ++i)
326     CheckShiftChar(kSymbolKeyCodes[i], kUpperSymbols[i], kLowerSymbols[i]);
327 }
328 
TEST(KeyConverter,AllEnglishKeyboardTextChars)329 TEST(KeyConverter, AllEnglishKeyboardTextChars) {
330   ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
331   std::string kLowerChars = "0123456789abcdefghijklmnopqrstuvwxyz";
332   std::string kUpperChars = ")!@#$%^&*(ABCDEFGHIJKLMNOPQRSTUVWXYZ";
333   for (size_t i = 0; i < kLowerChars.length(); ++i) {
334     int offset = 0;
335     if (i < 10)
336       offset = ui::VKEY_0;
337     else
338       offset = ui::VKEY_0 + 7;
339     ui::KeyboardCode expected_code = static_cast<ui::KeyboardCode>(offset + i);
340     CheckNonShiftChar(expected_code, kLowerChars[i]);
341   }
342   for (size_t i = 0; i < kUpperChars.length(); ++i) {
343     int offset = 0;
344     if (i < 10)
345       offset = ui::VKEY_0;
346     else
347       offset = ui::VKEY_0 + 7;
348     ui::KeyboardCode expected_code = static_cast<ui::KeyboardCode>(offset + i);
349     CheckShiftChar(expected_code, kUpperChars[i], kLowerChars[i]);
350   }
351 }
352 
TEST(KeyConverter,AllSpecialWebDriverKeysOnEnglishKeyboard)353 TEST(KeyConverter, AllSpecialWebDriverKeysOnEnglishKeyboard) {
354   ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
355   const char kTextForKeys[] = {
356 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
357       0, 0, 0, 0, '\t', 0, '\r', '\r', 0, 0, 0, 0, 0,
358 #else
359       0, 0, 0, 0, 0, 0, '\r', '\r', 0, 0, 0, 0, 0,
360 #endif
361       ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ';', '=',
362       '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
363       '*', '+', ',', '-', '.', '/'};
364   for (size_t i = 0; i <= 0x3D; ++i) {
365     if (i > 0x29 && i < 0x31)
366       continue;
367     base::string16 keys;
368     int modifiers = 0;
369     keys.push_back(0xE000U + i);
370     std::vector<KeyEvent> events;
371     EXPECT_EQ(kOk, ConvertKeysToKeyEvents(keys,
372                                           true /* release_modifiers */,
373                                           &modifiers, &events).code())
374         << "Index: " << i;
375     if (i == 0) {
376       EXPECT_EQ(0u, events.size()) << "Index: " << i;
377     } else if (i >= base::size(kTextForKeys) || kTextForKeys[i] == 0) {
378       EXPECT_EQ(2u, events.size()) << "Index: " << i;
379     } else {
380       ASSERT_EQ(3u, events.size()) << "Index: " << i;
381       std::vector<KeyEvent>::const_iterator it = events.begin();
382       ++it;  // Move to the second event.
383       ASSERT_EQ(1u, it->unmodified_text.length()) << "Index: " << i;
384       EXPECT_EQ(kTextForKeys[i], it->unmodified_text[0])
385           << "Index: " << i;
386     }
387   }
388 }
389 
TEST(KeyConverter,ModifiersState)390 TEST(KeyConverter, ModifiersState) {
391   KeyEventBuilder builder;
392   builder.SetType(kRawKeyDownEventType);
393   std::vector<KeyEvent> key_events;
394   key_events.push_back(builder.SetKeyCode(ui::VKEY_SHIFT)
395                            ->AddModifiers(kShiftKeyModifierMask)
396                            ->Build());
397   key_events.push_back(builder.SetKeyCode(ui::VKEY_CONTROL)
398                            ->AddModifiers(kControlKeyModifierMask)
399                            ->Build());
400   key_events.push_back(builder.SetKeyCode(ui::VKEY_MENU)
401                            ->AddModifiers(kAltKeyModifierMask)
402                            ->Build());
403   key_events.push_back(builder.SetKeyCode(ui::VKEY_COMMAND)
404                            ->AddModifiers(kMetaKeyModifierMask)
405                            ->Build());
406 
407   base::string16 keys;
408   keys.push_back(static_cast<base::char16>(0xE008U));
409   keys.push_back(static_cast<base::char16>(0xE009U));
410   keys.push_back(static_cast<base::char16>(0xE00AU));
411   keys.push_back(static_cast<base::char16>(0xE03DU));
412 
413   CheckEvents(keys, key_events, false /* release_modifiers */,
414               kShiftKeyModifierMask | kControlKeyModifierMask |
415                   kAltKeyModifierMask | kMetaKeyModifierMask);
416 }
417 
TEST(KeyConverter,ReleaseModifiers)418 TEST(KeyConverter, ReleaseModifiers) {
419   std::vector<KeyEvent> key_events;
420   KeyEventBuilder builder;
421   key_events.push_back(builder.SetType(kRawKeyDownEventType)
422                            ->SetKeyCode(ui::VKEY_SHIFT)
423                            ->AddModifiers(kShiftKeyModifierMask)
424                            ->Build());
425   key_events.push_back(builder.SetKeyCode(ui::VKEY_CONTROL)
426                            ->AddModifiers(kControlKeyModifierMask)
427                            ->Build());
428   key_events.push_back(builder.SetType(kKeyUpEventType)
429                            ->SetKeyCode(ui::VKEY_SHIFT)
430                            ->SetModifiers(0)
431                            ->Build());
432   key_events.push_back(builder.SetKeyCode(ui::VKEY_CONTROL)->Build());
433   base::string16 keys;
434   keys.push_back(static_cast<base::char16>(0xE008U));
435   keys.push_back(static_cast<base::char16>(0xE009U));
436 
437   CheckEvents(keys, key_events, true /* release_modifiers */, 0);
438 }
439