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"
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/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/geometry/vector2d_conversions.h"
21 #include "ui/events/test/events_test_utils_x11.h"
25 #include "ui/events/keycodes/keyboard_code_conversion.h"
32 void DummyCallback(EventType
, const gfx::Vector2dF
&) {
35 class TestTickClock
: public base::TickClock
{
37 // Starts off with a clock set to TimeTicks().
40 base::TimeTicks
NowTicks() override
{
41 return base::TimeTicks::FromInternalValue(ticks_
++ * 1000);
47 DISALLOW_COPY_AND_ASSIGN(TestTickClock
);
50 class TestKeyEvent
: public ui::KeyEvent
{
52 TestKeyEvent(const base::NativeEvent
& native_event
, int flags
)
53 : KeyEvent(native_event
) {
58 class TestTouchEvent
: public ui::TouchEvent
{
60 TestTouchEvent(ui::EventType type
,
61 const gfx::Point
& root_location
,
64 base::TimeDelta timestamp
)
65 : TouchEvent(type
, root_location
, flags
, touch_id
, timestamp
,
66 1.0f
, 1.0f
, 0.0f
, 0.0f
) {
70 DISALLOW_COPY_AND_ASSIGN(TestTouchEvent
);
73 const int kAllButtonMask
= ui::EF_LEFT_MOUSE_BUTTON
| ui::EF_RIGHT_MOUSE_BUTTON
;
77 EventGeneratorDelegate
* EventGenerator::default_delegate
= NULL
;
79 EventGenerator::EventGenerator(gfx::NativeWindow root_window
)
80 : current_target_(NULL
),
84 targeting_application_(false),
85 tick_clock_(new TestTickClock()) {
86 Init(root_window
, NULL
);
89 EventGenerator::EventGenerator(gfx::NativeWindow root_window
,
90 const gfx::Point
& point
)
91 : current_location_(point
),
92 current_target_(NULL
),
96 targeting_application_(false),
97 tick_clock_(new TestTickClock()) {
98 Init(root_window
, NULL
);
101 EventGenerator::EventGenerator(gfx::NativeWindow root_window
,
102 gfx::NativeWindow window
)
103 : current_target_(NULL
),
107 targeting_application_(false),
108 tick_clock_(new TestTickClock()) {
109 Init(root_window
, window
);
112 EventGenerator::EventGenerator(EventGeneratorDelegate
* delegate
)
113 : delegate_(delegate
),
114 current_target_(NULL
),
118 targeting_application_(false),
119 tick_clock_(new TestTickClock()) {
123 EventGenerator::~EventGenerator() {
124 for (std::list
<ui::Event
*>::iterator i
= pending_events_
.begin();
125 i
!= pending_events_
.end(); ++i
)
127 pending_events_
.clear();
128 delegate()->SetContext(NULL
, NULL
, NULL
);
131 void EventGenerator::PressLeftButton() {
132 PressButton(ui::EF_LEFT_MOUSE_BUTTON
);
135 void EventGenerator::ReleaseLeftButton() {
136 ReleaseButton(ui::EF_LEFT_MOUSE_BUTTON
);
139 void EventGenerator::ClickLeftButton() {
144 void EventGenerator::DoubleClickLeftButton() {
145 flags_
&= ~ui::EF_IS_DOUBLE_CLICK
;
147 flags_
|= ui::EF_IS_DOUBLE_CLICK
;
149 flags_
&= ~ui::EF_IS_DOUBLE_CLICK
;
152 void EventGenerator::PressRightButton() {
153 PressButton(ui::EF_RIGHT_MOUSE_BUTTON
);
156 void EventGenerator::ReleaseRightButton() {
157 ReleaseButton(ui::EF_RIGHT_MOUSE_BUTTON
);
160 void EventGenerator::MoveMouseWheel(int delta_x
, int delta_y
) {
161 gfx::Point location
= GetLocationInCurrentRoot();
162 ui::MouseEvent
mouseev(ui::ET_MOUSEWHEEL
, location
, location
,
163 ui::EventTimeForNow(), flags_
, 0);
164 ui::MouseWheelEvent
wheelev(mouseev
, delta_x
, delta_y
);
168 void EventGenerator::SendMouseExit() {
169 gfx::Point
exit_location(current_location_
);
170 delegate()->ConvertPointToTarget(current_target_
, &exit_location
);
171 ui::MouseEvent
mouseev(ui::ET_MOUSE_EXITED
, exit_location
, exit_location
,
172 ui::EventTimeForNow(), flags_
, 0);
176 void EventGenerator::MoveMouseToWithNative(const gfx::Point
& point_in_host
,
177 const gfx::Point
& point_for_native
) {
179 ui::ScopedXI2Event xevent
;
180 xevent
.InitMotionEvent(point_in_host
, point_for_native
, flags_
);
181 static_cast<XEvent
*>(xevent
)->xmotion
.time
= Now().InMicroseconds();
182 ui::MouseEvent
mouseev(xevent
);
183 #elif defined(USE_OZONE)
184 // Ozone uses the location in native event as a system location.
185 // Create a fake event with the point in host, which will be passed
186 // to the non native event, then update the native event with the native
188 scoped_ptr
<ui::MouseEvent
> native_event(new ui::MouseEvent(
189 ui::ET_MOUSE_MOVED
, point_in_host
, point_in_host
, Now(), flags_
, 0));
190 ui::MouseEvent
mouseev(native_event
.get());
191 native_event
->set_location(point_for_native
);
193 ui::MouseEvent
mouseev(ui::ET_MOUSE_MOVED
, point_in_host
, point_for_native
,
196 << "Generating a native motion event is not supported on this platform";
200 current_location_
= point_in_host
;
201 delegate()->ConvertPointFromHost(current_target_
, ¤t_location_
);
204 void EventGenerator::MoveMouseToInHost(const gfx::Point
& point_in_host
) {
205 const ui::EventType event_type
= (flags_
& ui::EF_LEFT_MOUSE_BUTTON
) ?
206 ui::ET_MOUSE_DRAGGED
: ui::ET_MOUSE_MOVED
;
207 ui::MouseEvent
mouseev(event_type
, point_in_host
, point_in_host
,
208 ui::EventTimeForNow(), flags_
, 0);
211 current_location_
= point_in_host
;
212 delegate()->ConvertPointFromHost(current_target_
, ¤t_location_
);
215 void EventGenerator::MoveMouseTo(const gfx::Point
& point_in_screen
,
218 const ui::EventType event_type
= (flags_
& ui::EF_LEFT_MOUSE_BUTTON
) ?
219 ui::ET_MOUSE_DRAGGED
: ui::ET_MOUSE_MOVED
;
221 gfx::Vector2dF
diff(point_in_screen
- current_location_
);
222 for (float i
= 1; i
<= count
; i
++) {
223 gfx::Vector2dF
step(diff
);
224 step
.Scale(i
/ count
);
225 gfx::Point move_point
= current_location_
+ gfx::ToRoundedVector2d(step
);
227 UpdateCurrentDispatcher(move_point
);
228 delegate()->ConvertPointToTarget(current_target_
, &move_point
);
229 ui::MouseEvent
mouseev(event_type
, move_point
, move_point
,
230 ui::EventTimeForNow(), flags_
, 0);
233 current_location_
= point_in_screen
;
236 void EventGenerator::MoveMouseRelativeTo(const EventTarget
* window
,
237 const gfx::Point
& point_in_parent
) {
238 gfx::Point
point(point_in_parent
);
239 delegate()->ConvertPointFromTarget(window
, &point
);
243 void EventGenerator::DragMouseTo(const gfx::Point
& point
) {
249 void EventGenerator::MoveMouseToCenterOf(EventTarget
* window
) {
250 MoveMouseTo(CenterOfWindow(window
));
253 void EventGenerator::PressTouch() {
257 void EventGenerator::PressTouchId(int touch_id
) {
258 TestTouchEvent
touchev(
259 ui::ET_TOUCH_PRESSED
, GetLocationInCurrentRoot(), touch_id
, flags_
,
264 void EventGenerator::MoveTouch(const gfx::Point
& point
) {
265 MoveTouchId(point
, 0);
268 void EventGenerator::MoveTouchId(const gfx::Point
& point
, int touch_id
) {
269 current_location_
= point
;
270 TestTouchEvent
touchev(
271 ui::ET_TOUCH_MOVED
, GetLocationInCurrentRoot(), touch_id
, flags_
,
276 UpdateCurrentDispatcher(point
);
279 void EventGenerator::ReleaseTouch() {
283 void EventGenerator::ReleaseTouchId(int touch_id
) {
284 TestTouchEvent
touchev(
285 ui::ET_TOUCH_RELEASED
, GetLocationInCurrentRoot(), touch_id
, flags_
,
290 void EventGenerator::PressMoveAndReleaseTouchTo(const gfx::Point
& point
) {
296 void EventGenerator::PressMoveAndReleaseTouchToCenterOf(EventTarget
* window
) {
297 PressMoveAndReleaseTouchTo(CenterOfWindow(window
));
300 void EventGenerator::GestureEdgeSwipe() {
301 ui::GestureEvent
gesture(
302 0, 0, 0, Now(), ui::GestureEventDetails(ui::ET_GESTURE_WIN8_EDGE_SWIPE
));
306 void EventGenerator::GestureTapAt(const gfx::Point
& location
) {
307 const int kTouchId
= 2;
308 ui::TouchEvent
press(ui::ET_TOUCH_PRESSED
,
314 ui::TouchEvent
release(
315 ui::ET_TOUCH_RELEASED
, location
, kTouchId
,
316 press
.time_stamp() + base::TimeDelta::FromMilliseconds(50));
320 void EventGenerator::GestureTapDownAndUp(const gfx::Point
& location
) {
321 const int kTouchId
= 3;
322 ui::TouchEvent
press(ui::ET_TOUCH_PRESSED
,
328 ui::TouchEvent
release(
329 ui::ET_TOUCH_RELEASED
, location
, kTouchId
,
330 press
.time_stamp() + base::TimeDelta::FromMilliseconds(1000));
334 base::TimeDelta
EventGenerator::CalculateScrollDurationForFlingVelocity(
335 const gfx::Point
& start
,
336 const gfx::Point
& end
,
339 const float kGestureDistance
= (start
- end
).Length();
340 const float kFlingStepDelay
= (kGestureDistance
/ velocity
) / steps
* 1000000;
341 return base::TimeDelta::FromMicroseconds(kFlingStepDelay
);
344 void EventGenerator::GestureScrollSequence(const gfx::Point
& start
,
345 const gfx::Point
& end
,
346 const base::TimeDelta
& step_delay
,
348 GestureScrollSequenceWithCallback(start
, end
, step_delay
, steps
,
349 base::Bind(&DummyCallback
));
352 void EventGenerator::GestureScrollSequenceWithCallback(
353 const gfx::Point
& start
,
354 const gfx::Point
& end
,
355 const base::TimeDelta
& step_delay
,
357 const ScrollStepCallback
& callback
) {
358 const int kTouchId
= 5;
359 base::TimeDelta timestamp
= Now();
360 ui::TouchEvent
press(ui::ET_TOUCH_PRESSED
, start
, kTouchId
, timestamp
);
363 callback
.Run(ui::ET_GESTURE_SCROLL_BEGIN
, gfx::Vector2dF());
365 float dx
= static_cast<float>(end
.x() - start
.x()) / steps
;
366 float dy
= static_cast<float>(end
.y() - start
.y()) / steps
;
367 gfx::PointF location
= start
;
368 for (int i
= 0; i
< steps
; ++i
) {
369 location
.Offset(dx
, dy
);
370 timestamp
+= step_delay
;
371 ui::TouchEvent
move(ui::ET_TOUCH_MOVED
, location
, kTouchId
, timestamp
);
373 callback
.Run(ui::ET_GESTURE_SCROLL_UPDATE
, gfx::Vector2dF(dx
, dy
));
376 ui::TouchEvent
release(ui::ET_TOUCH_RELEASED
, end
, kTouchId
, timestamp
);
379 callback
.Run(ui::ET_GESTURE_SCROLL_END
, gfx::Vector2dF());
382 void EventGenerator::GestureMultiFingerScroll(int count
,
383 const gfx::Point start
[],
384 int event_separation_time_ms
,
388 const int kMaxTouchPoints
= 10;
389 int delays
[kMaxTouchPoints
] = { 0 };
390 GestureMultiFingerScrollWithDelays(
391 count
, start
, delays
, event_separation_time_ms
, steps
, move_x
, move_y
);
394 void EventGenerator::GestureMultiFingerScrollWithDelays(
396 const gfx::Point start
[],
397 const int delay_adding_finger_ms
[],
398 int event_separation_time_ms
,
402 const int kMaxTouchPoints
= 10;
403 gfx::Point points
[kMaxTouchPoints
];
404 CHECK_LE(count
, kMaxTouchPoints
);
407 int delta_x
= move_x
/ steps
;
408 int delta_y
= move_y
/ steps
;
410 for (int i
= 0; i
< count
; ++i
) {
411 points
[i
] = start
[i
];
414 base::TimeDelta press_time_first
= Now();
415 base::TimeDelta press_time
[kMaxTouchPoints
];
416 bool pressed
[kMaxTouchPoints
];
417 for (int i
= 0; i
< count
; ++i
) {
419 press_time
[i
] = press_time_first
+
420 base::TimeDelta::FromMilliseconds(delay_adding_finger_ms
[i
]);
424 for (int step
= 0; step
< steps
; ++step
) {
425 base::TimeDelta move_time
= press_time_first
+
426 base::TimeDelta::FromMilliseconds(event_separation_time_ms
* step
);
428 while (last_id
< count
&&
430 move_time
>= press_time
[last_id
]) {
431 ui::TouchEvent
press(ui::ET_TOUCH_PRESSED
,
434 press_time
[last_id
]);
436 pressed
[last_id
] = true;
440 for (int i
= 0; i
< count
; ++i
) {
441 points
[i
].Offset(delta_x
, delta_y
);
444 ui::TouchEvent
move(ui::ET_TOUCH_MOVED
, points
[i
], i
, move_time
);
449 base::TimeDelta release_time
= press_time_first
+
450 base::TimeDelta::FromMilliseconds(event_separation_time_ms
* steps
);
451 for (int i
= 0; i
< last_id
; ++i
) {
452 ui::TouchEvent
release(
453 ui::ET_TOUCH_RELEASED
, points
[i
], i
, release_time
);
458 void EventGenerator::ScrollSequence(const gfx::Point
& start
,
459 const base::TimeDelta
& step_delay
,
464 base::TimeDelta timestamp
= Now();
465 ui::ScrollEvent
fling_cancel(ui::ET_SCROLL_FLING_CANCEL
,
472 Dispatch(&fling_cancel
);
474 float dx
= x_offset
/ steps
;
475 float dy
= y_offset
/ steps
;
476 for (int i
= 0; i
< steps
; ++i
) {
477 timestamp
+= step_delay
;
478 ui::ScrollEvent
move(ui::ET_SCROLL
,
488 ui::ScrollEvent
fling_start(ui::ET_SCROLL_FLING_START
,
495 Dispatch(&fling_start
);
498 void EventGenerator::ScrollSequence(const gfx::Point
& start
,
499 const base::TimeDelta
& step_delay
,
500 const std::vector
<gfx::PointF
>& offsets
,
502 size_t steps
= offsets
.size();
503 base::TimeDelta timestamp
= Now();
504 ui::ScrollEvent
fling_cancel(ui::ET_SCROLL_FLING_CANCEL
,
511 Dispatch(&fling_cancel
);
513 for (size_t i
= 0; i
< steps
; ++i
) {
514 timestamp
+= step_delay
;
515 ui::ScrollEvent
scroll(ui::ET_SCROLL
,
519 offsets
[i
].x(), offsets
[i
].y(),
520 offsets
[i
].x(), offsets
[i
].y(),
525 ui::ScrollEvent
fling_start(ui::ET_SCROLL_FLING_START
,
529 offsets
[steps
- 1].x(), offsets
[steps
- 1].y(),
530 offsets
[steps
- 1].x(), offsets
[steps
- 1].y(),
532 Dispatch(&fling_start
);
535 void EventGenerator::PressKey(ui::KeyboardCode key_code
, int flags
) {
536 DispatchKeyEvent(true, key_code
, flags
);
539 void EventGenerator::ReleaseKey(ui::KeyboardCode key_code
, int flags
) {
540 DispatchKeyEvent(false, key_code
, flags
);
543 void EventGenerator::Dispatch(ui::Event
* event
) {
544 DoDispatchEvent(event
, async_
);
547 void EventGenerator::SetTickClock(scoped_ptr
<base::TickClock
> tick_clock
) {
548 tick_clock_
= tick_clock
.Pass();
551 base::TimeDelta
EventGenerator::Now() {
552 // This is the same as what EventTimeForNow() does, but here we do it
553 // with a tick clock that can be replaced with a simulated clock for tests.
554 return base::TimeDelta::FromInternalValue(
555 tick_clock_
->NowTicks().ToInternalValue());
558 void EventGenerator::Init(gfx::NativeWindow root_window
,
559 gfx::NativeWindow window_context
) {
560 delegate()->SetContext(this, root_window
, window_context
);
562 current_location_
= delegate()->CenterOfWindow(window_context
);
563 current_target_
= delegate()->GetTargetAt(current_location_
);
566 void EventGenerator::DispatchKeyEvent(bool is_press
,
567 ui::KeyboardCode key_code
,
570 UINT key_press
= WM_KEYDOWN
;
571 uint16 character
= ui::GetCharacterFromKeyCode(key_code
, flags
);
572 if (is_press
&& character
) {
573 MSG native_event
= { NULL
, WM_KEYDOWN
, key_code
, 0 };
574 TestKeyEvent
keyev(native_event
, flags
);
576 // On Windows, WM_KEYDOWN event is followed by WM_CHAR with a character
577 // if the key event cooresponds to a real character.
579 key_code
= static_cast<ui::KeyboardCode
>(character
);
582 { NULL
, (is_press
? key_press
: WM_KEYUP
), key_code
, 0 };
583 native_event
.time
= Now().InMicroseconds();
584 TestKeyEvent
keyev(native_event
, flags
);
585 #elif defined(USE_X11)
586 ui::ScopedXI2Event xevent
;
587 xevent
.InitKeyEvent(is_press
? ui::ET_KEY_PRESSED
: ui::ET_KEY_RELEASED
,
590 static_cast<XEvent
*>(xevent
)->xkey
.time
= Now().InMicroseconds();
591 ui::KeyEvent
keyev(xevent
);
593 ui::EventType type
= is_press
? ui::ET_KEY_PRESSED
: ui::ET_KEY_RELEASED
;
594 ui::KeyEvent
keyev(type
, key_code
, flags
);
599 void EventGenerator::UpdateCurrentDispatcher(const gfx::Point
& point
) {
600 current_target_
= delegate()->GetTargetAt(point
);
603 void EventGenerator::PressButton(int flag
) {
604 if (!(flags_
& flag
)) {
606 grab_
= (flags_
& kAllButtonMask
) != 0;
607 gfx::Point location
= GetLocationInCurrentRoot();
608 ui::MouseEvent
mouseev(ui::ET_MOUSE_PRESSED
, location
, location
,
609 ui::EventTimeForNow(), flags_
, flag
);
614 void EventGenerator::ReleaseButton(int flag
) {
616 gfx::Point location
= GetLocationInCurrentRoot();
617 ui::MouseEvent
mouseev(ui::ET_MOUSE_RELEASED
, location
, location
,
618 ui::EventTimeForNow(), flags_
, flag
);
622 grab_
= (flags_
& kAllButtonMask
) != 0;
625 gfx::Point
EventGenerator::GetLocationInCurrentRoot() const {
626 gfx::Point
p(current_location_
);
627 delegate()->ConvertPointToTarget(current_target_
, &p
);
631 gfx::Point
EventGenerator::CenterOfWindow(const EventTarget
* window
) const {
632 return delegate()->CenterOfTarget(window
);
635 void EventGenerator::DoDispatchEvent(ui::Event
* event
, bool async
) {
637 ui::Event
* pending_event
;
638 if (event
->IsKeyEvent()) {
639 pending_event
= new ui::KeyEvent(*static_cast<ui::KeyEvent
*>(event
));
640 } else if (event
->IsMouseEvent()) {
641 pending_event
= new ui::MouseEvent(*static_cast<ui::MouseEvent
*>(event
));
642 } else if (event
->IsTouchEvent()) {
643 pending_event
= new ui::TouchEvent(*static_cast<ui::TouchEvent
*>(event
));
644 } else if (event
->IsScrollEvent()) {
646 new ui::ScrollEvent(*static_cast<ui::ScrollEvent
*>(event
));
648 NOTREACHED() << "Invalid event type";
651 if (pending_events_
.empty()) {
652 base::ThreadTaskRunnerHandle::Get()->PostTask(
654 base::Bind(&EventGenerator::DispatchNextPendingEvent
,
655 base::Unretained(this)));
657 pending_events_
.push_back(pending_event
);
659 ui::EventSource
* event_source
= delegate()->GetEventSource(current_target_
);
660 ui::EventSourceTestApi
event_source_test(event_source
);
661 ui::EventDispatchDetails details
=
662 event_source_test
.SendEventToProcessor(event
);
663 CHECK(!details
.dispatcher_destroyed
);
667 void EventGenerator::DispatchNextPendingEvent() {
668 DCHECK(!pending_events_
.empty());
669 ui::Event
* event
= pending_events_
.front();
670 DoDispatchEvent(event
, false);
671 pending_events_
.pop_front();
673 if (!pending_events_
.empty()) {
674 base::ThreadTaskRunnerHandle::Get()->PostTask(
676 base::Bind(&EventGenerator::DispatchNextPendingEvent
,
677 base::Unretained(this)));
681 const EventGeneratorDelegate
* EventGenerator::delegate() const {
683 return delegate_
.get();
685 DCHECK(default_delegate
);
686 return default_delegate
;
689 EventGeneratorDelegate
* EventGenerator::delegate() {
690 return const_cast<EventGeneratorDelegate
*>(
691 const_cast<const EventGenerator
*>(this)->delegate());