OLD | NEW |
| (Empty) |
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/wm/sticky_keys.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 | |
24 namespace ash { | |
25 | |
26 namespace { | |
27 | |
28 // The device id of the test scroll device. | |
29 const unsigned int kScrollDeviceId = 1; | |
30 | |
31 } // namespace | |
32 | |
33 // Keeps a buffer of handled events. | |
34 class 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. | |
65 class 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 | |
132 class 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 void SendActivateStickyKeyPattern(StickyKeysHandler* handler, | |
246 ui::KeyboardCode key_code) { | |
247 scoped_ptr<ui::KeyEvent> ev; | |
248 ev.reset(GenerateKey(true, key_code)); | |
249 handler->HandleKeyEvent(ev.get()); | |
250 ev.reset(GenerateKey(false, key_code)); | |
251 handler->HandleKeyEvent(ev.get()); | |
252 } | |
253 | |
254 void SendActivateStickyKeyPattern(aura::RootWindowHostDelegate* delegate, | |
255 ui::KeyboardCode key_code) { | |
256 scoped_ptr<ui::KeyEvent> ev; | |
257 ev.reset(GenerateKey(true, key_code)); | |
258 delegate->OnHostKeyEvent(ev.get()); | |
259 ev.reset(GenerateKey(false, key_code)); | |
260 delegate->OnHostKeyEvent(ev.get()); | |
261 } | |
262 | |
263 aura::Window* target() { return target_; } | |
264 | |
265 private: | |
266 // Owned by root window of shell, but we can still delete |target_| safely. | |
267 aura::Window* target_; | |
268 // The root window of |target_|. Not owned. | |
269 aura::Window* root_window_; | |
270 | |
271 // Used to construct the various X events. | |
272 ui::ScopedXI2Event scoped_xevent_; | |
273 | |
274 DISALLOW_COPY_AND_ASSIGN(StickyKeysTest); | |
275 }; | |
276 | |
277 TEST_F(StickyKeysTest, BasicOneshotScenarioTest) { | |
278 scoped_ptr<ui::KeyEvent> ev; | |
279 MockStickyKeysHandlerDelegate* mock_delegate = | |
280 new MockStickyKeysHandlerDelegate(this); | |
281 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate); | |
282 | |
283 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
284 | |
285 // By typing Shift key, internal state become ENABLED. | |
286 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); | |
287 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
288 | |
289 ev.reset(GenerateKey(true, ui::VKEY_A)); | |
290 sticky_key.HandleKeyEvent(ev.get()); | |
291 | |
292 // Next keyboard event is shift modified. | |
293 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); | |
294 | |
295 ev.reset(GenerateKey(false, ui::VKEY_A)); | |
296 sticky_key.HandleKeyEvent(ev.get()); | |
297 | |
298 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
299 // Making sure Shift up keyboard event is dispatched. | |
300 ASSERT_EQ(2U, mock_delegate->GetEventCount()); | |
301 EXPECT_EQ(ui::ET_KEY_PRESSED, mock_delegate->GetEvent(0)->type()); | |
302 EXPECT_EQ(ui::VKEY_A, | |
303 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(0)) | |
304 ->key_code()); | |
305 EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type()); | |
306 EXPECT_EQ(ui::VKEY_SHIFT, | |
307 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1)) | |
308 ->key_code()); | |
309 | |
310 // Enabled state is one shot, so next key event should not be shift modified. | |
311 ev.reset(GenerateKey(true, ui::VKEY_A)); | |
312 sticky_key.HandleKeyEvent(ev.get()); | |
313 EXPECT_FALSE(ev->flags() & ui::EF_SHIFT_DOWN); | |
314 | |
315 ev.reset(GenerateKey(false, ui::VKEY_A)); | |
316 sticky_key.HandleKeyEvent(ev.get()); | |
317 EXPECT_FALSE(ev->flags() & ui::EF_SHIFT_DOWN); | |
318 } | |
319 | |
320 TEST_F(StickyKeysTest, BasicLockedScenarioTest) { | |
321 scoped_ptr<ui::KeyEvent> ev; | |
322 MockStickyKeysHandlerDelegate* mock_delegate = | |
323 new MockStickyKeysHandlerDelegate(this); | |
324 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate); | |
325 | |
326 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
327 | |
328 // By typing shift key, internal state become ENABLED. | |
329 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); | |
330 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
331 | |
332 // By typing shift key again, internal state become LOCKED. | |
333 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); | |
334 EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state()); | |
335 | |
336 // All keyboard events including keyUp become shift modified. | |
337 ev.reset(GenerateKey(true, ui::VKEY_A)); | |
338 sticky_key.HandleKeyEvent(ev.get()); | |
339 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); | |
340 | |
341 ev.reset(GenerateKey(false, ui::VKEY_A)); | |
342 sticky_key.HandleKeyEvent(ev.get()); | |
343 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); | |
344 | |
345 // Locked state keeps after normal keyboard event. | |
346 EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state()); | |
347 | |
348 ev.reset(GenerateKey(true, ui::VKEY_B)); | |
349 sticky_key.HandleKeyEvent(ev.get()); | |
350 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); | |
351 | |
352 ev.reset(GenerateKey(false, ui::VKEY_B)); | |
353 sticky_key.HandleKeyEvent(ev.get()); | |
354 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); | |
355 | |
356 EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state()); | |
357 | |
358 // By typing shift key again, internal state become back to DISABLED. | |
359 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); | |
360 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
361 } | |
362 | |
363 TEST_F(StickyKeysTest, NonTargetModifierTest) { | |
364 scoped_ptr<ui::KeyEvent> ev; | |
365 MockStickyKeysHandlerDelegate* mock_delegate = | |
366 new MockStickyKeysHandlerDelegate(this); | |
367 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate); | |
368 | |
369 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
370 | |
371 // Non target modifier key does not affect internal state | |
372 ev.reset(GenerateKey(true, ui::VKEY_MENU)); | |
373 sticky_key.HandleKeyEvent(ev.get()); | |
374 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
375 | |
376 ev.reset(GenerateKey(false, ui::VKEY_MENU)); | |
377 sticky_key.HandleKeyEvent(ev.get()); | |
378 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
379 | |
380 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); | |
381 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
382 | |
383 // Non target modifier key does not affect internal state | |
384 ev.reset(GenerateKey(true, ui::VKEY_MENU)); | |
385 sticky_key.HandleKeyEvent(ev.get()); | |
386 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
387 | |
388 ev.reset(GenerateKey(false, ui::VKEY_MENU)); | |
389 sticky_key.HandleKeyEvent(ev.get()); | |
390 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
391 | |
392 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); | |
393 EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state()); | |
394 | |
395 // Non target modifier key does not affect internal state | |
396 ev.reset(GenerateKey(true, ui::VKEY_MENU)); | |
397 sticky_key.HandleKeyEvent(ev.get()); | |
398 EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state()); | |
399 | |
400 ev.reset(GenerateKey(false, ui::VKEY_MENU)); | |
401 sticky_key.HandleKeyEvent(ev.get()); | |
402 EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state()); | |
403 } | |
404 | |
405 TEST_F(StickyKeysTest, NormalShortcutTest) { | |
406 // Sticky keys should not be enabled if we perform a normal shortcut. | |
407 scoped_ptr<ui::KeyEvent> ev; | |
408 MockStickyKeysHandlerDelegate* mock_delegate = | |
409 new MockStickyKeysHandlerDelegate(this); | |
410 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); | |
411 | |
412 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
413 | |
414 // Perform ctrl+n shortcut. | |
415 ev.reset(GenerateKey(true, ui::VKEY_CONTROL)); | |
416 sticky_key.HandleKeyEvent(ev.get()); | |
417 ev.reset(GenerateKey(true, ui::VKEY_N)); | |
418 sticky_key.HandleKeyEvent(ev.get()); | |
419 ev.reset(GenerateKey(false, ui::VKEY_N)); | |
420 sticky_key.HandleKeyEvent(ev.get()); | |
421 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
422 | |
423 // Sticky keys should not be enabled afterwards. | |
424 ev.reset(GenerateKey(false, ui::VKEY_CONTROL)); | |
425 sticky_key.HandleKeyEvent(ev.get()); | |
426 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
427 } | |
428 | |
429 TEST_F(StickyKeysTest, NormalModifiedClickTest) { | |
430 scoped_ptr<ui::KeyEvent> kev; | |
431 scoped_ptr<ui::MouseEvent> mev; | |
432 MockStickyKeysHandlerDelegate* mock_delegate = | |
433 new MockStickyKeysHandlerDelegate(this); | |
434 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); | |
435 | |
436 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
437 | |
438 // Perform ctrl+click. | |
439 kev.reset(GenerateKey(true, ui::VKEY_CONTROL)); | |
440 sticky_key.HandleKeyEvent(kev.get()); | |
441 mev.reset(GenerateMouseEvent(true)); | |
442 sticky_key.HandleMouseEvent(mev.get()); | |
443 mev.reset(GenerateMouseEvent(false)); | |
444 sticky_key.HandleMouseEvent(mev.get()); | |
445 | |
446 // Sticky keys should not be enabled afterwards. | |
447 kev.reset(GenerateKey(false, ui::VKEY_CONTROL)); | |
448 sticky_key.HandleKeyEvent(kev.get()); | |
449 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
450 } | |
451 | |
452 TEST_F(StickyKeysTest, NormalModifiedScrollTest) { | |
453 ui::SetUpScrollDeviceForTest(kScrollDeviceId); | |
454 | |
455 scoped_ptr<ui::KeyEvent> kev; | |
456 scoped_ptr<ui::ScrollEvent> sev; | |
457 MockStickyKeysHandlerDelegate* mock_delegate = | |
458 new MockStickyKeysHandlerDelegate(this); | |
459 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); | |
460 | |
461 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
462 | |
463 // Perform ctrl+scroll. | |
464 kev.reset(GenerateKey(true, ui::VKEY_CONTROL)); | |
465 sev.reset(GenerateFlingScrollEvent(0, true)); | |
466 sticky_key.HandleScrollEvent(sev.get()); | |
467 sev.reset(GenerateScrollEvent(10)); | |
468 sticky_key.HandleScrollEvent(sev.get()); | |
469 sev.reset(GenerateFlingScrollEvent(10, false)); | |
470 sticky_key.HandleScrollEvent(sev.get()); | |
471 | |
472 // Sticky keys should not be enabled afterwards. | |
473 kev.reset(GenerateKey(false, ui::VKEY_CONTROL)); | |
474 sticky_key.HandleKeyEvent(kev.get()); | |
475 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
476 } | |
477 | |
478 TEST_F(StickyKeysTest, MouseEventOneshot) { | |
479 scoped_ptr<ui::MouseEvent> ev; | |
480 scoped_ptr<ui::KeyEvent> kev; | |
481 MockStickyKeysHandlerDelegate* mock_delegate = | |
482 new MockStickyKeysHandlerDelegate(this); | |
483 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); | |
484 | |
485 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
486 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); | |
487 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
488 | |
489 // We should still be in the ENABLED state until we get the mouse | |
490 // release event. | |
491 ev.reset(GenerateMouseEvent(true)); | |
492 sticky_key.HandleMouseEvent(ev.get()); | |
493 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); | |
494 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
495 | |
496 ev.reset(GenerateMouseEvent(false)); | |
497 sticky_key.HandleMouseEvent(ev.get()); | |
498 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); | |
499 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
500 | |
501 // Making sure modifier key release event is dispatched in the right order. | |
502 ASSERT_EQ(2u, mock_delegate->GetEventCount()); | |
503 EXPECT_EQ(ui::ET_MOUSE_RELEASED, mock_delegate->GetEvent(0)->type()); | |
504 EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type()); | |
505 EXPECT_EQ(ui::VKEY_CONTROL, | |
506 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1)) | |
507 ->key_code()); | |
508 | |
509 // Enabled state is one shot, so next click should not be control modified. | |
510 ev.reset(GenerateMouseEvent(true)); | |
511 sticky_key.HandleMouseEvent(ev.get()); | |
512 EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN); | |
513 | |
514 ev.reset(GenerateMouseEvent(false)); | |
515 sticky_key.HandleMouseEvent(ev.get()); | |
516 EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN); | |
517 } | |
518 | |
519 TEST_F(StickyKeysTest, MouseEventLocked) { | |
520 scoped_ptr<ui::MouseEvent> ev; | |
521 scoped_ptr<ui::KeyEvent> kev; | |
522 MockStickyKeysHandlerDelegate* mock_delegate = | |
523 new MockStickyKeysHandlerDelegate(this); | |
524 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); | |
525 | |
526 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
527 | |
528 // Pressing modifier key twice should make us enter lock state. | |
529 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); | |
530 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
531 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); | |
532 EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state()); | |
533 | |
534 // Mouse events should not disable locked mode. | |
535 for (int i = 0; i < 3; ++i) { | |
536 ev.reset(GenerateMouseEvent(true)); | |
537 sticky_key.HandleMouseEvent(ev.get()); | |
538 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); | |
539 ev.reset(GenerateMouseEvent(false)); | |
540 sticky_key.HandleMouseEvent(ev.get()); | |
541 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); | |
542 EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state()); | |
543 } | |
544 | |
545 // Test with mouse wheel. | |
546 for (int i = 0; i < 3; ++i) { | |
547 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta)); | |
548 sticky_key.HandleMouseEvent(ev.get()); | |
549 ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta)); | |
550 sticky_key.HandleMouseEvent(ev.get()); | |
551 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); | |
552 EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state()); | |
553 } | |
554 | |
555 // Test mixed case with mouse events and key events. | |
556 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta)); | |
557 sticky_key.HandleMouseEvent(ev.get()); | |
558 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); | |
559 kev.reset(GenerateKey(true, ui::VKEY_N)); | |
560 sticky_key.HandleKeyEvent(kev.get()); | |
561 EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN); | |
562 kev.reset(GenerateKey(false, ui::VKEY_N)); | |
563 sticky_key.HandleKeyEvent(kev.get()); | |
564 EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN); | |
565 | |
566 EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state()); | |
567 } | |
568 | |
569 TEST_F(StickyKeysTest, ScrollEventOneshot) { | |
570 ui::SetUpScrollDeviceForTest(kScrollDeviceId); | |
571 // Disable Australlian scrolling. | |
572 ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true); | |
573 | |
574 scoped_ptr<ui::ScrollEvent> ev; | |
575 scoped_ptr<ui::KeyEvent> kev; | |
576 MockStickyKeysHandlerDelegate* mock_delegate = | |
577 new MockStickyKeysHandlerDelegate(this); | |
578 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); | |
579 | |
580 int scroll_deltas[] = {-10, 10}; | |
581 for (int i = 0; i < 2; ++i) { | |
582 mock_delegate->ClearEvents(); | |
583 | |
584 // Enable sticky keys. | |
585 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
586 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); | |
587 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
588 | |
589 // Test a scroll sequence. Sticky keys should only be disabled at the end | |
590 // of the scroll sequence. Fling cancel event starts the scroll sequence. | |
591 ev.reset(GenerateFlingScrollEvent(0, true)); | |
592 sticky_key.HandleScrollEvent(ev.get()); | |
593 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); | |
594 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
595 | |
596 // Scrolls should all be modified but not disable sticky keys. | |
597 for (int j = 0; j < 3; ++j) { | |
598 ev.reset(GenerateScrollEvent(scroll_deltas[i])); | |
599 sticky_key.HandleScrollEvent(ev.get()); | |
600 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); | |
601 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
602 } | |
603 | |
604 // Fling start event ends scroll sequence. | |
605 ev.reset(GenerateFlingScrollEvent(scroll_deltas[i], false)); | |
606 sticky_key.HandleScrollEvent(ev.get()); | |
607 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); | |
608 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
609 | |
610 ASSERT_EQ(2U, mock_delegate->GetEventCount()); | |
611 EXPECT_EQ(ui::ET_SCROLL_FLING_START, mock_delegate->GetEvent(0)->type()); | |
612 EXPECT_FLOAT_EQ(scroll_deltas[i], | |
613 static_cast<const ui::ScrollEvent*>( | |
614 mock_delegate->GetEvent(0))->y_offset()); | |
615 EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type()); | |
616 EXPECT_EQ(ui::VKEY_CONTROL, | |
617 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1)) | |
618 ->key_code()); | |
619 } | |
620 } | |
621 | |
622 TEST_F(StickyKeysTest, ScrollDirectionChanged) { | |
623 ui::SetUpScrollDeviceForTest(kScrollDeviceId); | |
624 // Disable Australlian scrolling. | |
625 ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true); | |
626 | |
627 scoped_ptr<ui::ScrollEvent> ev; | |
628 scoped_ptr<ui::KeyEvent> kev; | |
629 MockStickyKeysHandlerDelegate* mock_delegate = | |
630 new MockStickyKeysHandlerDelegate(this); | |
631 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); | |
632 | |
633 // Test direction change with both boundary value and negative value. | |
634 const int direction_change_values[2] = {0, -10}; | |
635 for (int i = 0; i < 2; ++i) { | |
636 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); | |
637 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
638 | |
639 // Fling cancel starts scroll sequence. | |
640 ev.reset(GenerateFlingScrollEvent(0, true)); | |
641 sticky_key.HandleScrollEvent(ev.get()); | |
642 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
643 | |
644 // Test that changing directions in a scroll sequence will | |
645 // return sticky keys to DISABLED state. | |
646 for (int j = 0; j < 3; ++j) { | |
647 ev.reset(GenerateScrollEvent(10)); | |
648 sticky_key.HandleScrollEvent(ev.get()); | |
649 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); | |
650 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
651 } | |
652 | |
653 ev.reset(GenerateScrollEvent(direction_change_values[i])); | |
654 sticky_key.HandleScrollEvent(ev.get()); | |
655 EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN); | |
656 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
657 } | |
658 } | |
659 | |
660 TEST_F(StickyKeysTest, ScrollEventLocked) { | |
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 // Lock sticky keys. | |
672 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); | |
673 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); | |
674 EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state()); | |
675 | |
676 // Test scroll events are correctly modified in locked state. | |
677 for (int i = 0; i < 5; ++i) { | |
678 // Fling cancel starts scroll sequence. | |
679 ev.reset(GenerateFlingScrollEvent(0, true)); | |
680 sticky_key.HandleScrollEvent(ev.get()); | |
681 | |
682 ev.reset(GenerateScrollEvent(10)); | |
683 sticky_key.HandleScrollEvent(ev.get()); | |
684 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); | |
685 ev.reset(GenerateScrollEvent(-10)); | |
686 sticky_key.HandleScrollEvent(ev.get()); | |
687 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); | |
688 | |
689 // Fling start ends scroll sequence. | |
690 ev.reset(GenerateFlingScrollEvent(-10, false)); | |
691 sticky_key.HandleScrollEvent(ev.get()); | |
692 } | |
693 | |
694 EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state()); | |
695 } | |
696 | |
697 TEST_F(StickyKeysTest, EventTargetDestroyed) { | |
698 scoped_ptr<ui::KeyEvent> ev; | |
699 MockStickyKeysHandlerDelegate* mock_delegate = | |
700 new MockStickyKeysHandlerDelegate(this); | |
701 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); | |
702 | |
703 target()->Focus(); | |
704 | |
705 // Go into ENABLED state. | |
706 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
707 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); | |
708 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
709 | |
710 // CTRL+J is a special shortcut that will destroy the event target. | |
711 ev.reset(GenerateKey(true, ui::VKEY_J)); | |
712 sticky_key.HandleKeyEvent(ev.get()); | |
713 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
714 EXPECT_FALSE(target()); | |
715 } | |
716 | |
717 TEST_F(StickyKeysTest, SynthesizedEvents) { | |
718 // Non-native, internally generated events should be properly handled | |
719 // by sticky keys. | |
720 MockStickyKeysHandlerDelegate* mock_delegate = | |
721 new MockStickyKeysHandlerDelegate(this); | |
722 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); | |
723 | |
724 // Test non-native key events. | |
725 scoped_ptr<ui::KeyEvent> kev; | |
726 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); | |
727 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
728 | |
729 kev.reset(GenerateSynthesizedKeyEvent(true, ui::VKEY_K)); | |
730 sticky_key.HandleKeyEvent(kev.get()); | |
731 EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN); | |
732 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
733 | |
734 kev.reset(GenerateSynthesizedKeyEvent(false, ui::VKEY_K)); | |
735 sticky_key.HandleKeyEvent(kev.get()); | |
736 EXPECT_FALSE(kev->flags() & ui::EF_CONTROL_DOWN); | |
737 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
738 | |
739 // Test non-native mouse events. | |
740 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); | |
741 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
742 | |
743 scoped_ptr<ui::MouseEvent> mev; | |
744 mev.reset(GenerateSynthesizedMouseEvent(true)); | |
745 sticky_key.HandleMouseEvent(mev.get()); | |
746 EXPECT_TRUE(mev->flags() & ui::EF_CONTROL_DOWN); | |
747 EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state()); | |
748 | |
749 mev.reset(GenerateSynthesizedMouseEvent(false)); | |
750 sticky_key.HandleMouseEvent(mev.get()); | |
751 EXPECT_TRUE(mev->flags() & ui::EF_CONTROL_DOWN); | |
752 EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state()); | |
753 } | |
754 | |
755 TEST_F(StickyKeysTest, KeyEventDispatchImpl) { | |
756 // Test the actual key event dispatch implementation. | |
757 EventBuffer buffer; | |
758 ScopedVector<ui::Event> events; | |
759 aura::RootWindowHostDelegate* delegate = Shell::GetPrimaryRootWindow() | |
760 ->GetDispatcher()->AsRootWindowHostDelegate(); | |
761 Shell::GetInstance()->AddPreTargetHandler(&buffer); | |
762 Shell::GetInstance()->sticky_keys()->Enable(true); | |
763 | |
764 SendActivateStickyKeyPattern(delegate, ui::VKEY_CONTROL); | |
765 scoped_ptr<ui::KeyEvent> ev; | |
766 buffer.PopEvents(&events); | |
767 | |
768 // Test key press event is correctly modified and modifier release | |
769 // event is sent. | |
770 ev.reset(GenerateKey(true, ui::VKEY_C)); | |
771 delegate->OnHostKeyEvent(ev.get()); | |
772 buffer.PopEvents(&events); | |
773 EXPECT_EQ(2u, events.size()); | |
774 EXPECT_EQ(ui::ET_KEY_PRESSED, events[0]->type()); | |
775 EXPECT_EQ(ui::VKEY_C, static_cast<ui::KeyEvent*>(events[0])->key_code()); | |
776 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); | |
777 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); | |
778 EXPECT_EQ(ui::VKEY_CONTROL, | |
779 static_cast<ui::KeyEvent*>(events[1])->key_code()); | |
780 | |
781 // Test key release event is not modified. | |
782 ev.reset(GenerateKey(false, ui::VKEY_C)); | |
783 delegate->OnHostKeyEvent(ev.get()); | |
784 buffer.PopEvents(&events); | |
785 EXPECT_EQ(1u, events.size()); | |
786 EXPECT_EQ(ui::ET_KEY_RELEASED, events[0]->type()); | |
787 EXPECT_EQ(ui::VKEY_C, | |
788 static_cast<ui::KeyEvent*>(events[0])->key_code()); | |
789 EXPECT_FALSE(events[0]->flags() & ui::EF_CONTROL_DOWN); | |
790 | |
791 Shell::GetInstance()->RemovePreTargetHandler(&buffer); | |
792 } | |
793 | |
794 TEST_F(StickyKeysTest, MouseEventDispatchImpl) { | |
795 // Test the actual sticky mouse event dispatch implementation. | |
796 EventBuffer buffer; | |
797 ScopedVector<ui::Event> events; | |
798 aura::RootWindowHostDelegate* delegate = Shell::GetPrimaryRootWindow() | |
799 ->GetDispatcher()->AsRootWindowHostDelegate(); | |
800 Shell::GetInstance()->AddPreTargetHandler(&buffer); | |
801 Shell::GetInstance()->sticky_keys()->Enable(true); | |
802 | |
803 scoped_ptr<ui::MouseEvent> ev; | |
804 SendActivateStickyKeyPattern(delegate, ui::VKEY_CONTROL); | |
805 buffer.PopEvents(&events); | |
806 | |
807 // Test mouse press event is correctly modified. | |
808 ev.reset(GenerateMouseEvent(true)); | |
809 delegate->OnHostMouseEvent(ev.get()); | |
810 buffer.PopEvents(&events); | |
811 EXPECT_EQ(1u, events.size()); | |
812 EXPECT_EQ(ui::ET_MOUSE_PRESSED, events[0]->type()); | |
813 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); | |
814 | |
815 // Test mouse release event is correctly modified and modifier release | |
816 // event is sent. | |
817 ev.reset(GenerateMouseEvent(false)); | |
818 delegate->OnHostMouseEvent(ev.get()); | |
819 buffer.PopEvents(&events); | |
820 EXPECT_EQ(2u, events.size()); | |
821 EXPECT_EQ(ui::ET_MOUSE_RELEASED, events[0]->type()); | |
822 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); | |
823 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); | |
824 EXPECT_EQ(ui::VKEY_CONTROL, | |
825 static_cast<ui::KeyEvent*>(events[1])->key_code()); | |
826 | |
827 Shell::GetInstance()->RemovePreTargetHandler(&buffer); | |
828 } | |
829 | |
830 TEST_F(StickyKeysTest, MouseWheelEventDispatchImpl) { | |
831 // Test the actual mouse wheel event dispatch implementation. | |
832 EventBuffer buffer; | |
833 ScopedVector<ui::Event> events; | |
834 aura::RootWindowHostDelegate* delegate = Shell::GetPrimaryRootWindow() | |
835 ->GetDispatcher()->AsRootWindowHostDelegate(); | |
836 Shell::GetInstance()->AddPreTargetHandler(&buffer); | |
837 Shell::GetInstance()->sticky_keys()->Enable(true); | |
838 | |
839 scoped_ptr<ui::MouseWheelEvent> ev; | |
840 SendActivateStickyKeyPattern(delegate, ui::VKEY_CONTROL); | |
841 buffer.PopEvents(&events); | |
842 | |
843 // Test positive mouse wheel event is correctly modified and modifier release | |
844 // event is sent. | |
845 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta)); | |
846 delegate->OnHostMouseEvent(ev.get()); | |
847 buffer.PopEvents(&events); | |
848 EXPECT_EQ(2u, events.size()); | |
849 EXPECT_TRUE(events[0]->IsMouseWheelEvent()); | |
850 EXPECT_EQ(ui::MouseWheelEvent::kWheelDelta, | |
851 static_cast<ui::MouseWheelEvent*>(events[0])->y_offset()); | |
852 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); | |
853 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); | |
854 EXPECT_EQ(ui::VKEY_CONTROL, | |
855 static_cast<ui::KeyEvent*>(events[1])->key_code()); | |
856 | |
857 // Test negative mouse wheel event is correctly modified and modifier release | |
858 // event is sent. | |
859 SendActivateStickyKeyPattern(delegate, ui::VKEY_CONTROL); | |
860 buffer.PopEvents(&events); | |
861 | |
862 ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta)); | |
863 delegate->OnHostMouseEvent(ev.get()); | |
864 buffer.PopEvents(&events); | |
865 EXPECT_EQ(2u, events.size()); | |
866 EXPECT_TRUE(events[0]->IsMouseWheelEvent()); | |
867 EXPECT_EQ(-ui::MouseWheelEvent::kWheelDelta, | |
868 static_cast<ui::MouseWheelEvent*>(events[0])->y_offset()); | |
869 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); | |
870 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); | |
871 EXPECT_EQ(ui::VKEY_CONTROL, | |
872 static_cast<ui::KeyEvent*>(events[1])->key_code()); | |
873 | |
874 Shell::GetInstance()->RemovePreTargetHandler(&buffer); | |
875 } | |
876 | |
877 } // namespace ash | |
OLD | NEW |