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/event_utils.h"
21 #include "ui/events/test/events_test_utils_x11.h"
27 // The device id of the test touchpad device.
28 const unsigned int kTouchPadDeviceId
= 1;
32 class StickyKeysTest
: public test::AshTestBase
{
38 void SetUp() override
{
39 test::AshTestBase::SetUp();
41 // |target_| owned by root window of shell. It is still safe to delete
43 target_
= CreateTestWindowInShellWithId(0);
44 root_window_
= target_
->GetRootWindow();
46 ui::SetUpTouchPadForTest(kTouchPadDeviceId
);
49 void TearDown() override
{ test::AshTestBase::TearDown(); }
51 virtual void OnShortcutPressed() {
58 ui::KeyEvent
* GenerateKey(ui::EventType type
, ui::KeyboardCode code
) {
59 scoped_xevent_
.InitKeyEvent(type
, code
, 0);
60 ui::KeyEvent
* event
= new ui::KeyEvent(scoped_xevent_
);
64 // Creates a mouse event backed by a native XInput2 generic button event.
65 // This is the standard native event on Chromebooks.
66 ui::MouseEvent
* GenerateMouseEvent(ui::EventType type
) {
67 return GenerateMouseEventAt(type
, gfx::Point());
70 // Creates a mouse event backed by a native XInput2 generic button event.
71 // The |location| should be in physical pixels.
72 ui::MouseEvent
* GenerateMouseEventAt(ui::EventType type
,
73 const gfx::Point
& location
) {
74 scoped_xevent_
.InitGenericButtonEvent(
79 ui::MouseEvent
* event
= new ui::MouseEvent(scoped_xevent_
);
83 ui::MouseWheelEvent
* GenerateMouseWheelEvent(int wheel_delta
) {
84 EXPECT_NE(0, wheel_delta
);
85 scoped_xevent_
.InitGenericMouseWheelEvent(
86 kTouchPadDeviceId
, wheel_delta
, 0);
87 ui::MouseWheelEvent
* event
= new ui::MouseWheelEvent(scoped_xevent_
);
88 ui::Event::DispatcherApi
dispatcher(event
);
89 dispatcher
.set_target(target_
);
93 ui::ScrollEvent
* GenerateScrollEvent(int scroll_delta
) {
94 scoped_xevent_
.InitScrollEvent(kTouchPadDeviceId
, // deviceid
96 scroll_delta
, // y_offset
97 0, // x_offset_ordinal
98 scroll_delta
, // y_offset_ordinal
100 ui::ScrollEvent
* event
= new ui::ScrollEvent(scoped_xevent_
);
101 ui::Event::DispatcherApi
dispatcher(event
);
102 dispatcher
.set_target(target_
);
106 ui::ScrollEvent
* GenerateFlingScrollEvent(int fling_delta
,
108 scoped_xevent_
.InitFlingScrollEvent(
109 kTouchPadDeviceId
, // deviceid
111 fling_delta
, // y_velocity
112 0, // x_velocity_ordinal
113 fling_delta
, // y_velocity_ordinal
114 is_cancel
); // is_cancel
115 ui::ScrollEvent
* event
= new ui::ScrollEvent(scoped_xevent_
);
116 ui::Event::DispatcherApi
dispatcher(event
);
117 dispatcher
.set_target(target_
);
121 // Creates a synthesized KeyEvent that is not backed by a native event.
122 ui::KeyEvent
* GenerateSynthesizedKeyEvent(ui::EventType type
,
123 ui::KeyboardCode code
) {
124 return new ui::KeyEvent(type
, code
, ui::EF_NONE
);
127 // Creates a synthesized MouseEvent that is not backed by a native event.
128 ui::MouseEvent
* GenerateSynthesizedMouseEventAt(ui::EventType event_type
,
129 const gfx::Point
& location
) {
130 ui::MouseEvent
* event
= new ui::MouseEvent(
131 event_type
, location
, location
, ui::EventTimeForNow(),
132 ui::EF_LEFT_MOUSE_BUTTON
, ui::EF_LEFT_MOUSE_BUTTON
);
133 ui::Event::DispatcherApi
dispatcher(event
);
134 dispatcher
.set_target(target_
);
138 // Creates a synthesized mouse press or release event.
139 ui::MouseEvent
* GenerateSynthesizedMouseClickEvent(
141 const gfx::Point
& location
) {
142 return GenerateSynthesizedMouseEventAt(type
, location
);
145 // Creates a synthesized ET_MOUSE_MOVED event.
146 ui::MouseEvent
* GenerateSynthesizedMouseMoveEvent(
147 const gfx::Point
& location
) {
148 return GenerateSynthesizedMouseEventAt(ui::ET_MOUSE_MOVED
, location
);
151 // Creates a synthesized MouseWHeel event.
152 ui::MouseWheelEvent
* GenerateSynthesizedMouseWheelEvent(int wheel_delta
) {
153 scoped_ptr
<ui::MouseEvent
> mev(
154 GenerateSynthesizedMouseEventAt(ui::ET_MOUSEWHEEL
, gfx::Point(0, 0)));
155 ui::MouseWheelEvent
* event
= new ui::MouseWheelEvent(*mev
, 0, wheel_delta
);
156 ui::Event::DispatcherApi
dispatcher(event
);
157 dispatcher
.set_target(target_
);
161 void SendActivateStickyKeyPattern(StickyKeysHandler
* handler
,
162 ui::KeyboardCode key_code
) {
163 bool released
= false;
165 scoped_ptr
<ui::KeyEvent
> ev
;
166 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, key_code
));
167 handler
->HandleKeyEvent(*ev
.get(), key_code
, &down_flags
, &released
);
168 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, key_code
));
169 handler
->HandleKeyEvent(*ev
.get(), key_code
, &down_flags
, &released
);
172 bool HandleKeyEvent(const ui::KeyEvent
& key_event
,
173 StickyKeysHandler
* handler
,
176 return handler
->HandleKeyEvent(key_event
, key_event
.key_code(), down
, up
);
179 int HandleKeyEventForDownFlags(const ui::KeyEvent
& key_event
,
180 StickyKeysHandler
* handler
) {
181 bool released
= false;
183 handler
->HandleKeyEvent(key_event
, key_event
.key_code(), &down
, &released
);
187 aura::Window
* target() { return target_
; }
190 // Owned by root window of shell, but we can still delete |target_| safely.
191 aura::Window
* target_
;
192 // The root window of |target_|. Not owned.
193 aura::Window
* root_window_
;
195 // Used to construct the various X events.
196 ui::ScopedXI2Event scoped_xevent_
;
198 DISALLOW_COPY_AND_ASSIGN(StickyKeysTest
);
201 TEST_F(StickyKeysTest
, BasicOneshotScenarioTest
) {
202 scoped_ptr
<ui::KeyEvent
> ev
;
203 StickyKeysHandler
sticky_key(ui::EF_SHIFT_DOWN
);
205 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
207 // By typing Shift key, internal state become ENABLED.
208 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
209 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
211 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_A
));
212 bool released
= false;
213 int mod_down_flags
= 0;
214 HandleKeyEvent(*ev
.get(), &sticky_key
, &mod_down_flags
, &released
);
215 // Next keyboard event is shift modified.
216 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
217 // Modifier release notification happens.
218 EXPECT_TRUE(released
);
220 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_A
));
223 HandleKeyEvent(*ev
.get(), &sticky_key
, &mod_down_flags
, &released
);
225 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
226 // Making sure Shift up keyboard event is available.
227 scoped_ptr
<ui::Event
> up_event
;
228 ASSERT_EQ(0, sticky_key
.GetModifierUpEvent(&up_event
));
229 EXPECT_TRUE(up_event
.get());
230 EXPECT_EQ(ui::ET_KEY_RELEASED
, up_event
->type());
231 EXPECT_EQ(ui::VKEY_SHIFT
,
232 static_cast<const ui::KeyEvent
*>(up_event
.get())->key_code());
234 // Enabled state is one shot, so next key event should not be shift modified.
235 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_A
));
236 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
237 EXPECT_FALSE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
239 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_A
));
240 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
241 EXPECT_FALSE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
244 TEST_F(StickyKeysTest
, BasicLockedScenarioTest
) {
245 scoped_ptr
<ui::KeyEvent
> ev
;
246 StickyKeysHandler
sticky_key(ui::EF_SHIFT_DOWN
);
248 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
250 // By typing shift key, internal state become ENABLED.
251 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
252 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
254 // By typing shift key again, internal state become LOCKED.
255 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
256 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
258 // All keyboard events including keyUp become shift modified.
259 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_A
));
260 int mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
261 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
263 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_A
));
264 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
265 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
267 // Locked state keeps after normal keyboard event.
268 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
270 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_B
));
271 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
272 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
274 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_B
));
275 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
276 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
278 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
280 // By typing shift key again, internal state become back to DISABLED.
281 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
282 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
285 TEST_F(StickyKeysTest
, NonTargetModifierTest
) {
286 scoped_ptr
<ui::KeyEvent
> ev
;
287 StickyKeysHandler
sticky_key(ui::EF_SHIFT_DOWN
);
289 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
291 // Non target modifier key does not affect internal state
292 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_MENU
));
293 int mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
294 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
295 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
297 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_MENU
));
298 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
299 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
300 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
302 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
303 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
305 // Non target modifier key does not affect internal state
306 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_MENU
));
307 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
308 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
309 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
311 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_MENU
));
312 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
313 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
314 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
316 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
317 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
319 // Non target modifier key does not affect internal state
320 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_MENU
));
321 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
322 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
323 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
325 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_MENU
));
326 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
327 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
328 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
331 TEST_F(StickyKeysTest
, NormalShortcutTest
) {
332 // Sticky keys should not be enabled if we perform a normal shortcut.
333 scoped_ptr
<ui::KeyEvent
> ev
;
334 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
336 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
338 // Perform ctrl+n shortcut.
339 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
340 int mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
341 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_N
));
342 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
343 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_N
));
344 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
345 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
346 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
347 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
349 // Sticky keys should not be enabled afterwards.
350 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
351 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
353 // Perform ctrl+n shortcut, releasing ctrl first.
354 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
355 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
356 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_N
));
357 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
358 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
359 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
360 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
361 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_N
));
362 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
364 // Sticky keys should not be enabled afterwards.
365 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
366 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
369 TEST_F(StickyKeysTest
, NormalModifiedClickTest
) {
370 scoped_ptr
<ui::KeyEvent
> kev
;
371 scoped_ptr
<ui::MouseEvent
> mev
;
372 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
374 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
376 // Perform ctrl+click.
377 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
378 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
379 mev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
380 bool released
= false;
381 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
382 mev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
383 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
385 // Sticky keys should not be enabled afterwards.
386 kev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
387 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
388 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
389 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
392 TEST_F(StickyKeysTest
, MouseMovedModifierTest
) {
393 scoped_ptr
<ui::KeyEvent
> kev
;
394 scoped_ptr
<ui::MouseEvent
> mev
;
395 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
397 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
399 // Press ctrl and handle mouse move events.
400 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
401 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
402 mev
.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(0, 0)));
403 bool released
= false;
404 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
405 mev
.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(100, 100)));
406 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
408 // Sticky keys should be enabled afterwards.
409 kev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
410 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
411 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
412 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
415 TEST_F(StickyKeysTest
, NormalModifiedScrollTest
) {
416 scoped_ptr
<ui::KeyEvent
> kev
;
417 scoped_ptr
<ui::ScrollEvent
> sev
;
418 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
420 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
422 // Perform ctrl+scroll.
423 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
424 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
425 sev
.reset(GenerateFlingScrollEvent(0, true));
426 bool released
= false;
427 sticky_key
.HandleScrollEvent(*sev
.get(), &mod_down_flags
, &released
);
428 sev
.reset(GenerateScrollEvent(10));
429 sticky_key
.HandleScrollEvent(*sev
.get(), &mod_down_flags
, &released
);
430 sev
.reset(GenerateFlingScrollEvent(10, false));
431 sticky_key
.HandleScrollEvent(*sev
.get(), &mod_down_flags
, &released
);
433 // Sticky keys should not be enabled afterwards.
434 kev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
435 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
436 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
437 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
440 TEST_F(StickyKeysTest
, MouseEventOneshot
) {
441 scoped_ptr
<ui::MouseEvent
> ev
;
442 scoped_ptr
<ui::KeyEvent
> kev
;
443 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
445 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
446 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
447 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
449 // We should still be in the ENABLED state until we get the mouse
451 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
452 bool released
= false;
453 int mod_down_flags
= 0;
454 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
455 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
456 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
458 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
461 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
462 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
463 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
465 // Making sure modifier key release event is dispatched in the right order.
466 EXPECT_TRUE(released
);
467 scoped_ptr
<ui::Event
> up_event
;
468 ASSERT_EQ(0, sticky_key
.GetModifierUpEvent(&up_event
));
469 EXPECT_TRUE(up_event
.get());
470 EXPECT_EQ(ui::ET_KEY_RELEASED
, up_event
->type());
471 EXPECT_EQ(ui::VKEY_CONTROL
,
472 static_cast<const ui::KeyEvent
*>(up_event
.get())->key_code());
474 // Enabled state is one shot, so next click should not be control modified.
475 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
478 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
479 EXPECT_FALSE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
481 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
484 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
485 EXPECT_FALSE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
488 TEST_F(StickyKeysTest
, MouseEventLocked
) {
489 scoped_ptr
<ui::MouseEvent
> ev
;
490 scoped_ptr
<ui::KeyEvent
> kev
;
491 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
493 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
495 // Pressing modifier key twice should make us enter lock state.
496 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
497 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
498 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
499 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
501 // Mouse events should not disable locked mode.
502 for (int i
= 0; i
< 3; ++i
) {
503 bool released
= false;
504 int mod_down_flags
= 0;
505 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
506 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
507 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
508 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
511 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
512 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
513 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
516 // Test with mouse wheel.
517 for (int i
= 0; i
< 3; ++i
) {
518 bool released
= false;
519 int mod_down_flags
= 0;
520 ev
.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta
));
521 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
522 ev
.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta
));
525 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
526 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
527 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
530 // Test mixed case with mouse events and key events.
531 ev
.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta
));
532 bool released
= false;
533 int mod_down_flags
= 0;
534 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
535 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
536 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_N
));
537 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
538 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
539 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
540 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
542 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
545 TEST_F(StickyKeysTest
, ScrollEventOneshot
) {
546 scoped_ptr
<ui::ScrollEvent
> ev
;
547 scoped_ptr
<ui::KeyEvent
> kev
;
548 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
550 int scroll_deltas
[] = {-10, 10};
551 for (int i
= 0; i
< 2; ++i
) {
552 // Enable sticky keys.
553 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
554 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
555 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
557 // Test a scroll sequence. Sticky keys should only be disabled at the end
558 // of the scroll sequence. Fling cancel event starts the scroll sequence.
559 ev
.reset(GenerateFlingScrollEvent(0, true));
560 bool released
= false;
561 int mod_down_flags
= 0;
562 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
563 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
564 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
566 // Scrolls should all be modified but not disable sticky keys.
567 for (int j
= 0; j
< 3; ++j
) {
568 ev
.reset(GenerateScrollEvent(scroll_deltas
[i
]));
571 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
572 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
573 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
576 // Fling start event ends scroll sequence.
577 ev
.reset(GenerateFlingScrollEvent(scroll_deltas
[i
], false));
580 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
581 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
582 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
584 scoped_ptr
<ui::Event
> up_event
;
585 EXPECT_TRUE(released
);
586 ASSERT_EQ(0, sticky_key
.GetModifierUpEvent(&up_event
));
587 EXPECT_TRUE(up_event
.get());
588 EXPECT_EQ(ui::ET_KEY_RELEASED
, up_event
->type());
589 EXPECT_EQ(ui::VKEY_CONTROL
,
590 static_cast<const ui::KeyEvent
*>(up_event
.get())->key_code());
594 TEST_F(StickyKeysTest
, ScrollDirectionChanged
) {
595 scoped_ptr
<ui::ScrollEvent
> ev
;
596 scoped_ptr
<ui::KeyEvent
> kev
;
597 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
599 // Test direction change with both boundary value and negative value.
600 const int direction_change_values
[2] = {0, -10};
601 for (int i
= 0; i
< 2; ++i
) {
602 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
603 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
605 // Fling cancel starts scroll sequence.
606 ev
.reset(GenerateFlingScrollEvent(0, true));
607 bool released
= false;
608 int mod_down_flags
= 0;
609 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
610 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
612 // Test that changing directions in a scroll sequence will
613 // return sticky keys to DISABLED state.
614 for (int j
= 0; j
< 3; ++j
) {
615 ev
.reset(GenerateScrollEvent(10));
618 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
619 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
620 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
623 ev
.reset(GenerateScrollEvent(direction_change_values
[i
]));
626 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
627 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
631 TEST_F(StickyKeysTest
, ScrollEventLocked
) {
632 scoped_ptr
<ui::ScrollEvent
> ev
;
633 scoped_ptr
<ui::KeyEvent
> kev
;
634 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
637 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
638 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
639 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
641 // Test scroll events are correctly modified in locked state.
642 for (int i
= 0; i
< 5; ++i
) {
643 // Fling cancel starts scroll sequence.
644 ev
.reset(GenerateFlingScrollEvent(0, true));
645 bool released
= false;
646 int mod_down_flags
= 0;
647 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
649 ev
.reset(GenerateScrollEvent(10));
652 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
653 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
654 ev
.reset(GenerateScrollEvent(-10));
655 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
656 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
658 // Fling start ends scroll sequence.
659 ev
.reset(GenerateFlingScrollEvent(-10, false));
660 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
663 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
666 TEST_F(StickyKeysTest
, SynthesizedEvents
) {
667 // Non-native, internally generated events should be properly handled
669 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
671 // Test non-native key events.
672 scoped_ptr
<ui::KeyEvent
> kev
;
673 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
674 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
676 kev
.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_K
));
677 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
678 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
679 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
681 kev
.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_RELEASED
, ui::VKEY_K
));
682 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
683 EXPECT_FALSE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
684 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
686 // Test non-native mouse events.
687 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
688 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
690 scoped_ptr
<ui::MouseEvent
> mev
;
691 mev
.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_PRESSED
,
693 bool released
= false;
694 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
695 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
696 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
698 mev
.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_RELEASED
,
702 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
703 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
704 EXPECT_TRUE(released
);
705 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());