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"
8 #include "ash/test/ash_test_base.h"
10 #include "base/callback.h"
11 #include "base/memory/scoped_vector.h"
12 #include "ui/aura/window.h"
13 #include "ui/aura/window_tree_host.h"
14 #include "ui/events/event_source.h"
15 #include "ui/events/event_utils.h"
22 #include "ui/events/test/events_test_utils_x11.h"
30 // The device id of the test touchpad device.
31 const unsigned int kTouchPadDeviceId
= 1;
36 class StickyKeysTest
: public test::AshTestBase
{
42 void SetUp() override
{
43 test::AshTestBase::SetUp();
45 // |target_| owned by root window of shell. It is still safe to delete
47 target_
= CreateTestWindowInShellWithId(0);
48 root_window_
= target_
->GetRootWindow();
51 ui::SetUpTouchPadForTest(kTouchPadDeviceId
);
55 void TearDown() override
{ test::AshTestBase::TearDown(); }
57 virtual void OnShortcutPressed() {
64 ui::KeyEvent
* GenerateKey(ui::EventType type
, ui::KeyboardCode code
) {
66 scoped_xevent_
.InitKeyEvent(type
, code
, 0);
67 return new ui::KeyEvent(scoped_xevent_
);
69 return GenerateSynthesizedKeyEvent(type
, code
);
73 // Creates a mouse event backed by a native XInput2 generic button event.
74 // This is the standard native event on Chromebooks.
75 ui::MouseEvent
* GenerateMouseEvent(ui::EventType type
) {
76 return GenerateMouseEventAt(type
, gfx::Point());
79 // Creates a mouse event backed by a native XInput2 generic button event.
80 // The |location| should be in physical pixels.
81 ui::MouseEvent
* GenerateMouseEventAt(ui::EventType type
,
82 const gfx::Point
& location
) {
84 scoped_xevent_
.InitGenericButtonEvent(
89 return new ui::MouseEvent(scoped_xevent_
);
91 return GenerateSynthesizedMouseEventAt(type
, location
);
95 ui::MouseWheelEvent
* GenerateMouseWheelEvent(int wheel_delta
) {
97 EXPECT_NE(0, wheel_delta
);
98 scoped_xevent_
.InitGenericMouseWheelEvent(
99 kTouchPadDeviceId
, wheel_delta
, 0);
100 ui::MouseWheelEvent
* event
= new ui::MouseWheelEvent(scoped_xevent_
);
101 ui::Event::DispatcherApi
dispatcher(event
);
102 dispatcher
.set_target(target_
);
105 return GenerateSynthesizedMouseWheelEvent(wheel_delta
);
109 ui::ScrollEvent
* GenerateScrollEvent(int scroll_delta
) {
111 scoped_xevent_
.InitScrollEvent(kTouchPadDeviceId
, // deviceid
113 scroll_delta
, // y_offset
114 0, // x_offset_ordinal
115 scroll_delta
, // y_offset_ordinal
117 ui::ScrollEvent
* event
= new ui::ScrollEvent(scoped_xevent_
);
118 ui::Event::DispatcherApi
dispatcher(event
);
119 dispatcher
.set_target(target_
);
122 ui::ScrollEvent
* event
= new ui::ScrollEvent(
123 ui::ET_SCROLL
, gfx::Point(0, 0), ui::EventTimeForNow(), ui::EF_NONE
,
125 scroll_delta
, // y_offset
126 0, // x_offset_ordinal
127 scroll_delta
, // y_offset_ordinal
129 ui::Event::DispatcherApi
dispatcher(event
);
130 dispatcher
.set_target(target_
);
135 ui::ScrollEvent
* GenerateFlingScrollEvent(int fling_delta
,
138 scoped_xevent_
.InitFlingScrollEvent(
139 kTouchPadDeviceId
, // deviceid
141 fling_delta
, // y_velocity
142 0, // x_velocity_ordinal
143 fling_delta
, // y_velocity_ordinal
144 is_cancel
); // is_cancel
145 ui::ScrollEvent
* event
= new ui::ScrollEvent(scoped_xevent_
);
146 ui::Event::DispatcherApi
dispatcher(event
);
147 dispatcher
.set_target(target_
);
150 ui::ScrollEvent
* event
= new ui::ScrollEvent(
151 is_cancel
? ui::ET_SCROLL_FLING_CANCEL
: ui::ET_SCROLL_FLING_START
,
152 gfx::Point(0, 0), ui::EventTimeForNow(), ui::EF_NONE
,
154 fling_delta
, // y_velocity
155 0, // x_velocity_ordinal
156 fling_delta
, // y_velocity_ordinal
158 ui::Event::DispatcherApi
dispatcher(event
);
159 dispatcher
.set_target(target_
);
164 // Creates a synthesized KeyEvent that is not backed by a native event.
165 ui::KeyEvent
* GenerateSynthesizedKeyEvent(ui::EventType type
,
166 ui::KeyboardCode code
) {
167 return new ui::KeyEvent(type
, code
, ui::EF_NONE
);
170 // Creates a synthesized MouseEvent that is not backed by a native event.
171 ui::MouseEvent
* GenerateSynthesizedMouseEventAt(ui::EventType event_type
,
172 const gfx::Point
& location
) {
173 ui::MouseEvent
* event
= new ui::MouseEvent(
174 event_type
, location
, location
, ui::EventTimeForNow(),
175 ui::EF_LEFT_MOUSE_BUTTON
, ui::EF_LEFT_MOUSE_BUTTON
);
176 ui::Event::DispatcherApi
dispatcher(event
);
177 dispatcher
.set_target(target_
);
181 // Creates a synthesized mouse press or release event.
182 ui::MouseEvent
* GenerateSynthesizedMouseClickEvent(
184 const gfx::Point
& location
) {
185 return GenerateSynthesizedMouseEventAt(type
, location
);
188 // Creates a synthesized ET_MOUSE_MOVED event.
189 ui::MouseEvent
* GenerateSynthesizedMouseMoveEvent(
190 const gfx::Point
& location
) {
191 return GenerateSynthesizedMouseEventAt(ui::ET_MOUSE_MOVED
, location
);
194 // Creates a synthesized MouseWHeel event.
195 ui::MouseWheelEvent
* GenerateSynthesizedMouseWheelEvent(int wheel_delta
) {
196 scoped_ptr
<ui::MouseEvent
> mev(
197 GenerateSynthesizedMouseEventAt(ui::ET_MOUSEWHEEL
, gfx::Point(0, 0)));
198 ui::MouseWheelEvent
* event
= new ui::MouseWheelEvent(*mev
, 0, wheel_delta
);
199 ui::Event::DispatcherApi
dispatcher(event
);
200 dispatcher
.set_target(target_
);
204 void SendActivateStickyKeyPattern(StickyKeysHandler
* handler
,
205 ui::KeyboardCode key_code
) {
206 bool released
= false;
208 scoped_ptr
<ui::KeyEvent
> ev
;
209 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, key_code
));
210 handler
->HandleKeyEvent(*ev
.get(), key_code
, &down_flags
, &released
);
211 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, key_code
));
212 handler
->HandleKeyEvent(*ev
.get(), key_code
, &down_flags
, &released
);
215 bool HandleKeyEvent(const ui::KeyEvent
& key_event
,
216 StickyKeysHandler
* handler
,
219 return handler
->HandleKeyEvent(key_event
, key_event
.key_code(), down
, up
);
222 int HandleKeyEventForDownFlags(const ui::KeyEvent
& key_event
,
223 StickyKeysHandler
* handler
) {
224 bool released
= false;
226 handler
->HandleKeyEvent(key_event
, key_event
.key_code(), &down
, &released
);
230 aura::Window
* target() { return target_
; }
233 // Owned by root window of shell, but we can still delete |target_| safely.
234 aura::Window
* target_
;
235 // The root window of |target_|. Not owned.
236 aura::Window
* root_window_
;
239 // Used to construct the various X events.
240 ui::ScopedXI2Event scoped_xevent_
;
243 DISALLOW_COPY_AND_ASSIGN(StickyKeysTest
);
246 TEST_F(StickyKeysTest
, BasicOneshotScenarioTest
) {
247 scoped_ptr
<ui::KeyEvent
> ev
;
248 StickyKeysHandler
sticky_key(ui::EF_SHIFT_DOWN
);
250 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
252 // By typing Shift key, internal state become ENABLED.
253 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
254 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
256 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_A
));
257 bool released
= false;
258 int mod_down_flags
= 0;
259 HandleKeyEvent(*ev
.get(), &sticky_key
, &mod_down_flags
, &released
);
260 // Next keyboard event is shift modified.
261 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
262 // Modifier release notification happens.
263 EXPECT_TRUE(released
);
265 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_A
));
268 HandleKeyEvent(*ev
.get(), &sticky_key
, &mod_down_flags
, &released
);
270 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
271 // Making sure Shift up keyboard event is available.
272 scoped_ptr
<ui::Event
> up_event
;
273 ASSERT_EQ(0, sticky_key
.GetModifierUpEvent(&up_event
));
274 EXPECT_TRUE(up_event
.get());
275 EXPECT_EQ(ui::ET_KEY_RELEASED
, up_event
->type());
276 EXPECT_EQ(ui::VKEY_SHIFT
,
277 static_cast<const ui::KeyEvent
*>(up_event
.get())->key_code());
279 // Enabled state is one shot, so next key event should not be shift modified.
280 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_A
));
281 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
282 EXPECT_FALSE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
284 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_A
));
285 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
286 EXPECT_FALSE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
289 TEST_F(StickyKeysTest
, BasicLockedScenarioTest
) {
290 scoped_ptr
<ui::KeyEvent
> ev
;
291 StickyKeysHandler
sticky_key(ui::EF_SHIFT_DOWN
);
293 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
295 // By typing shift key, internal state become ENABLED.
296 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
297 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
299 // By typing shift key again, internal state become LOCKED.
300 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
301 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
303 // All keyboard events including keyUp become shift modified.
304 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_A
));
305 int mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
306 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
308 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_A
));
309 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
310 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
312 // Locked state keeps after normal keyboard event.
313 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
315 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_B
));
316 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
317 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
319 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_B
));
320 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
321 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
323 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
325 // By typing shift key again, internal state become back to DISABLED.
326 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
327 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
330 TEST_F(StickyKeysTest
, NonTargetModifierTest
) {
331 scoped_ptr
<ui::KeyEvent
> ev
;
332 StickyKeysHandler
sticky_key(ui::EF_SHIFT_DOWN
);
334 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
336 // Non target modifier key does not affect internal state
337 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_MENU
));
338 int mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
339 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
340 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
342 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_MENU
));
343 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
344 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
345 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
347 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
348 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
350 // Non target modifier key does not affect internal state
351 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_MENU
));
352 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
353 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
354 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
356 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_MENU
));
357 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
358 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
359 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
361 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
362 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
364 // Non target modifier key does not affect internal state
365 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_MENU
));
366 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
367 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
368 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
370 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_MENU
));
371 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
372 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
373 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
376 TEST_F(StickyKeysTest
, NormalShortcutTest
) {
377 // Sticky keys should not be enabled if we perform a normal shortcut.
378 scoped_ptr
<ui::KeyEvent
> ev
;
379 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
381 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
383 // Perform ctrl+n shortcut.
384 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
385 int mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
386 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_N
));
387 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
388 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_N
));
389 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
390 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
391 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
392 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
394 // Sticky keys should not be enabled afterwards.
395 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
396 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
398 // Perform ctrl+n shortcut, releasing ctrl first.
399 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
400 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
401 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_N
));
402 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
403 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
404 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
405 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
406 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_N
));
407 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
409 // Sticky keys should not be enabled afterwards.
410 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
411 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
414 TEST_F(StickyKeysTest
, NormalModifiedClickTest
) {
415 scoped_ptr
<ui::KeyEvent
> kev
;
416 scoped_ptr
<ui::MouseEvent
> mev
;
417 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
419 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
421 // Perform ctrl+click.
422 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
423 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
424 mev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
425 bool released
= false;
426 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
427 mev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
428 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
430 // Sticky keys should not be enabled afterwards.
431 kev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
432 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
433 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
434 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
437 TEST_F(StickyKeysTest
, MouseMovedModifierTest
) {
438 scoped_ptr
<ui::KeyEvent
> kev
;
439 scoped_ptr
<ui::MouseEvent
> mev
;
440 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
442 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
444 // Press ctrl and handle mouse move events.
445 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
446 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
447 mev
.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(0, 0)));
448 bool released
= false;
449 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
450 mev
.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(100, 100)));
451 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
453 // Sticky keys should be enabled afterwards.
454 kev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
455 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
456 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
457 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
460 TEST_F(StickyKeysTest
, NormalModifiedScrollTest
) {
461 scoped_ptr
<ui::KeyEvent
> kev
;
462 scoped_ptr
<ui::ScrollEvent
> sev
;
463 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
465 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
467 // Perform ctrl+scroll.
468 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
469 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
470 sev
.reset(GenerateFlingScrollEvent(0, true));
471 bool released
= false;
472 sticky_key
.HandleScrollEvent(*sev
.get(), &mod_down_flags
, &released
);
473 sev
.reset(GenerateScrollEvent(10));
474 sticky_key
.HandleScrollEvent(*sev
.get(), &mod_down_flags
, &released
);
475 sev
.reset(GenerateFlingScrollEvent(10, false));
476 sticky_key
.HandleScrollEvent(*sev
.get(), &mod_down_flags
, &released
);
478 // Sticky keys should not be enabled afterwards.
479 kev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
480 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
481 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
482 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
485 TEST_F(StickyKeysTest
, MouseEventOneshot
) {
486 scoped_ptr
<ui::MouseEvent
> ev
;
487 scoped_ptr
<ui::KeyEvent
> kev
;
488 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
490 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
491 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
492 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
494 // We should still be in the ENABLED state until we get the mouse
496 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
497 bool released
= false;
498 int mod_down_flags
= 0;
499 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
500 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
501 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
503 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
506 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
507 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
508 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
510 // Making sure modifier key release event is dispatched in the right order.
511 EXPECT_TRUE(released
);
512 scoped_ptr
<ui::Event
> up_event
;
513 ASSERT_EQ(0, sticky_key
.GetModifierUpEvent(&up_event
));
514 EXPECT_TRUE(up_event
.get());
515 EXPECT_EQ(ui::ET_KEY_RELEASED
, up_event
->type());
516 EXPECT_EQ(ui::VKEY_CONTROL
,
517 static_cast<const ui::KeyEvent
*>(up_event
.get())->key_code());
519 // Enabled state is one shot, so next click should not be control modified.
520 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
523 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
524 EXPECT_FALSE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
526 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
529 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
530 EXPECT_FALSE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
533 TEST_F(StickyKeysTest
, MouseEventLocked
) {
534 scoped_ptr
<ui::MouseEvent
> ev
;
535 scoped_ptr
<ui::KeyEvent
> kev
;
536 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
538 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
540 // Pressing modifier key twice should make us enter lock state.
541 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
542 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
543 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
544 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
546 // Mouse events should not disable locked mode.
547 for (int i
= 0; i
< 3; ++i
) {
548 bool released
= false;
549 int mod_down_flags
= 0;
550 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
551 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
552 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
553 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
556 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
557 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
558 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
561 // Test with mouse wheel.
562 for (int i
= 0; i
< 3; ++i
) {
563 bool released
= false;
564 int mod_down_flags
= 0;
565 ev
.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta
));
566 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
567 ev
.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta
));
570 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
571 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
572 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
575 // Test mixed case with mouse events and key events.
576 ev
.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta
));
577 bool released
= false;
578 int mod_down_flags
= 0;
579 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
580 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
581 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_N
));
582 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
583 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
584 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
585 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
587 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
590 TEST_F(StickyKeysTest
, ScrollEventOneshot
) {
591 scoped_ptr
<ui::ScrollEvent
> ev
;
592 scoped_ptr
<ui::KeyEvent
> kev
;
593 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
595 int scroll_deltas
[] = {-10, 10};
596 for (int i
= 0; i
< 2; ++i
) {
597 // Enable sticky keys.
598 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
599 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
600 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
602 // Test a scroll sequence. Sticky keys should only be disabled at the end
603 // of the scroll sequence. Fling cancel event starts the scroll sequence.
604 ev
.reset(GenerateFlingScrollEvent(0, true));
605 bool released
= false;
606 int mod_down_flags
= 0;
607 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
608 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
609 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
611 // Scrolls should all be modified but not disable sticky keys.
612 for (int j
= 0; j
< 3; ++j
) {
613 ev
.reset(GenerateScrollEvent(scroll_deltas
[i
]));
616 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
617 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
618 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
621 // Fling start event ends scroll sequence.
622 ev
.reset(GenerateFlingScrollEvent(scroll_deltas
[i
], false));
625 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
626 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
627 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
629 scoped_ptr
<ui::Event
> up_event
;
630 EXPECT_TRUE(released
);
631 ASSERT_EQ(0, sticky_key
.GetModifierUpEvent(&up_event
));
632 EXPECT_TRUE(up_event
.get());
633 EXPECT_EQ(ui::ET_KEY_RELEASED
, up_event
->type());
634 EXPECT_EQ(ui::VKEY_CONTROL
,
635 static_cast<const ui::KeyEvent
*>(up_event
.get())->key_code());
639 TEST_F(StickyKeysTest
, ScrollDirectionChanged
) {
640 scoped_ptr
<ui::ScrollEvent
> ev
;
641 scoped_ptr
<ui::KeyEvent
> kev
;
642 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
644 // Test direction change with both boundary value and negative value.
645 const int direction_change_values
[2] = {0, -10};
646 for (int i
= 0; i
< 2; ++i
) {
647 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
648 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
650 // Fling cancel starts scroll sequence.
651 ev
.reset(GenerateFlingScrollEvent(0, true));
652 bool released
= false;
653 int mod_down_flags
= 0;
654 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
655 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
657 // Test that changing directions in a scroll sequence will
658 // return sticky keys to DISABLED state.
659 for (int j
= 0; j
< 3; ++j
) {
660 ev
.reset(GenerateScrollEvent(10));
663 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
664 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
665 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
668 ev
.reset(GenerateScrollEvent(direction_change_values
[i
]));
671 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
672 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
676 TEST_F(StickyKeysTest
, ScrollEventLocked
) {
677 scoped_ptr
<ui::ScrollEvent
> ev
;
678 scoped_ptr
<ui::KeyEvent
> kev
;
679 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
682 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
683 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
684 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
686 // Test scroll events are correctly modified in locked state.
687 for (int i
= 0; i
< 5; ++i
) {
688 // Fling cancel starts scroll sequence.
689 ev
.reset(GenerateFlingScrollEvent(0, true));
690 bool released
= false;
691 int mod_down_flags
= 0;
692 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
694 ev
.reset(GenerateScrollEvent(10));
697 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
698 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
699 ev
.reset(GenerateScrollEvent(-10));
700 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
701 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
703 // Fling start ends scroll sequence.
704 ev
.reset(GenerateFlingScrollEvent(-10, false));
705 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
708 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
711 TEST_F(StickyKeysTest
, SynthesizedEvents
) {
712 // Non-native, internally generated events should be properly handled
714 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
716 // Test non-native key events.
717 scoped_ptr
<ui::KeyEvent
> kev
;
718 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
719 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
721 kev
.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_K
));
722 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
723 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
724 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
726 kev
.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_RELEASED
, ui::VKEY_K
));
727 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
728 EXPECT_FALSE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
729 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
731 // Test non-native mouse events.
732 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
733 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
735 scoped_ptr
<ui::MouseEvent
> mev
;
736 mev
.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_PRESSED
,
738 bool released
= false;
739 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
740 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
741 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
743 mev
.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_RELEASED
,
747 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
748 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
749 EXPECT_TRUE(released
);
750 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());