Upstream version 11.39.250.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / events / event_rewriter.cc
index 21c8a9c..879e85a 100644 (file)
@@ -4,88 +4,77 @@
 
 #include "chrome/browser/chromeos/events/event_rewriter.h"
 
-#include <X11/extensions/XInput2.h>
-#include <X11/keysym.h>
-#include <X11/XF86keysym.h>
-#include <X11/Xlib.h>
-// Get rid of macros from Xlib.h that conflicts with other parts of the code.
-#undef RootWindow
-#undef Status
-
 #include <vector>
 
-#include "ash/shell.h"
+#include "ash/sticky_keys/sticky_keys_controller.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "base/command_line.h"
 #include "base/logging.h"
+#include "base/macros.h"
 #include "base/prefs/pref_service.h"
 #include "base/strings/string_util.h"
 #include "base/sys_info.h"
-#include "chrome/browser/chromeos/events/keyboard_driven_event_rewriter.h"
-#include "chrome/browser/chromeos/events/xinput_hierarchy_changed_event_listener.h"
-#include "chrome/browser/chromeos/login/login_display_host_impl.h"
-#include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
+#include "chrome/browser/extensions/extension_commands_global_registry.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/chromeos_switches.h"
+#include "chromeos/ime/ime_keyboard.h"
 #include "chromeos/ime/input_method_manager.h"
-#include "chromeos/ime/xkeyboard.h"
-#include "ui/aura/root_window.h"
-#include "ui/base/x/x11_util.h"
+#include "components/user_manager/user_manager.h"
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
-#include "ui/events/keycodes/keyboard_code_conversion_x.h"
-#include "ui/views/corewm/window_util.h"
+#include "ui/events/keycodes/keyboard_code_conversion.h"
+#include "ui/wm/core/window_util.h"
 
