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 <vector>
6
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"
16
17#if defined(USE_AURA)
18#include "ui/aura/env.h"
19#include "ui/aura/test/test_screen.h"
20#endif
21
22using blink::WebGestureEvent;
23using blink::WebInputEvent;
24using blink::WebKeyboardEvent;
25using blink::WebMouseEvent;
26using blink::WebMouseWheelEvent;
27using blink::WebTouchEvent;
28using blink::WebTouchPoint;
29
30namespace content {
31
32class TouchEmulatorTest : public testing::Test,
33                          public TouchEmulatorClient {
34 public:
35  TouchEmulatorTest()
36      : shift_pressed_(false),
37        mouse_pressed_(false),
38        ack_touches_synchronously_(true),
39        last_mouse_x_(-1),
40        last_mouse_y_(-1) {
41    last_event_time_seconds_ =
42        (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
43    event_time_delta_seconds_ = 0.1;
44  }
45
46  virtual ~TouchEmulatorTest() {}
47
48  // testing::Test
49  virtual void SetUp() OVERRIDE {
50#if defined(USE_AURA)
51    aura::Env::CreateInstance(true);
52    screen_.reset(aura::TestScreen::Create(gfx::Size()));
53    gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get());
54#endif
55
56    emulator_.reset(new TouchEmulator(this));
57    emulator_->Enable();
58  }
59
60  virtual void TearDown() OVERRIDE {
61    emulator_->Disable();
62    EXPECT_EQ("", ExpectedEvents());
63
64#if defined(USE_AURA)
65    aura::Env::DeleteInstance();
66    screen_.reset();
67#endif
68  }
69
70  virtual void ForwardGestureEvent(
71      const blink::WebGestureEvent& event) OVERRIDE {
72    forwarded_events_.push_back(event.type);
73  }
74
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);
86    }
87  }
88
89  virtual void SetCursor(const WebCursor& cursor) OVERRIDE {}
90
91  virtual void ShowContextMenuAtPoint(const gfx::Point& point) OVERRIDE {}
92
93 protected:
94  TouchEmulator* emulator() const {
95    return emulator_.get();
96  }
97
98  int modifiers() const {
99    return shift_pressed_ ? WebInputEvent::ShiftKey : 0;
100  }
101
102  std::string ExpectedEvents() {
103    std::string result;
104    for (size_t i = 0; i < forwarded_events_.size(); ++i) {
105      if (i != 0)
106        result += " ";
107      result += WebInputEventTraits::GetName(forwarded_events_[i]);
108    }
109    forwarded_events_.clear();
110    return result;
111  }
112
113  double GetNextEventTimeSeconds() {
114    last_event_time_seconds_ += event_time_delta_seconds_;
115    return last_event_time_seconds_;
116  }
117
118  void set_event_time_delta_seconds_(double delta) {
119    event_time_delta_seconds_ = delta;
120  }
121
122  void SendKeyboardEvent(WebInputEvent::Type type) {
123    WebKeyboardEvent event;
124    event.timeStampSeconds = GetNextEventTimeSeconds();
125    event.type = type;
126    event.modifiers = modifiers();
127    emulator()->HandleKeyboardEvent(event);
128  }
129
130  void PressShift() {
131    DCHECK(!shift_pressed_);
132    shift_pressed_ = true;
133    SendKeyboardEvent(WebInputEvent::KeyDown);
134  }
135
136  void ReleaseShift() {
137    DCHECK(shift_pressed_);
138    shift_pressed_ = false;
139    SendKeyboardEvent(WebInputEvent::KeyUp);
140  }
141
142  void SendMouseEvent(WebInputEvent::Type type, int  x, int y) {
143    WebMouseEvent event;
144    event.timeStampSeconds = GetNextEventTimeSeconds();
145    event.type = type;
146    event.button = mouse_pressed_ ? WebMouseEvent::ButtonLeft :
147        WebMouseEvent::ButtonNone;
148    event.modifiers = modifiers();
149    last_mouse_x_ = x;
150    last_mouse_y_ = y;
151    event.x = event.windowX = event.globalX = x;
152    event.y = event.windowY = event.globalY = y;
153    emulator()->HandleMouseEvent(event);
154  }
155
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);
162  }
163
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);
170  }
171
172  void MouseDrag(int x, int y) {
173    DCHECK(mouse_pressed_);
174    SendMouseEvent(WebInputEvent::MouseMove, x, y);
175  }
176
177  void MouseMove(int x, int y) {
178    DCHECK(!mouse_pressed_);
179    SendMouseEvent(WebInputEvent::MouseMove, x, y);
180  }
181
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;
188  }
189
190  bool TouchStart(int x, int  y, bool ack) {
191    return SendTouchEvent(
192        WebInputEvent::TouchStart, WebTouchPoint::StatePressed, x, y, ack);
193  }
194
195  bool TouchMove(int x, int  y, bool ack) {
196    return SendTouchEvent(
197        WebInputEvent::TouchMove, WebTouchPoint::StateMoved, x, y, ack);
198  }
199
200  bool TouchEnd(int x, int  y, bool ack) {
201    return SendTouchEvent(
202        WebInputEvent::TouchEnd, WebTouchPoint::StateReleased, x, y, ack);
203  }
204
205  WebTouchEvent MakeTouchEvent(WebInputEvent::Type type,
206      WebTouchPoint::State state, int x, int y) {
207    WebTouchEvent event;
208    event.type = type;
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;
217    return event;
218  }
219
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.
225      return false;
226    }
227
228    if (ack) {
229      // Can't send ack if there are some pending acks.
230      DCHECK(!touch_events_to_ack_.size());
231
232      // Touch event is forwarded, ack should not be handled by emulator.
233      EXPECT_FALSE(emulator()->HandleTouchEventAck(
234          event, INPUT_EVENT_ACK_STATE_CONSUMED));
235    } else {
236      touch_events_to_ack_.push_back(event);
237    }
238    return true;
239  }
240
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));
248  }
249
250  void DisableSynchronousTouchAck() { ack_touches_synchronously_ = false; }
251
252 private:
253  scoped_ptr<TouchEmulator> emulator_;
254  std::vector<WebInputEvent::Type> forwarded_events_;
255#if defined(USE_AURA)
256  scoped_ptr<gfx::Screen> screen_;
257#endif
258  double last_event_time_seconds_;
259  double event_time_delta_seconds_;
260  bool shift_pressed_;
261  bool mouse_pressed_;
262  bool ack_touches_synchronously_;
263  int last_mouse_x_;
264  int last_mouse_y_;
265  std::vector<WebTouchEvent> touch_events_to_ack_;
266  base::MessageLoopForUI message_loop_;
267};
268
269
270TEST_F(TouchEmulatorTest, NoTouches) {
271  MouseMove(100, 200);
272  MouseMove(300, 300);
273  EXPECT_EQ("", ExpectedEvents());
274}
275
276TEST_F(TouchEmulatorTest, Touch) {
277  MouseMove(100, 200);
278  EXPECT_EQ("", ExpectedEvents());
279  MouseDown(100, 200);
280  EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
281  MouseUp(200, 200);
282  EXPECT_EQ(
283      "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate"
284      " TouchEnd GestureScrollEnd",
285      ExpectedEvents());
286}
287
288TEST_F(TouchEmulatorTest, MultipleTouches) {
289  MouseMove(100, 200);
290  EXPECT_EQ("", ExpectedEvents());
291  MouseDown(100, 200);
292  EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
293  MouseUp(200, 200);
294  EXPECT_EQ(
295      "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate"
296      " TouchEnd GestureScrollEnd",
297      ExpectedEvents());
298  MouseMove(300, 200);
299  MouseMove(200, 200);
300  EXPECT_EQ("", ExpectedEvents());
301  MouseDown(300, 200);
302  EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
303  MouseDrag(300, 300);
304  EXPECT_EQ(
305      "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate",
306      ExpectedEvents());
307  MouseDrag(300, 400);
308  EXPECT_EQ("TouchMove GestureScrollUpdate", ExpectedEvents());
309  MouseUp(300, 500);
310  EXPECT_EQ(
311      "TouchMove GestureScrollUpdate TouchEnd GestureScrollEnd",
312      ExpectedEvents());
313}
314
315TEST_F(TouchEmulatorTest, Pinch) {
316  MouseDown(100, 200);
317  EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
318  MouseDrag(200, 200);
319  EXPECT_EQ(
320      "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate",
321      ExpectedEvents());
322  PressShift();
323  EXPECT_EQ("", ExpectedEvents());
324  MouseDrag(300, 200);
325  EXPECT_EQ("TouchMove GesturePinchBegin", ExpectedEvents());
326  ReleaseShift();
327  EXPECT_EQ("", ExpectedEvents());
328  MouseDrag(400, 200);
329  EXPECT_EQ(
330      "TouchMove GesturePinchEnd GestureScrollUpdate",
331      ExpectedEvents());
332  MouseUp(400, 200);
333  EXPECT_EQ("TouchEnd GestureScrollEnd", ExpectedEvents());
334}
335
336TEST_F(TouchEmulatorTest, CancelWithDelayedAck) {
337  DisableSynchronousTouchAck();
338
339  // Simulate a sequence that is interrupted by |CancelTouch()|.
340  MouseDown(100, 200);
341  EXPECT_EQ("TouchStart", ExpectedEvents());
342  MouseDrag(200, 200);
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.
347  MouseUp(400, 200);
348  EXPECT_EQ("", ExpectedEvents());
349
350  // Simulate a sequence that fully completes before |CancelTouch()|.
351  MouseDown(100, 200);
352  EXPECT_EQ("TouchStart", ExpectedEvents());
353  MouseUp(100, 200);
354  EXPECT_EQ("TouchEnd", ExpectedEvents());
355  // |CancelTouch| should have no effect as the sequence was already terminated.
356  emulator()->CancelTouch();
357  EXPECT_EQ("", ExpectedEvents());
358}
359
360TEST_F(TouchEmulatorTest, DisableAndReenable) {
361  MouseDown(100, 200);
362  EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
363  MouseDrag(200, 200);
364  EXPECT_EQ(
365      "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate",
366      ExpectedEvents());
367  PressShift();
368  MouseDrag(300, 200);
369  EXPECT_EQ("TouchMove GesturePinchBegin", ExpectedEvents());
370
371  // Disable while pinch is in progress.
372  emulator()->Disable();
373  EXPECT_EQ("TouchCancel GesturePinchEnd GestureScrollEnd", ExpectedEvents());
374  MouseUp(300, 200);
375  ReleaseShift();
376  MouseMove(300, 300);
377  EXPECT_EQ("", ExpectedEvents());
378
379  emulator()->Enable();
380  MouseDown(300, 300);
381  EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
382  MouseDrag(300, 400);
383  EXPECT_EQ(
384      "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate",
385      ExpectedEvents());
386
387  // Disable while scroll is in progress.
388  emulator()->Disable();
389  EXPECT_EQ("TouchCancel GestureScrollEnd", ExpectedEvents());
390}
391
392TEST_F(TouchEmulatorTest, MouseMovesDropped) {
393  MouseMove(100, 200);
394  EXPECT_EQ("", ExpectedEvents());
395  MouseDown(100, 200);
396  EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
397
398  // Mouse move after mouse down is never dropped.
399  set_event_time_delta_seconds_(0.001);
400  MouseDrag(200, 200);
401  EXPECT_EQ(
402      "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate",
403      ExpectedEvents());
404
405  // The following mouse moves are dropped.
406  MouseDrag(300, 200);
407  EXPECT_EQ("", ExpectedEvents());
408  MouseDrag(350, 200);
409  EXPECT_EQ("", ExpectedEvents());
410
411  // Dispatching again.
412  set_event_time_delta_seconds_(0.1);
413  MouseDrag(400, 200);
414  EXPECT_EQ(
415      "TouchMove GestureScrollUpdate",
416      ExpectedEvents());
417  MouseUp(400, 200);
418  EXPECT_EQ(
419      "TouchEnd GestureScrollEnd",
420      ExpectedEvents());
421}
422
423TEST_F(TouchEmulatorTest, MouseWheel) {
424  MouseMove(100, 200);
425  EXPECT_EQ("", ExpectedEvents());
426  EXPECT_TRUE(SendMouseWheelEvent());
427  MouseDown(100, 200);
428  EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
429  EXPECT_FALSE(SendMouseWheelEvent());
430  MouseUp(100, 200);
431  EXPECT_EQ("TouchEnd GestureShowPress GestureTap", ExpectedEvents());
432  EXPECT_TRUE(SendMouseWheelEvent());
433  MouseDown(300, 200);
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());
441}
442
443TEST_F(TouchEmulatorTest, MultipleTouchStreams) {
444  // Native stream should be blocked while emulated is active.
445  MouseMove(100, 200);
446  EXPECT_EQ("", ExpectedEvents());
447  MouseDown(100, 200);
448  EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
449  EXPECT_FALSE(TouchStart(10, 10, true));
450  EXPECT_FALSE(TouchMove(20, 20, true));
451  MouseUp(200, 200);
452  EXPECT_EQ(
453      "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate"
454      " TouchEnd GestureScrollEnd",
455      ExpectedEvents());
456  EXPECT_FALSE(TouchEnd(20, 20, true));
457
458  // Emulated stream should be blocked while native is active.
459  EXPECT_TRUE(TouchStart(10, 10, true));
460  EXPECT_TRUE(TouchMove(20, 20, true));
461  MouseDown(300, 200);
462  EXPECT_EQ("", ExpectedEvents());
463  // Re-enabling in the middle of a touch sequence should not affect this.
464  emulator()->Disable();
465  emulator()->Enable();
466  MouseDrag(300, 300);
467  EXPECT_EQ("", ExpectedEvents());
468  MouseUp(300, 300);
469  EXPECT_EQ("", ExpectedEvents());
470  EXPECT_TRUE(TouchEnd(20, 20, true));
471  EXPECT_EQ("", ExpectedEvents());
472
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.
483  MouseDown(300, 200);
484  EXPECT_EQ("", ExpectedEvents());
485  MouseDrag(300, 300);
486  EXPECT_EQ("", ExpectedEvents());
487  MouseUp(300, 300);
488  EXPECT_EQ("", ExpectedEvents());
489  AckOldestTouchEvent(); // TouchStart.
490  MouseDown(300, 200);
491  EXPECT_EQ("", ExpectedEvents());
492  EXPECT_TRUE(TouchMove(30, 40, true));
493  EXPECT_TRUE(TouchEnd(30, 40, true));
494  MouseUp(300, 200);
495  EXPECT_EQ("", ExpectedEvents());
496
497  // Emulation should be back to normal.
498  MouseDown(100, 200);
499  EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
500  MouseUp(200, 200);
501  EXPECT_EQ(
502      "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate"
503      " TouchEnd GestureScrollEnd",
504      ExpectedEvents());
505}
506
507TEST_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
510  // does see.
511  WebTouchEvent event = MakeTouchEvent(
512      WebInputEvent::TouchEnd, WebTouchPoint::StateReleased, 10, 10);
513  EXPECT_FALSE(emulator()->HandleTouchEventAck(
514      event, INPUT_EVENT_ACK_STATE_CONSUMED));
515
516  MouseDown(100, 200);
517  EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents());
518  MouseUp(200, 200);
519  EXPECT_EQ(
520      "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate"
521      " TouchEnd GestureScrollEnd",
522      ExpectedEvents());
523}
524
525}  // namespace content
526