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_, ¤t_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_, ¤t_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, ¤t_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