Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / events / event_rewriter.cc
index c251889..2316a4a 100644 (file)
@@ -6,24 +6,26 @@
 
 #include <vector>
 
+#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/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 "components/user_manager/user_manager.h"
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/keycodes/keyboard_code_conversion.h"
-#include "ui/events/platform/platform_event_source.h"
 #include "ui/wm/core/window_util.h"
 
 #if defined(USE_X11)
@@ -33,7 +35,6 @@
 #undef RootWindow
 #undef Status
 
-#include "chrome/browser/chromeos/events/xinput_hierarchy_changed_event_listener.h"
 #include "ui/base/x/x11_util.h"
 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
 #endif
@@ -42,8 +43,6 @@ namespace chromeos {
 
 namespace {
 
-const int kBadDeviceId = -1;
-
 // 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
@@ -68,7 +67,7 @@ const struct ModifierRemapping {
       {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_CAPS_LOCK_DOWN, ui::VKEY_CAPITAL,
+      {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},
@@ -106,6 +105,28 @@ bool IsISOLevel5ShiftUsedByCurrentInputMethod() {
   return manager->IsISOLevel5ShiftUsedByCurrentInputMethod();
 }
 
+bool IsExtensionCommandRegistered(ui::KeyboardCode key_code, int flags) {
+  // 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;
+
+  int modifiers = flags & (ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN |
+                           ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
+  ui::Accelerator accelerator(key_code, modifiers);
+  return extensions::ExtensionCommandsGlobalRegistry::Get(profile)
+      ->IsRegistered(accelerator);
+}
+
 EventRewriter::DeviceType GetDeviceType(const std::string& device_name) {
   std::vector<std::string> tokens;
   Tokenize(device_name, " .", &tokens);
@@ -126,153 +147,143 @@ EventRewriter::DeviceType GetDeviceType(const std::string& device_name) {
   return EventRewriter::kDeviceUnknown;
 }
 
-#if defined(USE_X11)
-void UpdateX11EventMask(int ui_flags, unsigned int* x_flags) {
-  static struct {
-    int ui;
-    int x;
-  } flags[] = {
-    {ui::EF_CONTROL_DOWN, ControlMask},
-    {ui::EF_SHIFT_DOWN, ShiftMask},
-    {ui::EF_ALT_DOWN, Mod1Mask},
-    {ui::EF_CAPS_LOCK_DOWN, LockMask},
-    {ui::EF_ALTGR_DOWN, Mod5Mask},
-    {ui::EF_MOD3_DOWN, Mod3Mask},
-    {ui::EF_NUMPAD_KEY, Mod2Mask},
-    {ui::EF_LEFT_MOUSE_BUTTON, Button1Mask},
-    {ui::EF_MIDDLE_MOUSE_BUTTON, Button2Mask},
-    {ui::EF_RIGHT_MOUSE_BUTTON, Button3Mask},
-  };
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(flags); ++i) {
-    if (ui_flags & flags[i].ui)
-      *x_flags |= flags[i].x;
-    else
-      *x_flags &= ~flags[i].x;
-  }
-}
-#endif
-
 }  // namespace
 
-EventRewriter::EventRewriter()
-    : last_device_id_(kBadDeviceId),
+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) {
-#if defined(USE_X11)
-  ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this);
-  if (base::SysInfo::IsRunningOnChromeOS()) {
-    XInputHierarchyChangedEventListener::GetInstance()->AddObserver(this);
-  }
-#endif
+      pref_service_for_testing_(NULL),
+      sticky_keys_controller_(sticky_keys_controller),
+      current_diamond_key_modifier_flags_(ui::EF_NONE) {
 }
 
 EventRewriter::~EventRewriter() {
-#if defined(USE_X11)
-  ui::PlatformEventSource::GetInstance()->RemovePlatformEventObserver(this);
-  if (base::SysInfo::IsRunningOnChromeOS()) {
-    XInputHierarchyChangedEventListener::GetInstance()->RemoveObserver(this);
-  }
-#endif
 }
 
-EventRewriter::DeviceType EventRewriter::DeviceAddedForTesting(
+EventRewriter::DeviceType EventRewriter::KeyboardDeviceAddedForTesting(
     int device_id,
     const std::string& device_name) {
-  return DeviceAddedInternal(device_id, device_name);
+  // Tests must avoid XI2 reserved device IDs.
+  DCHECK((device_id < 0) || (device_id > 1));
+  return KeyboardDeviceAddedInternal(device_id, device_name);
 }
 
-void EventRewriter::RewriteLocatedEventForTesting(const ui::Event& event,
-                                                  int* flags) {
-  MutableKeyState state = {*flags, ui::VKEY_UNKNOWN};
-  RewriteLocatedEvent(event, &state);
-  *flags = state.flags;
+void EventRewriter::RewriteMouseButtonEventForTesting(
+    const ui::MouseEvent& event,
+    scoped_ptr<ui::Event>* rewritten_event) {
+  RewriteMouseButtonEvent(event, rewritten_event);
 }
 
 ui::EventRewriteStatus EventRewriter::RewriteEvent(
     const ui::Event& event,
     scoped_ptr<ui::Event>* rewritten_event) {
-#if defined(USE_X11)
-  // Do not rewrite an event sent by ui_controls::SendKeyPress(). See
-  // crbug.com/136465.
-  XEvent* xev = event.native_event();
-  if (xev && xev->xany.send_event)
-    return ui::EVENT_REWRITE_CONTINUE;
-#endif
-  switch (event.type()) {
-    case ui::ET_KEY_PRESSED:
-    case ui::ET_KEY_RELEASED: {
-      const ui::KeyEvent& key_event = static_cast<const ui::KeyEvent&>(event);
-      MutableKeyState state = {key_event.flags(), key_event.key_code()};
-      RewriteModifierKeys(key_event, &state);
-      RewriteNumPadKeys(key_event, &state);
-      RewriteExtendedKeys(key_event, &state);
-      RewriteFunctionKeys(key_event, &state);
-      if ((key_event.flags() != state.flags) ||
-          (key_event.key_code() != state.key_code)) {
-        ui::KeyEvent* rewritten_key_event = new ui::KeyEvent(key_event);
-        rewritten_event->reset(rewritten_key_event);
-        rewritten_key_event->set_flags(state.flags);
-        rewritten_key_event->set_key_code(state.key_code);
-        rewritten_key_event->set_character(
-            ui::GetCharacterFromKeyCode(state.key_code, state.flags));
-        rewritten_key_event->NormalizeFlags();
-#if defined(USE_X11)
-        xev = rewritten_key_event->native_event();
-        if (xev) {
-          XKeyEvent* xkey = &(xev->xkey);
-          UpdateX11EventMask(state.flags, &xkey->state);
-          xkey->keycode = XKeysymToKeycode(
-              gfx::GetXDisplay(),
-              ui::XKeysymForWindowsKeyCode(state.key_code,
-                                           state.flags & ui::EF_SHIFT_DOWN));
-        }
-#endif
-        return ui::EVENT_REWRITE_REWRITTEN;
-      }
-      return ui::EVENT_REWRITE_CONTINUE;
-    }
-    case ui::ET_MOUSE_PRESSED:
-    case ui::ET_MOUSE_RELEASED:
-    case ui::ET_TOUCH_PRESSED:
-    case ui::ET_TOUCH_RELEASED: {
-      MutableKeyState state = {event.flags(), ui::VKEY_UNKNOWN};
-      RewriteLocatedEvent(event, &state);
-      if (event.flags() != state.flags) {
-        if (event.IsMouseEvent()) {
-          rewritten_event->reset(
-              new ui::MouseEvent(static_cast<const ui::MouseEvent&>(event)));
-        } else {
-          rewritten_event->reset(
-              new ui::TouchEvent(static_cast<const ui::TouchEvent&>(event)));
-        }
-        rewritten_event->get()->set_flags(state.flags);
-        return ui::EVENT_REWRITE_REWRITTEN;
-      }
-      return ui::EVENT_REWRITE_CONTINUE;
-    }
-    default:
-      return ui::EVENT_REWRITE_CONTINUE;
+  if ((event.type() == ui::ET_KEY_PRESSED) ||
+      (event.type() == ui::ET_KEY_RELEASED)) {
+    return RewriteKeyEvent(static_cast<const ui::KeyEvent&>(event),
+                           rewritten_event);
   }
-  NOTREACHED();
+  if ((event.type() == ui::ET_MOUSE_PRESSED) ||
+      (event.type() == ui::ET_MOUSE_RELEASED)) {
+    return RewriteMouseButtonEvent(static_cast<const ui::MouseEvent&>(event),
+                                   rewritten_event);
+  }
+  if (event.type() == ui::ET_MOUSEWHEEL) {
+    return RewriteMouseWheelEvent(
+        static_cast<const ui::MouseWheelEvent&>(event), rewritten_event);
+  }
+  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;
 }
 
 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::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)
-void EventRewriter::DeviceKeyPressedOrReleased(int device_id) {
-  if (!device_id_to_type_.count(device_id)) {
-    // |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);
+  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));
+    }
   }
-  last_device_id_ = device_id;
-}
 #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);
+}
+
+void EventRewriter::DeviceKeyPressedOrReleased(int device_id) {
+  if (!device_id_to_type_.count(device_id))
+    KeyboardDeviceAdded(device_id);
+  last_keyboard_device_id_ = device_id;
+}
 
 const PrefService* EventRewriter::GetPrefService() const {
   if (pref_service_for_testing_)
@@ -282,14 +293,14 @@ const PrefService* EventRewriter::GetPrefService() const {
 }
 
 bool EventRewriter::IsAppleKeyboard() const {
-  if (last_device_id_ == kBadDeviceId)
+  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_device_id_);
+      device_id_to_type_.find(last_keyboard_device_id_);
   if (iter == device_id_to_type_.end()) {
-    LOG(ERROR) << "Device ID " << last_device_id_ << " is unknown.";
+    LOG(ERROR) << "Device ID " << last_keyboard_device_id_ << " is unknown.";
     return false;
   }
 
@@ -311,10 +322,10 @@ int EventRewriter::GetRemappedModifierMasks(const PrefService& pref_service,
                                             const ui::Event& event,
                                             int original_flags) const {
   int unmodified_flags = original_flags;
-  int rewritten_flags = 0;
+  int rewritten_flags = current_diamond_key_modifier_flags_;
   for (size_t i = 0; unmodified_flags && (i < arraysize(kModifierRemappings));
        ++i) {
-    const ModifierRemapping* remapped_key = 0;
+    const ModifierRemapping* remapped_key = NULL;
     if (!(unmodified_flags & kModifierRemappings[i].flag))
       continue;
     switch (kModifierRemappings[i].flag) {
@@ -325,11 +336,16 @@ int EventRewriter::GetRemappedModifierMasks(const PrefService& pref_service,
           remapped_key = kModifierRemappingCtrl;
         }
         break;
-      case ui::EF_CAPS_LOCK_DOWN:
-        // If CapsLock is used by the current input method, don't allow the
-        // CapsLock pref to remap it, or the keyboard behavior will be broken.
+      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;
@@ -364,6 +380,155 @@ bool EventRewriter::RewriteWithKeyboardRemappingsByKeyCode(
   return false;
 }
 
+ui::EventRewriteStatus EventRewriter::RewriteKeyEvent(
+    const ui::KeyEvent& key_event,
+    scoped_ptr<ui::Event>* rewritten_event) {
+  if (IsExtensionCommandRegistered(key_event.key_code(), key_event.flags()))
+    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 (!(key_event.flags() & ui::EF_FINAL)) {
+    RewriteModifierKeys(key_event, &state);
+    RewriteNumPadKeys(key_event, &state);
+  }
+  ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE;
+  bool is_sticky_key_extension_command = false;
+  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;
+    is_sticky_key_extension_command =
+        IsExtensionCommandRegistered(state.key_code, state.flags);
+  }
+
+  // If sticky key rewrites the event, and it matches an extension command, do
+  // not further rewrite the event since it won't match the extension command
+  // thereafter.
+  if (!is_sticky_key_extension_command && !(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;
+}
+
+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);
+  }
+  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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
 void EventRewriter::RewriteModifierKeys(const ui::KeyEvent& key_event,
                                         MutableKeyState* state) {
   DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
@@ -377,7 +542,7 @@ void EventRewriter::RewriteModifierKeys(const ui::KeyEvent& key_event,
   // TODO(glotov): remove the following condition when we do not restart chrome
   // when user logs in as guest.
   // TODO(kpschoedel): check whether this is still necessary.
-  if (UserManager::Get()->IsLoggedInAsGuest() &&
+  if (user_manager::UserManager::Get()->IsLoggedInAsGuest() &&
       LoginDisplayHostImpl::default_host())
     return;
 
@@ -401,11 +566,20 @@ void EventRewriter::RewriteModifierKeys(const ui::KeyEvent& key_event,
       if (HasDiamondKey())
         remapped_key =
             GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service);
-      // Default behavior is Ctrl 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;
-        characteristic_flag = ui::EF_CONTROL_DOWN;
+      }
+      // 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
@@ -653,46 +827,32 @@ void EventRewriter::RewriteFunctionKeys(const ui::KeyEvent& key_event,
 }
 
 void EventRewriter::RewriteLocatedEvent(const ui::Event& event,
-                                        MutableKeyState* state) {
+                                        int* flags) {
   const PrefService* pref_service = GetPrefService();
   if (!pref_service)
     return;
+  *flags = GetRemappedModifierMasks(*pref_service, event, *flags);
+}
 
-  // First, remap modifier masks.
-  state->flags = GetRemappedModifierMasks(*pref_service, event, state->flags);
-
-#if defined(USE_X11)
-  // TODO(kpschoedel): de-X11 with unified device ids from crbug.com/360377
-  XEvent* xevent = event.native_event();
-  if (!xevent || xevent->type != GenericEvent)
-    return;
-  XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data);
-  if (xievent->evtype != XI_ButtonPress && xievent->evtype != XI_ButtonRelease)
-    return;
-
-  // Then, remap Alt+Button1 to Button3.
-  if ((xievent->evtype == XI_ButtonPress ||
-       pressed_device_ids_.count(xievent->sourceid)) &&
-      (xievent->mods.effective & Mod1Mask) && xievent->detail == Button1) {
-    state->flags &= ~(ui::EF_ALT_DOWN | ui::EF_LEFT_MOUSE_BUTTON);
-    state->flags |= ui::EF_RIGHT_MOUSE_BUTTON;
-    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);
-    }
+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;
   }
-#endif  // defined(USE_X11)
+  return ui::EF_NONE;
 }
 
-EventRewriter::DeviceType EventRewriter::DeviceAddedInternal(
+EventRewriter::DeviceType EventRewriter::KeyboardDeviceAddedInternal(
     int device_id,
     const std::string& device_name) {
   const DeviceType type = GetDeviceType(device_name);
@@ -706,25 +866,8 @@ EventRewriter::DeviceType EventRewriter::DeviceAddedInternal(
   return type;
 }
 
+void EventRewriter::KeyboardDeviceAdded(int device_id) {
 #if defined(USE_X11)
-void EventRewriter::WillProcessEvent(const ui::PlatformEvent& event) {
-  XEvent* xevent = event;
-  if (xevent->type == GenericEvent) {
-    XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data);
-    if (xievent->evtype == XI_KeyPress || xievent->evtype == XI_KeyRelease) {
-      if (xievent->deviceid == xievent->sourceid)
-        DeviceKeyPressedOrReleased(xievent->deviceid);
-    }
-  }
-}
-
-void EventRewriter::DidProcessEvent(const ui::PlatformEvent& event) {
-}
-
-void EventRewriter::DeviceHierarchyChanged() {
-}
-
-void EventRewriter::DeviceAdded(int device_id) {
   DCHECK_NE(XIAllDevices, device_id);
   DCHECK_NE(XIAllMasterDevices, device_id);
   if (device_id == XIAllDevices || device_id == XIAllMasterDevices) {
@@ -747,15 +890,13 @@ void EventRewriter::DeviceAdded(int device_id) {
   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);
+    KeyboardDeviceAddedInternal(device_info[i].deviceid, device_info[i].name);
   }
 
   XIFreeDeviceInfo(device_info);
-}
-
-void EventRewriter::DeviceRemoved(int device_id) {
-  device_id_to_type_.erase(device_id);
-}
+#else
+  KeyboardDeviceAddedInternal(device_id, "keyboard");
 #endif
+}
 
 }  // namespace chromeos