sticky_keys_unittest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/root_window.h"
18#include "ui/aura/window.h"
19#include "ui/aura/window_tree_host_delegate.h"
20#include "ui/events/event_handler.h"
21#include "ui/events/test/events_test_utils_x11.h"
22#include "ui/events/x/device_data_manager.h"
23
24namespace ash {
25
26namespace {
27
28// The device id of the test scroll device.
29const unsigned int kScrollDeviceId = 1;
30
31}  // namespace
32
33// Keeps a buffer of handled events.
34class EventBuffer : public ui::EventHandler {
35 public:
36  EventBuffer() {}
37  virtual ~EventBuffer() {}
38
39  void PopEvents(ScopedVector<ui::Event>* events) {
40    events->clear();
41    events->swap(events_);
42  }
43
44 private:
45  // ui::EventHandler overrides:
46  virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE {
47    events_.push_back(new ui::KeyEvent(*event));
48  }
49
50  virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
51    if (event->IsMouseWheelEvent()) {
52      events_.push_back(
53          new ui::MouseWheelEvent(*static_cast<ui::MouseWheelEvent*>(event)));
54    } else {
55      events_.push_back(new ui::MouseEvent(event->native_event()));
56    }
57  }
58
59  ScopedVector<ui::Event> events_;
60
61  DISALLOW_COPY_AND_ASSIGN(EventBuffer);
62};
63
64// A testable and StickyKeysHandler.
65class MockStickyKeysHandlerDelegate :
66    public StickyKeysHandler::StickyKeysHandlerDelegate {
67 public:
68  class Delegate {
69   public:
70    virtual aura::Window* GetExpectedTarget() = 0;
71    virtual void OnShortcutPressed() = 0;
72
73   protected:
74    virtual ~Delegate() {}
75  };
76
77  MockStickyKeysHandlerDelegate(Delegate* delegate) : delegate_(delegate) {}
78
79  virtual ~MockStickyKeysHandlerDelegate() {}
80
81  // StickyKeysHandler override.
82  virtual void DispatchKeyEvent(ui::KeyEvent* event,
83                                aura::Window* target) OVERRIDE {
84    ASSERT_EQ(delegate_->GetExpectedTarget(), target);
85
86    // Detect a special shortcut when it is dispatched. This shortcut will
87    // not be hit in the LOCKED state as this case does not involve the
88    // delegate.
89    if (event->type() == ui::ET_KEY_PRESSED &&
90        event->key_code() == ui::VKEY_J &&
91        event->flags() | ui::EF_CONTROL_DOWN) {
92      delegate_->OnShortcutPressed();
93    }
94
95    events_.push_back(new ui::KeyEvent(*event));
96  }
97
98  virtual void DispatchMouseEvent(ui::MouseEvent* event,
99                                  aura::Window* target) OVERRIDE {
100    ASSERT_EQ(delegate_->GetExpectedTarget(), target);
101    events_.push_back(
102        new ui::MouseEvent(*event, target, target->GetRootWindow()));
103  }
104
105  virtual void DispatchScrollEvent(ui::ScrollEvent* event,
106                                   aura::Window* target) OVERRIDE {
107    events_.push_back(new ui::ScrollEvent(event->native_event()));
108  }
109
110  // Returns the count of dispatched events.
111  size_t GetEventCount() const {
112    return events_.size();
113  }
114
115  // Returns the |index|-th dispatched event.
116  const ui::Event* GetEvent(size_t index) const {
117    return events_[index];
118  }
119
120  // Clears all previously dispatched events.
121  void ClearEvents() {
122    events_.clear();
123  }
124
125 private:
126  ScopedVector<ui::Event> events_;
127  Delegate* delegate_;
128
129  DISALLOW_COPY_AND_ASSIGN(MockStickyKeysHandlerDelegate);
130};
131
132class StickyKeysTest : public test::AshTestBase,
133                       public MockStickyKeysHandlerDelegate::Delegate {
134 protected:
135  StickyKeysTest()
136      : target_(NULL),
137        root_window_(NULL) {}
138
139  virtual void SetUp() OVERRIDE {
140    test::AshTestBase::SetUp();
141
142    // |target_| owned by root window of shell. It is still safe to delete
143    // it ourselves.
144    target_ = CreateTestWindowInShellWithId(0);
145    root_window_ = target_->GetRootWindow();
146  }
147
148  virtual void TearDown() OVERRIDE {
149    test::AshTestBase::TearDown();
150  }
151
152  // Overridden from MockStickyKeysHandlerDelegate::Delegate:
153  virtual aura::Window* GetExpectedTarget() OVERRIDE {
154    return target_ ? target_ : root_window_;
155  }
156
157  virtual void OnShortcutPressed() OVERRIDE {
158    if (target_) {
159      delete target_;
160      target_ = NULL;
161    }
162  }
163
164  ui::KeyEvent* GenerateKey(bool is_key_press, ui::KeyboardCode code) {
165    scoped_xevent_.InitKeyEvent(
166        is_key_press ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED,
167        code,
168        0);
169    ui::KeyEvent* event =  new ui::KeyEvent(scoped_xevent_, false);
170    ui::Event::DispatcherApi dispatcher(event);
171    dispatcher.set_target(target_);
172    return event;
173  }
174
175  ui::MouseEvent* GenerateMouseEvent(bool is_button_press) {
176    scoped_xevent_.InitButtonEvent(
177        is_button_press ? ui::ET_MOUSE_PRESSED : ui::ET_MOUSE_RELEASED, 0);
178    ui::MouseEvent* event = new ui::MouseEvent(scoped_xevent_);
179    ui::Event::DispatcherApi dispatcher(event);
180    dispatcher.set_target(target_);
181    return event;
182  }
183
184  ui::MouseWheelEvent* GenerateMouseWheelEvent(int wheel_delta) {
185    EXPECT_NE(0, wheel_delta);
186    scoped_xevent_.InitMouseWheelEvent(wheel_delta, 0);
187    ui::MouseWheelEvent* event = new ui::MouseWheelEvent(scoped_xevent_);
188    ui::Event::DispatcherApi dispatcher(event);
189    dispatcher.set_target(target_);
190    return event;
191  }
192
193  ui::ScrollEvent* GenerateScrollEvent(int scroll_delta) {
194    scoped_xevent_.InitScrollEvent(kScrollDeviceId, // deviceid
195                                   0,               // x_offset
196                                   scroll_delta,    // y_offset
197                                   0,               // x_offset_ordinal
198                                   scroll_delta,    // y_offset_ordinal
199                                   2);              // finger_count
200    ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_);
201    ui::Event::DispatcherApi dispatcher(event);
202    dispatcher.set_target(target_);
203    return event;
204  }
205
206  ui::ScrollEvent* GenerateFlingScrollEvent(int fling_delta,
207                                            bool is_cancel) {
208    scoped_xevent_.InitFlingScrollEvent(
209        kScrollDeviceId, // deviceid
210        0,               // x_velocity
211        fling_delta,     // y_velocity
212        0,               // x_velocity_ordinal
213        fling_delta,     // y_velocity_ordinal
214        is_cancel);      // is_cancel
215    ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_);
216    ui::Event::DispatcherApi dispatcher(event);
217    dispatcher.set_target(target_);
218    return event;
219  }
220
221  // Creates a synthesized KeyEvent that is not backed by a native event.
222  ui::KeyEvent* GenerateSynthesizedKeyEvent(
223      bool is_key_press, ui::KeyboardCode code) {
224    ui::KeyEvent* event = new ui::KeyEvent(
225        is_key_press ? ui::ET_KEY_PRESSED : ui::ET_MOUSE_RELEASED,
226        code, 0, true);
227    ui::Event::DispatcherApi dispatcher(event);
228    dispatcher.set_target(target_);
229    return event;
230  }
231
232  // Creates a synthesized MouseEvent that is not backed by a native event.
233  ui::MouseEvent* GenerateSynthesizedMouseEvent(bool is_button_press) {
234    ui::MouseEvent* event = new ui::MouseEvent(
235        is_button_press ? ui::ET_MOUSE_PRESSED : ui::ET_MOUSE_RELEASED,
236        gfx::Point(0, 0),
237        gfx::Point(0, 0),
238        ui::EF_LEFT_MOUSE_BUTTON,
239        ui::EF_LEFT_MOUSE_BUTTON);
240    ui::Event::DispatcherApi dispatcher(event);
241    dispatcher.set_target(target_);
242    return event;
243  }
244
245  // Creates a synthesized ET_MOUSE_MOVED event.
246  ui::MouseEvent* GenerateSynthesizedMouseEvent(int x, int y) {
247    ui::MouseEvent* event = new ui::MouseEvent(
248        ui::ET_MOUSE_MOVED,
249        gfx::Point(x, y),
250        gfx::Point(x, y),
251        ui::EF_LEFT_MOUSE_BUTTON,
252        ui::EF_LEFT_MOUSE_BUTTON);
253    ui::Event::DispatcherApi dispatcher(event);
254    dispatcher.set_target(target_);
255    return event;
256  }
257
258  void SendActivateStickyKeyPattern(StickyKeysHandler* handler,
259                                    ui::KeyboardCode key_code) {
260    scoped_ptr<ui::KeyEvent> ev;
261    ev.reset(GenerateKey(true, key_code));
262    handler->HandleKeyEvent(ev.get());
263    ev.reset(GenerateKey(false, key_code));
264    handler->HandleKeyEvent(ev.get());
265  }
266
267  void SendActivateStickyKeyPattern(aura::WindowEventDispatcher* dispatcher,
268                                    ui::KeyboardCode key_code) {
269    scoped_ptr<ui::KeyEvent> ev;
270    ev.reset(GenerateKey(true, key_code));
271    ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get());
272    CHECK(!details.dispatcher_destroyed);
273    ev.reset(GenerateKey(false, key_code));
274    details = dispatcher->OnEventFromSource(ev.get());
275    CHECK(!details.dispatcher_destroyed);
276  }
277
278  aura::Window* target() { return target_; }
279
280 private:
281  // Owned by root window of shell, but we can still delete |target_| safely.
282  aura::Window* target_;
283  // The root window of |target_|. Not owned.
284  aura::Window* root_window_;
285
286  // Used to construct the various X events.
287  ui::ScopedXI2Event scoped_xevent_;
288
289  DISALLOW_COPY_AND_ASSIGN(StickyKeysTest);
290};
291
292TEST_F(StickyKeysTest, BasicOneshotScenarioTest) {
293  scoped_ptr<ui::KeyEvent> ev;
294  MockStickyKeysHandlerDelegate* mock_delegate =
295      new MockStickyKeysHandlerDelegate(this);
296  StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate);
297
298  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
299
300  // By typing Shift key, internal state become ENABLED.
301  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
302  EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
303
304  ev.reset(GenerateKey(true, ui::VKEY_A));
305  sticky_key.HandleKeyEvent(ev.get());
306
307  // Next keyboard event is shift modified.
308  EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN);
309
310  ev.reset(GenerateKey(false, ui::VKEY_A));
311  sticky_key.HandleKeyEvent(ev.get());
312
313  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
314  // Making sure Shift up keyboard event is dispatched.
315  ASSERT_EQ(2U, mock_delegate->GetEventCount());
316  EXPECT_EQ(ui::ET_KEY_PRESSED, mock_delegate->GetEvent(0)->type());
317  EXPECT_EQ(ui::VKEY_A,
318            static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(0))
319                ->key_code());
320  EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type());
321  EXPECT_EQ(ui::VKEY_SHIFT,
322            static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1))
323                ->key_code());
324
325  // Enabled state is one shot, so next key event should not be shift modified.
326  ev.reset(GenerateKey(true, ui::VKEY_A));
327  sticky_key.HandleKeyEvent(ev.get());
328  EXPECT_FALSE(ev->flags() & ui::EF_SHIFT_DOWN);
329
330  ev.reset(GenerateKey(false, ui::VKEY_A));
331  sticky_key.HandleKeyEvent(ev.get());
332  EXPECT_FALSE(ev->flags() & ui::EF_SHIFT_DOWN);
333}
334
335TEST_F(StickyKeysTest, BasicLockedScenarioTest) {
336  scoped_ptr<ui::KeyEvent> ev;
337  MockStickyKeysHandlerDelegate* mock_delegate =
338      new MockStickyKeysHandlerDelegate(this);
339  StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate);
340
341  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
342
343  // By typing shift key, internal state become ENABLED.
344  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
345  EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
346
347  // By typing shift key again, internal state become LOCKED.
348  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
349  EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
350
351  // All keyboard events including keyUp become shift modified.
352  ev.reset(GenerateKey(true, ui::VKEY_A));
353  sticky_key.HandleKeyEvent(ev.get());
354  EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN);
355
356  ev.reset(GenerateKey(false, ui::VKEY_A));
357  sticky_key.HandleKeyEvent(ev.get());
358  EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN);
359
360  // Locked state keeps after normal keyboard event.
361  EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
362
363  ev.reset(GenerateKey(true, ui::VKEY_B));
364  sticky_key.HandleKeyEvent(ev.get());
365  EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN);
366
367  ev.reset(GenerateKey(false, ui::VKEY_B));
368  sticky_key.HandleKeyEvent(ev.get());
369  EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN);
370
371  EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
372
373  // By typing shift key again, internal state become back to DISABLED.
374  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
375  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
376}
377
378TEST_F(StickyKeysTest, NonTargetModifierTest) {
379  scoped_ptr<ui::KeyEvent> ev;
380  MockStickyKeysHandlerDelegate* mock_delegate =
381      new MockStickyKeysHandlerDelegate(this);
382  StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate);
383
384  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
385
386  // Non target modifier key does not affect internal state
387  ev.reset(GenerateKey(true, ui::VKEY_MENU));
388  sticky_key.HandleKeyEvent(ev.get());
389  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
390
391  ev.reset(GenerateKey(false, ui::VKEY_MENU));
392  sticky_key.HandleKeyEvent(ev.get());
393  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
394
395  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
396  EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
397
398  // Non target modifier key does not affect internal state
399  ev.reset(GenerateKey(true, ui::VKEY_MENU));
400  sticky_key.HandleKeyEvent(ev.get());
401  EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
402
403  ev.reset(GenerateKey(false, ui::VKEY_MENU));
404  sticky_key.HandleKeyEvent(ev.get());
405  EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
406
407  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT);
408  EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
409
410  // Non target modifier key does not affect internal state
411  ev.reset(GenerateKey(true, ui::VKEY_MENU));
412  sticky_key.HandleKeyEvent(ev.get());
413  EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
414
415  ev.reset(GenerateKey(false, ui::VKEY_MENU));
416  sticky_key.HandleKeyEvent(ev.get());
417  EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
418}
419
420TEST_F(StickyKeysTest, NormalShortcutTest) {
421  // Sticky keys should not be enabled if we perform a normal shortcut.
422  scoped_ptr<ui::KeyEvent> ev;
423  MockStickyKeysHandlerDelegate* mock_delegate =
424      new MockStickyKeysHandlerDelegate(this);
425  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
426
427  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
428
429  // Perform ctrl+n shortcut.
430  ev.reset(GenerateKey(true, ui::VKEY_CONTROL));
431  sticky_key.HandleKeyEvent(ev.get());
432  ev.reset(GenerateKey(true, ui::VKEY_N));
433  sticky_key.HandleKeyEvent(ev.get());
434  ev.reset(GenerateKey(false, ui::VKEY_N));
435  sticky_key.HandleKeyEvent(ev.get());
436  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
437
438  // Sticky keys should not be enabled afterwards.
439  ev.reset(GenerateKey(false, ui::VKEY_CONTROL));
440  sticky_key.HandleKeyEvent(ev.get());
441  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
442}
443
444TEST_F(StickyKeysTest, NormalModifiedClickTest) {
445  scoped_ptr<ui::KeyEvent> kev;
446  scoped_ptr<ui::MouseEvent> mev;
447  MockStickyKeysHandlerDelegate* mock_delegate =
448      new MockStickyKeysHandlerDelegate(this);
449  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
450
451  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
452
453  // Perform ctrl+click.
454  kev.reset(GenerateKey(true, ui::VKEY_CONTROL));
455  sticky_key.HandleKeyEvent(kev.get());
456  mev.reset(GenerateMouseEvent(true));
457  sticky_key.HandleMouseEvent(mev.get());
458  mev.reset(GenerateMouseEvent(false));
459  sticky_key.HandleMouseEvent(mev.get());
460
461  // Sticky keys should not be enabled afterwards.
462  kev.reset(GenerateKey(false, ui::VKEY_CONTROL));
463  sticky_key.HandleKeyEvent(kev.get());
464  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
465}
466
467TEST_F(StickyKeysTest, MouseMovedModifierTest) {
468  scoped_ptr<ui::KeyEvent> kev;
469  scoped_ptr<ui::MouseEvent> mev;
470  MockStickyKeysHandlerDelegate* mock_delegate =
471      new MockStickyKeysHandlerDelegate(this);
472  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
473
474  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
475
476  // Press ctrl and handle mouse move events.
477  kev.reset(GenerateKey(true, ui::VKEY_CONTROL));
478  sticky_key.HandleKeyEvent(kev.get());
479  mev.reset(GenerateSynthesizedMouseEvent(0, 0));
480  sticky_key.HandleMouseEvent(mev.get());
481  mev.reset(GenerateSynthesizedMouseEvent(100, 100));
482  sticky_key.HandleMouseEvent(mev.get());
483
484  // Sticky keys should be enabled afterwards.
485  kev.reset(GenerateKey(false, ui::VKEY_CONTROL));
486  sticky_key.HandleKeyEvent(kev.get());
487  EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
488}
489
490TEST_F(StickyKeysTest, NormalModifiedScrollTest) {
491  ui::SetUpScrollDeviceForTest(kScrollDeviceId);
492
493  scoped_ptr<ui::KeyEvent> kev;
494  scoped_ptr<ui::ScrollEvent> sev;
495  MockStickyKeysHandlerDelegate* mock_delegate =
496      new MockStickyKeysHandlerDelegate(this);
497  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
498
499  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
500
501  // Perform ctrl+scroll.
502  kev.reset(GenerateKey(true, ui::VKEY_CONTROL));
503  sev.reset(GenerateFlingScrollEvent(0, true));
504  sticky_key.HandleScrollEvent(sev.get());
505  sev.reset(GenerateScrollEvent(10));
506  sticky_key.HandleScrollEvent(sev.get());
507  sev.reset(GenerateFlingScrollEvent(10, false));
508  sticky_key.HandleScrollEvent(sev.get());
509
510  // Sticky keys should not be enabled afterwards.
511  kev.reset(GenerateKey(false, ui::VKEY_CONTROL));
512  sticky_key.HandleKeyEvent(kev.get());
513  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
514}
515
516TEST_F(StickyKeysTest, MouseEventOneshot) {
517  scoped_ptr<ui::MouseEvent> ev;
518  scoped_ptr<ui::KeyEvent> kev;
519  MockStickyKeysHandlerDelegate* mock_delegate =
520      new MockStickyKeysHandlerDelegate(this);
521  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
522
523  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
524  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
525  EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
526
527  // We should still be in the ENABLED state until we get the mouse
528  // release event.
529  ev.reset(GenerateMouseEvent(true));
530  sticky_key.HandleMouseEvent(ev.get());
531  EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
532  EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
533
534  ev.reset(GenerateMouseEvent(false));
535  sticky_key.HandleMouseEvent(ev.get());
536  EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
537  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
538
539  // Making sure modifier key release event is dispatched in the right order.
540  ASSERT_EQ(2u, mock_delegate->GetEventCount());
541  EXPECT_EQ(ui::ET_MOUSE_RELEASED, mock_delegate->GetEvent(0)->type());
542  EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type());
543  EXPECT_EQ(ui::VKEY_CONTROL,
544            static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1))
545                ->key_code());
546
547  // Enabled state is one shot, so next click should not be control modified.
548  ev.reset(GenerateMouseEvent(true));
549  sticky_key.HandleMouseEvent(ev.get());
550  EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN);
551
552  ev.reset(GenerateMouseEvent(false));
553  sticky_key.HandleMouseEvent(ev.get());
554  EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN);
555}
556
557TEST_F(StickyKeysTest, MouseEventLocked) {
558  scoped_ptr<ui::MouseEvent> ev;
559  scoped_ptr<ui::KeyEvent> kev;
560  MockStickyKeysHandlerDelegate* mock_delegate =
561      new MockStickyKeysHandlerDelegate(this);
562  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
563
564  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
565
566  // Pressing modifier key twice should make us enter lock state.
567  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
568  EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
569  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
570  EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
571
572  // Mouse events should not disable locked mode.
573  for (int i = 0; i < 3; ++i) {
574    ev.reset(GenerateMouseEvent(true));
575    sticky_key.HandleMouseEvent(ev.get());
576    EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
577    ev.reset(GenerateMouseEvent(false));
578    sticky_key.HandleMouseEvent(ev.get());
579    EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
580    EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
581  }
582
583  // Test with mouse wheel.
584  for (int i = 0; i < 3; ++i) {
585    ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta));
586    sticky_key.HandleMouseEvent(ev.get());
587    ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta));
588    sticky_key.HandleMouseEvent(ev.get());
589    EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
590    EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
591  }
592
593  // Test mixed case with mouse events and key events.
594  ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta));
595  sticky_key.HandleMouseEvent(ev.get());
596  EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
597  kev.reset(GenerateKey(true, ui::VKEY_N));
598  sticky_key.HandleKeyEvent(kev.get());
599  EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN);
600  kev.reset(GenerateKey(false, ui::VKEY_N));
601  sticky_key.HandleKeyEvent(kev.get());
602  EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN);
603
604  EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
605}
606
607TEST_F(StickyKeysTest, ScrollEventOneshot) {
608  ui::SetUpScrollDeviceForTest(kScrollDeviceId);
609  // Disable Australlian scrolling.
610  ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true);
611
612  scoped_ptr<ui::ScrollEvent> ev;
613  scoped_ptr<ui::KeyEvent> kev;
614  MockStickyKeysHandlerDelegate* mock_delegate =
615      new MockStickyKeysHandlerDelegate(this);
616  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
617
618  int scroll_deltas[] = {-10, 10};
619  for (int i = 0; i < 2; ++i) {
620    mock_delegate->ClearEvents();
621
622    // Enable sticky keys.
623    EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
624    SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
625    EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
626
627    // Test a scroll sequence. Sticky keys should only be disabled at the end
628    // of the scroll sequence. Fling cancel event starts the scroll sequence.
629    ev.reset(GenerateFlingScrollEvent(0, true));
630    sticky_key.HandleScrollEvent(ev.get());
631    EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
632    EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
633
634    // Scrolls should all be modified but not disable sticky keys.
635    for (int j = 0; j < 3; ++j) {
636      ev.reset(GenerateScrollEvent(scroll_deltas[i]));
637      sticky_key.HandleScrollEvent(ev.get());
638      EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
639      EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
640    }
641
642    // Fling start event ends scroll sequence.
643    ev.reset(GenerateFlingScrollEvent(scroll_deltas[i], false));
644    sticky_key.HandleScrollEvent(ev.get());
645    EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
646    EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
647
648    ASSERT_EQ(2U, mock_delegate->GetEventCount());
649    EXPECT_EQ(ui::ET_SCROLL_FLING_START, mock_delegate->GetEvent(0)->type());
650    EXPECT_FLOAT_EQ(scroll_deltas[i],
651                    static_cast<const ui::ScrollEvent*>(
652                        mock_delegate->GetEvent(0))->y_offset());
653    EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type());
654    EXPECT_EQ(ui::VKEY_CONTROL,
655              static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1))
656                  ->key_code());
657  }
658}
659
660TEST_F(StickyKeysTest, ScrollDirectionChanged) {
661  ui::SetUpScrollDeviceForTest(kScrollDeviceId);
662  // Disable Australlian scrolling.
663  ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true);
664
665  scoped_ptr<ui::ScrollEvent> ev;
666  scoped_ptr<ui::KeyEvent> kev;
667  MockStickyKeysHandlerDelegate* mock_delegate =
668      new MockStickyKeysHandlerDelegate(this);
669  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
670
671  // Test direction change with both boundary value and negative value.
672  const int direction_change_values[2] = {0, -10};
673  for (int i = 0; i < 2; ++i) {
674    SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
675    EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
676
677    // Fling cancel starts scroll sequence.
678    ev.reset(GenerateFlingScrollEvent(0, true));
679    sticky_key.HandleScrollEvent(ev.get());
680    EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
681
682    // Test that changing directions in a scroll sequence will
683    // return sticky keys to DISABLED state.
684    for (int j = 0; j < 3; ++j) {
685      ev.reset(GenerateScrollEvent(10));
686      sticky_key.HandleScrollEvent(ev.get());
687      EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
688      EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
689    }
690
691    ev.reset(GenerateScrollEvent(direction_change_values[i]));
692    sticky_key.HandleScrollEvent(ev.get());
693    EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN);
694    EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
695  }
696}
697
698TEST_F(StickyKeysTest, ScrollEventLocked) {
699  ui::SetUpScrollDeviceForTest(kScrollDeviceId);
700  // Disable Australlian scrolling.
701  ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true);
702
703  scoped_ptr<ui::ScrollEvent> ev;
704  scoped_ptr<ui::KeyEvent> kev;
705  MockStickyKeysHandlerDelegate* mock_delegate =
706      new MockStickyKeysHandlerDelegate(this);
707  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
708
709  // Lock sticky keys.
710  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
711  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
712  EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
713
714  // Test scroll events are correctly modified in locked state.
715  for (int i = 0; i < 5; ++i) {
716    // Fling cancel starts scroll sequence.
717    ev.reset(GenerateFlingScrollEvent(0, true));
718    sticky_key.HandleScrollEvent(ev.get());
719
720    ev.reset(GenerateScrollEvent(10));
721    sticky_key.HandleScrollEvent(ev.get());
722    EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
723    ev.reset(GenerateScrollEvent(-10));
724    sticky_key.HandleScrollEvent(ev.get());
725    EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
726
727    // Fling start ends scroll sequence.
728    ev.reset(GenerateFlingScrollEvent(-10, false));
729    sticky_key.HandleScrollEvent(ev.get());
730  }
731
732  EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state());
733}
734
735TEST_F(StickyKeysTest, EventTargetDestroyed) {
736  scoped_ptr<ui::KeyEvent> ev;
737  MockStickyKeysHandlerDelegate* mock_delegate =
738      new MockStickyKeysHandlerDelegate(this);
739  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
740
741  target()->Focus();
742
743  // Go into ENABLED state.
744  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
745  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
746  EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
747
748  // CTRL+J is a special shortcut that will destroy the event target.
749  ev.reset(GenerateKey(true, ui::VKEY_J));
750  sticky_key.HandleKeyEvent(ev.get());
751  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
752  EXPECT_FALSE(target());
753}
754
755TEST_F(StickyKeysTest, SynthesizedEvents) {
756  // Non-native, internally generated events should be properly handled
757  // by sticky keys.
758  MockStickyKeysHandlerDelegate* mock_delegate =
759      new MockStickyKeysHandlerDelegate(this);
760  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
761
762  // Test non-native key events.
763  scoped_ptr<ui::KeyEvent> kev;
764  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
765  EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
766
767  kev.reset(GenerateSynthesizedKeyEvent(true, ui::VKEY_K));
768  sticky_key.HandleKeyEvent(kev.get());
769  EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN);
770  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
771
772  kev.reset(GenerateSynthesizedKeyEvent(false, ui::VKEY_K));
773  sticky_key.HandleKeyEvent(kev.get());
774  EXPECT_FALSE(kev->flags() & ui::EF_CONTROL_DOWN);
775  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
776
777  // Test non-native mouse events.
778  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
779  EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
780
781  scoped_ptr<ui::MouseEvent> mev;
782  mev.reset(GenerateSynthesizedMouseEvent(true));
783  sticky_key.HandleMouseEvent(mev.get());
784  EXPECT_TRUE(mev->flags() & ui::EF_CONTROL_DOWN);
785  EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state());
786
787  mev.reset(GenerateSynthesizedMouseEvent(false));
788  sticky_key.HandleMouseEvent(mev.get());
789  EXPECT_TRUE(mev->flags() & ui::EF_CONTROL_DOWN);
790  EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state());
791}
792
793TEST_F(StickyKeysTest, KeyEventDispatchImpl) {
794  // Test the actual key event dispatch implementation.
795  EventBuffer buffer;
796  ScopedVector<ui::Event> events;
797  aura::WindowEventDispatcher* dispatcher = Shell::GetPrimaryRootWindow()
798      ->GetDispatcher();
799  Shell::GetInstance()->AddPreTargetHandler(&buffer);
800  Shell::GetInstance()->sticky_keys_controller()->Enable(true);
801
802  SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL);
803  scoped_ptr<ui::KeyEvent> ev;
804  buffer.PopEvents(&events);
805
806  // Test key press event is correctly modified and modifier release
807  // event is sent.
808  ev.reset(GenerateKey(true, ui::VKEY_C));
809  ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get());
810  buffer.PopEvents(&events);
811  EXPECT_EQ(2u, events.size());
812  EXPECT_EQ(ui::ET_KEY_PRESSED, events[0]->type());
813  EXPECT_EQ(ui::VKEY_C, static_cast<ui::KeyEvent*>(events[0])->key_code());
814  EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
815  EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
816  EXPECT_EQ(ui::VKEY_CONTROL,
817            static_cast<ui::KeyEvent*>(events[1])->key_code());
818
819  // Test key release event is not modified.
820  ev.reset(GenerateKey(false, ui::VKEY_C));
821  details = dispatcher->OnEventFromSource(ev.get());
822  ASSERT_FALSE(details.dispatcher_destroyed);
823  buffer.PopEvents(&events);
824  EXPECT_EQ(1u, events.size());
825  EXPECT_EQ(ui::ET_KEY_RELEASED, events[0]->type());
826  EXPECT_EQ(ui::VKEY_C,
827            static_cast<ui::KeyEvent*>(events[0])->key_code());
828  EXPECT_FALSE(events[0]->flags() & ui::EF_CONTROL_DOWN);
829
830  Shell::GetInstance()->RemovePreTargetHandler(&buffer);
831}
832
833TEST_F(StickyKeysTest, MouseEventDispatchImpl) {
834  // Test the actual sticky mouse event dispatch implementation.
835  EventBuffer buffer;
836  ScopedVector<ui::Event> events;
837  aura::WindowEventDispatcher* dispatcher = Shell::GetPrimaryRootWindow()
838      ->GetDispatcher();
839  Shell::GetInstance()->AddPreTargetHandler(&buffer);
840  Shell::GetInstance()->sticky_keys_controller()->Enable(true);
841
842  scoped_ptr<ui::MouseEvent> ev;
843  SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL);
844  buffer.PopEvents(&events);
845
846  // Test mouse press event is correctly modified.
847  ev.reset(GenerateMouseEvent(true));
848  ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get());
849  buffer.PopEvents(&events);
850  EXPECT_EQ(1u, events.size());
851  EXPECT_EQ(ui::ET_MOUSE_PRESSED, events[0]->type());
852  EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
853
854  // Test mouse release event is correctly modified and modifier release
855  // event is sent.
856  ev.reset(GenerateMouseEvent(false));
857  details = dispatcher->OnEventFromSource(ev.get());
858  ASSERT_FALSE(details.dispatcher_destroyed);
859  buffer.PopEvents(&events);
860  EXPECT_EQ(2u, events.size());
861  EXPECT_EQ(ui::ET_MOUSE_RELEASED, events[0]->type());
862  EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
863  EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
864  EXPECT_EQ(ui::VKEY_CONTROL,
865            static_cast<ui::KeyEvent*>(events[1])->key_code());
866
867  Shell::GetInstance()->RemovePreTargetHandler(&buffer);
868}
869
870TEST_F(StickyKeysTest, MouseWheelEventDispatchImpl) {
871  // Test the actual mouse wheel event dispatch implementation.
872  EventBuffer buffer;
873  ScopedVector<ui::Event> events;
874  aura::WindowEventDispatcher* dispatcher = Shell::GetPrimaryRootWindow()
875      ->GetDispatcher();
876  Shell::GetInstance()->AddPreTargetHandler(&buffer);
877  Shell::GetInstance()->sticky_keys_controller()->Enable(true);
878
879  scoped_ptr<ui::MouseWheelEvent> ev;
880  SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL);
881  buffer.PopEvents(&events);
882
883  // Test positive mouse wheel event is correctly modified and modifier release
884  // event is sent.
885  ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta));
886  ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get());
887  ASSERT_FALSE(details.dispatcher_destroyed);
888  buffer.PopEvents(&events);
889  EXPECT_EQ(2u, events.size());
890  EXPECT_TRUE(events[0]->IsMouseWheelEvent());
891  EXPECT_EQ(ui::MouseWheelEvent::kWheelDelta,
892            static_cast<ui::MouseWheelEvent*>(events[0])->y_offset());
893  EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
894  EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
895  EXPECT_EQ(ui::VKEY_CONTROL,
896            static_cast<ui::KeyEvent*>(events[1])->key_code());
897
898  // Test negative mouse wheel event is correctly modified and modifier release
899  // event is sent.
900  SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL);
901  buffer.PopEvents(&events);
902
903  ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta));
904  details = dispatcher->OnEventFromSource(ev.get());
905  ASSERT_FALSE(details.dispatcher_destroyed);
906  buffer.PopEvents(&events);
907  EXPECT_EQ(2u, events.size());
908  EXPECT_TRUE(events[0]->IsMouseWheelEvent());
909  EXPECT_EQ(-ui::MouseWheelEvent::kWheelDelta,
910            static_cast<ui::MouseWheelEvent*>(events[0])->y_offset());
911  EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN);
912  EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type());
913  EXPECT_EQ(ui::VKEY_CONTROL,
914            static_cast<ui::KeyEvent*>(events[1])->key_code());
915
916  Shell::GetInstance()->RemovePreTargetHandler(&buffer);
917}
918
919}  // namespace ash
920