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_source.h"
20 #include "ui/events/test/events_test_utils_x11.h"
26 // The device id of the test touchpad device.
27 const unsigned int kTouchPadDeviceId
= 1;
31 class StickyKeysTest
: public test::AshTestBase
{
37 virtual void SetUp() override
{
38 test::AshTestBase::SetUp();
40 // |target_| owned by root window of shell. It is still safe to delete
42 target_
= CreateTestWindowInShellWithId(0);
43 root_window_
= target_
->GetRootWindow();
45 ui::SetUpTouchPadForTest(kTouchPadDeviceId
);
48 virtual void TearDown() override
{
49 test::AshTestBase::TearDown();
52 virtual void OnShortcutPressed() {
59 ui::KeyEvent
* GenerateKey(ui::EventType type
, ui::KeyboardCode code
) {
60 scoped_xevent_
.InitKeyEvent(type
, code
, 0);
61 ui::KeyEvent
* event
= new ui::KeyEvent(scoped_xevent_
);
65 // Creates a mouse event backed by a native XInput2 generic button event.
66 // This is the standard native event on Chromebooks.
67 ui::MouseEvent
* GenerateMouseEvent(ui::EventType type
) {
68 return GenerateMouseEventAt(type
, gfx::Point());
71 // Creates a mouse event backed by a native XInput2 generic button event.
72 // The |location| should be in physical pixels.
73 ui::MouseEvent
* GenerateMouseEventAt(ui::EventType type
,
74 const gfx::Point
& location
) {
75 scoped_xevent_
.InitGenericButtonEvent(
80 ui::MouseEvent
* event
= new ui::MouseEvent(scoped_xevent_
);
84 ui::MouseWheelEvent
* GenerateMouseWheelEvent(int wheel_delta
) {
85 EXPECT_NE(0, wheel_delta
);
86 scoped_xevent_
.InitGenericMouseWheelEvent(
87 kTouchPadDeviceId
, wheel_delta
, 0);
88 ui::MouseWheelEvent
* event
= new ui::MouseWheelEvent(scoped_xevent_
);
89 ui::Event::DispatcherApi
dispatcher(event
);
90 dispatcher
.set_target(target_
);
94 ui::ScrollEvent
* GenerateScrollEvent(int scroll_delta
) {
95 scoped_xevent_
.InitScrollEvent(kTouchPadDeviceId
, // deviceid
97 scroll_delta
, // y_offset
98 0, // x_offset_ordinal
99 scroll_delta
, // y_offset_ordinal
101 ui::ScrollEvent
* event
= new ui::ScrollEvent(scoped_xevent_
);
102 ui::Event::DispatcherApi
dispatcher(event
);
103 dispatcher
.set_target(target_
);
107 ui::ScrollEvent
* GenerateFlingScrollEvent(int fling_delta
,
109 scoped_xevent_
.InitFlingScrollEvent(
110 kTouchPadDeviceId
, // deviceid
112 fling_delta
, // y_velocity
113 0, // x_velocity_ordinal
114 fling_delta
, // y_velocity_ordinal
115 is_cancel
); // is_cancel
116 ui::ScrollEvent
* event
= new ui::ScrollEvent(scoped_xevent_
);
117 ui::Event::DispatcherApi
dispatcher(event
);
118 dispatcher
.set_target(target_
);
122 // Creates a synthesized KeyEvent that is not backed by a native event.
123 ui::KeyEvent
* GenerateSynthesizedKeyEvent(ui::EventType type
,
124 ui::KeyboardCode code
) {
125 return new ui::KeyEvent(type
, code
, ui::EF_NONE
);
128 // Creates a synthesized MouseEvent that is not backed by a native event.
129 ui::MouseEvent
* GenerateSynthesizedMouseEventAt(ui::EventType event_type
,
130 const gfx::Point
& location
) {
131 ui::MouseEvent
* event
= new ui::MouseEvent(event_type
,
134 ui::EF_LEFT_MOUSE_BUTTON
,
135 ui::EF_LEFT_MOUSE_BUTTON
);
136 ui::Event::DispatcherApi
dispatcher(event
);
137 dispatcher
.set_target(target_
);
141 // Creates a synthesized mouse press or release event.
142 ui::MouseEvent
* GenerateSynthesizedMouseClickEvent(
144 const gfx::Point
& location
) {
145 return GenerateSynthesizedMouseEventAt(type
, location
);
148 // Creates a synthesized ET_MOUSE_MOVED event.
149 ui::MouseEvent
* GenerateSynthesizedMouseMoveEvent(
150 const gfx::Point
& location
) {
151 return GenerateSynthesizedMouseEventAt(ui::ET_MOUSE_MOVED
, location
);
154 // Creates a synthesized MouseWHeel event.
155 ui::MouseWheelEvent
* GenerateSynthesizedMouseWheelEvent(int wheel_delta
) {
156 scoped_ptr
<ui::MouseEvent
> mev(
157 GenerateSynthesizedMouseEventAt(ui::ET_MOUSEWHEEL
, gfx::Point(0, 0)));
158 ui::MouseWheelEvent
* event
= new ui::MouseWheelEvent(*mev
, 0, wheel_delta
);
159 ui::Event::DispatcherApi
dispatcher(event
);
160 dispatcher
.set_target(target_
);
164 void SendActivateStickyKeyPattern(StickyKeysHandler
* handler
,
165 ui::KeyboardCode key_code
) {
166 bool released
= false;
168 scoped_ptr
<ui::KeyEvent
> ev
;
169 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, key_code
));
170 handler
->HandleKeyEvent(*ev
.get(), key_code
, &down_flags
, &released
);
171 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, key_code
));
172 handler
->HandleKeyEvent(*ev
.get(), key_code
, &down_flags
, &released
);
175 bool HandleKeyEvent(const ui::KeyEvent
& key_event
,
176 StickyKeysHandler
* handler
,
179 return handler
->HandleKeyEvent(key_event
, key_event
.key_code(), down
, up
);
182 int HandleKeyEventForDownFlags(const ui::KeyEvent
& key_event
,
183 StickyKeysHandler
* handler
) {
184 bool released
= false;
186 handler
->HandleKeyEvent(key_event
, key_event
.key_code(), &down
, &released
);
190 aura::Window
* target() { return target_
; }
193 // Owned by root window of shell, but we can still delete |target_| safely.
194 aura::Window
* target_
;
195 // The root window of |target_|. Not owned.
196 aura::Window
* root_window_
;
198 // Used to construct the various X events.
199 ui::ScopedXI2Event scoped_xevent_
;
201 DISALLOW_COPY_AND_ASSIGN(StickyKeysTest
);
204 TEST_F(StickyKeysTest
, BasicOneshotScenarioTest
) {
205 scoped_ptr
<ui::KeyEvent
> ev
;
206 StickyKeysHandler
sticky_key(ui::EF_SHIFT_DOWN
);
208 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
210 // By typing Shift key, internal state become ENABLED.
211 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
212 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
214 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_A
));
215 bool released
= false;
216 int mod_down_flags
= 0;
217 HandleKeyEvent(*ev
.get(), &sticky_key
, &mod_down_flags
, &released
);
218 // Next keyboard event is shift modified.
219 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
220 // Modifier release notification happens.
221 EXPECT_TRUE(released
);
223 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_A
));
226 HandleKeyEvent(*ev
.get(), &sticky_key
, &mod_down_flags
, &released
);
228 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
229 // Making sure Shift up keyboard event is available.
230 scoped_ptr
<ui::Event
> up_event
;
231 ASSERT_EQ(0, sticky_key
.GetModifierUpEvent(&up_event
));
232 EXPECT_TRUE(up_event
.get());
233 EXPECT_EQ(ui::ET_KEY_RELEASED
, up_event
->type());
234 EXPECT_EQ(ui::VKEY_SHIFT
,
235 static_cast<const ui::KeyEvent
*>(up_event
.get())->key_code());
237 // Enabled state is one shot, so next key event should not be shift modified.
238 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_A
));
239 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
240 EXPECT_FALSE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
242 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_A
));
243 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
244 EXPECT_FALSE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
247 TEST_F(StickyKeysTest
, BasicLockedScenarioTest
) {
248 scoped_ptr
<ui::KeyEvent
> ev
;
249 StickyKeysHandler
sticky_key(ui::EF_SHIFT_DOWN
);
251 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
253 // By typing shift key, internal state become ENABLED.
254 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
255 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
257 // By typing shift key again, internal state become LOCKED.
258 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
259 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
261 // All keyboard events including keyUp become shift modified.
262 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_A
));
263 int mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
264 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
266 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_A
));
267 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
268 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
270 // Locked state keeps after normal keyboard event.
271 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
273 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_B
));
274 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
275 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
277 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_B
));
278 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
279 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
281 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
283 // By typing shift key again, internal state become back to DISABLED.
284 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
285 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
288 TEST_F(StickyKeysTest
, NonTargetModifierTest
) {
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 // Non target modifier key does not affect internal state
295 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_MENU
));
296 int mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
297 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
298 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
300 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_MENU
));
301 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
302 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
303 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
305 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
306 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
308 // Non target modifier key does not affect internal state
309 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_MENU
));
310 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
311 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
312 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
314 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_MENU
));
315 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
316 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
317 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
319 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
320 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
322 // Non target modifier key does not affect internal state
323 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_MENU
));
324 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
325 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
326 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
328 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_MENU
));
329 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
330 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
331 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
334 TEST_F(StickyKeysTest
, NormalShortcutTest
) {
335 // Sticky keys should not be enabled if we perform a normal shortcut.
336 scoped_ptr
<ui::KeyEvent
> ev
;
337 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
339 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
341 // Perform ctrl+n shortcut.
342 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
343 int mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
344 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_N
));
345 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
346 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_N
));
347 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
348 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
349 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
350 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
352 // Sticky keys should not be enabled afterwards.
353 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
354 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
356 // Perform ctrl+n shortcut, releasing ctrl first.
357 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
358 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
359 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_N
));
360 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
361 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
362 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
363 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
364 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_N
));
365 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
367 // Sticky keys should not be enabled afterwards.
368 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
369 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
372 TEST_F(StickyKeysTest
, NormalModifiedClickTest
) {
373 scoped_ptr
<ui::KeyEvent
> kev
;
374 scoped_ptr
<ui::MouseEvent
> mev
;
375 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
377 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
379 // Perform ctrl+click.
380 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
381 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
382 mev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
383 bool released
= false;
384 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
385 mev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
386 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
388 // Sticky keys should not be enabled afterwards.
389 kev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
390 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
391 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
392 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
395 TEST_F(StickyKeysTest
, MouseMovedModifierTest
) {
396 scoped_ptr
<ui::KeyEvent
> kev
;
397 scoped_ptr
<ui::MouseEvent
> mev
;
398 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
400 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
402 // Press ctrl and handle mouse move events.
403 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
404 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
405 mev
.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(0, 0)));
406 bool released
= false;
407 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
408 mev
.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(100, 100)));
409 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
411 // Sticky keys should be enabled afterwards.
412 kev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
413 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
414 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
415 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
418 TEST_F(StickyKeysTest
, NormalModifiedScrollTest
) {
419 scoped_ptr
<ui::KeyEvent
> kev
;
420 scoped_ptr
<ui::ScrollEvent
> sev
;
421 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
423 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
425 // Perform ctrl+scroll.
426 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
427 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
428 sev
.reset(GenerateFlingScrollEvent(0, true));
429 bool released
= false;
430 sticky_key
.HandleScrollEvent(*sev
.get(), &mod_down_flags
, &released
);
431 sev
.reset(GenerateScrollEvent(10));
432 sticky_key
.HandleScrollEvent(*sev
.get(), &mod_down_flags
, &released
);
433 sev
.reset(GenerateFlingScrollEvent(10, false));
434 sticky_key
.HandleScrollEvent(*sev
.get(), &mod_down_flags
, &released
);
436 // Sticky keys should not be enabled afterwards.
437 kev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
438 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
439 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
440 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
443 TEST_F(StickyKeysTest
, MouseEventOneshot
) {
444 scoped_ptr
<ui::MouseEvent
> ev
;
445 scoped_ptr
<ui::KeyEvent
> kev
;
446 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
448 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
449 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
450 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
452 // We should still be in the ENABLED state until we get the mouse
454 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
455 bool released
= false;
456 int mod_down_flags
= 0;
457 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
458 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
459 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
461 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
464 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
465 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
466 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
468 // Making sure modifier key release event is dispatched in the right order.
469 EXPECT_TRUE(released
);
470 scoped_ptr
<ui::Event
> up_event
;
471 ASSERT_EQ(0, sticky_key
.GetModifierUpEvent(&up_event
));
472 EXPECT_TRUE(up_event
.get());
473 EXPECT_EQ(ui::ET_KEY_RELEASED
, up_event
->type());
474 EXPECT_EQ(ui::VKEY_CONTROL
,
475 static_cast<const ui::KeyEvent
*>(up_event
.get())->key_code());
477 // Enabled state is one shot, so next click should not be control modified.
478 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
481 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
482 EXPECT_FALSE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
484 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
487 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
488 EXPECT_FALSE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
491 TEST_F(StickyKeysTest
, MouseEventLocked
) {
492 scoped_ptr
<ui::MouseEvent
> ev
;
493 scoped_ptr
<ui::KeyEvent
> kev
;
494 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
496 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
498 // Pressing modifier key twice should make us enter lock state.
499 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
500 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
501 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
502 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
504 // Mouse events should not disable locked mode.
505 for (int i
= 0; i
< 3; ++i
) {
506 bool released
= false;
507 int mod_down_flags
= 0;
508 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
509 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
510 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
511 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
514 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
515 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
516 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
519 // Test with mouse wheel.
520 for (int i
= 0; i
< 3; ++i
) {
521 bool released
= false;
522 int mod_down_flags
= 0;
523 ev
.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta
));
524 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
525 ev
.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta
));
528 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
529 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
530 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
533 // Test mixed case with mouse events and key events.
534 ev
.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta
));
535 bool released
= false;
536 int mod_down_flags
= 0;
537 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
538 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
539 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_N
));
540 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
541 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
542 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
543 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
545 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
548 TEST_F(StickyKeysTest
, ScrollEventOneshot
) {
549 scoped_ptr
<ui::ScrollEvent
> ev
;
550 scoped_ptr
<ui::KeyEvent
> kev
;
551 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
553 int scroll_deltas
[] = {-10, 10};
554 for (int i
= 0; i
< 2; ++i
) {
555 // Enable sticky keys.
556 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
557 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
558 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
560 // Test a scroll sequence. Sticky keys should only be disabled at the end
561 // of the scroll sequence. Fling cancel event starts the scroll sequence.
562 ev
.reset(GenerateFlingScrollEvent(0, true));
563 bool released
= false;
564 int mod_down_flags
= 0;
565 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
566 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
567 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
569 // Scrolls should all be modified but not disable sticky keys.
570 for (int j
= 0; j
< 3; ++j
) {
571 ev
.reset(GenerateScrollEvent(scroll_deltas
[i
]));
574 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
575 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
576 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
579 // Fling start event ends scroll sequence.
580 ev
.reset(GenerateFlingScrollEvent(scroll_deltas
[i
], false));
583 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
584 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
585 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
587 scoped_ptr
<ui::Event
> up_event
;
588 EXPECT_TRUE(released
);
589 ASSERT_EQ(0, sticky_key
.GetModifierUpEvent(&up_event
));
590 EXPECT_TRUE(up_event
.get());
591 EXPECT_EQ(ui::ET_KEY_RELEASED
, up_event
->type());
592 EXPECT_EQ(ui::VKEY_CONTROL
,
593 static_cast<const ui::KeyEvent
*>(up_event
.get())->key_code());
597 TEST_F(StickyKeysTest
, ScrollDirectionChanged
) {
598 scoped_ptr
<ui::ScrollEvent
> ev
;
599 scoped_ptr
<ui::KeyEvent
> kev
;
600 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
602 // Test direction change with both boundary value and negative value.
603 const int direction_change_values
[2] = {0, -10};
604 for (int i
= 0; i
< 2; ++i
) {
605 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
606 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
608 // Fling cancel starts scroll sequence.
609 ev
.reset(GenerateFlingScrollEvent(0, true));
610 bool released
= false;
611 int mod_down_flags
= 0;
612 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
613 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
615 // Test that changing directions in a scroll sequence will
616 // return sticky keys to DISABLED state.
617 for (int j
= 0; j
< 3; ++j
) {
618 ev
.reset(GenerateScrollEvent(10));
621 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
622 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
623 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
626 ev
.reset(GenerateScrollEvent(direction_change_values
[i
]));
629 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
630 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
634 TEST_F(StickyKeysTest
, ScrollEventLocked
) {
635 scoped_ptr
<ui::ScrollEvent
> ev
;
636 scoped_ptr
<ui::KeyEvent
> kev
;
637 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
640 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
641 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
642 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
644 // Test scroll events are correctly modified in locked state.
645 for (int i
= 0; i
< 5; ++i
) {
646 // Fling cancel starts scroll sequence.
647 ev
.reset(GenerateFlingScrollEvent(0, true));
648 bool released
= false;
649 int mod_down_flags
= 0;
650 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
652 ev
.reset(GenerateScrollEvent(10));
655 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
656 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
657 ev
.reset(GenerateScrollEvent(-10));
658 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
659 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
661 // Fling start ends scroll sequence.
662 ev
.reset(GenerateFlingScrollEvent(-10, false));
663 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
666 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
669 TEST_F(StickyKeysTest
, SynthesizedEvents
) {
670 // Non-native, internally generated events should be properly handled
672 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
674 // Test non-native key events.
675 scoped_ptr
<ui::KeyEvent
> kev
;
676 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
677 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
679 kev
.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_K
));
680 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
681 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
682 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
684 kev
.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_RELEASED
, ui::VKEY_K
));
685 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
686 EXPECT_FALSE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
687 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
689 // Test non-native mouse events.
690 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
691 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
693 scoped_ptr
<ui::MouseEvent
> mev
;
694 mev
.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_PRESSED
,
696 bool released
= false;
697 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
698 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
699 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
701 mev
.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_RELEASED
,
705 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
706 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
707 EXPECT_TRUE(released
);
708 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());