1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ash/sticky_keys/sticky_keys_controller.h"
12 #include "ash/shell.h"
13 #include "ash/test/ash_test_base.h"
14 #include "base/bind.h"
15 #include "base/callback.h"
16 #include "base/memory/scoped_vector.h"
17 #include "ui/aura/window.h"
18 #include "ui/aura/window_tree_host.h"
19 #include "ui/events/event_handler.h"
20 #include "ui/events/event_processor.h"
21 #include "ui/events/test/events_test_utils_x11.h"
22 #include "ui/events/x/device_data_manager.h"
28 // The device id of the test touchpad device.
29 const unsigned int kTouchPadDeviceId = 1;
33 // Keeps a buffer of handled events.
34 class EventBuffer : public ui::EventHandler {
37 virtual ~EventBuffer() {}
39 void PopEvents(ScopedVector<ui::Event>* events) {
41 events->swap(events_);
45 // ui::EventHandler overrides:
46 virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE {
47 events_.push_back(new ui::KeyEvent(*event));
50 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
51 if (event->IsMouseWheelEvent()) {
53 new ui::MouseWheelEvent(*static_cast<ui::MouseWheelEvent*>(event)));
55 events_.push_back(new ui::MouseEvent(*event));
59 ScopedVector<ui::Event> events_;
61 DISALLOW_COPY_AND_ASSIGN(EventBuffer);
64 // A testable and StickyKeysHandler.
65 class MockStickyKeysHandlerDelegate :
66 public StickyKeysHandler::StickyKeysHandlerDelegate {
70 virtual aura::Window* GetExpectedTarget() = 0;
71 virtual void OnShortcutPressed() = 0;
74 virtual ~Delegate() {}
77 MockStickyKeysHandlerDelegate(Delegate* delegate) : delegate_(delegate) {}
79 virtual ~MockStickyKeysHandlerDelegate() {}
81 // StickyKeysHandler override.
82 virtual void DispatchKeyEvent(ui::KeyEvent* event,
83 aura::Window* target) OVERRIDE {
84 ASSERT_EQ(delegate_->GetExpectedTarget(), target);
86 // Detect a special shortcut when it is dispatched. This shortcut will
87 // not be hit in the LOCKED state as this case does not involve the
89 if (event->type() == ui::ET_KEY_PRESSED &&
90 event->key_code() == ui::VKEY_J &&
91 event->flags() | ui::EF_CONTROL_DOWN) {
92 delegate_->OnShortcutPressed();
95 events_.push_back(new ui::KeyEvent(*event));
98 virtual void DispatchMouseEvent(ui::MouseEvent* event,
99 aura::Window* target) OVERRIDE {
100 ASSERT_EQ(delegate_->GetExpectedTarget(), target);
102 new ui::MouseEvent(*event, target, target->GetRootWindow()));
105 virtual void DispatchScrollEvent(ui::ScrollEvent* event,
106 aura::Window* target) OVERRIDE {
107 events_.push_back(new ui::ScrollEvent(event->native_event()));
110 // Returns the count of dispatched events.
111 size_t GetEventCount() const {
112 return events_.size();
115 // Returns the |index|-th dispatched event.
116 const ui::Event* GetEvent(size_t index) const {
117 return events_[index];
120 // Clears all previously dispatched events.
126 ScopedVector<ui::Event> events_;
129 DISALLOW_COPY_AND_ASSIGN(MockStickyKeysHandlerDelegate);
132 class StickyKeysTest : public test::AshTestBase,
133 public MockStickyKeysHandlerDelegate::Delegate {
137 root_window_(NULL) {}
139 virtual void SetUp() OVERRIDE {
140 test::AshTestBase::SetUp();
142 // |target_| owned by root window of shell. It is still safe to delete
144 target_ = CreateTestWindowInShellWithId(0);
145 root_window_ = target_->GetRootWindow();
147 ui::SetUpTouchPadForTest(kTouchPadDeviceId);
150 virtual void TearDown() OVERRIDE {
151 test::AshTestBase::TearDown();
154 // Overridden from MockStickyKeysHandlerDelegate::Delegate:
155 virtual aura::Window* GetExpectedTarget() OVERRIDE {
156 return target_ ? target_ : root_window_;
159 virtual void OnShortcutPressed() OVERRIDE {
166 ui::KeyEvent* GenerateKey(bool is_key_press, ui::KeyboardCode code) {
167 scoped_xevent_.InitKeyEvent(
168 is_key_press ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED,
171 ui::KeyEvent* event = new ui::KeyEvent(scoped_xevent_, false);
172 ui::Event::DispatcherApi dispatcher(event);
173 dispatcher.set_target(target_);
177 // Creates a mouse event backed by a native XInput2 generic button event.
178 // This is the standard native event on Chromebooks.
179 ui::MouseEvent* GenerateMouseEvent(bool is_button_press) {
180 return GenerateMouseEventAt(is_button_press, gfx::Point());
183 // Creates a mouse event backed by a native XInput2 generic button event.
184 // The |location| should be in physical pixels.
185 ui::MouseEvent* GenerateMouseEventAt(bool is_button_press,
186 const gfx::Point& location) {
187 scoped_xevent_.InitGenericButtonEvent(
189 is_button_press ? ui::ET_MOUSE_PRESSED : ui::ET_MOUSE_RELEASED,
192 ui::MouseEvent* event = new ui::MouseEvent(scoped_xevent_);
193 ui::Event::DispatcherApi dispatcher(event);
194 dispatcher.set_target(target_);
198 ui::MouseWheelEvent* GenerateMouseWheelEvent(int wheel_delta) {
199 EXPECT_NE(0, wheel_delta);
200 scoped_xevent_.InitGenericMouseWheelEvent(
201 kTouchPadDeviceId, wheel_delta, 0);
202 ui::MouseWheelEvent* event = new ui::MouseWheelEvent(scoped_xevent_);
203 ui::Event::DispatcherApi dispatcher(event);
204 dispatcher.set_target(target_);
208 ui::ScrollEvent* GenerateScrollEvent(int scroll_delta) {
209 scoped_xevent_.InitScrollEvent(kTouchPadDeviceId, // deviceid
211 scroll_delta, // y_offset
212 0, // x_offset_ordinal
213 scroll_delta, // y_offset_ordinal
215 ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_);
216 ui::Event::DispatcherApi dispatcher(event);
217 dispatcher.set_target(target_);
221 ui::ScrollEvent* GenerateFlingScrollEvent(int fling_delta,
223 scoped_xevent_.InitFlingScrollEvent(
224 kTouchPadDeviceId, // deviceid
226 fling_delta, // y_velocity
227 0, // x_velocity_ordinal
228 fling_delta, // y_velocity_ordinal
229 is_cancel); // is_cancel
230 ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_);
231 ui::Event::DispatcherApi dispatcher(event);
232 dispatcher.set_target(target_);
236 // Creates a synthesized KeyEvent that is not backed by a native event.
237 ui::KeyEvent* GenerateSynthesizedKeyEvent(
238 bool is_key_press, ui::KeyboardCode code) {
239 ui::KeyEvent* event = new ui::KeyEvent(
240 is_key_press ? ui::ET_KEY_PRESSED : ui::ET_MOUSE_RELEASED,
242 ui::Event::DispatcherApi dispatcher(event);
243 dispatcher.set_target(target_);
247 // Creates a synthesized MouseEvent that is not backed by a native event.
248 ui::MouseEvent* GenerateSynthesizedMouseEventAt(ui::EventType event_type,
249 const gfx::Point& location) {
250 ui::MouseEvent* event = new ui::MouseEvent(event_type,
253 ui::EF_LEFT_MOUSE_BUTTON,
254 ui::EF_LEFT_MOUSE_BUTTON);
255 ui::Event::DispatcherApi dispatcher(event);
256 dispatcher.set_target(target_);
260 // Creates a synthesized mouse press or release event.
261 ui::MouseEvent* GenerateSynthesizedMouseClickEvent(
262 bool is_button_press,
263 const gfx::Point& location) {
264 return GenerateSynthesizedMouseEventAt(
265 is_button_press ? ui::ET_MOUSE_PRESSED : ui::ET_MOUSE_RELEASED,
269 // Creates a synthesized ET_MOUSE_MOVED event.
270 ui::MouseEvent* GenerateSynthesizedMouseMoveEvent(
271 const gfx::Point& location) {
272 return GenerateSynthesizedMouseEventAt(ui::ET_MOUSE_MOVED, location);
275 // Creates a synthesized MouseWHeel event.
276 ui::MouseWheelEvent* GenerateSynthesizedMouseWheelEvent(int wheel_delta) {
277 scoped_ptr<ui::MouseEvent> mev(
278 GenerateSynthesizedMouseEventAt(ui::ET_MOUSEWHEEL, gfx::Point(0, 0)));
279 ui::MouseWheelEvent* event = new ui::MouseWheelEvent(*mev, 0, wheel_delta);
280 ui::Event::DispatcherApi dispatcher(event);
281 dispatcher.set_target(target_);
285 void SendActivateStickyKeyPattern(StickyKeysHandler* handler,
286 ui::KeyboardCode key_code) {
287 scoped_ptr<ui::KeyEvent> ev;
288 ev.reset(GenerateKey(true, key_code));
289 handler->HandleKeyEvent(ev.get());
290 ev.reset(GenerateKey(false, key_code));
291 handler->HandleKeyEvent(ev.get());
294 void SendActivateStickyKeyPattern(ui::EventProcessor* dispatcher,
295 ui::KeyboardCode key_code) {
296 scoped_ptr<ui::KeyEvent> ev;
297 ev.reset(GenerateKey(true, key_code));
298 ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get());
299 CHECK(!details.dispatcher_destroyed);
300 ev.reset(GenerateKey(false, key_code));
301 details = dispatcher->OnEventFromSource(ev.get());
302 CHECK(!details.dispatcher_destroyed);
305 aura::Window* target() { return target_; }
308 // Owned by root window of shell, but we can still delete |target_| safely.
309 aura::Window* target_;
310 // The root window of |target_|. Not owned.
311 aura::Window* root_window_;
313 // Used to construct the various X events.
314 ui::ScopedXI2Event scoped_xevent_;
316 DISALLOW_COPY_AND_ASSIGN(StickyKeysTest);
319 TEST_F(StickyKeysTest, BasicOneshotScenarioTest) {
320 scoped_ptr<ui::KeyEvent> ev;
321 MockStickyKeysHandlerDelegate* mock_delegate =
322 new MockStickyKeysHandlerDelegate(this);
323 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate);
325 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
327 // By typing Shift key, internal state become ENABLED.
328 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
329 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
331 ev.reset(GenerateKey(true, ui::VKEY_A));
332 sticky_key.HandleKeyEvent(ev.get());
334 // Next keyboard event is shift modified.
335 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN);
337 ev.reset(GenerateKey(false, ui::VKEY_A));
338 sticky_key.HandleKeyEvent(ev.get());
340 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
341 // Making sure Shift up keyboard event is dispatched.
342 ASSERT_EQ(2U, mock_delegate->GetEventCount());
343 EXPECT_EQ(ui::ET_KEY_PRESSED, mock_delegate->GetEvent(0)->type());
344 EXPECT_EQ(ui::VKEY_A,
345 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(0))
347 EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type());
348 EXPECT_EQ(ui::VKEY_SHIFT,
349 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1))
352 // Enabled state is one shot, so next key event should not be shift modified.
353 ev.reset(GenerateKey(true, ui::VKEY_A));
354 sticky_key.HandleKeyEvent(ev.get());
355 EXPECT_FALSE(ev->flags() & ui::EF_SHIFT_DOWN);
357 ev.reset(GenerateKey(false, ui::VKEY_A));
358 sticky_key.HandleKeyEvent(ev.get());
359 EXPECT_FALSE(ev->flags() & ui::EF_SHIFT_DOWN);
362 TEST_F(StickyKeysTest, BasicLockedScenarioTest) {
363 scoped_ptr<ui::KeyEvent> ev;
364 MockStickyKeysHandlerDelegate* mock_delegate =
365 new MockStickyKeysHandlerDelegate(this);
366 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate);
368 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
370 // By typing shift key, internal state become ENABLED.
371 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
372 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
374 // By typing shift key again, internal state become LOCKED.
375 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
376 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
378 // All keyboard events including keyUp become shift modified.
379 ev.reset(GenerateKey(true, ui::VKEY_A));
380 sticky_key.HandleKeyEvent(ev.get());
381 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN);
383 ev.reset(GenerateKey(false, ui::VKEY_A));
384 sticky_key.HandleKeyEvent(ev.get());
385 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN);
387 // Locked state keeps after normal keyboard event.
388 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
390 ev.reset(GenerateKey(true, ui::VKEY_B));
391 sticky_key.HandleKeyEvent(ev.get());
392 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN);
394 ev.reset(GenerateKey(false, ui::VKEY_B));
395 sticky_key.HandleKeyEvent(ev.get());
396 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN);
398 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
400 // By typing shift key again, internal state become back to DISABLED.
401 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
402 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
405 TEST_F(StickyKeysTest, NonTargetModifierTest) {
406 scoped_ptr<ui::KeyEvent> ev;
407 MockStickyKeysHandlerDelegate* mock_delegate =
408 new MockStickyKeysHandlerDelegate(this);
409 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate);
411 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
413 // Non target modifier key does not affect internal state
414 ev.reset(GenerateKey(true, ui::VKEY_MENU));
415 sticky_key.HandleKeyEvent(ev.get());
416 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
418 ev.reset(GenerateKey(false, ui::VKEY_MENU));
419 sticky_key.HandleKeyEvent(ev.get());
420 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
422 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
423 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
425 // Non target modifier key does not affect internal state
426 ev.reset(GenerateKey(true, ui::VKEY_MENU));
427 sticky_key.HandleKeyEvent(ev.get());
428 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
430 ev.reset(GenerateKey(false, ui::VKEY_MENU));
431 sticky_key.HandleKeyEvent(ev.get());
432 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
434 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
435 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
437 // Non target modifier key does not affect internal state
438 ev.reset(GenerateKey(true, ui::VKEY_MENU));
439 sticky_key.HandleKeyEvent(ev.get());
440 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
442 ev.reset(GenerateKey(false, ui::VKEY_MENU));
443 sticky_key.HandleKeyEvent(ev.get());
444 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
447 TEST_F(StickyKeysTest, NormalShortcutTest) {
448 // Sticky keys should not be enabled if we perform a normal shortcut.
449 scoped_ptr<ui::KeyEvent> ev;
450 MockStickyKeysHandlerDelegate* mock_delegate =
451 new MockStickyKeysHandlerDelegate(this);
452 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
454 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
456 // Perform ctrl+n shortcut.
457 ev.reset(GenerateKey(true, ui::VKEY_CONTROL));
458 sticky_key.HandleKeyEvent(ev.get());
459 ev.reset(GenerateKey(true, ui::VKEY_N));
460 sticky_key.HandleKeyEvent(ev.get());
461 ev.reset(GenerateKey(false, ui::VKEY_N));
462 sticky_key.HandleKeyEvent(ev.get());
463 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
465 // Sticky keys should not be enabled afterwards.
466 ev.reset(GenerateKey(false, ui::VKEY_CONTROL));
467 sticky_key.HandleKeyEvent(ev.get());
468 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
471 TEST_F(StickyKeysTest, NormalModifiedClickTest) {
472 scoped_ptr<ui::KeyEvent> kev;
473 scoped_ptr<ui::MouseEvent> mev;
474 MockStickyKeysHandlerDelegate* mock_delegate =
475 new MockStickyKeysHandlerDelegate(this);
476 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
478 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
480 // Perform ctrl+click.
481 kev.reset(GenerateKey(true, ui::VKEY_CONTROL));
482 sticky_key.HandleKeyEvent(kev.get());
483 mev.reset(GenerateMouseEvent(true));
484 sticky_key.HandleMouseEvent(mev.get());
485 mev.reset(GenerateMouseEvent(false));
486 sticky_key.HandleMouseEvent(mev.get());
488 // Sticky keys should not be enabled afterwards.
489 kev.reset(GenerateKey(false, ui::VKEY_CONTROL));
490 sticky_key.HandleKeyEvent(kev.get());
491 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
494 TEST_F(StickyKeysTest, MouseMovedModifierTest) {
495 scoped_ptr<ui::KeyEvent> kev;
496 scoped_ptr<ui::MouseEvent> mev;
497 MockStickyKeysHandlerDelegate* mock_delegate =
498 new MockStickyKeysHandlerDelegate(this);
499 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
501 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
503 // Press ctrl and handle mouse move events.
504 kev.reset(GenerateKey(true, ui::VKEY_CONTROL));
505 sticky_key.HandleKeyEvent(kev.get());
506 mev.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(0, 0)));
507 sticky_key.HandleMouseEvent(mev.get());
508 mev.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(100, 100)));
509 sticky_key.HandleMouseEvent(mev.get());
511 // Sticky keys should be enabled afterwards.
512 kev.reset(GenerateKey(false, ui::VKEY_CONTROL));
513 sticky_key.HandleKeyEvent(kev.get());
514 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
517 TEST_F(StickyKeysTest, NormalModifiedScrollTest) {
518 scoped_ptr<ui::KeyEvent> kev;
519 scoped_ptr<ui::ScrollEvent> sev;
520 MockStickyKeysHandlerDelegate* mock_delegate =
521 new MockStickyKeysHandlerDelegate(this);
522 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
524 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
526 // Perform ctrl+scroll.
527 kev.reset(GenerateKey(true, ui::VKEY_CONTROL));
528 sev.reset(GenerateFlingScrollEvent(0, true));
529 sticky_key.HandleScrollEvent(sev.get());
530 sev.reset(GenerateScrollEvent(10));
531 sticky_key.HandleScrollEvent(sev.get());
532 sev.reset(GenerateFlingScrollEvent(10, false));
533 sticky_key.HandleScrollEvent(sev.get());
535 // Sticky keys should not be enabled afterwards.
536 kev.reset(GenerateKey(false, ui::VKEY_CONTROL));
537 sticky_key.HandleKeyEvent(kev.get());
538 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
541 TEST_F(StickyKeysTest, MouseEventOneshot) {
542 scoped_ptr<ui::MouseEvent> ev;
543 scoped_ptr<ui::KeyEvent> kev;
544 MockStickyKeysHandlerDelegate* mock_delegate =
545 new MockStickyKeysHandlerDelegate(this);
546 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
548 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
549 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
550 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
552 // We should still be in the ENABLED state until we get the mouse
554 ev.reset(GenerateMouseEvent(true));
555 sticky_key.HandleMouseEvent(ev.get());
556 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
557 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
559 ev.reset(GenerateMouseEvent(false));
560 sticky_key.HandleMouseEvent(ev.get());
561 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
562 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
564 // Making sure modifier key release event is dispatched in the right order.
565 ASSERT_EQ(2u, mock_delegate->GetEventCount());
566 EXPECT_EQ(ui::ET_MOUSE_RELEASED, mock_delegate->GetEvent(0)->type());
567 EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type());
568 EXPECT_EQ(ui::VKEY_CONTROL,
569 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1))
572 // Enabled state is one shot, so next click should not be control modified.
573 ev.reset(GenerateMouseEvent(true));
574 sticky_key.HandleMouseEvent(ev.get());
575 EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN);
577 ev.reset(GenerateMouseEvent(false));
578 sticky_key.HandleMouseEvent(ev.get());
579 EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN);
582 TEST_F(StickyKeysTest, MouseEventLocked) {
583 scoped_ptr<ui::MouseEvent> ev;
584 scoped_ptr<ui::KeyEvent> kev;
585 MockStickyKeysHandlerDelegate* mock_delegate =
586 new MockStickyKeysHandlerDelegate(this);
587 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
589 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
591 // Pressing modifier key twice should make us enter lock state.
592 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
593 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
594 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
595 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
597 // Mouse events should not disable locked mode.
598 for (int i = 0; i < 3; ++i) {
599 ev.reset(GenerateMouseEvent(true));
600 sticky_key.HandleMouseEvent(ev.get());
601 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
602 ev.reset(GenerateMouseEvent(false));
603 sticky_key.HandleMouseEvent(ev.get());
604 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
605 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
608 // Test with mouse wheel.
609 for (int i = 0; i < 3; ++i) {
610 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta));
611 sticky_key.HandleMouseEvent(ev.get());
612 ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta));
613 sticky_key.HandleMouseEvent(ev.get());
614 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
615 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
618 // Test mixed case with mouse events and key events.
619 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta));
620 sticky_key.HandleMouseEvent(ev.get());
621 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
622 kev.reset(GenerateKey(true, ui::VKEY_N));
623 sticky_key.HandleKeyEvent(kev.get());
624 EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN);
625 kev.reset(GenerateKey(false, ui::VKEY_N));
626 sticky_key.HandleKeyEvent(kev.get());
627 EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN);
629 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
632 TEST_F(StickyKeysTest, ScrollEventOneshot) {
633 // Disable Australlian scrolling.
634 ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true);
636 scoped_ptr<ui::ScrollEvent> ev;
637 scoped_ptr<ui::KeyEvent> kev;
638 MockStickyKeysHandlerDelegate* mock_delegate =
639 new MockStickyKeysHandlerDelegate(this);
640 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
642 int scroll_deltas[] = {-10, 10};
643 for (int i = 0; i < 2; ++i) {
644 mock_delegate->ClearEvents();
646 // Enable sticky keys.
647 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
648 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
649 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
651 // Test a scroll sequence. Sticky keys should only be disabled at the end
652 // of the scroll sequence. Fling cancel event starts the scroll sequence.
653 ev.reset(GenerateFlingScrollEvent(0, true));
654 sticky_key.HandleScrollEvent(ev.get());
655 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
656 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
658 // Scrolls should all be modified but not disable sticky keys.
659 for (int j = 0; j < 3; ++j) {
660 ev.reset(GenerateScrollEvent(scroll_deltas[i]));
661 sticky_key.HandleScrollEvent(ev.get());
662 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
663 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
666 // Fling start event ends scroll sequence.
667 ev.reset(GenerateFlingScrollEvent(scroll_deltas[i], false));
668 sticky_key.HandleScrollEvent(ev.get());
669 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
670 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
672 ASSERT_EQ(2U, mock_delegate->GetEventCount());
673 EXPECT_EQ(ui::ET_SCROLL_FLING_START, mock_delegate->GetEvent(0)->type());
674 EXPECT_FLOAT_EQ(scroll_deltas[i],
675 static_cast<const ui::ScrollEvent*>(
676 mock_delegate->GetEvent(0))->y_offset());
677 EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type());
678 EXPECT_EQ(ui::VKEY_CONTROL,
679 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1))
684 TEST_F(StickyKeysTest, ScrollDirectionChanged) {
685 // Disable Australlian scrolling.
686 ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true);
688 scoped_ptr<ui::ScrollEvent> ev;
689 scoped_ptr<ui::KeyEvent> kev;
690 MockStickyKeysHandlerDelegate* mock_delegate =
691 new MockStickyKeysHandlerDelegate(this);
692 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
694 // Test direction change with both boundary value and negative value.
695 const int direction_change_values[2] = {0, -10};
696 for (int i = 0; i < 2; ++i) {
697 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
698 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
700 // Fling cancel starts scroll sequence.
701 ev.reset(GenerateFlingScrollEvent(0, true));
702 sticky_key.HandleScrollEvent(ev.get());
703 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
705 // Test that changing directions in a scroll sequence will
706 // return sticky keys to DISABLED state.
707 for (int j = 0; j < 3; ++j) {
708 ev.reset(GenerateScrollEvent(10));
709 sticky_key.HandleScrollEvent(ev.get());
710 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
711 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
714 ev.reset(GenerateScrollEvent(direction_change_values[i]));
715 sticky_key.HandleScrollEvent(ev.get());
716 EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN);
717 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
721 TEST_F(StickyKeysTest, ScrollEventLocked) {
722 // Disable Australlian scrolling.
723 ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true);
725 scoped_ptr<ui::ScrollEvent> ev;
726 scoped_ptr<ui::KeyEvent> kev;
727 MockStickyKeysHandlerDelegate* mock_delegate =
728 new MockStickyKeysHandlerDelegate(this);
729 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
732 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
733 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
734 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
736 // Test scroll events are correctly modified in locked state.
737 for (int i = 0; i < 5; ++i) {
738 // Fling cancel starts scroll sequence.
739 ev.reset(GenerateFlingScrollEvent(0, true));
740 sticky_key.HandleScrollEvent(ev.get());
742 ev.reset(GenerateScrollEvent(10));
743 sticky_key.HandleScrollEvent(ev.get());
744 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
745 ev.reset(GenerateScrollEvent(-10));
746 sticky_key.HandleScrollEvent(ev.get());
747 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
749 // Fling start ends scroll sequence.
750 ev.reset(GenerateFlingScrollEvent(-10, false));
751 sticky_key.HandleScrollEvent(ev.get());
754 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
757 TEST_F(StickyKeysTest, EventTargetDestroyed) {
758 scoped_ptr<ui::KeyEvent> ev;
759 MockStickyKeysHandlerDelegate* mock_delegate =
760 new MockStickyKeysHandlerDelegate(this);
761 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
765 // Go into ENABLED state.
766 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
767 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
768 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
770 // CTRL+J is a special shortcut that will destroy the event target.
771 ev.reset(GenerateKey(true, ui::VKEY_J));
772 sticky_key.HandleKeyEvent(ev.get());
773 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
774 EXPECT_FALSE(target());
777 TEST_F(StickyKeysTest, SynthesizedEvents) {
778 // Non-native, internally generated events should be properly handled
780 MockStickyKeysHandlerDelegate* mock_delegate =
781 new MockStickyKeysHandlerDelegate(this);
782 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
784 // Test non-native key events.
785 scoped_ptr<ui::KeyEvent> kev;
786 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
787 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
789 kev.reset(GenerateSynthesizedKeyEvent(true, ui::VKEY_K));
790 sticky_key.HandleKeyEvent(kev.get());
791 EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN);
792 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
794 kev.reset(GenerateSynthesizedKeyEvent(false, ui::VKEY_K));
795 sticky_key.HandleKeyEvent(kev.get());
796 EXPECT_FALSE(kev->flags() & ui::EF_CONTROL_DOWN);
797 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
799 // Test non-native mouse events.
800 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
801 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
803 scoped_ptr<ui::MouseEvent> mev;
804 mev.reset(GenerateSynthesizedMouseClickEvent(true, gfx::Point(0, 0)));
805 sticky_key.HandleMouseEvent(mev.get());
806 EXPECT_TRUE(mev->flags() & ui::EF_CONTROL_DOWN);
807 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
809 mev.reset(GenerateSynthesizedMouseClickEvent(false, gfx::Point(0, 0)));
810 sticky_key.HandleMouseEvent(mev.get());
811 EXPECT_TRUE(mev->flags() & ui::EF_CONTROL_DOWN);
812 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
815 TEST_F(StickyKeysTest, KeyEventDispatchImpl) {
816 // Test the actual key event dispatch implementation.
818 ScopedVector<ui::Event> events;
819 ui::EventProcessor* dispatcher =
820 Shell::GetPrimaryRootWindow()->GetHost()->event_processor();
821 Shell::GetInstance()->AddPreTargetHandler(&buffer);
822 Shell::GetInstance()->sticky_keys_controller()->Enable(true);
824 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL);
825 scoped_ptr<ui::KeyEvent> ev;
826 buffer.PopEvents(&events);
828 // Test key press event is correctly modified and modifier release
830 ev.reset(GenerateKey(true, ui::VKEY_C));
831 ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get());
832 buffer.PopEvents(&events);
833 EXPECT_EQ(2u, events.size());
834 EXPECT_EQ(ui::ET_KEY_PRESSED, events[0]->type());
835 EXPECT_EQ(ui::VKEY_C, static_cast<ui::KeyEvent*>(events[0])->key_code());
836 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
837 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
838 EXPECT_EQ(ui::VKEY_CONTROL,
839 static_cast<ui::KeyEvent*>(events[1])->key_code());
841 // Test key release event is not modified.
842 ev.reset(GenerateKey(false, ui::VKEY_C));
843 details = dispatcher->OnEventFromSource(ev.get());
844 ASSERT_FALSE(details.dispatcher_destroyed);
845 buffer.PopEvents(&events);
846 EXPECT_EQ(1u, events.size());
847 EXPECT_EQ(ui::ET_KEY_RELEASED, events[0]->type());
848 EXPECT_EQ(ui::VKEY_C,
849 static_cast<ui::KeyEvent*>(events[0])->key_code());
850 EXPECT_FALSE(events[0]->flags() & ui::EF_CONTROL_DOWN);
852 // Test that synthesized key events are dispatched correctly.
853 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL);
854 buffer.PopEvents(&events);
855 scoped_ptr<ui::KeyEvent> kev;
856 kev.reset(GenerateSynthesizedKeyEvent(true, ui::VKEY_K));
857 dispatcher->OnEventFromSource(kev.get());
858 buffer.PopEvents(&events);
859 EXPECT_EQ(2u, events.size());
860 EXPECT_EQ(ui::ET_KEY_PRESSED, events[0]->type());
861 EXPECT_EQ(ui::VKEY_K, static_cast<ui::KeyEvent*>(events[0])->key_code());
862 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
863 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
864 EXPECT_EQ(ui::VKEY_CONTROL,
865 static_cast<ui::KeyEvent*>(events[1])->key_code());
867 Shell::GetInstance()->RemovePreTargetHandler(&buffer);
870 class StickyKeysMouseDispatchTest
871 : public StickyKeysTest,
872 public ::testing::WithParamInterface<int> {
875 TEST_P(StickyKeysMouseDispatchTest, MouseEventDispatchImpl) {
876 int scale_factor = GetParam();
877 std::ostringstream display_specs;
878 display_specs << "1280x1024*" << scale_factor;
879 UpdateDisplay(display_specs.str());
882 ScopedVector<ui::Event> events;
883 ui::EventProcessor* dispatcher =
884 Shell::GetPrimaryRootWindow()->GetHost()->event_processor();
885 Shell::GetInstance()->AddPreTargetHandler(&buffer);
886 Shell::GetInstance()->sticky_keys_controller()->Enable(true);
888 scoped_ptr<ui::MouseEvent> ev;
889 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL);
890 buffer.PopEvents(&events);
892 // Test mouse press event is correctly modified and has correct DIP location.
893 gfx::Point physical_location(400, 400);
894 gfx::Point dip_location(physical_location.x() / scale_factor,
895 physical_location.y() / scale_factor);
896 ev.reset(GenerateMouseEventAt(true, physical_location));
897 ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get());
898 buffer.PopEvents(&events);
899 EXPECT_EQ(1u, events.size());
900 EXPECT_EQ(ui::ET_MOUSE_PRESSED, events[0]->type());
901 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
902 EXPECT_EQ(dip_location.ToString(),
903 static_cast<ui::MouseEvent*>(events[0])->location().ToString());
905 // Test mouse release event is correctly modified and modifier release
906 // event is sent. The mouse event should have the correct DIP location.
907 ev.reset(GenerateMouseEventAt(false, physical_location));
908 details = dispatcher->OnEventFromSource(ev.get());
909 ASSERT_FALSE(details.dispatcher_destroyed);
910 buffer.PopEvents(&events);
911 EXPECT_EQ(2u, events.size());
912 EXPECT_EQ(ui::ET_MOUSE_RELEASED, events[0]->type());
913 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
914 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
915 EXPECT_EQ(dip_location.ToString(),
916 static_cast<ui::MouseEvent*>(events[0])->location().ToString());
917 EXPECT_EQ(ui::VKEY_CONTROL,
918 static_cast<ui::KeyEvent*>(events[1])->key_code());
920 // Test synthesized mouse events are dispatched correctly.
921 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL);
922 buffer.PopEvents(&events);
923 ev.reset(GenerateSynthesizedMouseClickEvent(false, physical_location));
924 dispatcher->OnEventFromSource(ev.get());
925 buffer.PopEvents(&events);
926 EXPECT_EQ(2u, events.size());
927 EXPECT_EQ(ui::ET_MOUSE_RELEASED, events[0]->type());
928 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
929 EXPECT_EQ(dip_location.ToString(),
930 static_cast<ui::MouseEvent*>(events[0])->location().ToString());
931 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
932 EXPECT_EQ(ui::VKEY_CONTROL,
933 static_cast<ui::KeyEvent*>(events[1])->key_code());
935 Shell::GetInstance()->RemovePreTargetHandler(&buffer);
938 TEST_P(StickyKeysMouseDispatchTest, MouseWheelEventDispatchImpl) {
939 int scale_factor = GetParam();
940 std::ostringstream display_specs;
941 display_specs << "1280x1024*" << scale_factor;
942 UpdateDisplay(display_specs.str());
944 // Test the actual mouse wheel event dispatch implementation.
946 ScopedVector<ui::Event> events;
947 ui::EventProcessor* dispatcher =
948 Shell::GetPrimaryRootWindow()->GetHost()->event_processor();
949 Shell::GetInstance()->AddPreTargetHandler(&buffer);
950 Shell::GetInstance()->sticky_keys_controller()->Enable(true);
952 scoped_ptr<ui::MouseWheelEvent> ev;
953 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL);
954 buffer.PopEvents(&events);
956 // Test positive mouse wheel event is correctly modified and modifier release
958 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta));
959 ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get());
960 ASSERT_FALSE(details.dispatcher_destroyed);
961 buffer.PopEvents(&events);
962 EXPECT_EQ(2u, events.size());
963 EXPECT_TRUE(events[0]->IsMouseWheelEvent());
964 EXPECT_EQ(ui::MouseWheelEvent::kWheelDelta / scale_factor,
965 static_cast<ui::MouseWheelEvent*>(events[0])->y_offset());
966 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
967 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
968 EXPECT_EQ(ui::VKEY_CONTROL,
969 static_cast<ui::KeyEvent*>(events[1])->key_code());
971 // Test negative mouse wheel event is correctly modified and modifier release
973 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL);
974 buffer.PopEvents(&events);
976 ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta));
977 details = dispatcher->OnEventFromSource(ev.get());
978 ASSERT_FALSE(details.dispatcher_destroyed);
979 buffer.PopEvents(&events);
980 EXPECT_EQ(2u, events.size());
981 EXPECT_TRUE(events[0]->IsMouseWheelEvent());
982 EXPECT_EQ(-ui::MouseWheelEvent::kWheelDelta / scale_factor,
983 static_cast<ui::MouseWheelEvent*>(events[0])->y_offset());
984 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
985 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
986 EXPECT_EQ(ui::VKEY_CONTROL,
987 static_cast<ui::KeyEvent*>(events[1])->key_code());
989 // Test synthesized mouse wheel events are dispatched correctly.
990 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL);
991 buffer.PopEvents(&events);
993 GenerateSynthesizedMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta));
994 dispatcher->OnEventFromSource(ev.get());
995 buffer.PopEvents(&events);
996 EXPECT_EQ(2u, events.size());
997 EXPECT_TRUE(events[0]->IsMouseWheelEvent());
998 EXPECT_EQ(ui::MouseWheelEvent::kWheelDelta / scale_factor,
999 static_cast<ui::MouseWheelEvent*>(events[0])->y_offset());
1000 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
1001 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
1002 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
1003 EXPECT_EQ(ui::VKEY_CONTROL,
1004 static_cast<ui::KeyEvent*>(events[1])->key_code());
1006 Shell::GetInstance()->RemovePreTargetHandler(&buffer);
1009 INSTANTIATE_TEST_CASE_P(DPIScaleFactors,
1010 StickyKeysMouseDispatchTest,
1011 ::testing::Values(1, 2));