-namespace {
+#if defined(USE_X11)
+#include <X11/extensions/XInput2.h>
+#include <X11/Xlib.h>
+// Get rid of macros from Xlib.h that conflicts with other parts of the code.
+#undef RootWindow
+#undef Status
 
-const int kBadDeviceId = -1;
+#include "ui/base/x/x11_util.h"
+#include "ui/events/keycodes/keyboard_code_conversion_x.h"
+#endif
 
-const char kNeo2LayoutId[] = "xkb:de:neo:ger";
-const char kCaMultixLayoutId[] = "xkb:ca:multix:fra";
+namespace chromeos {
+
+namespace {
 
-// A key code and a flag we should use when a key is remapped to |remap_to|.
+// Table of key properties of remappable keys and/or remapping targets.
+// This is searched in two distinct ways:
+//  - |remap_to| is an |input_method::ModifierKey|, which is the form
+//    held in user preferences. |GetRemappedKey()| maps this to the
+//    corresponding |key_code| and characterstic event |flag|.
+//  - |flag| is a |ui::EventFlags|. |GetRemappedModifierMasks()| maps this
+//    to the corresponding user preference |pref_name| for that flag's
+//    key, so that it can then be remapped as above.
+// In addition |kModifierRemappingCtrl| is a direct reference to the
+// Control key entry in the table, used in handling Chromebook Diamond
+// and Apple Command keys.
 const struct ModifierRemapping {
   int remap_to;
   int flag;
-  unsigned int native_modifier;
-  ui::KeyboardCode keycode;
-  KeySym native_keysyms[4];  // left, right, shift+left, shift+right.
+  ui::KeyboardCode key_code;
+  const char* pref_name;
 } kModifierRemappings[] = {
-  { chromeos::input_method::kSearchKey, 0, Mod4Mask, ui::VKEY_LWIN,
-    { XK_Super_L, XK_Super_L, XK_Super_L, XK_Super_L }},
-  { chromeos::input_method::kControlKey, ui::EF_CONTROL_DOWN, ControlMask,
-    ui::VKEY_CONTROL,
-    { XK_Control_L, XK_Control_R, XK_Control_L, XK_Control_R }},
-  { chromeos::input_method::kAltKey, ui::EF_ALT_DOWN, Mod1Mask,
-    ui::VKEY_MENU, { XK_Alt_L, XK_Alt_R, XK_Meta_L, XK_Meta_R }},
-  { chromeos::input_method::kVoidKey, 0, 0U, ui::VKEY_UNKNOWN,
-    { XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol }},
-  { chromeos::input_method::kCapsLockKey, 0, 0U, ui::VKEY_CAPITAL,
-    { XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock }},
-  { chromeos::input_method::kEscapeKey, 0, 0U, ui::VKEY_ESCAPE,
-    { XK_Escape, XK_Escape, XK_Escape, XK_Escape }},
+      {input_method::kSearchKey, ui::EF_COMMAND_DOWN, ui::VKEY_LWIN,
+       prefs::kLanguageRemapSearchKeyTo},
+      {input_method::kControlKey, ui::EF_CONTROL_DOWN, ui::VKEY_CONTROL,
+       prefs::kLanguageRemapControlKeyTo},
+      {input_method::kAltKey, ui::EF_ALT_DOWN, ui::VKEY_MENU,
+       prefs::kLanguageRemapAltKeyTo},
+      {input_method::kVoidKey, 0, ui::VKEY_UNKNOWN, NULL},
+      {input_method::kCapsLockKey, ui::EF_MOD3_DOWN, ui::VKEY_CAPITAL,
+       prefs::kLanguageRemapCapsLockKeyTo},
+      {input_method::kEscapeKey, 0, ui::VKEY_ESCAPE, NULL},
+      {0, 0, ui::VKEY_F15, prefs::kLanguageRemapDiamondKeyTo},
 };
 
 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1];
 
-// A structure for converting |native_modifier| to a pair of |flag| and
-// |pref_name|.
-const struct ModifierFlagToPrefName {
-  unsigned int native_modifier;
-  int flag;
-  const char* pref_name;
-} kModifierFlagToPrefName[] = {
-  // TODO(yusukes): When the device has a Chrome keyboard (i.e. the one without
-  // Caps Lock), we should not check kLanguageRemapCapsLockKeyTo.
-  { Mod3Mask, 0, prefs::kLanguageRemapCapsLockKeyTo },
-  { Mod4Mask, 0, prefs::kLanguageRemapSearchKeyTo },
-  { ControlMask, ui::EF_CONTROL_DOWN, prefs::kLanguageRemapControlKeyTo },
-  { Mod1Mask, ui::EF_ALT_DOWN, prefs::kLanguageRemapAltKeyTo },
-  { Mod2Mask, 0, prefs::kLanguageRemapDiamondKeyTo },
-};
-
 // Gets a remapped key for |pref_name| key. For example, to find out which
 // key Search is currently remapped to, call the function with
 // prefs::kLanguageRemapSearchKeyTo.
@@ -101,78 +90,44 @@ const ModifierRemapping* GetRemappedKey(const std::string& pref_name,
   return NULL;
 }
 
-bool IsRight(KeySym native_keysym) {
-  switch (native_keysym) {
-    case XK_Alt_R:
-    case XK_Control_R:
-    case XK_Hyper_R:
-    case XK_Meta_R:
-    case XK_Shift_R:
-    case XK_Super_R:
-      return true;
-    default:
-      break;
-  }
-  return false;
-}
-
 bool HasDiamondKey() {
   return CommandLine::ForCurrentProcess()->HasSwitch(
       chromeos::switches::kHasChromeOSDiamondKey);
 }
 
-bool IsMod3UsedByCurrentInputMethod() {
+bool IsISOLevel5ShiftUsedByCurrentInputMethod() {
   // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask,
   // it's not possible to make both features work. For now, we don't remap
   // Mod3Mask when Neo2 is in use.
   // TODO(yusukes): Remove the restriction.
-  chromeos::input_method::InputMethodManager* manager =
-      chromeos::input_method::InputMethodManager::Get();
-  return manager->GetCurrentInputMethod().id() == kNeo2LayoutId ||
-      manager->GetCurrentInputMethod().id() == kCaMultixLayoutId;
+  input_method::InputMethodManager* manager =
+      input_method::InputMethodManager::Get();
+  return manager->IsISOLevel5ShiftUsedByCurrentInputMethod();
 }
 
-}  // namespace
-
-namespace chromeos {
-
-EventRewriter::EventRewriter()
-    : last_device_id_(kBadDeviceId),
-      xkeyboard_for_testing_(NULL),
-      keyboard_driven_event_rewriter_(new KeyboardDrivenEventRewriter),
-      pref_service_for_testing_(NULL) {
-  // The ash shell isn't instantiated for our unit tests.
-  if (ash::Shell::HasInstance()) {
-    ash::Shell::GetPrimaryRootWindow()->GetDispatcher()->
-        AddRootWindowObserver(this);
-  }
-  base::MessageLoopForUI::current()->AddObserver(this);
-  if (base::SysInfo::IsRunningOnChromeOS()) {
-    XInputHierarchyChangedEventListener::GetInstance()->AddObserver(this);
-  }
-  RefreshKeycodes();
-}
-
-EventRewriter::~EventRewriter() {
-  base::MessageLoopForUI::current()->RemoveObserver(this);
-  if (ash::Shell::HasInstance()) {
-    ash::Shell::GetPrimaryRootWindow()->GetDispatcher()->
-        RemoveRootWindowObserver(this);
-  }
-  if (base::SysInfo::IsRunningOnChromeOS()) {
-    XInputHierarchyChangedEventListener::GetInstance()->RemoveObserver(this);
-  }
-}
+bool IsExtensionCommandRegistered(const ui::KeyEvent& key_event) {
+  // Some keyboard events for ChromeOS get rewritten, such as:
+  // Search+Shift+Left gets converted to Shift+Home (BeginDocument).
+  // This doesn't make sense if the user has assigned that shortcut
+  // to an extension. Because:
+  // 1) The extension would, upon seeing a request for Ctrl+Shift+Home have
+  //    to register for Shift+Home, instead.
+  // 2) The conversion is unnecessary, because Shift+Home (BeginDocument) isn't
+  //    going to be executed.
+  // Therefore, we skip converting the accelerator if an extension has
+  // registered for this shortcut.
+  Profile* profile = ProfileManager::GetActiveUserProfile();
+  if (!profile || !extensions::ExtensionCommandsGlobalRegistry::Get(profile))
+    return false;
 
-EventRewriter::DeviceType EventRewriter::DeviceAddedForTesting(
-    int device_id,
-    const std::string& device_name) {
-  return DeviceAddedInternal(device_id, device_name);
+  int modifiers = key_event.flags() & (ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN |
+                                       ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
+  ui::Accelerator accelerator(key_event.key_code(), modifiers);
+  return extensions::ExtensionCommandsGlobalRegistry::Get(profile)
+      ->IsRegistered(accelerator);
 }
 
-// static
-EventRewriter::DeviceType EventRewriter::GetDeviceType(
-    const std::string& device_name) {
+EventRewriter::DeviceType GetDeviceType(const std::string& device_name) {
   std::vector<std::string> tokens;
   Tokenize(device_name, " .", &tokens);
 
@@ -186,97 +141,176 @@ EventRewriter::DeviceType EventRewriter::GetDeviceType(
     if (!found_keyboard && LowerCaseEqualsASCII(tokens[i], "keyboard"))
       found_keyboard = true;
     if (found_apple && found_keyboard)
-      return kDeviceAppleKeyboard;
+      return EventRewriter::kDeviceAppleKeyboard;
   }
 
-  return kDeviceUnknown;
+  return EventRewriter::kDeviceUnknown;
 }
 
-void EventRewriter::RewriteForTesting(XEvent* event) {
-  Rewrite(event);
+}  // namespace
+
+EventRewriter::EventRewriter(ash::StickyKeysController* sticky_keys_controller)
+    : last_keyboard_device_id_(ui::ED_UNKNOWN_DEVICE),
+      ime_keyboard_for_testing_(NULL),
+      pref_service_for_testing_(NULL),
+      sticky_keys_controller_(sticky_keys_controller),
+      current_diamond_key_modifier_flags_(ui::EF_NONE) {
 }
 
-void EventRewriter::OnKeyboardMappingChanged(const aura::RootWindow* root) {
-  RefreshKeycodes();
+EventRewriter::~EventRewriter() {
 }
 
-base::EventStatus EventRewriter::WillProcessEvent(
-    const base::NativeEvent& event) {
-  XEvent* xevent = event;
-  if (xevent->type == KeyPress || xevent->type == KeyRelease)
-    Rewrite(xevent);
-  else if (xevent->type == GenericEvent)
-    RewriteLocatedEvent(xevent);
-  return base::EVENT_CONTINUE;
+EventRewriter::DeviceType EventRewriter::KeyboardDeviceAddedForTesting(
+    int device_id,
+    const std::string& device_name) {
+  // Tests must avoid XI2 reserved device IDs.
+  DCHECK((device_id < 0) || (device_id > 1));
+  return KeyboardDeviceAddedInternal(device_id, device_name);
 }
 
-void EventRewriter::DidProcessEvent(const base::NativeEvent& event) {
+void EventRewriter::RewriteMouseButtonEventForTesting(
+    const ui::MouseEvent& event,
+    scoped_ptr<ui::Event>* rewritten_event) {
+  RewriteMouseButtonEvent(event, rewritten_event);
 }
 
-void EventRewriter::DeviceAdded(int device_id) {
-  DCHECK_NE(XIAllDevices, device_id);
-  DCHECK_NE(XIAllMasterDevices, device_id);
-  if (device_id == XIAllDevices || device_id == XIAllMasterDevices) {
-    LOG(ERROR) << "Unexpected device_id passed: " << device_id;
-    return;
+ui::EventRewriteStatus EventRewriter::RewriteEvent(
+    const ui::Event& event,
+    scoped_ptr<ui::Event>* rewritten_event) {
+  if ((event.type() == ui::ET_KEY_PRESSED) ||
+      (event.type() == ui::ET_KEY_RELEASED)) {
+    return RewriteKeyEvent(static_cast<const ui::KeyEvent&>(event),
+                           rewritten_event);
   }
-
-  int ndevices_return = 0;
-  XIDeviceInfo* device_info = XIQueryDevice(gfx::GetXDisplay(),
-                                            device_id,
-                                            &ndevices_return);
-
-  // Since |device_id| is neither XIAllDevices nor XIAllMasterDevices,
-  // the number of devices found should be either 0 (not found) or 1.
-  if (!device_info) {
-    LOG(ERROR) << "XIQueryDevice: Device ID " << device_id << " is unknown.";
-    return;
+  if ((event.type() == ui::ET_MOUSE_PRESSED) ||
+      (event.type() == ui::ET_MOUSE_RELEASED)) {
+    return RewriteMouseButtonEvent(static_cast<const ui::MouseEvent&>(event),
+                                   rewritten_event);
   }
-
-  DCHECK_EQ(1, ndevices_return);
-  for (int i = 0; i < ndevices_return; ++i) {
-    DCHECK_EQ(device_id, device_info[i].deviceid);  // see the comment above.
-    DCHECK(device_info[i].name);
-    DeviceAddedInternal(device_info[i].deviceid, device_info[i].name);
+  if (event.type() == ui::ET_MOUSEWHEEL) {
+    return RewriteMouseWheelEvent(
+        static_cast<const ui::MouseWheelEvent&>(event), rewritten_event);
   }
-
-  XIFreeDeviceInfo(device_info);
+  if ((event.type() == ui::ET_TOUCH_PRESSED) ||
+      (event.type() == ui::ET_TOUCH_RELEASED)) {
+    return RewriteTouchEvent(static_cast<const ui::TouchEvent&>(event),
+                             rewritten_event);
+  }
+  if (event.IsScrollEvent()) {
+    return RewriteScrollEvent(static_cast<const ui::ScrollEvent&>(event),
+                              rewritten_event);
+  }
+  return ui::EVENT_REWRITE_CONTINUE;
 }
 
-void EventRewriter::DeviceRemoved(int device_id) {
-  device_id_to_type_.erase(device_id);
+ui::EventRewriteStatus EventRewriter::NextDispatchEvent(
+    const ui::Event& last_event,
+    scoped_ptr<ui::Event>* new_event) {
+  if (sticky_keys_controller_) {
+    // In the case of sticky keys, we know what the events obtained here are:
+    // modifier key releases that match the ones previously discarded. So, we
+    // know that they don't have to be passed through the post-sticky key
+    // rewriting phases, |RewriteExtendedKeys()| and |RewriteFunctionKeys()|,
+    // because those phases do nothing with modifier key releases.
+    return sticky_keys_controller_->NextDispatchEvent(new_event);
+  }
+  NOTREACHED();
+  return ui::EVENT_REWRITE_CONTINUE;
 }
 
-void EventRewriter::DeviceKeyPressedOrReleased(int device_id) {
-  std::map<int, DeviceType>::const_iterator iter =
-      device_id_to_type_.find(device_id);
-  if (iter == device_id_to_type_.end()) {
-    // |device_id| is unknown. This means the device was connected before
-    // booting the OS. Query the name of the device and add it to the map.
-    DeviceAdded(device_id);
+void EventRewriter::BuildRewrittenKeyEvent(
+    const ui::KeyEvent& key_event,
+    ui::KeyboardCode key_code,
+    int flags,
+    scoped_ptr<ui::Event>* rewritten_event) {
+  ui::KeyEvent* rewritten_key_event = NULL;
+#if defined(USE_X11)
+  XEvent* xev = key_event.native_event();
+  if (xev) {
+    XEvent xkeyevent;
+    // Convert all XI2-based key events into X11 core-based key events,
+    // until consumers no longer depend on receiving X11 core events.
+    if (xev->type == GenericEvent)
+      ui::InitXKeyEventFromXIDeviceEvent(*xev, &xkeyevent);
+    else
+      xkeyevent.xkey = xev->xkey;
+
+    unsigned int original_x11_keycode = xkeyevent.xkey.keycode;
+    // Update native event to match rewritten |ui::Event|.
+    // The X11 keycode represents a physical key position, so it shouldn't
+    // change unless we have actually changed keys, not just modifiers.
+    // This is one guard against problems like crbug.com/390263.
+    if (key_event.key_code() != key_code) {
+      xkeyevent.xkey.keycode =
+          XKeyCodeForWindowsKeyCode(key_code, flags, gfx::GetXDisplay());
+    }
+    ui::KeyEvent x11_key_event(&xkeyevent);
+    rewritten_key_event = new ui::KeyEvent(x11_key_event);
+
+    // For numpad keys, the key char should always NOT be changed because
+    // XKeyCodeForWindowsKeyCode method cannot handle non-US keyboard layout.
+    // The correct key char can be got from original X11 keycode but not for the
+    // rewritten X11 keycode.
+    // For Shift+NumpadKey cases, use the rewritten X11 keycode (US layout).
+    // Please see crbug.com/335644.
+    if (key_code >= ui::VKEY_NUMPAD0 && key_code <= ui::VKEY_DIVIDE) {
+      XEvent numpad_xevent;
+      numpad_xevent.xkey = xkeyevent.xkey;
+      // Remove the shift state before getting key char.
+      // Because X11/XKB sometimes returns unexpected key char for
+      // Shift+NumpadKey. e.g. Shift+Numpad_4 returns 'D', etc.
+      numpad_xevent.xkey.state &= ~ShiftMask;
+      numpad_xevent.xkey.state |= Mod2Mask;  // Always set NumLock mask.
+      if (!(flags & ui::EF_SHIFT_DOWN))
+        numpad_xevent.xkey.keycode = original_x11_keycode;
+      rewritten_key_event->set_character(
+          ui::GetCharacterFromXEvent(&numpad_xevent));
+    }
   }
+#endif
+  if (!rewritten_key_event)
+    rewritten_key_event = new ui::KeyEvent(key_event);
+  rewritten_key_event->set_flags(flags);
+  rewritten_key_event->set_key_code(key_code);
+#if defined(USE_X11)
+  ui::UpdateX11EventForFlags(rewritten_key_event);
+  rewritten_key_event->NormalizeFlags();
+#endif
+  rewritten_event->reset(rewritten_key_event);
+}
 
-  last_device_id_ = device_id;
+void EventRewriter::DeviceKeyPressedOrReleased(int device_id) {
+  if (!device_id_to_type_.count(device_id))
+    KeyboardDeviceAdded(device_id);
+  last_keyboard_device_id_ = device_id;
 }
 
-void EventRewriter::RefreshKeycodes() {
-  keysym_to_keycode_map_.clear();
+const PrefService* EventRewriter::GetPrefService() const {
+  if (pref_service_for_testing_)
+    return pref_service_for_testing_;
+  Profile* profile = ProfileManager::GetActiveUserProfile();
+  return profile ? profile->GetPrefs() : NULL;
 }
 
-KeyCode EventRewriter::NativeKeySymToNativeKeycode(KeySym keysym) {
-  if (keysym_to_keycode_map_.count(keysym))
-    return keysym_to_keycode_map_[keysym];
+bool EventRewriter::IsAppleKeyboard() const {
+  if (last_keyboard_device_id_ == ui::ED_UNKNOWN_DEVICE)
+    return false;
+
+  // Check which device generated |event|.
+  std::map<int, DeviceType>::const_iterator iter =
+      device_id_to_type_.find(last_keyboard_device_id_);
+  if (iter == device_id_to_type_.end()) {
+    LOG(ERROR) << "Device ID " << last_keyboard_device_id_ << " is unknown.";
+    return false;
+  }
 
-  XDisplay* display = gfx::GetXDisplay();
-  KeyCode keycode = XKeysymToKeycode(display, keysym);
-  keysym_to_keycode_map_[keysym] = keycode;
-  return keycode;
+  const DeviceType type = iter->second;
+  return type == kDeviceAppleKeyboard;
 }
 
-bool EventRewriter::TopRowKeysAreFunctionKeys(XEvent* event) const {
+bool EventRewriter::TopRowKeysAreFunctionKeys(const ui::KeyEvent& event) const {
   const PrefService* prefs = GetPrefService();
-  if (prefs &&
-      prefs->FindPreference(prefs::kLanguageSendFunctionKeys) &&
+  if (prefs && prefs->FindPreference(prefs::kLanguageSendFunctionKeys) &&
       prefs->GetBoolean(prefs::kLanguageSendFunctionKeys))
     return true;
 
@@ -284,156 +318,215 @@ bool EventRewriter::TopRowKeysAreFunctionKeys(XEvent* event) const {
   return state ? state->top_row_keys_are_function_keys() : false;
 }
 
-bool EventRewriter::RewriteWithKeyboardRemappingsByKeySym(
-    const KeyboardRemapping* remappings,
-    size_t num_remappings,
-    KeySym keysym,
-    unsigned int native_mods,
-    KeySym* remapped_native_keysym,
-    unsigned int* remapped_native_mods) {
-  for (size_t i = 0; i < num_remappings; ++i) {
-    const KeyboardRemapping& map = remappings[i];
-
-    if (keysym != map.input_keysym)
-      continue;
-    unsigned int matched_mods = native_mods & map.input_native_mods;
-    if (matched_mods != map.input_native_mods)
+int EventRewriter::GetRemappedModifierMasks(const PrefService& pref_service,
+                                            const ui::Event& event,
+                                            int original_flags) const {
+  int unmodified_flags = original_flags;
+  int rewritten_flags = current_diamond_key_modifier_flags_;
+  for (size_t i = 0; unmodified_flags && (i < arraysize(kModifierRemappings));
+       ++i) {
+    const ModifierRemapping* remapped_key = NULL;
+    if (!(unmodified_flags & kModifierRemappings[i].flag))
       continue;
-
-    *remapped_native_keysym = map.output_keysym;
-    *remapped_native_mods = (native_mods & ~map.input_native_mods) |
-                            map.output_native_mods;
-    return true;
+    switch (kModifierRemappings[i].flag) {
+      case ui::EF_COMMAND_DOWN:
+        // Rewrite Command key presses on an Apple keyboard to Control.
+        if (IsAppleKeyboard()) {
+          DCHECK_EQ(ui::EF_CONTROL_DOWN, kModifierRemappingCtrl->flag);
+          remapped_key = kModifierRemappingCtrl;
+        }
+        break;
+      case ui::EF_MOD3_DOWN:
+        // If EF_MOD3_DOWN is used by the current input method, leave it alone;
+        // it is not remappable.
+        if (IsISOLevel5ShiftUsedByCurrentInputMethod())
+          continue;
+        // Otherwise, Mod3Mask is set on X events when the Caps Lock key
+        // is down, but, if Caps Lock is remapped, CapsLock is NOT set,
+        // because pressing the key does not invoke caps lock. So, the
+        // kModifierRemappings[] table uses EF_MOD3_DOWN for the Caps
+        // Lock remapping.
+        break;
+      default:
+        break;
+    }
+    if (!remapped_key && kModifierRemappings[i].pref_name) {
+      remapped_key =
+          GetRemappedKey(kModifierRemappings[i].pref_name, pref_service);
+    }
+    if (remapped_key) {
+      unmodified_flags &= ~kModifierRemappings[i].flag;
+      rewritten_flags |= remapped_key->flag;
+    }
   }
-
-  return false;
+  return rewritten_flags | unmodified_flags;
 }
 
 bool EventRewriter::RewriteWithKeyboardRemappingsByKeyCode(
     const KeyboardRemapping* remappings,
     size_t num_remappings,
-    KeyCode keycode,
-    unsigned int native_mods,
-    KeySym* remapped_native_keysym,
-    unsigned int* remapped_native_mods) {
+    const MutableKeyState& input,
+    MutableKeyState* remapped_state) {
   for (size_t i = 0; i < num_remappings; ++i) {
     const KeyboardRemapping& map = remappings[i];
-
-    KeyCode input_keycode = NativeKeySymToNativeKeycode(map.input_keysym);
-    if (keycode != input_keycode)
+    if (input.key_code != map.input_key_code)
       continue;
-    unsigned int matched_mods = native_mods & map.input_native_mods;
-    if (matched_mods != map.input_native_mods)
+    if ((input.flags & map.input_flags) != map.input_flags)
       continue;
-
-    *remapped_native_keysym = map.output_keysym;
-    *remapped_native_mods = (native_mods & ~map.input_native_mods) |
-                            map.output_native_mods;
+    remapped_state->key_code = map.output_key_code;
+    remapped_state->flags = (input.flags & ~map.input_flags) | map.output_flags;
     return true;
   }
-
   return false;
 }
 
-const PrefService* EventRewriter::GetPrefService() const {
-  if (pref_service_for_testing_)
-    return pref_service_for_testing_;
-  Profile* profile = ProfileManager::GetActiveUserProfile();
-  return profile ? profile->GetPrefs() : NULL;
-}
-
-void EventRewriter::Rewrite(XEvent* event) {
+ui::EventRewriteStatus EventRewriter::RewriteKeyEvent(
+    const ui::KeyEvent& key_event,
+    scoped_ptr<ui::Event>* rewritten_event) {
+  if (IsExtensionCommandRegistered(key_event))
+    return ui::EVENT_REWRITE_CONTINUE;
+  if (key_event.source_device_id() != ui::ED_UNKNOWN_DEVICE)
+    DeviceKeyPressedOrReleased(key_event.source_device_id());
+  MutableKeyState state = {key_event.flags(), key_event.key_code()};
   // Do not rewrite an event sent by ui_controls::SendKeyPress(). See
   // crbug.com/136465.
-  if (event->xkey.send_event)
-    return;
-
-  // Keyboard driven rewriting happen first. Skip further processing if event is
-  // changed.
-  if (keyboard_driven_event_rewriter_->RewriteIfKeyboardDrivenOnLoginScreen(
-          event)) {
-    return;
+  if (!(key_event.flags() & ui::EF_FINAL)) {
+    RewriteModifierKeys(key_event, &state);
+    RewriteNumPadKeys(key_event, &state);
   }
-
-  RewriteModifiers(event);
-  RewriteNumPadKeys(event);
-  RewriteExtendedKeys(event);
-  RewriteFunctionKeys(event);
+  ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
+  if (sticky_keys_controller_) {
+    status = sticky_keys_controller_->RewriteKeyEvent(
+        key_event, state.key_code, &state.flags);
+    if (status == ui::EVENT_REWRITE_DISCARD)
+      return ui::EVENT_REWRITE_DISCARD;
+  }
+  if (!(key_event.flags() & ui::EF_FINAL)) {
+    RewriteExtendedKeys(key_event, &state);
+    RewriteFunctionKeys(key_event, &state);
+  }
+  if ((key_event.flags() == state.flags) &&
+      (key_event.key_code() == state.key_code) &&
+#if defined(USE_X11)
+      // TODO(kpschoedel): This test is present because several consumers of
+      // key events depend on having a native core X11 event, so we rewrite
+      // all XI2 key events (GenericEvent) into corresponding core X11 key
+      // events. Remove this when event consumers no longer care about
+      // native X11 event details (crbug.com/380349).
+      (!key_event.HasNativeEvent() ||
+       (key_event.native_event()->type != GenericEvent)) &&
+#endif
+      (status == ui::EVENT_REWRITE_CONTINUE)) {
+    return ui::EVENT_REWRITE_CONTINUE;
+  }
+  // Sticky keys may have returned a result other than |EVENT_REWRITE_CONTINUE|,
+  // in which case we need to preserve that return status. Alternatively, we
+  // might be here because key_event changed, in which case we need to
+  // return |EVENT_REWRITE_REWRITTEN|.
+  if (status == ui::EVENT_REWRITE_CONTINUE)
+    status = ui::EVENT_REWRITE_REWRITTEN;
+  BuildRewrittenKeyEvent(
+      key_event, state.key_code, state.flags, rewritten_event);
+  return status;
 }
 
-bool EventRewriter::IsAppleKeyboard() const {
-  if (last_device_id_ == kBadDeviceId)
-    return false;
-
-  // Check which device generated |event|.
-  std::map<int, DeviceType>::const_iterator iter =
-      device_id_to_type_.find(last_device_id_);
-  if (iter == device_id_to_type_.end()) {
-    LOG(ERROR) << "Device ID " << last_device_id_ << " is unknown.";
-    return false;
+ui::EventRewriteStatus EventRewriter::RewriteMouseButtonEvent(
+    const ui::MouseEvent& mouse_event,
+    scoped_ptr<ui::Event>* rewritten_event) {
+  int flags = mouse_event.flags();
+  RewriteLocatedEvent(mouse_event, &flags);
+  ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
+  if (sticky_keys_controller_)
+    status = sticky_keys_controller_->RewriteMouseEvent(mouse_event, &flags);
+  int changed_button = ui::EF_NONE;
+  if ((mouse_event.type() == ui::ET_MOUSE_PRESSED) ||
+      (mouse_event.type() == ui::ET_MOUSE_RELEASED)) {
+    changed_button = RewriteModifierClick(mouse_event, &flags);
   }
-
-  const DeviceType type = iter->second;
-  return type == kDeviceAppleKeyboard;
+  if ((mouse_event.flags() == flags) &&
+      (status == ui::EVENT_REWRITE_CONTINUE)) {
+    return ui::EVENT_REWRITE_CONTINUE;
+  }
+  if (status == ui::EVENT_REWRITE_CONTINUE)
+    status = ui::EVENT_REWRITE_REWRITTEN;
+  ui::MouseEvent* rewritten_mouse_event = new ui::MouseEvent(mouse_event);
+  rewritten_event->reset(rewritten_mouse_event);
+  rewritten_mouse_event->set_flags(flags);
+#if defined(USE_X11)
+  ui::UpdateX11EventForFlags(rewritten_mouse_event);
+#endif
+  if (changed_button != ui::EF_NONE) {
+    rewritten_mouse_event->set_changed_button_flags(changed_button);
+#if defined(USE_X11)
+    ui::UpdateX11EventForChangedButtonFlags(rewritten_mouse_event);
+#endif
+  }
+  return status;
 }
 
-void EventRewriter::GetRemappedModifierMasks(
-    unsigned int original_native_modifiers,
-    unsigned int* remapped_native_modifiers) const {
-  // TODO(glotov): remove the following condition when we do not restart chrome
-  // when user logs in as guest. See Rewrite() for details.
-  if (UserManager::Get()->IsLoggedInAsGuest() &&
-      LoginDisplayHostImpl::default_host()) {
-    return;
+ui::EventRewriteStatus EventRewriter::RewriteMouseWheelEvent(
+    const ui::MouseWheelEvent& wheel_event,
+    scoped_ptr<ui::Event>* rewritten_event) {
+  if (!sticky_keys_controller_)
+    return ui::EVENT_REWRITE_CONTINUE;
+  int flags = wheel_event.flags();
+  ui::EventRewriteStatus status =
+      sticky_keys_controller_->RewriteMouseEvent(wheel_event, &flags);
+  if ((wheel_event.flags() == flags) &&
+      (status == ui::EVENT_REWRITE_CONTINUE)) {
+    return ui::EVENT_REWRITE_CONTINUE;
   }
+  if (status == ui::EVENT_REWRITE_CONTINUE)
+    status = ui::EVENT_REWRITE_REWRITTEN;
+  ui::MouseWheelEvent* rewritten_wheel_event =
+      new ui::MouseWheelEvent(wheel_event);
+  rewritten_event->reset(rewritten_wheel_event);
+  rewritten_wheel_event->set_flags(flags);
+#if defined(USE_X11)
+  ui::UpdateX11EventForFlags(rewritten_wheel_event);
+#endif
+  return status;
+}
 
-  const PrefService* pref_service = GetPrefService();
-  if (!pref_service)
-    return;
-
-  // When a diamond key is not available, a Mod2Mask should not treated as a
-  // configurable modifier because Mod2Mask may be worked as NumLock mask.
-  // (cf. http://crbug.com/173956)
-  const bool skip_mod2 = !HasDiamondKey();
-  // If Mod3 is used by the current input method, don't allow the CapsLock
-  // pref to remap it, or the keyboard behavior will be broken.
-  const bool skip_mod3 = IsMod3UsedByCurrentInputMethod();
-
-  for (size_t i = 0; i < arraysize(kModifierFlagToPrefName); ++i) {
-    if ((skip_mod2 && kModifierFlagToPrefName[i].native_modifier == Mod2Mask) ||
-        (skip_mod3 && kModifierFlagToPrefName[i].native_modifier == Mod3Mask)) {
-      continue;
-    }
-    if (original_native_modifiers &
-        kModifierFlagToPrefName[i].native_modifier) {
-      const ModifierRemapping* remapped_key =
-          GetRemappedKey(kModifierFlagToPrefName[i].pref_name, *pref_service);
-      // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R.
-      if (IsAppleKeyboard() &&
-          (kModifierFlagToPrefName[i].native_modifier == Mod4Mask)) {
-        remapped_key = kModifierRemappingCtrl;
-      }
-      if (remapped_key) {
-        *remapped_native_modifiers |= remapped_key->native_modifier;
-      } else {
-        *remapped_native_modifiers |=
-            kModifierFlagToPrefName[i].native_modifier;
-      }
-    }
-  }
+ui::EventRewriteStatus EventRewriter::RewriteTouchEvent(
+    const ui::TouchEvent& touch_event,
+    scoped_ptr<ui::Event>* rewritten_event) {
+  int flags = touch_event.flags();
+  RewriteLocatedEvent(touch_event, &flags);
+  if (touch_event.flags() == flags)
+    return ui::EVENT_REWRITE_CONTINUE;
+  ui::TouchEvent* rewritten_touch_event = new ui::TouchEvent(touch_event);
+  rewritten_event->reset(rewritten_touch_event);
+  rewritten_touch_event->set_flags(flags);
+#if defined(USE_X11)
+  ui::UpdateX11EventForFlags(rewritten_touch_event);
+#endif
+  return ui::EVENT_REWRITE_REWRITTEN;
+}
 
-  unsigned int native_mask = Mod4Mask | ControlMask | Mod1Mask;
-  if (!skip_mod2)
-    native_mask |= Mod2Mask;
-  if (!skip_mod3)
-    native_mask |= Mod3Mask;
-  *remapped_native_modifiers =
-      (original_native_modifiers & ~native_mask) |
-      *remapped_native_modifiers;
+ui::EventRewriteStatus EventRewriter::RewriteScrollEvent(
+    const ui::ScrollEvent& scroll_event,
+    scoped_ptr<ui::Event>* rewritten_event) {
+  int flags = scroll_event.flags();
+  ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
+  if (sticky_keys_controller_)
+    status = sticky_keys_controller_->RewriteScrollEvent(scroll_event, &flags);
+  if (status == ui::EVENT_REWRITE_CONTINUE)
+    return status;
+  ui::ScrollEvent* rewritten_scroll_event = new ui::ScrollEvent(scroll_event);
+  rewritten_event->reset(rewritten_scroll_event);
+  rewritten_scroll_event->set_flags(flags);
+#if defined(USE_X11)
+  ui::UpdateX11EventForFlags(rewritten_scroll_event);
+#endif
+  return status;
 }
 
-bool EventRewriter::RewriteModifiers(XEvent* event) {
-  DCHECK(event->type == KeyPress || event->type == KeyRelease);
+void EventRewriter::RewriteModifierKeys(const ui::KeyEvent& key_event,
+                                        MutableKeyState* state) {
+  DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
+         key_event.type() == ui::ET_KEY_RELEASED);
+
   // Do nothing if we have just logged in as guest but have not restarted chrome
   // process yet (so we are still on the login screen). In this situations we
   // have no user profile so can not do anything useful.
@@ -441,66 +534,78 @@ bool EventRewriter::RewriteModifiers(XEvent* event) {
   // restart chrome process. In future this is to be changed.
   // TODO(glotov): remove the following condition when we do not restart chrome
   // when user logs in as guest.
-  if (UserManager::Get()->IsLoggedInAsGuest() &&
+  // TODO(kpschoedel): check whether this is still necessary.
+  if (user_manager::UserManager::Get()->IsLoggedInAsGuest() &&
       LoginDisplayHostImpl::default_host())
-    return false;
+    return;
 
   const PrefService* pref_service = GetPrefService();
   if (!pref_service)
-    return false;
-
-  DCHECK_EQ(input_method::kControlKey, kModifierRemappingCtrl->remap_to);
+    return;
 
-  XKeyEvent* xkey = &event->xkey;
-  KeySym keysym = XLookupKeysym(xkey, 0);
-  ui::KeyboardCode original_keycode = ui::KeyboardCodeFromNative(event);
-  ui::KeyboardCode remapped_keycode = original_keycode;
-  KeyCode remapped_native_keycode = xkey->keycode;
+  MutableKeyState incoming = *state;
+  state->flags = ui::EF_NONE;
+  int characteristic_flag = ui::EF_NONE;
 
-  // First, remap |keysym|.
+  // First, remap the key code.
   const ModifierRemapping* remapped_key = NULL;
-  switch (keysym) {
-    // On Chrome OS, XF86XK_Launch6 (F15) with Mod2Mask is sent when Diamond
-    // key is pressed.
-    case XF86XK_Launch6:
+  switch (incoming.key_code) {
+    // On Chrome OS, F15 (XF86XK_Launch6) with NumLock (Mod2Mask) is sent
+    // when Diamond key is pressed.
+    case ui::VKEY_F15:
       // When diamond key is not available, the configuration UI for Diamond
       // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo
       // syncable pref.
       if (HasDiamondKey())
         remapped_key =
             GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service);
-      // Default behavior is Ctrl key.
-      if (!remapped_key)
+      // Default behavior of F15 is Control, even if --has-chromeos-diamond-key
+      // is absent, according to unit test comments.
+      if (!remapped_key) {
+        DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->key_code);
         remapped_key = kModifierRemappingCtrl;
+      }
+      // F15 is not a modifier key, so we need to track its state directly.
+      if (key_event.type() == ui::ET_KEY_PRESSED) {
+        int remapped_flag = remapped_key->flag;
+        if (remapped_key->remap_to == input_method::kCapsLockKey)
+          remapped_flag |= ui::EF_CAPS_LOCK_DOWN;
+        current_diamond_key_modifier_flags_ = remapped_flag;
+      } else {
+        current_diamond_key_modifier_flags_ = ui::EF_NONE;
+      }
       break;
     // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock
-    // is pressed (with one exception: when IsMod3UsedByCurrentInputMethod() is
-    // true, the key generates XK_ISO_Level3_Shift with Mod3Mask, not
-    // XF86XK_Launch7).
-    case XF86XK_Launch7:
+    // is pressed (with one exception: when
+    // IsISOLevel5ShiftUsedByCurrentInputMethod() is true, the key generates
+    // XK_ISO_Level3_Shift with Mod3Mask, not XF86XK_Launch7).
+    case ui::VKEY_F16:
+      characteristic_flag = ui::EF_CAPS_LOCK_DOWN;
       remapped_key =
           GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service);
       break;
-    case XK_Super_L:
-    case XK_Super_R:
-      // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R.
-      if (IsAppleKeyboard())
+    case ui::VKEY_LWIN:
+    case ui::VKEY_RWIN:
+      characteristic_flag = ui::EF_COMMAND_DOWN;
+      // Rewrite Command-L/R key presses on an Apple keyboard to Control.
+      if (IsAppleKeyboard()) {
+        DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->key_code);
         remapped_key = kModifierRemappingCtrl;
-      else
+      } else {
         remapped_key =
             GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service);
+      }
       // Default behavior is Super key, hence don't remap the event if the pref
       // is unavailable.
       break;
-    case XK_Control_L:
-    case XK_Control_R:
+    case ui::VKEY_CONTROL:
+      characteristic_flag = ui::EF_CONTROL_DOWN;
       remapped_key =
           GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service);
       break;
-    case XK_Alt_L:
-    case XK_Alt_R:
-    case XK_Meta_L:
-    case XK_Meta_R:
+    case ui::VKEY_MENU:
+      // ALT key
+      characteristic_flag = ui::EF_ALT_DOWN;
       remapped_key =
           GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service);
       break;
@@ -509,308 +614,186 @@ bool EventRewriter::RewriteModifiers(XEvent* event) {
   }
 
   if (remapped_key) {
-    int flags = ui::EventFlagsFromNative(event);
-    remapped_keycode = remapped_key->keycode;
-    const size_t level = ((flags & ui::EF_SHIFT_DOWN) ? (1 << 1) : 0) +
-        (IsRight(keysym) ? (1 << 0) : 0);
-    const KeySym native_keysym = remapped_key->native_keysyms[level];
-    remapped_native_keycode = NativeKeySymToNativeKeycode(native_keysym);
+    state->key_code = remapped_key->key_code;
+    incoming.flags |= characteristic_flag;
+    characteristic_flag = remapped_key->flag;
   }
 
   // Next, remap modifier bits.
-  unsigned int remapped_native_modifiers = 0U;
-  GetRemappedModifierMasks(xkey->state, &remapped_native_modifiers);
+  state->flags |=
+      GetRemappedModifierMasks(*pref_service, key_event, incoming.flags);
+  if (key_event.type() == ui::ET_KEY_PRESSED)
+    state->flags |= characteristic_flag;
+  else
+    state->flags &= ~characteristic_flag;
 
   // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL, but do nothing if
   // the original key is ui::VKEY_CAPITAL (i.e. a Caps Lock key on an external
   // keyboard is pressed) since X can handle that case.
-  if (event->type == KeyPress &&
-      original_keycode != ui::VKEY_CAPITAL &&
-      remapped_keycode == ui::VKEY_CAPITAL) {
-    input_method::XKeyboard* xkeyboard = xkeyboard_for_testing_ ?
-        xkeyboard_for_testing_ :
-        input_method::InputMethodManager::Get()->GetXKeyboard();
-    xkeyboard->SetCapsLockEnabled(!xkeyboard->CapsLockIsEnabled());
+  if (key_event.type() == ui::ET_KEY_PRESSED &&
+      incoming.key_code != ui::VKEY_CAPITAL &&
+      state->key_code == ui::VKEY_CAPITAL) {
+    chromeos::input_method::ImeKeyboard* ime_keyboard =
+        ime_keyboard_for_testing_
+            ? ime_keyboard_for_testing_
+            : chromeos::input_method::InputMethodManager::Get()
+                  ->GetImeKeyboard();
+    ime_keyboard->SetCapsLockEnabled(!ime_keyboard->CapsLockIsEnabled());
   }
-
-  OverwriteEvent(event, remapped_native_keycode, remapped_native_modifiers);
-  return true;
 }
 
-bool EventRewriter::RewriteNumPadKeys(XEvent* event) {
-  DCHECK(event->type == KeyPress || event->type == KeyRelease);
-  bool rewritten = false;
-  XKeyEvent* xkey = &event->xkey;
-  const KeySym keysym = XLookupKeysym(xkey, 0);
-  switch (keysym) {
-    case XK_KP_Insert:
-      OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_0),
-                     xkey->state | Mod2Mask);
-      rewritten = true;
-      break;
-    case XK_KP_Delete:
-      OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_Decimal),
-                     xkey->state | Mod2Mask);
-      rewritten = true;
-      break;
-    case XK_KP_End:
-      OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_1),
-                     xkey->state | Mod2Mask);
-      rewritten = true;
-      break;
-    case XK_KP_Down:
-      OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_2),
-                     xkey->state | Mod2Mask);
-      rewritten = true;
-      break;
-    case XK_KP_Next:
-      OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_3),
-                     xkey->state | Mod2Mask);
-      rewritten = true;
-      break;
-    case XK_KP_Left:
-      OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_4),
-                     xkey->state | Mod2Mask);
-      rewritten = true;
-      break;
-    case XK_KP_Begin:
-      OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_5),
-                     xkey->state | Mod2Mask);
-      rewritten = true;
-      break;
-    case XK_KP_Right:
-      OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_6),
-                     xkey->state | Mod2Mask);
-      rewritten = true;
-      break;
-    case XK_KP_Home:
-      OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_7),
-                     xkey->state | Mod2Mask);
-      rewritten = true;
-      break;
-    case XK_KP_Up:
-      OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_8),
-                     xkey->state | Mod2Mask);
-      rewritten = true;
-      break;
-    case XK_KP_Prior:
-      OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_9),
-                     xkey->state | Mod2Mask);
-      rewritten = true;
-      break;
-    case XK_KP_Divide:
-    case XK_KP_Multiply:
-    case XK_KP_Subtract:
-    case XK_KP_Add:
-    case XK_KP_Enter:
-      // Add Mod2Mask for consistency.
-      OverwriteEvent(event, xkey->keycode, xkey->state | Mod2Mask);
-      rewritten = true;
-      break;
-    default:
-      break;
-  }
-  return rewritten;
+void EventRewriter::RewriteNumPadKeys(const ui::KeyEvent& key_event,
+                                      MutableKeyState* state) {
+  DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
+         key_event.type() == ui::ET_KEY_RELEASED);
+  if (!(state->flags & ui::EF_NUMPAD_KEY))
+    return;
+  MutableKeyState incoming = *state;
+
+  static const KeyboardRemapping kNumPadRemappings[] = {
+      {ui::VKEY_INSERT, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD0, ui::EF_NUMPAD_KEY},
+      {ui::VKEY_DELETE, ui::EF_NUMPAD_KEY, ui::VKEY_DECIMAL, ui::EF_NUMPAD_KEY},
+      {ui::VKEY_END, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD1, ui::EF_NUMPAD_KEY},
+      {ui::VKEY_DOWN, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD2, ui::EF_NUMPAD_KEY},
+      {ui::VKEY_NEXT, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD3, ui::EF_NUMPAD_KEY},
+      {ui::VKEY_LEFT, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD4, ui::EF_NUMPAD_KEY},
+      {ui::VKEY_CLEAR, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD5, ui::EF_NUMPAD_KEY},
+      {ui::VKEY_RIGHT, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD6, ui::EF_NUMPAD_KEY},
+      {ui::VKEY_HOME, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD7, ui::EF_NUMPAD_KEY},
+      {ui::VKEY_UP, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD8, ui::EF_NUMPAD_KEY},
+      {ui::VKEY_PRIOR, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD9, ui::EF_NUMPAD_KEY},
+  };
+
+  RewriteWithKeyboardRemappingsByKeyCode(
+      kNumPadRemappings, arraysize(kNumPadRemappings), incoming, state);
 }
 
-bool EventRewriter::RewriteExtendedKeys(XEvent* event) {
-  DCHECK(event->type == KeyPress || event->type == KeyRelease);
-  XKeyEvent* xkey = &event->xkey;
-  const KeySym keysym = XLookupKeysym(xkey, 0);
+void EventRewriter::RewriteExtendedKeys(const ui::KeyEvent& key_event,
+                                        MutableKeyState* state) {
+  DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
+         key_event.type() == ui::ET_KEY_RELEASED);
 
-  KeySym remapped_native_keysym = 0;
-  unsigned int remapped_native_mods = 0;
+  MutableKeyState incoming = *state;
   bool rewritten = false;
 
-  if (xkey->state & Mod4Mask) {
+  if ((incoming.flags & (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) ==
+      (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) {
     // Allow Search to avoid rewriting extended keys.
     static const KeyboardRemapping kAvoidRemappings[] = {
-      { // Alt+Backspace
-        XK_BackSpace, Mod1Mask | Mod4Mask,
-        XK_BackSpace, Mod1Mask,
-      },
-      { // Control+Alt+Up
-        XK_Up, Mod1Mask | ControlMask | Mod4Mask,
-        XK_Up, Mod1Mask | ControlMask,
-      },
-      { // Alt+Up
-        XK_Up, Mod1Mask | Mod4Mask,
-        XK_Up, Mod1Mask,
-      },
-      { // Control+Alt+Down
-        XK_Down, Mod1Mask | ControlMask | Mod4Mask,
-        XK_Down, Mod1Mask | ControlMask,
-      },
-      { // Alt+Down
-        XK_Down, Mod1Mask | Mod4Mask,
-        XK_Down, Mod1Mask,
-      }
-    };
+        {  // Alt+Backspace
+         ui::VKEY_BACK, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_BACK,
+         ui::EF_ALT_DOWN,
+        },
+        {  // Control+Alt+Up
+         ui::VKEY_UP,
+         ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN,
+         ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
+        },
+        {  // Alt+Up
+         ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_UP,
+         ui::EF_ALT_DOWN,
+        },
+        {  // Control+Alt+Down
+         ui::VKEY_DOWN,
+         ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN,
+         ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
+        },
+        {  // Alt+Down
+         ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_DOWN,
+         ui::EF_ALT_DOWN,
+        }};
 
-    rewritten = RewriteWithKeyboardRemappingsByKeySym(
-        kAvoidRemappings,
-        arraysize(kAvoidRemappings),
-        keysym,
-        xkey->state,
-        &remapped_native_keysym,
-        &remapped_native_mods);
+    rewritten = RewriteWithKeyboardRemappingsByKeyCode(
+        kAvoidRemappings, arraysize(kAvoidRemappings), incoming, state);
   }
 
-  if (!rewritten) {
+  if (!rewritten && (incoming.flags & ui::EF_COMMAND_DOWN)) {
     static const KeyboardRemapping kSearchRemappings[] = {
-      { // Search+BackSpace -> Delete
-        XK_BackSpace, Mod4Mask,
-        XK_Delete, 0
-      },
-      { // Search+Left -> Home
-        XK_Left, Mod4Mask,
-        XK_Home, 0
-      },
-      { // Search+Up -> Prior (aka PageUp)
-        XK_Up, Mod4Mask,
-        XK_Prior, 0
-      },
-      { // Search+Right -> End
-        XK_Right, Mod4Mask,
-        XK_End, 0
-      },
-      { // Search+Down -> Next (aka PageDown)
-        XK_Down, Mod4Mask,
-        XK_Next, 0
-      },
-      { // Search+Period -> Insert
-        XK_period, Mod4Mask,
-        XK_Insert, 0
-      }
-    };
+        {  // Search+BackSpace -> Delete
+         ui::VKEY_BACK, ui::EF_COMMAND_DOWN, ui::VKEY_DELETE, 0},
+        {  // Search+Left -> Home
+         ui::VKEY_LEFT, ui::EF_COMMAND_DOWN, ui::VKEY_HOME, 0},
+        {  // Search+Up -> Prior (aka PageUp)
+         ui::VKEY_UP, ui::EF_COMMAND_DOWN, ui::VKEY_PRIOR, 0},
+        {  // Search+Right -> End
+         ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN, ui::VKEY_END, 0},
+        {  // Search+Down -> Next (aka PageDown)
+         ui::VKEY_DOWN, ui::EF_COMMAND_DOWN, ui::VKEY_NEXT, 0},
+        {  // Search+Period -> Insert
+         ui::VKEY_OEM_PERIOD, ui::EF_COMMAND_DOWN, ui::VKEY_INSERT, 0}};
 
-    rewritten = RewriteWithKeyboardRemappingsByKeySym(
-        kSearchRemappings,
-        arraysize(kSearchRemappings),
-        keysym,
-        xkey->state,
-        &remapped_native_keysym,
-        &remapped_native_mods);
+    rewritten = RewriteWithKeyboardRemappingsByKeyCode(
+        kSearchRemappings, arraysize(kSearchRemappings), incoming, state);
   }
 
-  if (!rewritten) {
+  if (!rewritten && (incoming.flags & ui::EF_ALT_DOWN)) {
     static const KeyboardRemapping kNonSearchRemappings[] = {
-      { // Alt+BackSpace -> Delete
-        XK_BackSpace, Mod1Mask,
-        XK_Delete, 0
-      },
-      { // Control+Alt+Up -> Home
-        XK_Up, Mod1Mask | ControlMask,
-        XK_Home, 0
-      },
-      { // Alt+Up -> Prior (aka PageUp)
-        XK_Up, Mod1Mask,
-        XK_Prior, 0
-      },
-      { // Control+Alt+Down -> End
-        XK_Down, Mod1Mask | ControlMask,
-        XK_End, 0
-      },
-      { // Alt+Down -> Next (aka PageDown)
-        XK_Down, Mod1Mask,
-        XK_Next, 0
-      }
-    };
+        {  // Alt+BackSpace -> Delete
+         ui::VKEY_BACK, ui::EF_ALT_DOWN, ui::VKEY_DELETE, 0},
+        {  // Control+Alt+Up -> Home
+         ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::VKEY_HOME, 0},
+        {  // Alt+Up -> Prior (aka PageUp)
+         ui::VKEY_UP, ui::EF_ALT_DOWN, ui::VKEY_PRIOR, 0},
+        {  // Control+Alt+Down -> End
+         ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::VKEY_END, 0},
+        {  // Alt+Down -> Next (aka PageDown)
+         ui::VKEY_DOWN, ui::EF_ALT_DOWN, ui::VKEY_NEXT, 0}};
 
-    rewritten = RewriteWithKeyboardRemappingsByKeySym(
-        kNonSearchRemappings,
-        arraysize(kNonSearchRemappings),
-        keysym,
-        xkey->state,
-        &remapped_native_keysym,
-        &remapped_native_mods);
+    rewritten = RewriteWithKeyboardRemappingsByKeyCode(
+        kNonSearchRemappings, arraysize(kNonSearchRemappings), incoming, state);
   }
-
-  if (!rewritten)
-    return false;
-
-  OverwriteEvent(event,
-                 NativeKeySymToNativeKeycode(remapped_native_keysym),
-                 remapped_native_mods);
-  return true;
 }
 
-bool EventRewriter::RewriteFunctionKeys(XEvent* event) {
-  DCHECK(event->type == KeyPress || event->type == KeyRelease);
-  XKeyEvent* xkey = &event->xkey;
-  const KeySym keysym = XLookupKeysym(xkey, 0);
-
-  KeySym remapped_native_keysym = 0;
-  unsigned int remapped_native_mods = 0;
+void EventRewriter::RewriteFunctionKeys(const ui::KeyEvent& key_event,
+                                        MutableKeyState* state) {
+  CHECK(key_event.type() == ui::ET_KEY_PRESSED ||
+        key_event.type() == ui::ET_KEY_RELEASED);
+  MutableKeyState incoming = *state;
   bool rewritten = false;
 
-  // By default the top row (F1-F12) keys are special keys for back, forward,
-  // brightness, volume, etc. However, windows for v2 apps can optionally
-  // request raw function keys for these keys.
-  bool top_row_keys_are_special_keys = !TopRowKeysAreFunctionKeys(event);
-
-  if ((xkey->state & Mod4Mask) && top_row_keys_are_special_keys) {
-    // Allow Search to avoid rewriting F1-F12.
-    static const KeyboardRemapping kFkeysToFkeys[] = {
-      { XK_F1, Mod4Mask, XK_F1, },
-      { XK_F2, Mod4Mask, XK_F2, },
-      { XK_F3, Mod4Mask, XK_F3, },
-      { XK_F4, Mod4Mask, XK_F4, },
-      { XK_F5, Mod4Mask, XK_F5, },
-      { XK_F6, Mod4Mask, XK_F6, },
-      { XK_F7, Mod4Mask, XK_F7, },
-      { XK_F8, Mod4Mask, XK_F8, },
-      { XK_F9, Mod4Mask, XK_F9, },
-      { XK_F10, Mod4Mask, XK_F10, },
-      { XK_F11, Mod4Mask, XK_F11, },
-      { XK_F12, Mod4Mask, XK_F12, },
-    };
-
-    rewritten = RewriteWithKeyboardRemappingsByKeySym(
-        kFkeysToFkeys,
-        arraysize(kFkeysToFkeys),
-        keysym,
-        xkey->state,
-        &remapped_native_keysym,
-        &remapped_native_mods);
-  }
-
-  if (!rewritten) {
-    static const KeyboardRemapping kFkeysToSpecialKeys[] = {
-      { XK_F1, 0, XF86XK_Back, 0 },
-      { XK_F2, 0, XF86XK_Forward, 0 },
-      { XK_F3, 0, XF86XK_Reload, 0 },
-      { XK_F4, 0, XF86XK_LaunchB, 0 },
-      { XK_F5, 0, XF86XK_LaunchA, 0 },
-      { XK_F6, 0, XF86XK_MonBrightnessDown, 0 },
-      { XK_F7, 0, XF86XK_MonBrightnessUp, 0 },
-      { XK_F8, 0, XF86XK_AudioMute, 0 },
-      { XK_F9, 0, XF86XK_AudioLowerVolume, 0 },
-      { XK_F10, 0, XF86XK_AudioRaiseVolume, 0 },
-    };
-
-    if (top_row_keys_are_special_keys) {
-      // Rewrite the F1-F12 keys on a Chromebook keyboard to special keys.
-      rewritten = RewriteWithKeyboardRemappingsByKeySym(
-          kFkeysToSpecialKeys,
-          arraysize(kFkeysToSpecialKeys),
-          keysym,
-          xkey->state,
-          &remapped_native_keysym,
-          &remapped_native_mods);
-    } else if (xkey->state & Mod4Mask) {
-      // Use Search + F1-F12 for the special keys.
-      rewritten = RewriteWithKeyboardRemappingsByKeySym(
-          kFkeysToSpecialKeys,
-          arraysize(kFkeysToSpecialKeys),
-          keysym,
-          xkey->state & !Mod4Mask,
-          &remapped_native_keysym,
-          &remapped_native_mods);
+  if ((incoming.key_code >= ui::VKEY_F1) &&
+      (incoming.key_code <= ui::VKEY_F24)) {
+    // By default the top row (F1-F12) keys are system keys for back, forward,
+    // brightness, volume, etc. However, windows for v2 apps can optionally
+    // request raw function keys for these keys.
+    bool top_row_keys_are_function_keys = TopRowKeysAreFunctionKeys(key_event);
+    bool search_is_pressed = (incoming.flags & ui::EF_COMMAND_DOWN) != 0;
+
+    //  Search? Top Row   Result
+    //  ------- --------  ------
+    //  No      Fn        Unchanged
+    //  No      System    Fn -> System
+    //  Yes     Fn        Fn -> System
+    //  Yes     System    Search+Fn -> Fn
+    if (top_row_keys_are_function_keys == search_is_pressed) {
+      // Rewrite the F1-F12 keys on a Chromebook keyboard to system keys.
+      static const KeyboardRemapping kFkeysToSystemKeys[] = {
+          {ui::VKEY_F1, 0, ui::VKEY_BROWSER_BACK, 0},
+          {ui::VKEY_F2, 0, ui::VKEY_BROWSER_FORWARD, 0},
+          {ui::VKEY_F3, 0, ui::VKEY_BROWSER_REFRESH, 0},
+          {ui::VKEY_F4, 0, ui::VKEY_MEDIA_LAUNCH_APP2, 0},
+          {ui::VKEY_F5, 0, ui::VKEY_MEDIA_LAUNCH_APP1, 0},
+          {ui::VKEY_F6, 0, ui::VKEY_BRIGHTNESS_DOWN, 0},
+          {ui::VKEY_F7, 0, ui::VKEY_BRIGHTNESS_UP, 0},
+          {ui::VKEY_F8, 0, ui::VKEY_VOLUME_MUTE, 0},
+          {ui::VKEY_F9, 0, ui::VKEY_VOLUME_DOWN, 0},
+          {ui::VKEY_F10, 0, ui::VKEY_VOLUME_UP, 0},
+      };
+      MutableKeyState incoming_without_command = incoming;
+      incoming_without_command.flags &= ~ui::EF_COMMAND_DOWN;
+      rewritten =
+          RewriteWithKeyboardRemappingsByKeyCode(kFkeysToSystemKeys,
+                                                 arraysize(kFkeysToSystemKeys),
+                                                 incoming_without_command,
+                                                 state);
+    } else if (search_is_pressed) {
+      // Allow Search to avoid rewriting F1-F12.
+      state->flags &= ~ui::EF_COMMAND_DOWN;
+      rewritten = true;
     }
   }
 
-  if (!rewritten && (xkey->state & Mod4Mask)) {
+  if (!rewritten && (incoming.flags & ui::EF_COMMAND_DOWN)) {
     // Remap Search+<number> to F<number>.
     // We check the keycode here instead of the keysym, as these keys have
     // different keysyms when modifiers are pressed, such as shift.
@@ -819,81 +802,53 @@ bool EventRewriter::RewriteFunctionKeys(XEvent* event) {
     // should make layout-specific choices here. For eg. on a french keyboard
     // "-" and "6" are the same key, so F11 will not be accessible.
     static const KeyboardRemapping kNumberKeysToFkeys[] = {
-      { XK_1, Mod4Mask, XK_F1, 0 },
-      { XK_2, Mod4Mask, XK_F2, 0 },
-      { XK_3, Mod4Mask, XK_F3, 0 },
-      { XK_4, Mod4Mask, XK_F4, 0 },
-      { XK_5, Mod4Mask, XK_F5, 0 },
-      { XK_6, Mod4Mask, XK_F6, 0 },
-      { XK_7, Mod4Mask, XK_F7, 0 },
-      { XK_8, Mod4Mask, XK_F8, 0 },
-      { XK_9, Mod4Mask, XK_F9, 0 },
-      { XK_0, Mod4Mask, XK_F10, 0 },
-      { XK_minus, Mod4Mask, XK_F11, 0 },
-      { XK_equal, Mod4Mask, XK_F12, 0 }
-    };
-
+        {ui::VKEY_1, ui::EF_COMMAND_DOWN, ui::VKEY_F1, 0},
+        {ui::VKEY_2, ui::EF_COMMAND_DOWN, ui::VKEY_F2, 0},
+        {ui::VKEY_3, ui::EF_COMMAND_DOWN, ui::VKEY_F3, 0},
+        {ui::VKEY_4, ui::EF_COMMAND_DOWN, ui::VKEY_F4, 0},
+        {ui::VKEY_5, ui::EF_COMMAND_DOWN, ui::VKEY_F5, 0},
+        {ui::VKEY_6, ui::EF_COMMAND_DOWN, ui::VKEY_F6, 0},
+        {ui::VKEY_7, ui::EF_COMMAND_DOWN, ui::VKEY_F7, 0},
+        {ui::VKEY_8, ui::EF_COMMAND_DOWN, ui::VKEY_F8, 0},
+        {ui::VKEY_9, ui::EF_COMMAND_DOWN, ui::VKEY_F9, 0},
+        {ui::VKEY_0, ui::EF_COMMAND_DOWN, ui::VKEY_F10, 0},
+        {ui::VKEY_OEM_MINUS, ui::EF_COMMAND_DOWN, ui::VKEY_F11, 0},
+        {ui::VKEY_OEM_PLUS, ui::EF_COMMAND_DOWN, ui::VKEY_F12, 0}};
     rewritten = RewriteWithKeyboardRemappingsByKeyCode(
-        kNumberKeysToFkeys,
-        arraysize(kNumberKeysToFkeys),
-        xkey->keycode,
-        xkey->state,
-        &remapped_native_keysym,
-        &remapped_native_mods);
+        kNumberKeysToFkeys, arraysize(kNumberKeysToFkeys), incoming, state);
   }
-
-  if (!rewritten)
-    return false;
-
-  OverwriteEvent(event,
-                 NativeKeySymToNativeKeycode(remapped_native_keysym),
-                 remapped_native_mods);
-  return true;
 }
 
-void EventRewriter::RewriteLocatedEvent(XEvent* event) {
-  DCHECK_EQ(GenericEvent, event->type);
-  XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event->xcookie.data);
-  if (xievent->evtype != XI_ButtonPress && xievent->evtype != XI_ButtonRelease)
+void EventRewriter::RewriteLocatedEvent(const ui::Event& event,
+                                        int* flags) {
+  const PrefService* pref_service = GetPrefService();
+  if (!pref_service)
     return;
-
-  // First, remap modifier masks.
-  unsigned int remapped_native_modifiers = 0U;
-  GetRemappedModifierMasks(xievent->mods.effective, &remapped_native_modifiers);
-  xievent->mods.effective = remapped_native_modifiers;
-
-  // Then, remap Alt+Button1 to Button3.
-  if ((xievent->evtype == XI_ButtonPress ||
-       pressed_device_ids_.count(xievent->sourceid)) &&
-      (xievent->mods.effective & Mod1Mask) && xievent->detail == Button1) {
-    xievent->mods.effective &= ~Mod1Mask;
-    xievent->detail = Button3;
-    if (xievent->evtype == XI_ButtonRelease) {
-      // On the release clear the left button from the existing state and the
-      // mods, and set the right button.
-      XISetMask(xievent->buttons.mask, Button3);
-      XIClearMask(xievent->buttons.mask, Button1);
-      xievent->mods.effective &= ~Button1Mask;
-      pressed_device_ids_.erase(xievent->sourceid);
-    } else {
-      pressed_device_ids_.insert(xievent->sourceid);
-    }
-  }
+  *flags = GetRemappedModifierMasks(*pref_service, event, *flags);
 }
 
-void EventRewriter::OverwriteEvent(XEvent* event,
-                                   unsigned int new_native_keycode,
-                                   unsigned int new_native_state) {
-  DCHECK(event->type == KeyPress || event->type == KeyRelease);
-  XKeyEvent* xkey = &event->xkey;
-  xkey->keycode = new_native_keycode;
-  xkey->state = new_native_state;
+int EventRewriter::RewriteModifierClick(const ui::MouseEvent& mouse_event,
+                                        int* flags) {
+  // Remap Alt+Button1 to Button3.
+  const int kAltLeftButton = (ui::EF_ALT_DOWN | ui::EF_LEFT_MOUSE_BUTTON);
+  if (((*flags & kAltLeftButton) == kAltLeftButton) &&
+      ((mouse_event.type() == ui::ET_MOUSE_PRESSED) ||
+       pressed_device_ids_.count(mouse_event.source_device_id()))) {
+    *flags &= ~kAltLeftButton;
+    *flags |= ui::EF_RIGHT_MOUSE_BUTTON;
+    if (mouse_event.type() == ui::ET_MOUSE_PRESSED)
+      pressed_device_ids_.insert(mouse_event.source_device_id());
+    else
+      pressed_device_ids_.erase(mouse_event.source_device_id());
+    return ui::EF_RIGHT_MOUSE_BUTTON;
+  }
+  return ui::EF_NONE;
 }
 
-EventRewriter::DeviceType EventRewriter::DeviceAddedInternal(
+EventRewriter::DeviceType EventRewriter::KeyboardDeviceAddedInternal(
     int device_id,
     const std::string& device_name) {
-  const DeviceType type = EventRewriter::GetDeviceType(device_name);
+  const DeviceType type = GetDeviceType(device_name);
   if (type == kDeviceAppleKeyboard) {
     VLOG(1) << "Apple keyboard '" << device_name << "' connected: "
             << "id=" << device_id;
@@ -904,4 +859,37 @@ EventRewriter::DeviceType EventRewriter::DeviceAddedInternal(
   return type;
 }
 
+void EventRewriter::KeyboardDeviceAdded(int device_id) {
+#if defined(USE_X11)
+  DCHECK_NE(XIAllDevices, device_id);
+  DCHECK_NE(XIAllMasterDevices, device_id);
+  if (device_id == XIAllDevices || device_id == XIAllMasterDevices) {
+    LOG(ERROR) << "Unexpected device_id passed: " << device_id;
+    return;
+  }
+
+  int ndevices_return = 0;
+  XIDeviceInfo* device_info =
+      XIQueryDevice(gfx::GetXDisplay(), device_id, &ndevices_return);
+
+  // Since |device_id| is neither XIAllDevices nor XIAllMasterDevices,
+  // the number of devices found should be either 0 (not found) or 1.
+  if (!device_info) {
+    LOG(ERROR) << "XIQueryDevice: Device ID " << device_id << " is unknown.";
+    return;
+  }
+
+  DCHECK_EQ(1, ndevices_return);
+  for (int i = 0; i < ndevices_return; ++i) {
+    DCHECK_EQ(device_id, device_info[i].deviceid);  // see the comment above.
+    DCHECK(device_info[i].name);
+    KeyboardDeviceAddedInternal(device_info[i].deviceid, device_info[i].name);
+  }
+
+  XIFreeDeviceInfo(device_info);
+#else
+  KeyboardDeviceAddedInternal(device_id, "keyboard");
+#endif
+}
+
 }  // namespace chromeos