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 | |
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
-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, 1239 insertions, 1125 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 diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc index 03c8571..fa32096 100644 --- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc +++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc @@ -730,6 +730,11 @@ 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(). @@ -752,13 +757,7 @@ 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()); - - 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()))); + data_promo_notification_.reset(new DataPromoNotification()), 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 d7e0bcb..eee24f7 100644 --- a/chrome/browser/chromeos/events/event_rewriter.cc +++ b/chrome/browser/chromeos/events/event_rewriter.cc @@ -6,12 +6,10 @@ #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" @@ -155,24 +153,12 @@ 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(ash::StickyKeysController* sticky_keys_controller) +EventRewriter::EventRewriter() : last_device_id_(kBadDeviceId), ime_keyboard_for_testing_(NULL), - pref_service_for_testing_(NULL), - sticky_keys_controller_(sticky_keys_controller) { + pref_service_for_testing_(NULL) { #if defined(USE_X11) ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this); if (base::SysInfo::IsRunningOnChromeOS()) { @@ -204,38 +190,35 @@ void EventRewriter::RewriteLocatedEventForTesting(const ui::Event& event, ui::EventRewriteStatus EventRewriter::RewriteEvent( const ui::Event& event, scoped_ptr<ui::Event>* rewritten_event) { - if ((event.type() == ui::ET_KEY_PRESSED) || - (event.type() == ui::ET_KEY_RELEASED)) { - return RewriteKeyEvent(static_cast<const ui::KeyEvent&>(event), - rewritten_event); - } - if (event.IsMouseEvent()) { - return RewriteMouseEvent(static_cast<const ui::MouseEvent&>(event), - rewritten_event); - } - if ((event.type() == ui::ET_TOUCH_PRESSED) || - (event.type() == ui::ET_TOUCH_RELEASED)) { - return RewriteTouchEvent(static_cast<const ui::TouchEvent&>(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), 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; } - if (event.IsScrollEvent()) { - return RewriteScrollEvent(static_cast<const ui::ScrollEvent&>(event), - rewritten_event); - } - return ui::EVENT_REWRITE_CONTINUE; + NOTREACHED(); } 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; } @@ -350,33 +333,14 @@ ui::EventRewriteStatus EventRewriter::RewriteKeyEvent( const ui::KeyEvent& key_event, scoped_ptr<ui::Event>* rewritten_event) { MutableKeyState state = {key_event.flags(), key_event.key_code()}; - 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); - } + RewriteModifierKeys(key_event, &state); + RewriteNumPadKeys(key_event, &state); + RewriteExtendedKeys(key_event, &state); + RewriteFunctionKeys(key_event, &state); if ((key_event.flags() == state.flags) && - (key_event.key_code() == state.key_code) && - (status == ui::EVENT_REWRITE_CONTINUE)) { + (key_event.key_code() == state.key_code)) { 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); @@ -396,26 +360,16 @@ ui::EventRewriteStatus EventRewriter::RewriteKeyEvent( state.key_code, state.flags & ui::EF_SHIFT_DOWN)); } #endif - return status; + return ui::EVENT_REWRITE_REWRITTEN; } ui::EventRewriteStatus EventRewriter::RewriteMouseEvent( const ui::MouseEvent& mouse_event, scoped_ptr<ui::Event>* rewritten_event) { int flags = mouse_event.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)) { + RewriteLocatedEvent(mouse_event, &flags); + if (mouse_event.flags() == flags) 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); @@ -443,7 +397,7 @@ ui::EventRewriteStatus EventRewriter::RewriteMouseEvent( } } #endif - return status; + return ui::EVENT_REWRITE_REWRITTEN; } ui::EventRewriteStatus EventRewriter::RewriteTouchEvent( @@ -470,32 +424,6 @@ 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 1c946c9..3763a89 100644 --- a/chrome/browser/chromeos/events/event_rewriter.h +++ b/chrome/browser/chromeos/events/event_rewriter.h @@ -23,10 +23,6 @@ typedef union _XEvent XEvent; class PrefService; -namespace ash { -class StickyKeysController; -} - namespace chromeos { namespace input_method { class ImeKeyboard; @@ -55,10 +51,7 @@ class EventRewriter kDeviceAppleKeyboard, }; - // 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); + EventRewriter(); virtual ~EventRewriter(); // Calls DeviceAddedInternal. @@ -105,7 +98,7 @@ class EventRewriter #endif private: - // Things that keyboard-related rewriter phases can change about an Event. + // Things that internal rewriter phases can change about an Event. struct MutableKeyState { int flags; ui::KeyboardCode key_code; @@ -164,9 +157,6 @@ 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. @@ -185,10 +175,6 @@ 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 4b3a244..6b350ee 100644 --- a/chrome/browser/chromeos/events/event_rewriter_unittest.cc +++ b/chrome/browser/chromeos/events/event_rewriter_unittest.cc @@ -4,11 +4,15 @@ #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" @@ -26,24 +30,14 @@ #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) { @@ -198,7 +192,8 @@ namespace chromeos { class EventRewriterTest : public ash::test::AshTestBase { public: EventRewriterTest() - : mock_user_manager_(new chromeos::MockUserManager), + : display_(gfx::GetXDisplay()), + mock_user_manager_(new chromeos::MockUserManager), user_manager_enabler_(mock_user_manager_), input_method_manager_mock_(NULL) {} virtual ~EventRewriterTest() {} @@ -232,6 +227,7 @@ 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_; @@ -240,7 +236,7 @@ class EventRewriterTest : public ash::test::AshTestBase { TEST_F(EventRewriterTest, TestRewriteCommandToControl) { // First, test with a PC keyboard. TestingPrefServiceSyncable prefs; - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.DeviceAddedForTesting(0, "PC Keyboard"); rewriter.set_last_device_id_for_testing(0); rewriter.set_pref_service_for_testing(&prefs); @@ -321,7 +317,7 @@ TEST_F(EventRewriterTest, TestRewriteCommandToControlWithControlRemapped) { control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kAltKey); - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); rewriter.DeviceAddedForTesting(0, "PC Keyboard"); rewriter.set_last_device_id_for_testing(0); @@ -361,7 +357,7 @@ TEST_F(EventRewriterTest, TestRewriteCommandToControlWithControlRemapped) { void EventRewriterTest::TestRewriteNumPadKeys() { TestingPrefServiceSyncable prefs; - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { @@ -503,7 +499,7 @@ TEST_F(EventRewriterTest, TestRewriteNumPadKeysWithDiamondKeyFlag) { // Tests if the rewriter can handle a Command + Num Pad event. void EventRewriterTest::TestRewriteNumPadKeysOnAppleKeyboard() { TestingPrefServiceSyncable prefs; - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.DeviceAddedForTesting(0, "Apple Keyboard"); rewriter.set_last_device_id_for_testing(0); rewriter.set_pref_service_for_testing(&prefs); @@ -545,7 +541,7 @@ TEST_F(EventRewriterTest, TEST_F(EventRewriterTest, TestRewriteModifiersNoRemap) { TestingPrefServiceSyncable prefs; - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { @@ -588,7 +584,7 @@ TEST_F(EventRewriterTest, TestRewriteModifiersNoRemap) { TEST_F(EventRewriterTest, TestRewriteModifiersNoRemapMultipleKeys) { TestingPrefServiceSyncable prefs; - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { @@ -631,7 +627,7 @@ TEST_F(EventRewriterTest, TestRewriteModifiersDisableSome) { control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kVoidKey); - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); KeyTestCase disabled_modifier_tests[] = { @@ -706,7 +702,7 @@ TEST_F(EventRewriterTest, TestRewriteModifiersRemapToControl) { search.Init(prefs::kLanguageRemapSearchKeyTo, &prefs); search.SetValue(chromeos::input_method::kControlKey); - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); KeyTestCase s_tests[] = { @@ -770,7 +766,7 @@ TEST_F(EventRewriterTest, TestRewriteModifiersRemapToEscape) { search.Init(prefs::kLanguageRemapSearchKeyTo, &prefs); search.SetValue(chromeos::input_method::kEscapeKey); - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = {// Press Search. Confirm the event is now VKEY_ESCAPE. @@ -792,7 +788,7 @@ TEST_F(EventRewriterTest, TestRewriteModifiersRemapMany) { search.Init(prefs::kLanguageRemapSearchKeyTo, &prefs); search.SetValue(chromeos::input_method::kAltKey); - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); KeyTestCase s2a_tests[] = { @@ -869,7 +865,7 @@ TEST_F(EventRewriterTest, TestRewriteModifiersRemapToCapsLock) { search.SetValue(chromeos::input_method::kCapsLockKey); chromeos::input_method::FakeImeKeyboard ime_keyboard; - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); rewriter.set_ime_keyboard_for_testing(&ime_keyboard); EXPECT_FALSE(ime_keyboard.caps_lock_is_enabled_); @@ -939,7 +935,7 @@ TEST_F(EventRewriterTest, TestRewriteCapsLock) { chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); chromeos::input_method::FakeImeKeyboard ime_keyboard; - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); rewriter.set_ime_keyboard_for_testing(&ime_keyboard); EXPECT_FALSE(ime_keyboard.caps_lock_is_enabled_); @@ -958,7 +954,7 @@ TEST_F(EventRewriterTest, TestRewriteDiamondKey) { chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); chromeos::input_method::FakeImeKeyboard ime_keyboard; - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); rewriter.set_ime_keyboard_for_testing(&ime_keyboard); @@ -991,7 +987,7 @@ TEST_F(EventRewriterTest, TestRewriteDiamondKeyWithFlag) { chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); chromeos::input_method::FakeImeKeyboard ime_keyboard; - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); rewriter.set_ime_keyboard_for_testing(&ime_keyboard); @@ -1043,7 +1039,7 @@ TEST_F(EventRewriterTest, TestRewriteCapsLockToControl) { control.Init(prefs::kLanguageRemapCapsLockKeyTo, &prefs); control.SetValue(chromeos::input_method::kControlKey); - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); KeyTestCase tests[] = { @@ -1079,7 +1075,7 @@ TEST_F(EventRewriterTest, TestRewriteCapsLockMod3InUse) { control.Init(prefs::kLanguageRemapCapsLockKeyTo, &prefs); control.SetValue(chromeos::input_method::kControlKey); - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); input_method_manager_mock_->set_mod3_used(true); @@ -1096,7 +1092,7 @@ TEST_F(EventRewriterTest, TestRewriteCapsLockMod3InUse) { TEST_F(EventRewriterTest, TestRewriteExtendedKeys) { TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.DeviceAddedForTesting(0, "PC Keyboard"); rewriter.set_last_device_id_for_testing(0); rewriter.set_pref_service_for_testing(&prefs); @@ -1205,7 +1201,7 @@ TEST_F(EventRewriterTest, TestRewriteExtendedKeys) { TEST_F(EventRewriterTest, TestRewriteFunctionKeys) { TestingPrefServiceSyncable prefs; chromeos::Preferences::RegisterProfilePrefs(prefs.registry()); - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); FunctionKeyTestCase tests[] = { @@ -1533,7 +1529,7 @@ TEST_F(EventRewriterTest, TestRewriteExtendedKeysWithSearchRemapped) { search.Init(prefs::kLanguageRemapSearchKeyTo, &prefs); search.SetValue(chromeos::input_method::kControlKey); - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); CommandLine::ForCurrentProcess()->AppendSwitchASCII( @@ -1560,8 +1556,6 @@ 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()); @@ -1569,7 +1563,7 @@ TEST_F(EventRewriterTest, TestRewriteKeyEventSentByXSendEvent) { control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kAltKey); - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); // Send left control press. @@ -1588,7 +1582,6 @@ TEST_F(EventRewriterTest, TestRewriteKeyEventSentByXSendEvent) { rewriter.RewriteEvent(keyevent, &new_event)); EXPECT_FALSE(new_event); } -#endif } TEST_F(EventRewriterTest, TestRewriteNonNativeEvent) { @@ -1599,7 +1592,7 @@ TEST_F(EventRewriterTest, TestRewriteNonNativeEvent) { control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kAltKey); - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); const int kTouchId = 2; @@ -1616,60 +1609,11 @@ 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() - : source_(&buffer_), - mock_user_manager_(new chromeos::MockUserManager), + : mock_user_manager_(new chromeos::MockUserManager), user_manager_enabler_(mock_user_manager_) {} virtual ~EventRewriterAshTest() {} @@ -1678,40 +1622,12 @@ 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(); - sticky_keys_controller_ = - ash::Shell::GetInstance()->sticky_keys_controller(); - rewriter_.reset(new EventRewriter(sticky_keys_controller_)); + rewriter_.reset(new EventRewriter()); 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 { @@ -1719,18 +1635,13 @@ class EventRewriterAshTest : public ash::test::AshTestBase { AshTestBase::TearDown(); } - protected: - ash::StickyKeysController* sticky_keys_controller_; + TestingPrefServiceSyncable prefs_; 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); }; @@ -1739,7 +1650,6 @@ 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); @@ -1747,38 +1657,34 @@ 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); - ui::EventDispatchDetails details = Send(&press_f1); - ASSERT_FALSE(details.dispatcher_destroyed); - PopEvents(&events); - EXPECT_EQ(1u, events.size()); + scoped_ptr<ui::Event> rewritten_event; + ASSERT_FALSE(RewriteFunctionKeys(press_f1, &rewritten_event)); + ASSERT_FALSE(rewritten_event); EXPECT_EQ( GetExpectedResultAsString(ui::VKEY_F1, ui::EF_NONE, ui::ET_KEY_PRESSED), - GetKeyEventAsString(*static_cast<ui::KeyEvent*>(events[0]))); + GetKeyEventAsString(press_f1)); // 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); - details = Send(&press_f1); - ASSERT_FALSE(details.dispatcher_destroyed); - PopEvents(&events); - EXPECT_EQ(1u, events.size()); + ASSERT_FALSE(RewriteFunctionKeys(press_f1, &rewritten_event)); + ASSERT_FALSE(rewritten_event); EXPECT_EQ( GetExpectedResultAsString(ui::VKEY_F1, ui::EF_NONE, ui::ET_KEY_PRESSED), - GetKeyEventAsString(*static_cast<ui::KeyEvent*>(events[0]))); + GetKeyEventAsString(press_f1)); // 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); - details = Send(&press_f1); - ASSERT_FALSE(details.dispatcher_destroyed); - PopEvents(&events); - EXPECT_EQ(1u, events.size()); + ASSERT_TRUE(RewriteFunctionKeys(press_f1, &rewritten_event)); + ASSERT_TRUE(rewritten_event); EXPECT_EQ(GetExpectedResultAsString( ui::VKEY_BROWSER_BACK, ui::EF_NONE, ui::ET_KEY_PRESSED), - GetKeyEventAsString(*static_cast<ui::KeyEvent*>(events[0]))); + GetKeyEventAsString( + *static_cast<const ui::KeyEvent*>(rewritten_event.get()))); } TEST_F(EventRewriterTest, TestRewrittenModifierClick) { @@ -1793,7 +1699,7 @@ TEST_F(EventRewriterTest, TestRewrittenModifierClick) { control.Init(prefs::kLanguageRemapControlKeyTo, &prefs); control.SetValue(chromeos::input_method::kAltKey); - EventRewriter rewriter(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); // Check that Control + Left Button is converted (via Alt + Left Button) @@ -1815,15 +1721,12 @@ 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(NULL); + EventRewriter rewriter; rewriter.set_pref_service_for_testing(&prefs); const int kLeftAndAltFlag = ui::EF_LEFT_MOUSE_BUTTON | ui::EF_ALT_DOWN; { @@ -1889,299 +1792,6 @@ 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 |