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/root_window.h"
18 #include "ui/aura/window.h"
19 #include "ui/aura/window_tree_host_delegate.h"
20 #include "ui/events/event_handler.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 scroll device.
29 const unsigned int kScrollDeviceId = 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->native_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();
148 virtual void TearDown() OVERRIDE {
149 test::AshTestBase::TearDown();
152 // Overridden from MockStickyKeysHandlerDelegate::Delegate:
153 virtual aura::Window* GetExpectedTarget() OVERRIDE {
154 return target_ ? target_ : root_window_;
157 virtual void OnShortcutPressed() OVERRIDE {
164 ui::KeyEvent* GenerateKey(bool is_key_press, ui::KeyboardCode code) {
165 scoped_xevent_.InitKeyEvent(
166 is_key_press ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED,
169 ui::KeyEvent* event = new ui::KeyEvent(scoped_xevent_, false);
170 ui::Event::DispatcherApi dispatcher(event);
171 dispatcher.set_target(target_);
175 ui::MouseEvent* GenerateMouseEvent(bool is_button_press) {
176 scoped_xevent_.InitButtonEvent(
177 is_button_press ? ui::ET_MOUSE_PRESSED : ui::ET_MOUSE_RELEASED, 0);
178 ui::MouseEvent* event = new ui::MouseEvent(scoped_xevent_);
179 ui::Event::DispatcherApi dispatcher(event);
180 dispatcher.set_target(target_);
184 ui::MouseWheelEvent* GenerateMouseWheelEvent(int wheel_delta) {
185 EXPECT_NE(0, wheel_delta);
186 scoped_xevent_.InitMouseWheelEvent(wheel_delta, 0);
187 ui::MouseWheelEvent* event = new ui::MouseWheelEvent(scoped_xevent_);
188 ui::Event::DispatcherApi dispatcher(event);
189 dispatcher.set_target(target_);
193 ui::ScrollEvent* GenerateScrollEvent(int scroll_delta) {
194 scoped_xevent_.InitScrollEvent(kScrollDeviceId, // deviceid
196 scroll_delta, // y_offset
197 0, // x_offset_ordinal
198 scroll_delta, // y_offset_ordinal
200 ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_);
201 ui::Event::DispatcherApi dispatcher(event);
202 dispatcher.set_target(target_);
206 ui::ScrollEvent* GenerateFlingScrollEvent(int fling_delta,
208 scoped_xevent_.InitFlingScrollEvent(
209 kScrollDeviceId, // deviceid
211 fling_delta, // y_velocity
212 0, // x_velocity_ordinal
213 fling_delta, // y_velocity_ordinal
214 is_cancel); // is_cancel
215 ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_);
216 ui::Event::DispatcherApi dispatcher(event);
217 dispatcher.set_target(target_);
221 // Creates a synthesized KeyEvent that is not backed by a native event.
222 ui::KeyEvent* GenerateSynthesizedKeyEvent(
223 bool is_key_press, ui::KeyboardCode code) {
224 ui::KeyEvent* event = new ui::KeyEvent(
225 is_key_press ? ui::ET_KEY_PRESSED : ui::ET_MOUSE_RELEASED,
227 ui::Event::DispatcherApi dispatcher(event);
228 dispatcher.set_target(target_);
232 // Creates a synthesized MouseEvent that is not backed by a native event.
233 ui::MouseEvent* GenerateSynthesizedMouseEvent(bool is_button_press) {
234 ui::MouseEvent* event = new ui::MouseEvent(
235 is_button_press ? ui::ET_MOUSE_PRESSED : ui::ET_MOUSE_RELEASED,
238 ui::EF_LEFT_MOUSE_BUTTON,
239 ui::EF_LEFT_MOUSE_BUTTON);
240 ui::Event::DispatcherApi dispatcher(event);
241 dispatcher.set_target(target_);
245 // Creates a synthesized ET_MOUSE_MOVED event.
246 ui::MouseEvent* GenerateSynthesizedMouseEvent(int x, int y) {
247 ui::MouseEvent* event = new ui::MouseEvent(
251 ui::EF_LEFT_MOUSE_BUTTON,
252 ui::EF_LEFT_MOUSE_BUTTON);
253 ui::Event::DispatcherApi dispatcher(event);
254 dispatcher.set_target(target_);
258 void SendActivateStickyKeyPattern(StickyKeysHandler* handler,
259 ui::KeyboardCode key_code) {
260 scoped_ptr<ui::KeyEvent> ev;
261 ev.reset(GenerateKey(true, key_code));
262 handler->HandleKeyEvent(ev.get());
263 ev.reset(GenerateKey(false, key_code));
264 handler->HandleKeyEvent(ev.get());
267 void SendActivateStickyKeyPattern(aura::WindowEventDispatcher* dispatcher,
268 ui::KeyboardCode key_code) {
269 scoped_ptr<ui::KeyEvent> ev;
270 ev.reset(GenerateKey(true, key_code));
271 ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get());
272 CHECK(!details.dispatcher_destroyed);
273 ev.reset(GenerateKey(false, key_code));
274 details = dispatcher->OnEventFromSource(ev.get());
275 CHECK(!details.dispatcher_destroyed);
278 aura::Window* target() { return target_; }
281 // Owned by root window of shell, but we can still delete |target_| safely.
282 aura::Window* target_;
283 // The root window of |target_|. Not owned.
284 aura::Window* root_window_;
286 // Used to construct the various X events.
287 ui::ScopedXI2Event scoped_xevent_;
289 DISALLOW_COPY_AND_ASSIGN(StickyKeysTest);
292 TEST_F(StickyKeysTest, BasicOneshotScenarioTest) {
293 scoped_ptr<ui::KeyEvent> ev;
294 MockStickyKeysHandlerDelegate* mock_delegate =
295 new MockStickyKeysHandlerDelegate(this);
296 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate);
298 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
300 // By typing Shift key, internal state become ENABLED.
301 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
302 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
304 ev.reset(GenerateKey(true, ui::VKEY_A));
305 sticky_key.HandleKeyEvent(ev.get());
307 // Next keyboard event is shift modified.
308 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN);
310 ev.reset(GenerateKey(false, ui::VKEY_A));
311 sticky_key.HandleKeyEvent(ev.get());
313 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
314 // Making sure Shift up keyboard event is dispatched.
315 ASSERT_EQ(2U, mock_delegate->GetEventCount());
316 EXPECT_EQ(ui::ET_KEY_PRESSED, mock_delegate->GetEvent(0)->type());
317 EXPECT_EQ(ui::VKEY_A,
318 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(0))
320 EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type());
321 EXPECT_EQ(ui::VKEY_SHIFT,
322 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1))
325 // Enabled state is one shot, so next key event should not be shift modified.
326 ev.reset(GenerateKey(true, ui::VKEY_A));
327 sticky_key.HandleKeyEvent(ev.get());
328 EXPECT_FALSE(ev->flags() & ui::EF_SHIFT_DOWN);
330 ev.reset(GenerateKey(false, ui::VKEY_A));
331 sticky_key.HandleKeyEvent(ev.get());
332 EXPECT_FALSE(ev->flags() & ui::EF_SHIFT_DOWN);
335 TEST_F(StickyKeysTest, BasicLockedScenarioTest) {
336 scoped_ptr<ui::KeyEvent> ev;
337 MockStickyKeysHandlerDelegate* mock_delegate =
338 new MockStickyKeysHandlerDelegate(this);
339 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate);
341 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
343 // By typing shift key, internal state become ENABLED.
344 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
345 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
347 // By typing shift key again, internal state become LOCKED.
348 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
349 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
351 // All keyboard events including keyUp become shift modified.
352 ev.reset(GenerateKey(true, ui::VKEY_A));
353 sticky_key.HandleKeyEvent(ev.get());
354 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN);
356 ev.reset(GenerateKey(false, ui::VKEY_A));
357 sticky_key.HandleKeyEvent(ev.get());
358 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN);
360 // Locked state keeps after normal keyboard event.
361 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
363 ev.reset(GenerateKey(true, ui::VKEY_B));
364 sticky_key.HandleKeyEvent(ev.get());
365 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN);
367 ev.reset(GenerateKey(false, ui::VKEY_B));
368 sticky_key.HandleKeyEvent(ev.get());
369 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN);
371 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
373 // By typing shift key again, internal state become back to DISABLED.
374 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
375 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
378 TEST_F(StickyKeysTest, NonTargetModifierTest) {
379 scoped_ptr<ui::KeyEvent> ev;
380 MockStickyKeysHandlerDelegate* mock_delegate =
381 new MockStickyKeysHandlerDelegate(this);
382 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate);
384 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
386 // Non target modifier key does not affect internal state
387 ev.reset(GenerateKey(true, ui::VKEY_MENU));
388 sticky_key.HandleKeyEvent(ev.get());
389 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
391 ev.reset(GenerateKey(false, ui::VKEY_MENU));
392 sticky_key.HandleKeyEvent(ev.get());
393 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
395 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
396 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
398 // Non target modifier key does not affect internal state
399 ev.reset(GenerateKey(true, ui::VKEY_MENU));
400 sticky_key.HandleKeyEvent(ev.get());
401 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
403 ev.reset(GenerateKey(false, ui::VKEY_MENU));
404 sticky_key.HandleKeyEvent(ev.get());
405 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
407 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
408 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
410 // Non target modifier key does not affect internal state
411 ev.reset(GenerateKey(true, ui::VKEY_MENU));
412 sticky_key.HandleKeyEvent(ev.get());
413 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
415 ev.reset(GenerateKey(false, ui::VKEY_MENU));
416 sticky_key.HandleKeyEvent(ev.get());
417 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
420 TEST_F(StickyKeysTest, NormalShortcutTest) {
421 // Sticky keys should not be enabled if we perform a normal shortcut.
422 scoped_ptr<ui::KeyEvent> ev;
423 MockStickyKeysHandlerDelegate* mock_delegate =
424 new MockStickyKeysHandlerDelegate(this);
425 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
427 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
429 // Perform ctrl+n shortcut.
430 ev.reset(GenerateKey(true, ui::VKEY_CONTROL));
431 sticky_key.HandleKeyEvent(ev.get());
432 ev.reset(GenerateKey(true, ui::VKEY_N));
433 sticky_key.HandleKeyEvent(ev.get());
434 ev.reset(GenerateKey(false, ui::VKEY_N));
435 sticky_key.HandleKeyEvent(ev.get());
436 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
438 // Sticky keys should not be enabled afterwards.
439 ev.reset(GenerateKey(false, ui::VKEY_CONTROL));
440 sticky_key.HandleKeyEvent(ev.get());
441 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
444 TEST_F(StickyKeysTest, NormalModifiedClickTest) {
445 scoped_ptr<ui::KeyEvent> kev;
446 scoped_ptr<ui::MouseEvent> mev;
447 MockStickyKeysHandlerDelegate* mock_delegate =
448 new MockStickyKeysHandlerDelegate(this);
449 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
451 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
453 // Perform ctrl+click.
454 kev.reset(GenerateKey(true, ui::VKEY_CONTROL));
455 sticky_key.HandleKeyEvent(kev.get());
456 mev.reset(GenerateMouseEvent(true));
457 sticky_key.HandleMouseEvent(mev.get());
458 mev.reset(GenerateMouseEvent(false));
459 sticky_key.HandleMouseEvent(mev.get());
461 // Sticky keys should not be enabled afterwards.
462 kev.reset(GenerateKey(false, ui::VKEY_CONTROL));
463 sticky_key.HandleKeyEvent(kev.get());
464 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
467 TEST_F(StickyKeysTest, MouseMovedModifierTest) {
468 scoped_ptr<ui::KeyEvent> kev;
469 scoped_ptr<ui::MouseEvent> mev;
470 MockStickyKeysHandlerDelegate* mock_delegate =
471 new MockStickyKeysHandlerDelegate(this);
472 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
474 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
476 // Press ctrl and handle mouse move events.
477 kev.reset(GenerateKey(true, ui::VKEY_CONTROL));
478 sticky_key.HandleKeyEvent(kev.get());
479 mev.reset(GenerateSynthesizedMouseEvent(0, 0));
480 sticky_key.HandleMouseEvent(mev.get());
481 mev.reset(GenerateSynthesizedMouseEvent(100, 100));
482 sticky_key.HandleMouseEvent(mev.get());
484 // Sticky keys should be enabled afterwards.
485 kev.reset(GenerateKey(false, ui::VKEY_CONTROL));
486 sticky_key.HandleKeyEvent(kev.get());
487 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
490 TEST_F(StickyKeysTest, NormalModifiedScrollTest) {
491 ui::SetUpScrollDeviceForTest(kScrollDeviceId);
493 scoped_ptr<ui::KeyEvent> kev;
494 scoped_ptr<ui::ScrollEvent> sev;
495 MockStickyKeysHandlerDelegate* mock_delegate =
496 new MockStickyKeysHandlerDelegate(this);
497 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
499 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
501 // Perform ctrl+scroll.
502 kev.reset(GenerateKey(true, ui::VKEY_CONTROL));
503 sev.reset(GenerateFlingScrollEvent(0, true));
504 sticky_key.HandleScrollEvent(sev.get());
505 sev.reset(GenerateScrollEvent(10));
506 sticky_key.HandleScrollEvent(sev.get());
507 sev.reset(GenerateFlingScrollEvent(10, false));
508 sticky_key.HandleScrollEvent(sev.get());
510 // Sticky keys should not be enabled afterwards.
511 kev.reset(GenerateKey(false, ui::VKEY_CONTROL));
512 sticky_key.HandleKeyEvent(kev.get());
513 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
516 TEST_F(StickyKeysTest, MouseEventOneshot) {
517 scoped_ptr<ui::MouseEvent> ev;
518 scoped_ptr<ui::KeyEvent> kev;
519 MockStickyKeysHandlerDelegate* mock_delegate =
520 new MockStickyKeysHandlerDelegate(this);
521 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
523 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
524 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
525 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
527 // We should still be in the ENABLED state until we get the mouse
529 ev.reset(GenerateMouseEvent(true));
530 sticky_key.HandleMouseEvent(ev.get());
531 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
532 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
534 ev.reset(GenerateMouseEvent(false));
535 sticky_key.HandleMouseEvent(ev.get());
536 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
537 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
539 // Making sure modifier key release event is dispatched in the right order.
540 ASSERT_EQ(2u, mock_delegate->GetEventCount());
541 EXPECT_EQ(ui::ET_MOUSE_RELEASED, mock_delegate->GetEvent(0)->type());
542 EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type());
543 EXPECT_EQ(ui::VKEY_CONTROL,
544 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1))
547 // Enabled state is one shot, so next click should not be control modified.
548 ev.reset(GenerateMouseEvent(true));
549 sticky_key.HandleMouseEvent(ev.get());
550 EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN);
552 ev.reset(GenerateMouseEvent(false));
553 sticky_key.HandleMouseEvent(ev.get());
554 EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN);
557 TEST_F(StickyKeysTest, MouseEventLocked) {
558 scoped_ptr<ui::MouseEvent> ev;
559 scoped_ptr<ui::KeyEvent> kev;
560 MockStickyKeysHandlerDelegate* mock_delegate =
561 new MockStickyKeysHandlerDelegate(this);
562 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
564 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
566 // Pressing modifier key twice should make us enter lock state.
567 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
568 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
569 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
570 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
572 // Mouse events should not disable locked mode.
573 for (int i = 0; i < 3; ++i) {
574 ev.reset(GenerateMouseEvent(true));
575 sticky_key.HandleMouseEvent(ev.get());
576 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
577 ev.reset(GenerateMouseEvent(false));
578 sticky_key.HandleMouseEvent(ev.get());
579 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
580 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
583 // Test with mouse wheel.
584 for (int i = 0; i < 3; ++i) {
585 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta));
586 sticky_key.HandleMouseEvent(ev.get());
587 ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta));
588 sticky_key.HandleMouseEvent(ev.get());
589 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
590 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
593 // Test mixed case with mouse events and key events.
594 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta));
595 sticky_key.HandleMouseEvent(ev.get());
596 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
597 kev.reset(GenerateKey(true, ui::VKEY_N));
598 sticky_key.HandleKeyEvent(kev.get());
599 EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN);
600 kev.reset(GenerateKey(false, ui::VKEY_N));
601 sticky_key.HandleKeyEvent(kev.get());
602 EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN);
604 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
607 TEST_F(StickyKeysTest, ScrollEventOneshot) {
608 ui::SetUpScrollDeviceForTest(kScrollDeviceId);
609 // Disable Australlian scrolling.
610 ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true);
612 scoped_ptr<ui::ScrollEvent> ev;
613 scoped_ptr<ui::KeyEvent> kev;
614 MockStickyKeysHandlerDelegate* mock_delegate =
615 new MockStickyKeysHandlerDelegate(this);
616 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
618 int scroll_deltas[] = {-10, 10};
619 for (int i = 0; i < 2; ++i) {
620 mock_delegate->ClearEvents();
622 // Enable sticky keys.
623 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
624 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
625 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
627 // Test a scroll sequence. Sticky keys should only be disabled at the end
628 // of the scroll sequence. Fling cancel event starts the scroll sequence.
629 ev.reset(GenerateFlingScrollEvent(0, true));
630 sticky_key.HandleScrollEvent(ev.get());
631 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
632 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
634 // Scrolls should all be modified but not disable sticky keys.
635 for (int j = 0; j < 3; ++j) {
636 ev.reset(GenerateScrollEvent(scroll_deltas[i]));
637 sticky_key.HandleScrollEvent(ev.get());
638 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
639 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
642 // Fling start event ends scroll sequence.
643 ev.reset(GenerateFlingScrollEvent(scroll_deltas[i], false));
644 sticky_key.HandleScrollEvent(ev.get());
645 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
646 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
648 ASSERT_EQ(2U, mock_delegate->GetEventCount());
649 EXPECT_EQ(ui::ET_SCROLL_FLING_START, mock_delegate->GetEvent(0)->type());
650 EXPECT_FLOAT_EQ(scroll_deltas[i],
651 static_cast<const ui::ScrollEvent*>(
652 mock_delegate->GetEvent(0))->y_offset());
653 EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type());
654 EXPECT_EQ(ui::VKEY_CONTROL,
655 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1))
660 TEST_F(StickyKeysTest, ScrollDirectionChanged) {
661 ui::SetUpScrollDeviceForTest(kScrollDeviceId);
662 // Disable Australlian scrolling.
663 ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true);
665 scoped_ptr<ui::ScrollEvent> ev;
666 scoped_ptr<ui::KeyEvent> kev;
667 MockStickyKeysHandlerDelegate* mock_delegate =
668 new MockStickyKeysHandlerDelegate(this);
669 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
671 // Test direction change with both boundary value and negative value.
672 const int direction_change_values[2] = {0, -10};
673 for (int i = 0; i < 2; ++i) {
674 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
675 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
677 // Fling cancel starts scroll sequence.
678 ev.reset(GenerateFlingScrollEvent(0, true));
679 sticky_key.HandleScrollEvent(ev.get());
680 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
682 // Test that changing directions in a scroll sequence will
683 // return sticky keys to DISABLED state.
684 for (int j = 0; j < 3; ++j) {
685 ev.reset(GenerateScrollEvent(10));
686 sticky_key.HandleScrollEvent(ev.get());
687 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
688 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
691 ev.reset(GenerateScrollEvent(direction_change_values[i]));
692 sticky_key.HandleScrollEvent(ev.get());
693 EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN);
694 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
698 TEST_F(StickyKeysTest, ScrollEventLocked) {
699 ui::SetUpScrollDeviceForTest(kScrollDeviceId);
700 // Disable Australlian scrolling.
701 ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true);
703 scoped_ptr<ui::ScrollEvent> ev;
704 scoped_ptr<ui::KeyEvent> kev;
705 MockStickyKeysHandlerDelegate* mock_delegate =
706 new MockStickyKeysHandlerDelegate(this);
707 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
710 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
711 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
712 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
714 // Test scroll events are correctly modified in locked state.
715 for (int i = 0; i < 5; ++i) {
716 // Fling cancel starts scroll sequence.
717 ev.reset(GenerateFlingScrollEvent(0, true));
718 sticky_key.HandleScrollEvent(ev.get());
720 ev.reset(GenerateScrollEvent(10));
721 sticky_key.HandleScrollEvent(ev.get());
722 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
723 ev.reset(GenerateScrollEvent(-10));
724 sticky_key.HandleScrollEvent(ev.get());
725 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
727 // Fling start ends scroll sequence.
728 ev.reset(GenerateFlingScrollEvent(-10, false));
729 sticky_key.HandleScrollEvent(ev.get());
732 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
735 TEST_F(StickyKeysTest, EventTargetDestroyed) {
736 scoped_ptr<ui::KeyEvent> ev;
737 MockStickyKeysHandlerDelegate* mock_delegate =
738 new MockStickyKeysHandlerDelegate(this);
739 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
743 // Go into ENABLED state.
744 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
745 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
746 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
748 // CTRL+J is a special shortcut that will destroy the event target.
749 ev.reset(GenerateKey(true, ui::VKEY_J));
750 sticky_key.HandleKeyEvent(ev.get());
751 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
752 EXPECT_FALSE(target());
755 TEST_F(StickyKeysTest, SynthesizedEvents) {
756 // Non-native, internally generated events should be properly handled
758 MockStickyKeysHandlerDelegate* mock_delegate =
759 new MockStickyKeysHandlerDelegate(this);
760 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
762 // Test non-native key events.
763 scoped_ptr<ui::KeyEvent> kev;
764 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
765 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
767 kev.reset(GenerateSynthesizedKeyEvent(true, ui::VKEY_K));
768 sticky_key.HandleKeyEvent(kev.get());
769 EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN);
770 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
772 kev.reset(GenerateSynthesizedKeyEvent(false, ui::VKEY_K));
773 sticky_key.HandleKeyEvent(kev.get());
774 EXPECT_FALSE(kev->flags() & ui::EF_CONTROL_DOWN);
775 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
777 // Test non-native mouse events.
778 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
779 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
781 scoped_ptr<ui::MouseEvent> mev;
782 mev.reset(GenerateSynthesizedMouseEvent(true));
783 sticky_key.HandleMouseEvent(mev.get());
784 EXPECT_TRUE(mev->flags() & ui::EF_CONTROL_DOWN);
785 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
787 mev.reset(GenerateSynthesizedMouseEvent(false));
788 sticky_key.HandleMouseEvent(mev.get());
789 EXPECT_TRUE(mev->flags() & ui::EF_CONTROL_DOWN);
790 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
793 TEST_F(StickyKeysTest, KeyEventDispatchImpl) {
794 // Test the actual key event dispatch implementation.
796 ScopedVector<ui::Event> events;
797 aura::WindowEventDispatcher* dispatcher = Shell::GetPrimaryRootWindow()
799 Shell::GetInstance()->AddPreTargetHandler(&buffer);
800 Shell::GetInstance()->sticky_keys_controller()->Enable(true);
802 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL);
803 scoped_ptr<ui::KeyEvent> ev;
804 buffer.PopEvents(&events);
806 // Test key press event is correctly modified and modifier release
808 ev.reset(GenerateKey(true, ui::VKEY_C));
809 ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get());
810 buffer.PopEvents(&events);
811 EXPECT_EQ(2u, events.size());
812 EXPECT_EQ(ui::ET_KEY_PRESSED, events[0]->type());
813 EXPECT_EQ(ui::VKEY_C, static_cast<ui::KeyEvent*>(events[0])->key_code());
814 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
815 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
816 EXPECT_EQ(ui::VKEY_CONTROL,
817 static_cast<ui::KeyEvent*>(events[1])->key_code());
819 // Test key release event is not modified.
820 ev.reset(GenerateKey(false, ui::VKEY_C));
821 details = dispatcher->OnEventFromSource(ev.get());
822 ASSERT_FALSE(details.dispatcher_destroyed);
823 buffer.PopEvents(&events);
824 EXPECT_EQ(1u, events.size());
825 EXPECT_EQ(ui::ET_KEY_RELEASED, events[0]->type());
826 EXPECT_EQ(ui::VKEY_C,
827 static_cast<ui::KeyEvent*>(events[0])->key_code());
828 EXPECT_FALSE(events[0]->flags() & ui::EF_CONTROL_DOWN);
830 Shell::GetInstance()->RemovePreTargetHandler(&buffer);
833 TEST_F(StickyKeysTest, MouseEventDispatchImpl) {
834 // Test the actual sticky mouse event dispatch implementation.
836 ScopedVector<ui::Event> events;
837 aura::WindowEventDispatcher* dispatcher = Shell::GetPrimaryRootWindow()
839 Shell::GetInstance()->AddPreTargetHandler(&buffer);
840 Shell::GetInstance()->sticky_keys_controller()->Enable(true);
842 scoped_ptr<ui::MouseEvent> ev;
843 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL);
844 buffer.PopEvents(&events);
846 // Test mouse press event is correctly modified.
847 ev.reset(GenerateMouseEvent(true));
848 ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get());
849 buffer.PopEvents(&events);
850 EXPECT_EQ(1u, events.size());
851 EXPECT_EQ(ui::ET_MOUSE_PRESSED, events[0]->type());
852 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
854 // Test mouse release event is correctly modified and modifier release
856 ev.reset(GenerateMouseEvent(false));
857 details = dispatcher->OnEventFromSource(ev.get());
858 ASSERT_FALSE(details.dispatcher_destroyed);
859 buffer.PopEvents(&events);
860 EXPECT_EQ(2u, events.size());
861 EXPECT_EQ(ui::ET_MOUSE_RELEASED, events[0]->type());
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 TEST_F(StickyKeysTest, MouseWheelEventDispatchImpl) {
871 // Test the actual mouse wheel event dispatch implementation.
873 ScopedVector<ui::Event> events;
874 aura::WindowEventDispatcher* dispatcher = Shell::GetPrimaryRootWindow()
876 Shell::GetInstance()->AddPreTargetHandler(&buffer);
877 Shell::GetInstance()->sticky_keys_controller()->Enable(true);
879 scoped_ptr<ui::MouseWheelEvent> ev;
880 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL);
881 buffer.PopEvents(&events);
883 // Test positive mouse wheel event is correctly modified and modifier release
885 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta));
886 ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get());
887 ASSERT_FALSE(details.dispatcher_destroyed);
888 buffer.PopEvents(&events);
889 EXPECT_EQ(2u, events.size());
890 EXPECT_TRUE(events[0]->IsMouseWheelEvent());
891 EXPECT_EQ(ui::MouseWheelEvent::kWheelDelta,
892 static_cast<ui::MouseWheelEvent*>(events[0])->y_offset());
893 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
894 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
895 EXPECT_EQ(ui::VKEY_CONTROL,
896 static_cast<ui::KeyEvent*>(events[1])->key_code());
898 // Test negative mouse wheel event is correctly modified and modifier release
900 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL);
901 buffer.PopEvents(&events);
903 ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta));
904 details = dispatcher->OnEventFromSource(ev.get());
905 ASSERT_FALSE(details.dispatcher_destroyed);
906 buffer.PopEvents(&events);
907 EXPECT_EQ(2u, events.size());
908 EXPECT_TRUE(events[0]->IsMouseWheelEvent());
909 EXPECT_EQ(-ui::MouseWheelEvent::kWheelDelta,
910 static_cast<ui::MouseWheelEvent*>(events[0])->y_offset());
911 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
912 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
913 EXPECT_EQ(ui::VKEY_CONTROL,
914 static_cast<ui::KeyEvent*>(events[1])->key_code());
916 Shell::GetInstance()->RemovePreTargetHandler(&buffer);