1 // Copyright 2014 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 "ui/chromeos/events/event_rewriter_chromeos.h"
6 
7 #include <fcntl.h>
8 #include <stddef.h>
9 
10 #include "base/feature_list.h"
11 #include "base/files/file_path.h"
12 #include "base/files/scoped_file.h"
13 #include "base/logging.h"
14 #include "base/metrics/user_metrics.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_split.h"
18 #include "base/strings/string_util.h"
19 #include "base/system/sys_info.h"
20 #include "chromeos/constants/chromeos_features.h"
21 #include "chromeos/constants/chromeos_switches.h"
22 #include "device/udev_linux/scoped_udev.h"
23 #include "ui/base/ime/chromeos/ime_keyboard.h"
24 #include "ui/base/ime/chromeos/input_method_manager.h"
25 #include "ui/base/ui_base_features.h"
26 #include "ui/chromeos/events/modifier_key.h"
27 #include "ui/chromeos/events/pref_names.h"
28 #include "ui/events/devices/device_data_manager.h"
29 #include "ui/events/event_utils.h"
30 #include "ui/events/keycodes/dom/dom_code.h"
31 #include "ui/events/keycodes/dom/keycode_converter.h"
32 #include "ui/events/keycodes/keyboard_code_conversion.h"
33 #include "ui/events/ozone/evdev/event_device_info.h"
34 
35 namespace ui {
36 
37 namespace {
38 
39 // Hotrod controller vendor/product ids.
40 const int kHotrodRemoteVendorId = 0x0471;
41 const int kHotrodRemoteProductId = 0x21cc;
42 
43 // Flag masks for remapping alt+click or search+click to right click.
44 constexpr int kAltLeftButton = (EF_ALT_DOWN | EF_LEFT_MOUSE_BUTTON);
45 constexpr int kSearchLeftButton = (EF_COMMAND_DOWN | EF_LEFT_MOUSE_BUTTON);
46 
47 // Table of properties of remappable keys and/or remapping targets (not
48 // strictly limited to "modifiers").
49 //
50 // This is used in two distinct ways: for rewriting key up/down events,
51 // and for rewriting modifier EventFlags on any kind of event.
52 //
53 // For the first case, rewriting key up/down events, |RewriteModifierKeys()|
54 // determines the preference name |prefs::kLanguageRemap...KeyTo| for the
55 // incoming key and, using |GetRemappedKey()|, gets the user preference
56 // value |input_method::k...Key| for the incoming key, and finally finds that
57 // value in this table to obtain the |result| properties of the target key.
58 //
59 // For the second case, rewriting modifier EventFlags,
60 // |GetRemappedModifierMasks()| processes every table entry whose |flag|
61 // is set in the incoming event. Using the |pref_name| in the table entry,
62 // it likewise uses |GetRemappedKey()| to find the properties of the
63 // user preference target key, and replaces the flag accordingly.
64 const struct ModifierRemapping {
65   int flag;
66   chromeos::ModifierKey remap_to;
67   const char* pref_name;
68   EventRewriterChromeOS::MutableKeyState result;
69 } kModifierRemappings[] = {
70     {EF_CONTROL_DOWN,
71      chromeos::ModifierKey::kControlKey,
72      prefs::kLanguageRemapControlKeyTo,
73      {EF_CONTROL_DOWN, DomCode::CONTROL_LEFT, DomKey::CONTROL, VKEY_CONTROL}},
74     {// kModifierRemappingNeoMod3 references this entry by index.
75      EF_MOD3_DOWN | EF_ALTGR_DOWN,
76      chromeos::ModifierKey::kNumModifierKeys,
77      nullptr,
78      {EF_MOD3_DOWN | EF_ALTGR_DOWN, DomCode::CAPS_LOCK, DomKey::ALT_GRAPH,
79       VKEY_ALTGR}},
80     {EF_COMMAND_DOWN,
81      chromeos::ModifierKey::kSearchKey,
82      prefs::kLanguageRemapSearchKeyTo,
83      {EF_COMMAND_DOWN, DomCode::META_LEFT, DomKey::META, VKEY_LWIN}},
84     {EF_ALT_DOWN,
85      chromeos::ModifierKey::kAltKey,
86      prefs::kLanguageRemapAltKeyTo,
87      {EF_ALT_DOWN, DomCode::ALT_LEFT, DomKey::ALT, VKEY_MENU}},
88     {EF_NONE,
89      chromeos::ModifierKey::kVoidKey,
90      nullptr,
91      {EF_NONE, DomCode::NONE, DomKey::NONE, VKEY_UNKNOWN}},
92     {EF_MOD3_DOWN,
93      chromeos::ModifierKey::kCapsLockKey,
94      prefs::kLanguageRemapCapsLockKeyTo,
95      {EF_MOD3_DOWN, DomCode::CAPS_LOCK, DomKey::CAPS_LOCK, VKEY_CAPITAL}},
96     {EF_NONE,
97      chromeos::ModifierKey::kEscapeKey,
98      prefs::kLanguageRemapEscapeKeyTo,
99      {EF_NONE, DomCode::ESCAPE, DomKey::ESCAPE, VKEY_ESCAPE}},
100     {EF_NONE,
101      chromeos::ModifierKey::kBackspaceKey,
102      prefs::kLanguageRemapBackspaceKeyTo,
103      {EF_NONE, DomCode::BACKSPACE, DomKey::BACKSPACE, VKEY_BACK}},
104     {EF_NONE,
105      chromeos::ModifierKey::kAssistantKey,
106      prefs::kLanguageRemapAssistantKeyTo,
107      {EF_NONE, DomCode::LAUNCH_ASSISTANT, DomKey::LAUNCH_ASSISTANT,
108       VKEY_ASSISTANT}}};
109 
110 const EventRewriterChromeOS::MutableKeyState kCustomTopRowLayoutFKeys[] = {
111     {EF_NONE, DomCode::F1, DomKey::F1, VKEY_F1},
112     {EF_NONE, DomCode::F2, DomKey::F2, VKEY_F2},
113     {EF_NONE, DomCode::F3, DomKey::F3, VKEY_F3},
114     {EF_NONE, DomCode::F4, DomKey::F4, VKEY_F4},
115     {EF_NONE, DomCode::F5, DomKey::F5, VKEY_F5},
116     {EF_NONE, DomCode::F6, DomKey::F6, VKEY_F6},
117     {EF_NONE, DomCode::F7, DomKey::F7, VKEY_F7},
118     {EF_NONE, DomCode::F8, DomKey::F8, VKEY_F8},
119     {EF_NONE, DomCode::F9, DomKey::F9, VKEY_F9},
120     {EF_NONE, DomCode::F10, DomKey::F10, VKEY_F10},
121     {EF_NONE, DomCode::F11, DomKey::F11, VKEY_F11},
122     {EF_NONE, DomCode::F12, DomKey::F12, VKEY_F12},
123     {EF_NONE, DomCode::F13, DomKey::F13, VKEY_F13},
124     {EF_NONE, DomCode::F14, DomKey::F14, VKEY_F14},
125     {EF_NONE, DomCode::F15, DomKey::F15, VKEY_F15},
126 };
127 const size_t kAllFKeysSize = base::size(kCustomTopRowLayoutFKeys);
128 constexpr KeyboardCode kMaxCustomTopRowLayoutFKeyCode = VKEY_F15;
129 
IsCustomLayoutFunctionKey(KeyboardCode key_code)130 bool IsCustomLayoutFunctionKey(KeyboardCode key_code) {
131   return key_code >= VKEY_F1 && key_code <= kMaxCustomTopRowLayoutFKeyCode;
132 }
133 
134 const ModifierRemapping* kModifierRemappingNeoMod3 = &kModifierRemappings[1];
135 
136 // Gets a remapped key for |pref_name| key. For example, to find out which
137 // key Ctrl is currently remapped to, call the function with
138 // prefs::kLanguageRemapControlKeyTo.
139 // Note: For the Search key, call GetSearchRemappedKey().
GetRemappedKey(const std::string & pref_name,EventRewriterChromeOS::Delegate * delegate)140 const ModifierRemapping* GetRemappedKey(
141     const std::string& pref_name,
142     EventRewriterChromeOS::Delegate* delegate) {
143   if (!delegate)
144     return nullptr;
145 
146   int value = -1;
147   if (!delegate->GetKeyboardRemappedPrefValue(pref_name, &value))
148     return nullptr;
149 
150   for (auto& remapping : kModifierRemappings) {
151     if (value == static_cast<int>(remapping.remap_to))
152       return &remapping;
153   }
154 
155   return nullptr;
156 }
157 
158 // Gets a remapped key for the Search key based on the |keyboard_type| of the
159 // last event. Internal Search key, Command key on external Apple keyboards, and
160 // Meta key (either Search or Windows) on external non-Apple keyboards can all
161 // be remapped separately.
GetSearchRemappedKey(EventRewriterChromeOS::Delegate * delegate,EventRewriterChromeOS::DeviceType keyboard_type)162 const ModifierRemapping* GetSearchRemappedKey(
163     EventRewriterChromeOS::Delegate* delegate,
164     EventRewriterChromeOS::DeviceType keyboard_type) {
165   std::string pref_name;
166   switch (keyboard_type) {
167     case EventRewriterChromeOS::kDeviceExternalAppleKeyboard:
168       pref_name = prefs::kLanguageRemapExternalCommandKeyTo;
169       break;
170 
171     case EventRewriterChromeOS::kDeviceExternalGenericKeyboard:
172     case EventRewriterChromeOS::kDeviceExternalUnknown:
173       pref_name = prefs::kLanguageRemapExternalMetaKeyTo;
174       break;
175 
176     case EventRewriterChromeOS::kDeviceExternalChromeOsKeyboard:
177     case EventRewriterChromeOS::kDeviceInternalKeyboard:
178     case EventRewriterChromeOS::kDeviceHotrodRemote:
179     case EventRewriterChromeOS::kDeviceVirtualCoreKeyboard:
180     case EventRewriterChromeOS::kDeviceUnknown:
181       // Use the preference for internal Search key remapping.
182       pref_name = prefs::kLanguageRemapSearchKeyTo;
183       break;
184   }
185 
186   return GetRemappedKey(pref_name, delegate);
187 }
188 
IsISOLevel5ShiftUsedByCurrentInputMethod()189 bool IsISOLevel5ShiftUsedByCurrentInputMethod() {
190   // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask,
191   // it's not possible to make both features work. For now, we don't remap
192   // Mod3Mask when Neo2 is in use.
193   // TODO(yusukes): Remove the restriction.
194   ::chromeos::input_method::InputMethodManager* manager =
195       ::chromeos::input_method::InputMethodManager::Get();
196   return manager->IsISOLevel5ShiftUsedByCurrentInputMethod();
197 }
198 
199 struct KeyboardRemapping {
200   // MatchKeyboardRemapping() succeeds if the tested has all of the specified
201   // flags (and possibly other flags), and either the key_code matches or the
202   // condition's key_code is VKEY_UNKNOWN.
203   struct Condition {
204     int flags;
205     KeyboardCode key_code;
206   } condition;
207   // ApplyRemapping(), which is the primary user of this structure,
208   // conditionally sets the output fields from the |result| here.
209   // - |dom_code| is set if |result.dom_code| is not NONE.
210   // - |dom_key| and |character| are set if |result.dom_key| is not NONE.
211   // -|key_code| is set if |result.key_code| is not VKEY_UNKNOWN.
212   // - |flags| are always set from |result.flags|, but this can be |EF_NONE|.
213   EventRewriterChromeOS::MutableKeyState result;
214 };
215 
216 // If |strict| is true, the flags must match exactly the same. In other words,
217 // the event will be rewritten only if the exactly specified modifier is
218 // pressed.  If false, it can match even if other modifiers are pressed.
MatchKeyboardRemapping(const EventRewriterChromeOS::MutableKeyState & suspect,const KeyboardRemapping::Condition & test,bool strict=false)219 bool MatchKeyboardRemapping(
220     const EventRewriterChromeOS::MutableKeyState& suspect,
221     const KeyboardRemapping::Condition& test,
222     bool strict = false) {
223   // Reset non modifier key event related flags for strict mode.
224   constexpr int kKeyEventModifiersMask = EF_SHIFT_DOWN | EF_CONTROL_DOWN |
225                                          EF_ALT_DOWN | EF_COMMAND_DOWN |
226                                          EF_ALTGR_DOWN | EF_MOD3_DOWN;
227 
228   const int suspect_flags_for_strict = suspect.flags & kKeyEventModifiersMask;
229   const bool flag_matched = strict
230                                 ? suspect_flags_for_strict == test.flags
231                                 : ((suspect.flags & test.flags) == test.flags);
232   return flag_matched && ((test.key_code == VKEY_UNKNOWN) ||
233                           (test.key_code == suspect.key_code));
234 }
235 
ApplyRemapping(const EventRewriterChromeOS::MutableKeyState & changes,EventRewriterChromeOS::MutableKeyState * state)236 void ApplyRemapping(const EventRewriterChromeOS::MutableKeyState& changes,
237                     EventRewriterChromeOS::MutableKeyState* state) {
238   state->flags |= changes.flags;
239   if (changes.code != DomCode::NONE)
240     state->code = changes.code;
241   if (changes.key != DomKey::NONE)
242     state->key = changes.key;
243   if (changes.key_code != VKEY_UNKNOWN)
244     state->key_code = changes.key_code;
245 }
246 
247 // Given a set of KeyboardRemapping structs, finds a matching struct
248 // if possible, and updates the remapped event values. Returns true if a
249 // remapping was found and remapped values were updated.
250 // See MatchKeyboardRemapping() for |strict|.
RewriteWithKeyboardRemappings(const KeyboardRemapping * mappings,size_t num_mappings,const EventRewriterChromeOS::MutableKeyState & input_state,EventRewriterChromeOS::MutableKeyState * remapped_state,bool strict=false)251 bool RewriteWithKeyboardRemappings(
252     const KeyboardRemapping* mappings,
253     size_t num_mappings,
254     const EventRewriterChromeOS::MutableKeyState& input_state,
255     EventRewriterChromeOS::MutableKeyState* remapped_state,
256     bool strict = false) {
257   for (size_t i = 0; i < num_mappings; ++i) {
258     const KeyboardRemapping& map = mappings[i];
259     if (MatchKeyboardRemapping(input_state, map.condition, strict)) {
260       remapped_state->flags = (input_state.flags & ~map.condition.flags);
261       ApplyRemapping(map.result, remapped_state);
262       return true;
263     }
264   }
265   return false;
266 }
267 
SetMeaningForLayout(EventType type,EventRewriterChromeOS::MutableKeyState * state)268 void SetMeaningForLayout(EventType type,
269                          EventRewriterChromeOS::MutableKeyState* state) {
270   // Currently layout is applied by creating a temporary key event with the
271   // current physical state, and extracting the layout results.
272   KeyEvent key(type, state->key_code, state->code, state->flags);
273   state->key = key.GetDomKey();
274 }
275 
RelocateModifier(DomCode code,DomKeyLocation location)276 DomCode RelocateModifier(DomCode code, DomKeyLocation location) {
277   bool right = (location == DomKeyLocation::RIGHT);
278   switch (code) {
279     case DomCode::CONTROL_LEFT:
280     case DomCode::CONTROL_RIGHT:
281       return right ? DomCode::CONTROL_RIGHT : DomCode::CONTROL_LEFT;
282     case DomCode::SHIFT_LEFT:
283     case DomCode::SHIFT_RIGHT:
284       return right ? DomCode::SHIFT_RIGHT : DomCode::SHIFT_LEFT;
285     case DomCode::ALT_LEFT:
286     case DomCode::ALT_RIGHT:
287       return right ? DomCode::ALT_RIGHT : DomCode::ALT_LEFT;
288     case DomCode::META_LEFT:
289     case DomCode::META_RIGHT:
290       return right ? DomCode::META_RIGHT : DomCode::META_LEFT;
291     default:
292       break;
293   }
294   return code;
295 }
296 
297 // Returns true if |mouse_event| was generated from a touchpad device.
IsFromTouchpadDevice(const MouseEvent & mouse_event)298 bool IsFromTouchpadDevice(const MouseEvent& mouse_event) {
299   for (const InputDevice& touchpad :
300        DeviceDataManager::GetInstance()->GetTouchpadDevices()) {
301     if (touchpad.id == mouse_event.source_device_id())
302       return true;
303   }
304 
305   return false;
306 }
307 
308 // Returns true if |value| is replaced with the specific device property value
309 // without getting an error.
GetDeviceProperty(const base::FilePath & device_path,const char * key,std::string * value)310 bool GetDeviceProperty(const base::FilePath& device_path,
311                        const char* key,
312                        std::string* value) {
313   device::ScopedUdevPtr udev(device::udev_new());
314   if (!udev.get())
315     return false;
316 
317   device::ScopedUdevDevicePtr device(device::udev_device_new_from_syspath(
318       udev.get(), device_path.value().c_str()));
319   if (!device.get())
320     return false;
321 
322   *value = device::UdevDeviceGetPropertyValue(device.get(), key);
323   return true;
324 }
325 
326 // Returns true if |value| is replaced with the specific device attribute value
327 // without getting an error. |device_path| should be obtained from the
328 // |InputDevice.sys_path| field.
GetDeviceAttributeRecursive(const base::FilePath & device_path,const char * key,std::string * value)329 bool GetDeviceAttributeRecursive(const base::FilePath& device_path,
330                                  const char* key,
331                                  std::string* value) {
332   device::ScopedUdevPtr udev(device::udev_new());
333   if (!udev.get())
334     return false;
335 
336   device::ScopedUdevDevicePtr device(device::udev_device_new_from_syspath(
337       udev.get(), device_path.value().c_str()));
338   if (!device.get())
339     return false;
340 
341   *value = device::UdevDeviceRecursiveGetSysattrValue(device.get(), key);
342   return true;
343 }
344 
345 constexpr char kLayoutProperty[] = "CROS_KEYBOARD_TOP_ROW_LAYOUT";
346 constexpr char kCustomTopRowLayoutAttribute[] = "function_row_physmap";
347 constexpr char kCustomTopRowLayoutProperty[] = "FUNCTION_ROW_PHYSMAP";
348 
GetTopRowLayoutProperty(const InputDevice & keyboard_device,std::string * out_prop)349 bool GetTopRowLayoutProperty(const InputDevice& keyboard_device,
350                              std::string* out_prop) {
351   return GetDeviceProperty(keyboard_device.sys_path, kLayoutProperty, out_prop);
352 }
353 
354 // Parses keyboard to row layout string. Returns true if data is valid.
ParseKeyboardTopRowLayout(const std::string & layout_string,EventRewriterChromeOS::KeyboardTopRowLayout * out_layout)355 bool ParseKeyboardTopRowLayout(
356     const std::string& layout_string,
357     EventRewriterChromeOS::KeyboardTopRowLayout* out_layout) {
358   if (layout_string.empty()) {
359     *out_layout = EventRewriterChromeOS::kKbdTopRowLayoutDefault;
360     return true;
361   }
362 
363   int layout_id;
364   if (!base::StringToInt(layout_string, &layout_id)) {
365     LOG(WARNING) << "Failed to parse layout " << kLayoutProperty << " value '"
366                  << layout_string << "'";
367     return false;
368   }
369   if (layout_id < EventRewriterChromeOS::kKbdTopRowLayoutMin ||
370       layout_id > EventRewriterChromeOS::kKbdTopRowLayoutMax) {
371     LOG(WARNING) << "Invalid " << kLayoutProperty << " '" << layout_string
372                  << "'";
373     return false;
374   }
375   *out_layout =
376       static_cast<EventRewriterChromeOS::KeyboardTopRowLayout>(layout_id);
377   return true;
378 }
379 
380 // Parses the custom top row layout string. The string contains a space
381 // separated list of scan codes in hex. eg "aa ab ac" for F1, F2, F3, etc.
382 // Returns true if the string can be parsed.
ParseCustomTopRowLayoutMap(const std::string & layout,base::flat_map<uint32_t,EventRewriterChromeOS::MutableKeyState> * out_scan_code_map)383 bool ParseCustomTopRowLayoutMap(
384     const std::string& layout,
385     base::flat_map<uint32_t, EventRewriterChromeOS::MutableKeyState>*
386         out_scan_code_map) {
387   const std::vector<std::string> scan_code_strings = base::SplitString(
388       layout, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
389   if (scan_code_strings.size() == 0 ||
390       scan_code_strings.size() > kAllFKeysSize) {
391     return false;
392   }
393 
394   base::flat_map<uint32_t, EventRewriterChromeOS::MutableKeyState>
395       scan_code_map;
396   for (size_t i = 0; i < scan_code_strings.size(); i++) {
397     uint32_t scan_code = 0;
398     if (!base::HexStringToUInt(scan_code_strings[i], &scan_code)) {
399       return false;
400     }
401 
402     scan_code_map[scan_code] = kCustomTopRowLayoutFKeys[i];
403   }
404 
405   *out_scan_code_map = std::move(scan_code_map);
406   return true;
407 }
408 
GetCustomTopRowLayoutAttribute(const InputDevice & keyboard_device,std::string * out_prop)409 bool GetCustomTopRowLayoutAttribute(const InputDevice& keyboard_device,
410                                     std::string* out_prop) {
411   bool result = GetDeviceAttributeRecursive(
412       keyboard_device.sys_path, kCustomTopRowLayoutAttribute, out_prop);
413 
414   if (result && out_prop->size() > 0) {
415     VLOG(1) << "Identified custom top row keyboard layout: sys_path="
416             << keyboard_device.sys_path << " layout=" << *out_prop;
417     return true;
418   }
419 
420   return false;
421 }
422 
GetCustomTopRowLayout(const InputDevice & keyboard_device,std::string * out_prop)423 bool GetCustomTopRowLayout(const InputDevice& keyboard_device,
424                            std::string* out_prop) {
425   if (GetCustomTopRowLayoutAttribute(keyboard_device, out_prop))
426     return true;
427   return GetDeviceProperty(keyboard_device.sys_path,
428                            kCustomTopRowLayoutProperty, out_prop);
429 }
430 
HasCustomTopRowLayout(const InputDevice & keyboard_device)431 bool HasCustomTopRowLayout(const InputDevice& keyboard_device) {
432   std::string layout;
433   base::flat_map<uint32_t, EventRewriterChromeOS::MutableKeyState> top_row_map;
434   return GetCustomTopRowLayout(keyboard_device, &layout) &&
435          ParseCustomTopRowLayoutMap(layout, &top_row_map);
436 }
437 
438 // Returns whether |key_code| appears as one of the key codes that might be
439 // remapped by table mappings.
IsKeyCodeInMappings(KeyboardCode key_code,const KeyboardRemapping * mappings,size_t num_mappings)440 bool IsKeyCodeInMappings(KeyboardCode key_code,
441                          const KeyboardRemapping* mappings,
442                          size_t num_mappings) {
443   for (size_t i = 0; i < num_mappings; ++i) {
444     const KeyboardRemapping& map = mappings[i];
445     if (key_code == map.condition.key_code) {
446       return true;
447     }
448   }
449   return false;
450 }
451 
452 // Returns true if all bits in |flag_mask| are set in |flags|.
AreFlagsSet(int flags,int flag_mask)453 bool AreFlagsSet(int flags, int flag_mask) {
454   return (flags & flag_mask) == flag_mask;
455 }
456 
457 // Determines the type of |keyboard_device| we are dealing with.
458 // |has_chromeos_top_row| argument indicates that the keyboard's top
459 // row has "action" keys (such as back, refresh, etc.) instead of the
460 // standard F1-F12 keys.
IdentifyKeyboardType(const InputDevice & keyboard_device,bool has_chromeos_top_row)461 EventRewriterChromeOS::DeviceType IdentifyKeyboardType(
462     const InputDevice& keyboard_device,
463     bool has_chromeos_top_row) {
464   if (keyboard_device.vendor_id == kHotrodRemoteVendorId &&
465       keyboard_device.product_id == kHotrodRemoteProductId) {
466     VLOG(1) << "Hotrod remote '" << keyboard_device.name
467             << "' connected: id=" << keyboard_device.id;
468     return EventRewriterChromeOS::kDeviceHotrodRemote;
469   }
470 
471   if (base::LowerCaseEqualsASCII(keyboard_device.name,
472                                  "virtual core keyboard")) {
473     VLOG(1) << "Xorg virtual '" << keyboard_device.name
474             << "' connected: id=" << keyboard_device.id;
475     return EventRewriterChromeOS::kDeviceVirtualCoreKeyboard;
476   }
477 
478   if (keyboard_device.type == INPUT_DEVICE_INTERNAL) {
479     VLOG(1) << "Internal keyboard '" << keyboard_device.name
480             << "' connected: id=" << keyboard_device.id;
481     return EventRewriterChromeOS::kDeviceInternalKeyboard;
482   }
483 
484   // This is an external device.
485   if (has_chromeos_top_row) {
486     // If the device was tagged as having Chrome OS top row layout it must be a
487     // Chrome OS keyboard.
488     VLOG(1) << "External Chrome OS keyboard '" << keyboard_device.name
489             << "' connected: id=" << keyboard_device.id;
490     return EventRewriterChromeOS::kDeviceExternalChromeOsKeyboard;
491   }
492 
493   const std::vector<std::string> tokens =
494       base::SplitString(keyboard_device.name, " .", base::KEEP_WHITESPACE,
495                         base::SPLIT_WANT_NONEMPTY);
496 
497   // Parse |device_name| to help classify it.
498   bool found_apple = false;
499   bool found_keyboard = false;
500   for (size_t i = 0; i < tokens.size(); ++i) {
501     if (!found_apple && base::LowerCaseEqualsASCII(tokens[i], "apple"))
502       found_apple = true;
503     if (!found_keyboard && base::LowerCaseEqualsASCII(tokens[i], "keyboard"))
504       found_keyboard = true;
505   }
506   if (found_apple) {
507     // If the |device_name| contains the two words, "apple" and "keyboard",
508     // treat it as an Apple keyboard.
509     if (found_keyboard) {
510       VLOG(1) << "Apple keyboard '" << keyboard_device.name
511               << "' connected: id=" << keyboard_device.id;
512       return EventRewriterChromeOS::kDeviceExternalAppleKeyboard;
513     } else {
514       VLOG(1) << "Apple device '" << keyboard_device.name
515               << "' connected: id=" << keyboard_device.id;
516       return EventRewriterChromeOS::kDeviceExternalUnknown;
517     }
518   } else if (found_keyboard) {
519     VLOG(1) << "External keyboard '" << keyboard_device.name
520             << "' connected: id=" << keyboard_device.id;
521     return EventRewriterChromeOS::kDeviceExternalGenericKeyboard;
522   } else {
523     VLOG(1) << "External device '" << keyboard_device.name
524             << "' connected: id=" << keyboard_device.id;
525     return EventRewriterChromeOS::kDeviceExternalUnknown;
526   }
527 }
528 
IdentifyKeyboard(const InputDevice & keyboard_device,EventRewriterChromeOS::DeviceType * out_type,EventRewriterChromeOS::KeyboardTopRowLayout * out_layout)529 bool IdentifyKeyboard(const InputDevice& keyboard_device,
530                       EventRewriterChromeOS::DeviceType* out_type,
531                       EventRewriterChromeOS::KeyboardTopRowLayout* out_layout) {
532   std::string layout_string;
533   EventRewriterChromeOS::KeyboardTopRowLayout layout;
534   const bool has_custom_top_row = HasCustomTopRowLayout(keyboard_device);
535   if (has_custom_top_row) {
536     layout = EventRewriterChromeOS::kKbdTopRowLayoutCustom;
537   } else if (!GetTopRowLayoutProperty(keyboard_device, &layout_string) ||
538              !ParseKeyboardTopRowLayout(layout_string, &layout)) {
539     *out_type = EventRewriterChromeOS::kDeviceUnknown;
540     *out_layout = EventRewriterChromeOS::kKbdTopRowLayoutDefault;
541     return false;
542   }
543 
544   *out_type = IdentifyKeyboardType(
545       keyboard_device, has_custom_top_row || !layout_string.empty());
546   *out_layout = layout;
547   return true;
548 }
549 
550 }  // namespace
551 
552 ///////////////////////////////////////////////////////////////////////////////
553 
MutableKeyState()554 EventRewriterChromeOS::MutableKeyState::MutableKeyState()
555     : MutableKeyState(0, DomCode::NONE, 0, KeyboardCode::VKEY_NONAME) {}
556 
MutableKeyState(const KeyEvent * key_event)557 EventRewriterChromeOS::MutableKeyState::MutableKeyState(
558     const KeyEvent* key_event)
559     : MutableKeyState(key_event->flags(),
560                       key_event->code(),
561                       key_event->GetDomKey(),
562                       key_event->key_code()) {}
563 
MutableKeyState(int input_flags,DomCode input_code,DomKey::Base input_key,KeyboardCode input_key_code)564 EventRewriterChromeOS::MutableKeyState::MutableKeyState(
565     int input_flags,
566     DomCode input_code,
567     DomKey::Base input_key,
568     KeyboardCode input_key_code)
569     : flags(input_flags),
570       code(input_code),
571       key(input_key),
572       key_code(input_key_code) {}
573 
574 ///////////////////////////////////////////////////////////////////////////////
575 
EventRewriterChromeOS(Delegate * delegate,EventRewriter * sticky_keys_controller,bool privacy_screen_supported)576 EventRewriterChromeOS::EventRewriterChromeOS(
577     Delegate* delegate,
578     EventRewriter* sticky_keys_controller,
579     bool privacy_screen_supported)
580     : last_keyboard_device_id_(ED_UNKNOWN_DEVICE),
581       ime_keyboard_for_testing_(nullptr),
582       delegate_(delegate),
583       sticky_keys_controller_(sticky_keys_controller),
584       privacy_screen_supported_(privacy_screen_supported),
585       pressed_modifier_latches_(EF_NONE),
586       latched_modifier_latches_(EF_NONE),
587       used_modifier_latches_(EF_NONE) {}
588 
~EventRewriterChromeOS()589 EventRewriterChromeOS::~EventRewriterChromeOS() {}
590 
KeyboardDeviceAddedForTesting(int device_id)591 void EventRewriterChromeOS::KeyboardDeviceAddedForTesting(int device_id) {
592   KeyboardDeviceAdded(device_id);
593 }
594 
ResetStateForTesting()595 void EventRewriterChromeOS::ResetStateForTesting() {
596   pressed_key_states_.clear();
597 
598   pressed_modifier_latches_ = latched_modifier_latches_ =
599       used_modifier_latches_ = EF_NONE;
600 }
601 
RewriteMouseButtonEventForTesting(const MouseEvent & event,const Continuation continuation)602 void EventRewriterChromeOS::RewriteMouseButtonEventForTesting(
603     const MouseEvent& event,
604     const Continuation continuation) {
605   RewriteMouseButtonEvent(event, continuation);
606 }
607 
RewriteEvent(const Event & event,const Continuation continuation)608 EventDispatchDetails EventRewriterChromeOS::RewriteEvent(
609     const Event& event,
610     const Continuation continuation) {
611   if ((event.type() == ET_KEY_PRESSED) || (event.type() == ET_KEY_RELEASED)) {
612     std::unique_ptr<Event> rewritten_event;
613     EventRewriteStatus status =
614         RewriteKeyEvent(*((&event)->AsKeyEvent()), &rewritten_event);
615     return RewriteKeyEventInContext(*((&event)->AsKeyEvent()),
616                                     std::move(rewritten_event), status,
617                                     continuation);
618   }
619   if ((event.type() == ET_MOUSE_PRESSED) ||
620       (event.type() == ET_MOUSE_RELEASED)) {
621     return RewriteMouseButtonEvent(static_cast<const MouseEvent&>(event),
622                                    continuation);
623   }
624   if (event.type() == ET_MOUSEWHEEL) {
625     return RewriteMouseWheelEvent(static_cast<const MouseWheelEvent&>(event),
626                                   continuation);
627   }
628   if ((event.type() == ET_TOUCH_PRESSED) ||
629       (event.type() == ET_TOUCH_RELEASED)) {
630     return RewriteTouchEvent(static_cast<const TouchEvent&>(event),
631                              continuation);
632   }
633   if (event.IsScrollEvent()) {
634     return RewriteScrollEvent(static_cast<const ScrollEvent&>(event),
635                               continuation);
636   }
637 
638   return SendEvent(continuation, &event);
639 }
640 
BuildRewrittenKeyEvent(const KeyEvent & key_event,const MutableKeyState & state,std::unique_ptr<Event> * rewritten_event)641 void EventRewriterChromeOS::BuildRewrittenKeyEvent(
642     const KeyEvent& key_event,
643     const MutableKeyState& state,
644     std::unique_ptr<Event>* rewritten_event) {
645   auto key_event_ptr = std::make_unique<KeyEvent>(
646       key_event.type(), state.key_code, state.code, state.flags, state.key,
647       key_event.time_stamp());
648   key_event_ptr->set_scan_code(key_event.scan_code());
649   *rewritten_event = std::move(key_event_ptr);
650 }
651 
652 // static
GetDeviceType(const InputDevice & keyboard_device)653 EventRewriterChromeOS::DeviceType EventRewriterChromeOS::GetDeviceType(
654     const InputDevice& keyboard_device) {
655   DeviceType type;
656   KeyboardTopRowLayout layout;
657   if (IdentifyKeyboard(keyboard_device, &type, &layout))
658     return type;
659 
660   return EventRewriterChromeOS::kDeviceUnknown;
661 }
662 
663 // static
664 EventRewriterChromeOS::KeyboardTopRowLayout
GetKeyboardTopRowLayout(const InputDevice & keyboard_device)665 EventRewriterChromeOS::GetKeyboardTopRowLayout(
666     const InputDevice& keyboard_device) {
667   DeviceType type;
668   KeyboardTopRowLayout layout;
669   if (IdentifyKeyboard(keyboard_device, &type, &layout))
670     return layout;
671 
672   return kKbdTopRowLayoutDefault;
673 }
674 
675 // static
HasAssistantKeyOnKeyboard(const InputDevice & keyboard_device,bool * has_assistant_key)676 bool EventRewriterChromeOS::HasAssistantKeyOnKeyboard(
677     const InputDevice& keyboard_device,
678     bool* has_assistant_key) {
679   const char kDevNameProperty[] = "DEVNAME";
680   std::string dev_name;
681   if (!GetDeviceProperty(keyboard_device.sys_path, kDevNameProperty,
682                          &dev_name) ||
683       dev_name.empty()) {
684     return false;
685   }
686 
687   base::ScopedFD fd(open(dev_name.c_str(), O_RDONLY));
688   if (fd.get() < 0) {
689     LOG(ERROR) << "Cannot open " << dev_name.c_str() << " : " << errno;
690     return false;
691   }
692 
693   EventDeviceInfo devinfo;
694   if (!devinfo.Initialize(fd.get(), keyboard_device.sys_path)) {
695     LOG(ERROR) << "Failed to get device information for "
696                << keyboard_device.sys_path.value();
697     return false;
698   }
699 
700   *has_assistant_key = devinfo.HasKeyEvent(KEY_ASSISTANT);
701   return true;
702 }
703 
RewriteModifierKeys(const KeyEvent & key_event,MutableKeyState * state)704 bool EventRewriterChromeOS::RewriteModifierKeys(const KeyEvent& key_event,
705                                                 MutableKeyState* state) {
706   DCHECK(key_event.type() == ET_KEY_PRESSED ||
707          key_event.type() == ET_KEY_RELEASED);
708 
709   if (!delegate_ || !delegate_->RewriteModifierKeys())
710     return false;
711 
712   // Preserve a copy of the original before rewriting |state| based on
713   // user preferences, device configuration, and certain IME properties.
714   MutableKeyState incoming = *state;
715   state->flags = EF_NONE;
716   int characteristic_flag = EF_NONE;
717   bool exact_event = false;
718 
719   // First, remap the key code.
720   const ModifierRemapping* remapped_key = nullptr;
721   // Remapping based on DomKey.
722   switch (incoming.key) {
723     case DomKey::ALT_GRAPH:
724       // The Neo2 codes modifiers such that CapsLock appears as VKEY_ALTGR,
725       // but AltGraph (right Alt) also appears as VKEY_ALTGR in Neo2,
726       // as it does in other layouts. Neo2's "Mod3" is represented in
727       // EventFlags by a combination of AltGr+Mod3, while its "Mod4" is
728       // AltGr alone.
729       if (IsISOLevel5ShiftUsedByCurrentInputMethod()) {
730         if (incoming.code == DomCode::CAPS_LOCK) {
731           characteristic_flag = EF_ALTGR_DOWN | EF_MOD3_DOWN;
732           remapped_key =
733               GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, delegate_);
734         } else {
735           characteristic_flag = EF_ALTGR_DOWN;
736           remapped_key = GetSearchRemappedKey(delegate_, GetLastKeyboardType());
737         }
738       }
739       if (remapped_key && remapped_key->result.key_code == VKEY_CAPITAL)
740         remapped_key = kModifierRemappingNeoMod3;
741       break;
742     case DomKey::ALT_GRAPH_LATCH:
743       if (key_event.type() == ET_KEY_PRESSED) {
744         pressed_modifier_latches_ |= EF_ALTGR_DOWN;
745       } else {
746         pressed_modifier_latches_ &= ~EF_ALTGR_DOWN;
747         if (used_modifier_latches_ & EF_ALTGR_DOWN)
748           used_modifier_latches_ &= ~EF_ALTGR_DOWN;
749         else
750           latched_modifier_latches_ |= EF_ALTGR_DOWN;
751       }
752       // Rewrite to AltGraph. When this key is used like a regular modifier,
753       // the web-exposed result looks like a use of the regular modifier.
754       // When it's used as a latch, the web-exposed result is a vacuous
755       // modifier press-and-release, which should be harmless, but preserves
756       // the event for applications using the |code| (e.g. remoting).
757       state->key = DomKey::ALT_GRAPH;
758       state->key_code = VKEY_ALTGR;
759       exact_event = true;
760       break;
761     default:
762       break;
763   }
764 
765   // Remapping based on DomCode.
766   switch (incoming.code) {
767     // On Chrome OS, Caps_Lock with Mod3Mask is sent when Caps Lock is pressed
768     // (with one exception: when IsISOLevel5ShiftUsedByCurrentInputMethod() is
769     // true, the key generates XK_ISO_Level3_Shift with Mod3Mask, not
770     // Caps_Lock).
771     case DomCode::CAPS_LOCK:
772       // This key is already remapped to Mod3 in remapping based on DomKey. Skip
773       // more remapping.
774       if (IsISOLevel5ShiftUsedByCurrentInputMethod() && remapped_key)
775         break;
776 
777       characteristic_flag = EF_CAPS_LOCK_ON;
778       remapped_key =
779           GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, delegate_);
780       break;
781     case DomCode::META_LEFT:
782     case DomCode::META_RIGHT:
783       characteristic_flag = EF_COMMAND_DOWN;
784       remapped_key = GetSearchRemappedKey(delegate_, GetLastKeyboardType());
785       // Default behavior is Super key, hence don't remap the event if the pref
786       // is unavailable.
787       break;
788     case DomCode::CONTROL_LEFT:
789     case DomCode::CONTROL_RIGHT:
790       characteristic_flag = EF_CONTROL_DOWN;
791       remapped_key =
792           GetRemappedKey(prefs::kLanguageRemapControlKeyTo, delegate_);
793       break;
794     case DomCode::ALT_LEFT:
795     case DomCode::ALT_RIGHT:
796       // ALT key
797       characteristic_flag = EF_ALT_DOWN;
798       remapped_key = GetRemappedKey(prefs::kLanguageRemapAltKeyTo, delegate_);
799       break;
800     case DomCode::ESCAPE:
801       remapped_key =
802           GetRemappedKey(prefs::kLanguageRemapEscapeKeyTo, delegate_);
803       break;
804     case DomCode::BACKSPACE:
805       remapped_key =
806           GetRemappedKey(prefs::kLanguageRemapBackspaceKeyTo, delegate_);
807       break;
808     case DomCode::LAUNCH_ASSISTANT:
809       remapped_key =
810           GetRemappedKey(prefs::kLanguageRemapAssistantKeyTo, delegate_);
811       break;
812     default:
813       break;
814   }
815 
816   if (remapped_key) {
817     state->key_code = remapped_key->result.key_code;
818     state->code = remapped_key->result.code;
819     state->key = remapped_key->result.key;
820     incoming.flags |= characteristic_flag;
821     characteristic_flag = remapped_key->flag;
822     if (incoming.key_code == VKEY_CAPITAL) {
823       // Caps Lock is rewritten to another key event, remove EF_CAPS_LOCK_ON
824       // flag to prevent the keyboard's Caps Lock state being synced to the
825       // rewritten key event's flag in InputMethodChromeOS.
826       incoming.flags &= ~EF_CAPS_LOCK_ON;
827     }
828     if (remapped_key->remap_to == chromeos::ModifierKey::kCapsLockKey)
829       characteristic_flag |= EF_CAPS_LOCK_ON;
830     state->code = RelocateModifier(
831         state->code, KeycodeConverter::DomCodeToLocation(incoming.code));
832   }
833 
834   // Next, remap modifier bits.
835   state->flags |= GetRemappedModifierMasks(key_event, incoming.flags);
836 
837   // If the DomKey is not a modifier before remapping but is after, set the
838   // modifier latches for the later non-modifier key's modifier states.
839   bool non_modifier_to_modifier =
840       !KeycodeConverter::IsDomKeyForModifier(incoming.key) &&
841       KeycodeConverter::IsDomKeyForModifier(state->key);
842   if (key_event.type() == ET_KEY_PRESSED) {
843     state->flags |= characteristic_flag;
844     if (non_modifier_to_modifier)
845       pressed_modifier_latches_ |= characteristic_flag;
846   } else {
847     state->flags &= ~characteristic_flag;
848     if (non_modifier_to_modifier)
849       pressed_modifier_latches_ &= ~characteristic_flag;
850   }
851 
852   if (key_event.type() == ET_KEY_PRESSED) {
853     if (!KeycodeConverter::IsDomKeyForModifier(state->key)) {
854       used_modifier_latches_ |= pressed_modifier_latches_;
855       latched_modifier_latches_ = EF_NONE;
856     }
857   }
858 
859   // Implement the Caps Lock modifier here, rather than in the
860   // AcceleratorController, so that the event is visible to apps (see
861   // crbug.com/775743).
862   if (key_event.type() == ET_KEY_RELEASED && state->key_code == VKEY_CAPITAL) {
863     ::chromeos::input_method::ImeKeyboard* ime_keyboard =
864         ime_keyboard_for_testing_
865             ? ime_keyboard_for_testing_
866             : ::chromeos::input_method::InputMethodManager::Get()
867                   ->GetImeKeyboard();
868     ime_keyboard->SetCapsLockEnabled(!ime_keyboard->CapsLockIsEnabled());
869   }
870   return exact_event;
871 }
872 
DeviceKeyPressedOrReleased(int device_id)873 void EventRewriterChromeOS::DeviceKeyPressedOrReleased(int device_id) {
874   const auto iter = device_id_to_info_.find(device_id);
875   DeviceType type;
876   if (iter != device_id_to_info_.end())
877     type = iter->second.type;
878   else
879     type = KeyboardDeviceAdded(device_id);
880 
881   // Ignore virtual Xorg keyboard (magic that generates key repeat
882   // events). Pretend that the previous real keyboard is the one that is still
883   // in use.
884   if (type == kDeviceVirtualCoreKeyboard)
885     return;
886 
887   last_keyboard_device_id_ = device_id;
888 }
889 
IsHotrodRemote() const890 bool EventRewriterChromeOS::IsHotrodRemote() const {
891   return IsLastKeyboardOfType(kDeviceHotrodRemote);
892 }
893 
IsLastKeyboardOfType(DeviceType device_type) const894 bool EventRewriterChromeOS::IsLastKeyboardOfType(DeviceType device_type) const {
895   return GetLastKeyboardType() == device_type;
896 }
897 
GetLastKeyboardType() const898 EventRewriterChromeOS::DeviceType EventRewriterChromeOS::GetLastKeyboardType()
899     const {
900   if (last_keyboard_device_id_ == ED_UNKNOWN_DEVICE)
901     return kDeviceUnknown;
902 
903   const auto iter = device_id_to_info_.find(last_keyboard_device_id_);
904   if (iter == device_id_to_info_.end()) {
905     LOG(ERROR) << "Device ID " << last_keyboard_device_id_ << " is unknown.";
906     return kDeviceUnknown;
907   }
908 
909   return iter->second.type;
910 }
911 
GetRemappedModifierMasks(const Event & event,int original_flags) const912 int EventRewriterChromeOS::GetRemappedModifierMasks(const Event& event,
913                                                     int original_flags) const {
914   int unmodified_flags = original_flags;
915   int rewritten_flags = pressed_modifier_latches_ | latched_modifier_latches_;
916   for (size_t i = 0; unmodified_flags && (i < base::size(kModifierRemappings));
917        ++i) {
918     const ModifierRemapping* remapped_key = nullptr;
919     if (!(unmodified_flags & kModifierRemappings[i].flag))
920       continue;
921     switch (kModifierRemappings[i].flag) {
922       case EF_COMMAND_DOWN:
923         remapped_key = GetSearchRemappedKey(delegate_, GetLastKeyboardType());
924         break;
925       case EF_MOD3_DOWN:
926         // If EF_MOD3_DOWN is used by the current input method, leave it alone;
927         // it is not remappable.
928         if (IsISOLevel5ShiftUsedByCurrentInputMethod())
929           continue;
930         // Otherwise, Mod3Mask is set on X events when the Caps Lock key
931         // is down, but, if Caps Lock is remapped, CapsLock is NOT set,
932         // because pressing the key does not invoke caps lock. So, the
933         // kModifierRemappings[] table uses EF_MOD3_DOWN for the Caps
934         // Lock remapping.
935         break;
936       case EF_MOD3_DOWN | EF_ALTGR_DOWN:
937         if ((original_flags & EF_ALTGR_DOWN) &&
938             IsISOLevel5ShiftUsedByCurrentInputMethod()) {
939           remapped_key = kModifierRemappingNeoMod3;
940         }
941         break;
942       default:
943         break;
944     }
945     if (!remapped_key && kModifierRemappings[i].pref_name) {
946       remapped_key =
947           GetRemappedKey(kModifierRemappings[i].pref_name, delegate_);
948     }
949     if (remapped_key) {
950       unmodified_flags &= ~kModifierRemappings[i].flag;
951       rewritten_flags |= remapped_key->flag;
952     }
953   }
954   return rewritten_flags | unmodified_flags;
955 }
956 
ShouldRemapToRightClick(const MouseEvent & mouse_event,int flags,int * matched_mask) const957 bool EventRewriterChromeOS::ShouldRemapToRightClick(
958     const MouseEvent& mouse_event,
959     int flags,
960     int* matched_mask) const {
961   *matched_mask = 0;
962   if (base::FeatureList::IsEnabled(
963           ::chromeos::features::kUseSearchClickForRightClick)) {
964     if (AreFlagsSet(flags, kSearchLeftButton)) {
965       *matched_mask = kSearchLeftButton;
966     }
967   } else {
968     if (AreFlagsSet(flags, kAltLeftButton)) {
969       *matched_mask = kAltLeftButton;
970     }
971   }
972 
973   return (*matched_mask != 0) &&
974          ((mouse_event.type() == ET_MOUSE_PRESSED) ||
975           pressed_device_ids_.count(mouse_event.source_device_id())) &&
976          IsFromTouchpadDevice(mouse_event);
977 }
978 
RewriteKeyEvent(const KeyEvent & key_event,std::unique_ptr<Event> * rewritten_event)979 EventRewriteStatus EventRewriterChromeOS::RewriteKeyEvent(
980     const KeyEvent& key_event,
981     std::unique_ptr<Event>* rewritten_event) {
982   if (delegate_ && delegate_->IsExtensionCommandRegistered(key_event.key_code(),
983                                                            key_event.flags()))
984     return EVENT_REWRITE_CONTINUE;
985   if (key_event.source_device_id() != ED_UNKNOWN_DEVICE)
986     DeviceKeyPressedOrReleased(key_event.source_device_id());
987 
988   // Drop repeated keys from Hotrod remote.
989   if ((key_event.flags() & EF_IS_REPEAT) &&
990       (key_event.type() == ET_KEY_PRESSED) && IsHotrodRemote() &&
991       key_event.key_code() != VKEY_BACK) {
992     return EVENT_REWRITE_DISCARD;
993   }
994 
995   MutableKeyState state = {key_event.flags(), key_event.code(),
996                            key_event.GetDomKey(), key_event.key_code()};
997 
998   // Do not rewrite an event sent by ui_controls::SendKeyPress(). See
999   // crbug.com/136465.
1000   if (!(key_event.flags() & EF_FINAL)) {
1001     if (RewriteModifierKeys(key_event, &state)) {
1002       // Early exit with completed event.
1003       BuildRewrittenKeyEvent(key_event, state, rewritten_event);
1004       return EVENT_REWRITE_REWRITTEN;
1005     }
1006     RewriteNumPadKeys(key_event, &state);
1007   }
1008 
1009   EventRewriteStatus status = EVENT_REWRITE_CONTINUE;
1010   bool is_sticky_key_extension_command = false;
1011   if (sticky_keys_controller_) {
1012     KeyEvent tmp_event = key_event;
1013     tmp_event.set_key_code(state.key_code);
1014     tmp_event.set_flags(state.flags);
1015     std::unique_ptr<Event> output_event;
1016     status = sticky_keys_controller_->RewriteEvent(tmp_event, &output_event);
1017     if (status == EVENT_REWRITE_REWRITTEN ||
1018         status == EVENT_REWRITE_DISPATCH_ANOTHER)
1019       state.flags = output_event->flags();
1020     if (status == EVENT_REWRITE_DISCARD)
1021       return EVENT_REWRITE_DISCARD;
1022     is_sticky_key_extension_command =
1023         delegate_ &&
1024         delegate_->IsExtensionCommandRegistered(state.key_code, state.flags);
1025   }
1026 
1027   // If flags have changed, this may change the interpretation of the key,
1028   // so reapply layout.
1029   if (state.flags != key_event.flags())
1030     SetMeaningForLayout(key_event.type(), &state);
1031 
1032   // If sticky key rewrites the event, and it matches an extension command, do
1033   // not further rewrite the event since it won't match the extension command
1034   // thereafter.
1035   if (!is_sticky_key_extension_command && !(key_event.flags() & EF_FINAL)) {
1036     RewriteExtendedKeys(key_event, &state);
1037     RewriteFunctionKeys(key_event, &state);
1038   }
1039   if ((key_event.flags() == state.flags) &&
1040       (key_event.key_code() == state.key_code) &&
1041       (status == EVENT_REWRITE_CONTINUE)) {
1042     return EVENT_REWRITE_CONTINUE;
1043   }
1044   // Sticky keys may have returned a result other than |EVENT_REWRITE_CONTINUE|,
1045   // in which case we need to preserve that return status. Alternatively, we
1046   // might be here because key_event changed, in which case we need to
1047   // return |EVENT_REWRITE_REWRITTEN|.
1048   if (status == EVENT_REWRITE_CONTINUE)
1049     status = EVENT_REWRITE_REWRITTEN;
1050   BuildRewrittenKeyEvent(key_event, state, rewritten_event);
1051   return status;
1052 }
1053 
1054 // TODO(yhanada): Clean up this method once StickyKeysController migrates to the
1055 // new API.
RewriteMouseButtonEvent(const MouseEvent & mouse_event,const Continuation continuation)1056 EventDispatchDetails EventRewriterChromeOS::RewriteMouseButtonEvent(
1057     const MouseEvent& mouse_event,
1058     const Continuation continuation) {
1059   int flags = RewriteLocatedEvent(mouse_event);
1060   EventRewriteStatus status = EVENT_REWRITE_CONTINUE;
1061   if (sticky_keys_controller_) {
1062     MouseEvent tmp_event = mouse_event;
1063     tmp_event.set_flags(flags);
1064     std::unique_ptr<Event> output_event;
1065     status = sticky_keys_controller_->RewriteEvent(tmp_event, &output_event);
1066     if (status == EVENT_REWRITE_REWRITTEN ||
1067         status == EVENT_REWRITE_DISPATCH_ANOTHER) {
1068       flags = output_event->flags();
1069     }
1070   }
1071   int changed_button = EF_NONE;
1072   if ((mouse_event.type() == ET_MOUSE_PRESSED) ||
1073       (mouse_event.type() == ET_MOUSE_RELEASED)) {
1074     changed_button = RewriteModifierClick(mouse_event, &flags);
1075   }
1076   if ((mouse_event.flags() == flags) && (status == EVENT_REWRITE_CONTINUE)) {
1077     return SendEvent(continuation, &mouse_event);
1078   }
1079 
1080   std::unique_ptr<Event> rewritten_event = Event::Clone(mouse_event);
1081   rewritten_event->set_flags(flags);
1082   if (changed_button != EF_NONE) {
1083     static_cast<MouseEvent*>(rewritten_event.get())
1084         ->set_changed_button_flags(changed_button);
1085   }
1086 
1087   EventDispatchDetails details =
1088       SendEventFinally(continuation, rewritten_event.get());
1089   if (status == EVENT_REWRITE_DISPATCH_ANOTHER &&
1090       !details.dispatcher_destroyed) {
1091     // Here, we know that another event is a modifier key release event from
1092     // StickyKeysController.
1093     return SendStickyKeysReleaseEvents(std::move(rewritten_event),
1094                                        continuation);
1095   }
1096   return details;
1097 }
1098 
RewriteMouseWheelEvent(const MouseWheelEvent & wheel_event,const Continuation continuation)1099 EventDispatchDetails EventRewriterChromeOS::RewriteMouseWheelEvent(
1100     const MouseWheelEvent& wheel_event,
1101     const Continuation continuation) {
1102   if (!sticky_keys_controller_)
1103     return SendEvent(continuation, &wheel_event);
1104 
1105   const int flags = RewriteLocatedEvent(wheel_event);
1106   MouseWheelEvent tmp_event = wheel_event;
1107   tmp_event.set_flags(flags);
1108   return sticky_keys_controller_->RewriteEvent(tmp_event, continuation);
1109 }
1110 
RewriteTouchEvent(const TouchEvent & touch_event,const Continuation continuation)1111 EventDispatchDetails EventRewriterChromeOS::RewriteTouchEvent(
1112     const TouchEvent& touch_event,
1113     const Continuation continuation) {
1114   const int flags = RewriteLocatedEvent(touch_event);
1115   if (touch_event.flags() == flags)
1116     return SendEvent(continuation, &touch_event);
1117   TouchEvent rewritten_touch_event(touch_event);
1118   rewritten_touch_event.set_flags(flags);
1119   return SendEventFinally(continuation, &rewritten_touch_event);
1120 }
1121 
RewriteScrollEvent(const ScrollEvent & scroll_event,const Continuation continuation)1122 EventDispatchDetails EventRewriterChromeOS::RewriteScrollEvent(
1123     const ScrollEvent& scroll_event,
1124     const Continuation continuation) {
1125   if (!sticky_keys_controller_)
1126     return SendEvent(continuation, &scroll_event);
1127   return sticky_keys_controller_->RewriteEvent(scroll_event, continuation);
1128 }
1129 
RewriteNumPadKeys(const KeyEvent & key_event,MutableKeyState * state)1130 void EventRewriterChromeOS::RewriteNumPadKeys(const KeyEvent& key_event,
1131                                               MutableKeyState* state) {
1132   DCHECK(key_event.type() == ET_KEY_PRESSED ||
1133          key_event.type() == ET_KEY_RELEASED);
1134   static const struct NumPadRemapping {
1135     KeyboardCode input_key_code;
1136     MutableKeyState result;
1137   } kNumPadRemappings[] = {{VKEY_DELETE,
1138                             {EF_NONE, DomCode::NONE,
1139                              DomKey::Constant<'.'>::Character, VKEY_DECIMAL}},
1140                            {VKEY_INSERT,
1141                             {EF_NONE, DomCode::NONE,
1142                              DomKey::Constant<'0'>::Character, VKEY_NUMPAD0}},
1143                            {VKEY_END,
1144                             {EF_NONE, DomCode::NONE,
1145                              DomKey::Constant<'1'>::Character, VKEY_NUMPAD1}},
1146                            {VKEY_DOWN,
1147                             {EF_NONE, DomCode::NONE,
1148                              DomKey::Constant<'2'>::Character, VKEY_NUMPAD2}},
1149                            {VKEY_NEXT,
1150                             {EF_NONE, DomCode::NONE,
1151                              DomKey::Constant<'3'>::Character, VKEY_NUMPAD3}},
1152                            {VKEY_LEFT,
1153                             {EF_NONE, DomCode::NONE,
1154                              DomKey::Constant<'4'>::Character, VKEY_NUMPAD4}},
1155                            {VKEY_CLEAR,
1156                             {EF_NONE, DomCode::NONE,
1157                              DomKey::Constant<'5'>::Character, VKEY_NUMPAD5}},
1158                            {VKEY_RIGHT,
1159                             {EF_NONE, DomCode::NONE,
1160                              DomKey::Constant<'6'>::Character, VKEY_NUMPAD6}},
1161                            {VKEY_HOME,
1162                             {EF_NONE, DomCode::NONE,
1163                              DomKey::Constant<'7'>::Character, VKEY_NUMPAD7}},
1164                            {VKEY_UP,
1165                             {EF_NONE, DomCode::NONE,
1166                              DomKey::Constant<'8'>::Character, VKEY_NUMPAD8}},
1167                            {VKEY_PRIOR,
1168                             {EF_NONE, DomCode::NONE,
1169                              DomKey::Constant<'9'>::Character, VKEY_NUMPAD9}}};
1170   for (const auto& map : kNumPadRemappings) {
1171     if (state->key_code == map.input_key_code) {
1172       if (KeycodeConverter::DomCodeToLocation(state->code) ==
1173           DomKeyLocation::NUMPAD) {
1174         ApplyRemapping(map.result, state);
1175       }
1176       return;
1177     }
1178   }
1179 }
1180 
RewriteExtendedKeys(const KeyEvent & key_event,MutableKeyState * state)1181 void EventRewriterChromeOS::RewriteExtendedKeys(const KeyEvent& key_event,
1182                                                 MutableKeyState* state) {
1183   DCHECK(key_event.type() == ET_KEY_PRESSED ||
1184          key_event.type() == ET_KEY_RELEASED);
1185   MutableKeyState incoming = *state;
1186 
1187   if ((incoming.flags & (EF_COMMAND_DOWN | EF_ALT_DOWN)) ==
1188       (EF_COMMAND_DOWN | EF_ALT_DOWN)) {
1189     // Allow Search to avoid rewriting extended keys.
1190     // For these, we only remove the EF_COMMAND_DOWN flag.
1191     static const KeyboardRemapping::Condition kAvoidRemappings[] = {
1192         {// Alt+Backspace
1193          EF_ALT_DOWN | EF_COMMAND_DOWN, VKEY_BACK},
1194         {// Control+Alt+Up
1195          EF_ALT_DOWN | EF_CONTROL_DOWN | EF_COMMAND_DOWN, VKEY_UP},
1196         {// Control+Alt+Down
1197          EF_ALT_DOWN | EF_CONTROL_DOWN | EF_COMMAND_DOWN, VKEY_DOWN}};
1198     for (const auto& condition : kAvoidRemappings) {
1199       if (MatchKeyboardRemapping(*state, condition)) {
1200         state->flags = incoming.flags & ~EF_COMMAND_DOWN;
1201         return;
1202       }
1203     }
1204   }
1205 
1206   if (incoming.flags & EF_COMMAND_DOWN) {
1207     bool strict = ::features::IsNewShortcutMappingEnabled();
1208     bool skip_search_key_remapping =
1209         delegate_ && delegate_->IsSearchKeyAcceleratorReserved();
1210     if (strict) {
1211       // These two keys are used to select to Home/End.
1212       static const KeyboardRemapping kNewSearchRemappings[] = {
1213           {// Search+Shift+Left -> select to home.
1214            {EF_COMMAND_DOWN | EF_SHIFT_DOWN, VKEY_LEFT},
1215            {EF_SHIFT_DOWN, DomCode::HOME, DomKey::HOME, VKEY_HOME}},
1216           {// Search+Shift+Right -> select to end.
1217            {EF_COMMAND_DOWN | EF_SHIFT_DOWN, VKEY_RIGHT},
1218            {EF_SHIFT_DOWN, DomCode::END, DomKey::END, VKEY_END}},
1219       };
1220       if (!skip_search_key_remapping &&
1221           RewriteWithKeyboardRemappings(kNewSearchRemappings,
1222                                         base::size(kNewSearchRemappings),
1223                                         incoming, state, /*strict=*/true)) {
1224         return;
1225       }
1226     }
1227     static const KeyboardRemapping kSearchRemappings[] = {
1228         {// Search+BackSpace -> Delete
1229          {EF_COMMAND_DOWN, VKEY_BACK},
1230          {EF_NONE, DomCode::DEL, DomKey::DEL, VKEY_DELETE}},
1231         {// Search+Left -> Home
1232          {EF_COMMAND_DOWN, VKEY_LEFT},
1233          {EF_NONE, DomCode::HOME, DomKey::HOME, VKEY_HOME}},
1234         {// Search+Up -> Prior (aka PageUp)
1235          {EF_COMMAND_DOWN, VKEY_UP},
1236          {EF_NONE, DomCode::PAGE_UP, DomKey::PAGE_UP, VKEY_PRIOR}},
1237         {// Search+Right -> End
1238          {EF_COMMAND_DOWN, VKEY_RIGHT},
1239          {EF_NONE, DomCode::END, DomKey::END, VKEY_END}},
1240         {// Search+Down -> Next (aka PageDown)
1241          {EF_COMMAND_DOWN, VKEY_DOWN},
1242          {EF_NONE, DomCode::PAGE_DOWN, DomKey::PAGE_DOWN, VKEY_NEXT}},
1243         {// Search+Period -> Insert
1244          {EF_COMMAND_DOWN, VKEY_OEM_PERIOD},
1245          {EF_NONE, DomCode::INSERT, DomKey::INSERT, VKEY_INSERT}}};
1246     if (!skip_search_key_remapping &&
1247         RewriteWithKeyboardRemappings(kSearchRemappings,
1248                                       base::size(kSearchRemappings), incoming,
1249                                       state, strict)) {
1250       return;
1251     }
1252   }
1253 
1254   if (incoming.flags & EF_ALT_DOWN) {
1255     static const KeyboardRemapping kNonSearchRemappings[] = {
1256         {// Alt+BackSpace -> Delete
1257          {EF_ALT_DOWN, VKEY_BACK},
1258          {EF_NONE, DomCode::DEL, DomKey::DEL, VKEY_DELETE}},
1259         {// Control+Alt+Up -> Home
1260          {EF_ALT_DOWN | EF_CONTROL_DOWN, VKEY_UP},
1261          {EF_NONE, DomCode::HOME, DomKey::HOME, VKEY_HOME}},
1262         {// Alt+Up -> Prior (aka PageUp)
1263          {EF_ALT_DOWN, VKEY_UP},
1264          {EF_NONE, DomCode::PAGE_UP, DomKey::PAGE_UP, VKEY_PRIOR}},
1265         {// Control+Alt+Down -> End
1266          {EF_ALT_DOWN | EF_CONTROL_DOWN, VKEY_DOWN},
1267          {EF_NONE, DomCode::END, DomKey::END, VKEY_END}},
1268         {// Alt+Down -> Next (aka PageDown)
1269          {EF_ALT_DOWN, VKEY_DOWN},
1270          {EF_NONE, DomCode::PAGE_DOWN, DomKey::PAGE_DOWN, VKEY_NEXT}}};
1271     if (RewriteWithKeyboardRemappings(kNonSearchRemappings,
1272                                       base::size(kNonSearchRemappings),
1273                                       incoming, state)) {
1274       return;
1275     }
1276   }
1277 }
1278 
RewriteFunctionKeys(const KeyEvent & key_event,MutableKeyState * state)1279 void EventRewriterChromeOS::RewriteFunctionKeys(const KeyEvent& key_event,
1280                                                 MutableKeyState* state) {
1281   CHECK(key_event.type() == ET_KEY_PRESSED ||
1282         key_event.type() == ET_KEY_RELEASED);
1283 
1284   // Some action key codes are mapped to standard VKEY and DomCode values
1285   // during event to KeyEvent translation. However, in Chrome, different VKEY
1286   // combinations trigger those actions. This table maps event VKEYs to the
1287   // right action VKEYs.
1288   // TODO(dtor): Either add proper accelerators for VKEY_ZOOM or move
1289   // from VKEY_MEDIA_LAUNCH_APP2 to VKEY_ZOOM.
1290   static const KeyboardRemapping kActionToActionKeys[] = {
1291       // Zoom toggle is actually through VKEY_MEDIA_LAUNCH_APP2.
1292       {{EF_NONE, VKEY_ZOOM},
1293        {EF_NONE, DomCode::ZOOM_TOGGLE, DomKey::ZOOM_TOGGLE,
1294         VKEY_MEDIA_LAUNCH_APP2}},
1295   };
1296 
1297   // Map certain action keys to the right VKey and modifier.
1298   RewriteWithKeyboardRemappings(kActionToActionKeys,
1299                                 base::size(kActionToActionKeys), *state, state);
1300 
1301   // Some key codes have a Dom code but no VKEY value assigned. They're mapped
1302   // to VKEY values here.
1303   if (state->key_code == VKEY_UNKNOWN) {
1304     if (state->code == DomCode::SHOW_ALL_WINDOWS) {
1305       // Show all windows is through VKEY_MEDIA_LAUNCH_APP1.
1306       state->key_code = VKEY_MEDIA_LAUNCH_APP1;
1307       state->key = DomKey::F4;
1308     } else if (state->code == DomCode::DISPLAY_TOGGLE_INT_EXT) {
1309       // Display toggle is through control + VKEY_MEDIA_LAUNCH_APP2.
1310       state->flags |= EF_CONTROL_DOWN;
1311       state->key_code = VKEY_MEDIA_LAUNCH_APP2;
1312       state->key = DomKey::F12;
1313     }
1314   }
1315 
1316   const auto iter = device_id_to_info_.find(key_event.source_device_id());
1317   KeyboardTopRowLayout layout = kKbdTopRowLayoutDefault;
1318   if (iter != device_id_to_info_.end()) {
1319     layout = iter->second.top_row_layout;
1320   }
1321 
1322   const bool search_is_pressed = (state->flags & EF_COMMAND_DOWN) != 0;
1323   if (layout == kKbdTopRowLayoutCustom) {
1324     if (RewriteTopRowKeysForCustomLayout(key_event.source_device_id(),
1325                                          key_event, search_is_pressed, state)) {
1326       return;
1327     }
1328   } else if (layout == kKbdTopRowLayoutWilco ||
1329              layout == kKbdTopRowLayoutDrallion) {
1330     if (RewriteTopRowKeysForLayoutWilco(key_event, search_is_pressed, state,
1331                                         layout)) {
1332       return;
1333     }
1334   } else if ((state->key_code >= VKEY_F1) && (state->key_code <= VKEY_F12)) {
1335     //  Search? Top Row   Result
1336     //  ------- --------  ------
1337     //  No      Fn        Unchanged
1338     //  No      System    Fn -> System
1339     //  Yes     Fn        Fn -> System
1340     //  Yes     System    Search+Fn -> Fn
1341     if (ForceTopRowAsFunctionKeys() == search_is_pressed) {
1342       // Rewrite the F1-F12 keys on a Chromebook keyboard to system keys.
1343       // This is the original Chrome OS layout.
1344       static const KeyboardRemapping kFkeysToSystemKeys1[] = {
1345           {{EF_NONE, VKEY_F1},
1346            {EF_NONE, DomCode::BROWSER_BACK, DomKey::BROWSER_BACK,
1347             VKEY_BROWSER_BACK}},
1348           {{EF_NONE, VKEY_F2},
1349            {EF_NONE, DomCode::BROWSER_FORWARD, DomKey::BROWSER_FORWARD,
1350             VKEY_BROWSER_FORWARD}},
1351           {{EF_NONE, VKEY_F3},
1352            {EF_NONE, DomCode::BROWSER_REFRESH, DomKey::BROWSER_REFRESH,
1353             VKEY_BROWSER_REFRESH}},
1354           {{EF_NONE, VKEY_F4},
1355            {EF_NONE, DomCode::ZOOM_TOGGLE, DomKey::ZOOM_TOGGLE,
1356             VKEY_MEDIA_LAUNCH_APP2}},
1357           {{EF_NONE, VKEY_F5},
1358            {EF_NONE, DomCode::SELECT_TASK, DomKey::LAUNCH_MY_COMPUTER,
1359             VKEY_MEDIA_LAUNCH_APP1}},
1360           {{EF_NONE, VKEY_F6},
1361            {EF_NONE, DomCode::BRIGHTNESS_DOWN, DomKey::BRIGHTNESS_DOWN,
1362             VKEY_BRIGHTNESS_DOWN}},
1363           {{EF_NONE, VKEY_F7},
1364            {EF_NONE, DomCode::BRIGHTNESS_UP, DomKey::BRIGHTNESS_UP,
1365             VKEY_BRIGHTNESS_UP}},
1366           {{EF_NONE, VKEY_F8},
1367            {EF_NONE, DomCode::VOLUME_MUTE, DomKey::AUDIO_VOLUME_MUTE,
1368             VKEY_VOLUME_MUTE}},
1369           {{EF_NONE, VKEY_F9},
1370            {EF_NONE, DomCode::VOLUME_DOWN, DomKey::AUDIO_VOLUME_DOWN,
1371             VKEY_VOLUME_DOWN}},
1372           {{EF_NONE, VKEY_F10},
1373            {EF_NONE, DomCode::VOLUME_UP, DomKey::AUDIO_VOLUME_UP,
1374             VKEY_VOLUME_UP}},
1375       };
1376       // The new layout with forward button removed and play/pause added.
1377       static const KeyboardRemapping kFkeysToSystemKeys2[] = {
1378           {{EF_NONE, VKEY_F1},
1379            {EF_NONE, DomCode::BROWSER_BACK, DomKey::BROWSER_BACK,
1380             VKEY_BROWSER_BACK}},
1381           {{EF_NONE, VKEY_F2},
1382            {EF_NONE, DomCode::BROWSER_REFRESH, DomKey::BROWSER_REFRESH,
1383             VKEY_BROWSER_REFRESH}},
1384           {{EF_NONE, VKEY_F3},
1385            {EF_NONE, DomCode::ZOOM_TOGGLE, DomKey::ZOOM_TOGGLE,
1386             VKEY_MEDIA_LAUNCH_APP2}},
1387           {{EF_NONE, VKEY_F4},
1388            {EF_NONE, DomCode::SELECT_TASK, DomKey::LAUNCH_MY_COMPUTER,
1389             VKEY_MEDIA_LAUNCH_APP1}},
1390           {{EF_NONE, VKEY_F5},
1391            {EF_NONE, DomCode::BRIGHTNESS_DOWN, DomKey::BRIGHTNESS_DOWN,
1392             VKEY_BRIGHTNESS_DOWN}},
1393           {{EF_NONE, VKEY_F6},
1394            {EF_NONE, DomCode::BRIGHTNESS_UP, DomKey::BRIGHTNESS_UP,
1395             VKEY_BRIGHTNESS_UP}},
1396           {{EF_NONE, VKEY_F7},
1397            {EF_NONE, DomCode::MEDIA_PLAY_PAUSE, DomKey::MEDIA_PLAY_PAUSE,
1398             VKEY_MEDIA_PLAY_PAUSE}},
1399           {{EF_NONE, VKEY_F8},
1400            {EF_NONE, DomCode::VOLUME_MUTE, DomKey::AUDIO_VOLUME_MUTE,
1401             VKEY_VOLUME_MUTE}},
1402           {{EF_NONE, VKEY_F9},
1403            {EF_NONE, DomCode::VOLUME_DOWN, DomKey::AUDIO_VOLUME_DOWN,
1404             VKEY_VOLUME_DOWN}},
1405           {{EF_NONE, VKEY_F10},
1406            {EF_NONE, DomCode::VOLUME_UP, DomKey::AUDIO_VOLUME_UP,
1407             VKEY_VOLUME_UP}},
1408       };
1409 
1410       const KeyboardRemapping* mapping = nullptr;
1411       size_t mappingSize = 0u;
1412       switch (layout) {
1413         case kKbdTopRowLayout2:
1414           mapping = kFkeysToSystemKeys2;
1415           mappingSize = base::size(kFkeysToSystemKeys2);
1416           break;
1417         case kKbdTopRowLayout1:
1418         default:
1419           mapping = kFkeysToSystemKeys1;
1420           mappingSize = base::size(kFkeysToSystemKeys1);
1421           break;
1422       }
1423 
1424       MutableKeyState incoming_without_command = *state;
1425       incoming_without_command.flags &= ~EF_COMMAND_DOWN;
1426       if (RewriteWithKeyboardRemappings(mapping, mappingSize,
1427                                         incoming_without_command, state)) {
1428         return;
1429       }
1430     } else if (search_is_pressed) {
1431       // Allow Search to avoid rewriting F1-F12.
1432       state->flags &= ~EF_COMMAND_DOWN;
1433       return;
1434     }
1435   }
1436 
1437   if (state->flags & EF_COMMAND_DOWN) {
1438     const bool strict = ::features::IsNewShortcutMappingEnabled();
1439     struct SearchToFunctionMap {
1440       DomCode input_dom_code;
1441       MutableKeyState result;
1442     };
1443 
1444     // We check the DOM3 |code| here instead of the VKEY, as these keys may
1445     // have different |KeyboardCode|s when modifiers are pressed, such as
1446     // shift.
1447     if (strict) {
1448       // Remap Search + 1/2 to F11/12.
1449       static const SearchToFunctionMap kNumberKeysToFkeys[] = {
1450           {DomCode::DIGIT1, {EF_NONE, DomCode::F11, DomKey::F12, VKEY_F11}},
1451           {DomCode::DIGIT2, {EF_NONE, DomCode::F12, DomKey::F12, VKEY_F12}},
1452       };
1453       for (const auto& map : kNumberKeysToFkeys) {
1454         if (state->code == map.input_dom_code) {
1455           state->flags &= ~EF_COMMAND_DOWN;
1456           ApplyRemapping(map.result, state);
1457           return;
1458         }
1459       }
1460     } else {
1461       // Remap Search + top row to F1~F12.
1462       static const SearchToFunctionMap kNumberKeysToFkeys[] = {
1463           {DomCode::DIGIT1, {EF_NONE, DomCode::F1, DomKey::F1, VKEY_F1}},
1464           {DomCode::DIGIT2, {EF_NONE, DomCode::F2, DomKey::F2, VKEY_F2}},
1465           {DomCode::DIGIT3, {EF_NONE, DomCode::F3, DomKey::F3, VKEY_F3}},
1466           {DomCode::DIGIT4, {EF_NONE, DomCode::F4, DomKey::F4, VKEY_F4}},
1467           {DomCode::DIGIT5, {EF_NONE, DomCode::F5, DomKey::F5, VKEY_F5}},
1468           {DomCode::DIGIT6, {EF_NONE, DomCode::F6, DomKey::F6, VKEY_F6}},
1469           {DomCode::DIGIT7, {EF_NONE, DomCode::F7, DomKey::F7, VKEY_F7}},
1470           {DomCode::DIGIT8, {EF_NONE, DomCode::F8, DomKey::F8, VKEY_F8}},
1471           {DomCode::DIGIT9, {EF_NONE, DomCode::F9, DomKey::F9, VKEY_F9}},
1472           {DomCode::DIGIT0, {EF_NONE, DomCode::F10, DomKey::F10, VKEY_F10}},
1473           {DomCode::MINUS, {EF_NONE, DomCode::F11, DomKey::F11, VKEY_F11}},
1474           {DomCode::EQUAL, {EF_NONE, DomCode::F12, DomKey::F12, VKEY_F12}}};
1475       for (const auto& map : kNumberKeysToFkeys) {
1476         if (state->code == map.input_dom_code) {
1477           state->flags &= ~EF_COMMAND_DOWN;
1478           ApplyRemapping(map.result, state);
1479           return;
1480         }
1481       }
1482     }
1483   }
1484 }
1485 
RewriteLocatedEvent(const Event & event)1486 int EventRewriterChromeOS::RewriteLocatedEvent(const Event& event) {
1487   if (!delegate_)
1488     return event.flags();
1489   return GetRemappedModifierMasks(event, event.flags());
1490 }
1491 
RewriteModifierClick(const MouseEvent & mouse_event,int * flags)1492 int EventRewriterChromeOS::RewriteModifierClick(const MouseEvent& mouse_event,
1493                                                 int* flags) {
1494   // Note that this behavior is limited to mouse events coming from touchpad
1495   // devices. https://crbug.com/890648.
1496 
1497   // Remap either Alt+Button1 or Search+Button1 to Button3 based on
1498   // flag/setting.
1499   int matched_mask;
1500   if (ShouldRemapToRightClick(mouse_event, *flags, &matched_mask)) {
1501     *flags &= ~matched_mask;
1502     *flags |= EF_RIGHT_MOUSE_BUTTON;
1503     if (mouse_event.type() == ET_MOUSE_PRESSED) {
1504       pressed_device_ids_.insert(mouse_event.source_device_id());
1505       if (matched_mask == kSearchLeftButton) {
1506         base::RecordAction(
1507             base::UserMetricsAction("SearchClickMappedToRightClick"));
1508       } else {
1509         DCHECK(matched_mask == kAltLeftButton);
1510         base::RecordAction(
1511             base::UserMetricsAction("AltClickMappedToRightClick"));
1512       }
1513     } else {
1514       pressed_device_ids_.erase(mouse_event.source_device_id());
1515     }
1516     return EF_RIGHT_MOUSE_BUTTON;
1517   }
1518   return EF_NONE;
1519 }
1520 
RewriteKeyEventInContext(const KeyEvent & key_event,std::unique_ptr<Event> rewritten_event,EventRewriteStatus status,const Continuation continuation)1521 EventDispatchDetails EventRewriterChromeOS::RewriteKeyEventInContext(
1522     const KeyEvent& key_event,
1523     std::unique_ptr<Event> rewritten_event,
1524     EventRewriteStatus status,
1525     const Continuation continuation) {
1526   if (status == EventRewriteStatus::EVENT_REWRITE_DISCARD)
1527     return DiscardEvent(continuation);
1528 
1529   MutableKeyState current_key_state;
1530   auto key_state_comparator =
1531       [&current_key_state](
1532           const std::pair<MutableKeyState, MutableKeyState>& key_state) {
1533         return (current_key_state.code == key_state.first.code) &&
1534                (current_key_state.key == key_state.first.key) &&
1535                (current_key_state.key_code == key_state.first.key_code);
1536       };
1537 
1538   const int mapped_flag = ModifierDomKeyToEventFlag(key_event.GetDomKey());
1539 
1540   if (key_event.type() == ET_KEY_PRESSED) {
1541     current_key_state = MutableKeyState(
1542         rewritten_event ? static_cast<const KeyEvent*>(rewritten_event.get())
1543                         : &key_event);
1544     MutableKeyState original_key_state(&key_event);
1545     auto iter = std::find_if(pressed_key_states_.begin(),
1546                              pressed_key_states_.end(), key_state_comparator);
1547 
1548     // When a key is pressed, store |current_key_state| if it is not stored
1549     // before.
1550     if (iter == pressed_key_states_.end()) {
1551       pressed_key_states_.push_back(
1552           std::make_pair(current_key_state, original_key_state));
1553     }
1554 
1555     if (status == EventRewriteStatus::EVENT_REWRITE_CONTINUE)
1556       return SendEvent(continuation, &key_event);
1557 
1558     EventDispatchDetails details =
1559         SendEventFinally(continuation, rewritten_event.get());
1560     if (status == EventRewriteStatus::EVENT_REWRITE_DISPATCH_ANOTHER &&
1561         !details.dispatcher_destroyed) {
1562       return SendStickyKeysReleaseEvents(std::move(rewritten_event),
1563                                          continuation);
1564     }
1565     return details;
1566   }
1567 
1568   DCHECK_EQ(key_event.type(), ET_KEY_RELEASED);
1569 
1570   if (mapped_flag != EF_NONE) {
1571     // The released key is a modifier
1572 
1573     DomKey::Base current_key = key_event.GetDomKey();
1574     auto key_state_iter = pressed_key_states_.begin();
1575     int event_flags =
1576         rewritten_event ? rewritten_event->flags() : key_event.flags();
1577     rewritten_event.reset();
1578 
1579     // Iterate the keys being pressed. Release the key events which satisfy one
1580     // of the following conditions:
1581     // (1) the key event's original key code (before key event rewriting if
1582     // any) is the same with the key to be released.
1583     // (2) the key event is rewritten and its original flags are influenced by
1584     // the key to be released.
1585     // Example: Press the Launcher button, Press the Up Arrow button, Release
1586     // the Launcher button. When Launcher is released: the key event whose key
1587     // code is Launcher should be released because it satisfies the condition 1;
1588     // the key event whose key code is PageUp should be released because it
1589     // satisfies the condition 2.
1590     EventDispatchDetails details;
1591     while (key_state_iter != pressed_key_states_.end() &&
1592            !details.dispatcher_destroyed) {
1593       const bool is_rewritten =
1594           (key_state_iter->first.key != key_state_iter->second.key);
1595       const bool flag_affected = key_state_iter->second.flags & mapped_flag;
1596       const bool should_release = key_state_iter->second.key == current_key ||
1597                                   (flag_affected && is_rewritten);
1598 
1599       if (should_release) {
1600         // If the key should be released, create a key event for it.
1601         auto dispatched_event = std::make_unique<KeyEvent>(
1602             key_event.type(), key_state_iter->first.key_code,
1603             key_state_iter->first.code, event_flags, key_state_iter->first.key,
1604             key_event.time_stamp());
1605         dispatched_event->set_scan_code(key_event.scan_code());
1606         details = SendEventFinally(continuation, dispatched_event.get());
1607 
1608         key_state_iter = pressed_key_states_.erase(key_state_iter);
1609         continue;
1610       }
1611       key_state_iter++;
1612     }
1613     return details;
1614   }
1615 
1616   // The released key is not a modifier
1617 
1618   current_key_state = MutableKeyState(
1619       rewritten_event ? static_cast<const KeyEvent*>(rewritten_event.get())
1620                       : &key_event);
1621   auto iter = std::find_if(pressed_key_states_.begin(),
1622                            pressed_key_states_.end(), key_state_comparator);
1623   if (iter != pressed_key_states_.end()) {
1624     pressed_key_states_.erase(iter);
1625 
1626     if (status == EventRewriteStatus::EVENT_REWRITE_CONTINUE)
1627       return SendEvent(continuation, &key_event);
1628 
1629     EventDispatchDetails details =
1630         SendEventFinally(continuation, rewritten_event.get());
1631     if (status == EventRewriteStatus::EVENT_REWRITE_DISPATCH_ANOTHER &&
1632         !details.dispatcher_destroyed) {
1633       return SendStickyKeysReleaseEvents(std::move(rewritten_event),
1634                                          continuation);
1635     }
1636     return details;
1637   }
1638 
1639   // Event rewriting may create a meaningless key event.
1640   // For example: press the Up Arrow button, press the Launcher button,
1641   // release the Up Arrow. When the Up Arrow button is released, key event
1642   // rewriting happens. However, the rewritten event is not among
1643   // |pressed_key_states_|. So it should be blocked and the original event
1644   // should be propagated.
1645   return SendEvent(continuation, &key_event);
1646 }
1647 
StoreCustomTopRowMapping(const InputDevice & keyboard_device)1648 bool EventRewriterChromeOS::StoreCustomTopRowMapping(
1649     const InputDevice& keyboard_device) {
1650   std::string layout;
1651   if (!GetCustomTopRowLayout(keyboard_device, &layout)) {
1652     LOG(WARNING) << "Could not read top row layout map for device "
1653                  << keyboard_device.id;
1654     return false;
1655   }
1656 
1657   base::flat_map<uint32_t, MutableKeyState> top_row_map;
1658   if (!ParseCustomTopRowLayoutMap(layout, &top_row_map)) {
1659     LOG(WARNING) << "Could not parse top row layout map: " << layout;
1660     return false;
1661   }
1662 
1663   top_row_scan_code_map_[keyboard_device.id] = std::move(top_row_map);
1664   return true;
1665 }
1666 
1667 // New CrOS keyboards differ from previous Chrome OS keyboards in a few
1668 // ways. Previous keyboards always sent F1-Fxx keys and allowed Chrome to
1669 // decide how to interpret them. New CrOS keyboards now always send action
1670 // keys (eg. Back, Refresh, Overview). So while the default previously was
1671 // to always expect to remap F-Key to action key, for these devices respect
1672 // what the keyboard sends unless the user overrides with either the Search
1673 // key or the "Top Row is always F-Key" setting.
1674 //
1675 // Additionally, these keyboards provide the mapping via sysfs so each
1676 // new keyboard does not need to be explicitly special cased in the future.
1677 //
1678 //  Search  Force function keys Key code   Result
1679 //  ------- ------------------- --------   ------
1680 //  No        No                Function   Unchanged
1681 //  Yes       No                Function   Unchanged
1682 //  No        Yes               Function   Unchanged
1683 //  Yes       Yes               Function   Unchanged
1684 //  No        No                Action     Unchanged
1685 //  Yes       No                Action     Action -> Fn
1686 //  No        Yes               Action     Action -> Fn
1687 //  Yes       Yes               Action     Unchanged
RewriteTopRowKeysForCustomLayout(int device_id,const KeyEvent & key_event,bool search_is_pressed,EventRewriterChromeOS::MutableKeyState * state)1688 bool EventRewriterChromeOS::RewriteTopRowKeysForCustomLayout(
1689     int device_id,
1690     const KeyEvent& key_event,
1691     bool search_is_pressed,
1692     EventRewriterChromeOS::MutableKeyState* state) {
1693   // Incoming function keys are never remapped.
1694   if (IsCustomLayoutFunctionKey(key_event.key_code())) {
1695     return true;
1696   }
1697 
1698   const auto& scan_code_map_iter = top_row_scan_code_map_.find(device_id);
1699   if (scan_code_map_iter == top_row_scan_code_map_.end()) {
1700     LOG(WARNING) << "Found no top row key mapping for device " << device_id;
1701     return false;
1702   }
1703 
1704   const base::flat_map<uint32_t, MutableKeyState>& scan_code_map =
1705       scan_code_map_iter->second;
1706   const auto& key_iter = scan_code_map.find(key_event.scan_code());
1707 
1708   // If the scan code appears in the top row mapping it is an action key.
1709   const bool is_action_key = (key_iter != scan_code_map.end());
1710   if (is_action_key) {
1711     if (search_is_pressed != ForceTopRowAsFunctionKeys()) {
1712       ApplyRemapping(key_iter->second, state);
1713     }
1714 
1715     // Clear command/search key if pressed. It's been consumed in the remapping
1716     // or wasn't pressed.
1717     state->flags &= ~EF_COMMAND_DOWN;
1718     return true;
1719   }
1720 
1721   return false;
1722 }
1723 
1724 // The keyboard layout for Wilco has a slightly different top-row layout, emits
1725 // both Fn and action keys from kernel and has key events with Dom codes and no
1726 // VKey value == VKEY_UNKNOWN. Depending on the state of the search key and
1727 // force-function-key preference, function keys have to be mapped to action keys
1728 // or vice versa.
1729 //
1730 //  Search  force function keys key code   Result
1731 //  ------- ------------------- --------   ------
1732 //  No        No                Function   Unchanged
1733 //  Yes       No                Function   Fn -> Action
1734 //  No        Yes               Function   Unchanged
1735 //  Yes       Yes               Function   Fn -> Action
1736 //  No        No                Action     Unchanged
1737 //  Yes       No                Action     Action -> Fn
1738 //  No        Yes               Action     Action -> Fn
1739 //  Yes       Yes               Action     Unchanged
RewriteTopRowKeysForLayoutWilco(const KeyEvent & key_event,bool search_is_pressed,MutableKeyState * state,KeyboardTopRowLayout layout)1740 bool EventRewriterChromeOS::RewriteTopRowKeysForLayoutWilco(
1741     const KeyEvent& key_event,
1742     bool search_is_pressed,
1743     MutableKeyState* state,
1744     KeyboardTopRowLayout layout) {
1745   // When the kernel issues an function key (Fn modifier help down) and the
1746   // search key is pressed, the function key needs to be mapped to its
1747   // corresponding action key. This table defines those function-to-action
1748   // mappings.
1749   static const KeyboardRemapping kFnkeysToActionKeys[] = {
1750       {{EF_NONE, VKEY_F1},
1751        {EF_NONE, DomCode::BROWSER_BACK, DomKey::BROWSER_BACK,
1752         VKEY_BROWSER_BACK}},
1753       {{EF_NONE, VKEY_F2},
1754        {EF_NONE, DomCode::BROWSER_REFRESH, DomKey::BROWSER_REFRESH,
1755         VKEY_BROWSER_REFRESH}},
1756       // Map F3 to VKEY_MEDIA_LAUNCH_APP2 + EF_NONE == toggle full screen:
1757       {{EF_NONE, VKEY_F3},
1758        {EF_NONE, DomCode::ZOOM_TOGGLE, DomKey::ZOOM_TOGGLE,
1759         VKEY_MEDIA_LAUNCH_APP2}},
1760       // Map F4 to VKEY_MEDIA_LAUNCH_APP1 + EF_NONE == overview:
1761       {{EF_NONE, VKEY_F4},
1762        {EF_NONE, DomCode::F4, DomKey::F4, VKEY_MEDIA_LAUNCH_APP1}},
1763       {{EF_NONE, VKEY_F5},
1764        {EF_NONE, DomCode::BRIGHTNESS_DOWN, DomKey::BRIGHTNESS_DOWN,
1765         VKEY_BRIGHTNESS_DOWN}},
1766       {{EF_NONE, VKEY_F6},
1767        {EF_NONE, DomCode::BRIGHTNESS_UP, DomKey::BRIGHTNESS_UP,
1768         VKEY_BRIGHTNESS_UP}},
1769       {{EF_NONE, VKEY_F7},
1770        {EF_NONE, DomCode::VOLUME_MUTE, DomKey::AUDIO_VOLUME_MUTE,
1771         VKEY_VOLUME_MUTE}},
1772       {{EF_NONE, VKEY_F8},
1773        {EF_NONE, DomCode::VOLUME_DOWN, DomKey::AUDIO_VOLUME_DOWN,
1774         VKEY_VOLUME_DOWN}},
1775       {{EF_NONE, VKEY_F9},
1776        {EF_NONE, DomCode::VOLUME_UP, DomKey::AUDIO_VOLUME_UP, VKEY_VOLUME_UP}},
1777       // Note: F10 and F11 are left as-is since no action is associated with
1778       // these keys.
1779       {{EF_NONE, VKEY_F10}, {EF_NONE, DomCode::F10, DomKey::F10, VKEY_F10}},
1780       {{EF_NONE, VKEY_F11}, {EF_NONE, DomCode::F11, DomKey::F11, VKEY_F11}},
1781       {{EF_NONE, VKEY_F12},
1782        // Map F12 to VKEY_MEDIA_LAUNCH_APP2 + EF_CONTROL_DOWN == toggle mirror
1783        // mode:
1784        {EF_CONTROL_DOWN, DomCode::F12, DomKey::F12, VKEY_MEDIA_LAUNCH_APP2}},
1785   };
1786 
1787   // When the kernel issues an action key (default mode) and the search key is
1788   // pressed, the action key needs to be mapped back to its corresponding
1789   // action key. This table defines those action-to-function mappings. Note:
1790   // this table is essentially the dual of kFnToActionLeys above.
1791   static const KeyboardRemapping kActionToFnKeys[] = {
1792       {{EF_NONE, VKEY_BROWSER_BACK},
1793        {EF_NONE, DomCode::F1, DomKey::F1, VKEY_F1}},
1794       {{EF_NONE, VKEY_BROWSER_REFRESH},
1795        {EF_NONE, DomCode::F2, DomKey::F2, VKEY_F2}},
1796       {{EF_NONE, VKEY_MEDIA_LAUNCH_APP1},
1797        {EF_NONE, DomCode::F4, DomKey::F4, VKEY_F4}},
1798       {{EF_NONE, VKEY_BRIGHTNESS_DOWN},
1799        {EF_NONE, DomCode::F5, DomKey::F5, VKEY_F5}},
1800       {{EF_NONE, VKEY_BRIGHTNESS_UP},
1801        {EF_NONE, DomCode::F6, DomKey::F6, VKEY_F6}},
1802       {{EF_NONE, VKEY_VOLUME_MUTE},
1803        {EF_NONE, DomCode::F7, DomKey::F7, VKEY_F7}},
1804       {{EF_NONE, VKEY_VOLUME_DOWN},
1805        {EF_NONE, DomCode::F8, DomKey::F8, VKEY_F8}},
1806       {{EF_NONE, VKEY_VOLUME_UP}, {EF_NONE, DomCode::F9, DomKey::F9, VKEY_F9}},
1807       // Do not change the order of the next two entries. The remapping of
1808       // VKEY_MEDIA_LAUNCH_APP2 with Control held down must appear before
1809       // VKEY_MEDIA_LAUNCH_APP2 by itself to be considered.
1810       {{EF_CONTROL_DOWN, VKEY_MEDIA_LAUNCH_APP2},
1811        {EF_NONE, DomCode::F12, DomKey::F12, VKEY_F12}},
1812       {{EF_NONE, VKEY_MEDIA_LAUNCH_APP2},
1813        {EF_NONE, DomCode::F3, DomKey::F3, VKEY_F3}},
1814       // VKEY_PRIVACY_SCREEN_TOGGLE shares a key with F12 on Drallion.
1815       {{EF_NONE, VKEY_PRIVACY_SCREEN_TOGGLE},
1816        {EF_NONE, DomCode::F12, DomKey::F12, VKEY_F12}},
1817   };
1818 
1819   MutableKeyState incoming_without_command = *state;
1820   incoming_without_command.flags &= ~EF_COMMAND_DOWN;
1821 
1822   if ((state->key_code >= VKEY_F1) && (state->key_code <= VKEY_F12)) {
1823     // Incoming key code is a Fn key. Check if it needs to be mapped back to its
1824     // corresponding action key.
1825     if (search_is_pressed) {
1826       // On some Drallion devices, F12 shares a key with privacy screen toggle.
1827       // Account for this before rewriting for Wilco 1.0 layout.
1828       if (layout == kKbdTopRowLayoutDrallion && state->key_code == VKEY_F12) {
1829         if (privacy_screen_supported_) {
1830           state->key_code = VKEY_PRIVACY_SCREEN_TOGGLE;
1831           state->code = DomCode::PRIVACY_SCREEN_TOGGLE;
1832         }
1833         // Clear command flag before returning
1834         state->flags = (state->flags & ~EF_COMMAND_DOWN);
1835         return true;
1836       }
1837       return RewriteWithKeyboardRemappings(kFnkeysToActionKeys,
1838                                            base::size(kFnkeysToActionKeys),
1839                                            incoming_without_command, state);
1840     }
1841     return true;
1842   } else if (IsKeyCodeInMappings(state->key_code, kActionToFnKeys,
1843                                  base::size(kActionToFnKeys))) {
1844     // Incoming key code is an action key. Check if it needs to be mapped back
1845     // to its corresponding function key.
1846     if (search_is_pressed != ForceTopRowAsFunctionKeys()) {
1847       // On Drallion, mirror mode toggle is on its own key so don't remap it.
1848       if (layout == kKbdTopRowLayoutDrallion &&
1849           MatchKeyboardRemapping(*state,
1850                                  {EF_CONTROL_DOWN, VKEY_MEDIA_LAUNCH_APP2})) {
1851         // Clear command flag before returning
1852         state->flags = (state->flags & ~EF_COMMAND_DOWN);
1853         return true;
1854       }
1855       return RewriteWithKeyboardRemappings(kActionToFnKeys,
1856                                            base::size(kActionToFnKeys),
1857                                            incoming_without_command, state);
1858     }
1859     // Remap Privacy Screen Toggle to F12 on Drallion devices that do not have
1860     // privacy screens.
1861     if (layout == kKbdTopRowLayoutDrallion && !privacy_screen_supported_ &&
1862         MatchKeyboardRemapping(*state, {EF_NONE, VKEY_PRIVACY_SCREEN_TOGGLE})) {
1863       state->key_code = VKEY_F12;
1864       state->code = DomCode::F12;
1865       state->key = DomKey::F12;
1866     }
1867     // At this point we know search_is_pressed == ForceTopRowAsFunctionKeys().
1868     // If they're both true, they cancel each other. Thus we can clear the
1869     // search-key modifier flag.
1870     state->flags &= ~EF_COMMAND_DOWN;
1871 
1872     return true;
1873   }
1874 
1875   return false;
1876 }
1877 
ForceTopRowAsFunctionKeys() const1878 bool EventRewriterChromeOS::ForceTopRowAsFunctionKeys() const {
1879   return delegate_ && delegate_->TopRowKeysAreFunctionKeys();
1880 }
1881 
KeyboardDeviceAdded(int device_id)1882 EventRewriterChromeOS::DeviceType EventRewriterChromeOS::KeyboardDeviceAdded(
1883     int device_id) {
1884   if (!DeviceDataManager::HasInstance())
1885     return kDeviceUnknown;
1886   const std::vector<InputDevice>& keyboard_devices =
1887       DeviceDataManager::GetInstance()->GetKeyboardDevices();
1888   for (const auto& keyboard : keyboard_devices) {
1889     if (keyboard.id != device_id)
1890       continue;
1891 
1892     DeviceType type;
1893     KeyboardTopRowLayout layout;
1894     // Don't store a device info when an error occurred while reading from
1895     // udev. This gives a chance to reattempt reading from udev on
1896     // subsequent key events, rather than being stuck in a bad state until
1897     // next reboot. crbug.com/783166.
1898     if (!IdentifyKeyboard(keyboard, &type, &layout)) {
1899       return type;
1900     }
1901 
1902     // For custom layouts, parse and save the top row mapping.
1903     if (layout == EventRewriterChromeOS::kKbdTopRowLayoutCustom) {
1904       if (!StoreCustomTopRowMapping(keyboard)) {
1905         return type;
1906       }
1907     }
1908 
1909     // Always overwrite the existing device_id since the X server may
1910     // reuse a device id for an unattached device.
1911     device_id_to_info_[keyboard.id] = {type, layout};
1912     return type;
1913   }
1914   return kDeviceUnknown;
1915 }
1916 
SendStickyKeysReleaseEvents(std::unique_ptr<Event> rewritten_event,const Continuation continuation)1917 EventDispatchDetails EventRewriterChromeOS::SendStickyKeysReleaseEvents(
1918     std::unique_ptr<Event> rewritten_event,
1919     const Continuation continuation) {
1920   EventDispatchDetails details;
1921   std::unique_ptr<Event> last_sent_event = std::move(rewritten_event);
1922   while (sticky_keys_controller_ && !details.dispatcher_destroyed) {
1923     std::unique_ptr<Event> new_event;
1924     EventRewriteStatus status = sticky_keys_controller_->NextDispatchEvent(
1925         *last_sent_event, &new_event);
1926     details = SendEventFinally(continuation, new_event.get());
1927     last_sent_event = std::move(new_event);
1928     if (status != EventRewriteStatus::EVENT_REWRITE_DISPATCH_ANOTHER)
1929       return details;
1930   }
1931   return details;
1932 }
1933 
1934 }  // namespace ui
1935