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_, &current_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