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 "ui/aura/window.h"
12 #include "ui/aura/window_tree_host.h"
13 #include "ui/events/event_source.h"
14 #include "ui/events/event_utils.h"
21 #include "ui/events/test/events_test_utils_x11.h"
29 // The device id of the test touchpad device.
30 const unsigned int kTouchPadDeviceId
= 1;
35 class StickyKeysTest
: public test::AshTestBase
{
41 void SetUp() override
{
42 test::AshTestBase::SetUp();
44 // |target_| owned by root window of shell. It is still safe to delete
46 target_
= CreateTestWindowInShellWithId(0);
47 root_window_
= target_
->GetRootWindow();
50 ui::SetUpTouchPadForTest(kTouchPadDeviceId
);
54 void TearDown() override
{ test::AshTestBase::TearDown(); }
56 virtual void OnShortcutPressed() {
63 ui::KeyEvent
* GenerateKey(ui::EventType type
, ui::KeyboardCode code
) {
65 scoped_xevent_
.InitKeyEvent(type
, code
, 0);
66 return new ui::KeyEvent(scoped_xevent_
);
68 return GenerateSynthesizedKeyEvent(type
, code
);
72 // Creates a mouse event backed by a native XInput2 generic button event.
73 // This is the standard native event on Chromebooks.
74 ui::MouseEvent
* GenerateMouseEvent(ui::EventType type
) {
75 return GenerateMouseEventAt(type
, gfx::Point());
78 // Creates a mouse event backed by a native XInput2 generic button event.
79 // The |location| should be in physical pixels.
80 ui::MouseEvent
* GenerateMouseEventAt(ui::EventType type
,
81 const gfx::Point
& location
) {
83 scoped_xevent_
.InitGenericButtonEvent(
88 return new ui::MouseEvent(scoped_xevent_
);
90 return GenerateSynthesizedMouseEventAt(type
, location
);
94 ui::MouseWheelEvent
* GenerateMouseWheelEvent(int wheel_delta
) {
96 EXPECT_NE(0, wheel_delta
);
97 scoped_xevent_
.InitGenericMouseWheelEvent(
98 kTouchPadDeviceId
, wheel_delta
, 0);
99 ui::MouseWheelEvent
* event
= new ui::MouseWheelEvent(scoped_xevent_
);
100 ui::Event::DispatcherApi
dispatcher(event
);
101 dispatcher
.set_target(target_
);
104 return GenerateSynthesizedMouseWheelEvent(wheel_delta
);
108 ui::ScrollEvent
* GenerateScrollEvent(int scroll_delta
) {
110 scoped_xevent_
.InitScrollEvent(kTouchPadDeviceId
, // deviceid
112 scroll_delta
, // y_offset
113 0, // x_offset_ordinal
114 scroll_delta
, // y_offset_ordinal
116 ui::ScrollEvent
* event
= new ui::ScrollEvent(scoped_xevent_
);
117 ui::Event::DispatcherApi
dispatcher(event
);
118 dispatcher
.set_target(target_
);
121 ui::ScrollEvent
* event
= new ui::ScrollEvent(
122 ui::ET_SCROLL
, gfx::Point(0, 0), ui::EventTimeForNow(), ui::EF_NONE
,
124 scroll_delta
, // y_offset
125 0, // x_offset_ordinal
126 scroll_delta
, // y_offset_ordinal
128 ui::Event::DispatcherApi
dispatcher(event
);
129 dispatcher
.set_target(target_
);
134 ui::ScrollEvent
* GenerateFlingScrollEvent(int fling_delta
,
137 scoped_xevent_
.InitFlingScrollEvent(
138 kTouchPadDeviceId
, // deviceid
140 fling_delta
, // y_velocity
141 0, // x_velocity_ordinal
142 fling_delta
, // y_velocity_ordinal
143 is_cancel
); // is_cancel
144 ui::ScrollEvent
* event
= new ui::ScrollEvent(scoped_xevent_
);
145 ui::Event::DispatcherApi
dispatcher(event
);
146 dispatcher
.set_target(target_
);
149 ui::ScrollEvent
* event
= new ui::ScrollEvent(
150 is_cancel
? ui::ET_SCROLL_FLING_CANCEL
: ui::ET_SCROLL_FLING_START
,
151 gfx::Point(0, 0), ui::EventTimeForNow(), ui::EF_NONE
,
153 fling_delta
, // y_velocity
154 0, // x_velocity_ordinal
155 fling_delta
, // y_velocity_ordinal
157 ui::Event::DispatcherApi
dispatcher(event
);
158 dispatcher
.set_target(target_
);
163 // Creates a synthesized KeyEvent that is not backed by a native event.
164 ui::KeyEvent
* GenerateSynthesizedKeyEvent(ui::EventType type
,
165 ui::KeyboardCode code
) {
166 return new ui::KeyEvent(type
, code
, ui::EF_NONE
);
169 // Creates a synthesized MouseEvent that is not backed by a native event.
170 ui::MouseEvent
* GenerateSynthesizedMouseEventAt(ui::EventType event_type
,
171 const gfx::Point
& location
) {
172 ui::MouseEvent
* event
= new ui::MouseEvent(
173 event_type
, location
, location
, ui::EventTimeForNow(),
174 ui::EF_LEFT_MOUSE_BUTTON
, ui::EF_LEFT_MOUSE_BUTTON
);
175 ui::Event::DispatcherApi
dispatcher(event
);
176 dispatcher
.set_target(target_
);
180 // Creates a synthesized mouse press or release event.
181 ui::MouseEvent
* GenerateSynthesizedMouseClickEvent(
183 const gfx::Point
& location
) {
184 return GenerateSynthesizedMouseEventAt(type
, location
);
187 // Creates a synthesized ET_MOUSE_MOVED event.
188 ui::MouseEvent
* GenerateSynthesizedMouseMoveEvent(
189 const gfx::Point
& location
) {
190 return GenerateSynthesizedMouseEventAt(ui::ET_MOUSE_MOVED
, location
);
193 // Creates a synthesized MouseWHeel event.
194 ui::MouseWheelEvent
* GenerateSynthesizedMouseWheelEvent(int wheel_delta
) {
195 scoped_ptr
<ui::MouseEvent
> mev(
196 GenerateSynthesizedMouseEventAt(ui::ET_MOUSEWHEEL
, gfx::Point(0, 0)));
197 ui::MouseWheelEvent
* event
= new ui::MouseWheelEvent(*mev
, 0, wheel_delta
);
198 ui::Event::DispatcherApi
dispatcher(event
);
199 dispatcher
.set_target(target_
);
203 void SendActivateStickyKeyPattern(StickyKeysHandler
* handler
,
204 ui::KeyboardCode key_code
) {
205 bool released
= false;
207 scoped_ptr
<ui::KeyEvent
> ev
;
208 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, key_code
));
209 handler
->HandleKeyEvent(*ev
.get(), key_code
, &down_flags
, &released
);
210 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, key_code
));
211 handler
->HandleKeyEvent(*ev
.get(), key_code
, &down_flags
, &released
);
214 bool HandleKeyEvent(const ui::KeyEvent
& key_event
,
215 StickyKeysHandler
* handler
,
218 return handler
->HandleKeyEvent(key_event
, key_event
.key_code(), down
, up
);
221 int HandleKeyEventForDownFlags(const ui::KeyEvent
& key_event
,
222 StickyKeysHandler
* handler
) {
223 bool released
= false;
225 handler
->HandleKeyEvent(key_event
, key_event
.key_code(), &down
, &released
);
229 aura::Window
* target() { return target_
; }
232 // Owned by root window of shell, but we can still delete |target_| safely.
233 aura::Window
* target_
;
234 // The root window of |target_|. Not owned.
235 aura::Window
* root_window_
;
238 // Used to construct the various X events.
239 ui::ScopedXI2Event scoped_xevent_
;
242 DISALLOW_COPY_AND_ASSIGN(StickyKeysTest
);
245 TEST_F(StickyKeysTest
, BasicOneshotScenarioTest
) {
246 scoped_ptr
<ui::KeyEvent
> ev
;
247 StickyKeysHandler
sticky_key(ui::EF_SHIFT_DOWN
);
249 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
251 // By typing Shift key, internal state become ENABLED.
252 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
253 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
255 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_A
));
256 bool released
= false;
257 int mod_down_flags
= 0;
258 HandleKeyEvent(*ev
.get(), &sticky_key
, &mod_down_flags
, &released
);
259 // Next keyboard event is shift modified.
260 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
261 // Modifier release notification happens.
262 EXPECT_TRUE(released
);
264 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_A
));
267 HandleKeyEvent(*ev
.get(), &sticky_key
, &mod_down_flags
, &released
);
269 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
270 // Making sure Shift up keyboard event is available.
271 scoped_ptr
<ui::Event
> up_event
;
272 ASSERT_EQ(0, sticky_key
.GetModifierUpEvent(&up_event
));
273 EXPECT_TRUE(up_event
.get());
274 EXPECT_EQ(ui::ET_KEY_RELEASED
, up_event
->type());
275 EXPECT_EQ(ui::VKEY_SHIFT
,
276 static_cast<const ui::KeyEvent
*>(up_event
.get())->key_code());
278 // Enabled state is one shot, so next key event should not be shift modified.
279 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_A
));
280 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
281 EXPECT_FALSE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
283 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_A
));
284 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
285 EXPECT_FALSE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
288 TEST_F(StickyKeysTest
, BasicLockedScenarioTest
) {
289 scoped_ptr
<ui::KeyEvent
> ev
;
290 StickyKeysHandler
sticky_key(ui::EF_SHIFT_DOWN
);
292 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
294 // By typing shift key, internal state become ENABLED.
295 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
296 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
298 // By typing shift key again, internal state become LOCKED.
299 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
300 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
302 // All keyboard events including keyUp become shift modified.
303 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_A
));
304 int mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
305 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
307 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_A
));
308 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
309 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
311 // Locked state keeps after normal keyboard event.
312 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
314 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_B
));
315 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
316 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
318 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_B
));
319 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
320 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
322 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
324 // By typing shift key again, internal state become back to DISABLED.
325 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
326 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
329 TEST_F(StickyKeysTest
, NonTargetModifierTest
) {
330 scoped_ptr
<ui::KeyEvent
> ev
;
331 StickyKeysHandler
sticky_key(ui::EF_SHIFT_DOWN
);
333 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
335 // Non target modifier key does not affect internal state
336 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_MENU
));
337 int mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
338 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
339 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
341 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_MENU
));
342 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
343 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
344 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
346 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
347 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
349 // Non target modifier key does not affect internal state
350 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_MENU
));
351 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
352 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
353 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
355 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_MENU
));
356 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
357 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
358 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
360 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
361 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
363 // Non target modifier key does not affect internal state
364 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_MENU
));
365 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
366 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
367 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
369 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_MENU
));
370 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
371 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
372 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
375 TEST_F(StickyKeysTest
, NormalShortcutTest
) {
376 // Sticky keys should not be enabled if we perform a normal shortcut.
377 scoped_ptr
<ui::KeyEvent
> ev
;
378 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
380 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
382 // Perform ctrl+n shortcut.
383 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
384 int mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
385 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_N
));
386 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
387 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_N
));
388 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
389 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
390 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
391 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
393 // Sticky keys should not be enabled afterwards.
394 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
395 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
397 // Perform ctrl+n shortcut, releasing ctrl first.
398 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
399 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
400 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_N
));
401 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
402 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
403 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
404 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
405 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_N
));
406 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
408 // Sticky keys should not be enabled afterwards.
409 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
410 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
413 TEST_F(StickyKeysTest
, NormalModifiedClickTest
) {
414 scoped_ptr
<ui::KeyEvent
> kev
;
415 scoped_ptr
<ui::MouseEvent
> mev
;
416 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
418 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
420 // Perform ctrl+click.
421 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
422 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
423 mev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
424 bool released
= false;
425 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
426 mev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
427 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
429 // Sticky keys should not be enabled afterwards.
430 kev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
431 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
432 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
433 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
436 TEST_F(StickyKeysTest
, MouseMovedModifierTest
) {
437 scoped_ptr
<ui::KeyEvent
> kev
;
438 scoped_ptr
<ui::MouseEvent
> mev
;
439 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
441 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
443 // Press ctrl and handle mouse move events.
444 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
445 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
446 mev
.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(0, 0)));
447 bool released
= false;
448 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
449 mev
.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(100, 100)));
450 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
452 // Sticky keys should be enabled afterwards.
453 kev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
454 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
455 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
456 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
459 TEST_F(StickyKeysTest
, NormalModifiedScrollTest
) {
460 scoped_ptr
<ui::KeyEvent
> kev
;
461 scoped_ptr
<ui::ScrollEvent
> sev
;
462 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
464 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
466 // Perform ctrl+scroll.
467 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
468 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
469 sev
.reset(GenerateFlingScrollEvent(0, true));
470 bool released
= false;
471 sticky_key
.HandleScrollEvent(*sev
.get(), &mod_down_flags
, &released
);
472 sev
.reset(GenerateScrollEvent(10));
473 sticky_key
.HandleScrollEvent(*sev
.get(), &mod_down_flags
, &released
);
474 sev
.reset(GenerateFlingScrollEvent(10, false));
475 sticky_key
.HandleScrollEvent(*sev
.get(), &mod_down_flags
, &released
);
477 // Sticky keys should not be enabled afterwards.
478 kev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
479 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
480 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
481 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
484 TEST_F(StickyKeysTest
, MouseEventOneshot
) {
485 scoped_ptr
<ui::MouseEvent
> ev
;
486 scoped_ptr
<ui::KeyEvent
> kev
;
487 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
489 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
490 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
491 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
493 // We should still be in the ENABLED state until we get the mouse
495 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
496 bool released
= false;
497 int mod_down_flags
= 0;
498 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
499 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
500 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
502 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
505 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
506 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
507 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
509 // Making sure modifier key release event is dispatched in the right order.
510 EXPECT_TRUE(released
);
511 scoped_ptr
<ui::Event
> up_event
;
512 ASSERT_EQ(0, sticky_key
.GetModifierUpEvent(&up_event
));
513 EXPECT_TRUE(up_event
.get());
514 EXPECT_EQ(ui::ET_KEY_RELEASED
, up_event
->type());
515 EXPECT_EQ(ui::VKEY_CONTROL
,
516 static_cast<const ui::KeyEvent
*>(up_event
.get())->key_code());
518 // Enabled state is one shot, so next click should not be control modified.
519 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
522 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
523 EXPECT_FALSE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
525 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
528 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
529 EXPECT_FALSE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
532 TEST_F(StickyKeysTest
, MouseEventLocked
) {
533 scoped_ptr
<ui::MouseEvent
> ev
;
534 scoped_ptr
<ui::KeyEvent
> kev
;
535 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
537 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
539 // Pressing modifier key twice should make us enter lock state.
540 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
541 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
542 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
543 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
545 // Mouse events should not disable locked mode.
546 for (int i
= 0; i
< 3; ++i
) {
547 bool released
= false;
548 int mod_down_flags
= 0;
549 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
550 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
551 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
552 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
555 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
556 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
557 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
560 // Test with mouse wheel.
561 for (int i
= 0; i
< 3; ++i
) {
562 bool released
= false;
563 int mod_down_flags
= 0;
564 ev
.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta
));
565 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
566 ev
.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta
));
569 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
570 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
571 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
574 // Test mixed case with mouse events and key events.
575 ev
.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta
));
576 bool released
= false;
577 int mod_down_flags
= 0;
578 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
579 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
580 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_N
));
581 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
582 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
583 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
584 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
586 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
589 TEST_F(StickyKeysTest
, ScrollEventOneshot
) {
590 scoped_ptr
<ui::ScrollEvent
> ev
;
591 scoped_ptr
<ui::KeyEvent
> kev
;
592 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
594 int scroll_deltas
[] = {-10, 10};
595 for (int i
= 0; i
< 2; ++i
) {
596 // Enable sticky keys.
597 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
598 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
599 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
601 // Test a scroll sequence. Sticky keys should only be disabled at the end
602 // of the scroll sequence. Fling cancel event starts the scroll sequence.
603 ev
.reset(GenerateFlingScrollEvent(0, true));
604 bool released
= false;
605 int mod_down_flags
= 0;
606 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
607 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
608 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
610 // Scrolls should all be modified but not disable sticky keys.
611 for (int j
= 0; j
< 3; ++j
) {
612 ev
.reset(GenerateScrollEvent(scroll_deltas
[i
]));
615 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
616 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
617 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
620 // Fling start event ends scroll sequence.
621 ev
.reset(GenerateFlingScrollEvent(scroll_deltas
[i
], false));
624 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
625 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
626 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
628 scoped_ptr
<ui::Event
> up_event
;
629 EXPECT_TRUE(released
);
630 ASSERT_EQ(0, sticky_key
.GetModifierUpEvent(&up_event
));
631 EXPECT_TRUE(up_event
.get());
632 EXPECT_EQ(ui::ET_KEY_RELEASED
, up_event
->type());
633 EXPECT_EQ(ui::VKEY_CONTROL
,
634 static_cast<const ui::KeyEvent
*>(up_event
.get())->key_code());
638 TEST_F(StickyKeysTest
, ScrollDirectionChanged
) {
639 scoped_ptr
<ui::ScrollEvent
> ev
;
640 scoped_ptr
<ui::KeyEvent
> kev
;
641 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
643 // Test direction change with both boundary value and negative value.
644 const int direction_change_values
[2] = {0, -10};
645 for (int i
= 0; i
< 2; ++i
) {
646 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
647 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
649 // Fling cancel starts scroll sequence.
650 ev
.reset(GenerateFlingScrollEvent(0, true));
651 bool released
= false;
652 int mod_down_flags
= 0;
653 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
654 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
656 // Test that changing directions in a scroll sequence will
657 // return sticky keys to DISABLED state.
658 for (int j
= 0; j
< 3; ++j
) {
659 ev
.reset(GenerateScrollEvent(10));
662 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
663 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
664 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
667 ev
.reset(GenerateScrollEvent(direction_change_values
[i
]));
670 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
671 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
675 TEST_F(StickyKeysTest
, ScrollEventLocked
) {
676 scoped_ptr
<ui::ScrollEvent
> ev
;
677 scoped_ptr
<ui::KeyEvent
> kev
;
678 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
681 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
682 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
683 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
685 // Test scroll events are correctly modified in locked state.
686 for (int i
= 0; i
< 5; ++i
) {
687 // Fling cancel starts scroll sequence.
688 ev
.reset(GenerateFlingScrollEvent(0, true));
689 bool released
= false;
690 int mod_down_flags
= 0;
691 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
693 ev
.reset(GenerateScrollEvent(10));
696 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
697 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
698 ev
.reset(GenerateScrollEvent(-10));
699 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
700 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
702 // Fling start ends scroll sequence.
703 ev
.reset(GenerateFlingScrollEvent(-10, false));
704 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
707 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
710 TEST_F(StickyKeysTest
, SynthesizedEvents
) {
711 // Non-native, internally generated events should be properly handled
713 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
715 // Test non-native key events.
716 scoped_ptr
<ui::KeyEvent
> kev
;
717 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
718 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
720 kev
.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_K
));
721 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
722 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
723 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
725 kev
.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_RELEASED
, ui::VKEY_K
));
726 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
727 EXPECT_FALSE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
728 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
730 // Test non-native mouse events.
731 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
732 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
734 scoped_ptr
<ui::MouseEvent
> mev
;
735 mev
.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_PRESSED
,
737 bool released
= false;
738 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
739 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
740 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
742 mev
.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_RELEASED
,
746 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
747 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
748 EXPECT_TRUE(released
);
749 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());