sticky_keys_unittest.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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_handler.h" 20#include "ui/events/event_processor.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(ui::EventProcessor* 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 ui::EventProcessor* dispatcher = 798 Shell::GetPrimaryRootWindow()->GetHost()->event_processor(); 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 ui::EventProcessor* dispatcher = 838 Shell::GetPrimaryRootWindow()->GetHost()->event_processor(); 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 ui::EventProcessor* dispatcher = 875 Shell::GetPrimaryRootWindow()->GetHost()->event_processor(); 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