• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
22 namespace ash {
23 
24 namespace {
25 
26 // The device id of the test touchpad device.
27 const unsigned int kTouchPadDeviceId = 1;
28 
29 }  // namespace
30 
31 class StickyKeysTest : public test::AshTestBase {
32  protected:
StickyKeysTest()33   StickyKeysTest()
34       : target_(NULL),
35         root_window_(NULL) {}
36 
SetUp()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 
TearDown()48   virtual void TearDown() OVERRIDE {
49     test::AshTestBase::TearDown();
50   }
51 
OnShortcutPressed()52   virtual void OnShortcutPressed() {
53     if (target_) {
54       delete target_;
55       target_ = NULL;
56     }
57   }
58 
GenerateKey(ui::EventType type,ui::KeyboardCode code)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.
GenerateMouseEvent(ui::EventType type)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.
GenerateMouseEventAt(ui::EventType type,const gfx::Point & location)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 
GenerateMouseWheelEvent(int wheel_delta)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 
GenerateScrollEvent(int scroll_delta)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 
GenerateFlingScrollEvent(int fling_delta,bool is_cancel)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.
GenerateSynthesizedKeyEvent(ui::EventType type,ui::KeyboardCode code)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.
GenerateSynthesizedMouseEventAt(ui::EventType event_type,const gfx::Point & location)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.
GenerateSynthesizedMouseClickEvent(ui::EventType type,const gfx::Point & location)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.
GenerateSynthesizedMouseMoveEvent(const gfx::Point & location)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.
GenerateSynthesizedMouseWheelEvent(int wheel_delta)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 
SendActivateStickyKeyPattern(StickyKeysHandler * handler,ui::KeyboardCode key_code)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 
HandleKeyEvent(const ui::KeyEvent & key_event,StickyKeysHandler * handler,int * down,bool * up)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 
HandleKeyEventForDownFlags(const ui::KeyEvent & key_event,StickyKeysHandler * handler)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 
target()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 
TEST_F(StickyKeysTest,BasicOneshotScenarioTest)204 TEST_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 
TEST_F(StickyKeysTest,BasicLockedScenarioTest)247 TEST_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 
TEST_F(StickyKeysTest,NonTargetModifierTest)288 TEST_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 
TEST_F(StickyKeysTest,NormalShortcutTest)334 TEST_F(StickyKeysTest, NormalShortcutTest) {
335   // Sticky keys should not be enabled if we perform a normal shortcut.
336   scoped_ptr<ui::KeyEvent> ev;
337   StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
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 
TEST_F(StickyKeysTest,NormalModifiedClickTest)372 TEST_F(StickyKeysTest, NormalModifiedClickTest) {
373   scoped_ptr<ui::KeyEvent> kev;
374   scoped_ptr<ui::MouseEvent> mev;
375   StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
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 
TEST_F(StickyKeysTest,MouseMovedModifierTest)395 TEST_F(StickyKeysTest, MouseMovedModifierTest) {
396   scoped_ptr<ui::KeyEvent> kev;
397   scoped_ptr<ui::MouseEvent> mev;
398   StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
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 
TEST_F(StickyKeysTest,NormalModifiedScrollTest)418 TEST_F(StickyKeysTest, NormalModifiedScrollTest) {
419   scoped_ptr<ui::KeyEvent> kev;
420   scoped_ptr<ui::ScrollEvent> sev;
421   StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
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 
TEST_F(StickyKeysTest,MouseEventOneshot)443 TEST_F(StickyKeysTest, MouseEventOneshot) {
444   scoped_ptr<ui::MouseEvent> ev;
445   scoped_ptr<ui::KeyEvent> kev;
446   StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
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 
TEST_F(StickyKeysTest,MouseEventLocked)491 TEST_F(StickyKeysTest, MouseEventLocked) {
492   scoped_ptr<ui::MouseEvent> ev;
493   scoped_ptr<ui::KeyEvent> kev;
494   StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
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 
TEST_F(StickyKeysTest,ScrollEventOneshot)548 TEST_F(StickyKeysTest, ScrollEventOneshot) {
549   scoped_ptr<ui::ScrollEvent> ev;
550   scoped_ptr<ui::KeyEvent> kev;
551   StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
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 
TEST_F(StickyKeysTest,ScrollDirectionChanged)597 TEST_F(StickyKeysTest, ScrollDirectionChanged) {
598   scoped_ptr<ui::ScrollEvent> ev;
599   scoped_ptr<ui::KeyEvent> kev;
600   StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
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 
TEST_F(StickyKeysTest,ScrollEventLocked)634 TEST_F(StickyKeysTest, ScrollEventLocked) {
635   scoped_ptr<ui::ScrollEvent> ev;
636   scoped_ptr<ui::KeyEvent> kev;
637   StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN);
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 
TEST_F(StickyKeysTest,SynthesizedEvents)669 TEST_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