diff options
-rw-r--r-- | ash/shell.cc | 11 | ||||
-rw-r--r-- | ash/sticky_keys/sticky_keys_controller.cc | 515 | ||||
-rw-r--r-- | ash/sticky_keys/sticky_keys_controller.h | 168 | ||||
-rw-r--r-- | ash/sticky_keys/sticky_keys_overlay_unittest.cc | 200 | ||||
-rw-r--r-- | ash/sticky_keys/sticky_keys_unittest.cc | 815 | ||||
-rw-r--r-- | chrome/browser/chromeos/chrome_browser_main_chromeos.cc | 13 | ||||
-rw-r--r-- | chrome/browser/chromeos/events/event_rewriter.cc | 138 | ||||
-rw-r--r-- | chrome/browser/chromeos/events/event_rewriter.h | 18 | ||||
-rw-r--r-- | chrome/browser/chromeos/events/event_rewriter_unittest.cc | 486 |
9 files changed, 1125 insertions, 1239 deletions
diff --git a/ash/shell.cc b/ash/shell.cc index c8b3279..2c39306 100644 --- a/ash/shell.cc +++ b/ash/shell.cc @@ -889,13 +889,6 @@ void Shell::Init(const ShellInitParams& init_params) { // The order in which event filters are added is significant. -#if defined(OS_CHROMEOS) - // The StickyKeysController also rewrites events and must be added - // before observers, but after the EventRewriterEventFilter. - sticky_keys_controller_.reset(new StickyKeysController); - AddPreTargetHandler(sticky_keys_controller_.get()); -#endif - // wm::UserActivityDetector passes events to observers, so let them get // rewritten first. user_activity_detector_.reset(new ::wm::UserActivityDetector); @@ -930,6 +923,10 @@ void Shell::Init(const ShellInitParams& init_params) { keyboard::InitializeKeyboard(); #endif +#if defined(OS_CHROMEOS) + sticky_keys_controller_.reset(new StickyKeysController); +#endif + lock_state_controller_.reset(new LockStateController); power_button_controller_.reset(new PowerButtonController( lock_state_controller_.get())); diff --git a/ash/sticky_keys/sticky_keys_controller.cc b/ash/sticky_keys/sticky_keys_controller.cc index 53be1fc..6237004 100644 --- a/ash/sticky_keys/sticky_keys_controller.cc +++ b/ash/sticky_keys/sticky_keys_controller.cc @@ -4,12 +4,6 @@ #include "ash/sticky_keys/sticky_keys_controller.h" -#if defined(USE_X11) -#include <X11/extensions/XInput2.h> -#include <X11/Xlib.h> -#undef RootWindow -#endif - #include "ash/sticky_keys/sticky_keys_overlay.h" #include "base/basictypes.h" #include "base/debug/stack_trace.h" @@ -25,79 +19,26 @@ namespace ash { namespace { // Returns true if the type of mouse event should be modified by sticky keys. -bool ShouldModifyMouseEvent(ui::MouseEvent* event) { - ui::EventType type = event->type(); +bool ShouldModifyMouseEvent(const ui::MouseEvent& event) { + ui::EventType type = event.type(); return type == ui::ET_MOUSE_PRESSED || type == ui::ET_MOUSE_RELEASED || type == ui::ET_MOUSEWHEEL; } -// An implementation of StickyKeysHandler::StickyKeysHandlerDelegate. -class StickyKeysHandlerDelegateImpl : - public StickyKeysHandler::StickyKeysHandlerDelegate { - public: - StickyKeysHandlerDelegateImpl(); - virtual ~StickyKeysHandlerDelegateImpl(); - - // StickyKeysHandlerDelegate overrides. - virtual void DispatchKeyEvent(ui::KeyEvent* event, - aura::Window* target) OVERRIDE; - - virtual void DispatchMouseEvent(ui::MouseEvent* event, - aura::Window* target) OVERRIDE; - - virtual void DispatchScrollEvent(ui::ScrollEvent* event, - aura::Window* target) OVERRIDE; - private: - void DispatchEvent(ui::Event* event, aura::Window* target); - - DISALLOW_COPY_AND_ASSIGN(StickyKeysHandlerDelegateImpl); -}; - -StickyKeysHandlerDelegateImpl::StickyKeysHandlerDelegateImpl() { -} - -StickyKeysHandlerDelegateImpl::~StickyKeysHandlerDelegateImpl() { -} - -void StickyKeysHandlerDelegateImpl::DispatchKeyEvent(ui::KeyEvent* event, - aura::Window* target) { - DispatchEvent(event, target); -} - -void StickyKeysHandlerDelegateImpl::DispatchMouseEvent(ui::MouseEvent* event, - aura::Window* target) { - DCHECK(target); - // We need to send a new, untransformed mouse event to the host. - if (event->IsMouseWheelEvent()) { - aura::Window* source = static_cast<aura::Window*>(event->target()); - ui::MouseWheelEvent new_event(*static_cast<ui::MouseWheelEvent*>(event), - source, - source->GetRootWindow()); - // Transform the location back to host coordinates before dispatching. - new_event.UpdateForRootTransform(source->GetHost()->GetRootTransform()); - DispatchEvent(&new_event, target); - } else { - aura::Window* source = static_cast<aura::Window*>(event->target()); - ui::MouseEvent new_event(*event, source, source->GetRootWindow()); - // Transform the location back to host coordinates before dispatching. - new_event.UpdateForRootTransform(source->GetHost()->GetRootTransform()); - DispatchEvent(&new_event, target); - } -} - -void StickyKeysHandlerDelegateImpl::DispatchScrollEvent( - ui::ScrollEvent* event, - aura::Window* target) { - DispatchEvent(event, target); -} - -void StickyKeysHandlerDelegateImpl::DispatchEvent(ui::Event* event, - aura::Window* target) { - DCHECK(target); - ui::EventDispatchDetails details = - target->GetHost()->event_processor()->OnEventFromSource(event); - if (details.dispatcher_destroyed) - return; +// Handle the common tail of event rewriting. +ui::EventRewriteStatus RewriteUpdate(bool consumed, + bool released, + int mod_down_flags, + int* flags) { + int changed_down_flags = mod_down_flags & ~*flags; + *flags |= mod_down_flags; + if (consumed) + return ui::EVENT_REWRITE_DISCARD; + if (released) + return ui::EVENT_REWRITE_DISPATCH_ANOTHER; + if (changed_down_flags) + return ui::EVENT_REWRITE_REWRITTEN; + return ui::EVENT_REWRITE_CONTINUE; } } // namespace @@ -120,21 +61,11 @@ void StickyKeysController::Enable(bool enabled) { // Reset key handlers when activating sticky keys to ensure all // the handlers' states are reset. if (enabled_) { - shift_sticky_key_.reset( - new StickyKeysHandler(ui::EF_SHIFT_DOWN, - new StickyKeysHandlerDelegateImpl())); - alt_sticky_key_.reset( - new StickyKeysHandler(ui::EF_ALT_DOWN, - new StickyKeysHandlerDelegateImpl())); - altgr_sticky_key_.reset( - new StickyKeysHandler(ui::EF_ALTGR_DOWN, - new StickyKeysHandlerDelegateImpl())); - ctrl_sticky_key_.reset( - new StickyKeysHandler(ui::EF_CONTROL_DOWN, - new StickyKeysHandlerDelegateImpl())); - mod3_sticky_key_.reset( - new StickyKeysHandler(ui::EF_MOD3_DOWN, - new StickyKeysHandlerDelegateImpl())); + shift_sticky_key_.reset(new StickyKeysHandler(ui::EF_SHIFT_DOWN)); + alt_sticky_key_.reset(new StickyKeysHandler(ui::EF_ALT_DOWN)); + altgr_sticky_key_.reset(new StickyKeysHandler(ui::EF_ALTGR_DOWN)); + ctrl_sticky_key_.reset(new StickyKeysHandler(ui::EF_CONTROL_DOWN)); + mod3_sticky_key_.reset(new StickyKeysHandler(ui::EF_MOD3_DOWN)); overlay_.reset(new StickyKeysOverlay()); overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_); @@ -155,56 +86,103 @@ void StickyKeysController::SetModifiersEnabled(bool mod3_enabled, } } -bool StickyKeysController::HandleKeyEvent(ui::KeyEvent* event) { - return shift_sticky_key_->HandleKeyEvent(event) || - alt_sticky_key_->HandleKeyEvent(event) || - altgr_sticky_key_->HandleKeyEvent(event) || - ctrl_sticky_key_->HandleKeyEvent(event) || - mod3_sticky_key_->HandleKeyEvent(event); -} - -bool StickyKeysController::HandleMouseEvent(ui::MouseEvent* event) { - return shift_sticky_key_->HandleMouseEvent(event) || - alt_sticky_key_->HandleMouseEvent(event) || - altgr_sticky_key_->HandleMouseEvent(event) || - ctrl_sticky_key_->HandleMouseEvent(event) || - mod3_sticky_key_->HandleMouseEvent(event); -} - -bool StickyKeysController::HandleScrollEvent(ui::ScrollEvent* event) { - return shift_sticky_key_->HandleScrollEvent(event) || - alt_sticky_key_->HandleScrollEvent(event) || - altgr_sticky_key_->HandleScrollEvent(event) || - ctrl_sticky_key_->HandleScrollEvent(event) || - mod3_sticky_key_->HandleScrollEvent(event); -} - -void StickyKeysController::OnKeyEvent(ui::KeyEvent* event) { - // Do not consume a translated key event which is generated by an IME. - if (event->IsTranslated()) - return; - - if (enabled_) { - if (HandleKeyEvent(event)) - event->StopPropagation(); - UpdateOverlay(); - } -} - -void StickyKeysController::OnMouseEvent(ui::MouseEvent* event) { - if (enabled_) { - if (HandleMouseEvent(event)) - event->StopPropagation(); - UpdateOverlay(); - } -} - -void StickyKeysController::OnScrollEvent(ui::ScrollEvent* event) { - if (enabled_) { - if (HandleScrollEvent(event)) - event->StopPropagation(); - UpdateOverlay(); - } +bool StickyKeysController::HandleKeyEvent(const ui::KeyEvent& event, + ui::KeyboardCode key_code, + int* mod_down_flags, + bool* released) { + return shift_sticky_key_->HandleKeyEvent( + event, key_code, mod_down_flags, released) || + alt_sticky_key_->HandleKeyEvent( + event, key_code, mod_down_flags, released) || + altgr_sticky_key_->HandleKeyEvent( + event, key_code, mod_down_flags, released) || + ctrl_sticky_key_->HandleKeyEvent( + event, key_code, mod_down_flags, released) || + mod3_sticky_key_->HandleKeyEvent( + event, key_code, mod_down_flags, released); +} + +bool StickyKeysController::HandleMouseEvent(const ui::MouseEvent& event, + int* mod_down_flags, + bool* released) { + return shift_sticky_key_->HandleMouseEvent( + event, mod_down_flags, released) || + alt_sticky_key_->HandleMouseEvent( + event, mod_down_flags, released) || + altgr_sticky_key_->HandleMouseEvent( + event, mod_down_flags, released) || + ctrl_sticky_key_->HandleMouseEvent( + event, mod_down_flags, released) || + mod3_sticky_key_->HandleMouseEvent( + event, mod_down_flags, released); +} + +bool StickyKeysController::HandleScrollEvent(const ui::ScrollEvent& event, + int* mod_down_flags, + bool* released) { + return shift_sticky_key_->HandleScrollEvent( + event, mod_down_flags, released) || + alt_sticky_key_->HandleScrollEvent( + event, mod_down_flags, released) || + altgr_sticky_key_->HandleScrollEvent( + event, mod_down_flags, released) || + ctrl_sticky_key_->HandleScrollEvent( + event, mod_down_flags, released) || + mod3_sticky_key_->HandleScrollEvent( + event, mod_down_flags, released); +} + +ui::EventRewriteStatus StickyKeysController::RewriteKeyEvent( + const ui::KeyEvent& event, + ui::KeyboardCode key_code, + int* flags) { + if (!enabled_) + return ui::EVENT_REWRITE_CONTINUE; + int mod_down_flags = 0; + bool released = false; + bool consumed = HandleKeyEvent(event, key_code, &mod_down_flags, &released); + UpdateOverlay(); + return RewriteUpdate(consumed, released, mod_down_flags, flags); +} + +ui::EventRewriteStatus StickyKeysController::RewriteMouseEvent( + const ui::MouseEvent& event, + int* flags) { + if (!enabled_) + return ui::EVENT_REWRITE_CONTINUE; + int mod_down_flags = 0; + bool released = false; + bool consumed = HandleMouseEvent(event, &mod_down_flags, &released); + UpdateOverlay(); + return RewriteUpdate(consumed, released, mod_down_flags, flags); +} + +ui::EventRewriteStatus StickyKeysController::RewriteScrollEvent( + const ui::ScrollEvent& event, + int* flags) { + if (!enabled_) + return ui::EVENT_REWRITE_CONTINUE; + int mod_down_flags = 0; + bool released = false; + bool consumed = HandleScrollEvent(event, &mod_down_flags, &released); + UpdateOverlay(); + return RewriteUpdate(consumed, released, mod_down_flags, flags); +} + +ui::EventRewriteStatus StickyKeysController::NextDispatchEvent( + scoped_ptr<ui::Event>* new_event) { + DCHECK(new_event); + new_event->reset(); + int remaining = shift_sticky_key_->GetModifierUpEvent(new_event) + + alt_sticky_key_->GetModifierUpEvent(new_event) + + altgr_sticky_key_->GetModifierUpEvent(new_event) + + ctrl_sticky_key_->GetModifierUpEvent(new_event) + + mod3_sticky_key_->GetModifierUpEvent(new_event); + if (!new_event) + return ui::EVENT_REWRITE_CONTINUE; + if (remaining) + return ui::EVENT_REWRITE_DISPATCH_ANOTHER; + return ui::EVENT_REWRITE_REWRITTEN; } void StickyKeysController::UpdateOverlay() { @@ -235,66 +213,64 @@ StickyKeysOverlay* StickyKeysController::GetOverlayForTest() { /////////////////////////////////////////////////////////////////////////////// // StickyKeysHandler -StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag, - StickyKeysHandlerDelegate* delegate) +StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag) : modifier_flag_(modifier_flag), current_state_(STICKY_KEY_STATE_DISABLED), - event_from_myself_(false), preparing_to_enable_(false), - scroll_delta_(0), - delegate_(delegate) { + scroll_delta_(0) { } StickyKeysHandler::~StickyKeysHandler() { } -StickyKeysHandler::StickyKeysHandlerDelegate::StickyKeysHandlerDelegate() { -} - -StickyKeysHandler::StickyKeysHandlerDelegate::~StickyKeysHandlerDelegate() { -} - -bool StickyKeysHandler::HandleKeyEvent(ui::KeyEvent* event) { - if (event_from_myself_) - return false; // Do not handle self-generated key event. +bool StickyKeysHandler::HandleKeyEvent(const ui::KeyEvent& event, + ui::KeyboardCode key_code, + int* mod_down_flags, + bool* released) { switch (current_state_) { case STICKY_KEY_STATE_DISABLED: - return HandleDisabledState(event); + return HandleDisabledState(event, key_code); case STICKY_KEY_STATE_ENABLED: - return HandleEnabledState(event); + return HandleEnabledState(event, key_code, mod_down_flags, released); case STICKY_KEY_STATE_LOCKED: - return HandleLockedState(event); + return HandleLockedState(event, key_code, mod_down_flags, released); } NOTREACHED(); return false; } -bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent* event) { +bool StickyKeysHandler::HandleMouseEvent( + const ui::MouseEvent& event, + int* mod_down_flags, + bool* released) { if (ShouldModifyMouseEvent(event)) preparing_to_enable_ = false; - if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED - || !ShouldModifyMouseEvent(event)) { + if (current_state_ == STICKY_KEY_STATE_DISABLED || + !ShouldModifyMouseEvent(event)) { return false; } DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED || current_state_ == STICKY_KEY_STATE_LOCKED); - AppendModifier(event); + *mod_down_flags |= modifier_flag_; // Only disable on the mouse released event in normal, non-locked mode. if (current_state_ == STICKY_KEY_STATE_ENABLED && - event->type() != ui::ET_MOUSE_PRESSED) { + event.type() != ui::ET_MOUSE_PRESSED) { current_state_ = STICKY_KEY_STATE_DISABLED; - DispatchEventAndReleaseModifier(event); - return true; + *released = true; + return false; } return false; } -bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) { +bool StickyKeysHandler::HandleScrollEvent( + const ui::ScrollEvent& event, + int* mod_down_flags, + bool* released) { preparing_to_enable_ = false; - if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED) + if (current_state_ == STICKY_KEY_STATE_DISABLED) return false; DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED || current_state_ == STICKY_KEY_STATE_LOCKED); @@ -303,70 +279,82 @@ bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) { // and the offset of the current scroll event has the opposing sign. bool direction_changed = false; if (current_state_ == STICKY_KEY_STATE_ENABLED && - event->type() == ui::ET_SCROLL) { - int offset = event->y_offset(); + event.type() == ui::ET_SCROLL) { + int offset = event.y_offset(); if (scroll_delta_) direction_changed = offset * scroll_delta_ <= 0; scroll_delta_ = offset; } if (!direction_changed) - AppendModifier(event); + *mod_down_flags |= modifier_flag_; // We want to modify all the scroll events in the scroll sequence, which ends // with a fling start event. We also stop when the scroll sequence changes // direction. if (current_state_ == STICKY_KEY_STATE_ENABLED && - (event->type() == ui::ET_SCROLL_FLING_START || direction_changed)) { + (event.type() == ui::ET_SCROLL_FLING_START || direction_changed)) { current_state_ = STICKY_KEY_STATE_DISABLED; scroll_delta_ = 0; - DispatchEventAndReleaseModifier(event); - return true; + *released = true; + return false; } return false; } -StickyKeysHandler::KeyEventType - StickyKeysHandler::TranslateKeyEvent(ui::KeyEvent* event) { +int StickyKeysHandler::GetModifierUpEvent(scoped_ptr<ui::Event>* new_event) { + if (current_state_ != STICKY_KEY_STATE_DISABLED || !modifier_up_event_) + return 0; + DCHECK(new_event); + if (*new_event) + return 1; + new_event->reset(modifier_up_event_.release()); + return 0; +} + +StickyKeysHandler::KeyEventType StickyKeysHandler::TranslateKeyEvent( + ui::EventType type, + ui::KeyboardCode key_code) { bool is_target_key = false; - if (event->key_code() == ui::VKEY_SHIFT || - event->key_code() == ui::VKEY_LSHIFT || - event->key_code() == ui::VKEY_RSHIFT) { + if (key_code == ui::VKEY_SHIFT || + key_code == ui::VKEY_LSHIFT || + key_code == ui::VKEY_RSHIFT) { is_target_key = (modifier_flag_ == ui::EF_SHIFT_DOWN); - } else if (event->key_code() == ui::VKEY_CONTROL || - event->key_code() == ui::VKEY_LCONTROL || - event->key_code() == ui::VKEY_RCONTROL) { + } else if (key_code == ui::VKEY_CONTROL || + key_code == ui::VKEY_LCONTROL || + key_code == ui::VKEY_RCONTROL) { is_target_key = (modifier_flag_ == ui::EF_CONTROL_DOWN); - } else if (event->key_code() == ui::VKEY_MENU || - event->key_code() == ui::VKEY_LMENU || - event->key_code() == ui::VKEY_RMENU) { + } else if (key_code == ui::VKEY_MENU || + key_code == ui::VKEY_LMENU || + key_code == ui::VKEY_RMENU) { is_target_key = (modifier_flag_ == ui::EF_ALT_DOWN); - } else if (event->key_code() == ui::VKEY_ALTGR) { + } else if (key_code == ui::VKEY_ALTGR) { is_target_key = (modifier_flag_ == ui::EF_ALTGR_DOWN); - } else if (event->key_code() == ui::VKEY_OEM_8) { + } else if (key_code == ui::VKEY_OEM_8) { is_target_key = (modifier_flag_ == ui::EF_MOD3_DOWN); } else { - return event->type() == ui::ET_KEY_PRESSED ? + return type == ui::ET_KEY_PRESSED ? NORMAL_KEY_DOWN : NORMAL_KEY_UP; } if (is_target_key) { - return event->type() == ui::ET_KEY_PRESSED ? + return type == ui::ET_KEY_PRESSED ? TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP; } - return event->type() == ui::ET_KEY_PRESSED ? + return type == ui::ET_KEY_PRESSED ? OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP; } -bool StickyKeysHandler::HandleDisabledState(ui::KeyEvent* event) { - switch (TranslateKeyEvent(event)) { +bool StickyKeysHandler::HandleDisabledState(const ui::KeyEvent& event, + ui::KeyboardCode key_code) { + switch (TranslateKeyEvent(event.type(), key_code)) { case TARGET_MODIFIER_UP: if (preparing_to_enable_) { preparing_to_enable_ = false; scroll_delta_ = 0; current_state_ = STICKY_KEY_STATE_ENABLED; - modifier_up_event_.reset(new ui::KeyEvent(*event)); + modifier_up_event_.reset(new ui::KeyEvent(event)); return true; } return false; @@ -385,20 +373,23 @@ bool StickyKeysHandler::HandleDisabledState(ui::KeyEvent* event) { return false; } -bool StickyKeysHandler::HandleEnabledState(ui::KeyEvent* event) { - switch (TranslateKeyEvent(event)) { +bool StickyKeysHandler::HandleEnabledState(const ui::KeyEvent& event, + ui::KeyboardCode key_code, + int* mod_down_flags, + bool* released) { + switch (TranslateKeyEvent(event.type(), key_code)) { case NORMAL_KEY_UP: case TARGET_MODIFIER_DOWN: - return true; + return false; case TARGET_MODIFIER_UP: current_state_ = STICKY_KEY_STATE_LOCKED; modifier_up_event_.reset(); return true; case NORMAL_KEY_DOWN: { current_state_ = STICKY_KEY_STATE_DISABLED; - AppendModifier(event); - DispatchEventAndReleaseModifier(event); - return true; + *mod_down_flags |= modifier_flag_; + *released = true; + return false; } case OTHER_MODIFIER_DOWN: case OTHER_MODIFIER_UP: @@ -408,8 +399,11 @@ bool StickyKeysHandler::HandleEnabledState(ui::KeyEvent* event) { return false; } -bool StickyKeysHandler::HandleLockedState(ui::KeyEvent* event) { - switch (TranslateKeyEvent(event)) { +bool StickyKeysHandler::HandleLockedState(const ui::KeyEvent& event, + ui::KeyboardCode key_code, + int* mod_down_flags, + bool* released) { + switch (TranslateKeyEvent(event.type(), key_code)) { case TARGET_MODIFIER_DOWN: return true; case TARGET_MODIFIER_UP: @@ -417,7 +411,7 @@ bool StickyKeysHandler::HandleLockedState(ui::KeyEvent* event) { return false; case NORMAL_KEY_DOWN: case NORMAL_KEY_UP: - AppendModifier(event); + *mod_down_flags |= modifier_flag_; return false; case OTHER_MODIFIER_DOWN: case OTHER_MODIFIER_UP: @@ -427,125 +421,4 @@ bool StickyKeysHandler::HandleLockedState(ui::KeyEvent* event) { return false; } -void StickyKeysHandler::DispatchEventAndReleaseModifier(ui::Event* event) { - DCHECK(event->IsKeyEvent() || - event->IsMouseEvent() || - event->IsScrollEvent()); - DCHECK(modifier_up_event_.get()); - aura::Window* target = static_cast<aura::Window*>(event->target()); - DCHECK(target); - aura::Window* root_window = target->GetRootWindow(); - DCHECK(root_window); - - aura::WindowTracker window_tracker; - window_tracker.Add(target); - - event_from_myself_ = true; - if (event->IsKeyEvent()) { - delegate_->DispatchKeyEvent(static_cast<ui::KeyEvent*>(event), target); - } else if (event->IsMouseEvent()) { - delegate_->DispatchMouseEvent(static_cast<ui::MouseEvent*>(event), target); - } else { - delegate_->DispatchScrollEvent( - static_cast<ui::ScrollEvent*>(event), target); - } - - // The action triggered above may have destroyed the event target, in which - // case we will dispatch the modifier up event to the root window instead. - aura::Window* modifier_up_target = - window_tracker.Contains(target) ? target : root_window; - delegate_->DispatchKeyEvent(modifier_up_event_.get(), modifier_up_target); - event_from_myself_ = false; -} - -void StickyKeysHandler::AppendNativeEventMask(unsigned int* state) { -#if defined(USE_X11) - unsigned int& state_ref = *state; - switch (modifier_flag_) { - case ui::EF_CONTROL_DOWN: - state_ref |= ControlMask; - break; - case ui::EF_ALT_DOWN: - state_ref |= Mod1Mask; - break; - case ui::EF_ALTGR_DOWN: - state_ref |= Mod5Mask; - break; - case ui::EF_SHIFT_DOWN: - state_ref |= ShiftMask; - break; - case ui::EF_MOD3_DOWN: - state_ref |= Mod3Mask; - break; - default: - NOTREACHED(); - } -#endif -} - -void StickyKeysHandler::AppendModifier(ui::KeyEvent* event) { -#if defined(USE_X11) - XEvent* xev = event->native_event(); - if (xev) { - XKeyEvent* xkey = &(xev->xkey); - AppendNativeEventMask(&xkey->state); - } -#elif defined(USE_OZONE) - NOTIMPLEMENTED() << "Modifier key is not handled"; -#endif - event->set_flags(event->flags() | modifier_flag_); - event->set_character(ui::GetCharacterFromKeyCode(event->key_code(), - event->flags())); - event->NormalizeFlags(); -} - -void StickyKeysHandler::AppendModifier(ui::MouseEvent* event) { -#if defined(USE_X11) - // The native mouse event can either be a classic X button event or an - // XInput2 button event. - XEvent* xev = event->native_event(); - if (xev) { - switch (xev->type) { - case ButtonPress: - case ButtonRelease: { - XButtonEvent* xkey = &(xev->xbutton); - AppendNativeEventMask(&xkey->state); - break; - } - case GenericEvent: { - XIDeviceEvent* xievent = - static_cast<XIDeviceEvent*>(xev->xcookie.data); - CHECK(xievent->evtype == XI_ButtonPress || - xievent->evtype == XI_ButtonRelease); - AppendNativeEventMask( - reinterpret_cast<unsigned int*>(&xievent->mods.effective)); - break; - } - default: - NOTREACHED(); - } - } -#elif defined(USE_OZONE) - NOTIMPLEMENTED() << "Modifier key is not handled"; -#endif - event->set_flags(event->flags() | modifier_flag_); -} - -void StickyKeysHandler::AppendModifier(ui::ScrollEvent* event) { -#if defined(USE_X11) - XEvent* xev = event->native_event(); - if (xev) { - XIDeviceEvent* xievent = - static_cast<XIDeviceEvent*>(xev->xcookie.data); - if (xievent) { - AppendNativeEventMask(reinterpret_cast<unsigned int*>( - &xievent->mods.effective)); - } - } -#elif defined(USE_OZONE) - NOTIMPLEMENTED() << "Modifier key is not handled"; -#endif - event->set_flags(event->flags() | modifier_flag_); -} - } // namespace ash diff --git a/ash/sticky_keys/sticky_keys_controller.h b/ash/sticky_keys/sticky_keys_controller.h index 89a196a..fd5d389 100644 --- a/ash/sticky_keys/sticky_keys_controller.h +++ b/ash/sticky_keys/sticky_keys_controller.h @@ -10,6 +10,8 @@ #include "base/memory/scoped_ptr.h" #include "ui/events/event_constants.h" #include "ui/events/event_handler.h" +#include "ui/events/event_rewriter.h" +#include "ui/events/keycodes/keyboard_codes.h" namespace ui { class Event; @@ -59,7 +61,7 @@ class StickyKeysHandler; // modifiers. Each handling or state is performed independently. // // StickyKeysController is disabled by default. -class ASH_EXPORT StickyKeysController : public ui::EventHandler { +class ASH_EXPORT StickyKeysController { public: StickyKeysController(); virtual ~StickyKeysController(); @@ -69,24 +71,64 @@ class ASH_EXPORT StickyKeysController : public ui::EventHandler { void SetModifiersEnabled(bool mod3_enabled, bool altgr_enabled); - // Overridden from ui::EventHandler: - virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; - virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE; - virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE; - // Returns the StickyKeyOverlay used by the controller. Ownership is not // passed. StickyKeysOverlay* GetOverlayForTest(); + // Handles keyboard event. Returns an |EventRewriteStatus|, and may + // modify |flags|: + // - Returns ui::EVENT_REWRITE_DISCARD, and leaves |flags| untouched, + // if the event is consumed (i.e. a sticky modifier press or release); + // - Returns ui::EVENT_REWRITE_REWRITTEN if the event needs to be modified + // according to the returned |flags| (i.e. a sticky-modified key); + // - Returns ui::EVENT_REWRITE_DISPATCH_ANOTHER if the event needs to be + // modified according to the returned |flags|, and there are delayed + // modifier-up events now to be retrieved using |NextDispatchEvent()| + // (i.e. a sticky-modified key that ends a sticky state); + // - Otherwise returns ui::EVENT_REWRITE_CONTINUE and leaves |flags| + // unchanged. + ui::EventRewriteStatus RewriteKeyEvent(const ui::KeyEvent& event, + ui::KeyboardCode key_code, + int* flags); + + // Handles mouse event. + ui::EventRewriteStatus RewriteMouseEvent(const ui::MouseEvent& event, + int* flags); + + // Handles scroll event. + ui::EventRewriteStatus RewriteScrollEvent(const ui::ScrollEvent& event, + int* flags); + + // Obtains a pending modifier-up event. If the immediately previous + // call to |Rewrite...Event()| or |NextDispatchEvent()| returned + // ui::EVENT_REWRITE_DISPATCH_ANOTHER, this sets |new_event| and returns: + // - ui::EVENT_REWRITE_DISPATCH_ANOTHER if there is at least one more + // pending modifier-up event; + // - ui::EVENT_REWRITE_REWRITE if this is the last or only modifier-up event; + // Otherwise, there is no pending modifier-up event, and this function + // returns ui::EVENT_REWRITE_CONTINUE and sets |new_event| to NULL. + ui::EventRewriteStatus NextDispatchEvent(scoped_ptr<ui::Event>* new_event); + private: // Handles keyboard event. Returns true if Sticky key consumes keyboard event. - bool HandleKeyEvent(ui::KeyEvent* event); - - // Handles mouse event. Returns true if sticky key consumes mouse event. - bool HandleMouseEvent(ui::MouseEvent* event); - - // Handles scroll event. Returns true if sticky key consumes scroll event. - bool HandleScrollEvent(ui::ScrollEvent* event); + // Adds to |mod_down_flags| any flag to be added to the key event. + // Sets |released| if any modifier is to be released after the key event. + bool HandleKeyEvent(const ui::KeyEvent& event, + ui::KeyboardCode key_code, + int* mod_down_flags, + bool* released); + + // Handles mouse event. Returns true if Sticky key consumes keyboard event. + // Sets |released| if any modifier is to be released after the key event. + bool HandleMouseEvent(const ui::MouseEvent& event, + int* mod_down_flags, + bool* released); + + // Handles scroll event. Returns true if Sticky key consumes keyboard event. + // Sets |released| if any modifier is to be released after the key event. + bool HandleScrollEvent(const ui::ScrollEvent& event, + int* mod_down_flags, + bool* released); // Updates the overlay UI with the current state of the sticky keys. void UpdateOverlay(); @@ -143,40 +185,35 @@ class ASH_EXPORT StickyKeysController : public ui::EventHandler { // is modified. class ASH_EXPORT StickyKeysHandler { public: - class StickyKeysHandlerDelegate { - public: - StickyKeysHandlerDelegate(); - virtual ~StickyKeysHandlerDelegate(); - - // Dispatches keyboard event synchronously. |event| is an event that has - // been previously dispatched. - virtual void DispatchKeyEvent(ui::KeyEvent* event, - aura::Window* target) = 0; - - // Dispatches mouse event synchronously. |event| is an event that has - // been previously dispatched. - virtual void DispatchMouseEvent(ui::MouseEvent* event, - aura::Window* target) = 0; - - // Dispatches scroll event synchronously. |event| is an event that has - // been previously dispatched. - virtual void DispatchScrollEvent(ui::ScrollEvent* event, - aura::Window* target) = 0; - }; - - // This class takes an ownership of |delegate|. - StickyKeysHandler(ui::EventFlags modifier_flag, - StickyKeysHandlerDelegate* delegate); + explicit StickyKeysHandler(ui::EventFlags modifier_flag); ~StickyKeysHandler(); - // Handles key event. Returns true if key is consumed. - bool HandleKeyEvent(ui::KeyEvent* event); + // Handles keyboard event. Returns true if Sticky key consumes keyboard event. + // Sets its own modifier flag in |mod_down_flags| if it is active and needs + // to be added to the event, and sets |released| if releasing it. + bool HandleKeyEvent(const ui::KeyEvent& event, + ui::KeyboardCode key_code, + int* mod_down_flags, + bool* released); - // Handles a mouse event. Returns true if mouse event is consumed. - bool HandleMouseEvent(ui::MouseEvent* event); + // Handles mouse event. Returns true if sticky key consumes mouse event. + // Sets its own modifier flag in |mod_down_flags| if it is active and needs + // to be added to the event, and sets |released| if releasing it. + bool HandleMouseEvent(const ui::MouseEvent& event, + int* mod_down_flags, + bool* released); + + // Handles scroll event. Returns true if sticky key consumes scroll event. + // Sets its own modifier flag in |mod_down_flags| if it is active and needs + // to be added to the event, and sets |released| if releasing it. + bool HandleScrollEvent(const ui::ScrollEvent& event, + int* mod_down_flags, + bool* released); - // Handles a scroll event. Returns true if scroll event is consumed. - bool HandleScrollEvent(ui::ScrollEvent* event); + // Fetches a pending modifier-up event if one exists and the return + // parameter |new_event| is available (i.e. not set). Returns the number + // of pending events still remaining to be returned. + int GetModifierUpEvent(scoped_ptr<ui::Event>* new_event); // Returns current internal state. StickyKeyState current_state() const { return current_state_; } @@ -192,31 +229,27 @@ class ASH_EXPORT StickyKeysHandler { OTHER_MODIFIER_UP, // The modifier key but not monitored key is up. }; - // Translates |event| to sticky keys event type. - KeyEventType TranslateKeyEvent(ui::KeyEvent* event); - - // Handles key event in DISABLED state. - bool HandleDisabledState(ui::KeyEvent* event); + // Translates event type and key code to sticky keys event type. + KeyEventType TranslateKeyEvent(ui::EventType type, ui::KeyboardCode key_code); - // Handles key event in ENABLED state. - bool HandleEnabledState(ui::KeyEvent* event); + // Handles key event in DISABLED state. Returns true if sticky keys + // consumes the keyboard event. + bool HandleDisabledState(const ui::KeyEvent& event, + ui::KeyboardCode key_code); - // Handles key event in LOCKED state. - bool HandleLockedState(ui::KeyEvent* event); + // Handles key event in ENABLED state. Returns true if sticky keys + // consumes the keyboard event. + bool HandleEnabledState(const ui::KeyEvent& event, + ui::KeyboardCode key_code, + int* mod_down_flags, + bool* released); - // Dispatches |event| to its target and then dispatch a key released event - // for the modifier key. This function is required to ensure that the events - // are sent in the correct order when disabling sticky key after a key/mouse - // button press. - void DispatchEventAndReleaseModifier(ui::Event* event); - - // Adds |modifier_flags_| to a native X11 event state mask. - void AppendNativeEventMask(unsigned int* state); - - // Adds |modifier_flags_| into |event|. - void AppendModifier(ui::KeyEvent* event); - void AppendModifier(ui::MouseEvent* event); - void AppendModifier(ui::ScrollEvent* event); + // Handles key event in LOCKED state. Returns true if sticky keys + // consumes the keyboard event. + bool HandleLockedState(const ui::KeyEvent& event, + ui::KeyboardCode key_code, + int* mod_down_flags, + bool* released); // The modifier flag to be monitored and appended to events. const ui::EventFlags modifier_flag_; @@ -224,9 +257,6 @@ class ASH_EXPORT StickyKeysHandler { // The current sticky key status. StickyKeyState current_state_; - // True if the received key event is sent by StickyKeyHandler. - bool event_from_myself_; - // True if we received the TARGET_MODIFIER_DOWN event while in the DISABLED // state but before we receive the TARGET_MODIFIER_UP event. Normal // shortcuts (eg. ctrl + t) during this time will prevent a transition to @@ -241,8 +271,6 @@ class ASH_EXPORT StickyKeysHandler { // The modifier up key event to be sent on non modifier key on ENABLED state. scoped_ptr<ui::KeyEvent> modifier_up_event_; - scoped_ptr<StickyKeysHandlerDelegate> delegate_; - DISALLOW_COPY_AND_ASSIGN(StickyKeysHandler); }; diff --git a/ash/sticky_keys/sticky_keys_overlay_unittest.cc b/ash/sticky_keys/sticky_keys_overlay_unittest.cc index 678eff4..0d6f161 100644 --- a/ash/sticky_keys/sticky_keys_overlay_unittest.cc +++ b/ash/sticky_keys/sticky_keys_overlay_unittest.cc @@ -7,47 +7,14 @@ #include "ash/shell.h" #include "ash/sticky_keys/sticky_keys_controller.h" #include "ash/test/ash_test_base.h" -#include "base/memory/scoped_ptr.h" -#include "ui/aura/window_tree_host.h" #include "ui/events/event.h" -#include "ui/events/event_processor.h" namespace ash { class StickyKeysOverlayTest : public test::AshTestBase { public: - StickyKeysOverlayTest() : - controller_(NULL), - overlay_(NULL) {} - + StickyKeysOverlayTest() {} virtual ~StickyKeysOverlayTest() {} - - virtual void SetUp() OVERRIDE { - test::AshTestBase::SetUp(); - - controller_ = Shell::GetInstance()->sticky_keys_controller(); - controller_->Enable(true); - overlay_ = controller_->GetOverlayForTest(); - ASSERT_TRUE(overlay_); - } - - void PressAndReleaseKey(ui::KeyboardCode code) { - SendKeyEvent(ui::ET_KEY_PRESSED, code); - SendKeyEvent(ui::ET_KEY_RELEASED, code); - } - - void SendKeyEvent(ui::EventType type, ui::KeyboardCode code) { - ui::KeyEvent event(type, code, 0, false); - ui::Event::DispatcherApi dispatcher(&event); - dispatcher.set_target(Shell::GetInstance()->GetPrimaryRootWindow()); - - ui::EventDispatchDetails details = Shell::GetPrimaryRootWindow()-> - GetHost()->event_processor()->OnEventFromSource(&event); - CHECK(!details.dispatcher_destroyed); - } - - StickyKeysController* controller_; - StickyKeysOverlay* overlay_; }; TEST_F(StickyKeysOverlayTest, OverlayVisibility) { @@ -71,168 +38,7 @@ TEST_F(StickyKeysOverlayTest, ModifierKeyState) { overlay.GetModifierKeyState(ui::EF_CONTROL_DOWN)); } -TEST_F(StickyKeysOverlayTest, OneModifierEnabled) { - EXPECT_FALSE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); - - // Pressing modifier key should show overlay. - PressAndReleaseKey(ui::VKEY_CONTROL); - EXPECT_TRUE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_ENABLED, - overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); - - // Pressing a normal key should hide overlay. - PressAndReleaseKey(ui::VKEY_T); - EXPECT_FALSE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); -} - -TEST_F(StickyKeysOverlayTest, TwoModifiersEnabled) { - EXPECT_FALSE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); - - // Pressing two modifiers should show overlay. - PressAndReleaseKey(ui::VKEY_SHIFT); - PressAndReleaseKey(ui::VKEY_CONTROL); - EXPECT_TRUE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_ENABLED, - overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); - EXPECT_EQ(STICKY_KEY_STATE_ENABLED, - overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); - - // Pressing a normal key should hide overlay. - PressAndReleaseKey(ui::VKEY_N); - EXPECT_FALSE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); -} - -TEST_F(StickyKeysOverlayTest, LockedModifier) { - EXPECT_FALSE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); - - // Pressing a modifier key twice should lock modifier and show overlay. - PressAndReleaseKey(ui::VKEY_LMENU); - PressAndReleaseKey(ui::VKEY_LMENU); - EXPECT_TRUE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_LOCKED, - overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); - - // Pressing a normal key should not hide overlay. - PressAndReleaseKey(ui::VKEY_D); - EXPECT_TRUE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_LOCKED, - overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); -} - -TEST_F(StickyKeysOverlayTest, LockedAndNormalModifier) { - EXPECT_FALSE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); - - // Pressing a modifier key twice should lock modifier and show overlay. - PressAndReleaseKey(ui::VKEY_CONTROL); - PressAndReleaseKey(ui::VKEY_CONTROL); - EXPECT_TRUE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_LOCKED, - overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); - - // Pressing another modifier key should still show overlay. - PressAndReleaseKey(ui::VKEY_SHIFT); - EXPECT_TRUE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_LOCKED, - overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); - EXPECT_EQ(STICKY_KEY_STATE_ENABLED, - overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); - - // Pressing a normal key should not hide overlay but disable normal modifier. - PressAndReleaseKey(ui::VKEY_D); - EXPECT_TRUE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_LOCKED, - overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); -} - -TEST_F(StickyKeysOverlayTest, ModifiersDisabled) { - EXPECT_FALSE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); - - // Enable modifiers. - PressAndReleaseKey(ui::VKEY_CONTROL); - PressAndReleaseKey(ui::VKEY_SHIFT); - PressAndReleaseKey(ui::VKEY_SHIFT); - PressAndReleaseKey(ui::VKEY_LMENU); - - EXPECT_TRUE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_ENABLED, - overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); - EXPECT_EQ(STICKY_KEY_STATE_LOCKED, - overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); - EXPECT_EQ(STICKY_KEY_STATE_ENABLED, - overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); - - // Disable modifiers and overlay should be hidden. - PressAndReleaseKey(ui::VKEY_CONTROL); - PressAndReleaseKey(ui::VKEY_CONTROL); - PressAndReleaseKey(ui::VKEY_SHIFT); - PressAndReleaseKey(ui::VKEY_LMENU); - PressAndReleaseKey(ui::VKEY_LMENU); - - EXPECT_FALSE(overlay_->is_visible()); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, - overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); -} - -TEST_F(StickyKeysOverlayTest, ModifierVisibility) { - // All but AltGr and Mod3 should initially be visible. - EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_CONTROL_DOWN)); - EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_SHIFT_DOWN)); - EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_ALT_DOWN)); - EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_ALTGR_DOWN)); - EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_MOD3_DOWN)); - - // Turn all modifiers on. - controller_->SetModifiersEnabled(true, true); - EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_CONTROL_DOWN)); - EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_SHIFT_DOWN)); - EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_ALT_DOWN)); - EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_ALTGR_DOWN)); - EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_MOD3_DOWN)); - - // Turn off Mod3. - controller_->SetModifiersEnabled(false, true); - EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_ALTGR_DOWN)); - EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_MOD3_DOWN)); - - // Turn off AltGr. - controller_->SetModifiersEnabled(true, false); - EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_ALTGR_DOWN)); - EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_MOD3_DOWN)); - - // Turn off AltGr and Mod3. - controller_->SetModifiersEnabled(false, false); - EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_ALTGR_DOWN)); - EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_MOD3_DOWN)); -} +// Additional sticky key overlay tests that depend on chromeos::EventRewriter +// are now in chrome/browser/chromeos/events/event_rewriter_unittest.cc . } // namespace ash diff --git a/ash/sticky_keys/sticky_keys_unittest.cc b/ash/sticky_keys/sticky_keys_unittest.cc index 4c505e1..c164923 100644 --- a/ash/sticky_keys/sticky_keys_unittest.cc +++ b/ash/sticky_keys/sticky_keys_unittest.cc @@ -16,8 +16,7 @@ #include "base/memory/scoped_vector.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" -#include "ui/events/event_handler.h" -#include "ui/events/event_processor.h" +#include "ui/events/event_source.h" #include "ui/events/test/events_test_utils_x11.h" #include "ui/events/x/device_data_manager.h" @@ -30,107 +29,7 @@ const unsigned int kTouchPadDeviceId = 1; } // namespace -// Keeps a buffer of handled events. -class EventBuffer : public ui::EventHandler { - public: - EventBuffer() {} - virtual ~EventBuffer() {} - - void PopEvents(ScopedVector<ui::Event>* events) { - events->clear(); - events->swap(events_); - } - - private: - // ui::EventHandler overrides: - virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE { - events_.push_back(new ui::KeyEvent(*event)); - } - - virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE { - if (event->IsMouseWheelEvent()) { - events_.push_back( - new ui::MouseWheelEvent(*static_cast<ui::MouseWheelEvent*>(event))); - } else { - events_.push_back(new ui::MouseEvent(*event)); - } - } - - ScopedVector<ui::Event> events_; - - DISALLOW_COPY_AND_ASSIGN(EventBuffer); -}; - -// A testable and StickyKeysHandler. -class MockStickyKeysHandlerDelegate : - public StickyKeysHandler::StickyKeysHandlerDelegate { - public: - class Delegate { - public: - virtual aura::Window* GetExpectedTarget() = 0; - virtual void OnShortcutPressed() = 0; - - protected: - virtual ~Delegate() {} - }; - - MockStickyKeysHandlerDelegate(Delegate* delegate) : delegate_(delegate) {} - - virtual ~MockStickyKeysHandlerDelegate() {} - - // StickyKeysHandler override. - virtual void DispatchKeyEvent(ui::KeyEvent* event, - aura::Window* target) OVERRIDE { - ASSERT_EQ(delegate_->GetExpectedTarget(), target); - - // Detect a special shortcut when it is dispatched. This shortcut will - // not be hit in the LOCKED state as this case does not involve the - // delegate. - if (event->type() == ui::ET_KEY_PRESSED && - event->key_code() == ui::VKEY_J && - event->flags() | ui::EF_CONTROL_DOWN) { - delegate_->OnShortcutPressed(); - } - - events_.push_back(new ui::KeyEvent(*event)); - } - - virtual void DispatchMouseEvent(ui::MouseEvent* event, - aura::Window* target) OVERRIDE { - ASSERT_EQ(delegate_->GetExpectedTarget(), target); - events_.push_back( - new ui::MouseEvent(*event, target, target->GetRootWindow())); - } - - virtual void DispatchScrollEvent(ui::ScrollEvent* event, - aura::Window* target) OVERRIDE { - events_.push_back(new ui::ScrollEvent(event->native_event())); - } - - // Returns the count of dispatched events. - size_t GetEventCount() const { - return events_.size(); - } - - // Returns the |index|-th dispatched event. - const ui::Event* GetEvent(size_t index) const { - return events_[index]; - } - - // Clears all previously dispatched events. - void ClearEvents() { - events_.clear(); - } - - private: - ScopedVector<ui::Event> events_; - Delegate* delegate_; - - DISALLOW_COPY_AND_ASSIGN(MockStickyKeysHandlerDelegate); -}; - -class StickyKeysTest : public test::AshTestBase, - public MockStickyKeysHandlerDelegate::Delegate { +class StickyKeysTest : public test::AshTestBase { protected: StickyKeysTest() : target_(NULL), @@ -151,47 +50,35 @@ class StickyKeysTest : public test::AshTestBase, test::AshTestBase::TearDown(); } - // Overridden from MockStickyKeysHandlerDelegate::Delegate: - virtual aura::Window* GetExpectedTarget() OVERRIDE { - return target_ ? target_ : root_window_; - } - - virtual void OnShortcutPressed() OVERRIDE { + virtual void OnShortcutPressed() { if (target_) { delete target_; target_ = NULL; } } - ui::KeyEvent* GenerateKey(bool is_key_press, ui::KeyboardCode code) { - scoped_xevent_.InitKeyEvent( - is_key_press ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED, - code, - 0); - ui::KeyEvent* event = new ui::KeyEvent(scoped_xevent_, false); - ui::Event::DispatcherApi dispatcher(event); - dispatcher.set_target(target_); + ui::KeyEvent* GenerateKey(ui::EventType type, ui::KeyboardCode code) { + scoped_xevent_.InitKeyEvent(type, code, 0); + ui::KeyEvent* event = new ui::KeyEvent(scoped_xevent_, false); return event; } // Creates a mouse event backed by a native XInput2 generic button event. // This is the standard native event on Chromebooks. - ui::MouseEvent* GenerateMouseEvent(bool is_button_press) { - return GenerateMouseEventAt(is_button_press, gfx::Point()); + ui::MouseEvent* GenerateMouseEvent(ui::EventType type) { + return GenerateMouseEventAt(type, gfx::Point()); } // Creates a mouse event backed by a native XInput2 generic button event. // The |location| should be in physical pixels. - ui::MouseEvent* GenerateMouseEventAt(bool is_button_press, + ui::MouseEvent* GenerateMouseEventAt(ui::EventType type, const gfx::Point& location) { scoped_xevent_.InitGenericButtonEvent( kTouchPadDeviceId, - is_button_press ? ui::ET_MOUSE_PRESSED : ui::ET_MOUSE_RELEASED, + type, location, 0); ui::MouseEvent* event = new ui::MouseEvent(scoped_xevent_); - ui::Event::DispatcherApi dispatcher(event); - dispatcher.set_target(target_); return event; } @@ -234,14 +121,9 @@ class StickyKeysTest : public test::AshTestBase, } // Creates a synthesized KeyEvent that is not backed by a native event. - ui::KeyEvent* GenerateSynthesizedKeyEvent( - bool is_key_press, ui::KeyboardCode code) { - ui::KeyEvent* event = new ui::KeyEvent( - is_key_press ? ui::ET_KEY_PRESSED : ui::ET_MOUSE_RELEASED, - code, 0, true); - ui::Event::DispatcherApi dispatcher(event); - dispatcher.set_target(target_); - return event; + ui::KeyEvent* GenerateSynthesizedKeyEvent(ui::EventType type, + ui::KeyboardCode code) { + return new ui::KeyEvent(type, code, 0, true); } // Creates a synthesized MouseEvent that is not backed by a native event. @@ -259,11 +141,9 @@ class StickyKeysTest : public test::AshTestBase, // Creates a synthesized mouse press or release event. ui::MouseEvent* GenerateSynthesizedMouseClickEvent( - bool is_button_press, + ui::EventType type, const gfx::Point& location) { - return GenerateSynthesizedMouseEventAt( - is_button_press ? ui::ET_MOUSE_PRESSED : ui::ET_MOUSE_RELEASED, - location); + return GenerateSynthesizedMouseEventAt(type, location); } // Creates a synthesized ET_MOUSE_MOVED event. @@ -284,22 +164,28 @@ class StickyKeysTest : public test::AshTestBase, void SendActivateStickyKeyPattern(StickyKeysHandler* handler, ui::KeyboardCode key_code) { + bool released = false; + int down_flags = 0; scoped_ptr<ui::KeyEvent> ev; - ev.reset(GenerateKey(true, key_code)); - handler->HandleKeyEvent(ev.get()); - ev.reset(GenerateKey(false, key_code)); - handler->HandleKeyEvent(ev.get()); + ev.reset(GenerateKey(ui::ET_KEY_PRESSED, key_code)); + handler->HandleKeyEvent(*ev.get(), key_code, &down_flags, &released); + ev.reset(GenerateKey(ui::ET_KEY_RELEASED, key_code)); + handler->HandleKeyEvent(*ev.get(), key_code, &down_flags, &released); } - void SendActivateStickyKeyPattern(ui::EventProcessor* dispatcher, - ui::KeyboardCode key_code) { - scoped_ptr<ui::KeyEvent> ev; - ev.reset(GenerateKey(true, key_code)); - ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get()); - CHECK(!details.dispatcher_destroyed); - ev.reset(GenerateKey(false, key_code)); - details = dispatcher->OnEventFromSource(ev.get()); - CHECK(!details.dispatcher_destroyed); + bool HandleKeyEvent(const ui::KeyEvent& key_event, + StickyKeysHandler* handler, + int* down, + bool* up) { + return handler->HandleKeyEvent(key_event, key_event.key_code(), down, up); + } + + int HandleKeyEventForDownFlags(const ui::KeyEvent& key_event, + StickyKeysHandler* handler) { + bool released = false; + int down = 0; + handler->HandleKeyEvent(key_event, key_event.key_code(), &down, &released); + return down; } aura::Window* target() { return target_; } @@ -318,9 +204,7 @@ class StickyKeysTest : public test::AshTestBase, TEST_F(StickyKeysTest, BasicOneshotScenarioTest) { scoped_ptr<ui::KeyEvent> ev; - MockStickyKeysHandlerDelegate* mock_delegate = - new MockStickyKeysHandlerDelegate(this); - StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate); + StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); @@ -328,42 +212,42 @@ TEST_F(StickyKeysTest, BasicOneshotScenarioTest) { SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); - ev.reset(GenerateKey(true, ui::VKEY_A)); - sticky_key.HandleKeyEvent(ev.get()); - + ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_A)); + bool released = false; + int mod_down_flags = 0; + HandleKeyEvent(*ev.get(), &sticky_key, &mod_down_flags, &released); // Next keyboard event is shift modified. - EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); + EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN); + // Modifier release notification happens. + EXPECT_TRUE(released); - ev.reset(GenerateKey(false, ui::VKEY_A)); - sticky_key.HandleKeyEvent(ev.get()); + ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_A)); + released = false; + mod_down_flags = 0; + HandleKeyEvent(*ev.get(), &sticky_key, &mod_down_flags, &released); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); - // Making sure Shift up keyboard event is dispatched. - ASSERT_EQ(2U, mock_delegate->GetEventCount()); - EXPECT_EQ(ui::ET_KEY_PRESSED, mock_delegate->GetEvent(0)->type()); - EXPECT_EQ(ui::VKEY_A, - static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(0)) - ->key_code()); - EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type()); + // Making sure Shift up keyboard event is available. + scoped_ptr<ui::Event> up_event; + ASSERT_EQ(0, sticky_key.GetModifierUpEvent(&up_event)); + EXPECT_TRUE(up_event.get()); + EXPECT_EQ(ui::ET_KEY_RELEASED, up_event->type()); EXPECT_EQ(ui::VKEY_SHIFT, - static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1)) - ->key_code()); + static_cast<const ui::KeyEvent*>(up_event.get())->key_code()); // Enabled state is one shot, so next key event should not be shift modified. - ev.reset(GenerateKey(true, ui::VKEY_A)); - sticky_key.HandleKeyEvent(ev.get()); - EXPECT_FALSE(ev->flags() & ui::EF_SHIFT_DOWN); + ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_A)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + EXPECT_FALSE(mod_down_flags & ui::EF_SHIFT_DOWN); - ev.reset(GenerateKey(false, ui::VKEY_A)); - sticky_key.HandleKeyEvent(ev.get()); - EXPECT_FALSE(ev->flags() & ui::EF_SHIFT_DOWN); + ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_A)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + EXPECT_FALSE(mod_down_flags & ui::EF_SHIFT_DOWN); } TEST_F(StickyKeysTest, BasicLockedScenarioTest) { scoped_ptr<ui::KeyEvent> ev; - MockStickyKeysHandlerDelegate* mock_delegate = - new MockStickyKeysHandlerDelegate(this); - StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate); + StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); @@ -376,24 +260,24 @@ TEST_F(StickyKeysTest, BasicLockedScenarioTest) { EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); // All keyboard events including keyUp become shift modified. - ev.reset(GenerateKey(true, ui::VKEY_A)); - sticky_key.HandleKeyEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); + ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_A)); + int mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN); - ev.reset(GenerateKey(false, ui::VKEY_A)); - sticky_key.HandleKeyEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); + ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_A)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN); // Locked state keeps after normal keyboard event. EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); - ev.reset(GenerateKey(true, ui::VKEY_B)); - sticky_key.HandleKeyEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); + ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_B)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN); - ev.reset(GenerateKey(false, ui::VKEY_B)); - sticky_key.HandleKeyEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); + ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_B)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN); EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); @@ -404,146 +288,163 @@ TEST_F(StickyKeysTest, BasicLockedScenarioTest) { TEST_F(StickyKeysTest, NonTargetModifierTest) { scoped_ptr<ui::KeyEvent> ev; - MockStickyKeysHandlerDelegate* mock_delegate = - new MockStickyKeysHandlerDelegate(this); - StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate); + StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); // Non target modifier key does not affect internal state - ev.reset(GenerateKey(true, ui::VKEY_MENU)); - sticky_key.HandleKeyEvent(ev.get()); + ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_MENU)); + int mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); + EXPECT_EQ(ui::EF_NONE, mod_down_flags); - ev.reset(GenerateKey(false, ui::VKEY_MENU)); - sticky_key.HandleKeyEvent(ev.get()); + ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_MENU)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); + EXPECT_EQ(ui::EF_NONE, mod_down_flags); SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); // Non target modifier key does not affect internal state - ev.reset(GenerateKey(true, ui::VKEY_MENU)); - sticky_key.HandleKeyEvent(ev.get()); + ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_MENU)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); + EXPECT_EQ(ui::EF_NONE, mod_down_flags); - ev.reset(GenerateKey(false, ui::VKEY_MENU)); - sticky_key.HandleKeyEvent(ev.get()); + ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_MENU)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); + EXPECT_EQ(ui::EF_NONE, mod_down_flags); SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); // Non target modifier key does not affect internal state - ev.reset(GenerateKey(true, ui::VKEY_MENU)); - sticky_key.HandleKeyEvent(ev.get()); + ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_MENU)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); + EXPECT_EQ(ui::EF_NONE, mod_down_flags); - ev.reset(GenerateKey(false, ui::VKEY_MENU)); - sticky_key.HandleKeyEvent(ev.get()); + ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_MENU)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); + EXPECT_EQ(ui::EF_NONE, mod_down_flags); } TEST_F(StickyKeysTest, NormalShortcutTest) { // Sticky keys should not be enabled if we perform a normal shortcut. scoped_ptr<ui::KeyEvent> ev; - MockStickyKeysHandlerDelegate* mock_delegate = - new MockStickyKeysHandlerDelegate(this); - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); // Perform ctrl+n shortcut. - ev.reset(GenerateKey(true, ui::VKEY_CONTROL)); - sticky_key.HandleKeyEvent(ev.get()); - ev.reset(GenerateKey(true, ui::VKEY_N)); - sticky_key.HandleKeyEvent(ev.get()); - ev.reset(GenerateKey(false, ui::VKEY_N)); - sticky_key.HandleKeyEvent(ev.get()); + ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL)); + int mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_N)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_N)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); + ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + + // Sticky keys should not be enabled afterwards. + EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); + EXPECT_EQ(ui::EF_NONE, mod_down_flags); + + // Perform ctrl+n shortcut, releasing ctrl first. + ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_N)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); + ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_N)); + mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); // Sticky keys should not be enabled afterwards. - ev.reset(GenerateKey(false, ui::VKEY_CONTROL)); - sticky_key.HandleKeyEvent(ev.get()); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); + EXPECT_EQ(ui::EF_NONE, mod_down_flags); } TEST_F(StickyKeysTest, NormalModifiedClickTest) { scoped_ptr<ui::KeyEvent> kev; scoped_ptr<ui::MouseEvent> mev; - MockStickyKeysHandlerDelegate* mock_delegate = - new MockStickyKeysHandlerDelegate(this); - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); // Perform ctrl+click. - kev.reset(GenerateKey(true, ui::VKEY_CONTROL)); - sticky_key.HandleKeyEvent(kev.get()); - mev.reset(GenerateMouseEvent(true)); - sticky_key.HandleMouseEvent(mev.get()); - mev.reset(GenerateMouseEvent(false)); - sticky_key.HandleMouseEvent(mev.get()); + kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL)); + int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); + mev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED)); + bool released = false; + sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released); + mev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED)); + sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released); // Sticky keys should not be enabled afterwards. - kev.reset(GenerateKey(false, ui::VKEY_CONTROL)); - sticky_key.HandleKeyEvent(kev.get()); + kev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL)); + mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); + EXPECT_EQ(ui::EF_NONE, mod_down_flags); } TEST_F(StickyKeysTest, MouseMovedModifierTest) { scoped_ptr<ui::KeyEvent> kev; scoped_ptr<ui::MouseEvent> mev; - MockStickyKeysHandlerDelegate* mock_delegate = - new MockStickyKeysHandlerDelegate(this); - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); // Press ctrl and handle mouse move events. - kev.reset(GenerateKey(true, ui::VKEY_CONTROL)); - sticky_key.HandleKeyEvent(kev.get()); + kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL)); + int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); mev.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(0, 0))); - sticky_key.HandleMouseEvent(mev.get()); + bool released = false; + sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released); mev.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(100, 100))); - sticky_key.HandleMouseEvent(mev.get()); + sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released); // Sticky keys should be enabled afterwards. - kev.reset(GenerateKey(false, ui::VKEY_CONTROL)); - sticky_key.HandleKeyEvent(kev.get()); + kev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL)); + mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); + EXPECT_EQ(ui::EF_NONE, mod_down_flags); } TEST_F(StickyKeysTest, NormalModifiedScrollTest) { scoped_ptr<ui::KeyEvent> kev; scoped_ptr<ui::ScrollEvent> sev; - MockStickyKeysHandlerDelegate* mock_delegate = - new MockStickyKeysHandlerDelegate(this); - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); // Perform ctrl+scroll. - kev.reset(GenerateKey(true, ui::VKEY_CONTROL)); + kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL)); + int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); sev.reset(GenerateFlingScrollEvent(0, true)); - sticky_key.HandleScrollEvent(sev.get()); + bool released = false; + sticky_key.HandleScrollEvent(*sev.get(), &mod_down_flags, &released); sev.reset(GenerateScrollEvent(10)); - sticky_key.HandleScrollEvent(sev.get()); + sticky_key.HandleScrollEvent(*sev.get(), &mod_down_flags, &released); sev.reset(GenerateFlingScrollEvent(10, false)); - sticky_key.HandleScrollEvent(sev.get()); + sticky_key.HandleScrollEvent(*sev.get(), &mod_down_flags, &released); // Sticky keys should not be enabled afterwards. - kev.reset(GenerateKey(false, ui::VKEY_CONTROL)); - sticky_key.HandleKeyEvent(kev.get()); + kev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL)); + mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); + EXPECT_EQ(ui::EF_NONE, mod_down_flags); } TEST_F(StickyKeysTest, MouseEventOneshot) { scoped_ptr<ui::MouseEvent> ev; scoped_ptr<ui::KeyEvent> kev; - MockStickyKeysHandlerDelegate* mock_delegate = - new MockStickyKeysHandlerDelegate(this); - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); @@ -551,40 +452,47 @@ TEST_F(StickyKeysTest, MouseEventOneshot) { // We should still be in the ENABLED state until we get the mouse // release event. - ev.reset(GenerateMouseEvent(true)); - sticky_key.HandleMouseEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); + ev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED)); + bool released = false; + int mod_down_flags = 0; + sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); - ev.reset(GenerateMouseEvent(false)); - sticky_key.HandleMouseEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); + ev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED)); + released = false; + mod_down_flags = 0; + sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); // Making sure modifier key release event is dispatched in the right order. - ASSERT_EQ(2u, mock_delegate->GetEventCount()); - EXPECT_EQ(ui::ET_MOUSE_RELEASED, mock_delegate->GetEvent(0)->type()); - EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type()); + EXPECT_TRUE(released); + scoped_ptr<ui::Event> up_event; + ASSERT_EQ(0, sticky_key.GetModifierUpEvent(&up_event)); + EXPECT_TRUE(up_event.get()); + EXPECT_EQ(ui::ET_KEY_RELEASED, up_event->type()); EXPECT_EQ(ui::VKEY_CONTROL, - static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1)) - ->key_code()); + static_cast<const ui::KeyEvent*>(up_event.get())->key_code()); // Enabled state is one shot, so next click should not be control modified. - ev.reset(GenerateMouseEvent(true)); - sticky_key.HandleMouseEvent(ev.get()); - EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN); - - ev.reset(GenerateMouseEvent(false)); - sticky_key.HandleMouseEvent(ev.get()); - EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN); + ev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED)); + released = false; + mod_down_flags = 0; + sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); + EXPECT_FALSE(mod_down_flags & ui::EF_CONTROL_DOWN); + + ev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED)); + released = false; + mod_down_flags = 0; + sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); + EXPECT_FALSE(mod_down_flags & ui::EF_CONTROL_DOWN); } TEST_F(StickyKeysTest, MouseEventLocked) { scoped_ptr<ui::MouseEvent> ev; scoped_ptr<ui::KeyEvent> kev; - MockStickyKeysHandlerDelegate* mock_delegate = - new MockStickyKeysHandlerDelegate(this); - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); @@ -596,35 +504,44 @@ TEST_F(StickyKeysTest, MouseEventLocked) { // Mouse events should not disable locked mode. for (int i = 0; i < 3; ++i) { - ev.reset(GenerateMouseEvent(true)); - sticky_key.HandleMouseEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); - ev.reset(GenerateMouseEvent(false)); - sticky_key.HandleMouseEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); + bool released = false; + int mod_down_flags = 0; + ev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED)); + sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); + ev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED)); + released = false; + mod_down_flags = 0; + sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); } // Test with mouse wheel. for (int i = 0; i < 3; ++i) { + bool released = false; + int mod_down_flags = 0; ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta)); - sticky_key.HandleMouseEvent(ev.get()); + sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta)); - sticky_key.HandleMouseEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); + released = false; + mod_down_flags = 0; + sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); } // Test mixed case with mouse events and key events. ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta)); - sticky_key.HandleMouseEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); - kev.reset(GenerateKey(true, ui::VKEY_N)); - sticky_key.HandleKeyEvent(kev.get()); - EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN); - kev.reset(GenerateKey(false, ui::VKEY_N)); - sticky_key.HandleKeyEvent(kev.get()); - EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN); + bool released = false; + int mod_down_flags = 0; + sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); + kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_N)); + mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); + mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); } @@ -632,14 +549,10 @@ TEST_F(StickyKeysTest, MouseEventLocked) { TEST_F(StickyKeysTest, ScrollEventOneshot) { scoped_ptr<ui::ScrollEvent> ev; scoped_ptr<ui::KeyEvent> kev; - MockStickyKeysHandlerDelegate* mock_delegate = - new MockStickyKeysHandlerDelegate(this); - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); int scroll_deltas[] = {-10, 10}; for (int i = 0; i < 2; ++i) { - mock_delegate->ClearEvents(); - // Enable sticky keys. EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); @@ -648,42 +561,44 @@ TEST_F(StickyKeysTest, ScrollEventOneshot) { // Test a scroll sequence. Sticky keys should only be disabled at the end // of the scroll sequence. Fling cancel event starts the scroll sequence. ev.reset(GenerateFlingScrollEvent(0, true)); - sticky_key.HandleScrollEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); + bool released = false; + int mod_down_flags = 0; + sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); // Scrolls should all be modified but not disable sticky keys. for (int j = 0; j < 3; ++j) { ev.reset(GenerateScrollEvent(scroll_deltas[i])); - sticky_key.HandleScrollEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); + released = false; + mod_down_flags = 0; + sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); } // Fling start event ends scroll sequence. ev.reset(GenerateFlingScrollEvent(scroll_deltas[i], false)); - sticky_key.HandleScrollEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); + released = false; + mod_down_flags = 0; + sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); - ASSERT_EQ(2U, mock_delegate->GetEventCount()); - EXPECT_EQ(ui::ET_SCROLL_FLING_START, mock_delegate->GetEvent(0)->type()); - EXPECT_FLOAT_EQ(scroll_deltas[i], - static_cast<const ui::ScrollEvent*>( - mock_delegate->GetEvent(0))->y_offset()); - EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type()); + scoped_ptr<ui::Event> up_event; + EXPECT_TRUE(released); + ASSERT_EQ(0, sticky_key.GetModifierUpEvent(&up_event)); + EXPECT_TRUE(up_event.get()); + EXPECT_EQ(ui::ET_KEY_RELEASED, up_event->type()); EXPECT_EQ(ui::VKEY_CONTROL, - static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1)) - ->key_code()); + static_cast<const ui::KeyEvent*>(up_event.get())->key_code()); } } TEST_F(StickyKeysTest, ScrollDirectionChanged) { scoped_ptr<ui::ScrollEvent> ev; scoped_ptr<ui::KeyEvent> kev; - MockStickyKeysHandlerDelegate* mock_delegate = - new MockStickyKeysHandlerDelegate(this); - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); // Test direction change with both boundary value and negative value. const int direction_change_values[2] = {0, -10}; @@ -693,21 +608,26 @@ TEST_F(StickyKeysTest, ScrollDirectionChanged) { // Fling cancel starts scroll sequence. ev.reset(GenerateFlingScrollEvent(0, true)); - sticky_key.HandleScrollEvent(ev.get()); + bool released = false; + int mod_down_flags = 0; + sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); // Test that changing directions in a scroll sequence will // return sticky keys to DISABLED state. for (int j = 0; j < 3; ++j) { ev.reset(GenerateScrollEvent(10)); - sticky_key.HandleScrollEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); + released = false; + mod_down_flags = 0; + sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); } ev.reset(GenerateScrollEvent(direction_change_values[i])); - sticky_key.HandleScrollEvent(ev.get()); - EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN); + released = false; + mod_down_flags = 0; + sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); } } @@ -715,9 +635,7 @@ TEST_F(StickyKeysTest, ScrollDirectionChanged) { TEST_F(StickyKeysTest, ScrollEventLocked) { scoped_ptr<ui::ScrollEvent> ev; scoped_ptr<ui::KeyEvent> kev; - MockStickyKeysHandlerDelegate* mock_delegate = - new MockStickyKeysHandlerDelegate(this); - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); // Lock sticky keys. SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); @@ -728,63 +646,45 @@ TEST_F(StickyKeysTest, ScrollEventLocked) { for (int i = 0; i < 5; ++i) { // Fling cancel starts scroll sequence. ev.reset(GenerateFlingScrollEvent(0, true)); - sticky_key.HandleScrollEvent(ev.get()); + bool released = false; + int mod_down_flags = 0; + sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); ev.reset(GenerateScrollEvent(10)); - sticky_key.HandleScrollEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); + released = false; + mod_down_flags = 0; + sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); ev.reset(GenerateScrollEvent(-10)); - sticky_key.HandleScrollEvent(ev.get()); - EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); + sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); // Fling start ends scroll sequence. ev.reset(GenerateFlingScrollEvent(-10, false)); - sticky_key.HandleScrollEvent(ev.get()); + sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); } EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); } -TEST_F(StickyKeysTest, EventTargetDestroyed) { - scoped_ptr<ui::KeyEvent> ev; - MockStickyKeysHandlerDelegate* mock_delegate = - new MockStickyKeysHandlerDelegate(this); - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); - - target()->Focus(); - - // Go into ENABLED state. - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); - SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); - EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); - - // CTRL+J is a special shortcut that will destroy the event target. - ev.reset(GenerateKey(true, ui::VKEY_J)); - sticky_key.HandleKeyEvent(ev.get()); - EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); - EXPECT_FALSE(target()); -} - TEST_F(StickyKeysTest, SynthesizedEvents) { // Non-native, internally generated events should be properly handled // by sticky keys. - MockStickyKeysHandlerDelegate* mock_delegate = - new MockStickyKeysHandlerDelegate(this); - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); // Test non-native key events. scoped_ptr<ui::KeyEvent> kev; SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); - kev.reset(GenerateSynthesizedKeyEvent(true, ui::VKEY_K)); - sticky_key.HandleKeyEvent(kev.get()); - EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN); + kev.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_K)); + int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); - kev.reset(GenerateSynthesizedKeyEvent(false, ui::VKEY_K)); - sticky_key.HandleKeyEvent(kev.get()); - EXPECT_FALSE(kev->flags() & ui::EF_CONTROL_DOWN); + kev.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_K)); + mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); + EXPECT_FALSE(mod_down_flags & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); // Test non-native mouse events. @@ -792,216 +692,21 @@ TEST_F(StickyKeysTest, SynthesizedEvents) { EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); scoped_ptr<ui::MouseEvent> mev; - mev.reset(GenerateSynthesizedMouseClickEvent(true, gfx::Point(0, 0))); - sticky_key.HandleMouseEvent(mev.get()); - EXPECT_TRUE(mev->flags() & ui::EF_CONTROL_DOWN); + mev.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_PRESSED, + gfx::Point(0, 0))); + bool released = false; + sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); - mev.reset(GenerateSynthesizedMouseClickEvent(false, gfx::Point(0, 0))); - sticky_key.HandleMouseEvent(mev.get()); - EXPECT_TRUE(mev->flags() & ui::EF_CONTROL_DOWN); + mev.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_RELEASED, + gfx::Point(0, 0))); + released = false; + mod_down_flags = 0; + sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released); + EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); + EXPECT_TRUE(released); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); } -TEST_F(StickyKeysTest, KeyEventDispatchImpl) { - // Test the actual key event dispatch implementation. - EventBuffer buffer; - ScopedVector<ui::Event> events; - ui::EventProcessor* dispatcher = - Shell::GetPrimaryRootWindow()->GetHost()->event_processor(); - Shell::GetInstance()->AddPreTargetHandler(&buffer); - Shell::GetInstance()->sticky_keys_controller()->Enable(true); - - SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL); - scoped_ptr<ui::KeyEvent> ev; - buffer.PopEvents(&events); - - // Test key press event is correctly modified and modifier release - // event is sent. - ev.reset(GenerateKey(true, ui::VKEY_C)); - ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get()); - buffer.PopEvents(&events); - EXPECT_EQ(2u, events.size()); - EXPECT_EQ(ui::ET_KEY_PRESSED, events[0]->type()); - EXPECT_EQ(ui::VKEY_C, static_cast<ui::KeyEvent*>(events[0])->key_code()); - EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); - EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); - EXPECT_EQ(ui::VKEY_CONTROL, - static_cast<ui::KeyEvent*>(events[1])->key_code()); - - // Test key release event is not modified. - ev.reset(GenerateKey(false, ui::VKEY_C)); - details = dispatcher->OnEventFromSource(ev.get()); - ASSERT_FALSE(details.dispatcher_destroyed); - buffer.PopEvents(&events); - EXPECT_EQ(1u, events.size()); - EXPECT_EQ(ui::ET_KEY_RELEASED, events[0]->type()); - EXPECT_EQ(ui::VKEY_C, - static_cast<ui::KeyEvent*>(events[0])->key_code()); - EXPECT_FALSE(events[0]->flags() & ui::EF_CONTROL_DOWN); - - // Test that synthesized key events are dispatched correctly. - SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL); - buffer.PopEvents(&events); - scoped_ptr<ui::KeyEvent> kev; - kev.reset(GenerateSynthesizedKeyEvent(true, ui::VKEY_K)); - details = dispatcher->OnEventFromSource(kev.get()); - ASSERT_FALSE(details.dispatcher_destroyed); - buffer.PopEvents(&events); - EXPECT_EQ(2u, events.size()); - EXPECT_EQ(ui::ET_KEY_PRESSED, events[0]->type()); - EXPECT_EQ(ui::VKEY_K, static_cast<ui::KeyEvent*>(events[0])->key_code()); - EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); - EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); - EXPECT_EQ(ui::VKEY_CONTROL, - static_cast<ui::KeyEvent*>(events[1])->key_code()); - - Shell::GetInstance()->RemovePreTargetHandler(&buffer); -} - -class StickyKeysMouseDispatchTest - : public StickyKeysTest, - public ::testing::WithParamInterface<int> { -}; - -TEST_P(StickyKeysMouseDispatchTest, MouseEventDispatchImpl) { - int scale_factor = GetParam(); - std::ostringstream display_specs; - display_specs << "1280x1024*" << scale_factor; - UpdateDisplay(display_specs.str()); - - EventBuffer buffer; - ScopedVector<ui::Event> events; - ui::EventProcessor* dispatcher = - Shell::GetPrimaryRootWindow()->GetHost()->event_processor(); - Shell::GetInstance()->AddPreTargetHandler(&buffer); - Shell::GetInstance()->sticky_keys_controller()->Enable(true); - - scoped_ptr<ui::MouseEvent> ev; - SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL); - buffer.PopEvents(&events); - - // Test mouse press event is correctly modified and has correct DIP location. - gfx::Point physical_location(400, 400); - gfx::Point dip_location(physical_location.x() / scale_factor, - physical_location.y() / scale_factor); - ev.reset(GenerateMouseEventAt(true, physical_location)); - ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get()); - buffer.PopEvents(&events); - EXPECT_EQ(1u, events.size()); - EXPECT_EQ(ui::ET_MOUSE_PRESSED, events[0]->type()); - EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); - EXPECT_EQ(dip_location.ToString(), - static_cast<ui::MouseEvent*>(events[0])->location().ToString()); - - // Test mouse release event is correctly modified and modifier release - // event is sent. The mouse event should have the correct DIP location. - ev.reset(GenerateMouseEventAt(false, physical_location)); - details = dispatcher->OnEventFromSource(ev.get()); - ASSERT_FALSE(details.dispatcher_destroyed); - buffer.PopEvents(&events); - EXPECT_EQ(2u, events.size()); - EXPECT_EQ(ui::ET_MOUSE_RELEASED, events[0]->type()); - EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); - EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); - EXPECT_EQ(dip_location.ToString(), - static_cast<ui::MouseEvent*>(events[0])->location().ToString()); - EXPECT_EQ(ui::VKEY_CONTROL, - static_cast<ui::KeyEvent*>(events[1])->key_code()); - - // Test synthesized mouse events are dispatched correctly. - SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL); - buffer.PopEvents(&events); - ev.reset(GenerateSynthesizedMouseClickEvent(false, physical_location)); - details = dispatcher->OnEventFromSource(ev.get()); - ASSERT_FALSE(details.dispatcher_destroyed); - buffer.PopEvents(&events); - EXPECT_EQ(2u, events.size()); - EXPECT_EQ(ui::ET_MOUSE_RELEASED, events[0]->type()); - EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); - EXPECT_EQ(dip_location.ToString(), - static_cast<ui::MouseEvent*>(events[0])->location().ToString()); - EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); - EXPECT_EQ(ui::VKEY_CONTROL, - static_cast<ui::KeyEvent*>(events[1])->key_code()); - - Shell::GetInstance()->RemovePreTargetHandler(&buffer); -} - -TEST_P(StickyKeysMouseDispatchTest, MouseWheelEventDispatchImpl) { - int scale_factor = GetParam(); - std::ostringstream display_specs; - display_specs << "1280x1024*" << scale_factor; - UpdateDisplay(display_specs.str()); - - // Test the actual mouse wheel event dispatch implementation. - EventBuffer buffer; - ScopedVector<ui::Event> events; - ui::EventProcessor* dispatcher = - Shell::GetPrimaryRootWindow()->GetHost()->event_processor(); - Shell::GetInstance()->AddPreTargetHandler(&buffer); - Shell::GetInstance()->sticky_keys_controller()->Enable(true); - - scoped_ptr<ui::MouseWheelEvent> ev; - SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL); - buffer.PopEvents(&events); - - // Test positive mouse wheel event is correctly modified and modifier release - // event is sent. - ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta)); - ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get()); - ASSERT_FALSE(details.dispatcher_destroyed); - buffer.PopEvents(&events); - EXPECT_EQ(2u, events.size()); - EXPECT_TRUE(events[0]->IsMouseWheelEvent()); - EXPECT_EQ(ui::MouseWheelEvent::kWheelDelta / scale_factor, - static_cast<ui::MouseWheelEvent*>(events[0])->y_offset()); - EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); - EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); - EXPECT_EQ(ui::VKEY_CONTROL, - static_cast<ui::KeyEvent*>(events[1])->key_code()); - - // Test negative mouse wheel event is correctly modified and modifier release - // event is sent. - SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL); - buffer.PopEvents(&events); - - ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta)); - details = dispatcher->OnEventFromSource(ev.get()); - ASSERT_FALSE(details.dispatcher_destroyed); - buffer.PopEvents(&events); - EXPECT_EQ(2u, events.size()); - EXPECT_TRUE(events[0]->IsMouseWheelEvent()); - EXPECT_EQ(-ui::MouseWheelEvent::kWheelDelta / scale_factor, - static_cast<ui::MouseWheelEvent*>(events[0])->y_offset()); - EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); - EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); - EXPECT_EQ(ui::VKEY_CONTROL, - static_cast<ui::KeyEvent*>(events[1])->key_code()); - - // Test synthesized mouse wheel events are dispatched correctly. - SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL); - buffer.PopEvents(&events); - ev.reset( - GenerateSynthesizedMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta)); - details = dispatcher->OnEventFromSource(ev.get()); - ASSERT_FALSE(details.dispatcher_destroyed); - buffer.PopEvents(&events); - EXPECT_EQ(2u, events.size()); - EXPECT_TRUE(events[0]->IsMouseWheelEvent()); - EXPECT_EQ(ui::MouseWheelEvent::kWheelDelta / scale_factor, - static_cast<ui::MouseWheelEvent*>(events[0])->y_offset()); - EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); - EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); - EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); - EXPECT_EQ(ui::VKEY_CONTROL, - static_cast<ui::KeyEvent*>(events[1])->key_code()); - - Shell::GetInstance()->RemovePreTargetHandler(&buffer); -} - -INSTANTIATE_TEST_CASE_P(DPIScaleFactors, - StickyKeysMouseDispatchTest, - ::testing::Values(1, 2)); - } // namespace ash diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc index fa32096..03c8571 100644 --- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc +++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc @@ -730,11 +730,6 @@ void ChromeBrowserMainPartsChromeos::PreBrowserStart() { // Start the CrOS input device UMA watcher DeviceUMA::GetInstance(); #endif - keyboard_event_rewriters_.reset(new EventRewriterController()); - keyboard_event_rewriters_->AddEventRewriter( - scoped_ptr<ui::EventRewriter>(new KeyboardDrivenEventRewriter())); - keyboard_event_rewriters_->AddEventRewriter( - scoped_ptr<ui::EventRewriter>(new EventRewriter())); // -- This used to be in ChromeBrowserMainParts::PreMainMessageLoopRun() // -- immediately after ChildProcess::WaitForDebugger(). @@ -757,7 +752,13 @@ void ChromeBrowserMainPartsChromeos::PostBrowserStart() { // These are dependent on the ash::Shell singleton already having been // initialized. power_button_observer_.reset(new PowerButtonObserver); - data_promo_notification_.reset(new DataPromoNotification()), + data_promo_notification_.reset(new DataPromoNotification()); + + keyboard_event_rewriters_.reset(new EventRewriterController()); + keyboard_event_rewriters_->AddEventRewriter( + scoped_ptr<ui::EventRewriter>(new KeyboardDrivenEventRewriter())); + keyboard_event_rewriters_->AddEventRewriter(scoped_ptr<ui::EventRewriter>( + new EventRewriter(ash::Shell::GetInstance()->sticky_keys_controller()))); keyboard_event_rewriters_->Init(); ChromeBrowserMainPartsLinux::PostBrowserStart(); diff --git a/chrome/browser/chromeos/events/event_rewriter.cc b/chrome/browser/chromeos/events/event_rewriter.cc index eee24f7..d7e0bcb 100644 --- a/chrome/browser/chromeos/events/event_rewriter.cc +++ b/chrome/browser/chromeos/events/event_rewriter.cc @@ -6,10 +6,12 @@ #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" @@ -153,12 +155,24 @@ void UpdateX11EventMask(int ui_flags, unsigned int* x_flags) { } #endif +bool IsSendEvent(const ui::Event& 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 true; +#endif + return false; +} + } // namespace -EventRewriter::EventRewriter() +EventRewriter::EventRewriter(ash::StickyKeysController* sticky_keys_controller) : last_device_id_(kBadDeviceId), ime_keyboard_for_testing_(NULL), - pref_service_for_testing_(NULL) { + pref_service_for_testing_(NULL), + sticky_keys_controller_(sticky_keys_controller) { #if defined(USE_X11) ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this); if (base::SysInfo::IsRunningOnChromeOS()) { @@ -190,35 +204,38 @@ void EventRewriter::RewriteLocatedEventForTesting(const ui::Event& 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: - return RewriteKeyEvent(static_cast<const ui::KeyEvent&>(event), + if ((event.type() == ui::ET_KEY_PRESSED) || + (event.type() == ui::ET_KEY_RELEASED)) { + return RewriteKeyEvent(static_cast<const ui::KeyEvent&>(event), + rewritten_event); + } + if (event.IsMouseEvent()) { + return RewriteMouseEvent(static_cast<const ui::MouseEvent&>(event), rewritten_event); - case ui::ET_MOUSE_PRESSED: - case ui::ET_MOUSE_RELEASED: - return RewriteMouseEvent(static_cast<const ui::MouseEvent&>(event), - rewritten_event); - case ui::ET_TOUCH_PRESSED: - case ui::ET_TOUCH_RELEASED: - return RewriteTouchEvent(static_cast<const ui::TouchEvent&>(event), - rewritten_event); - default: - return ui::EVENT_REWRITE_CONTINUE; } - NOTREACHED(); + 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; } @@ -333,14 +350,33 @@ ui::EventRewriteStatus EventRewriter::RewriteKeyEvent( const ui::KeyEvent& key_event, scoped_ptr<ui::Event>* rewritten_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); + bool is_send_event = IsSendEvent(key_event); + if (!is_send_event) { + RewriteModifierKeys(key_event, &state); + RewriteNumPadKeys(key_event, &state); + } + 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 (!is_send_event) { + RewriteExtendedKeys(key_event, &state); + RewriteFunctionKeys(key_event, &state); + } if ((key_event.flags() == state.flags) && - (key_event.key_code() == state.key_code)) { + (key_event.key_code() == state.key_code) && + (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; ui::KeyEvent* rewritten_key_event = new ui::KeyEvent(key_event); rewritten_event->reset(rewritten_key_event); rewritten_key_event->set_flags(state.flags); @@ -360,16 +396,26 @@ ui::EventRewriteStatus EventRewriter::RewriteKeyEvent( state.key_code, state.flags & ui::EF_SHIFT_DOWN)); } #endif - return ui::EVENT_REWRITE_REWRITTEN; + return status; } ui::EventRewriteStatus EventRewriter::RewriteMouseEvent( const ui::MouseEvent& mouse_event, scoped_ptr<ui::Event>* rewritten_event) { int flags = mouse_event.flags(); - RewriteLocatedEvent(mouse_event, &flags); - if (mouse_event.flags() == flags) + if ((mouse_event.type() == ui::ET_MOUSE_PRESSED) || + (mouse_event.type() == ui::ET_MOUSE_RELEASED)) { + RewriteLocatedEvent(mouse_event, &flags); + } + ui::EventRewriteStatus status = ui::EVENT_REWRITE_CONTINUE; + if (sticky_keys_controller_) + status = sticky_keys_controller_->RewriteMouseEvent(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); @@ -397,7 +443,7 @@ ui::EventRewriteStatus EventRewriter::RewriteMouseEvent( } } #endif - return ui::EVENT_REWRITE_REWRITTEN; + return status; } ui::EventRewriteStatus EventRewriter::RewriteTouchEvent( @@ -424,6 +470,32 @@ ui::EventRewriteStatus EventRewriter::RewriteTouchEvent( 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) + XEvent* xev = rewritten_scroll_event->native_event(); + if (xev) { + XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev->xcookie.data); + if (xievent) { + UpdateX11EventMask( + rewritten_scroll_event->flags(), + reinterpret_cast<unsigned int*>(&xievent->mods.effective)); + } + } +#endif + return status; +} + void EventRewriter::RewriteModifierKeys(const ui::KeyEvent& key_event, MutableKeyState* state) { DCHECK(key_event.type() == ui::ET_KEY_PRESSED || diff --git a/chrome/browser/chromeos/events/event_rewriter.h b/chrome/browser/chromeos/events/event_rewriter.h index 3763a89..1c946c9 100644 --- a/chrome/browser/chromeos/events/event_rewriter.h +++ b/chrome/browser/chromeos/events/event_rewriter.h @@ -23,6 +23,10 @@ typedef union _XEvent XEvent; class PrefService; +namespace ash { +class StickyKeysController; +} + namespace chromeos { namespace input_method { class ImeKeyboard; @@ -51,7 +55,10 @@ class EventRewriter kDeviceAppleKeyboard, }; - EventRewriter(); + // Does not take ownership of the |sticky_keys_controller|, which may also + // be NULL (for testing without ash), in which case sticky key operations + // don't happen. + explicit EventRewriter(ash::StickyKeysController* sticky_keys_controller); virtual ~EventRewriter(); // Calls DeviceAddedInternal. @@ -98,7 +105,7 @@ class EventRewriter #endif private: - // Things that internal rewriter phases can change about an Event. + // Things that keyboard-related rewriter phases can change about an Event. struct MutableKeyState { int flags; ui::KeyboardCode key_code; @@ -157,6 +164,9 @@ class EventRewriter ui::EventRewriteStatus RewriteTouchEvent( const ui::TouchEvent& touch_event, scoped_ptr<ui::Event>* rewritten_event); + ui::EventRewriteStatus RewriteScrollEvent( + const ui::ScrollEvent& scroll_event, + scoped_ptr<ui::Event>* rewritten_event); // Rewriter phases. These can inspect the original |event|, but operate using // the current |state|, which may have been modified by previous phases. @@ -175,6 +185,10 @@ class EventRewriter chromeos::input_method::ImeKeyboard* ime_keyboard_for_testing_; const PrefService* pref_service_for_testing_; + // The sticky keys controller is not owned here; + // at time of writing it is a singleton in ash::Shell> + ash::StickyKeysController* sticky_keys_controller_; + DISALLOW_COPY_AND_ASSIGN(EventRewriter); }; diff --git a/chrome/browser/chromeos/events/event_rewriter_unittest.cc b/chrome/browser/chromeos/events/event_rewriter_unittest.cc index 6b350ee..4b3a244 100644 --- a/chrome/browser/chromeos/events/event_rewriter_unittest.cc +++ b/chrome/browser/chromeos/events/event_rewriter_unittest.cc @@ -4,15 +4,11 @@ #include "chrome/browser/chromeos/events/event_rewriter.h" -#include <X11/keysym.h> -#include <X11/XF86keysym.h> -#include <X11/Xlib.h> -#undef Bool -#undef None -#undef RootWindow - #include <vector> +#include "ash/shell.h" +#include "ash/sticky_keys/sticky_keys_controller.h" +#include "ash/sticky_keys/sticky_keys_overlay.h" #include "ash/test/ash_test_base.h" #include "ash/wm/window_state.h" #include "base/basictypes.h" @@ -30,14 +26,24 @@ #include "chromeos/ime/fake_ime_keyboard.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" #include "ui/events/event.h" #include "ui/events/event_rewriter.h" +#include "ui/events/test/test_event_processor.h" + +#if defined(USE_X11) +#include <X11/keysym.h> + #include "ui/events/test/events_test_utils_x11.h" #include "ui/events/x/touch_factory_x11.h" #include "ui/gfx/x/x11_types.h" +#endif namespace { +// The device id of the test touchpad device. +const unsigned int kTouchPadDeviceId = 1; + std::string GetExpectedResultAsString(ui::KeyboardCode ui_keycode, int ui_flags, ui::EventType ui_type) { @@ -192,8 +198,7 @@ namespace chromeos { class EventRewriterTest : public ash::test::AshTestBase { public: EventRewriterTest() - : display_(gfx::GetXDisplay()), - mock_user_manager_(new chromeos::MockUserManager), + : mock_user_manager_(new chromeos::MockUserManager), user_manager_enabler_(mock_user_manager_), input_method_manager_mock_(NULL) {} virtual ~EventRewriterTest() {} @@ -227,7 +232,6 @@ class EventRewriterTest : public ash::test::AshTestBase { return flags; } - Display* display_; chromeos::MockUserManager* mock_user_manager_; // Not owned. chromeos::ScopedUserManagerEnabler user_manager_enabler_; chromeos::input_method::MockInputMethodManager* input_method_manager_mock_; @@ -236,7 +240,7 @@ class EventRewriterTest : public ash::test::AshTestBase { TEST_F(EventRewriterTest, TestRewriteCommandToControl) { // First, test with a PC keyboard. TestingPrefServiceSyncable prefs; - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.DeviceAddedForTesting(0, "PC Keyboard"); rewriter.set_last_device_id_for_testing(0); rewriter.set_pref_service_for_testing(&prefs); @@ -317,7 +321,7 @@ TEST_F(EventRewriterTest, TestRewriteCommandToControlWithControlRemapped) { control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kAltKey); - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); rewriter.DeviceAddedForTesting(0, "PC Keyboard"); rewriter.set_last_device_id_for_testing(0); @@ -357,7 +361,7 @@ TEST_F(EventRewriterTest, TestRewriteCommandToControlWithControlRemapped) { void EventRewriterTest::TestRewriteNumPadKeys() { TestingPrefServiceSyncable prefs; - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { @@ -499,7 +503,7 @@ TEST_F(EventRewriterTest, TestRewriteNumPadKeysWithDiamondKeyFlag) { // Tests if the rewriter can handle a Command + Num Pad event. void EventRewriterTest::TestRewriteNumPadKeysOnAppleKeyboard() { TestingPrefServiceSyncable prefs; - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.DeviceAddedForTesting(0, "Apple Keyboard"); rewriter.set_last_device_id_for_testing(0); rewriter.set_pref_service_for_testing(&prefs); @@ -541,7 +545,7 @@ TEST_F(EventRewriterTest, TEST_F(EventRewriterTest, TestRewriteModifiersNoRemap) { TestingPrefServiceSyncable prefs; - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { @@ -584,7 +588,7 @@ TEST_F(EventRewriterTest, TestRewriteModifiersNoRemap) { TEST_F(EventRewriterTest, TestRewriteModifiersNoRemapMultipleKeys) { TestingPrefServiceSyncable prefs; - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { @@ -627,7 +631,7 @@ TEST_F(EventRewriterTest, TestRewriteModifiersDisableSome) { control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kVoidKey); - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase disabled_modifier_tests[] = { @@ -702,7 +706,7 @@ TEST_F(EventRewriterTest, TestRewriteModifiersRemapToControl) { search.Init(prefs::kLanguageRemapSearchKeyTo, &prefs); search.SetValue(chromeos::input_method::kControlKey); - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase s_tests[] = { @@ -766,7 +770,7 @@ TEST_F(EventRewriterTest, TestRewriteModifiersRemapToEscape) { search.Init(prefs::kLanguageRemapSearchKeyTo, &prefs); search.SetValue(chromeos::input_method::kEscapeKey); - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = {// Press Search. Confirm the event is now VKEY_ESCAPE. @@ -788,7 +792,7 @@ TEST_F(EventRewriterTest, TestRewriteModifiersRemapMany) { search.Init(prefs::kLanguageRemapSearchKeyTo, &prefs); search.SetValue(chromeos::input_method::kAltKey); - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase s2a_tests[] = { @@ -865,7 +869,7 @@ TEST_F(EventRewriterTest, TestRewriteModifiersRemapToCapsLock) { search.SetValue(chromeos::input_method::kCapsLockKey); chromeos::input_method::FakeImeKeyboard ime_keyboard; - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); rewriter.set_ime_keyboard_for_testing(&ime_keyboard); EXPECT_FALSE(ime_keyboard.caps_lock_is_enabled_); @@ -935,7 +939,7 @@ TEST_F(EventRewriterTest, TestRewriteCapsLock) { chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); chromeos::input_method::FakeImeKeyboard ime_keyboard; - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); rewriter.set_ime_keyboard_for_testing(&ime_keyboard); EXPECT_FALSE(ime_keyboard.caps_lock_is_enabled_); @@ -954,7 +958,7 @@ TEST_F(EventRewriterTest, TestRewriteDiamondKey) { chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); chromeos::input_method::FakeImeKeyboard ime_keyboard; - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); rewriter.set_ime_keyboard_for_testing(&ime_keyboard); @@ -987,7 +991,7 @@ TEST_F(EventRewriterTest, TestRewriteDiamondKeyWithFlag) { chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); chromeos::input_method::FakeImeKeyboard ime_keyboard; - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); rewriter.set_ime_keyboard_for_testing(&ime_keyboard); @@ -1039,7 +1043,7 @@ TEST_F(EventRewriterTest, TestRewriteCapsLockToControl) { control.Init(prefs::kLanguageRemapCapsLockKeyTo, &prefs); control.SetValue(chromeos::input_method::kControlKey); - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { @@ -1075,7 +1079,7 @@ TEST_F(EventRewriterTest, TestRewriteCapsLockMod3InUse) { control.Init(prefs::kLanguageRemapCapsLockKeyTo, &prefs); control.SetValue(chromeos::input_method::kControlKey); - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); input_method_manager_mock_->set_mod3_used(true); @@ -1092,7 +1096,7 @@ TEST_F(EventRewriterTest, TestRewriteCapsLockMod3InUse) { TEST_F(EventRewriterTest, TestRewriteExtendedKeys) { TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.DeviceAddedForTesting(0, "PC Keyboard"); rewriter.set_last_device_id_for_testing(0); rewriter.set_pref_service_for_testing(&prefs); @@ -1201,7 +1205,7 @@ TEST_F(EventRewriterTest, TestRewriteExtendedKeys) { TEST_F(EventRewriterTest, TestRewriteFunctionKeys) { TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); FunctionKeyTestCase tests[] = { @@ -1529,7 +1533,7 @@ TEST_F(EventRewriterTest, TestRewriteExtendedKeysWithSearchRemapped) { search.Init(prefs::kLanguageRemapSearchKeyTo, &prefs); search.SetValue(chromeos::input_method::kControlKey); - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); CommandLine::ForCurrentProcess()->AppendSwitchASCII( @@ -1556,6 +1560,8 @@ TEST_F(EventRewriterTest, TestRewriteExtendedKeysWithSearchRemapped) { } TEST_F(EventRewriterTest, TestRewriteKeyEventSentByXSendEvent) { +#if defined(USE_X11) + // TODO(kpschoedel): pending alternative to xevent.xany.send_event // Remap Control to Alt. TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); @@ -1563,7 +1569,7 @@ TEST_F(EventRewriterTest, TestRewriteKeyEventSentByXSendEvent) { control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kAltKey); - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); // Send left control press. @@ -1582,6 +1588,7 @@ TEST_F(EventRewriterTest, TestRewriteKeyEventSentByXSendEvent) { rewriter.RewriteEvent(keyevent, &new_event)); EXPECT_FALSE(new_event); } +#endif } TEST_F(EventRewriterTest, TestRewriteNonNativeEvent) { @@ -1592,7 +1599,7 @@ TEST_F(EventRewriterTest, TestRewriteNonNativeEvent) { control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kAltKey); - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); const int kTouchId = 2; @@ -1609,11 +1616,60 @@ TEST_F(EventRewriterTest, TestRewriteNonNativeEvent) { new_event->flags() & (ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)); } +// Keeps a buffer of handled events. +class EventBuffer : public ui::test::TestEventProcessor { + public: + EventBuffer() {} + virtual ~EventBuffer() {} + + void PopEvents(ScopedVector<ui::Event>* events) { + events->clear(); + events->swap(events_); + } + + private: + // ui::EventProcessor overrides: + virtual ui::EventDispatchDetails OnEventFromSource( + ui::Event* event) OVERRIDE { + if (event->IsKeyEvent()) { + events_.push_back(new ui::KeyEvent(*static_cast<ui::KeyEvent*>(event))); + } else if (event->IsMouseWheelEvent()) { + events_.push_back( + new ui::MouseWheelEvent(*static_cast<ui::MouseWheelEvent*>(event))); + } else if (event->IsMouseEvent()) { + events_.push_back( + new ui::MouseEvent(*static_cast<ui::MouseEvent*>(event))); + } + return ui::EventDispatchDetails(); + } + + ScopedVector<ui::Event> events_; + + DISALLOW_COPY_AND_ASSIGN(EventBuffer); +}; + +// Trivial EventSource that does nothing but send events. +class TestEventSource : public ui::EventSource { + public: + explicit TestEventSource(ui::EventProcessor* processor) + : processor_(processor) {} + virtual ui::EventProcessor* GetEventProcessor() OVERRIDE { + return processor_; + } + ui::EventDispatchDetails Send(ui::Event* event) { + return SendEventToProcessor(event); + } + + private: + ui::EventProcessor* processor_; +}; + // Tests of event rewriting that depend on the Ash window manager. class EventRewriterAshTest : public ash::test::AshTestBase { public: EventRewriterAshTest() - : mock_user_manager_(new chromeos::MockUserManager), + : source_(&buffer_), + mock_user_manager_(new chromeos::MockUserManager), user_manager_enabler_(mock_user_manager_) {} virtual ~EventRewriterAshTest() {} @@ -1622,12 +1678,40 @@ class EventRewriterAshTest : public ash::test::AshTestBase { return rewriter_->RewriteEvent(event, rewritten_event); } + ui::EventDispatchDetails Send(ui::Event* event) { + return source_.Send(event); + } + + void SendKeyEvent(ui::EventType type, ui::KeyboardCode key_code) { + ui::KeyEvent press(type, key_code, ui::EF_NONE, false); + ui::EventDispatchDetails details = Send(&press); + CHECK(!details.dispatcher_destroyed); + } + + void SendActivateStickyKeyPattern(ui::KeyboardCode key_code) { + SendKeyEvent(ui::ET_KEY_PRESSED, key_code); + SendKeyEvent(ui::ET_KEY_RELEASED, key_code); + } + protected: + TestingPrefServiceSyncable* prefs() { return &prefs_; } + + void PopEvents(ScopedVector<ui::Event>* events) { + buffer_.PopEvents(events); + } + virtual void SetUp() OVERRIDE { AshTestBase::SetUp(); - rewriter_.reset(new EventRewriter()); + sticky_keys_controller_ = + ash::Shell::GetInstance()->sticky_keys_controller(); + rewriter_.reset(new EventRewriter(sticky_keys_controller_)); chromeos::Preferences::RegisterProfilePrefs(prefs_.registry()); rewriter_->set_pref_service_for_testing(&prefs_); +#if defined(USE_X11) + ui::SetUpTouchPadForTest(kTouchPadDeviceId); +#endif + source_.AddEventRewriter(rewriter_.get()); + sticky_keys_controller_->Enable(true); } virtual void TearDown() OVERRIDE { @@ -1635,13 +1719,18 @@ class EventRewriterAshTest : public ash::test::AshTestBase { AshTestBase::TearDown(); } - TestingPrefServiceSyncable prefs_; + protected: + ash::StickyKeysController* sticky_keys_controller_; private: scoped_ptr<EventRewriter> rewriter_; + EventBuffer buffer_; + TestEventSource source_; + chromeos::MockUserManager* mock_user_manager_; // Not owned. chromeos::ScopedUserManagerEnabler user_manager_enabler_; + TestingPrefServiceSyncable prefs_; DISALLOW_COPY_AND_ASSIGN(EventRewriterAshTest); }; @@ -1650,6 +1739,7 @@ TEST_F(EventRewriterAshTest, TopRowKeysAreFunctionKeys) { scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(1)); ash::wm::WindowState* window_state = ash::wm::GetWindowState(window.get()); window_state->Activate(); + ScopedVector<ui::Event> events; // Create a simulated keypress of F1 targetted at the window. ui::KeyEvent press_f1(ui::ET_KEY_PRESSED, ui::VKEY_F1, 0, false); @@ -1657,34 +1747,38 @@ TEST_F(EventRewriterAshTest, TopRowKeysAreFunctionKeys) { // Simulate an apps v2 window that has requested top row keys as function // keys. The event should not be rewritten. window_state->set_top_row_keys_are_function_keys(true); - scoped_ptr<ui::Event> rewritten_event; - ASSERT_FALSE(RewriteFunctionKeys(press_f1, &rewritten_event)); - ASSERT_FALSE(rewritten_event); + ui::EventDispatchDetails details = Send(&press_f1); + ASSERT_FALSE(details.dispatcher_destroyed); + PopEvents(&events); + EXPECT_EQ(1u, events.size()); EXPECT_EQ( GetExpectedResultAsString(ui::VKEY_F1, ui::EF_NONE, ui::ET_KEY_PRESSED), - GetKeyEventAsString(press_f1)); + GetKeyEventAsString(*static_cast<ui::KeyEvent*>(events[0]))); // The event should also not be rewritten if the send-function-keys pref is // additionally set, for both apps v2 and regular windows. BooleanPrefMember send_function_keys_pref; - send_function_keys_pref.Init(prefs::kLanguageSendFunctionKeys, &prefs_); + send_function_keys_pref.Init(prefs::kLanguageSendFunctionKeys, prefs()); send_function_keys_pref.SetValue(true); window_state->set_top_row_keys_are_function_keys(false); - ASSERT_FALSE(RewriteFunctionKeys(press_f1, &rewritten_event)); - ASSERT_FALSE(rewritten_event); + details = Send(&press_f1); + ASSERT_FALSE(details.dispatcher_destroyed); + PopEvents(&events); + EXPECT_EQ(1u, events.size()); EXPECT_EQ( GetExpectedResultAsString(ui::VKEY_F1, ui::EF_NONE, ui::ET_KEY_PRESSED), - GetKeyEventAsString(press_f1)); + GetKeyEventAsString(*static_cast<ui::KeyEvent*>(events[0]))); // If the pref isn't set when an event is sent to a regular window, F1 is // rewritten to the back key. send_function_keys_pref.SetValue(false); - ASSERT_TRUE(RewriteFunctionKeys(press_f1, &rewritten_event)); - ASSERT_TRUE(rewritten_event); + details = Send(&press_f1); + ASSERT_FALSE(details.dispatcher_destroyed); + PopEvents(&events); + EXPECT_EQ(1u, events.size()); EXPECT_EQ(GetExpectedResultAsString( ui::VKEY_BROWSER_BACK, ui::EF_NONE, ui::ET_KEY_PRESSED), - GetKeyEventAsString( - *static_cast<const ui::KeyEvent*>(rewritten_event.get()))); + GetKeyEventAsString(*static_cast<ui::KeyEvent*>(events[0]))); } TEST_F(EventRewriterTest, TestRewrittenModifierClick) { @@ -1699,7 +1793,7 @@ TEST_F(EventRewriterTest, TestRewrittenModifierClick) { control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kAltKey); - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); // Check that Control + Left Button is converted (via Alt + Left Button) @@ -1721,12 +1815,15 @@ TEST_F(EventRewriterTest, TestRewrittenModifierClick) { TEST_F(EventRewriterTest, DontRewriteIfNotRewritten) { +#if defined(USE_X11) + // TODO(kpschoedel): pending changes for crbug.com/360377 + // to |chromeos::EventRewriter::RewriteLocatedEvent() std::vector<unsigned int> device_list; device_list.push_back(10); device_list.push_back(11); ui::TouchFactory::GetInstance()->SetPointerDeviceForTest(device_list); TestingPrefServiceSyncable prefs; - EventRewriter rewriter; + EventRewriter rewriter(NULL); rewriter.set_pref_service_for_testing(&prefs); const int kLeftAndAltFlag = ui::EF_LEFT_MOUSE_BUTTON | ui::EF_ALT_DOWN; { @@ -1792,6 +1889,299 @@ TEST_F(EventRewriterTest, DontRewriteIfNotRewritten) { int flags = RewriteMouseEvent(&rewriter, release); EXPECT_TRUE(ui::EF_RIGHT_MOUSE_BUTTON & flags); } +#endif +} + +TEST_F(EventRewriterAshTest, StickyKeyEventDispatchImpl) { + // Test the actual key event dispatch implementation. + ScopedVector<ui::Event> events; + + SendActivateStickyKeyPattern(ui::VKEY_CONTROL); + PopEvents(&events); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(ui::ET_KEY_PRESSED, events[0]->type()); + EXPECT_EQ(ui::VKEY_CONTROL, + static_cast<ui::KeyEvent*>(events[0])->key_code()); + + // Test key press event is correctly modified and modifier release + // event is sent. + ui::KeyEvent press(ui::ET_KEY_PRESSED, ui::VKEY_C, ui::EF_NONE, false); + ui::EventDispatchDetails details = Send(&press); + PopEvents(&events); + EXPECT_EQ(2u, events.size()); + EXPECT_EQ(ui::ET_KEY_PRESSED, events[0]->type()); + EXPECT_EQ(ui::VKEY_C, static_cast<ui::KeyEvent*>(events[0])->key_code()); + EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); + EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); + EXPECT_EQ(ui::VKEY_CONTROL, + static_cast<ui::KeyEvent*>(events[1])->key_code()); + + // Test key release event is not modified. + ui::KeyEvent release(ui::ET_KEY_RELEASED, ui::VKEY_C, ui::EF_NONE, false); + details = Send(&release); + ASSERT_FALSE(details.dispatcher_destroyed); + PopEvents(&events); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(ui::ET_KEY_RELEASED, events[0]->type()); + EXPECT_EQ(ui::VKEY_C, static_cast<ui::KeyEvent*>(events[0])->key_code()); + EXPECT_FALSE(events[0]->flags() & ui::EF_CONTROL_DOWN); +} + +TEST_F(EventRewriterAshTest, MouseEventDispatchImpl) { + ScopedVector<ui::Event> events; + + SendActivateStickyKeyPattern(ui::VKEY_CONTROL); + PopEvents(&events); + + // Test mouse press event is correctly modified. + gfx::Point location(0, 0); + ui::MouseEvent press(ui::ET_MOUSE_PRESSED, + location, + location, + ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); + ui::EventDispatchDetails details = Send(&press); + ASSERT_FALSE(details.dispatcher_destroyed); + PopEvents(&events); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(ui::ET_MOUSE_PRESSED, events[0]->type()); + EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); + + // Test mouse release event is correctly modified and modifier release + // event is sent. The mouse event should have the correct DIP location. + ui::MouseEvent release(ui::ET_MOUSE_RELEASED, + location, + location, + ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); + details = Send(&release); + ASSERT_FALSE(details.dispatcher_destroyed); + PopEvents(&events); + EXPECT_EQ(2u, events.size()); + EXPECT_EQ(ui::ET_MOUSE_RELEASED, events[0]->type()); + EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); + EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); + EXPECT_EQ(ui::VKEY_CONTROL, + static_cast<ui::KeyEvent*>(events[1])->key_code()); +} + +TEST_F(EventRewriterAshTest, MouseWheelEventDispatchImpl) { + ScopedVector<ui::Event> events; + + // Test positive mouse wheel event is correctly modified and modifier release + // event is sent. + SendActivateStickyKeyPattern(ui::VKEY_CONTROL); + PopEvents(&events); + gfx::Point location(0, 0); + ui::MouseEvent mev(ui::ET_MOUSEWHEEL, + location, + location, + ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); + ui::MouseWheelEvent positive(mev, 0, ui::MouseWheelEvent::kWheelDelta); + ui::EventDispatchDetails details = Send(&positive); + ASSERT_FALSE(details.dispatcher_destroyed); + PopEvents(&events); + EXPECT_EQ(2u, events.size()); + EXPECT_TRUE(events[0]->IsMouseWheelEvent()); + EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); + EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); + EXPECT_EQ(ui::VKEY_CONTROL, + static_cast<ui::KeyEvent*>(events[1])->key_code()); + + // Test negative mouse wheel event is correctly modified and modifier release + // event is sent. + SendActivateStickyKeyPattern(ui::VKEY_CONTROL); + PopEvents(&events); + ui::MouseWheelEvent negative(mev, 0, -ui::MouseWheelEvent::kWheelDelta); + details = Send(&negative); + ASSERT_FALSE(details.dispatcher_destroyed); + PopEvents(&events); + EXPECT_EQ(2u, events.size()); + EXPECT_TRUE(events[0]->IsMouseWheelEvent()); + EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); + EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); + EXPECT_EQ(ui::VKEY_CONTROL, + static_cast<ui::KeyEvent*>(events[1])->key_code()); +} + +class StickyKeysOverlayTest : public EventRewriterAshTest { + public: + StickyKeysOverlayTest() : overlay_(NULL) {} + + virtual ~StickyKeysOverlayTest() {} + + virtual void SetUp() OVERRIDE { + EventRewriterAshTest::SetUp(); + overlay_ = sticky_keys_controller_->GetOverlayForTest(); + ASSERT_TRUE(overlay_); + } + + ash::StickyKeysOverlay* overlay_; +}; + +TEST_F(StickyKeysOverlayTest, OneModifierEnabled) { + EXPECT_FALSE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); + + // Pressing modifier key should show overlay. + SendActivateStickyKeyPattern(ui::VKEY_CONTROL); + EXPECT_TRUE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_ENABLED, + overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); + + // Pressing a normal key should hide overlay. + SendActivateStickyKeyPattern(ui::VKEY_T); + EXPECT_FALSE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); +} + +TEST_F(StickyKeysOverlayTest, TwoModifiersEnabled) { + EXPECT_FALSE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); + + // Pressing two modifiers should show overlay. + SendActivateStickyKeyPattern(ui::VKEY_SHIFT); + SendActivateStickyKeyPattern(ui::VKEY_CONTROL); + EXPECT_TRUE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_ENABLED, + overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); + EXPECT_EQ(ash::STICKY_KEY_STATE_ENABLED, + overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); + + // Pressing a normal key should hide overlay. + SendActivateStickyKeyPattern(ui::VKEY_N); + EXPECT_FALSE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); +} + +TEST_F(StickyKeysOverlayTest, LockedModifier) { + EXPECT_FALSE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); + + // Pressing a modifier key twice should lock modifier and show overlay. + SendActivateStickyKeyPattern(ui::VKEY_LMENU); + SendActivateStickyKeyPattern(ui::VKEY_LMENU); + EXPECT_TRUE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED, + overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); + + // Pressing a normal key should not hide overlay. + SendActivateStickyKeyPattern(ui::VKEY_D); + EXPECT_TRUE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED, + overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); +} + +TEST_F(StickyKeysOverlayTest, LockedAndNormalModifier) { + EXPECT_FALSE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); + + // Pressing a modifier key twice should lock modifier and show overlay. + SendActivateStickyKeyPattern(ui::VKEY_CONTROL); + SendActivateStickyKeyPattern(ui::VKEY_CONTROL); + EXPECT_TRUE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED, + overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); + + // Pressing another modifier key should still show overlay. + SendActivateStickyKeyPattern(ui::VKEY_SHIFT); + EXPECT_TRUE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED, + overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); + EXPECT_EQ(ash::STICKY_KEY_STATE_ENABLED, + overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); + + // Pressing a normal key should not hide overlay but disable normal modifier. + SendActivateStickyKeyPattern(ui::VKEY_D); + EXPECT_TRUE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED, + overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); +} + +TEST_F(StickyKeysOverlayTest, ModifiersDisabled) { + EXPECT_FALSE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); + + // Enable modifiers. + SendActivateStickyKeyPattern(ui::VKEY_CONTROL); + SendActivateStickyKeyPattern(ui::VKEY_SHIFT); + SendActivateStickyKeyPattern(ui::VKEY_SHIFT); + SendActivateStickyKeyPattern(ui::VKEY_LMENU); + + EXPECT_TRUE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_ENABLED, + overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); + EXPECT_EQ(ash::STICKY_KEY_STATE_LOCKED, + overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); + EXPECT_EQ(ash::STICKY_KEY_STATE_ENABLED, + overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); + + // Disable modifiers and overlay should be hidden. + SendActivateStickyKeyPattern(ui::VKEY_CONTROL); + SendActivateStickyKeyPattern(ui::VKEY_CONTROL); + SendActivateStickyKeyPattern(ui::VKEY_SHIFT); + SendActivateStickyKeyPattern(ui::VKEY_LMENU); + SendActivateStickyKeyPattern(ui::VKEY_LMENU); + + EXPECT_FALSE(overlay_->is_visible()); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN)); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN)); + EXPECT_EQ(ash::STICKY_KEY_STATE_DISABLED, + overlay_->GetModifierKeyState(ui::EF_ALT_DOWN)); +} + +TEST_F(StickyKeysOverlayTest, ModifierVisibility) { + // All but AltGr and Mod3 should initially be visible. + EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_CONTROL_DOWN)); + EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_SHIFT_DOWN)); + EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_ALT_DOWN)); + EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_ALTGR_DOWN)); + EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_MOD3_DOWN)); + + // Turn all modifiers on. + sticky_keys_controller_->SetModifiersEnabled(true, true); + EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_CONTROL_DOWN)); + EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_SHIFT_DOWN)); + EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_ALT_DOWN)); + EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_ALTGR_DOWN)); + EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_MOD3_DOWN)); + + // Turn off Mod3. + sticky_keys_controller_->SetModifiersEnabled(false, true); + EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_ALTGR_DOWN)); + EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_MOD3_DOWN)); + + // Turn off AltGr. + sticky_keys_controller_->SetModifiersEnabled(true, false); + EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_ALTGR_DOWN)); + EXPECT_TRUE(overlay_->GetModifierVisible(ui::EF_MOD3_DOWN)); + + // Turn off AltGr and Mod3. + sticky_keys_controller_->SetModifiersEnabled(false, false); + EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_ALTGR_DOWN)); + EXPECT_FALSE(overlay_->GetModifierVisible(ui::EF_MOD3_DOWN)); } } // namespace chromeos |