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.
4
5#include "ash/sticky_keys/sticky_keys_controller.h"
6
7#include <X11/Xlib.h>
8#undef None
9#undef Bool
10#undef RootWindow
11
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"
21
22namespace ash {
23
24namespace {
25
26// The device id of the test touchpad device.
27const unsigned int kTouchPadDeviceId = 1;
28
29}  // namespace
30
31class StickyKeysTest : public test::AshTestBase {
32 protected:
33  StickyKeysTest()
34      : target_(NULL),
35        root_window_(NULL) {}
36
37  virtual void SetUp() OVERRIDE {
38    test::AshTestBase::SetUp();
39
40    // |target_| owned by root window of shell. It is still safe to delete
41    // it ourselves.
42    target_ = CreateTestWindowInShellWithId(0);
43    root_window_ = target_->GetRootWindow();
44
45    ui::SetUpTouchPadForTest(kTouchPadDeviceId);
46  }
47
48  virtual void TearDown() OVERRIDE {
49    test::AshTestBase::TearDown();
50  }
51
52  virtual void OnShortcutPressed() {
53    if (target_) {
54      delete target_;
55      target_ = NULL;
56    }
57  }
58
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_);
62    return event;
63  }
64
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());
69  }
70
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(
76        kTouchPadDeviceId,
77        type,
78        location,
79        0);
80    ui::MouseEvent* event = new ui::MouseEvent(scoped_xevent_);
81    return event;
82  }
83
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_);
91    return event;
92  }
93
94  ui::ScrollEvent* GenerateScrollEvent(int scroll_delta) {
95    scoped_xevent_.InitScrollEvent(kTouchPadDeviceId, // deviceid
96                                   0,               // x_offset
97                                   scroll_delta,    // y_offset
98                                   0,               // x_offset_ordinal
99                                   scroll_delta,    // y_offset_ordinal
100                                   2);              // finger_count
101    ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_);
102    ui::Event::DispatcherApi dispatcher(event);
103    dispatcher.set_target(target_);
104    return event;
105  }
106
107  ui::ScrollEvent* GenerateFlingScrollEvent(int fling_delta,
108                                            bool is_cancel) {
109    scoped_xevent_.InitFlingScrollEvent(
110        kTouchPadDeviceId, // deviceid
111        0,               // x_velocity
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_);
119    return event;
120  }
121
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);
126  }
127
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,
132                                               location,
133                                               location,
134                                               ui::EF_LEFT_MOUSE_BUTTON,
135                                               ui::EF_LEFT_MOUSE_BUTTON);
136    ui::Event::DispatcherApi dispatcher(event);
137    dispatcher.set_target(target_);
138    return event;
139  }
140
141  // Creates a synthesized mouse press or release event.
142  ui::MouseEvent* GenerateSynthesizedMouseClickEvent(
143      ui::EventType type,
144      const gfx::Point& location) {
145    return GenerateSynthesizedMouseEventAt(type, location);
146  }
147
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);
152  }
153
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_);
161    return event;
162  }
163
164  void SendActivateStickyKeyPattern(StickyKeysHandler* handler,
165                                    ui::KeyboardCode key_code) {
166    bool released = false;
167    int down_flags = 0;
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);
173  }
174
175  bool HandleKeyEvent(const ui::KeyEvent& key_event,
176                      StickyKeysHandler* handler,
177                      int* down,
178                      bool* up) {
179    return handler->HandleKeyEvent(key_event, key_event.key_code(), down, up);
180  }
181
182  int HandleKeyEventForDownFlags(const ui::KeyEvent& key_event,
183                                 StickyKeysHandler* handler) {
184    bool released = false;
185    int down = 0;
186    handler->HandleKeyEvent(key_event, key_event.key_code(), &down, &released);
187    return down;
188  }
189
190  aura::Window* target() { return target_; }
191
192 private:
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_;
197
198  // Used to construct the various X events.
199  ui::ScopedXI2Event scoped_xevent_;
200
201  DISALLOW_COPY_AND_ASSIGN(StickyKeysTest);
202};
203
204TEST_F(StickyKeysTest, BasicOneshotScenarioTest) {
205  scoped_ptr<ui::KeyEvent> ev;
206  StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN);
207
208  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
209
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());
213
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);
222
223  ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_A));
224  released = false;
225  mod_down_flags = 0;
226  HandleKeyEvent(*ev.get(), &sticky_key, &mod_down_flags, &released);
227
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());
236
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);
241
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);
245}
246
247TEST_F(StickyKeysTest, BasicLockedScenarioTest) {
248  scoped_ptr<ui::KeyEvent> ev;
249  StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN);
250
251  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
252
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());
256
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());
260
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);
265
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);
269
270  // Locked state keeps after normal keyboard event.
271  EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
272
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);
276
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);
280
281  EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
282
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());
286}
287
288TEST_F(StickyKeysTest, NonTargetModifierTest) {
289  scoped_ptr<ui::KeyEvent> ev;
290  StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN);
291
292  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
293
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);
299
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);
304
305  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
306  EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
307
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);
313
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);
318
319  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
320  EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
321
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);
327
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);
332}
333
334TEST_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);
338
339  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
340
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);
351
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);
355
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);
366
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);
370}
371
372TEST_F(StickyKeysTest, NormalModifiedClickTest) {
373  scoped_ptr<ui::KeyEvent> kev;
374  scoped_ptr<ui::MouseEvent> mev;
375  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
376
377  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
378
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);
387
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);
393}
394
395TEST_F(StickyKeysTest, MouseMovedModifierTest) {
396  scoped_ptr<ui::KeyEvent> kev;
397  scoped_ptr<ui::MouseEvent> mev;
398  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
399
400  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
401
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);
410
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);
416}
417
418TEST_F(StickyKeysTest, NormalModifiedScrollTest) {
419  scoped_ptr<ui::KeyEvent> kev;
420  scoped_ptr<ui::ScrollEvent> sev;
421  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
422
423  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
424
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);
435
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);
441}
442
443TEST_F(StickyKeysTest, MouseEventOneshot) {
444  scoped_ptr<ui::MouseEvent> ev;
445  scoped_ptr<ui::KeyEvent> kev;
446  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
447
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());
451
452  // We should still be in the ENABLED state until we get the mouse
453  // release event.
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());
460
461  ev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED));
462  released = false;
463  mod_down_flags = 0;
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());
467
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());
476
477  // Enabled state is one shot, so next click should not be control modified.
478  ev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED));
479  released = false;
480  mod_down_flags = 0;
481  sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
482  EXPECT_FALSE(mod_down_flags & ui::EF_CONTROL_DOWN);
483
484  ev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED));
485  released = false;
486  mod_down_flags = 0;
487  sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released);
488  EXPECT_FALSE(mod_down_flags & ui::EF_CONTROL_DOWN);
489}
490
491TEST_F(StickyKeysTest, MouseEventLocked) {
492  scoped_ptr<ui::MouseEvent> ev;
493  scoped_ptr<ui::KeyEvent> kev;
494  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
495
496  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
497
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());
503
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));
512    released = false;
513  mod_down_flags = 0;
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());
517  }
518
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));
526    released = false;
527  mod_down_flags = 0;
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());
531  }
532
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);
544
545  EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
546}
547
548TEST_F(StickyKeysTest, ScrollEventOneshot) {
549  scoped_ptr<ui::ScrollEvent> ev;
550  scoped_ptr<ui::KeyEvent> kev;
551  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
552
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());
559
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());
568
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]));
572      released = false;
573  mod_down_flags = 0;
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());
577    }
578
579    // Fling start event ends scroll sequence.
580    ev.reset(GenerateFlingScrollEvent(scroll_deltas[i], false));
581    released = false;
582  mod_down_flags = 0;
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());
586
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());
594  }
595}
596
597TEST_F(StickyKeysTest, ScrollDirectionChanged) {
598  scoped_ptr<ui::ScrollEvent> ev;
599  scoped_ptr<ui::KeyEvent> kev;
600  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
601
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());
607
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());
614
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));
619      released = false;
620  mod_down_flags = 0;
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());
624    }
625
626    ev.reset(GenerateScrollEvent(direction_change_values[i]));
627    released = false;
628  mod_down_flags = 0;
629    sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
630    EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
631  }
632}
633
634TEST_F(StickyKeysTest, ScrollEventLocked) {
635  scoped_ptr<ui::ScrollEvent> ev;
636  scoped_ptr<ui::KeyEvent> kev;
637  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
638
639  // Lock sticky keys.
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());
643
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);
651
652    ev.reset(GenerateScrollEvent(10));
653    released = false;
654  mod_down_flags = 0;
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);
660
661    // Fling start ends scroll sequence.
662    ev.reset(GenerateFlingScrollEvent(-10, false));
663    sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released);
664  }
665
666  EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
667}
668
669TEST_F(StickyKeysTest, SynthesizedEvents) {
670  // Non-native, internally generated events should be properly handled
671  // by sticky keys.
672  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
673
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());
678
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());
683
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());
688
689  // Test non-native mouse events.
690  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
691  EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
692
693  scoped_ptr<ui::MouseEvent> mev;
694  mev.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_PRESSED,
695                                               gfx::Point(0, 0)));
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());
700
701  mev.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_RELEASED,
702                                               gfx::Point(0, 0)));
703  released = false;
704  mod_down_flags = 0;
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());
709}
710
711}  // namespace ash
712