event_generator.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright 2014 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 "ui/events/test/event_generator.h" 6 7#include "base/bind.h" 8#include "base/location.h" 9#include "base/memory/scoped_ptr.h" 10#include "base/single_thread_task_runner.h" 11#include "base/thread_task_runner_handle.h" 12#include "base/time/default_tick_clock.h" 13#include "ui/events/event.h" 14#include "ui/events/event_source.h" 15#include "ui/events/event_utils.h" 16#include "ui/events/test/events_test_utils.h" 17#include "ui/gfx/vector2d_conversions.h" 18 19#if defined(USE_X11) 20#include <X11/Xlib.h> 21#include "ui/events/test/events_test_utils_x11.h" 22#endif 23 24#if defined(OS_WIN) 25#include "ui/events/keycodes/keyboard_code_conversion.h" 26#endif 27 28namespace ui { 29namespace test { 30namespace { 31 32void DummyCallback(EventType, const gfx::Vector2dF&) { 33} 34 35class TestKeyEvent : public ui::KeyEvent { 36 public: 37 TestKeyEvent(const base::NativeEvent& native_event, int flags) 38 : KeyEvent(native_event) { 39 set_flags(flags); 40 } 41}; 42 43class TestTouchEvent : public ui::TouchEvent { 44 public: 45 TestTouchEvent(ui::EventType type, 46 const gfx::Point& root_location, 47 int touch_id, 48 int flags, 49 base::TimeDelta timestamp) 50 : TouchEvent(type, root_location, flags, touch_id, timestamp, 51 1.0f, 1.0f, 0.0f, 0.0f) { 52 } 53 54 private: 55 DISALLOW_COPY_AND_ASSIGN(TestTouchEvent); 56}; 57 58const int kAllButtonMask = ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON; 59 60} // namespace 61 62EventGeneratorDelegate* EventGenerator::default_delegate = NULL; 63 64EventGenerator::EventGenerator(gfx::NativeWindow root_window) 65 : current_target_(NULL), 66 flags_(0), 67 grab_(false), 68 async_(false), 69 tick_clock_(new base::DefaultTickClock()) { 70 Init(root_window, NULL); 71} 72 73EventGenerator::EventGenerator(gfx::NativeWindow root_window, 74 const gfx::Point& point) 75 : current_location_(point), 76 current_target_(NULL), 77 flags_(0), 78 grab_(false), 79 async_(false), 80 tick_clock_(new base::DefaultTickClock()) { 81 Init(root_window, NULL); 82} 83 84EventGenerator::EventGenerator(gfx::NativeWindow root_window, 85 gfx::NativeWindow window) 86 : current_target_(NULL), 87 flags_(0), 88 grab_(false), 89 async_(false), 90 tick_clock_(new base::DefaultTickClock()) { 91 Init(root_window, window); 92} 93 94EventGenerator::EventGenerator(EventGeneratorDelegate* delegate) 95 : delegate_(delegate), 96 current_target_(NULL), 97 flags_(0), 98 grab_(false), 99 async_(false), 100 tick_clock_(new base::DefaultTickClock()) { 101 Init(NULL, NULL); 102} 103 104EventGenerator::~EventGenerator() { 105 for (std::list<ui::Event*>::iterator i = pending_events_.begin(); 106 i != pending_events_.end(); ++i) 107 delete *i; 108 pending_events_.clear(); 109 delegate()->SetContext(NULL, NULL, NULL); 110} 111 112void EventGenerator::PressLeftButton() { 113 PressButton(ui::EF_LEFT_MOUSE_BUTTON); 114} 115 116void EventGenerator::ReleaseLeftButton() { 117 ReleaseButton(ui::EF_LEFT_MOUSE_BUTTON); 118} 119 120void EventGenerator::ClickLeftButton() { 121 PressLeftButton(); 122 ReleaseLeftButton(); 123} 124 125void EventGenerator::DoubleClickLeftButton() { 126 flags_ |= ui::EF_IS_DOUBLE_CLICK; 127 PressLeftButton(); 128 flags_ ^= ui::EF_IS_DOUBLE_CLICK; 129 ReleaseLeftButton(); 130} 131 132void EventGenerator::PressRightButton() { 133 PressButton(ui::EF_RIGHT_MOUSE_BUTTON); 134} 135 136void EventGenerator::ReleaseRightButton() { 137 ReleaseButton(ui::EF_RIGHT_MOUSE_BUTTON); 138} 139 140void EventGenerator::MoveMouseWheel(int delta_x, int delta_y) { 141 gfx::Point location = GetLocationInCurrentRoot(); 142 ui::MouseEvent mouseev(ui::ET_MOUSEWHEEL, location, location, flags_, 0); 143 ui::MouseWheelEvent wheelev(mouseev, delta_x, delta_y); 144 Dispatch(&wheelev); 145} 146 147void EventGenerator::SendMouseExit() { 148 gfx::Point exit_location(current_location_); 149 delegate()->ConvertPointToTarget(current_target_, &exit_location); 150 ui::MouseEvent mouseev(ui::ET_MOUSE_EXITED, exit_location, exit_location, 151 flags_, 0); 152 Dispatch(&mouseev); 153} 154 155void EventGenerator::MoveMouseToInHost(const gfx::Point& point_in_host) { 156 const ui::EventType event_type = (flags_ & ui::EF_LEFT_MOUSE_BUTTON) ? 157 ui::ET_MOUSE_DRAGGED : ui::ET_MOUSE_MOVED; 158 ui::MouseEvent mouseev(event_type, point_in_host, point_in_host, flags_, 0); 159 Dispatch(&mouseev); 160 161 current_location_ = point_in_host; 162 delegate()->ConvertPointFromHost(current_target_, ¤t_location_); 163} 164 165void EventGenerator::MoveMouseTo(const gfx::Point& point_in_screen, 166 int count) { 167 DCHECK_GT(count, 0); 168 const ui::EventType event_type = (flags_ & ui::EF_LEFT_MOUSE_BUTTON) ? 169 ui::ET_MOUSE_DRAGGED : ui::ET_MOUSE_MOVED; 170 171 gfx::Vector2dF diff(point_in_screen - current_location_); 172 for (float i = 1; i <= count; i++) { 173 gfx::Vector2dF step(diff); 174 step.Scale(i / count); 175 gfx::Point move_point = current_location_ + gfx::ToRoundedVector2d(step); 176 if (!grab_) 177 UpdateCurrentDispatcher(move_point); 178 delegate()->ConvertPointToTarget(current_target_, &move_point); 179 ui::MouseEvent mouseev(event_type, move_point, move_point, flags_, 0); 180 Dispatch(&mouseev); 181 } 182 current_location_ = point_in_screen; 183} 184 185void EventGenerator::MoveMouseRelativeTo(const EventTarget* window, 186 const gfx::Point& point_in_parent) { 187 gfx::Point point(point_in_parent); 188 delegate()->ConvertPointFromTarget(window, &point); 189 MoveMouseTo(point); 190} 191 192void EventGenerator::DragMouseTo(const gfx::Point& point) { 193 PressLeftButton(); 194 MoveMouseTo(point); 195 ReleaseLeftButton(); 196} 197 198void EventGenerator::MoveMouseToCenterOf(EventTarget* window) { 199 MoveMouseTo(CenterOfWindow(window)); 200} 201 202void EventGenerator::PressTouch() { 203 PressTouchId(0); 204} 205 206void EventGenerator::PressTouchId(int touch_id) { 207 TestTouchEvent touchev( 208 ui::ET_TOUCH_PRESSED, GetLocationInCurrentRoot(), touch_id, flags_, 209 Now()); 210 Dispatch(&touchev); 211} 212 213void EventGenerator::MoveTouch(const gfx::Point& point) { 214 MoveTouchId(point, 0); 215} 216 217void EventGenerator::MoveTouchId(const gfx::Point& point, int touch_id) { 218 current_location_ = point; 219 TestTouchEvent touchev( 220 ui::ET_TOUCH_MOVED, GetLocationInCurrentRoot(), touch_id, flags_, 221 Now()); 222 Dispatch(&touchev); 223 224 if (!grab_) 225 UpdateCurrentDispatcher(point); 226} 227 228void EventGenerator::ReleaseTouch() { 229 ReleaseTouchId(0); 230} 231 232void EventGenerator::ReleaseTouchId(int touch_id) { 233 TestTouchEvent touchev( 234 ui::ET_TOUCH_RELEASED, GetLocationInCurrentRoot(), touch_id, flags_, 235 Now()); 236 Dispatch(&touchev); 237} 238 239void EventGenerator::PressMoveAndReleaseTouchTo(const gfx::Point& point) { 240 PressTouch(); 241 MoveTouch(point); 242 ReleaseTouch(); 243} 244 245void EventGenerator::PressMoveAndReleaseTouchToCenterOf(EventTarget* window) { 246 PressMoveAndReleaseTouchTo(CenterOfWindow(window)); 247} 248 249void EventGenerator::GestureEdgeSwipe() { 250 ui::GestureEvent gesture( 251 0, 0, 0, Now(), ui::GestureEventDetails(ui::ET_GESTURE_WIN8_EDGE_SWIPE)); 252 Dispatch(&gesture); 253} 254 255void EventGenerator::GestureTapAt(const gfx::Point& location) { 256 const int kTouchId = 2; 257 ui::TouchEvent press(ui::ET_TOUCH_PRESSED, 258 location, 259 kTouchId, 260 Now()); 261 Dispatch(&press); 262 263 ui::TouchEvent release( 264 ui::ET_TOUCH_RELEASED, location, kTouchId, 265 press.time_stamp() + base::TimeDelta::FromMilliseconds(50)); 266 Dispatch(&release); 267} 268 269void EventGenerator::GestureTapDownAndUp(const gfx::Point& location) { 270 const int kTouchId = 3; 271 ui::TouchEvent press(ui::ET_TOUCH_PRESSED, 272 location, 273 kTouchId, 274 Now()); 275 Dispatch(&press); 276 277 ui::TouchEvent release( 278 ui::ET_TOUCH_RELEASED, location, kTouchId, 279 press.time_stamp() + base::TimeDelta::FromMilliseconds(1000)); 280 Dispatch(&release); 281} 282 283void EventGenerator::GestureScrollSequence(const gfx::Point& start, 284 const gfx::Point& end, 285 const base::TimeDelta& step_delay, 286 int steps) { 287 GestureScrollSequenceWithCallback(start, end, step_delay, steps, 288 base::Bind(&DummyCallback)); 289} 290 291void EventGenerator::GestureScrollSequenceWithCallback( 292 const gfx::Point& start, 293 const gfx::Point& end, 294 const base::TimeDelta& step_delay, 295 int steps, 296 const ScrollStepCallback& callback) { 297 const int kTouchId = 5; 298 base::TimeDelta timestamp = Now(); 299 ui::TouchEvent press(ui::ET_TOUCH_PRESSED, start, kTouchId, timestamp); 300 Dispatch(&press); 301 302 callback.Run(ui::ET_GESTURE_SCROLL_BEGIN, gfx::Vector2dF()); 303 304 int dx = (end.x() - start.x()) / steps; 305 int dy = (end.y() - start.y()) / steps; 306 gfx::Point location = start; 307 for (int i = 0; i < steps; ++i) { 308 location.Offset(dx, dy); 309 timestamp += step_delay; 310 ui::TouchEvent move(ui::ET_TOUCH_MOVED, location, kTouchId, timestamp); 311 Dispatch(&move); 312 callback.Run(ui::ET_GESTURE_SCROLL_UPDATE, gfx::Vector2dF(dx, dy)); 313 } 314 315 ui::TouchEvent release(ui::ET_TOUCH_RELEASED, end, kTouchId, timestamp); 316 Dispatch(&release); 317 318 callback.Run(ui::ET_GESTURE_SCROLL_END, gfx::Vector2dF()); 319} 320 321void EventGenerator::GestureMultiFingerScroll(int count, 322 const gfx::Point start[], 323 int event_separation_time_ms, 324 int steps, 325 int move_x, 326 int move_y) { 327 const int kMaxTouchPoints = 10; 328 int delays[kMaxTouchPoints] = { 0 }; 329 GestureMultiFingerScrollWithDelays( 330 count, start, delays, event_separation_time_ms, steps, move_x, move_y); 331} 332 333void EventGenerator::GestureMultiFingerScrollWithDelays( 334 int count, 335 const gfx::Point start[], 336 const int delay_adding_finger_ms[], 337 int event_separation_time_ms, 338 int steps, 339 int move_x, 340 int move_y) { 341 const int kMaxTouchPoints = 10; 342 gfx::Point points[kMaxTouchPoints]; 343 CHECK_LE(count, kMaxTouchPoints); 344 CHECK_GT(steps, 0); 345 346 int delta_x = move_x / steps; 347 int delta_y = move_y / steps; 348 349 for (int i = 0; i < count; ++i) { 350 points[i] = start[i]; 351 } 352 353 base::TimeDelta press_time_first = Now(); 354 base::TimeDelta press_time[kMaxTouchPoints]; 355 bool pressed[kMaxTouchPoints]; 356 for (int i = 0; i < count; ++i) { 357 pressed[i] = false; 358 press_time[i] = press_time_first + 359 base::TimeDelta::FromMilliseconds(delay_adding_finger_ms[i]); 360 } 361 362 int last_id = 0; 363 for (int step = 0; step < steps; ++step) { 364 base::TimeDelta move_time = press_time_first + 365 base::TimeDelta::FromMilliseconds(event_separation_time_ms * step); 366 367 while (last_id < count && 368 !pressed[last_id] && 369 move_time >= press_time[last_id]) { 370 ui::TouchEvent press(ui::ET_TOUCH_PRESSED, 371 points[last_id], 372 last_id, 373 press_time[last_id]); 374 Dispatch(&press); 375 pressed[last_id] = true; 376 last_id++; 377 } 378 379 for (int i = 0; i < count; ++i) { 380 points[i].Offset(delta_x, delta_y); 381 if (i >= last_id) 382 continue; 383 ui::TouchEvent move(ui::ET_TOUCH_MOVED, points[i], i, move_time); 384 Dispatch(&move); 385 } 386 } 387 388 base::TimeDelta release_time = press_time_first + 389 base::TimeDelta::FromMilliseconds(event_separation_time_ms * steps); 390 for (int i = 0; i < last_id; ++i) { 391 ui::TouchEvent release( 392 ui::ET_TOUCH_RELEASED, points[i], i, release_time); 393 Dispatch(&release); 394 } 395} 396 397void EventGenerator::ScrollSequence(const gfx::Point& start, 398 const base::TimeDelta& step_delay, 399 float x_offset, 400 float y_offset, 401 int steps, 402 int num_fingers) { 403 base::TimeDelta timestamp = Now(); 404 ui::ScrollEvent fling_cancel(ui::ET_SCROLL_FLING_CANCEL, 405 start, 406 timestamp, 407 0, 408 0, 0, 409 0, 0, 410 num_fingers); 411 Dispatch(&fling_cancel); 412 413 float dx = x_offset / steps; 414 float dy = y_offset / steps; 415 for (int i = 0; i < steps; ++i) { 416 timestamp += step_delay; 417 ui::ScrollEvent move(ui::ET_SCROLL, 418 start, 419 timestamp, 420 0, 421 dx, dy, 422 dx, dy, 423 num_fingers); 424 Dispatch(&move); 425 } 426 427 ui::ScrollEvent fling_start(ui::ET_SCROLL_FLING_START, 428 start, 429 timestamp, 430 0, 431 x_offset, y_offset, 432 x_offset, y_offset, 433 num_fingers); 434 Dispatch(&fling_start); 435} 436 437void EventGenerator::ScrollSequence(const gfx::Point& start, 438 const base::TimeDelta& step_delay, 439 const std::vector<gfx::Point>& offsets, 440 int num_fingers) { 441 size_t steps = offsets.size(); 442 base::TimeDelta timestamp = Now(); 443 ui::ScrollEvent fling_cancel(ui::ET_SCROLL_FLING_CANCEL, 444 start, 445 timestamp, 446 0, 447 0, 0, 448 0, 0, 449 num_fingers); 450 Dispatch(&fling_cancel); 451 452 for (size_t i = 0; i < steps; ++i) { 453 timestamp += step_delay; 454 ui::ScrollEvent scroll(ui::ET_SCROLL, 455 start, 456 timestamp, 457 0, 458 offsets[i].x(), offsets[i].y(), 459 offsets[i].x(), offsets[i].y(), 460 num_fingers); 461 Dispatch(&scroll); 462 } 463 464 ui::ScrollEvent fling_start(ui::ET_SCROLL_FLING_START, 465 start, 466 timestamp, 467 0, 468 offsets[steps - 1].x(), offsets[steps - 1].y(), 469 offsets[steps - 1].x(), offsets[steps - 1].y(), 470 num_fingers); 471 Dispatch(&fling_start); 472} 473 474void EventGenerator::PressKey(ui::KeyboardCode key_code, int flags) { 475 DispatchKeyEvent(true, key_code, flags); 476} 477 478void EventGenerator::ReleaseKey(ui::KeyboardCode key_code, int flags) { 479 DispatchKeyEvent(false, key_code, flags); 480} 481 482void EventGenerator::Dispatch(ui::Event* event) { 483 DoDispatchEvent(event, async_); 484} 485 486void EventGenerator::SetTickClock(scoped_ptr<base::TickClock> tick_clock) { 487 tick_clock_ = tick_clock.Pass(); 488} 489 490base::TimeDelta EventGenerator::Now() { 491 // This is the same as what EventTimeForNow() does, but here we do it 492 // with a tick clock that can be replaced with a simulated clock for tests. 493 return base::TimeDelta::FromInternalValue( 494 tick_clock_->NowTicks().ToInternalValue()); 495} 496 497void EventGenerator::Init(gfx::NativeWindow root_window, 498 gfx::NativeWindow window_context) { 499 delegate()->SetContext(this, root_window, window_context); 500 if (window_context) 501 current_location_ = delegate()->CenterOfWindow(window_context); 502 current_target_ = delegate()->GetTargetAt(current_location_); 503} 504 505void EventGenerator::DispatchKeyEvent(bool is_press, 506 ui::KeyboardCode key_code, 507 int flags) { 508#if defined(OS_WIN) 509 UINT key_press = WM_KEYDOWN; 510 uint16 character = ui::GetCharacterFromKeyCode(key_code, flags); 511 if (is_press && character) { 512 MSG native_event = { NULL, WM_KEYDOWN, key_code, 0 }; 513 TestKeyEvent keyev(native_event, flags); 514 Dispatch(&keyev); 515 // On Windows, WM_KEYDOWN event is followed by WM_CHAR with a character 516 // if the key event cooresponds to a real character. 517 key_press = WM_CHAR; 518 key_code = static_cast<ui::KeyboardCode>(character); 519 } 520 MSG native_event = 521 { NULL, (is_press ? key_press : WM_KEYUP), key_code, 0 }; 522 TestKeyEvent keyev(native_event, flags); 523#elif defined(USE_X11) 524 ui::ScopedXI2Event xevent; 525 xevent.InitKeyEvent(is_press ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED, 526 key_code, 527 flags); 528 ui::KeyEvent keyev(xevent); 529#else 530 ui::EventType type = is_press ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED; 531 ui::KeyEvent keyev(type, key_code, flags); 532#endif // OS_WIN 533 Dispatch(&keyev); 534} 535 536void EventGenerator::UpdateCurrentDispatcher(const gfx::Point& point) { 537 current_target_ = delegate()->GetTargetAt(point); 538} 539 540void EventGenerator::PressButton(int flag) { 541 if (!(flags_ & flag)) { 542 flags_ |= flag; 543 grab_ = (flags_ & kAllButtonMask) != 0; 544 gfx::Point location = GetLocationInCurrentRoot(); 545 ui::MouseEvent mouseev(ui::ET_MOUSE_PRESSED, location, location, flags_, 546 flag); 547 Dispatch(&mouseev); 548 } 549} 550 551void EventGenerator::ReleaseButton(int flag) { 552 if (flags_ & flag) { 553 gfx::Point location = GetLocationInCurrentRoot(); 554 ui::MouseEvent mouseev(ui::ET_MOUSE_RELEASED, location, 555 location, flags_, flag); 556 Dispatch(&mouseev); 557 flags_ ^= flag; 558 } 559 grab_ = (flags_ & kAllButtonMask) != 0; 560} 561 562gfx::Point EventGenerator::GetLocationInCurrentRoot() const { 563 gfx::Point p(current_location_); 564 delegate()->ConvertPointToTarget(current_target_, &p); 565 return p; 566} 567 568gfx::Point EventGenerator::CenterOfWindow(const EventTarget* window) const { 569 return delegate()->CenterOfTarget(window); 570} 571 572void EventGenerator::DoDispatchEvent(ui::Event* event, bool async) { 573 if (async) { 574 ui::Event* pending_event; 575 if (event->IsKeyEvent()) { 576 pending_event = new ui::KeyEvent(*static_cast<ui::KeyEvent*>(event)); 577 } else if (event->IsMouseEvent()) { 578 pending_event = new ui::MouseEvent(*static_cast<ui::MouseEvent*>(event)); 579 } else if (event->IsTouchEvent()) { 580 pending_event = new ui::TouchEvent(*static_cast<ui::TouchEvent*>(event)); 581 } else if (event->IsScrollEvent()) { 582 pending_event = 583 new ui::ScrollEvent(*static_cast<ui::ScrollEvent*>(event)); 584 } else { 585 NOTREACHED() << "Invalid event type"; 586 return; 587 } 588 if (pending_events_.empty()) { 589 base::ThreadTaskRunnerHandle::Get()->PostTask( 590 FROM_HERE, 591 base::Bind(&EventGenerator::DispatchNextPendingEvent, 592 base::Unretained(this))); 593 } 594 pending_events_.push_back(pending_event); 595 } else { 596 ui::EventSource* event_source = delegate()->GetEventSource(current_target_); 597 ui::EventSourceTestApi event_source_test(event_source); 598 ui::EventDispatchDetails details = 599 event_source_test.SendEventToProcessor(event); 600 CHECK(!details.dispatcher_destroyed); 601 } 602} 603 604void EventGenerator::DispatchNextPendingEvent() { 605 DCHECK(!pending_events_.empty()); 606 ui::Event* event = pending_events_.front(); 607 DoDispatchEvent(event, false); 608 pending_events_.pop_front(); 609 delete event; 610 if (!pending_events_.empty()) { 611 base::ThreadTaskRunnerHandle::Get()->PostTask( 612 FROM_HERE, 613 base::Bind(&EventGenerator::DispatchNextPendingEvent, 614 base::Unretained(this))); 615 } 616} 617 618const EventGeneratorDelegate* EventGenerator::delegate() const { 619 if (delegate_) 620 return delegate_.get(); 621 622 DCHECK(default_delegate); 623 return default_delegate; 624} 625 626EventGeneratorDelegate* EventGenerator::delegate() { 627 return const_cast<EventGeneratorDelegate*>( 628 const_cast<const EventGenerator*>(this)->delegate()); 629} 630 631} // namespace test 632} // namespace ui 633