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