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 <stddef.h>
8 #include <stdint.h>
9 
10 #include <memory>
11 #include <utility>
12 
13 #include "base/bind.h"
14 #include "base/location.h"
15 #include "base/macros.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/threading/thread_task_runner_handle.h"
18 #include "base/time/tick_clock.h"
19 #include "build/build_config.h"
20 #include "ui/events/event.h"
21 #include "ui/events/event_source.h"
22 #include "ui/events/event_utils.h"
23 #include "ui/events/test/events_test_utils.h"
24 #include "ui/gfx/geometry/vector2d_conversions.h"
25 
26 #if defined(USE_X11)
27 #include "ui/events/test/events_test_utils_x11.h"
28 #include "ui/events/x/events_x_utils.h"
29 #endif
30 
31 #if defined(OS_WIN)
32 #include "ui/events/keycodes/keyboard_code_conversion.h"
33 #endif
34 
35 namespace ui {
36 namespace test {
37 
38 // A TickClock that advances time by one millisecond on each call to NowTicks().
39 class TestTickClock : public base::TickClock {
40  public:
41   TestTickClock() = default;
42 
43   // Unconditionally returns a tick count that is 1ms later than the previous
44   // call, starting at 1ms.
NowTicks() const45   base::TimeTicks NowTicks() const override {
46     static constexpr base::TimeDelta kOneMillisecond =
47         base::TimeDelta::FromMilliseconds(1);
48     return ticks_ += kOneMillisecond;
49   }
50 
51  private:
52   mutable base::TimeTicks ticks_;
53 
54   DISALLOW_COPY_AND_ASSIGN(TestTickClock);
55 };
56 
57 namespace {
58 
DummyCallback(EventType,const gfx::Vector2dF &)59 void DummyCallback(EventType, const gfx::Vector2dF&) {}
60 
61 class TestTouchEvent : public ui::TouchEvent {
62  public:
TestTouchEvent(ui::EventType type,const gfx::Point & root_location,int touch_id,int flags,base::TimeTicks timestamp)63   TestTouchEvent(ui::EventType type,
64                  const gfx::Point& root_location,
65                  int touch_id,
66                  int flags,
67                  base::TimeTicks timestamp)
68       : TouchEvent(type,
69                    root_location,
70                    timestamp,
71                    ui::PointerDetails(ui::EventPointerType::kTouch,
72                                       /* pointer_id*/ touch_id,
73                                       /* radius_x */ 1.0f,
74                                       /* radius_y */ 1.0f,
75                                       /* force */ 0.0f),
76                    flags) {}
77 
78  private:
79   DISALLOW_COPY_AND_ASSIGN(TestTouchEvent);
80 };
81 
82 const int kAllButtonMask = ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON;
83 
84 EventGeneratorDelegate::FactoryFunction g_event_generator_delegate_factory;
85 
86 }  // namespace
87 
88 // static
SetFactoryFunction(FactoryFunction factory)89 void EventGeneratorDelegate::SetFactoryFunction(FactoryFunction factory) {
90   g_event_generator_delegate_factory = std::move(factory);
91 }
92 
EventGenerator(std::unique_ptr<EventGeneratorDelegate> delegate)93 EventGenerator::EventGenerator(std::unique_ptr<EventGeneratorDelegate> delegate)
94     : delegate_(std::move(delegate)) {
95   Init(nullptr, nullptr);
96 }
97 
EventGenerator(gfx::NativeWindow root_window)98 EventGenerator::EventGenerator(gfx::NativeWindow root_window) {
99   Init(root_window, nullptr);
100 }
101 
EventGenerator(gfx::NativeWindow root_window,const gfx::Point & point)102 EventGenerator::EventGenerator(gfx::NativeWindow root_window,
103                                const gfx::Point& point)
104     : current_screen_location_(point) {
105   Init(root_window, nullptr);
106 }
107 
EventGenerator(gfx::NativeWindow root_window,gfx::NativeWindow target_window)108 EventGenerator::EventGenerator(gfx::NativeWindow root_window,
109                                gfx::NativeWindow target_window) {
110   Init(root_window, target_window);
111 }
112 
~EventGenerator()113 EventGenerator::~EventGenerator() {
114   ui::SetEventTickClockForTesting(nullptr);
115 }
116 
SetTargetWindow(gfx::NativeWindow target_window)117 void EventGenerator::SetTargetWindow(gfx::NativeWindow target_window) {
118   delegate()->SetTargetWindow(target_window);
119   current_screen_location_ = delegate()->CenterOfWindow(target_window);
120   UpdateCurrentDispatcher(current_screen_location_);
121 }
122 
PressLeftButton()123 void EventGenerator::PressLeftButton() {
124   PressButton(ui::EF_LEFT_MOUSE_BUTTON);
125 }
126 
ReleaseLeftButton()127 void EventGenerator::ReleaseLeftButton() {
128   ReleaseButton(ui::EF_LEFT_MOUSE_BUTTON);
129 }
130 
ClickLeftButton()131 void EventGenerator::ClickLeftButton() {
132   PressLeftButton();
133   ReleaseLeftButton();
134 }
135 
ClickRightButton()136 void EventGenerator::ClickRightButton() {
137   PressRightButton();
138   ReleaseRightButton();
139 }
140 
DoubleClickLeftButton()141 void EventGenerator::DoubleClickLeftButton() {
142   flags_ &= ~ui::EF_IS_DOUBLE_CLICK;
143   ClickLeftButton();
144   flags_ |= ui::EF_IS_DOUBLE_CLICK;
145   ClickLeftButton();
146   flags_ &= ~ui::EF_IS_DOUBLE_CLICK;
147 }
148 
PressRightButton()149 void EventGenerator::PressRightButton() {
150   PressButton(ui::EF_RIGHT_MOUSE_BUTTON);
151 }
152 
ReleaseRightButton()153 void EventGenerator::ReleaseRightButton() {
154   ReleaseButton(ui::EF_RIGHT_MOUSE_BUTTON);
155 }
156 
MoveMouseWheel(int delta_x,int delta_y)157 void EventGenerator::MoveMouseWheel(int delta_x, int delta_y) {
158   gfx::Point location = GetLocationInCurrentRoot();
159   ui::MouseWheelEvent wheelev(gfx::Vector2d(delta_x, delta_y), location,
160                               location, ui::EventTimeForNow(), flags_, 0);
161   Dispatch(&wheelev);
162 }
163 
SendMouseEnter()164 void EventGenerator::SendMouseEnter() {
165   gfx::Point enter_location(current_screen_location_);
166   delegate()->ConvertPointToTarget(current_target_, &enter_location);
167   ui::MouseEvent mouseev(ui::ET_MOUSE_ENTERED, enter_location, enter_location,
168                          ui::EventTimeForNow(), flags_, 0);
169   Dispatch(&mouseev);
170 }
171 
SendMouseExit()172 void EventGenerator::SendMouseExit() {
173   gfx::Point exit_location(current_screen_location_);
174   delegate()->ConvertPointToTarget(current_target_, &exit_location);
175   ui::MouseEvent mouseev(ui::ET_MOUSE_EXITED, exit_location, exit_location,
176                          ui::EventTimeForNow(), flags_, 0);
177   Dispatch(&mouseev);
178 }
179 
180 #if defined(OS_CHROMEOS)
MoveMouseToWithNative(const gfx::Point & point_in_host,const gfx::Point & point_for_native)181 void EventGenerator::MoveMouseToWithNative(const gfx::Point& point_in_host,
182                                            const gfx::Point& point_for_native) {
183   // Ozone uses the location in native event as a system location.
184   // Create a fake event with the point in host, which will be passed
185   // to the non native event, then update the native event with the native
186   // (root) one.
187   std::unique_ptr<ui::MouseEvent> native_event(
188       new ui::MouseEvent(ui::ET_MOUSE_MOVED, point_in_host, point_in_host,
189                          ui::EventTimeForNow(), flags_, 0));
190   ui::MouseEvent mouseev(native_event.get());
191   native_event->set_location(point_for_native);
192   Dispatch(&mouseev);
193 
194   current_screen_location_ = point_in_host;
195   delegate()->ConvertPointFromHost(current_target_, &current_screen_location_);
196 }
197 #endif
198 
MoveMouseToInHost(const gfx::Point & point_in_host)199 void EventGenerator::MoveMouseToInHost(const gfx::Point& point_in_host) {
200   const ui::EventType event_type = (flags_ & ui::EF_LEFT_MOUSE_BUTTON) ?
201       ui::ET_MOUSE_DRAGGED : ui::ET_MOUSE_MOVED;
202   ui::MouseEvent mouseev(event_type, point_in_host, point_in_host,
203                          ui::EventTimeForNow(), flags_, 0);
204   Dispatch(&mouseev);
205 
206   current_screen_location_ = point_in_host;
207   delegate()->ConvertPointFromHost(current_target_, &current_screen_location_);
208 }
209 
MoveMouseTo(const gfx::Point & point_in_screen,int count)210 void EventGenerator::MoveMouseTo(const gfx::Point& point_in_screen,
211                                  int count) {
212   DCHECK_GT(count, 0);
213   const ui::EventType event_type = (flags_ & ui::EF_LEFT_MOUSE_BUTTON) ?
214       ui::ET_MOUSE_DRAGGED : ui::ET_MOUSE_MOVED;
215 
216   gfx::Vector2dF diff(point_in_screen - current_screen_location_);
217   for (float i = 1; i <= count; i++) {
218     gfx::Vector2dF step(diff);
219     step.Scale(i / count);
220     gfx::Point move_point =
221         current_screen_location_ + gfx::ToRoundedVector2d(step);
222     if (!grab_)
223       UpdateCurrentDispatcher(move_point);
224     delegate()->ConvertPointToTarget(current_target_, &move_point);
225     ui::MouseEvent mouseev(event_type, move_point, move_point,
226                            ui::EventTimeForNow(), flags_, 0);
227     Dispatch(&mouseev);
228   }
229   current_screen_location_ = point_in_screen;
230 }
231 
MoveMouseRelativeTo(const EventTarget * window,const gfx::Point & point_in_parent)232 void EventGenerator::MoveMouseRelativeTo(const EventTarget* window,
233                                          const gfx::Point& point_in_parent) {
234   gfx::Point point(point_in_parent);
235   delegate()->ConvertPointFromTarget(window, &point);
236   MoveMouseTo(point);
237 }
238 
DragMouseTo(const gfx::Point & point)239 void EventGenerator::DragMouseTo(const gfx::Point& point) {
240   PressLeftButton();
241   MoveMouseTo(point);
242   ReleaseLeftButton();
243 }
244 
MoveMouseToCenterOf(EventTarget * window)245 void EventGenerator::MoveMouseToCenterOf(EventTarget* window) {
246   MoveMouseTo(CenterOfWindow(window));
247 }
248 
EnterPenPointerMode()249 void EventGenerator::EnterPenPointerMode() {
250   touch_pointer_details_.pointer_type = ui::EventPointerType::kPen;
251 }
252 
ExitPenPointerMode()253 void EventGenerator::ExitPenPointerMode() {
254   touch_pointer_details_.pointer_type = ui::EventPointerType::kTouch;
255 }
256 
SetTouchRadius(float x,float y)257 void EventGenerator::SetTouchRadius(float x, float y) {
258   touch_pointer_details_.radius_x = x;
259   touch_pointer_details_.radius_y = y;
260 }
261 
SetTouchTilt(float x,float y)262 void EventGenerator::SetTouchTilt(float x, float y) {
263   touch_pointer_details_.tilt_x = x;
264   touch_pointer_details_.tilt_y = y;
265 }
266 
PressTouch(const base::Optional<gfx::Point> & touch_location_in_screen)267 void EventGenerator::PressTouch(
268     const base::Optional<gfx::Point>& touch_location_in_screen) {
269   PressTouchId(0, touch_location_in_screen);
270 }
271 
PressTouchId(int touch_id,const base::Optional<gfx::Point> & touch_location_in_screen)272 void EventGenerator::PressTouchId(
273     int touch_id,
274     const base::Optional<gfx::Point>& touch_location_in_screen) {
275   if (touch_location_in_screen.has_value())
276     current_screen_location_ = *touch_location_in_screen;
277   TestTouchEvent touchev(ui::ET_TOUCH_PRESSED, GetLocationInCurrentRoot(),
278                          touch_id, flags_, ui::EventTimeForNow());
279   Dispatch(&touchev);
280 }
281 
MoveTouch(const gfx::Point & point)282 void EventGenerator::MoveTouch(const gfx::Point& point) {
283   MoveTouchId(point, 0);
284 }
285 
MoveTouchId(const gfx::Point & point,int touch_id)286 void EventGenerator::MoveTouchId(const gfx::Point& point, int touch_id) {
287   current_screen_location_ = point;
288   TestTouchEvent touchev(ui::ET_TOUCH_MOVED, GetLocationInCurrentRoot(),
289                          touch_id, flags_, ui::EventTimeForNow());
290   Dispatch(&touchev);
291 
292   if (!grab_)
293     UpdateCurrentDispatcher(point);
294 }
295 
ReleaseTouch()296 void EventGenerator::ReleaseTouch() {
297   ReleaseTouchId(0);
298 }
299 
ReleaseTouchId(int touch_id)300 void EventGenerator::ReleaseTouchId(int touch_id) {
301   TestTouchEvent touchev(ui::ET_TOUCH_RELEASED, GetLocationInCurrentRoot(),
302                          touch_id, flags_, ui::EventTimeForNow());
303   Dispatch(&touchev);
304 }
305 
PressMoveAndReleaseTouchTo(const gfx::Point & point)306 void EventGenerator::PressMoveAndReleaseTouchTo(const gfx::Point& point) {
307   PressTouch();
308   MoveTouch(point);
309   ReleaseTouch();
310 }
311 
PressMoveAndReleaseTouchToCenterOf(EventTarget * window)312 void EventGenerator::PressMoveAndReleaseTouchToCenterOf(EventTarget* window) {
313   PressMoveAndReleaseTouchTo(CenterOfWindow(window));
314 }
315 
GestureTapAt(const gfx::Point & location)316 void EventGenerator::GestureTapAt(const gfx::Point& location) {
317   UpdateCurrentDispatcher(location);
318   gfx::Point converted_location = location;
319   delegate()->ConvertPointToTarget(current_target_, &converted_location);
320 
321   const int kTouchId = 2;
322   ui::TouchEvent press(
323       ui::ET_TOUCH_PRESSED, converted_location, ui::EventTimeForNow(),
324       ui::PointerDetails(ui::EventPointerType::kTouch, kTouchId));
325   Dispatch(&press);
326 
327   ui::TouchEvent release(
328       ui::ET_TOUCH_RELEASED, converted_location,
329       press.time_stamp() + base::TimeDelta::FromMilliseconds(50),
330       ui::PointerDetails(ui::EventPointerType::kTouch, kTouchId));
331   Dispatch(&release);
332 }
333 
GestureTapDownAndUp(const gfx::Point & location)334 void EventGenerator::GestureTapDownAndUp(const gfx::Point& location) {
335   UpdateCurrentDispatcher(location);
336   gfx::Point converted_location = location;
337   delegate()->ConvertPointToTarget(current_target_, &converted_location);
338 
339   const int kTouchId = 3;
340   ui::TouchEvent press(
341       ui::ET_TOUCH_PRESSED, converted_location, ui::EventTimeForNow(),
342       ui::PointerDetails(ui::EventPointerType::kTouch, kTouchId));
343   Dispatch(&press);
344 
345   ui::TouchEvent release(
346       ui::ET_TOUCH_RELEASED, converted_location,
347       press.time_stamp() + base::TimeDelta::FromMilliseconds(1000),
348       ui::PointerDetails(ui::EventPointerType::kTouch, kTouchId));
349   Dispatch(&release);
350 }
351 
CalculateScrollDurationForFlingVelocity(const gfx::Point & start,const gfx::Point & end,float velocity,int steps)352 base::TimeDelta EventGenerator::CalculateScrollDurationForFlingVelocity(
353     const gfx::Point& start,
354     const gfx::Point& end,
355     float velocity,
356     int steps) {
357   const float kGestureDistance = (start - end).Length();
358   const float kFlingStepDelay = (kGestureDistance / velocity) / steps * 1000000;
359   return base::TimeDelta::FromMicroseconds(kFlingStepDelay);
360 }
361 
GestureScrollSequence(const gfx::Point & start,const gfx::Point & end,const base::TimeDelta & step_delay,int steps)362 void EventGenerator::GestureScrollSequence(const gfx::Point& start,
363                                            const gfx::Point& end,
364                                            const base::TimeDelta& step_delay,
365                                            int steps) {
366   GestureScrollSequenceWithCallback(start, end, step_delay, steps,
367                                     base::BindRepeating(&DummyCallback));
368 }
369 
GestureScrollSequenceWithCallback(const gfx::Point & start,const gfx::Point & end,const base::TimeDelta & step_delay,int steps,const ScrollStepCallback & callback)370 void EventGenerator::GestureScrollSequenceWithCallback(
371     const gfx::Point& start,
372     const gfx::Point& end,
373     const base::TimeDelta& step_delay,
374     int steps,
375     const ScrollStepCallback& callback) {
376   UpdateCurrentDispatcher(start);
377   const int kTouchId = 5;
378   base::TimeTicks timestamp = ui::EventTimeForNow();
379   ui::TouchEvent press(ui::ET_TOUCH_PRESSED, start, timestamp,
380                        PointerDetails(ui::EventPointerType::kTouch,
381                                       /* pointer_id*/ kTouchId,
382                                       /* radius_x */ 5.0f,
383                                       /* radius_y */ 5.0f,
384                                       /* force */ 1.0f));
385   Dispatch(&press);
386 
387   callback.Run(ui::ET_GESTURE_SCROLL_BEGIN, gfx::Vector2dF());
388 
389   float dx = static_cast<float>(end.x() - start.x()) / steps;
390   float dy = static_cast<float>(end.y() - start.y()) / steps;
391   gfx::PointF location(start);
392   for (int i = 0; i < steps; ++i) {
393     location.Offset(dx, dy);
394     timestamp += step_delay;
395     ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(), timestamp,
396                         PointerDetails(ui::EventPointerType::kTouch,
397                                        /* pointer_id*/ kTouchId,
398                                        /* radius_x */ 5.0f,
399                                        /* radius_y */ 5.0f,
400                                        /* force */ 1.0f));
401     move.set_location_f(location);
402     move.set_root_location_f(location);
403     Dispatch(&move);
404     callback.Run(ui::ET_GESTURE_SCROLL_UPDATE, gfx::Vector2dF(dx, dy));
405   }
406 
407   ui::TouchEvent release(ui::ET_TOUCH_RELEASED, end, timestamp,
408                          PointerDetails(ui::EventPointerType::kTouch,
409                                         /* pointer_id*/ kTouchId,
410                                         /* radius_x */ 5.0f,
411                                         /* radius_y */ 5.0f,
412                                         /* force */ 1.0f));
413   Dispatch(&release);
414 
415   callback.Run(ui::ET_GESTURE_SCROLL_END, gfx::Vector2dF());
416 }
417 
GestureMultiFingerScrollWithDelays(int count,const gfx::Point start[],const gfx::Vector2d delta[],const int delay_adding_finger_ms[],const int delay_releasing_finger_ms[],int event_separation_time_ms,int steps)418 void EventGenerator::GestureMultiFingerScrollWithDelays(
419     int count,
420     const gfx::Point start[],
421     const gfx::Vector2d delta[],
422     const int delay_adding_finger_ms[],
423     const int delay_releasing_finger_ms[],
424     int event_separation_time_ms,
425     int steps) {
426   const int kMaxTouchPoints = 10;
427   CHECK_LE(count, kMaxTouchPoints);
428   CHECK_GT(steps, 0);
429 
430   gfx::Point points[kMaxTouchPoints];
431   gfx::Vector2d delta_per_step[kMaxTouchPoints];
432   for (int i = 0; i < count; ++i) {
433     points[i] = start[i];
434     delta_per_step[i].set_x(delta[i].x() / steps);
435     delta_per_step[i].set_y(delta[i].y() / steps);
436   }
437 
438   base::TimeTicks press_time_first = ui::EventTimeForNow();
439   base::TimeTicks press_time[kMaxTouchPoints];
440   base::TimeTicks release_time[kMaxTouchPoints];
441   bool pressed[kMaxTouchPoints];
442   for (int i = 0; i < count; ++i) {
443     pressed[i] = false;
444     press_time[i] = press_time_first +
445         base::TimeDelta::FromMilliseconds(delay_adding_finger_ms[i]);
446     release_time[i] = press_time_first + base::TimeDelta::FromMilliseconds(
447                                              delay_releasing_finger_ms[i]);
448     DCHECK_LE(press_time[i], release_time[i]);
449   }
450 
451   for (int step = 0; step < steps; ++step) {
452     base::TimeTicks move_time =
453         press_time_first +
454         base::TimeDelta::FromMilliseconds(event_separation_time_ms * step);
455 
456     for (int i = 0; i < count; ++i) {
457       if (!pressed[i] && move_time >= press_time[i]) {
458         ui::TouchEvent press(
459             ui::ET_TOUCH_PRESSED, points[i], press_time[i],
460             ui::PointerDetails(ui::EventPointerType::kTouch, i));
461         Dispatch(&press);
462         pressed[i] = true;
463       }
464     }
465 
466     // All touch release events should occur at the end if
467     // |event_separation_time_ms| is 0.
468     for (int i = 0; i < count && event_separation_time_ms > 0; ++i) {
469       if (pressed[i] && move_time >= release_time[i]) {
470         ui::TouchEvent release(
471             ui::ET_TOUCH_RELEASED, points[i], release_time[i],
472             ui::PointerDetails(ui::EventPointerType::kTouch, i));
473         Dispatch(&release);
474         pressed[i] = false;
475       }
476     }
477 
478     for (int i = 0; i < count; ++i) {
479       points[i] += delta_per_step[i];
480       if (pressed[i]) {
481         ui::TouchEvent move(
482             ui::ET_TOUCH_MOVED, points[i], move_time,
483             ui::PointerDetails(ui::EventPointerType::kTouch, i));
484         Dispatch(&move);
485       }
486     }
487   }
488 
489   base::TimeTicks default_release_time =
490       press_time_first +
491       base::TimeDelta::FromMilliseconds(event_separation_time_ms * steps);
492   // Ensures that all pressed fingers are released in the end.
493   for (int i = 0; i < count; ++i) {
494     if (pressed[i]) {
495       ui::TouchEvent release(
496           ui::ET_TOUCH_RELEASED, points[i], default_release_time,
497           ui::PointerDetails(ui::EventPointerType::kTouch, i));
498       Dispatch(&release);
499       pressed[i] = false;
500     }
501   }
502 }
503 
GestureMultiFingerScrollWithDelays(int count,const gfx::Point start[],const int delay_adding_finger_ms[],int event_separation_time_ms,int steps,int move_x,int move_y)504 void EventGenerator::GestureMultiFingerScrollWithDelays(
505     int count,
506     const gfx::Point start[],
507     const int delay_adding_finger_ms[],
508     int event_separation_time_ms,
509     int steps,
510     int move_x,
511     int move_y) {
512   const int kMaxTouchPoints = 10;
513   int delay_releasing_finger_ms[kMaxTouchPoints];
514   gfx::Vector2d delta[kMaxTouchPoints];
515   for (int i = 0; i < kMaxTouchPoints; ++i) {
516     delay_releasing_finger_ms[i] = event_separation_time_ms * steps;
517     delta[i].set_x(move_x);
518     delta[i].set_y(move_y);
519   }
520   GestureMultiFingerScrollWithDelays(
521       count, start, delta, delay_adding_finger_ms, delay_releasing_finger_ms,
522       event_separation_time_ms, steps);
523 }
524 
GestureMultiFingerScroll(int count,const gfx::Point start[],int event_separation_time_ms,int steps,int move_x,int move_y)525 void EventGenerator::GestureMultiFingerScroll(int count,
526                                               const gfx::Point start[],
527                                               int event_separation_time_ms,
528                                               int steps,
529                                               int move_x,
530                                               int move_y) {
531   const int kMaxTouchPoints = 10;
532   int delays[kMaxTouchPoints] = {0};
533   GestureMultiFingerScrollWithDelays(
534       count, start, delays, event_separation_time_ms, steps, move_x, move_y);
535 }
536 
ScrollSequence(const gfx::Point & start,const base::TimeDelta & step_delay,float x_offset,float y_offset,int steps,int num_fingers)537 void EventGenerator::ScrollSequence(const gfx::Point& start,
538                                     const base::TimeDelta& step_delay,
539                                     float x_offset,
540                                     float y_offset,
541                                     int steps,
542                                     int num_fingers) {
543   UpdateCurrentDispatcher(start);
544 
545   base::TimeTicks timestamp = ui::EventTimeForNow();
546   ui::ScrollEvent fling_cancel(ui::ET_SCROLL_FLING_CANCEL,
547                                start,
548                                timestamp,
549                                0,
550                                0, 0,
551                                0, 0,
552                                num_fingers);
553   Dispatch(&fling_cancel);
554 
555   float dx = x_offset / steps;
556   float dy = y_offset / steps;
557   for (int i = 0; i < steps; ++i) {
558     timestamp += step_delay;
559     ui::ScrollEvent move(ui::ET_SCROLL,
560                          start,
561                          timestamp,
562                          0,
563                          dx, dy,
564                          dx, dy,
565                          num_fingers);
566     Dispatch(&move);
567   }
568 
569   ui::ScrollEvent fling_start(ui::ET_SCROLL_FLING_START,
570                               start,
571                               timestamp,
572                               0,
573                               x_offset, y_offset,
574                               x_offset, y_offset,
575                               num_fingers);
576   Dispatch(&fling_start);
577 }
578 
GenerateTrackpadRest()579 void EventGenerator::GenerateTrackpadRest() {
580   int num_fingers = 2;
581   ui::ScrollEvent scroll(ui::ET_SCROLL, current_screen_location_,
582                          ui::EventTimeForNow(), 0, 0, 0, 0, 0, num_fingers,
583                          EventMomentumPhase::MAY_BEGIN);
584   Dispatch(&scroll);
585 }
586 
CancelTrackpadRest()587 void EventGenerator::CancelTrackpadRest() {
588   int num_fingers = 2;
589   ui::ScrollEvent scroll(ui::ET_SCROLL, current_screen_location_,
590                          ui::EventTimeForNow(), 0, 0, 0, 0, 0, num_fingers,
591                          EventMomentumPhase::END);
592   Dispatch(&scroll);
593 }
594 
PressKey(ui::KeyboardCode key_code,int flags,int source_device_id)595 void EventGenerator::PressKey(ui::KeyboardCode key_code,
596                               int flags,
597                               int source_device_id) {
598   DispatchKeyEvent(true, key_code, flags, source_device_id);
599 }
600 
ReleaseKey(ui::KeyboardCode key_code,int flags,int source_device_id)601 void EventGenerator::ReleaseKey(ui::KeyboardCode key_code,
602                                 int flags,
603                                 int source_device_id) {
604   DispatchKeyEvent(false, key_code, flags, source_device_id);
605 }
606 
Dispatch(ui::Event * event)607 void EventGenerator::Dispatch(ui::Event* event) {
608   if (event->IsTouchEvent()) {
609     ui::TouchEvent* touch_event = static_cast<ui::TouchEvent*>(event);
610     touch_pointer_details_.id = touch_event->pointer_details().id;
611     touch_event->SetPointerDetailsForTest(touch_pointer_details_);
612   }
613 
614   if (!event->handled()) {
615     ui::EventSource* event_source = delegate()->GetEventSource(current_target_);
616     ui::EventSourceTestApi event_source_test(event_source);
617     ui::EventDispatchDetails details = event_source_test.SendEventToSink(event);
618     if (details.dispatcher_destroyed)
619       current_target_ = nullptr;
620   }
621 }
622 
Init(gfx::NativeWindow root_window,gfx::NativeWindow target_window)623 void EventGenerator::Init(gfx::NativeWindow root_window,
624                           gfx::NativeWindow target_window) {
625   tick_clock_ = std::make_unique<TestTickClock>();
626   ui::SetEventTickClockForTesting(tick_clock_.get());
627   if (!delegate_) {
628     DCHECK(g_event_generator_delegate_factory);
629     delegate_ = g_event_generator_delegate_factory.Run(this, root_window,
630                                                        target_window);
631   }
632   if (target_window)
633     current_screen_location_ = delegate()->CenterOfWindow(target_window);
634   else if (root_window)
635     delegate()->ConvertPointFromWindow(root_window, &current_screen_location_);
636   current_target_ = delegate()->GetTargetAt(current_screen_location_);
637   touch_pointer_details_ = PointerDetails(ui::EventPointerType::kTouch,
638                                           /* pointer_id*/ 0,
639                                           /* radius_x */ 1.0f,
640                                           /* radius_y */ 1.0f,
641                                           /* force */ 0.0f);
642 }
643 
DispatchKeyEvent(bool is_press,ui::KeyboardCode key_code,int flags,int source_device_id)644 void EventGenerator::DispatchKeyEvent(bool is_press,
645                                       ui::KeyboardCode key_code,
646                                       int flags,
647                                       int source_device_id) {
648 #if defined(OS_WIN)
649   UINT key_press = WM_KEYDOWN;
650   uint16_t character = ui::DomCodeToUsLayoutCharacter(
651       ui::UsLayoutKeyboardCodeToDomCode(key_code), flags);
652   if (is_press && character) {
653     MSG native_event = { NULL, WM_KEYDOWN, key_code, 0 };
654     native_event.time =
655         (ui::EventTimeForNow() - base::TimeTicks()).InMilliseconds() &
656         UINT32_MAX;
657     ui::KeyEvent keyev(native_event, flags);
658     Dispatch(&keyev);
659     // On Windows, WM_KEYDOWN event is followed by WM_CHAR with a character
660     // if the key event corresponds to a real character.
661     key_press = WM_CHAR;
662     key_code = static_cast<ui::KeyboardCode>(character);
663   }
664   MSG native_event =
665       { NULL, (is_press ? key_press : WM_KEYUP), key_code, 0 };
666   native_event.time =
667       (ui::EventTimeForNow() - base::TimeTicks()).InMilliseconds() & UINT32_MAX;
668   ui::KeyEvent keyev(native_event, flags);
669 #else
670   ui::EventType type = is_press ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED;
671   ui::KeyEvent keyev(type, key_code, flags);
672 #endif  // OS_WIN
673   keyev.set_source_device_id(source_device_id);
674   Dispatch(&keyev);
675 }
676 
UpdateCurrentDispatcher(const gfx::Point & point)677 void EventGenerator::UpdateCurrentDispatcher(const gfx::Point& point) {
678   current_target_ = delegate()->GetTargetAt(point);
679 }
680 
PressButton(int flag)681 void EventGenerator::PressButton(int flag) {
682   if (!(flags_ & flag)) {
683     flags_ |= flag;
684     grab_ = (flags_ & kAllButtonMask) != 0;
685     gfx::Point location = GetLocationInCurrentRoot();
686     ui::MouseEvent mouseev(ui::ET_MOUSE_PRESSED, location, location,
687                            ui::EventTimeForNow(), flags_, flag);
688     Dispatch(&mouseev);
689   }
690 }
691 
ReleaseButton(int flag)692 void EventGenerator::ReleaseButton(int flag) {
693   if (flags_ & flag) {
694     gfx::Point location = GetLocationInCurrentRoot();
695     ui::MouseEvent mouseev(ui::ET_MOUSE_RELEASED, location, location,
696                            ui::EventTimeForNow(), flags_, flag);
697     Dispatch(&mouseev);
698     flags_ ^= flag;
699   }
700   grab_ = (flags_ & kAllButtonMask) != 0;
701 }
702 
GetLocationInCurrentRoot() const703 gfx::Point EventGenerator::GetLocationInCurrentRoot() const {
704   gfx::Point p = current_screen_location_;
705   delegate()->ConvertPointToTarget(current_target_, &p);
706   return p;
707 }
708 
CenterOfWindow(const EventTarget * window) const709 gfx::Point EventGenerator::CenterOfWindow(const EventTarget* window) const {
710   return delegate()->CenterOfTarget(window);
711 }
712 
713 }  // namespace test
714 }  // namespace ui
715