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 24 namespace ash { 25 26 namespace { 27 28 // The device id of the test touchpad device. 29 const unsigned int kTouchPadDeviceId = 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)); 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 ui::SetUpTouchPadForTest(kTouchPadDeviceId); 148 } 149 150 virtual void TearDown() OVERRIDE { 151 test::AshTestBase::TearDown(); 152 } 153 154 // Overridden from MockStickyKeysHandlerDelegate::Delegate: 155 virtual aura::Window* GetExpectedTarget() OVERRIDE { 156 return target_ ? target_ : root_window_; 157 } 158 159 virtual void OnShortcutPressed() OVERRIDE { 160 if (target_) { 161 delete target_; 162 target_ = NULL; 163 } 164 } 165 166 ui::KeyEvent* GenerateKey(bool is_key_press, ui::KeyboardCode code) { 167 scoped_xevent_.InitKeyEvent( 168 is_key_press ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED, 169 code, 170 0); 171 ui::KeyEvent* event = new ui::KeyEvent(scoped_xevent_, false); 172 ui::Event::DispatcherApi dispatcher(event); 173 dispatcher.set_target(target_); 174 return event; 175 } 176 177 // Creates a mouse event backed by a native XInput2 generic button event. 178 // This is the standard native event on Chromebooks. 179 ui::MouseEvent* GenerateMouseEvent(bool is_button_press) { 180 return GenerateMouseEventAt(is_button_press, gfx::Point()); 181 } 182 183 // Creates a mouse event backed by a native XInput2 generic button event. 184 // The |location| should be in physical pixels. 185 ui::MouseEvent* GenerateMouseEventAt(bool is_button_press, 186 const gfx::Point& location) { 187 scoped_xevent_.InitGenericButtonEvent( 188 kTouchPadDeviceId, 189 is_button_press ? ui::ET_MOUSE_PRESSED : ui::ET_MOUSE_RELEASED, 190 location, 191 0); 192 ui::MouseEvent* event = new ui::MouseEvent(scoped_xevent_); 193 ui::Event::DispatcherApi dispatcher(event); 194 dispatcher.set_target(target_); 195 return event; 196 } 197 198 ui::MouseWheelEvent* GenerateMouseWheelEvent(int wheel_delta) { 199 EXPECT_NE(0, wheel_delta); 200 scoped_xevent_.InitGenericMouseWheelEvent( 201 kTouchPadDeviceId, wheel_delta, 0); 202 ui::MouseWheelEvent* event = new ui::MouseWheelEvent(scoped_xevent_); 203 ui::Event::DispatcherApi dispatcher(event); 204 dispatcher.set_target(target_); 205 return event; 206 } 207 208 ui::ScrollEvent* GenerateScrollEvent(int scroll_delta) { 209 scoped_xevent_.InitScrollEvent(kTouchPadDeviceId, // deviceid 210 0, // x_offset 211 scroll_delta, // y_offset 212 0, // x_offset_ordinal 213 scroll_delta, // y_offset_ordinal 214 2); // finger_count 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 ui::ScrollEvent* GenerateFlingScrollEvent(int fling_delta, 222 bool is_cancel) { 223 scoped_xevent_.InitFlingScrollEvent( 224 kTouchPadDeviceId, // deviceid 225 0, // x_velocity 226 fling_delta, // y_velocity 227 0, // x_velocity_ordinal 228 fling_delta, // y_velocity_ordinal 229 is_cancel); // is_cancel 230 ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_); 231 ui::Event::DispatcherApi dispatcher(event); 232 dispatcher.set_target(target_); 233 return event; 234 } 235 236 // Creates a synthesized KeyEvent that is not backed by a native event. 237 ui::KeyEvent* GenerateSynthesizedKeyEvent( 238 bool is_key_press, ui::KeyboardCode code) { 239 ui::KeyEvent* event = new ui::KeyEvent( 240 is_key_press ? ui::ET_KEY_PRESSED : ui::ET_MOUSE_RELEASED, 241 code, 0, true); 242 ui::Event::DispatcherApi dispatcher(event); 243 dispatcher.set_target(target_); 244 return event; 245 } 246 247 // Creates a synthesized MouseEvent that is not backed by a native event. 248 ui::MouseEvent* GenerateSynthesizedMouseEventAt(ui::EventType event_type, 249 const gfx::Point& location) { 250 ui::MouseEvent* event = new ui::MouseEvent(event_type, 251 location, 252 location, 253 ui::EF_LEFT_MOUSE_BUTTON, 254 ui::EF_LEFT_MOUSE_BUTTON); 255 ui::Event::DispatcherApi dispatcher(event); 256 dispatcher.set_target(target_); 257 return event; 258 } 259 260 // Creates a synthesized mouse press or release event. 261 ui::MouseEvent* GenerateSynthesizedMouseClickEvent( 262 bool is_button_press, 263 const gfx::Point& location) { 264 return GenerateSynthesizedMouseEventAt( 265 is_button_press ? ui::ET_MOUSE_PRESSED : ui::ET_MOUSE_RELEASED, 266 location); 267 } 268 269 // Creates a synthesized ET_MOUSE_MOVED event. 270 ui::MouseEvent* GenerateSynthesizedMouseMoveEvent( 271 const gfx::Point& location) { 272 return GenerateSynthesizedMouseEventAt(ui::ET_MOUSE_MOVED, location); 273 } 274 275 // Creates a synthesized MouseWHeel event. 276 ui::MouseWheelEvent* GenerateSynthesizedMouseWheelEvent(int wheel_delta) { 277 scoped_ptr<ui::MouseEvent> mev( 278 GenerateSynthesizedMouseEventAt(ui::ET_MOUSEWHEEL, gfx::Point(0, 0))); 279 ui::MouseWheelEvent* event = new ui::MouseWheelEvent(*mev, 0, wheel_delta); 280 ui::Event::DispatcherApi dispatcher(event); 281 dispatcher.set_target(target_); 282 return event; 283 } 284 285 void SendActivateStickyKeyPattern(StickyKeysHandler* handler, 286 ui::KeyboardCode key_code) { 287 scoped_ptr<ui::KeyEvent> ev; 288 ev.reset(GenerateKey(true, key_code)); 289 handler->HandleKeyEvent(ev.get()); 290 ev.reset(GenerateKey(false, key_code)); 291 handler->HandleKeyEvent(ev.get()); 292 } 293 294 void SendActivateStickyKeyPattern(ui::EventProcessor* dispatcher, 295 ui::KeyboardCode key_code) { 296 scoped_ptr<ui::KeyEvent> ev; 297 ev.reset(GenerateKey(true, key_code)); 298 ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get()); 299 CHECK(!details.dispatcher_destroyed); 300 ev.reset(GenerateKey(false, key_code)); 301 details = dispatcher->OnEventFromSource(ev.get()); 302 CHECK(!details.dispatcher_destroyed); 303 } 304 305 aura::Window* target() { return target_; } 306 307 private: 308 // Owned by root window of shell, but we can still delete |target_| safely. 309 aura::Window* target_; 310 // The root window of |target_|. Not owned. 311 aura::Window* root_window_; 312 313 // Used to construct the various X events. 314 ui::ScopedXI2Event scoped_xevent_; 315 316 DISALLOW_COPY_AND_ASSIGN(StickyKeysTest); 317 }; 318 319 TEST_F(StickyKeysTest, BasicOneshotScenarioTest) { 320 scoped_ptr<ui::KeyEvent> ev; 321 MockStickyKeysHandlerDelegate* mock_delegate = 322 new MockStickyKeysHandlerDelegate(this); 323 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate); 324 325 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 326 327 // By typing Shift key, internal state become ENABLED. 328 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); 329 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 330 331 ev.reset(GenerateKey(true, ui::VKEY_A)); 332 sticky_key.HandleKeyEvent(ev.get()); 333 334 // Next keyboard event is shift modified. 335 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); 336 337 ev.reset(GenerateKey(false, ui::VKEY_A)); 338 sticky_key.HandleKeyEvent(ev.get()); 339 340 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 341 // Making sure Shift up keyboard event is dispatched. 342 ASSERT_EQ(2U, mock_delegate->GetEventCount()); 343 EXPECT_EQ(ui::ET_KEY_PRESSED, mock_delegate->GetEvent(0)->type()); 344 EXPECT_EQ(ui::VKEY_A, 345 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(0)) 346 ->key_code()); 347 EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type()); 348 EXPECT_EQ(ui::VKEY_SHIFT, 349 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1)) 350 ->key_code()); 351 352 // Enabled state is one shot, so next key event should not be shift modified. 353 ev.reset(GenerateKey(true, ui::VKEY_A)); 354 sticky_key.HandleKeyEvent(ev.get()); 355 EXPECT_FALSE(ev->flags() & ui::EF_SHIFT_DOWN); 356 357 ev.reset(GenerateKey(false, ui::VKEY_A)); 358 sticky_key.HandleKeyEvent(ev.get()); 359 EXPECT_FALSE(ev->flags() & ui::EF_SHIFT_DOWN); 360 } 361 362 TEST_F(StickyKeysTest, BasicLockedScenarioTest) { 363 scoped_ptr<ui::KeyEvent> ev; 364 MockStickyKeysHandlerDelegate* mock_delegate = 365 new MockStickyKeysHandlerDelegate(this); 366 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate); 367 368 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 369 370 // By typing shift key, internal state become ENABLED. 371 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); 372 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 373 374 // By typing shift key again, internal state become LOCKED. 375 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); 376 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 377 378 // All keyboard events including keyUp become shift modified. 379 ev.reset(GenerateKey(true, ui::VKEY_A)); 380 sticky_key.HandleKeyEvent(ev.get()); 381 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); 382 383 ev.reset(GenerateKey(false, ui::VKEY_A)); 384 sticky_key.HandleKeyEvent(ev.get()); 385 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); 386 387 // Locked state keeps after normal keyboard event. 388 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 389 390 ev.reset(GenerateKey(true, ui::VKEY_B)); 391 sticky_key.HandleKeyEvent(ev.get()); 392 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); 393 394 ev.reset(GenerateKey(false, ui::VKEY_B)); 395 sticky_key.HandleKeyEvent(ev.get()); 396 EXPECT_TRUE(ev->flags() & ui::EF_SHIFT_DOWN); 397 398 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 399 400 // By typing shift key again, internal state become back to DISABLED. 401 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); 402 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 403 } 404 405 TEST_F(StickyKeysTest, NonTargetModifierTest) { 406 scoped_ptr<ui::KeyEvent> ev; 407 MockStickyKeysHandlerDelegate* mock_delegate = 408 new MockStickyKeysHandlerDelegate(this); 409 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN, mock_delegate); 410 411 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 412 413 // Non target modifier key does not affect internal state 414 ev.reset(GenerateKey(true, ui::VKEY_MENU)); 415 sticky_key.HandleKeyEvent(ev.get()); 416 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 417 418 ev.reset(GenerateKey(false, ui::VKEY_MENU)); 419 sticky_key.HandleKeyEvent(ev.get()); 420 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 421 422 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); 423 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 424 425 // Non target modifier key does not affect internal state 426 ev.reset(GenerateKey(true, ui::VKEY_MENU)); 427 sticky_key.HandleKeyEvent(ev.get()); 428 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 429 430 ev.reset(GenerateKey(false, ui::VKEY_MENU)); 431 sticky_key.HandleKeyEvent(ev.get()); 432 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 433 434 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); 435 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 436 437 // Non target modifier key does not affect internal state 438 ev.reset(GenerateKey(true, ui::VKEY_MENU)); 439 sticky_key.HandleKeyEvent(ev.get()); 440 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 441 442 ev.reset(GenerateKey(false, ui::VKEY_MENU)); 443 sticky_key.HandleKeyEvent(ev.get()); 444 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 445 } 446 447 TEST_F(StickyKeysTest, NormalShortcutTest) { 448 // Sticky keys should not be enabled if we perform a normal shortcut. 449 scoped_ptr<ui::KeyEvent> ev; 450 MockStickyKeysHandlerDelegate* mock_delegate = 451 new MockStickyKeysHandlerDelegate(this); 452 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); 453 454 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 455 456 // Perform ctrl+n shortcut. 457 ev.reset(GenerateKey(true, ui::VKEY_CONTROL)); 458 sticky_key.HandleKeyEvent(ev.get()); 459 ev.reset(GenerateKey(true, ui::VKEY_N)); 460 sticky_key.HandleKeyEvent(ev.get()); 461 ev.reset(GenerateKey(false, ui::VKEY_N)); 462 sticky_key.HandleKeyEvent(ev.get()); 463 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 464 465 // Sticky keys should not be enabled afterwards. 466 ev.reset(GenerateKey(false, ui::VKEY_CONTROL)); 467 sticky_key.HandleKeyEvent(ev.get()); 468 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 469 } 470 471 TEST_F(StickyKeysTest, NormalModifiedClickTest) { 472 scoped_ptr<ui::KeyEvent> kev; 473 scoped_ptr<ui::MouseEvent> mev; 474 MockStickyKeysHandlerDelegate* mock_delegate = 475 new MockStickyKeysHandlerDelegate(this); 476 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); 477 478 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 479 480 // Perform ctrl+click. 481 kev.reset(GenerateKey(true, ui::VKEY_CONTROL)); 482 sticky_key.HandleKeyEvent(kev.get()); 483 mev.reset(GenerateMouseEvent(true)); 484 sticky_key.HandleMouseEvent(mev.get()); 485 mev.reset(GenerateMouseEvent(false)); 486 sticky_key.HandleMouseEvent(mev.get()); 487 488 // Sticky keys should not be enabled afterwards. 489 kev.reset(GenerateKey(false, ui::VKEY_CONTROL)); 490 sticky_key.HandleKeyEvent(kev.get()); 491 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 492 } 493 494 TEST_F(StickyKeysTest, MouseMovedModifierTest) { 495 scoped_ptr<ui::KeyEvent> kev; 496 scoped_ptr<ui::MouseEvent> mev; 497 MockStickyKeysHandlerDelegate* mock_delegate = 498 new MockStickyKeysHandlerDelegate(this); 499 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); 500 501 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 502 503 // Press ctrl and handle mouse move events. 504 kev.reset(GenerateKey(true, ui::VKEY_CONTROL)); 505 sticky_key.HandleKeyEvent(kev.get()); 506 mev.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(0, 0))); 507 sticky_key.HandleMouseEvent(mev.get()); 508 mev.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(100, 100))); 509 sticky_key.HandleMouseEvent(mev.get()); 510 511 // Sticky keys should be enabled afterwards. 512 kev.reset(GenerateKey(false, ui::VKEY_CONTROL)); 513 sticky_key.HandleKeyEvent(kev.get()); 514 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 515 } 516 517 TEST_F(StickyKeysTest, NormalModifiedScrollTest) { 518 scoped_ptr<ui::KeyEvent> kev; 519 scoped_ptr<ui::ScrollEvent> sev; 520 MockStickyKeysHandlerDelegate* mock_delegate = 521 new MockStickyKeysHandlerDelegate(this); 522 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); 523 524 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 525 526 // Perform ctrl+scroll. 527 kev.reset(GenerateKey(true, ui::VKEY_CONTROL)); 528 sev.reset(GenerateFlingScrollEvent(0, true)); 529 sticky_key.HandleScrollEvent(sev.get()); 530 sev.reset(GenerateScrollEvent(10)); 531 sticky_key.HandleScrollEvent(sev.get()); 532 sev.reset(GenerateFlingScrollEvent(10, false)); 533 sticky_key.HandleScrollEvent(sev.get()); 534 535 // Sticky keys should not be enabled afterwards. 536 kev.reset(GenerateKey(false, ui::VKEY_CONTROL)); 537 sticky_key.HandleKeyEvent(kev.get()); 538 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 539 } 540 541 TEST_F(StickyKeysTest, MouseEventOneshot) { 542 scoped_ptr<ui::MouseEvent> ev; 543 scoped_ptr<ui::KeyEvent> kev; 544 MockStickyKeysHandlerDelegate* mock_delegate = 545 new MockStickyKeysHandlerDelegate(this); 546 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); 547 548 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 549 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 550 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 551 552 // We should still be in the ENABLED state until we get the mouse 553 // release event. 554 ev.reset(GenerateMouseEvent(true)); 555 sticky_key.HandleMouseEvent(ev.get()); 556 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); 557 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 558 559 ev.reset(GenerateMouseEvent(false)); 560 sticky_key.HandleMouseEvent(ev.get()); 561 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); 562 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 563 564 // Making sure modifier key release event is dispatched in the right order. 565 ASSERT_EQ(2u, mock_delegate->GetEventCount()); 566 EXPECT_EQ(ui::ET_MOUSE_RELEASED, mock_delegate->GetEvent(0)->type()); 567 EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type()); 568 EXPECT_EQ(ui::VKEY_CONTROL, 569 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1)) 570 ->key_code()); 571 572 // Enabled state is one shot, so next click should not be control modified. 573 ev.reset(GenerateMouseEvent(true)); 574 sticky_key.HandleMouseEvent(ev.get()); 575 EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN); 576 577 ev.reset(GenerateMouseEvent(false)); 578 sticky_key.HandleMouseEvent(ev.get()); 579 EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN); 580 } 581 582 TEST_F(StickyKeysTest, MouseEventLocked) { 583 scoped_ptr<ui::MouseEvent> ev; 584 scoped_ptr<ui::KeyEvent> kev; 585 MockStickyKeysHandlerDelegate* mock_delegate = 586 new MockStickyKeysHandlerDelegate(this); 587 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); 588 589 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 590 591 // Pressing modifier key twice should make us enter lock state. 592 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 593 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 594 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 595 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 596 597 // Mouse events should not disable locked mode. 598 for (int i = 0; i < 3; ++i) { 599 ev.reset(GenerateMouseEvent(true)); 600 sticky_key.HandleMouseEvent(ev.get()); 601 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); 602 ev.reset(GenerateMouseEvent(false)); 603 sticky_key.HandleMouseEvent(ev.get()); 604 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); 605 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 606 } 607 608 // Test with mouse wheel. 609 for (int i = 0; i < 3; ++i) { 610 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta)); 611 sticky_key.HandleMouseEvent(ev.get()); 612 ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta)); 613 sticky_key.HandleMouseEvent(ev.get()); 614 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); 615 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 616 } 617 618 // Test mixed case with mouse events and key events. 619 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta)); 620 sticky_key.HandleMouseEvent(ev.get()); 621 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); 622 kev.reset(GenerateKey(true, ui::VKEY_N)); 623 sticky_key.HandleKeyEvent(kev.get()); 624 EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN); 625 kev.reset(GenerateKey(false, ui::VKEY_N)); 626 sticky_key.HandleKeyEvent(kev.get()); 627 EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN); 628 629 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 630 } 631 632 TEST_F(StickyKeysTest, ScrollEventOneshot) { 633 scoped_ptr<ui::ScrollEvent> ev; 634 scoped_ptr<ui::KeyEvent> kev; 635 MockStickyKeysHandlerDelegate* mock_delegate = 636 new MockStickyKeysHandlerDelegate(this); 637 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); 638 639 int scroll_deltas[] = {-10, 10}; 640 for (int i = 0; i < 2; ++i) { 641 mock_delegate->ClearEvents(); 642 643 // Enable sticky keys. 644 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 645 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 646 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 647 648 // Test a scroll sequence. Sticky keys should only be disabled at the end 649 // of the scroll sequence. Fling cancel event starts the scroll sequence. 650 ev.reset(GenerateFlingScrollEvent(0, true)); 651 sticky_key.HandleScrollEvent(ev.get()); 652 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); 653 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 654 655 // Scrolls should all be modified but not disable sticky keys. 656 for (int j = 0; j < 3; ++j) { 657 ev.reset(GenerateScrollEvent(scroll_deltas[i])); 658 sticky_key.HandleScrollEvent(ev.get()); 659 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); 660 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 661 } 662 663 // Fling start event ends scroll sequence. 664 ev.reset(GenerateFlingScrollEvent(scroll_deltas[i], false)); 665 sticky_key.HandleScrollEvent(ev.get()); 666 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); 667 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 668 669 ASSERT_EQ(2U, mock_delegate->GetEventCount()); 670 EXPECT_EQ(ui::ET_SCROLL_FLING_START, mock_delegate->GetEvent(0)->type()); 671 EXPECT_FLOAT_EQ(scroll_deltas[i], 672 static_cast<const ui::ScrollEvent*>( 673 mock_delegate->GetEvent(0))->y_offset()); 674 EXPECT_EQ(ui::ET_KEY_RELEASED, mock_delegate->GetEvent(1)->type()); 675 EXPECT_EQ(ui::VKEY_CONTROL, 676 static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1)) 677 ->key_code()); 678 } 679 } 680 681 TEST_F(StickyKeysTest, ScrollDirectionChanged) { 682 scoped_ptr<ui::ScrollEvent> ev; 683 scoped_ptr<ui::KeyEvent> kev; 684 MockStickyKeysHandlerDelegate* mock_delegate = 685 new MockStickyKeysHandlerDelegate(this); 686 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); 687 688 // Test direction change with both boundary value and negative value. 689 const int direction_change_values[2] = {0, -10}; 690 for (int i = 0; i < 2; ++i) { 691 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 692 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 693 694 // Fling cancel starts scroll sequence. 695 ev.reset(GenerateFlingScrollEvent(0, true)); 696 sticky_key.HandleScrollEvent(ev.get()); 697 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 698 699 // Test that changing directions in a scroll sequence will 700 // return sticky keys to DISABLED state. 701 for (int j = 0; j < 3; ++j) { 702 ev.reset(GenerateScrollEvent(10)); 703 sticky_key.HandleScrollEvent(ev.get()); 704 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); 705 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 706 } 707 708 ev.reset(GenerateScrollEvent(direction_change_values[i])); 709 sticky_key.HandleScrollEvent(ev.get()); 710 EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN); 711 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 712 } 713 } 714 715 TEST_F(StickyKeysTest, ScrollEventLocked) { 716 scoped_ptr<ui::ScrollEvent> ev; 717 scoped_ptr<ui::KeyEvent> kev; 718 MockStickyKeysHandlerDelegate* mock_delegate = 719 new MockStickyKeysHandlerDelegate(this); 720 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); 721 722 // Lock sticky keys. 723 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 724 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 725 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 726 727 // Test scroll events are correctly modified in locked state. 728 for (int i = 0; i < 5; ++i) { 729 // Fling cancel starts scroll sequence. 730 ev.reset(GenerateFlingScrollEvent(0, true)); 731 sticky_key.HandleScrollEvent(ev.get()); 732 733 ev.reset(GenerateScrollEvent(10)); 734 sticky_key.HandleScrollEvent(ev.get()); 735 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); 736 ev.reset(GenerateScrollEvent(-10)); 737 sticky_key.HandleScrollEvent(ev.get()); 738 EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN); 739 740 // Fling start ends scroll sequence. 741 ev.reset(GenerateFlingScrollEvent(-10, false)); 742 sticky_key.HandleScrollEvent(ev.get()); 743 } 744 745 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 746 } 747 748 TEST_F(StickyKeysTest, EventTargetDestroyed) { 749 scoped_ptr<ui::KeyEvent> ev; 750 MockStickyKeysHandlerDelegate* mock_delegate = 751 new MockStickyKeysHandlerDelegate(this); 752 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); 753 754 target()->Focus(); 755 756 // Go into ENABLED state. 757 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 758 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 759 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 760 761 // CTRL+J is a special shortcut that will destroy the event target. 762 ev.reset(GenerateKey(true, ui::VKEY_J)); 763 sticky_key.HandleKeyEvent(ev.get()); 764 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 765 EXPECT_FALSE(target()); 766 } 767 768 TEST_F(StickyKeysTest, SynthesizedEvents) { 769 // Non-native, internally generated events should be properly handled 770 // by sticky keys. 771 MockStickyKeysHandlerDelegate* mock_delegate = 772 new MockStickyKeysHandlerDelegate(this); 773 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate); 774 775 // Test non-native key events. 776 scoped_ptr<ui::KeyEvent> kev; 777 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 778 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 779 780 kev.reset(GenerateSynthesizedKeyEvent(true, ui::VKEY_K)); 781 sticky_key.HandleKeyEvent(kev.get()); 782 EXPECT_TRUE(kev->flags() & ui::EF_CONTROL_DOWN); 783 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 784 785 kev.reset(GenerateSynthesizedKeyEvent(false, ui::VKEY_K)); 786 sticky_key.HandleKeyEvent(kev.get()); 787 EXPECT_FALSE(kev->flags() & ui::EF_CONTROL_DOWN); 788 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 789 790 // Test non-native mouse events. 791 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 792 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 793 794 scoped_ptr<ui::MouseEvent> mev; 795 mev.reset(GenerateSynthesizedMouseClickEvent(true, gfx::Point(0, 0))); 796 sticky_key.HandleMouseEvent(mev.get()); 797 EXPECT_TRUE(mev->flags() & ui::EF_CONTROL_DOWN); 798 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 799 800 mev.reset(GenerateSynthesizedMouseClickEvent(false, gfx::Point(0, 0))); 801 sticky_key.HandleMouseEvent(mev.get()); 802 EXPECT_TRUE(mev->flags() & ui::EF_CONTROL_DOWN); 803 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 804 } 805 806 TEST_F(StickyKeysTest, KeyEventDispatchImpl) { 807 // Test the actual key event dispatch implementation. 808 EventBuffer buffer; 809 ScopedVector<ui::Event> events; 810 ui::EventProcessor* dispatcher = 811 Shell::GetPrimaryRootWindow()->GetHost()->event_processor(); 812 Shell::GetInstance()->AddPreTargetHandler(&buffer); 813 Shell::GetInstance()->sticky_keys_controller()->Enable(true); 814 815 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL); 816 scoped_ptr<ui::KeyEvent> ev; 817 buffer.PopEvents(&events); 818 819 // Test key press event is correctly modified and modifier release 820 // event is sent. 821 ev.reset(GenerateKey(true, ui::VKEY_C)); 822 ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get()); 823 buffer.PopEvents(&events); 824 EXPECT_EQ(2u, events.size()); 825 EXPECT_EQ(ui::ET_KEY_PRESSED, events[0]->type()); 826 EXPECT_EQ(ui::VKEY_C, static_cast<ui::KeyEvent*>(events[0])->key_code()); 827 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); 828 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); 829 EXPECT_EQ(ui::VKEY_CONTROL, 830 static_cast<ui::KeyEvent*>(events[1])->key_code()); 831 832 // Test key release event is not modified. 833 ev.reset(GenerateKey(false, ui::VKEY_C)); 834 details = dispatcher->OnEventFromSource(ev.get()); 835 ASSERT_FALSE(details.dispatcher_destroyed); 836 buffer.PopEvents(&events); 837 EXPECT_EQ(1u, events.size()); 838 EXPECT_EQ(ui::ET_KEY_RELEASED, events[0]->type()); 839 EXPECT_EQ(ui::VKEY_C, 840 static_cast<ui::KeyEvent*>(events[0])->key_code()); 841 EXPECT_FALSE(events[0]->flags() & ui::EF_CONTROL_DOWN); 842 843 // Test that synthesized key events are dispatched correctly. 844 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL); 845 buffer.PopEvents(&events); 846 scoped_ptr<ui::KeyEvent> kev; 847 kev.reset(GenerateSynthesizedKeyEvent(true, ui::VKEY_K)); 848 details = dispatcher->OnEventFromSource(kev.get()); 849 ASSERT_FALSE(details.dispatcher_destroyed); 850 buffer.PopEvents(&events); 851 EXPECT_EQ(2u, events.size()); 852 EXPECT_EQ(ui::ET_KEY_PRESSED, events[0]->type()); 853 EXPECT_EQ(ui::VKEY_K, static_cast<ui::KeyEvent*>(events[0])->key_code()); 854 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); 855 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); 856 EXPECT_EQ(ui::VKEY_CONTROL, 857 static_cast<ui::KeyEvent*>(events[1])->key_code()); 858 859 Shell::GetInstance()->RemovePreTargetHandler(&buffer); 860 } 861 862 class StickyKeysMouseDispatchTest 863 : public StickyKeysTest, 864 public ::testing::WithParamInterface<int> { 865 }; 866 867 TEST_P(StickyKeysMouseDispatchTest, MouseEventDispatchImpl) { 868 int scale_factor = GetParam(); 869 std::ostringstream display_specs; 870 display_specs << "1280x1024*" << scale_factor; 871 UpdateDisplay(display_specs.str()); 872 873 EventBuffer buffer; 874 ScopedVector<ui::Event> events; 875 ui::EventProcessor* dispatcher = 876 Shell::GetPrimaryRootWindow()->GetHost()->event_processor(); 877 Shell::GetInstance()->AddPreTargetHandler(&buffer); 878 Shell::GetInstance()->sticky_keys_controller()->Enable(true); 879 880 scoped_ptr<ui::MouseEvent> ev; 881 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL); 882 buffer.PopEvents(&events); 883 884 // Test mouse press event is correctly modified and has correct DIP location. 885 gfx::Point physical_location(400, 400); 886 gfx::Point dip_location(physical_location.x() / scale_factor, 887 physical_location.y() / scale_factor); 888 ev.reset(GenerateMouseEventAt(true, physical_location)); 889 ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get()); 890 buffer.PopEvents(&events); 891 EXPECT_EQ(1u, events.size()); 892 EXPECT_EQ(ui::ET_MOUSE_PRESSED, events[0]->type()); 893 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); 894 EXPECT_EQ(dip_location.ToString(), 895 static_cast<ui::MouseEvent*>(events[0])->location().ToString()); 896 897 // Test mouse release event is correctly modified and modifier release 898 // event is sent. The mouse event should have the correct DIP location. 899 ev.reset(GenerateMouseEventAt(false, physical_location)); 900 details = dispatcher->OnEventFromSource(ev.get()); 901 ASSERT_FALSE(details.dispatcher_destroyed); 902 buffer.PopEvents(&events); 903 EXPECT_EQ(2u, events.size()); 904 EXPECT_EQ(ui::ET_MOUSE_RELEASED, events[0]->type()); 905 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); 906 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); 907 EXPECT_EQ(dip_location.ToString(), 908 static_cast<ui::MouseEvent*>(events[0])->location().ToString()); 909 EXPECT_EQ(ui::VKEY_CONTROL, 910 static_cast<ui::KeyEvent*>(events[1])->key_code()); 911 912 // Test synthesized mouse events are dispatched correctly. 913 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL); 914 buffer.PopEvents(&events); 915 ev.reset(GenerateSynthesizedMouseClickEvent(false, physical_location)); 916 details = dispatcher->OnEventFromSource(ev.get()); 917 ASSERT_FALSE(details.dispatcher_destroyed); 918 buffer.PopEvents(&events); 919 EXPECT_EQ(2u, events.size()); 920 EXPECT_EQ(ui::ET_MOUSE_RELEASED, events[0]->type()); 921 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); 922 EXPECT_EQ(dip_location.ToString(), 923 static_cast<ui::MouseEvent*>(events[0])->location().ToString()); 924 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); 925 EXPECT_EQ(ui::VKEY_CONTROL, 926 static_cast<ui::KeyEvent*>(events[1])->key_code()); 927 928 Shell::GetInstance()->RemovePreTargetHandler(&buffer); 929 } 930 931 TEST_P(StickyKeysMouseDispatchTest, MouseWheelEventDispatchImpl) { 932 int scale_factor = GetParam(); 933 std::ostringstream display_specs; 934 display_specs << "1280x1024*" << scale_factor; 935 UpdateDisplay(display_specs.str()); 936 937 // Test the actual mouse wheel event dispatch implementation. 938 EventBuffer buffer; 939 ScopedVector<ui::Event> events; 940 ui::EventProcessor* dispatcher = 941 Shell::GetPrimaryRootWindow()->GetHost()->event_processor(); 942 Shell::GetInstance()->AddPreTargetHandler(&buffer); 943 Shell::GetInstance()->sticky_keys_controller()->Enable(true); 944 945 scoped_ptr<ui::MouseWheelEvent> ev; 946 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL); 947 buffer.PopEvents(&events); 948 949 // Test positive mouse wheel event is correctly modified and modifier release 950 // event is sent. 951 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta)); 952 ui::EventDispatchDetails details = dispatcher->OnEventFromSource(ev.get()); 953 ASSERT_FALSE(details.dispatcher_destroyed); 954 buffer.PopEvents(&events); 955 EXPECT_EQ(2u, events.size()); 956 EXPECT_TRUE(events[0]->IsMouseWheelEvent()); 957 EXPECT_EQ(ui::MouseWheelEvent::kWheelDelta / scale_factor, 958 static_cast<ui::MouseWheelEvent*>(events[0])->y_offset()); 959 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); 960 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); 961 EXPECT_EQ(ui::VKEY_CONTROL, 962 static_cast<ui::KeyEvent*>(events[1])->key_code()); 963 964 // Test negative mouse wheel event is correctly modified and modifier release 965 // event is sent. 966 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL); 967 buffer.PopEvents(&events); 968 969 ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta)); 970 details = dispatcher->OnEventFromSource(ev.get()); 971 ASSERT_FALSE(details.dispatcher_destroyed); 972 buffer.PopEvents(&events); 973 EXPECT_EQ(2u, events.size()); 974 EXPECT_TRUE(events[0]->IsMouseWheelEvent()); 975 EXPECT_EQ(-ui::MouseWheelEvent::kWheelDelta / scale_factor, 976 static_cast<ui::MouseWheelEvent*>(events[0])->y_offset()); 977 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); 978 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); 979 EXPECT_EQ(ui::VKEY_CONTROL, 980 static_cast<ui::KeyEvent*>(events[1])->key_code()); 981 982 // Test synthesized mouse wheel events are dispatched correctly. 983 SendActivateStickyKeyPattern(dispatcher, ui::VKEY_CONTROL); 984 buffer.PopEvents(&events); 985 ev.reset( 986 GenerateSynthesizedMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta)); 987 details = dispatcher->OnEventFromSource(ev.get()); 988 ASSERT_FALSE(details.dispatcher_destroyed); 989 buffer.PopEvents(&events); 990 EXPECT_EQ(2u, events.size()); 991 EXPECT_TRUE(events[0]->IsMouseWheelEvent()); 992 EXPECT_EQ(ui::MouseWheelEvent::kWheelDelta / scale_factor, 993 static_cast<ui::MouseWheelEvent*>(events[0])->y_offset()); 994 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); 995 EXPECT_TRUE(events[0]->flags() & ui::EF_CONTROL_DOWN); 996 EXPECT_EQ(ui::ET_KEY_RELEASED, events[1]->type()); 997 EXPECT_EQ(ui::VKEY_CONTROL, 998 static_cast<ui::KeyEvent*>(events[1])->key_code()); 999 1000 Shell::GetInstance()->RemovePreTargetHandler(&buffer); 1001 } 1002 1003 INSTANTIATE_TEST_CASE_P(DPIScaleFactors, 1004 StickyKeysMouseDispatchTest, 1005 ::testing::Values(1, 2)); 1006 1007 } // namespace ash 1008