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/message_loop/message_loop_proxy.h"
11 #include "base/time/default_tick_clock.h"
12 #include "ui/events/event.h"
13 #include "ui/events/event_source.h"
14 #include "ui/events/event_utils.h"
15 #include "ui/events/test/events_test_utils.h"
16 #include "ui/gfx/vector2d_conversions.h"
20 #include "ui/events/test/events_test_utils_x11.h"
24 #include "ui/events/keycodes/keyboard_code_conversion.h"
31 void DummyCallback(EventType
, const gfx::Vector2dF
&) {
34 class TestKeyEvent
: public ui::KeyEvent
{
36 TestKeyEvent(const base::NativeEvent
& native_event
, int flags
)
37 : KeyEvent(native_event
) {
42 class TestTouchEvent
: public ui::TouchEvent
{
44 TestTouchEvent(ui::EventType type
,
45 const gfx::Point
& root_location
,
48 base::TimeDelta timestamp
)
49 : TouchEvent(type
, root_location
, flags
, touch_id
, timestamp
,
50 1.0f
, 1.0f
, 1.0f
, 1.0f
) {
54 DISALLOW_COPY_AND_ASSIGN(TestTouchEvent
);
57 const int kAllButtonMask
= ui::EF_LEFT_MOUSE_BUTTON
| ui::EF_RIGHT_MOUSE_BUTTON
;
61 EventGeneratorDelegate
* EventGenerator::default_delegate
= NULL
;
63 EventGenerator::EventGenerator(gfx::NativeWindow root_window
)
64 : current_target_(NULL
),
68 tick_clock_(new base::DefaultTickClock()) {
69 Init(root_window
, NULL
);
72 EventGenerator::EventGenerator(gfx::NativeWindow root_window
,
73 const gfx::Point
& point
)
74 : current_location_(point
),
75 current_target_(NULL
),
79 tick_clock_(new base::DefaultTickClock()) {
80 Init(root_window
, NULL
);
83 EventGenerator::EventGenerator(gfx::NativeWindow root_window
,
84 gfx::NativeWindow window
)
85 : current_target_(NULL
),
89 tick_clock_(new base::DefaultTickClock()) {
90 Init(root_window
, window
);
93 EventGenerator::EventGenerator(EventGeneratorDelegate
* delegate
)
94 : delegate_(delegate
),
95 current_target_(NULL
),
99 tick_clock_(new base::DefaultTickClock()) {
103 EventGenerator::~EventGenerator() {
104 for (std::list
<ui::Event
*>::iterator i
= pending_events_
.begin();
105 i
!= pending_events_
.end(); ++i
)
107 pending_events_
.clear();
108 delegate()->SetContext(NULL
, NULL
, NULL
);
111 void EventGenerator::PressLeftButton() {
112 PressButton(ui::EF_LEFT_MOUSE_BUTTON
);
115 void EventGenerator::ReleaseLeftButton() {
116 ReleaseButton(ui::EF_LEFT_MOUSE_BUTTON
);
119 void EventGenerator::ClickLeftButton() {
124 void EventGenerator::DoubleClickLeftButton() {
125 flags_
|= ui::EF_IS_DOUBLE_CLICK
;
127 flags_
^= ui::EF_IS_DOUBLE_CLICK
;
131 void EventGenerator::PressRightButton() {
132 PressButton(ui::EF_RIGHT_MOUSE_BUTTON
);
135 void EventGenerator::ReleaseRightButton() {
136 ReleaseButton(ui::EF_RIGHT_MOUSE_BUTTON
);
139 void EventGenerator::MoveMouseWheel(int delta_x
, int delta_y
) {
140 gfx::Point location
= GetLocationInCurrentRoot();
141 ui::MouseEvent
mouseev(ui::ET_MOUSEWHEEL
, location
, location
, flags_
, 0);
142 ui::MouseWheelEvent
wheelev(mouseev
, delta_x
, delta_y
);
146 void EventGenerator::SendMouseExit() {
147 gfx::Point
exit_location(current_location_
);
148 delegate()->ConvertPointToTarget(current_target_
, &exit_location
);
149 ui::MouseEvent
mouseev(ui::ET_MOUSE_EXITED
, exit_location
, exit_location
,
154 void EventGenerator::MoveMouseToInHost(const gfx::Point
& point_in_host
) {
155 const ui::EventType event_type
= (flags_
& ui::EF_LEFT_MOUSE_BUTTON
) ?
156 ui::ET_MOUSE_DRAGGED
: ui::ET_MOUSE_MOVED
;
157 ui::MouseEvent
mouseev(event_type
, point_in_host
, point_in_host
, flags_
, 0);
160 current_location_
= point_in_host
;
161 delegate()->ConvertPointFromHost(current_target_
, ¤t_location_
);
164 void EventGenerator::MoveMouseTo(const gfx::Point
& point_in_screen
,
167 const ui::EventType event_type
= (flags_
& ui::EF_LEFT_MOUSE_BUTTON
) ?
168 ui::ET_MOUSE_DRAGGED
: ui::ET_MOUSE_MOVED
;
170 gfx::Vector2dF
diff(point_in_screen
- current_location_
);
171 for (float i
= 1; i
<= count
; i
++) {
172 gfx::Vector2dF
step(diff
);
173 step
.Scale(i
/ count
);
174 gfx::Point move_point
= current_location_
+ gfx::ToRoundedVector2d(step
);
176 UpdateCurrentDispatcher(move_point
);
177 delegate()->ConvertPointToTarget(current_target_
, &move_point
);
178 ui::MouseEvent
mouseev(event_type
, move_point
, move_point
, flags_
, 0);
181 current_location_
= point_in_screen
;
184 void EventGenerator::MoveMouseRelativeTo(const EventTarget
* window
,
185 const gfx::Point
& point_in_parent
) {
186 gfx::Point
point(point_in_parent
);
187 delegate()->ConvertPointFromTarget(window
, &point
);
191 void EventGenerator::DragMouseTo(const gfx::Point
& point
) {
197 void EventGenerator::MoveMouseToCenterOf(EventTarget
* window
) {
198 MoveMouseTo(CenterOfWindow(window
));
201 void EventGenerator::PressTouch() {
205 void EventGenerator::PressTouchId(int touch_id
) {
206 TestTouchEvent
touchev(
207 ui::ET_TOUCH_PRESSED
, GetLocationInCurrentRoot(), touch_id
, flags_
,
212 void EventGenerator::MoveTouch(const gfx::Point
& point
) {
213 MoveTouchId(point
, 0);
216 void EventGenerator::MoveTouchId(const gfx::Point
& point
, int touch_id
) {
217 current_location_
= point
;
218 TestTouchEvent
touchev(
219 ui::ET_TOUCH_MOVED
, GetLocationInCurrentRoot(), touch_id
, flags_
,
224 UpdateCurrentDispatcher(point
);
227 void EventGenerator::ReleaseTouch() {
231 void EventGenerator::ReleaseTouchId(int touch_id
) {
232 TestTouchEvent
touchev(
233 ui::ET_TOUCH_RELEASED
, GetLocationInCurrentRoot(), touch_id
, flags_
,
238 void EventGenerator::PressMoveAndReleaseTouchTo(const gfx::Point
& point
) {
244 void EventGenerator::PressMoveAndReleaseTouchToCenterOf(EventTarget
* window
) {
245 PressMoveAndReleaseTouchTo(CenterOfWindow(window
));
248 void EventGenerator::GestureEdgeSwipe() {
249 ui::GestureEvent
gesture(
254 ui::GestureEventDetails(ui::ET_GESTURE_WIN8_EDGE_SWIPE
, 0, 0));
258 void EventGenerator::GestureTapAt(const gfx::Point
& location
) {
259 const int kTouchId
= 2;
260 ui::TouchEvent
press(ui::ET_TOUCH_PRESSED
,
266 ui::TouchEvent
release(
267 ui::ET_TOUCH_RELEASED
, location
, kTouchId
,
268 press
.time_stamp() + base::TimeDelta::FromMilliseconds(50));
272 void EventGenerator::GestureTapDownAndUp(const gfx::Point
& location
) {
273 const int kTouchId
= 3;
274 ui::TouchEvent
press(ui::ET_TOUCH_PRESSED
,
280 ui::TouchEvent
release(
281 ui::ET_TOUCH_RELEASED
, location
, kTouchId
,
282 press
.time_stamp() + base::TimeDelta::FromMilliseconds(1000));
286 void EventGenerator::GestureScrollSequence(const gfx::Point
& start
,
287 const gfx::Point
& end
,
288 const base::TimeDelta
& step_delay
,
290 GestureScrollSequenceWithCallback(start
, end
, step_delay
, steps
,
291 base::Bind(&DummyCallback
));
294 void EventGenerator::GestureScrollSequenceWithCallback(
295 const gfx::Point
& start
,
296 const gfx::Point
& end
,
297 const base::TimeDelta
& step_delay
,
299 const ScrollStepCallback
& callback
) {
300 const int kTouchId
= 5;
301 base::TimeDelta timestamp
= Now();
302 ui::TouchEvent
press(ui::ET_TOUCH_PRESSED
, start
, kTouchId
, timestamp
);
305 callback
.Run(ui::ET_GESTURE_SCROLL_BEGIN
, gfx::Vector2dF());
307 int dx
= (end
.x() - start
.x()) / steps
;
308 int dy
= (end
.y() - start
.y()) / steps
;
309 gfx::Point location
= start
;
310 for (int i
= 0; i
< steps
; ++i
) {
311 location
.Offset(dx
, dy
);
312 timestamp
+= step_delay
;
313 ui::TouchEvent
move(ui::ET_TOUCH_MOVED
, location
, kTouchId
, timestamp
);
315 callback
.Run(ui::ET_GESTURE_SCROLL_UPDATE
, gfx::Vector2dF(dx
, dy
));
318 ui::TouchEvent
release(ui::ET_TOUCH_RELEASED
, end
, kTouchId
, timestamp
);
321 callback
.Run(ui::ET_GESTURE_SCROLL_END
, gfx::Vector2dF());
324 void EventGenerator::GestureMultiFingerScroll(int count
,
325 const gfx::Point start
[],
326 int event_separation_time_ms
,
330 const int kMaxTouchPoints
= 10;
331 int delays
[kMaxTouchPoints
] = { 0 };
332 GestureMultiFingerScrollWithDelays(
333 count
, start
, delays
, event_separation_time_ms
, steps
, move_x
, move_y
);
336 void EventGenerator::GestureMultiFingerScrollWithDelays(
338 const gfx::Point start
[],
339 const int delay_adding_finger_ms
[],
340 int event_separation_time_ms
,
344 const int kMaxTouchPoints
= 10;
345 gfx::Point points
[kMaxTouchPoints
];
346 CHECK_LE(count
, kMaxTouchPoints
);
349 int delta_x
= move_x
/ steps
;
350 int delta_y
= move_y
/ steps
;
352 for (int i
= 0; i
< count
; ++i
) {
353 points
[i
] = start
[i
];
356 base::TimeDelta press_time_first
= Now();
357 base::TimeDelta press_time
[kMaxTouchPoints
];
358 bool pressed
[kMaxTouchPoints
];
359 for (int i
= 0; i
< count
; ++i
) {
361 press_time
[i
] = press_time_first
+
362 base::TimeDelta::FromMilliseconds(delay_adding_finger_ms
[i
]);
366 for (int step
= 0; step
< steps
; ++step
) {
367 base::TimeDelta move_time
= press_time_first
+
368 base::TimeDelta::FromMilliseconds(event_separation_time_ms
* step
);
370 while (last_id
< count
&&
372 move_time
>= press_time
[last_id
]) {
373 ui::TouchEvent
press(ui::ET_TOUCH_PRESSED
,
376 press_time
[last_id
]);
378 pressed
[last_id
] = true;
382 for (int i
= 0; i
< count
; ++i
) {
383 points
[i
].Offset(delta_x
, delta_y
);
386 ui::TouchEvent
move(ui::ET_TOUCH_MOVED
, points
[i
], i
, move_time
);
391 base::TimeDelta release_time
= press_time_first
+
392 base::TimeDelta::FromMilliseconds(event_separation_time_ms
* steps
);
393 for (int i
= 0; i
< last_id
; ++i
) {
394 ui::TouchEvent
release(
395 ui::ET_TOUCH_RELEASED
, points
[i
], i
, release_time
);
400 void EventGenerator::ScrollSequence(const gfx::Point
& start
,
401 const base::TimeDelta
& step_delay
,
406 base::TimeDelta timestamp
= Now();
407 ui::ScrollEvent
fling_cancel(ui::ET_SCROLL_FLING_CANCEL
,
414 Dispatch(&fling_cancel
);
416 float dx
= x_offset
/ steps
;
417 float dy
= y_offset
/ steps
;
418 for (int i
= 0; i
< steps
; ++i
) {
419 timestamp
+= step_delay
;
420 ui::ScrollEvent
move(ui::ET_SCROLL
,
430 ui::ScrollEvent
fling_start(ui::ET_SCROLL_FLING_START
,
437 Dispatch(&fling_start
);
440 void EventGenerator::ScrollSequence(const gfx::Point
& start
,
441 const base::TimeDelta
& step_delay
,
442 const std::vector
<gfx::Point
>& offsets
,
444 size_t steps
= offsets
.size();
445 base::TimeDelta timestamp
= Now();
446 ui::ScrollEvent
fling_cancel(ui::ET_SCROLL_FLING_CANCEL
,
453 Dispatch(&fling_cancel
);
455 for (size_t i
= 0; i
< steps
; ++i
) {
456 timestamp
+= step_delay
;
457 ui::ScrollEvent
scroll(ui::ET_SCROLL
,
461 offsets
[i
].x(), offsets
[i
].y(),
462 offsets
[i
].x(), offsets
[i
].y(),
467 ui::ScrollEvent
fling_start(ui::ET_SCROLL_FLING_START
,
471 offsets
[steps
- 1].x(), offsets
[steps
- 1].y(),
472 offsets
[steps
- 1].x(), offsets
[steps
- 1].y(),
474 Dispatch(&fling_start
);
477 void EventGenerator::PressKey(ui::KeyboardCode key_code
, int flags
) {
478 DispatchKeyEvent(true, key_code
, flags
);
481 void EventGenerator::ReleaseKey(ui::KeyboardCode key_code
, int flags
) {
482 DispatchKeyEvent(false, key_code
, flags
);
485 void EventGenerator::Dispatch(ui::Event
* event
) {
486 DoDispatchEvent(event
, async_
);
489 void EventGenerator::SetTickClock(scoped_ptr
<base::TickClock
> tick_clock
) {
490 tick_clock_
= tick_clock
.Pass();
493 base::TimeDelta
EventGenerator::Now() {
494 // This is the same as what EventTimeForNow() does, but here we do it
495 // with a tick clock that can be replaced with a simulated clock for tests.
496 return base::TimeDelta::FromInternalValue(
497 tick_clock_
->NowTicks().ToInternalValue());
500 void EventGenerator::Init(gfx::NativeWindow root_window
,
501 gfx::NativeWindow window_context
) {
502 delegate()->SetContext(this, root_window
, window_context
);
504 current_location_
= delegate()->CenterOfWindow(window_context
);
505 current_target_
= delegate()->GetTargetAt(current_location_
);
508 void EventGenerator::DispatchKeyEvent(bool is_press
,
509 ui::KeyboardCode key_code
,
512 UINT key_press
= WM_KEYDOWN
;
513 uint16 character
= ui::GetCharacterFromKeyCode(key_code
, flags
);
514 if (is_press
&& character
) {
515 MSG native_event
= { NULL
, WM_KEYDOWN
, key_code
, 0 };
516 TestKeyEvent
keyev(native_event
, flags
);
518 // On Windows, WM_KEYDOWN event is followed by WM_CHAR with a character
519 // if the key event cooresponds to a real character.
521 key_code
= static_cast<ui::KeyboardCode
>(character
);
524 { NULL
, (is_press
? key_press
: WM_KEYUP
), key_code
, 0 };
525 TestKeyEvent
keyev(native_event
, flags
);
526 #elif defined(USE_X11)
527 ui::ScopedXI2Event xevent
;
528 xevent
.InitKeyEvent(is_press
? ui::ET_KEY_PRESSED
: ui::ET_KEY_RELEASED
,
531 ui::KeyEvent
keyev(xevent
);
533 ui::EventType type
= is_press
? ui::ET_KEY_PRESSED
: ui::ET_KEY_RELEASED
;
534 ui::KeyEvent
keyev(type
, key_code
, flags
);
539 void EventGenerator::UpdateCurrentDispatcher(const gfx::Point
& point
) {
540 current_target_
= delegate()->GetTargetAt(point
);
543 void EventGenerator::PressButton(int flag
) {
544 if (!(flags_
& flag
)) {
546 grab_
= (flags_
& kAllButtonMask
) != 0;
547 gfx::Point location
= GetLocationInCurrentRoot();
548 ui::MouseEvent
mouseev(ui::ET_MOUSE_PRESSED
, location
, location
, flags_
,
554 void EventGenerator::ReleaseButton(int flag
) {
556 gfx::Point location
= GetLocationInCurrentRoot();
557 ui::MouseEvent
mouseev(ui::ET_MOUSE_RELEASED
, location
,
558 location
, flags_
, flag
);
562 grab_
= (flags_
& kAllButtonMask
) != 0;
565 gfx::Point
EventGenerator::GetLocationInCurrentRoot() const {
566 gfx::Point
p(current_location_
);
567 delegate()->ConvertPointToTarget(current_target_
, &p
);
571 gfx::Point
EventGenerator::CenterOfWindow(const EventTarget
* window
) const {
572 return delegate()->CenterOfTarget(window
);
575 void EventGenerator::DoDispatchEvent(ui::Event
* event
, bool async
) {
577 ui::Event
* pending_event
;
578 if (event
->IsKeyEvent()) {
579 pending_event
= new ui::KeyEvent(*static_cast<ui::KeyEvent
*>(event
));
580 } else if (event
->IsMouseEvent()) {
581 pending_event
= new ui::MouseEvent(*static_cast<ui::MouseEvent
*>(event
));
582 } else if (event
->IsTouchEvent()) {
583 pending_event
= new ui::TouchEvent(*static_cast<ui::TouchEvent
*>(event
));
584 } else if (event
->IsScrollEvent()) {
586 new ui::ScrollEvent(*static_cast<ui::ScrollEvent
*>(event
));
588 NOTREACHED() << "Invalid event type";
591 if (pending_events_
.empty()) {
592 base::MessageLoopProxy::current()->PostTask(
594 base::Bind(&EventGenerator::DispatchNextPendingEvent
,
595 base::Unretained(this)));
597 pending_events_
.push_back(pending_event
);
599 ui::EventSource
* event_source
= delegate()->GetEventSource(current_target_
);
600 ui::EventSourceTestApi
event_source_test(event_source
);
601 ui::EventDispatchDetails details
=
602 event_source_test
.SendEventToProcessor(event
);
603 CHECK(!details
.dispatcher_destroyed
);
607 void EventGenerator::DispatchNextPendingEvent() {
608 DCHECK(!pending_events_
.empty());
609 ui::Event
* event
= pending_events_
.front();
610 DoDispatchEvent(event
, false);
611 pending_events_
.pop_front();
613 if (!pending_events_
.empty()) {
614 base::MessageLoopProxy::current()->PostTask(
616 base::Bind(&EventGenerator::DispatchNextPendingEvent
,
617 base::Unretained(this)));
621 const EventGeneratorDelegate
* EventGenerator::delegate() const {
623 return delegate_
.get();
625 DCHECK(default_delegate
);
626 return default_delegate
;
629 EventGeneratorDelegate
* EventGenerator::delegate() {
630 return const_cast<EventGeneratorDelegate
*>(
631 const_cast<const EventGenerator
*>(this)->delegate());