1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "ash/sticky_keys/sticky_keys_controller.h" 6 7#include <X11/Xlib.h> 8#undef None 9#undef Bool 10#undef RootWindow 11 12#include "ash/shell.h" 13#include "ash/test/ash_test_base.h" 14#include "base/bind.h" 15#include "base/callback.h" 16#include "base/memory/scoped_vector.h" 17#include "ui/aura/window.h" 18#include "ui/aura/window_tree_host.h" 19#include "ui/events/event_source.h" 20#include "ui/events/test/events_test_utils_x11.h" 21 22namespace ash { 23 24namespace { 25 26// The device id of the test touchpad device. 27const unsigned int kTouchPadDeviceId = 1; 28 29} // namespace 30 31class StickyKeysTest : public test::AshTestBase { 32 protected: 33 StickyKeysTest() 34 : target_(NULL), 35 root_window_(NULL) {} 36 37 virtual void SetUp() OVERRIDE { 38 test::AshTestBase::SetUp(); 39 40 // |target_| owned by root window of shell. It is still safe to delete 41 // it ourselves. 42 target_ = CreateTestWindowInShellWithId(0); 43 root_window_ = target_->GetRootWindow(); 44 45 ui::SetUpTouchPadForTest(kTouchPadDeviceId); 46 } 47 48 virtual void TearDown() OVERRIDE { 49 test::AshTestBase::TearDown(); 50 } 51 52 virtual void OnShortcutPressed() { 53 if (target_) { 54 delete target_; 55 target_ = NULL; 56 } 57 } 58 59 ui::KeyEvent* GenerateKey(ui::EventType type, ui::KeyboardCode code) { 60 scoped_xevent_.InitKeyEvent(type, code, 0); 61 ui::KeyEvent* event = new ui::KeyEvent(scoped_xevent_); 62 return event; 63 } 64 65 // Creates a mouse event backed by a native XInput2 generic button event. 66 // This is the standard native event on Chromebooks. 67 ui::MouseEvent* GenerateMouseEvent(ui::EventType type) { 68 return GenerateMouseEventAt(type, gfx::Point()); 69 } 70 71 // Creates a mouse event backed by a native XInput2 generic button event. 72 // The |location| should be in physical pixels. 73 ui::MouseEvent* GenerateMouseEventAt(ui::EventType type, 74 const gfx::Point& location) { 75 scoped_xevent_.InitGenericButtonEvent( 76 kTouchPadDeviceId, 77 type, 78 location, 79 0); 80 ui::MouseEvent* event = new ui::MouseEvent(scoped_xevent_); 81 return event; 82 } 83 84 ui::MouseWheelEvent* GenerateMouseWheelEvent(int wheel_delta) { 85 EXPECT_NE(0, wheel_delta); 86 scoped_xevent_.InitGenericMouseWheelEvent( 87 kTouchPadDeviceId, wheel_delta, 0); 88 ui::MouseWheelEvent* event = new ui::MouseWheelEvent(scoped_xevent_); 89 ui::Event::DispatcherApi dispatcher(event); 90 dispatcher.set_target(target_); 91 return event; 92 } 93 94 ui::ScrollEvent* GenerateScrollEvent(int scroll_delta) { 95 scoped_xevent_.InitScrollEvent(kTouchPadDeviceId, // deviceid 96 0, // x_offset 97 scroll_delta, // y_offset 98 0, // x_offset_ordinal 99 scroll_delta, // y_offset_ordinal 100 2); // finger_count 101 ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_); 102 ui::Event::DispatcherApi dispatcher(event); 103 dispatcher.set_target(target_); 104 return event; 105 } 106 107 ui::ScrollEvent* GenerateFlingScrollEvent(int fling_delta, 108 bool is_cancel) { 109 scoped_xevent_.InitFlingScrollEvent( 110 kTouchPadDeviceId, // deviceid 111 0, // x_velocity 112 fling_delta, // y_velocity 113 0, // x_velocity_ordinal 114 fling_delta, // y_velocity_ordinal 115 is_cancel); // is_cancel 116 ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_); 117 ui::Event::DispatcherApi dispatcher(event); 118 dispatcher.set_target(target_); 119 return event; 120 } 121 122 // Creates a synthesized KeyEvent that is not backed by a native event. 123 ui::KeyEvent* GenerateSynthesizedKeyEvent(ui::EventType type, 124 ui::KeyboardCode code) { 125 return new ui::KeyEvent(type, code, ui::EF_NONE); 126 } 127 128 // Creates a synthesized MouseEvent that is not backed by a native event. 129 ui::MouseEvent* GenerateSynthesizedMouseEventAt(ui::EventType event_type, 130 const gfx::Point& location) { 131 ui::MouseEvent* event = new ui::MouseEvent(event_type, 132 location, 133 location, 134 ui::EF_LEFT_MOUSE_BUTTON, 135 ui::EF_LEFT_MOUSE_BUTTON); 136 ui::Event::DispatcherApi dispatcher(event); 137 dispatcher.set_target(target_); 138 return event; 139 } 140 141 // Creates a synthesized mouse press or release event. 142 ui::MouseEvent* GenerateSynthesizedMouseClickEvent( 143 ui::EventType type, 144 const gfx::Point& location) { 145 return GenerateSynthesizedMouseEventAt(type, location); 146 } 147 148 // Creates a synthesized ET_MOUSE_MOVED event. 149 ui::MouseEvent* GenerateSynthesizedMouseMoveEvent( 150 const gfx::Point& location) { 151 return GenerateSynthesizedMouseEventAt(ui::ET_MOUSE_MOVED, location); 152 } 153 154 // Creates a synthesized MouseWHeel event. 155 ui::MouseWheelEvent* GenerateSynthesizedMouseWheelEvent(int wheel_delta) { 156 scoped_ptr<ui::MouseEvent> mev( 157 GenerateSynthesizedMouseEventAt(ui::ET_MOUSEWHEEL, gfx::Point(0, 0))); 158 ui::MouseWheelEvent* event = new ui::MouseWheelEvent(*mev, 0, wheel_delta); 159 ui::Event::DispatcherApi dispatcher(event); 160 dispatcher.set_target(target_); 161 return event; 162 } 163 164 void SendActivateStickyKeyPattern(StickyKeysHandler* handler, 165 ui::KeyboardCode key_code) { 166 bool released = false; 167 int down_flags = 0; 168 scoped_ptr<ui::KeyEvent> ev; 169 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, key_code)); 170 handler->HandleKeyEvent(*ev.get(), key_code, &down_flags, &released); 171 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, key_code)); 172 handler->HandleKeyEvent(*ev.get(), key_code, &down_flags, &released); 173 } 174 175 bool HandleKeyEvent(const ui::KeyEvent& key_event, 176 StickyKeysHandler* handler, 177 int* down, 178 bool* up) { 179 return handler->HandleKeyEvent(key_event, key_event.key_code(), down, up); 180 } 181 182 int HandleKeyEventForDownFlags(const ui::KeyEvent& key_event, 183 StickyKeysHandler* handler) { 184 bool released = false; 185 int down = 0; 186 handler->HandleKeyEvent(key_event, key_event.key_code(), &down, &released); 187 return down; 188 } 189 190 aura::Window* target() { return target_; } 191 192 private: 193 // Owned by root window of shell, but we can still delete |target_| safely. 194 aura::Window* target_; 195 // The root window of |target_|. Not owned. 196 aura::Window* root_window_; 197 198 // Used to construct the various X events. 199 ui::ScopedXI2Event scoped_xevent_; 200 201 DISALLOW_COPY_AND_ASSIGN(StickyKeysTest); 202}; 203 204TEST_F(StickyKeysTest, BasicOneshotScenarioTest) { 205 scoped_ptr<ui::KeyEvent> ev; 206 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN); 207 208 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 209 210 // By typing Shift key, internal state become ENABLED. 211 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); 212 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 213 214 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_A)); 215 bool released = false; 216 int mod_down_flags = 0; 217 HandleKeyEvent(*ev.get(), &sticky_key, &mod_down_flags, &released); 218 // Next keyboard event is shift modified. 219 EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN); 220 // Modifier release notification happens. 221 EXPECT_TRUE(released); 222 223 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_A)); 224 released = false; 225 mod_down_flags = 0; 226 HandleKeyEvent(*ev.get(), &sticky_key, &mod_down_flags, &released); 227 228 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 229 // Making sure Shift up keyboard event is available. 230 scoped_ptr<ui::Event> up_event; 231 ASSERT_EQ(0, sticky_key.GetModifierUpEvent(&up_event)); 232 EXPECT_TRUE(up_event.get()); 233 EXPECT_EQ(ui::ET_KEY_RELEASED, up_event->type()); 234 EXPECT_EQ(ui::VKEY_SHIFT, 235 static_cast<const ui::KeyEvent*>(up_event.get())->key_code()); 236 237 // Enabled state is one shot, so next key event should not be shift modified. 238 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_A)); 239 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 240 EXPECT_FALSE(mod_down_flags & ui::EF_SHIFT_DOWN); 241 242 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_A)); 243 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 244 EXPECT_FALSE(mod_down_flags & ui::EF_SHIFT_DOWN); 245} 246 247TEST_F(StickyKeysTest, BasicLockedScenarioTest) { 248 scoped_ptr<ui::KeyEvent> ev; 249 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN); 250 251 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 252 253 // By typing shift key, internal state become ENABLED. 254 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); 255 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 256 257 // By typing shift key again, internal state become LOCKED. 258 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); 259 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 260 261 // All keyboard events including keyUp become shift modified. 262 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_A)); 263 int mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 264 EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN); 265 266 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_A)); 267 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 268 EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN); 269 270 // Locked state keeps after normal keyboard event. 271 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 272 273 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_B)); 274 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 275 EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN); 276 277 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_B)); 278 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 279 EXPECT_TRUE(mod_down_flags & ui::EF_SHIFT_DOWN); 280 281 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 282 283 // By typing shift key again, internal state become back to DISABLED. 284 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); 285 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 286} 287 288TEST_F(StickyKeysTest, NonTargetModifierTest) { 289 scoped_ptr<ui::KeyEvent> ev; 290 StickyKeysHandler sticky_key(ui::EF_SHIFT_DOWN); 291 292 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 293 294 // Non target modifier key does not affect internal state 295 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_MENU)); 296 int mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 297 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 298 EXPECT_EQ(ui::EF_NONE, mod_down_flags); 299 300 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_MENU)); 301 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 302 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 303 EXPECT_EQ(ui::EF_NONE, mod_down_flags); 304 305 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); 306 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 307 308 // Non target modifier key does not affect internal state 309 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_MENU)); 310 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 311 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 312 EXPECT_EQ(ui::EF_NONE, mod_down_flags); 313 314 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_MENU)); 315 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 316 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 317 EXPECT_EQ(ui::EF_NONE, mod_down_flags); 318 319 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_SHIFT); 320 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 321 322 // Non target modifier key does not affect internal state 323 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_MENU)); 324 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 325 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 326 EXPECT_EQ(ui::EF_NONE, mod_down_flags); 327 328 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_MENU)); 329 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 330 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 331 EXPECT_EQ(ui::EF_NONE, mod_down_flags); 332} 333 334TEST_F(StickyKeysTest, NormalShortcutTest) { 335 // Sticky keys should not be enabled if we perform a normal shortcut. 336 scoped_ptr<ui::KeyEvent> ev; 337 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); 338 339 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 340 341 // Perform ctrl+n shortcut. 342 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL)); 343 int mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 344 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_N)); 345 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 346 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_N)); 347 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 348 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 349 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL)); 350 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 351 352 // Sticky keys should not be enabled afterwards. 353 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 354 EXPECT_EQ(ui::EF_NONE, mod_down_flags); 355 356 // Perform ctrl+n shortcut, releasing ctrl first. 357 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL)); 358 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 359 ev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_N)); 360 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 361 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 362 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL)); 363 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 364 ev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_N)); 365 mod_down_flags = HandleKeyEventForDownFlags(*ev.get(), &sticky_key); 366 367 // Sticky keys should not be enabled afterwards. 368 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 369 EXPECT_EQ(ui::EF_NONE, mod_down_flags); 370} 371 372TEST_F(StickyKeysTest, NormalModifiedClickTest) { 373 scoped_ptr<ui::KeyEvent> kev; 374 scoped_ptr<ui::MouseEvent> mev; 375 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); 376 377 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 378 379 // Perform ctrl+click. 380 kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL)); 381 int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); 382 mev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED)); 383 bool released = false; 384 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released); 385 mev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED)); 386 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released); 387 388 // Sticky keys should not be enabled afterwards. 389 kev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL)); 390 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); 391 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 392 EXPECT_EQ(ui::EF_NONE, mod_down_flags); 393} 394 395TEST_F(StickyKeysTest, MouseMovedModifierTest) { 396 scoped_ptr<ui::KeyEvent> kev; 397 scoped_ptr<ui::MouseEvent> mev; 398 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); 399 400 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 401 402 // Press ctrl and handle mouse move events. 403 kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL)); 404 int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); 405 mev.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(0, 0))); 406 bool released = false; 407 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released); 408 mev.reset(GenerateSynthesizedMouseMoveEvent(gfx::Point(100, 100))); 409 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released); 410 411 // Sticky keys should be enabled afterwards. 412 kev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL)); 413 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); 414 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 415 EXPECT_EQ(ui::EF_NONE, mod_down_flags); 416} 417 418TEST_F(StickyKeysTest, NormalModifiedScrollTest) { 419 scoped_ptr<ui::KeyEvent> kev; 420 scoped_ptr<ui::ScrollEvent> sev; 421 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); 422 423 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 424 425 // Perform ctrl+scroll. 426 kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL)); 427 int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); 428 sev.reset(GenerateFlingScrollEvent(0, true)); 429 bool released = false; 430 sticky_key.HandleScrollEvent(*sev.get(), &mod_down_flags, &released); 431 sev.reset(GenerateScrollEvent(10)); 432 sticky_key.HandleScrollEvent(*sev.get(), &mod_down_flags, &released); 433 sev.reset(GenerateFlingScrollEvent(10, false)); 434 sticky_key.HandleScrollEvent(*sev.get(), &mod_down_flags, &released); 435 436 // Sticky keys should not be enabled afterwards. 437 kev.reset(GenerateKey(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL)); 438 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); 439 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 440 EXPECT_EQ(ui::EF_NONE, mod_down_flags); 441} 442 443TEST_F(StickyKeysTest, MouseEventOneshot) { 444 scoped_ptr<ui::MouseEvent> ev; 445 scoped_ptr<ui::KeyEvent> kev; 446 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); 447 448 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 449 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 450 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 451 452 // We should still be in the ENABLED state until we get the mouse 453 // release event. 454 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED)); 455 bool released = false; 456 int mod_down_flags = 0; 457 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); 458 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 459 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 460 461 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED)); 462 released = false; 463 mod_down_flags = 0; 464 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); 465 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 466 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 467 468 // Making sure modifier key release event is dispatched in the right order. 469 EXPECT_TRUE(released); 470 scoped_ptr<ui::Event> up_event; 471 ASSERT_EQ(0, sticky_key.GetModifierUpEvent(&up_event)); 472 EXPECT_TRUE(up_event.get()); 473 EXPECT_EQ(ui::ET_KEY_RELEASED, up_event->type()); 474 EXPECT_EQ(ui::VKEY_CONTROL, 475 static_cast<const ui::KeyEvent*>(up_event.get())->key_code()); 476 477 // Enabled state is one shot, so next click should not be control modified. 478 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED)); 479 released = false; 480 mod_down_flags = 0; 481 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); 482 EXPECT_FALSE(mod_down_flags & ui::EF_CONTROL_DOWN); 483 484 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED)); 485 released = false; 486 mod_down_flags = 0; 487 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); 488 EXPECT_FALSE(mod_down_flags & ui::EF_CONTROL_DOWN); 489} 490 491TEST_F(StickyKeysTest, MouseEventLocked) { 492 scoped_ptr<ui::MouseEvent> ev; 493 scoped_ptr<ui::KeyEvent> kev; 494 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); 495 496 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 497 498 // Pressing modifier key twice should make us enter lock state. 499 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 500 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 501 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 502 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 503 504 // Mouse events should not disable locked mode. 505 for (int i = 0; i < 3; ++i) { 506 bool released = false; 507 int mod_down_flags = 0; 508 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_PRESSED)); 509 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); 510 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 511 ev.reset(GenerateMouseEvent(ui::ET_MOUSE_RELEASED)); 512 released = false; 513 mod_down_flags = 0; 514 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); 515 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 516 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 517 } 518 519 // Test with mouse wheel. 520 for (int i = 0; i < 3; ++i) { 521 bool released = false; 522 int mod_down_flags = 0; 523 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta)); 524 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); 525 ev.reset(GenerateMouseWheelEvent(-ui::MouseWheelEvent::kWheelDelta)); 526 released = false; 527 mod_down_flags = 0; 528 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); 529 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 530 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 531 } 532 533 // Test mixed case with mouse events and key events. 534 ev.reset(GenerateMouseWheelEvent(ui::MouseWheelEvent::kWheelDelta)); 535 bool released = false; 536 int mod_down_flags = 0; 537 sticky_key.HandleMouseEvent(*ev.get(), &mod_down_flags, &released); 538 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 539 kev.reset(GenerateKey(ui::ET_KEY_PRESSED, ui::VKEY_N)); 540 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); 541 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 542 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); 543 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 544 545 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 546} 547 548TEST_F(StickyKeysTest, ScrollEventOneshot) { 549 scoped_ptr<ui::ScrollEvent> ev; 550 scoped_ptr<ui::KeyEvent> kev; 551 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); 552 553 int scroll_deltas[] = {-10, 10}; 554 for (int i = 0; i < 2; ++i) { 555 // Enable sticky keys. 556 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 557 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 558 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 559 560 // Test a scroll sequence. Sticky keys should only be disabled at the end 561 // of the scroll sequence. Fling cancel event starts the scroll sequence. 562 ev.reset(GenerateFlingScrollEvent(0, true)); 563 bool released = false; 564 int mod_down_flags = 0; 565 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); 566 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 567 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 568 569 // Scrolls should all be modified but not disable sticky keys. 570 for (int j = 0; j < 3; ++j) { 571 ev.reset(GenerateScrollEvent(scroll_deltas[i])); 572 released = false; 573 mod_down_flags = 0; 574 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); 575 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 576 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 577 } 578 579 // Fling start event ends scroll sequence. 580 ev.reset(GenerateFlingScrollEvent(scroll_deltas[i], false)); 581 released = false; 582 mod_down_flags = 0; 583 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); 584 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 585 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 586 587 scoped_ptr<ui::Event> up_event; 588 EXPECT_TRUE(released); 589 ASSERT_EQ(0, sticky_key.GetModifierUpEvent(&up_event)); 590 EXPECT_TRUE(up_event.get()); 591 EXPECT_EQ(ui::ET_KEY_RELEASED, up_event->type()); 592 EXPECT_EQ(ui::VKEY_CONTROL, 593 static_cast<const ui::KeyEvent*>(up_event.get())->key_code()); 594 } 595} 596 597TEST_F(StickyKeysTest, ScrollDirectionChanged) { 598 scoped_ptr<ui::ScrollEvent> ev; 599 scoped_ptr<ui::KeyEvent> kev; 600 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); 601 602 // Test direction change with both boundary value and negative value. 603 const int direction_change_values[2] = {0, -10}; 604 for (int i = 0; i < 2; ++i) { 605 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 606 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 607 608 // Fling cancel starts scroll sequence. 609 ev.reset(GenerateFlingScrollEvent(0, true)); 610 bool released = false; 611 int mod_down_flags = 0; 612 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); 613 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 614 615 // Test that changing directions in a scroll sequence will 616 // return sticky keys to DISABLED state. 617 for (int j = 0; j < 3; ++j) { 618 ev.reset(GenerateScrollEvent(10)); 619 released = false; 620 mod_down_flags = 0; 621 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); 622 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 623 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 624 } 625 626 ev.reset(GenerateScrollEvent(direction_change_values[i])); 627 released = false; 628 mod_down_flags = 0; 629 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); 630 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 631 } 632} 633 634TEST_F(StickyKeysTest, ScrollEventLocked) { 635 scoped_ptr<ui::ScrollEvent> ev; 636 scoped_ptr<ui::KeyEvent> kev; 637 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); 638 639 // Lock sticky keys. 640 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 641 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 642 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 643 644 // Test scroll events are correctly modified in locked state. 645 for (int i = 0; i < 5; ++i) { 646 // Fling cancel starts scroll sequence. 647 ev.reset(GenerateFlingScrollEvent(0, true)); 648 bool released = false; 649 int mod_down_flags = 0; 650 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); 651 652 ev.reset(GenerateScrollEvent(10)); 653 released = false; 654 mod_down_flags = 0; 655 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); 656 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 657 ev.reset(GenerateScrollEvent(-10)); 658 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); 659 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 660 661 // Fling start ends scroll sequence. 662 ev.reset(GenerateFlingScrollEvent(-10, false)); 663 sticky_key.HandleScrollEvent(*ev.get(), &mod_down_flags, &released); 664 } 665 666 EXPECT_EQ(STICKY_KEY_STATE_LOCKED, sticky_key.current_state()); 667} 668 669TEST_F(StickyKeysTest, SynthesizedEvents) { 670 // Non-native, internally generated events should be properly handled 671 // by sticky keys. 672 StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN); 673 674 // Test non-native key events. 675 scoped_ptr<ui::KeyEvent> kev; 676 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 677 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 678 679 kev.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_K)); 680 int mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); 681 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 682 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 683 684 kev.reset(GenerateSynthesizedKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_K)); 685 mod_down_flags = HandleKeyEventForDownFlags(*kev.get(), &sticky_key); 686 EXPECT_FALSE(mod_down_flags & ui::EF_CONTROL_DOWN); 687 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 688 689 // Test non-native mouse events. 690 SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL); 691 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 692 693 scoped_ptr<ui::MouseEvent> mev; 694 mev.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_PRESSED, 695 gfx::Point(0, 0))); 696 bool released = false; 697 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released); 698 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 699 EXPECT_EQ(STICKY_KEY_STATE_ENABLED, sticky_key.current_state()); 700 701 mev.reset(GenerateSynthesizedMouseClickEvent(ui::ET_MOUSE_RELEASED, 702 gfx::Point(0, 0))); 703 released = false; 704 mod_down_flags = 0; 705 sticky_key.HandleMouseEvent(*mev.get(), &mod_down_flags, &released); 706 EXPECT_TRUE(mod_down_flags & ui::EF_CONTROL_DOWN); 707 EXPECT_TRUE(released); 708 EXPECT_EQ(STICKY_KEY_STATE_DISABLED, sticky_key.current_state()); 709} 710 711} // namespace ash 712