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 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 void TearDown() override
{ test::AshTestBase::TearDown(); }
50 virtual void OnShortcutPressed() {
57 ui::KeyEvent
* GenerateKey(ui::EventType type
, ui::KeyboardCode code
) {
58 scoped_xevent_
.InitKeyEvent(type
, code
, 0);
59 ui::KeyEvent
* event
= new ui::KeyEvent(scoped_xevent_
);
63 // Creates a mouse event backed by a native XInput2 generic button event.
64 // This is the standard native event on Chromebooks.
65 ui::MouseEvent
* GenerateMouseEvent(ui::EventType type
) {
66 return GenerateMouseEventAt(type
, gfx::Point());
69 // Creates a mouse event backed by a native XInput2 generic button event.
70 // The |location| should be in physical pixels.
71 ui::MouseEvent
* GenerateMouseEventAt(ui::EventType type
,
72 const gfx::Point
& location
) {
73 scoped_xevent_
.InitGenericButtonEvent(
78 ui::MouseEvent
* event
= new ui::MouseEvent(scoped_xevent_
);
82 ui::MouseWheelEvent
* GenerateMouseWheelEvent(int wheel_delta
) {
83 EXPECT_NE(0, wheel_delta
);
84 scoped_xevent_
.InitGenericMouseWheelEvent(
85 kTouchPadDeviceId
, wheel_delta
, 0);
86 ui::MouseWheelEvent
* event
= new ui::MouseWheelEvent(scoped_xevent_
);
87 ui::Event::DispatcherApi
dispatcher(event
);
88 dispatcher
.set_target(target_
);
92 ui::ScrollEvent
* GenerateScrollEvent(int scroll_delta
) {
93 scoped_xevent_
.InitScrollEvent(kTouchPadDeviceId
, // deviceid
95 scroll_delta
, // y_offset
96 0, // x_offset_ordinal
97 scroll_delta
, // y_offset_ordinal
99 ui::ScrollEvent
* event
= new ui::ScrollEvent(scoped_xevent_
);
100 ui::Event::DispatcherApi
dispatcher(event
);
101 dispatcher
.set_target(target_
);
105 ui::ScrollEvent
* GenerateFlingScrollEvent(int fling_delta
,
107 scoped_xevent_
.InitFlingScrollEvent(
108 kTouchPadDeviceId
, // deviceid
110 fling_delta
, // y_velocity
111 0, // x_velocity_ordinal
112 fling_delta
, // y_velocity_ordinal
113 is_cancel
); // is_cancel
114 ui::ScrollEvent
* event
= new ui::ScrollEvent(scoped_xevent_
);
115 ui::Event::DispatcherApi
dispatcher(event
);
116 dispatcher
.set_target(target_
);
120 // Creates a synthesized KeyEvent that is not backed by a native event.
121 ui::KeyEvent
* GenerateSynthesizedKeyEvent(ui::EventType type
,
122 ui::KeyboardCode code
) {
123 return new ui::KeyEvent(type
, code
, ui::EF_NONE
);
126 // Creates a synthesized MouseEvent that is not backed by a native event.
127 ui::MouseEvent
* GenerateSynthesizedMouseEventAt(ui::EventType event_type
,
128 const gfx::Point
& location
) {
129 ui::MouseEvent
* event
= new ui::MouseEvent(event_type
,
132 ui::EF_LEFT_MOUSE_BUTTON
,
133 ui::EF_LEFT_MOUSE_BUTTON
);
134 ui::Event::DispatcherApi
dispatcher(event
);
135 dispatcher
.set_target(target_
);
139 // Creates a synthesized mouse press or release event.
140 ui::MouseEvent
* GenerateSynthesizedMouseClickEvent(
142 const gfx::Point
& location
) {
143 return GenerateSynthesizedMouseEventAt(type
, location
);
146 // Creates a synthesized ET_MOUSE_MOVED event.
147 ui::MouseEvent
* GenerateSynthesizedMouseMoveEvent(
148 const gfx::Point
& location
) {
149 return GenerateSynthesizedMouseEventAt(ui::ET_MOUSE_MOVED
, location
);
152 // Creates a synthesized MouseWHeel event.
153 ui::MouseWheelEvent
* GenerateSynthesizedMouseWheelEvent(int wheel_delta
) {
154 scoped_ptr
<ui::MouseEvent
> mev(
155 GenerateSynthesizedMouseEventAt(ui::ET_MOUSEWHEEL
, gfx::Point(0, 0)));
156 ui::MouseWheelEvent
* event
= new ui::MouseWheelEvent(*mev
, 0, wheel_delta
);
157 ui::Event::DispatcherApi
dispatcher(event
);
158 dispatcher
.set_target(target_
);
162 void SendActivateStickyKeyPattern(StickyKeysHandler
* handler
,
163 ui::KeyboardCode key_code
) {
164 bool released
= false;
166 scoped_ptr
<ui::KeyEvent
> ev
;
167 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, key_code
));
168 handler
->HandleKeyEvent(*ev
.get(), key_code
, &down_flags
, &released
);
169 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, key_code
));
170 handler
->HandleKeyEvent(*ev
.get(), key_code
, &down_flags
, &released
);
173 bool HandleKeyEvent(const ui::KeyEvent
& key_event
,
174 StickyKeysHandler
* handler
,
177 return handler
->HandleKeyEvent(key_event
, key_event
.key_code(), down
, up
);
180 int HandleKeyEventForDownFlags(const ui::KeyEvent
& key_event
,
181 StickyKeysHandler
* handler
) {
182 bool released
= false;
184 handler
->HandleKeyEvent(key_event
, key_event
.key_code(), &down
, &released
);
188 aura::Window
* target() { return target_
; }
191 // Owned by root window of shell, but we can still delete |target_| safely.
192 aura::Window
* target_
;
193 // The root window of |target_|. Not owned.
194 aura::Window
* root_window_
;
196 // Used to construct the various X events.
197 ui::ScopedXI2Event scoped_xevent_
;
199 DISALLOW_COPY_AND_ASSIGN(StickyKeysTest
);
202 TEST_F(StickyKeysTest
, BasicOneshotScenarioTest
) {
203 scoped_ptr
<ui::KeyEvent
> ev
;
204 StickyKeysHandler
sticky_key(ui::EF_SHIFT_DOWN
);
206 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
208 // By typing Shift key, internal state become ENABLED.
209 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
210 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
212 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_A
));
213 bool released
= false;
214 int mod_down_flags
= 0;
215 HandleKeyEvent(*ev
.get(), &sticky_key
, &mod_down_flags
, &released
);
216 // Next keyboard event is shift modified.
217 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
218 // Modifier release notification happens.
219 EXPECT_TRUE(released
);
221 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_A
));
224 HandleKeyEvent(*ev
.get(), &sticky_key
, &mod_down_flags
, &released
);
226 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
227 // Making sure Shift up keyboard event is available.
228 scoped_ptr
<ui::Event
> up_event
;
229 ASSERT_EQ(0, sticky_key
.GetModifierUpEvent(&up_event
));
230 EXPECT_TRUE(up_event
.get());
231 EXPECT_EQ(ui::ET_KEY_RELEASED
, up_event
->type());
232 EXPECT_EQ(ui::VKEY_SHIFT
,
233 static_cast<const ui::KeyEvent
*>(up_event
.get())->key_code());
235 // Enabled state is one shot, so next key event should not be shift modified.
236 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_A
));
237 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
238 EXPECT_FALSE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
240 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_A
));
241 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
242 EXPECT_FALSE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
245 TEST_F(StickyKeysTest
, BasicLockedScenarioTest
) {
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 // By typing shift key again, internal state become LOCKED.
256 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
257 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
259 // All keyboard events including keyUp become shift modified.
260 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_A
));
261 int mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
262 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
264 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_A
));
265 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
266 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
268 // Locked state keeps after normal keyboard event.
269 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
271 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_B
));
272 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
273 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
275 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_B
));
276 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
277 EXPECT_TRUE(mod_down_flags
& ui::EF_SHIFT_DOWN
);
279 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
281 // By typing shift key again, internal state become back to DISABLED.
282 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
283 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
286 TEST_F(StickyKeysTest
, NonTargetModifierTest
) {
287 scoped_ptr
<ui::KeyEvent
> ev
;
288 StickyKeysHandler
sticky_key(ui::EF_SHIFT_DOWN
);
290 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
292 // Non target modifier key does not affect internal state
293 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_MENU
));
294 int mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
295 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
296 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
298 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_MENU
));
299 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
300 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
301 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
303 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
304 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
306 // Non target modifier key does not affect internal state
307 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_MENU
));
308 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
309 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
310 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
312 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_MENU
));
313 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
314 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
315 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
317 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_SHIFT
);
318 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
320 // Non target modifier key does not affect internal state
321 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_MENU
));
322 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
323 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
324 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
326 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_MENU
));
327 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
328 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
329 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
332 TEST_F(StickyKeysTest
, NormalShortcutTest
) {
333 // Sticky keys should not be enabled if we perform a normal shortcut.
334 scoped_ptr
<ui::KeyEvent
> ev
;
335 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
337 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
339 // Perform ctrl+n shortcut.
340 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
341 int mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
342 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_N
));
343 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
344 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_N
));
345 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
346 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
347 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
348 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
350 // Sticky keys should not be enabled afterwards.
351 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
352 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
354 // Perform ctrl+n shortcut, releasing ctrl first.
355 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
356 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
357 ev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_N
));
358 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
359 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
360 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
361 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
362 ev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_N
));
363 mod_down_flags
= HandleKeyEventForDownFlags(*ev
.get(), &sticky_key
);
365 // Sticky keys should not be enabled afterwards.
366 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
367 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
370 TEST_F(StickyKeysTest
, NormalModifiedClickTest
) {
371 scoped_ptr
<ui::KeyEvent
> kev
;
372 scoped_ptr
<ui::MouseEvent
> mev
;
373 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
375 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
377 // Perform ctrl+click.
378 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
379 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
380 mev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
381 bool released
= false;
382 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
383 mev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
384 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
386 // Sticky keys should not be enabled afterwards.
387 kev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
388 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
389 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
390 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
393 TEST_F(StickyKeysTest
, MouseMovedModifierTest
) {
394 scoped_ptr
<ui::KeyEvent
> kev
;
395 scoped_ptr
<ui::MouseEvent
> mev
;
396 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
398 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
400 // Press ctrl and handle mouse move events.
401 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
402 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
403 mev
.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(0, 0)));
404 bool released
= false;
405 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
406 mev
.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(100, 100)));
407 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
409 // Sticky keys should be enabled afterwards.
410 kev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
411 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
412 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
413 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
416 TEST_F(StickyKeysTest
, NormalModifiedScrollTest
) {
417 scoped_ptr
<ui::KeyEvent
> kev
;
418 scoped_ptr
<ui::ScrollEvent
> sev
;
419 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
421 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
423 // Perform ctrl+scroll.
424 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_CONTROL
));
425 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
426 sev
.reset(GenerateFlingScrollEvent(0, true));
427 bool released
= false;
428 sticky_key
.HandleScrollEvent(*sev
.get(), &mod_down_flags
, &released
);
429 sev
.reset(GenerateScrollEvent(10));
430 sticky_key
.HandleScrollEvent(*sev
.get(), &mod_down_flags
, &released
);
431 sev
.reset(GenerateFlingScrollEvent(10, false));
432 sticky_key
.HandleScrollEvent(*sev
.get(), &mod_down_flags
, &released
);
434 // Sticky keys should not be enabled afterwards.
435 kev
.reset(GenerateKey(ui::ET_KEY_RELEASED
, ui::VKEY_CONTROL
));
436 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
437 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
438 EXPECT_EQ(ui::EF_NONE
, mod_down_flags
);
441 TEST_F(StickyKeysTest
, MouseEventOneshot
) {
442 scoped_ptr
<ui::MouseEvent
> ev
;
443 scoped_ptr
<ui::KeyEvent
> kev
;
444 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
446 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
447 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
448 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
450 // We should still be in the ENABLED state until we get the mouse
452 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
453 bool released
= false;
454 int mod_down_flags
= 0;
455 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
456 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
457 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
459 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
462 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
463 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
464 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
466 // Making sure modifier key release event is dispatched in the right order.
467 EXPECT_TRUE(released
);
468 scoped_ptr
<ui::Event
> up_event
;
469 ASSERT_EQ(0, sticky_key
.GetModifierUpEvent(&up_event
));
470 EXPECT_TRUE(up_event
.get());
471 EXPECT_EQ(ui::ET_KEY_RELEASED
, up_event
->type());
472 EXPECT_EQ(ui::VKEY_CONTROL
,
473 static_cast<const ui::KeyEvent
*>(up_event
.get())->key_code());
475 // Enabled state is one shot, so next click should not be control modified.
476 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
479 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
480 EXPECT_FALSE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
482 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
485 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
486 EXPECT_FALSE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
489 TEST_F(StickyKeysTest
, MouseEventLocked
) {
490 scoped_ptr
<ui::MouseEvent
> ev
;
491 scoped_ptr
<ui::KeyEvent
> kev
;
492 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
494 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
496 // Pressing modifier key twice should make us enter lock state.
497 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
498 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
499 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
500 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
502 // Mouse events should not disable locked mode.
503 for (int i
= 0; i
< 3; ++i
) {
504 bool released
= false;
505 int mod_down_flags
= 0;
506 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED
));
507 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
508 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
509 ev
.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED
));
512 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
513 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
514 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
517 // Test with mouse wheel.
518 for (int i
= 0; i
< 3; ++i
) {
519 bool released
= false;
520 int mod_down_flags
= 0;
521 ev
.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta
));
522 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
523 ev
.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta
));
526 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
527 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
528 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
531 // Test mixed case with mouse events and key events.
532 ev
.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta
));
533 bool released
= false;
534 int mod_down_flags
= 0;
535 sticky_key
.HandleMouseEvent(*ev
.get(), &mod_down_flags
, &released
);
536 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
537 kev
.reset(GenerateKey(ui::ET_KEY_PRESSED
, ui::VKEY_N
));
538 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
539 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
540 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
541 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
543 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
546 TEST_F(StickyKeysTest
, ScrollEventOneshot
) {
547 scoped_ptr
<ui::ScrollEvent
> ev
;
548 scoped_ptr
<ui::KeyEvent
> kev
;
549 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
551 int scroll_deltas
[] = {-10, 10};
552 for (int i
= 0; i
< 2; ++i
) {
553 // Enable sticky keys.
554 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
555 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
556 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
558 // Test a scroll sequence. Sticky keys should only be disabled at the end
559 // of the scroll sequence. Fling cancel event starts the scroll sequence.
560 ev
.reset(GenerateFlingScrollEvent(0, true));
561 bool released
= false;
562 int mod_down_flags
= 0;
563 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
564 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
565 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
567 // Scrolls should all be modified but not disable sticky keys.
568 for (int j
= 0; j
< 3; ++j
) {
569 ev
.reset(GenerateScrollEvent(scroll_deltas
[i
]));
572 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
573 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
574 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
577 // Fling start event ends scroll sequence.
578 ev
.reset(GenerateFlingScrollEvent(scroll_deltas
[i
], false));
581 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
582 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
583 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
585 scoped_ptr
<ui::Event
> up_event
;
586 EXPECT_TRUE(released
);
587 ASSERT_EQ(0, sticky_key
.GetModifierUpEvent(&up_event
));
588 EXPECT_TRUE(up_event
.get());
589 EXPECT_EQ(ui::ET_KEY_RELEASED
, up_event
->type());
590 EXPECT_EQ(ui::VKEY_CONTROL
,
591 static_cast<const ui::KeyEvent
*>(up_event
.get())->key_code());
595 TEST_F(StickyKeysTest
, ScrollDirectionChanged
) {
596 scoped_ptr
<ui::ScrollEvent
> ev
;
597 scoped_ptr
<ui::KeyEvent
> kev
;
598 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
600 // Test direction change with both boundary value and negative value.
601 const int direction_change_values
[2] = {0, -10};
602 for (int i
= 0; i
< 2; ++i
) {
603 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
604 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
606 // Fling cancel starts scroll sequence.
607 ev
.reset(GenerateFlingScrollEvent(0, true));
608 bool released
= false;
609 int mod_down_flags
= 0;
610 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
611 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
613 // Test that changing directions in a scroll sequence will
614 // return sticky keys to DISABLED state.
615 for (int j
= 0; j
< 3; ++j
) {
616 ev
.reset(GenerateScrollEvent(10));
619 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
620 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
621 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
624 ev
.reset(GenerateScrollEvent(direction_change_values
[i
]));
627 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
628 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
632 TEST_F(StickyKeysTest
, ScrollEventLocked
) {
633 scoped_ptr
<ui::ScrollEvent
> ev
;
634 scoped_ptr
<ui::KeyEvent
> kev
;
635 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
638 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
639 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
640 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
642 // Test scroll events are correctly modified in locked state.
643 for (int i
= 0; i
< 5; ++i
) {
644 // Fling cancel starts scroll sequence.
645 ev
.reset(GenerateFlingScrollEvent(0, true));
646 bool released
= false;
647 int mod_down_flags
= 0;
648 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
650 ev
.reset(GenerateScrollEvent(10));
653 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
654 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
655 ev
.reset(GenerateScrollEvent(-10));
656 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
657 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
659 // Fling start ends scroll sequence.
660 ev
.reset(GenerateFlingScrollEvent(-10, false));
661 sticky_key
.HandleScrollEvent(*ev
.get(), &mod_down_flags
, &released
);
664 EXPECT_EQ(STICKY_KEY_STATE_LOCKED
, sticky_key
.current_state());
667 TEST_F(StickyKeysTest
, SynthesizedEvents
) {
668 // Non-native, internally generated events should be properly handled
670 StickyKeysHandler
sticky_key(ui::EF_CONTROL_DOWN
);
672 // Test non-native key events.
673 scoped_ptr
<ui::KeyEvent
> kev
;
674 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
675 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
677 kev
.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_K
));
678 int mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
679 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
680 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
682 kev
.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_RELEASED
, ui::VKEY_K
));
683 mod_down_flags
= HandleKeyEventForDownFlags(*kev
.get(), &sticky_key
);
684 EXPECT_FALSE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
685 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());
687 // Test non-native mouse events.
688 SendActivateStickyKeyPattern(&sticky_key
, ui::VKEY_CONTROL
);
689 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
691 scoped_ptr
<ui::MouseEvent
> mev
;
692 mev
.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_PRESSED
,
694 bool released
= false;
695 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
696 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
697 EXPECT_EQ(STICKY_KEY_STATE_ENABLED
, sticky_key
.current_state());
699 mev
.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_RELEASED
,
703 sticky_key
.HandleMouseEvent(*mev
.get(), &mod_down_flags
, &released
);
704 EXPECT_TRUE(mod_down_flags
& ui::EF_CONTROL_DOWN
);
705 EXPECT_TRUE(released
);
706 EXPECT_EQ(STICKY_KEY_STATE_DISABLED
, sticky_key
.current_state());