diff options
author | oshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-19 17:58:51 +0000 |
---|---|---|
committer | oshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-19 17:58:51 +0000 |
commit | 0bb0c5a32a4348037c515372c5aa4b4bd5c0893f (patch) | |
tree | 09dd59ad4214ad83818d7ac2587d8530fdc131fd /ash | |
parent | bfcaff5da257f13f967c50162958b2a7be7cf2d6 (diff) | |
download | chromium_src-0bb0c5a32a4348037c515372c5aa4b4bd5c0893f.zip chromium_src-0bb0c5a32a4348037c515372c5aa4b4bd5c0893f.tar.gz chromium_src-0bb0c5a32a4348037c515372c5aa4b4bd5c0893f.tar.bz2 |
Revert 278342 "Convert sticky keys to a chromeos::EventRewriter ..."
> Convert sticky keys to a chromeos::EventRewriter phase.
>
> BUG=354035
> TEST=unit_tests,ash_unittests,manual
> R=sadrul@chromium.org,derat@chromium.org
>
> Review URL: https://codereview.chromium.org/255033003
TBR=kpschoedel@chromium.org
Review URL: https://codereview.chromium.org/341923006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278419 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-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 |
5 files changed, 1150 insertions, 559 deletions
diff --git a/ash/shell.cc b/ash/shell.cc index 2c39306..c8b3279 100644 --- a/ash/shell.cc +++ b/ash/shell.cc @@ -889,6 +889,13 @@ 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); @@ -923,10 +930,6 @@ 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 6237004..53be1fc 100644 --- a/ash/sticky_keys/sticky_keys_controller.cc +++ b/ash/sticky_keys/sticky_keys_controller.cc @@ -4,6 +4,12 @@ #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" @@ -19,26 +25,79 @@ namespace ash { namespace { // Returns true if the type of mouse event should be modified by sticky keys. -bool ShouldModifyMouseEvent(const ui::MouseEvent& event) { - ui::EventType type = event.type(); +bool ShouldModifyMouseEvent(ui::MouseEvent* event) { + ui::EventType type = event->type(); return type == ui::ET_MOUSE_PRESSED || type == ui::ET_MOUSE_RELEASED || type == ui::ET_MOUSEWHEEL; } -// 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; +// 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; } } // namespace @@ -61,11 +120,21 @@ 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)); - 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)); + 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())); overlay_.reset(new StickyKeysOverlay()); overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_); @@ -86,103 +155,56 @@ void StickyKeysController::SetModifiersEnabled(bool mod3_enabled, } } -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; +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(); + } } void StickyKeysController::UpdateOverlay() { @@ -213,64 +235,66 @@ StickyKeysOverlay* StickyKeysController::GetOverlayForTest() { /////////////////////////////////////////////////////////////////////////////// // StickyKeysHandler -StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag) +StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag, + StickyKeysHandlerDelegate* delegate) : modifier_flag_(modifier_flag), current_state_(STICKY_KEY_STATE_DISABLED), + event_from_myself_(false), preparing_to_enable_(false), - scroll_delta_(0) { + scroll_delta_(0), + delegate_(delegate) { } StickyKeysHandler::~StickyKeysHandler() { } -bool StickyKeysHandler::HandleKeyEvent(const ui::KeyEvent& event, - ui::KeyboardCode key_code, - int* mod_down_flags, - bool* released) { +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. switch (current_state_) { case STICKY_KEY_STATE_DISABLED: - return HandleDisabledState(event, key_code); + return HandleDisabledState(event); case STICKY_KEY_STATE_ENABLED: - return HandleEnabledState(event, key_code, mod_down_flags, released); + return HandleEnabledState(event); case STICKY_KEY_STATE_LOCKED: - return HandleLockedState(event, key_code, mod_down_flags, released); + return HandleLockedState(event); } NOTREACHED(); return false; } -bool StickyKeysHandler::HandleMouseEvent( - const ui::MouseEvent& event, - int* mod_down_flags, - bool* released) { +bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent* event) { if (ShouldModifyMouseEvent(event)) preparing_to_enable_ = false; - if (current_state_ == STICKY_KEY_STATE_DISABLED || - !ShouldModifyMouseEvent(event)) { + if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED + || !ShouldModifyMouseEvent(event)) { return false; } DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED || current_state_ == STICKY_KEY_STATE_LOCKED); - *mod_down_flags |= modifier_flag_; + AppendModifier(event); // 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; - *released = true; - return false; + DispatchEventAndReleaseModifier(event); + return true; } return false; } -bool StickyKeysHandler::HandleScrollEvent( - const ui::ScrollEvent& event, - int* mod_down_flags, - bool* released) { +bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) { preparing_to_enable_ = false; - if (current_state_ == STICKY_KEY_STATE_DISABLED) + if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED) return false; DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED || current_state_ == STICKY_KEY_STATE_LOCKED); @@ -279,82 +303,70 @@ bool StickyKeysHandler::HandleScrollEvent( // 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) - *mod_down_flags |= modifier_flag_; + AppendModifier(event); // 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; - *released = true; - return false; + DispatchEventAndReleaseModifier(event); + return true; } return false; } -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) { +StickyKeysHandler::KeyEventType + StickyKeysHandler::TranslateKeyEvent(ui::KeyEvent* event) { bool is_target_key = false; - if (key_code == ui::VKEY_SHIFT || - key_code == ui::VKEY_LSHIFT || - key_code == ui::VKEY_RSHIFT) { + if (event->key_code() == ui::VKEY_SHIFT || + event->key_code() == ui::VKEY_LSHIFT || + event->key_code() == ui::VKEY_RSHIFT) { is_target_key = (modifier_flag_ == ui::EF_SHIFT_DOWN); - } else if (key_code == ui::VKEY_CONTROL || - key_code == ui::VKEY_LCONTROL || - key_code == ui::VKEY_RCONTROL) { + } else if (event->key_code() == ui::VKEY_CONTROL || + event->key_code() == ui::VKEY_LCONTROL || + event->key_code() == ui::VKEY_RCONTROL) { is_target_key = (modifier_flag_ == ui::EF_CONTROL_DOWN); - } else if (key_code == ui::VKEY_MENU || - key_code == ui::VKEY_LMENU || - key_code == ui::VKEY_RMENU) { + } else if (event->key_code() == ui::VKEY_MENU || + event->key_code() == ui::VKEY_LMENU || + event->key_code() == ui::VKEY_RMENU) { is_target_key = (modifier_flag_ == ui::EF_ALT_DOWN); - } else if (key_code == ui::VKEY_ALTGR) { + } else if (event->key_code() == ui::VKEY_ALTGR) { is_target_key = (modifier_flag_ == ui::EF_ALTGR_DOWN); - } else if (key_code == ui::VKEY_OEM_8) { + } else if (event->key_code() == ui::VKEY_OEM_8) { is_target_key = (modifier_flag_ == ui::EF_MOD3_DOWN); } else { - return type == ui::ET_KEY_PRESSED ? + return event->type() == ui::ET_KEY_PRESSED ? NORMAL_KEY_DOWN : NORMAL_KEY_UP; } if (is_target_key) { - return type == ui::ET_KEY_PRESSED ? + return event->type() == ui::ET_KEY_PRESSED ? TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP; } - return type == ui::ET_KEY_PRESSED ? + return event->type() == ui::ET_KEY_PRESSED ? OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP; } -bool StickyKeysHandler::HandleDisabledState(const ui::KeyEvent& event, - ui::KeyboardCode key_code) { - switch (TranslateKeyEvent(event.type(), key_code)) { +bool StickyKeysHandler::HandleDisabledState(ui::KeyEvent* event) { + switch (TranslateKeyEvent(event)) { 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; @@ -373,23 +385,20 @@ bool StickyKeysHandler::HandleDisabledState(const ui::KeyEvent& event, return false; } -bool StickyKeysHandler::HandleEnabledState(const ui::KeyEvent& event, - ui::KeyboardCode key_code, - int* mod_down_flags, - bool* released) { - switch (TranslateKeyEvent(event.type(), key_code)) { +bool StickyKeysHandler::HandleEnabledState(ui::KeyEvent* event) { + switch (TranslateKeyEvent(event)) { case NORMAL_KEY_UP: case TARGET_MODIFIER_DOWN: - return false; + return true; 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; - *mod_down_flags |= modifier_flag_; - *released = true; - return false; + AppendModifier(event); + DispatchEventAndReleaseModifier(event); + return true; } case OTHER_MODIFIER_DOWN: case OTHER_MODIFIER_UP: @@ -399,11 +408,8 @@ bool StickyKeysHandler::HandleEnabledState(const ui::KeyEvent& event, return false; } -bool StickyKeysHandler::HandleLockedState(const ui::KeyEvent& event, - ui::KeyboardCode key_code, - int* mod_down_flags, - bool* released) { - switch (TranslateKeyEvent(event.type(), key_code)) { +bool StickyKeysHandler::HandleLockedState(ui::KeyEvent* event) { + switch (TranslateKeyEvent(event)) { case TARGET_MODIFIER_DOWN: return true; case TARGET_MODIFIER_UP: @@ -411,7 +417,7 @@ bool StickyKeysHandler::HandleLockedState(const ui::KeyEvent& event, return false; case NORMAL_KEY_DOWN: case NORMAL_KEY_UP: - *mod_down_flags |= modifier_flag_; + AppendModifier(event); return false; case OTHER_MODIFIER_DOWN: case OTHER_MODIFIER_UP: @@ -421,4 +427,125 @@ bool StickyKeysHandler::HandleLockedState(const 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 fd5d389..89a196a 100644 --- a/ash/sticky_keys/sticky_keys_controller.h +++ b/ash/sticky_keys/sticky_keys_controller.h @@ -10,8 +10,6 @@ #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; @@ -61,7 +59,7 @@ class StickyKeysHandler; // modifiers. Each handling or state is performed independently. // // StickyKeysController is disabled by default. -class ASH_EXPORT StickyKeysController { +class ASH_EXPORT StickyKeysController : public ui::EventHandler { public: StickyKeysController(); virtual ~StickyKeysController(); @@ -71,64 +69,24 @@ class ASH_EXPORT StickyKeysController { 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. - // 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); + 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); // Updates the overlay UI with the current state of the sticky keys. void UpdateOverlay(); @@ -185,35 +143,40 @@ class ASH_EXPORT StickyKeysController { // is modified. class ASH_EXPORT StickyKeysHandler { public: - explicit StickyKeysHandler(ui::EventFlags modifier_flag); - ~StickyKeysHandler(); + 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; + }; - // 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); + // This class takes an ownership of |delegate|. + StickyKeysHandler(ui::EventFlags modifier_flag, + StickyKeysHandlerDelegate* delegate); + ~StickyKeysHandler(); - // 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 key event. Returns true if key is consumed. + bool HandleKeyEvent(ui::KeyEvent* event); - // 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 mouse event. Returns true if mouse event is consumed. + bool HandleMouseEvent(ui::MouseEvent* 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); + // Handles a scroll event. Returns true if scroll event is consumed. + bool HandleScrollEvent(ui::ScrollEvent* event); // Returns current internal state. StickyKeyState current_state() const { return current_state_; } @@ -229,27 +192,31 @@ class ASH_EXPORT StickyKeysHandler { OTHER_MODIFIER_UP, // The modifier key but not monitored key is up. }; - // Translates event type and key code to sticky keys event type. - KeyEventType TranslateKeyEvent(ui::EventType type, ui::KeyboardCode key_code); + // Translates |event| to sticky keys event type. + KeyEventType TranslateKeyEvent(ui::KeyEvent* event); + + // Handles key event in DISABLED state. + bool HandleDisabledState(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 ENABLED state. + bool HandleEnabledState(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); + // Handles key event in LOCKED state. + bool HandleLockedState(ui::KeyEvent* 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); + // 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); // The modifier flag to be monitored and appended to events. const ui::EventFlags modifier_flag_; @@ -257,6 +224,9 @@ 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 @@ -271,6 +241,8 @@ 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 0d6f161..678eff4 100644 --- a/ash/sticky_keys/sticky_keys_overlay_unittest.cc +++ b/ash/sticky_keys/sticky_keys_overlay_unittest.cc @@ -7,14 +7,47 @@ #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() {} + StickyKeysOverlayTest() : + controller_(NULL), + overlay_(NULL) {} + 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) { @@ -38,7 +71,168 @@ TEST_F(StickyKeysOverlayTest, ModifierKeyState) { overlay.GetModifierKeyState(ui::EF_CONTROL_DOWN)); } -// Additional sticky key overlay tests that depend on chromeos::EventRewriter -// are now in chrome/browser/chromeos/events/event_rewriter_unittest.cc . +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)); +} } // namespace ash diff --git a/ash/sticky_keys/sticky_keys_unittest.cc b/ash/sticky_keys/sticky_keys_unittest.cc index c164923..4c505e1 100644 --- a/ash/sticky_keys/sticky_keys_unittest.cc +++ b/ash/sticky_keys/sticky_keys_unittest.cc @@ -16,7 +16,8 @@ #include "base/memory/scoped_vector.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" -#include "ui/events/event_source.h" +#include "ui/events/event_handler.h" +#include "ui/events/event_processor.h" #include "ui/events/test/events_test_utils_x11.h" #include "ui/events/x/device_data_manager.h" @@ -29,7 +30,107 @@ const unsigned int kTouchPadDeviceId = 1; } // namespace -class StickyKeysTest : public test::AshTestBase { +// 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 { protected: StickyKeysTest() : target_(NULL), @@ -50,35 +151,47 @@ class StickyKeysTest : public test::AshTestBase { test::AshTestBase::TearDown(); } - virtual void OnShortcutPressed() { + // Overridden from MockStickyKeysHandlerDelegate::Delegate: + virtual aura::Window* GetExpectedTarget() OVERRIDE { + return target_ ? target_ : root_window_; + } + + virtual void OnShortcutPressed() OVERRIDE { if (target_) { delete target_; target_ = NULL; } } - ui::KeyEvent* GenerateKey(ui::EventType type, ui::KeyboardCode code) { - scoped_xevent_.InitKeyEvent(type, code, 0); - ui::KeyEvent* event = new ui::KeyEvent(scoped_xevent_, false); + 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_); 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(ui::EventType type) { - return GenerateMouseEventAt(type, gfx::Point()); + ui::MouseEvent* GenerateMouseEvent(bool is_button_press) { + return GenerateMouseEventAt(is_button_press, gfx::Point()); } // Creates a mouse event backed by a native XInput2 generic button event. // The |location| should be in physical pixels. - ui::MouseEvent* GenerateMouseEventAt(ui::EventType type, + ui::MouseEvent* GenerateMouseEventAt(bool is_button_press, const gfx::Point& location) { scoped_xevent_.InitGenericButtonEvent( kTouchPadDeviceId, - type, + is_button_press ? ui::ET_MOUSE_PRESSED : ui::ET_MOUSE_RELEASED, location, 0); ui::MouseEvent* event = new ui::MouseEvent(scoped_xevent_); + ui::Event::DispatcherApi dispatcher(event); + dispatcher.set_target(target_); return event; } @@ -121,9 +234,14 @@ class StickyKeysTest : public test::AshTestBase { } // Creates a synthesized KeyEvent that is not backed by a native event. - ui::KeyEvent* GenerateSynthesizedKeyEvent(ui::EventType type, - ui::KeyboardCode code) { - return new ui::KeyEvent(type, code, 0, true); + 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; } // Creates a synthesized MouseEvent that is not backed by a native event. @@ -141,9 +259,11 @@ class StickyKeysTest : public test::AshTestBase { // Creates a synthesized mouse press or release event. ui::MouseEvent* GenerateSynthesizedMouseClickEvent( - ui::EventType type, + bool is_button_press, const gfx::Point& location) { - return GenerateSynthesizedMouseEventAt(type, location); + return GenerateSynthesizedMouseEventAt( + is_button_press ? ui::ET_MOUSE_PRESSED : ui::ET_MOUSE_RELEASED, + location); } // Creates a synthesized ET_MOUSE_MOVED event. @@ -164,28 +284,22 @@ 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(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); - } - - bool HandleKeyEvent(const ui::KeyEvent& key_event, - StickyKeysHandler* handler, - int* down, - bool* up) { - return handler->HandleKeyEvent(key_event, key_event.key_code(), down, up); + ev.reset(GenerateKey(true, key_code)); + handler->HandleKeyEvent(ev.get()); + ev.reset(GenerateKey(false, key_code)); + handler->HandleKeyEvent(ev.get()); } - 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; + 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); } aura::Window* target() { return target_; } @@ -204,7 +318,9 @@ class StickyKeysTest : public test::AshTestBase { TEST_F(StickyKeysTest, BasicOneshotScenarioTest) { scoped_ptr<ui::KeyEvent> ev; - StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN); + MockStickyKeysHandlerDelegate* mock_delegate = + new MockStickyKeysHandlerDelegate(this); + StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); @@ -212,42 +328,42 @@ TEST_F(StickyKeysTest, BasicOneshotScenarioTest) { SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); - 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); + ev.reset(GenerateKey(true, ui::VKEY_A)); + sticky_key.HandleKeyEvent(ev.get()); + // Next keyboard event is shift modified. - EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN); - // Modifier release notification happens. - EXPECT_TRUE(released); + EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); - 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); + ev.reset(GenerateKey(false, ui::VKEY_A)); + sticky_key.HandleKeyEvent(ev.get()); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); - // 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()); + // 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()); EXPECT_EQ(ui::VKEY_SHIFT, - static_cast<const ui::KeyEvent*>(up_event.get())->key_code()); + static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1)) + ->key_code()); // Enabled state is one shot, so next key event should not be shift modified. - 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(true, 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); + ev.reset(GenerateKey(false, ui::VKEY_A)); + sticky_key.HandleKeyEvent(ev.get()); + EXPECT_FALSE(ev->flags() & ui::EF_SHIFT_DOWN); } TEST_F(StickyKeysTest, BasicLockedScenarioTest) { scoped_ptr<ui::KeyEvent> ev; - StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN); + MockStickyKeysHandlerDelegate* mock_delegate = + new MockStickyKeysHandlerDelegate(this); + StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); @@ -260,24 +376,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(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(true, 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); + ev.reset(GenerateKey(false, ui::VKEY_A)); + sticky_key.HandleKeyEvent(ev.get()); + EXPECT_TRUE(ev->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(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(true, 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); + ev.reset(GenerateKey(false, ui::VKEY_B)); + sticky_key.HandleKeyEvent(ev.get()); + EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); @@ -288,163 +404,146 @@ TEST_F(StickyKeysTest, BasicLockedScenarioTest) { TEST_F(StickyKeysTest, NonTargetModifierTest) { scoped_ptr<ui::KeyEvent> ev; - StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN); + MockStickyKeysHandlerDelegate* mock_delegate = + new MockStickyKeysHandlerDelegate(this); + StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); // Non target modifier key does not affect internal state - ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_MENU)); - int mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + ev.reset(GenerateKey(true, ui::VKEY_MENU)); + sticky_key.HandleKeyEvent(ev.get()); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); - EXPECT_EQ(ui::EF_NONE, mod_down_flags); - ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_MENU)); - mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + ev.reset(GenerateKey(false, ui::VKEY_MENU)); + sticky_key.HandleKeyEvent(ev.get()); 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(ui::ET_KEY_PRESSED, ui::VKEY_MENU)); - mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + ev.reset(GenerateKey(true, ui::VKEY_MENU)); + sticky_key.HandleKeyEvent(ev.get()); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); - EXPECT_EQ(ui::EF_NONE, mod_down_flags); - ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_MENU)); - mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + ev.reset(GenerateKey(false, ui::VKEY_MENU)); + sticky_key.HandleKeyEvent(ev.get()); 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(ui::ET_KEY_PRESSED, ui::VKEY_MENU)); - mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + ev.reset(GenerateKey(true, ui::VKEY_MENU)); + sticky_key.HandleKeyEvent(ev.get()); EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); - EXPECT_EQ(ui::EF_NONE, mod_down_flags); - ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_MENU)); - mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); + ev.reset(GenerateKey(false, ui::VKEY_MENU)); + sticky_key.HandleKeyEvent(ev.get()); 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; - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); + MockStickyKeysHandlerDelegate* mock_delegate = + new MockStickyKeysHandlerDelegate(this); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); // Perform ctrl+n shortcut. - 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); + 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()); 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; - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); + MockStickyKeysHandlerDelegate* mock_delegate = + new MockStickyKeysHandlerDelegate(this); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); // Perform ctrl+click. - 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); + 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()); // Sticky keys should not be enabled afterwards. - kev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL)); - mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); + kev.reset(GenerateKey(false, ui::VKEY_CONTROL)); + sticky_key.HandleKeyEvent(kev.get()); 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; - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); + MockStickyKeysHandlerDelegate* mock_delegate = + new MockStickyKeysHandlerDelegate(this); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); // Press ctrl and handle mouse move events. - kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL)); - int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); + kev.reset(GenerateKey(true, ui::VKEY_CONTROL)); + sticky_key.HandleKeyEvent(kev.get()); mev.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(0, 0))); - bool released = false; - sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released); + sticky_key.HandleMouseEvent(mev.get()); mev.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(100, 100))); - sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released); + sticky_key.HandleMouseEvent(mev.get()); // Sticky keys should be enabled afterwards. - kev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL)); - mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); + kev.reset(GenerateKey(false, ui::VKEY_CONTROL)); + sticky_key.HandleKeyEvent(kev.get()); 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; - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); + MockStickyKeysHandlerDelegate* mock_delegate = + new MockStickyKeysHandlerDelegate(this); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); // Perform ctrl+scroll. - kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL)); - int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); + kev.reset(GenerateKey(true, ui::VKEY_CONTROL)); sev.reset(GenerateFlingScrollEvent(0, true)); - bool released = false; - sticky_key.HandleScrollEvent(*sev.get(), &mod_down_flags, &released); + sticky_key.HandleScrollEvent(sev.get()); sev.reset(GenerateScrollEvent(10)); - sticky_key.HandleScrollEvent(*sev.get(), &mod_down_flags, &released); + sticky_key.HandleScrollEvent(sev.get()); sev.reset(GenerateFlingScrollEvent(10, false)); - sticky_key.HandleScrollEvent(*sev.get(), &mod_down_flags, &released); + sticky_key.HandleScrollEvent(sev.get()); // Sticky keys should not be enabled afterwards. - kev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL)); - mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); + kev.reset(GenerateKey(false, ui::VKEY_CONTROL)); + sticky_key.HandleKeyEvent(kev.get()); 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; - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); + MockStickyKeysHandlerDelegate* mock_delegate = + new MockStickyKeysHandlerDelegate(this); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); @@ -452,47 +551,40 @@ TEST_F(StickyKeysTest, MouseEventOneshot) { // We should still be in the ENABLED state until we get the mouse // release event. - 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); + ev.reset(GenerateMouseEvent(true)); + sticky_key.HandleMouseEvent(ev.get()); + EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); - 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); + ev.reset(GenerateMouseEvent(false)); + sticky_key.HandleMouseEvent(ev.get()); + EXPECT_TRUE(ev->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. - 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()); + 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_EQ(ui::VKEY_CONTROL, - static_cast<const ui::KeyEvent*>(up_event.get())->key_code()); + static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1)) + ->key_code()); // Enabled state is one shot, so next click should not be control modified. - 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); + 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); } TEST_F(StickyKeysTest, MouseEventLocked) { scoped_ptr<ui::MouseEvent> ev; scoped_ptr<ui::KeyEvent> kev; - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); + MockStickyKeysHandlerDelegate* mock_delegate = + new MockStickyKeysHandlerDelegate(this); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); @@ -504,44 +596,35 @@ TEST_F(StickyKeysTest, MouseEventLocked) { // Mouse events should not disable locked mode. for (int i = 0; i < 3; ++i) { - 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); + 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); 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(), &mod_down_flags, &released); + sticky_key.HandleMouseEvent(ev.get()); ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta)); - released = false; - mod_down_flags = 0; - sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); - EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); + sticky_key.HandleMouseEvent(ev.get()); + EXPECT_TRUE(ev->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)); - 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); + 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); EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); } @@ -549,10 +632,14 @@ TEST_F(StickyKeysTest, MouseEventLocked) { TEST_F(StickyKeysTest, ScrollEventOneshot) { scoped_ptr<ui::ScrollEvent> ev; scoped_ptr<ui::KeyEvent> kev; - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); + MockStickyKeysHandlerDelegate* mock_delegate = + new MockStickyKeysHandlerDelegate(this); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); 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); @@ -561,44 +648,42 @@ 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)); - 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); + sticky_key.HandleScrollEvent(ev.get()); + EXPECT_TRUE(ev->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])); - released = false; - mod_down_flags = 0; - sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); - EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); + sticky_key.HandleScrollEvent(ev.get()); + EXPECT_TRUE(ev->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)); - released = false; - mod_down_flags = 0; - sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); - EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); + sticky_key.HandleScrollEvent(ev.get()); + EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); - 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()); + 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()); EXPECT_EQ(ui::VKEY_CONTROL, - static_cast<const ui::KeyEvent*>(up_event.get())->key_code()); + static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1)) + ->key_code()); } } TEST_F(StickyKeysTest, ScrollDirectionChanged) { scoped_ptr<ui::ScrollEvent> ev; scoped_ptr<ui::KeyEvent> kev; - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); + MockStickyKeysHandlerDelegate* mock_delegate = + new MockStickyKeysHandlerDelegate(this); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); // Test direction change with both boundary value and negative value. const int direction_change_values[2] = {0, -10}; @@ -608,26 +693,21 @@ TEST_F(StickyKeysTest, ScrollDirectionChanged) { // Fling cancel starts scroll sequence. ev.reset(GenerateFlingScrollEvent(0, true)); - bool released = false; - int mod_down_flags = 0; - sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); + sticky_key.HandleScrollEvent(ev.get()); 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)); - released = false; - mod_down_flags = 0; - sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); - EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); + sticky_key.HandleScrollEvent(ev.get()); + EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); } ev.reset(GenerateScrollEvent(direction_change_values[i])); - released = false; - mod_down_flags = 0; - sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); + sticky_key.HandleScrollEvent(ev.get()); + EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); } } @@ -635,7 +715,9 @@ TEST_F(StickyKeysTest, ScrollDirectionChanged) { TEST_F(StickyKeysTest, ScrollEventLocked) { scoped_ptr<ui::ScrollEvent> ev; scoped_ptr<ui::KeyEvent> kev; - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); + MockStickyKeysHandlerDelegate* mock_delegate = + new MockStickyKeysHandlerDelegate(this); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); // Lock sticky keys. SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); @@ -646,45 +728,63 @@ TEST_F(StickyKeysTest, ScrollEventLocked) { for (int i = 0; i < 5; ++i) { // Fling cancel starts scroll sequence. ev.reset(GenerateFlingScrollEvent(0, true)); - bool released = false; - int mod_down_flags = 0; - sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); + sticky_key.HandleScrollEvent(ev.get()); ev.reset(GenerateScrollEvent(10)); - released = false; - mod_down_flags = 0; - sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); - EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); + sticky_key.HandleScrollEvent(ev.get()); + EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); ev.reset(GenerateScrollEvent(-10)); - sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); - EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); + sticky_key.HandleScrollEvent(ev.get()); + EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); // Fling start ends scroll sequence. ev.reset(GenerateFlingScrollEvent(-10, false)); - sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); + sticky_key.HandleScrollEvent(ev.get()); } 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. - StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); + MockStickyKeysHandlerDelegate* mock_delegate = + new MockStickyKeysHandlerDelegate(this); + StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); // 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(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); + kev.reset(GenerateSynthesizedKeyEvent(true, ui::VKEY_K)); + sticky_key.HandleKeyEvent(kev.get()); + EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); - 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); + kev.reset(GenerateSynthesizedKeyEvent(false, ui::VKEY_K)); + sticky_key.HandleKeyEvent(kev.get()); + EXPECT_FALSE(kev->flags() & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); // Test non-native mouse events. @@ -692,21 +792,216 @@ TEST_F(StickyKeysTest, SynthesizedEvents) { EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); scoped_ptr<ui::MouseEvent> mev; - 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); + mev.reset(GenerateSynthesizedMouseClickEvent(true, gfx::Point(0, 0))); + sticky_key.HandleMouseEvent(mev.get()); + EXPECT_TRUE(mev->flags() & ui::EF_CONTROL_DOWN); EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); - 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); + mev.reset(GenerateSynthesizedMouseClickEvent(false, gfx::Point(0, 0))); + sticky_key.HandleMouseEvent(mev.get()); + EXPECT_TRUE(mev->flags() & ui::EF_CONTROL_DOWN); 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 |