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.
7 #include "base/basictypes.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/time/time.h"
11 #include "content/browser/renderer_host/input/touch_emulator.h"
12 #include "content/browser/renderer_host/input/touch_emulator_client.h"
13 #include "content/common/input/web_input_event_traits.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/events/gesture_detection/gesture_config_helper.h"
18 #include "ui/aura/env.h"
19 #include "ui/aura/test/test_screen.h"
22 using blink::WebGestureEvent
;
23 using blink::WebInputEvent
;
24 using blink::WebKeyboardEvent
;
25 using blink::WebMouseEvent
;
26 using blink::WebMouseWheelEvent
;
27 using blink::WebTouchEvent
;
28 using blink::WebTouchPoint
;
32 class TouchEmulatorTest
: public testing::Test
,
33 public TouchEmulatorClient
{
36 : shift_pressed_(false),
37 mouse_pressed_(false),
38 ack_touches_synchronously_(true),
41 last_event_time_seconds_
=
42 (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
43 event_time_delta_seconds_
= 0.1;
46 virtual ~TouchEmulatorTest() {}
49 virtual void SetUp() OVERRIDE
{
51 aura::Env::CreateInstance(true);
52 screen_
.reset(aura::TestScreen::Create(gfx::Size()));
53 gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE
, screen_
.get());
56 emulator_
.reset(new TouchEmulator(this));
60 virtual void TearDown() OVERRIDE
{
62 EXPECT_EQ("", ExpectedEvents());
65 aura::Env::DeleteInstance();
70 virtual void ForwardGestureEvent(
71 const blink::WebGestureEvent
& event
) OVERRIDE
{
72 forwarded_events_
.push_back(event
.type
);
75 virtual void ForwardEmulatedTouchEvent(
76 const blink::WebTouchEvent
& event
) OVERRIDE
{
77 forwarded_events_
.push_back(event
.type
);
78 EXPECT_EQ(1U, event
.touchesLength
);
79 EXPECT_EQ(last_mouse_x_
, event
.touches
[0].position
.x
);
80 EXPECT_EQ(last_mouse_y_
, event
.touches
[0].position
.y
);
81 int expectedCancelable
= event
.type
!= WebInputEvent::TouchCancel
;
82 EXPECT_EQ(expectedCancelable
, event
.cancelable
);
83 if (ack_touches_synchronously_
) {
84 emulator()->HandleTouchEventAck(
85 event
, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
);
89 virtual void SetCursor(const WebCursor
& cursor
) OVERRIDE
{}
91 virtual void ShowContextMenuAtPoint(const gfx::Point
& point
) OVERRIDE
{}
94 TouchEmulator
* emulator() const {
95 return emulator_
.get();
98 int modifiers() const {
99 return shift_pressed_
? WebInputEvent::ShiftKey
: 0;
102 std::string
ExpectedEvents() {
104 for (size_t i
= 0; i
< forwarded_events_
.size(); ++i
) {
107 result
+= WebInputEventTraits::GetName(forwarded_events_
[i
]);
109 forwarded_events_
.clear();
113 double GetNextEventTimeSeconds() {
114 last_event_time_seconds_
+= event_time_delta_seconds_
;
115 return last_event_time_seconds_
;
118 void set_event_time_delta_seconds_(double delta
) {
119 event_time_delta_seconds_
= delta
;
122 void SendKeyboardEvent(WebInputEvent::Type type
) {
123 WebKeyboardEvent event
;
124 event
.timeStampSeconds
= GetNextEventTimeSeconds();
126 event
.modifiers
= modifiers();
127 emulator()->HandleKeyboardEvent(event
);
131 DCHECK(!shift_pressed_
);
132 shift_pressed_
= true;
133 SendKeyboardEvent(WebInputEvent::KeyDown
);
136 void ReleaseShift() {
137 DCHECK(shift_pressed_
);
138 shift_pressed_
= false;
139 SendKeyboardEvent(WebInputEvent::KeyUp
);
142 void SendMouseEvent(WebInputEvent::Type type
, int x
, int y
) {
144 event
.timeStampSeconds
= GetNextEventTimeSeconds();
146 event
.button
= mouse_pressed_
? WebMouseEvent::ButtonLeft
:
147 WebMouseEvent::ButtonNone
;
148 event
.modifiers
= modifiers();
151 event
.x
= event
.windowX
= event
.globalX
= x
;
152 event
.y
= event
.windowY
= event
.globalY
= y
;
153 emulator()->HandleMouseEvent(event
);
156 bool SendMouseWheelEvent() {
157 WebMouseWheelEvent event
;
158 event
.type
= WebInputEvent::MouseWheel
;
159 event
.timeStampSeconds
= GetNextEventTimeSeconds();
160 // Return whether mouse wheel is forwarded.
161 return !emulator()->HandleMouseWheelEvent(event
);
164 void MouseDown(int x
, int y
) {
165 DCHECK(!mouse_pressed_
);
166 if (x
!= last_mouse_x_
|| y
!= last_mouse_y_
)
167 SendMouseEvent(WebInputEvent::MouseMove
, x
, y
);
168 mouse_pressed_
= true;
169 SendMouseEvent(WebInputEvent::MouseDown
, x
, y
);
172 void MouseDrag(int x
, int y
) {
173 DCHECK(mouse_pressed_
);
174 SendMouseEvent(WebInputEvent::MouseMove
, x
, y
);
177 void MouseMove(int x
, int y
) {
178 DCHECK(!mouse_pressed_
);
179 SendMouseEvent(WebInputEvent::MouseMove
, x
, y
);
182 void MouseUp(int x
, int y
) {
183 DCHECK(mouse_pressed_
);
184 if (x
!= last_mouse_x_
|| y
!= last_mouse_y_
)
185 SendMouseEvent(WebInputEvent::MouseMove
, x
, y
);
186 SendMouseEvent(WebInputEvent::MouseUp
, x
, y
);
187 mouse_pressed_
= false;
190 bool TouchStart(int x
, int y
, bool ack
) {
191 return SendTouchEvent(
192 WebInputEvent::TouchStart
, WebTouchPoint::StatePressed
, x
, y
, ack
);
195 bool TouchMove(int x
, int y
, bool ack
) {
196 return SendTouchEvent(
197 WebInputEvent::TouchMove
, WebTouchPoint::StateMoved
, x
, y
, ack
);
200 bool TouchEnd(int x
, int y
, bool ack
) {
201 return SendTouchEvent(
202 WebInputEvent::TouchEnd
, WebTouchPoint::StateReleased
, x
, y
, ack
);
205 WebTouchEvent
MakeTouchEvent(WebInputEvent::Type type
,
206 WebTouchPoint::State state
, int x
, int y
) {
209 event
.timeStampSeconds
= GetNextEventTimeSeconds();
210 event
.touchesLength
= 1;
211 event
.touches
[0].id
= 0;
212 event
.touches
[0].state
= state
;
213 event
.touches
[0].position
.x
= x
;
214 event
.touches
[0].position
.y
= y
;
215 event
.touches
[0].screenPosition
.x
= x
;
216 event
.touches
[0].screenPosition
.y
= y
;
220 bool SendTouchEvent(WebInputEvent::Type type
, WebTouchPoint::State state
,
221 int x
, int y
, bool ack
) {
222 WebTouchEvent event
= MakeTouchEvent(type
, state
, x
, y
);
223 if (emulator()->HandleTouchEvent(event
)) {
224 // Touch event is not forwarded.
229 // Can't send ack if there are some pending acks.
230 DCHECK(!touch_events_to_ack_
.size());
232 // Touch event is forwarded, ack should not be handled by emulator.
233 EXPECT_FALSE(emulator()->HandleTouchEventAck(
234 event
, INPUT_EVENT_ACK_STATE_CONSUMED
));
236 touch_events_to_ack_
.push_back(event
);
241 void AckOldestTouchEvent() {
242 DCHECK(touch_events_to_ack_
.size());
243 WebTouchEvent event
= touch_events_to_ack_
[0];
244 touch_events_to_ack_
.erase(touch_events_to_ack_
.begin());
245 // Emulator should not handle ack from native stream.
246 EXPECT_FALSE(emulator()->HandleTouchEventAck(
247 event
, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
));
250 void DisableSynchronousTouchAck() { ack_touches_synchronously_
= false; }
253 scoped_ptr
<TouchEmulator
> emulator_
;
254 std::vector
<WebInputEvent::Type
> forwarded_events_
;
255 #if defined(USE_AURA)
256 scoped_ptr
<gfx::Screen
> screen_
;
258 double last_event_time_seconds_
;
259 double event_time_delta_seconds_
;
262 bool ack_touches_synchronously_
;
265 std::vector
<WebTouchEvent
> touch_events_to_ack_
;
266 base::MessageLoopForUI message_loop_
;
270 TEST_F(TouchEmulatorTest
, NoTouches
) {
273 EXPECT_EQ("", ExpectedEvents());
276 TEST_F(TouchEmulatorTest
, Touch
) {
278 EXPECT_EQ("", ExpectedEvents());
280 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
283 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate"
284 " TouchEnd GestureScrollEnd",
288 TEST_F(TouchEmulatorTest
, MultipleTouches
) {
290 EXPECT_EQ("", ExpectedEvents());
292 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
295 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate"
296 " TouchEnd GestureScrollEnd",
300 EXPECT_EQ("", ExpectedEvents());
302 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
305 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate",
308 EXPECT_EQ("TouchMove GestureScrollUpdate", ExpectedEvents());
311 "TouchMove GestureScrollUpdate TouchEnd GestureScrollEnd",
315 TEST_F(TouchEmulatorTest
, Pinch
) {
317 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
320 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate",
323 EXPECT_EQ("", ExpectedEvents());
325 EXPECT_EQ("TouchMove GesturePinchBegin", ExpectedEvents());
327 EXPECT_EQ("", ExpectedEvents());
330 "TouchMove GesturePinchEnd GestureScrollUpdate",
333 EXPECT_EQ("TouchEnd GestureScrollEnd", ExpectedEvents());
336 TEST_F(TouchEmulatorTest
, CancelWithDelayedAck
) {
337 DisableSynchronousTouchAck();
339 // Simulate a sequence that is interrupted by |CancelTouch()|.
341 EXPECT_EQ("TouchStart", ExpectedEvents());
343 EXPECT_EQ("TouchMove", ExpectedEvents());
344 emulator()->CancelTouch();
345 EXPECT_EQ("TouchCancel", ExpectedEvents());
346 // The mouse up should have no effect as the sequence was already cancelled.
348 EXPECT_EQ("", ExpectedEvents());
350 // Simulate a sequence that fully completes before |CancelTouch()|.
352 EXPECT_EQ("TouchStart", ExpectedEvents());
354 EXPECT_EQ("TouchEnd", ExpectedEvents());
355 // |CancelTouch| should have no effect as the sequence was already terminated.
356 emulator()->CancelTouch();
357 EXPECT_EQ("", ExpectedEvents());
360 TEST_F(TouchEmulatorTest
, DisableAndReenable
) {
362 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
365 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate",
369 EXPECT_EQ("TouchMove GesturePinchBegin", ExpectedEvents());
371 // Disable while pinch is in progress.
372 emulator()->Disable();
373 EXPECT_EQ("TouchCancel GesturePinchEnd GestureScrollEnd", ExpectedEvents());
377 EXPECT_EQ("", ExpectedEvents());
379 emulator()->Enable();
381 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
384 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate",
387 // Disable while scroll is in progress.
388 emulator()->Disable();
389 EXPECT_EQ("TouchCancel GestureScrollEnd", ExpectedEvents());
392 TEST_F(TouchEmulatorTest
, MouseMovesDropped
) {
394 EXPECT_EQ("", ExpectedEvents());
396 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
398 // Mouse move after mouse down is never dropped.
399 set_event_time_delta_seconds_(0.001);
402 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate",
405 // The following mouse moves are dropped.
407 EXPECT_EQ("", ExpectedEvents());
409 EXPECT_EQ("", ExpectedEvents());
411 // Dispatching again.
412 set_event_time_delta_seconds_(0.1);
415 "TouchMove GestureScrollUpdate",
419 "TouchEnd GestureScrollEnd",
423 TEST_F(TouchEmulatorTest
, MouseWheel
) {
425 EXPECT_EQ("", ExpectedEvents());
426 EXPECT_TRUE(SendMouseWheelEvent());
428 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
429 EXPECT_FALSE(SendMouseWheelEvent());
431 EXPECT_EQ("TouchEnd GestureShowPress GestureTap", ExpectedEvents());
432 EXPECT_TRUE(SendMouseWheelEvent());
434 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
435 EXPECT_FALSE(SendMouseWheelEvent());
436 emulator()->Disable();
437 EXPECT_EQ("TouchCancel GestureTapCancel", ExpectedEvents());
438 EXPECT_TRUE(SendMouseWheelEvent());
439 emulator()->Enable();
440 EXPECT_TRUE(SendMouseWheelEvent());
443 TEST_F(TouchEmulatorTest
, MultipleTouchStreams
) {
444 // Native stream should be blocked while emulated is active.
446 EXPECT_EQ("", ExpectedEvents());
448 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
449 EXPECT_FALSE(TouchStart(10, 10, true));
450 EXPECT_FALSE(TouchMove(20, 20, true));
453 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate"
454 " TouchEnd GestureScrollEnd",
456 EXPECT_FALSE(TouchEnd(20, 20, true));
458 // Emulated stream should be blocked while native is active.
459 EXPECT_TRUE(TouchStart(10, 10, true));
460 EXPECT_TRUE(TouchMove(20, 20, true));
462 EXPECT_EQ("", ExpectedEvents());
463 // Re-enabling in the middle of a touch sequence should not affect this.
464 emulator()->Disable();
465 emulator()->Enable();
467 EXPECT_EQ("", ExpectedEvents());
469 EXPECT_EQ("", ExpectedEvents());
470 EXPECT_TRUE(TouchEnd(20, 20, true));
471 EXPECT_EQ("", ExpectedEvents());
473 // Late ack for TouchEnd should not mess things up.
474 EXPECT_TRUE(TouchStart(10, 10, false));
475 EXPECT_TRUE(TouchMove(20, 20, false));
476 emulator()->Disable();
477 EXPECT_TRUE(TouchEnd(20, 20, false));
478 EXPECT_TRUE(TouchStart(30, 30, false));
479 AckOldestTouchEvent(); // TouchStart.
480 emulator()->Enable();
481 AckOldestTouchEvent(); // TouchMove.
482 AckOldestTouchEvent(); // TouchEnd.
484 EXPECT_EQ("", ExpectedEvents());
486 EXPECT_EQ("", ExpectedEvents());
488 EXPECT_EQ("", ExpectedEvents());
489 AckOldestTouchEvent(); // TouchStart.
491 EXPECT_EQ("", ExpectedEvents());
492 EXPECT_TRUE(TouchMove(30, 40, true));
493 EXPECT_TRUE(TouchEnd(30, 40, true));
495 EXPECT_EQ("", ExpectedEvents());
497 // Emulation should be back to normal.
499 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
502 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate"
503 " TouchEnd GestureScrollEnd",
507 TEST_F(TouchEmulatorTest
, MultipleTouchStreamsLateEnable
) {
508 // Enabling in the middle of native touch sequence should be handled.
509 // Send artificial late TouchEnd ack, like it is the first thing emulator
511 WebTouchEvent event
= MakeTouchEvent(
512 WebInputEvent::TouchEnd
, WebTouchPoint::StateReleased
, 10, 10);
513 EXPECT_FALSE(emulator()->HandleTouchEventAck(
514 event
, INPUT_EVENT_ACK_STATE_CONSUMED
));
517 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
520 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate"
521 " TouchEnd GestureScrollEnd",
525 } // namespace content