gesture_provider.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
1b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// Copyright 2014 The Chromium Authors. All rights reserved.
2b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// Use of this source code is governed by a BSD-style license that can be
3b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// found in the LICENSE file.
4b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
59ad441ffec97db647fee3725b3424284fb913e14Howard Hinnant#include "ui/events/gesture_detection/gesture_provider.h"
69ad441ffec97db647fee3725b3424284fb913e14Howard Hinnant
7b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include <cmath>
8b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
9b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "base/auto_reset.h"
10b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "base/debug/trace_event.h"
11b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "ui/events/event_constants.h"
12b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "ui/events/gesture_detection/gesture_event_data.h"
13b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "ui/events/gesture_detection/motion_event.h"
14b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
15b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarnamespace ui {
16b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarnamespace {
17b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
182d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines// Double-tap drag zoom sensitivity (speed).
192d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hinesconst float kDoubleTapDragZoomSpeed = 0.005f;
20b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
21b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarconst char* GetMotionEventActionName(MotionEvent::Action action) {
22b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  switch(action) {
23b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar    case MotionEvent::ACTION_POINTER_DOWN: return "ACTION_POINTER_DOWN";
24b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar    case MotionEvent::ACTION_POINTER_UP:   return "ACTION_POINTER_UP";
25b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar    case MotionEvent::ACTION_DOWN:         return "ACTION_DOWN";
26b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar    case MotionEvent::ACTION_UP:           return "ACTION_UP";
27b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar    case MotionEvent::ACTION_CANCEL:       return "ACTION_CANCEL";
28b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar    case MotionEvent::ACTION_MOVE:         return "ACTION_MOVE";
29b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  }
30b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  return "";
31b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar}
32b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
33b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbargfx::RectF GetBoundingBox(const MotionEvent& event) {
34b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  gfx::RectF bounds;
35b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  for (size_t i = 0; i < event.GetPointerCount(); ++i) {
36b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar    float diameter = event.GetTouchMajor(i);
37cff5248a12a36a7225b707b23ac088f5ba214f8aDaniel Dunbar    bounds.Union(gfx::RectF(event.GetX(i) - diameter / 2,
38b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                            event.GetY(i) - diameter / 2,
39b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                            diameter,
40b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                            diameter));
41b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  }
42b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  return bounds;
43b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar}
44b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
45b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel DunbarGestureEventData CreateGesture(EventType type,
46b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                               int motion_event_id,
47b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                               base::TimeTicks time,
48b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                               float x,
49b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                               float y,
502d1fdb26e458c4ddc04155c1d421bced3ba90cd0Stephen Hines                               size_t touch_point_count,
51b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                               const gfx::RectF& bounding_box,
52b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                               const GestureEventDetails& details) {
53b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  return GestureEventData(type,
54b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                          motion_event_id,
55b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                          time,
56b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                          x,
57b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                          y,
58b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                          static_cast<int>(touch_point_count),
59b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                          bounding_box,
60b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                          details);
61b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar}
62b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
63b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel DunbarGestureEventData CreateGesture(EventType type,
64b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                               int motion_event_id,
65b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                               base::TimeTicks time,
66b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                               float x,
67b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                               float y,
68b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                               size_t touch_point_count,
69b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                               const gfx::RectF& bounding_box) {
70b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  return GestureEventData(type,
71b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                          motion_event_id,
72b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                          time,
73b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                          x,
74b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                          y,
75b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                          static_cast<int>(touch_point_count),
76b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                          bounding_box);
77b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar}
78b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
79b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel DunbarGestureEventData CreateGesture(EventType type,
80b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                               const MotionEvent& event,
81b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                               const GestureEventDetails& details) {
82b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  return CreateGesture(type,
83b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                       event.GetId(),
84b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                       event.GetEventTime(),
85b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                       event.GetX(),
86b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                       event.GetY(),
87b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                       event.GetPointerCount(),
88b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                       GetBoundingBox(event),
89b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                       details);
90b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar}
91b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
92b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel DunbarGestureEventData CreateGesture(EventType type,
93b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                               const MotionEvent& event) {
94b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  return CreateGesture(type,
95b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                       event.GetId(),
96b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                       event.GetEventTime(),
97b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                       event.GetX(),
98b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                       event.GetY(),
99b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                       event.GetPointerCount(),
100b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                       GetBoundingBox(event));
101b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar}
102b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
103b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel DunbarGestureEventDetails CreateTapGestureDetails(EventType type,
104b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                                            const MotionEvent& event) {
105b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  // Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be
106b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  // consistent with double tap behavior on a mobile viewport. See
107b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  // crbug.com/234986 for context.
108b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  GestureEventDetails tap_details(type, 1, 0);
109b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  return tap_details;
110b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar}
111b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
112b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar}  // namespace
113b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
114b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// GestureProvider:::Config
115b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
116b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel DunbarGestureProvider::Config::Config()
117b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar    : display(gfx::Display::kInvalidDisplayID, gfx::Rect(1, 1)),
118b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar      disable_click_delay(false),
119b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar      gesture_begin_end_types_enabled(false) {}
120b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
121b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel DunbarGestureProvider::Config::~Config() {}
122b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
123b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// GestureProvider::ScaleGestureListener
124b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
125b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarclass GestureProvider::ScaleGestureListenerImpl
126b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar    : public ScaleGestureDetector::ScaleGestureListener {
127b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar public:
128b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  ScaleGestureListenerImpl(const ScaleGestureDetector::Config& config,
129b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar                           GestureProvider* provider)
130b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar      : scale_gesture_detector_(config, this),
131b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar        provider_(provider),
1327482815716cd9b87931d82dca7298fc3c707229fJoerg Sonnenberger        ignore_multitouch_events_(false),
1337482815716cd9b87931d82dca7298fc3c707229fJoerg Sonnenberger        pinch_event_sent_(false) {}
134b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar
135b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar  bool OnTouchEvent(const MotionEvent& event) {
136b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar    // TODO: Need to deal with multi-touch transition.
137    const bool in_scale_gesture = IsScaleGestureDetectionInProgress();
138    bool handled = scale_gesture_detector_.OnTouchEvent(event);
139    if (!in_scale_gesture &&
140        (event.GetAction() == MotionEvent::ACTION_UP ||
141         event.GetAction() == MotionEvent::ACTION_CANCEL)) {
142      return false;
143    }
144    return handled;
145  }
146
147  // ScaleGestureDetector::ScaleGestureListener implementation.
148  virtual bool OnScaleBegin(const ScaleGestureDetector& detector,
149                            const MotionEvent& e) OVERRIDE {
150    if (ignore_multitouch_events_ && !detector.InDoubleTapMode())
151      return false;
152    pinch_event_sent_ = false;
153    return true;
154  }
155
156  virtual void OnScaleEnd(const ScaleGestureDetector& detector,
157                          const MotionEvent& e) OVERRIDE {
158    if (!pinch_event_sent_)
159      return;
160    provider_->Send(CreateGesture(ET_GESTURE_PINCH_END,
161                                  e.GetId(),
162                                  detector.GetEventTime(),
163                                  0,
164                                  0,
165                                  e.GetPointerCount(),
166                                  GetBoundingBox(e)));
167    pinch_event_sent_ = false;
168  }
169
170  virtual bool OnScale(const ScaleGestureDetector& detector,
171                       const MotionEvent& e) OVERRIDE {
172    if (ignore_multitouch_events_ && !detector.InDoubleTapMode())
173      return false;
174    if (!pinch_event_sent_) {
175      pinch_event_sent_ = true;
176      provider_->Send(CreateGesture(ET_GESTURE_PINCH_BEGIN,
177                                    e.GetId(),
178                                    detector.GetEventTime(),
179                                    detector.GetFocusX(),
180                                    detector.GetFocusY(),
181                                    e.GetPointerCount(),
182                                    GetBoundingBox(e)));
183    }
184
185    float scale = detector.GetScaleFactor();
186    if (scale == 1)
187      return true;
188
189    if (detector.InDoubleTapMode()) {
190      // Relative changes in the double-tap scale factor computed by |detector|
191      // diminish as the touch moves away from the original double-tap focus.
192      // For historical reasons, Chrome has instead adopted a scale factor
193      // computation that is invariant to the focal distance, where
194      // the scale delta remains constant if the touch velocity is constant.
195      float dy =
196          (detector.GetCurrentSpanY() - detector.GetPreviousSpanY()) * 0.5f;
197      scale = std::pow(scale > 1 ? 1.0f + kDoubleTapDragZoomSpeed
198                                 : 1.0f - kDoubleTapDragZoomSpeed,
199                       std::abs(dy));
200    }
201    GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0);
202    provider_->Send(CreateGesture(ET_GESTURE_PINCH_UPDATE,
203                                  e.GetId(),
204                                  detector.GetEventTime(),
205                                  detector.GetFocusX(),
206                                  detector.GetFocusY(),
207                                  e.GetPointerCount(),
208                                  GetBoundingBox(e),
209                                  pinch_details));
210    return true;
211  }
212
213  void SetDoubleTapEnabled(bool enabled) {
214    DCHECK(!IsDoubleTapInProgress());
215    scale_gesture_detector_.SetQuickScaleEnabled(enabled);
216  }
217
218  void SetMultiTouchEnabled(bool enabled) {
219    // Note that returning false from OnScaleBegin / OnScale makes the
220    // gesture detector not to emit further scaling notifications
221    // related to this gesture. Thus, if detector events are enabled in
222    // the middle of the gesture, we don't need to do anything.
223    ignore_multitouch_events_ = !enabled;
224  }
225
226  bool IsDoubleTapInProgress() const {
227    return IsScaleGestureDetectionInProgress() && InDoubleTapMode();
228  }
229
230  bool IsScaleGestureDetectionInProgress() const {
231    return scale_gesture_detector_.IsInProgress();
232  }
233
234 private:
235  bool InDoubleTapMode() const {
236    return scale_gesture_detector_.InDoubleTapMode();
237  }
238
239  ScaleGestureDetector scale_gesture_detector_;
240
241  GestureProvider* const provider_;
242
243  // Completely silence multi-touch (pinch) scaling events. Used in WebView when
244  // zoom support is turned off.
245  bool ignore_multitouch_events_;
246
247  // Whether any pinch zoom event has been sent to native.
248  bool pinch_event_sent_;
249
250  DISALLOW_COPY_AND_ASSIGN(ScaleGestureListenerImpl);
251};
252
253// GestureProvider::GestureListener
254
255class GestureProvider::GestureListenerImpl
256    : public GestureDetector::GestureListener,
257      public GestureDetector::DoubleTapListener {
258 public:
259  GestureListenerImpl(
260      const gfx::Display& display,
261      const GestureDetector::Config& gesture_detector_config,
262      bool disable_click_delay,
263      GestureProvider* provider)
264      : gesture_detector_(gesture_detector_config, this, this),
265        snap_scroll_controller_(display),
266        provider_(provider),
267        disable_click_delay_(disable_click_delay),
268        touch_slop_(gesture_detector_config.touch_slop),
269        double_tap_timeout_(gesture_detector_config.double_tap_timeout),
270        ignore_single_tap_(false),
271        seen_first_scroll_event_(false) {}
272
273  virtual ~GestureListenerImpl() {}
274
275  bool OnTouchEvent(const MotionEvent& e,
276                    bool is_scale_gesture_detection_in_progress) {
277    snap_scroll_controller_.SetSnapScrollingMode(
278        e, is_scale_gesture_detection_in_progress);
279
280    if (is_scale_gesture_detection_in_progress)
281      SetIgnoreSingleTap(true);
282
283    if (e.GetAction() == MotionEvent::ACTION_DOWN)
284      gesture_detector_.set_longpress_enabled(true);
285
286    return gesture_detector_.OnTouchEvent(e);
287  }
288
289  // GestureDetector::GestureListener implementation.
290  virtual bool OnDown(const MotionEvent& e) OVERRIDE {
291    current_down_time_ = e.GetEventTime();
292    ignore_single_tap_ = false;
293    seen_first_scroll_event_ = false;
294
295    GestureEventDetails tap_details(ET_GESTURE_TAP_DOWN, 0, 0);
296    provider_->Send(CreateGesture(ET_GESTURE_TAP_DOWN, e, tap_details));
297
298    // Return true to indicate that we want to handle touch.
299    return true;
300  }
301
302  virtual bool OnScroll(const MotionEvent& e1,
303                        const MotionEvent& e2,
304                        float raw_distance_x,
305                        float raw_distance_y) OVERRIDE {
306    float distance_x = raw_distance_x;
307    float distance_y = raw_distance_y;
308    if (!seen_first_scroll_event_) {
309      // Remove the touch slop region from the first scroll event to avoid a
310      // jump.
311      seen_first_scroll_event_ = true;
312      double distance =
313          std::sqrt(distance_x * distance_x + distance_y * distance_y);
314      double epsilon = 1e-3;
315      if (distance > epsilon) {
316        double ratio = std::max(0., distance - touch_slop_) / distance;
317        distance_x *= ratio;
318        distance_y *= ratio;
319      }
320    }
321    snap_scroll_controller_.UpdateSnapScrollMode(distance_x, distance_y);
322    if (snap_scroll_controller_.IsSnappingScrolls()) {
323      if (snap_scroll_controller_.IsSnapHorizontal()) {
324        distance_y = 0;
325      } else {
326        distance_x = 0;
327      }
328    }
329
330    if (!provider_->IsScrollInProgress()) {
331      // Note that scroll start hints are in distance traveled, where
332      // scroll deltas are in the opposite direction.
333      GestureEventDetails scroll_details(
334          ET_GESTURE_SCROLL_BEGIN, -raw_distance_x, -raw_distance_y);
335
336      // Use the co-ordinates from the touch down, as these co-ordinates are
337      // used to determine which layer the scroll should affect.
338      provider_->Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN,
339                                    e2.GetId(),
340                                    e2.GetEventTime(),
341                                    e1.GetX(),
342                                    e1.GetY(),
343                                    e2.GetPointerCount(),
344                                    GetBoundingBox(e2),
345                                    scroll_details));
346    }
347
348    if (distance_x || distance_y) {
349      GestureEventDetails scroll_details(
350          ET_GESTURE_SCROLL_UPDATE, -distance_x, -distance_y);
351      provider_->Send(
352          CreateGesture(ET_GESTURE_SCROLL_UPDATE, e2, scroll_details));
353    }
354
355    return true;
356  }
357
358  virtual bool OnFling(const MotionEvent& e1,
359                       const MotionEvent& e2,
360                       float velocity_x,
361                       float velocity_y) OVERRIDE {
362    if (snap_scroll_controller_.IsSnappingScrolls()) {
363      if (snap_scroll_controller_.IsSnapHorizontal()) {
364        velocity_y = 0;
365      } else {
366        velocity_x = 0;
367      }
368    }
369
370    provider_->Fling(e2, velocity_x, velocity_y);
371    return true;
372  }
373
374  virtual bool OnSwipe(const MotionEvent& e1,
375                       const MotionEvent& e2,
376                       float velocity_x,
377                       float velocity_y) OVERRIDE {
378    GestureEventDetails swipe_details(
379        ET_GESTURE_MULTIFINGER_SWIPE, velocity_x, velocity_y);
380    provider_->Send(
381        CreateGesture(ET_GESTURE_MULTIFINGER_SWIPE, e2, swipe_details));
382    return true;
383  }
384
385  virtual void OnShowPress(const MotionEvent& e) OVERRIDE {
386    GestureEventDetails show_press_details(ET_GESTURE_SHOW_PRESS, 0, 0);
387    provider_->Send(
388        CreateGesture(ET_GESTURE_SHOW_PRESS, e, show_press_details));
389  }
390
391  virtual bool OnSingleTapUp(const MotionEvent& e) OVERRIDE {
392    // This is a hack to address the issue where user hovers
393    // over a link for longer than double_tap_timeout_, then
394    // OnSingleTapConfirmed() is not triggered. But we still
395    // want to trigger the tap event at UP. So we override
396    // OnSingleTapUp() in this case. This assumes singleTapUp
397    // gets always called before singleTapConfirmed.
398    if (!ignore_single_tap_) {
399      if (e.GetEventTime() - current_down_time_ > double_tap_timeout_) {
400        return OnSingleTapConfirmed(e);
401      } else if (!IsDoubleTapEnabled() || disable_click_delay_) {
402        // If double-tap has been disabled, there is no need to wait
403        // for the double-tap timeout.
404        return OnSingleTapConfirmed(e);
405      } else {
406        // Notify Blink about this tapUp event anyway, when none of the above
407        // conditions applied.
408        provider_->Send(CreateGesture(
409            ET_GESTURE_TAP_UNCONFIRMED,
410            e,
411            CreateTapGestureDetails(ET_GESTURE_TAP_UNCONFIRMED, e)));
412      }
413    }
414
415    return provider_->SendLongTapIfNecessary(e);
416  }
417
418  // GestureDetector::DoubleTapListener implementation.
419  virtual bool OnSingleTapConfirmed(const MotionEvent& e) OVERRIDE {
420    // Long taps in the edges of the screen have their events delayed by
421    // ContentViewHolder for tab swipe operations. As a consequence of the delay
422    // this method might be called after receiving the up event.
423    // These corner cases should be ignored.
424    if (ignore_single_tap_)
425      return true;
426
427    ignore_single_tap_ = true;
428
429    provider_->Send(CreateGesture(
430        ET_GESTURE_TAP, e, CreateTapGestureDetails(ET_GESTURE_TAP, e)));
431    return true;
432  }
433
434  virtual bool OnDoubleTap(const MotionEvent& e) OVERRIDE { return false; }
435
436  virtual bool OnDoubleTapEvent(const MotionEvent& e) OVERRIDE {
437    switch (e.GetAction()) {
438      case MotionEvent::ACTION_DOWN:
439        gesture_detector_.set_longpress_enabled(false);
440        break;
441
442      case MotionEvent::ACTION_UP:
443        if (!provider_->IsPinchInProgress() &&
444            !provider_->IsScrollInProgress()) {
445          provider_->Send(
446              CreateGesture(ET_GESTURE_DOUBLE_TAP,
447                            e,
448                            CreateTapGestureDetails(ET_GESTURE_DOUBLE_TAP, e)));
449          return true;
450        }
451        break;
452      default:
453        break;
454    }
455    return false;
456  }
457
458  virtual bool OnLongPress(const MotionEvent& e) OVERRIDE {
459    DCHECK(!IsDoubleTapInProgress());
460    SetIgnoreSingleTap(true);
461
462    GestureEventDetails long_press_details(ET_GESTURE_LONG_PRESS, 0, 0);
463    provider_->Send(
464        CreateGesture(ET_GESTURE_LONG_PRESS, e, long_press_details));
465
466    // Returning true puts the GestureDetector in "longpress" mode, disabling
467    // further scrolling.  This is undesirable, as it is quite common for a
468    // longpress gesture to fire on content that won't trigger a context menu.
469    return false;
470  }
471
472  void SetDoubleTapEnabled(bool enabled) {
473    DCHECK(!IsDoubleTapInProgress());
474    gesture_detector_.SetDoubleTapListener(enabled ? this : NULL);
475  }
476
477  bool IsDoubleTapInProgress() const {
478    return gesture_detector_.is_double_tapping();
479  }
480
481 private:
482  void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; }
483
484  bool IsDoubleTapEnabled() const {
485    return gesture_detector_.has_doubletap_listener();
486  }
487
488  GestureDetector gesture_detector_;
489  SnapScrollController snap_scroll_controller_;
490
491  GestureProvider* const provider_;
492
493  // Whether the click delay should always be disabled by sending clicks for
494  // double-tap gestures.
495  const bool disable_click_delay_;
496
497  const float touch_slop_;
498
499  const base::TimeDelta double_tap_timeout_;
500
501  base::TimeTicks current_down_time_;
502
503  // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch,
504  // always_in_tap_region_ is not reset. So when the last finger is up,
505  // OnSingleTapUp() will be mistakenly fired.
506  bool ignore_single_tap_;
507
508  // Used to remove the touch slop from the initial scroll event in a scroll
509  // gesture.
510  bool seen_first_scroll_event_;
511
512  DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl);
513};
514
515// GestureProvider
516
517GestureProvider::GestureProvider(const Config& config,
518                                 GestureProviderClient* client)
519    : client_(client),
520      touch_scroll_in_progress_(false),
521      pinch_in_progress_(false),
522      double_tap_support_for_page_(true),
523      double_tap_support_for_platform_(true),
524      gesture_begin_end_types_enabled_(config.gesture_begin_end_types_enabled) {
525  DCHECK(client);
526  InitGestureDetectors(config);
527}
528
529GestureProvider::~GestureProvider() {}
530
531bool GestureProvider::OnTouchEvent(const MotionEvent& event) {
532  TRACE_EVENT1("input", "GestureProvider::OnTouchEvent",
533               "action", GetMotionEventActionName(event.GetAction()));
534
535  DCHECK_NE(0u, event.GetPointerCount());
536
537  if (!CanHandle(event))
538    return false;
539
540  const bool in_scale_gesture =
541      scale_gesture_listener_->IsScaleGestureDetectionInProgress();
542
543  OnTouchEventHandlingBegin(event);
544  gesture_listener_->OnTouchEvent(event, in_scale_gesture);
545  scale_gesture_listener_->OnTouchEvent(event);
546  OnTouchEventHandlingEnd(event);
547  return true;
548}
549
550void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) {
551  scale_gesture_listener_->SetMultiTouchEnabled(enabled);
552}
553
554void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) {
555  if (double_tap_support_for_platform_ == enabled)
556    return;
557  double_tap_support_for_platform_ = enabled;
558  UpdateDoubleTapDetectionSupport();
559}
560
561void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) {
562  if (double_tap_support_for_page_ == enabled)
563    return;
564  double_tap_support_for_page_ = enabled;
565  UpdateDoubleTapDetectionSupport();
566}
567
568bool GestureProvider::IsScrollInProgress() const {
569  // TODO(wangxianzhu): Also return true when fling is active once the UI knows
570  // exactly when the fling ends.
571  return touch_scroll_in_progress_;
572}
573
574bool GestureProvider::IsPinchInProgress() const { return pinch_in_progress_; }
575
576bool GestureProvider::IsDoubleTapInProgress() const {
577  return gesture_listener_->IsDoubleTapInProgress() ||
578         scale_gesture_listener_->IsDoubleTapInProgress();
579}
580
581void GestureProvider::InitGestureDetectors(const Config& config) {
582  TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors");
583  gesture_listener_.reset(
584      new GestureListenerImpl(config.display,
585                              config.gesture_detector_config,
586                              config.disable_click_delay,
587                              this));
588
589  scale_gesture_listener_.reset(
590      new ScaleGestureListenerImpl(config.scale_gesture_detector_config, this));
591
592  UpdateDoubleTapDetectionSupport();
593}
594
595bool GestureProvider::CanHandle(const MotionEvent& event) const {
596  return event.GetAction() == MotionEvent::ACTION_DOWN || current_down_event_;
597}
598
599void GestureProvider::Fling(const MotionEvent& event,
600                            float velocity_x,
601                            float velocity_y) {
602  if (!velocity_x && !velocity_y) {
603    EndTouchScrollIfNecessary(event, true);
604    return;
605  }
606
607  if (!touch_scroll_in_progress_) {
608    // The native side needs a ET_GESTURE_SCROLL_BEGIN before
609    // ET_SCROLL_FLING_START to send the fling to the correct target. Send if it
610    // has not sent.  The distance traveled in one second is a reasonable scroll
611    // start hint.
612    GestureEventDetails scroll_details(
613        ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y);
614    Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN, event, scroll_details));
615  }
616  EndTouchScrollIfNecessary(event, false);
617
618  GestureEventDetails fling_details(
619      ET_SCROLL_FLING_START, velocity_x, velocity_y);
620  Send(CreateGesture(
621      ET_SCROLL_FLING_START, event, fling_details));
622}
623
624void GestureProvider::Send(const GestureEventData& gesture) {
625  DCHECK(!gesture.time.is_null());
626  // The only valid events that should be sent without an active touch sequence
627  // are SHOW_PRESS and TAP, potentially triggered by the double-tap
628  // delay timing out.
629  DCHECK(current_down_event_ || gesture.type == ET_GESTURE_TAP ||
630         gesture.type == ET_GESTURE_SHOW_PRESS);
631
632  switch (gesture.type) {
633    case ET_GESTURE_LONG_PRESS:
634      DCHECK(!scale_gesture_listener_->IsScaleGestureDetectionInProgress());
635      current_longpress_time_ = gesture.time;
636      break;
637    case ET_GESTURE_LONG_TAP:
638      current_longpress_time_ = base::TimeTicks();
639      break;
640    case ET_GESTURE_SCROLL_BEGIN:
641      DCHECK(!touch_scroll_in_progress_);
642      touch_scroll_in_progress_ = true;
643      break;
644    case ET_GESTURE_SCROLL_END:
645      DCHECK(touch_scroll_in_progress_);
646      if (pinch_in_progress_)
647        Send(CreateGesture(ET_GESTURE_PINCH_END,
648                           gesture.motion_event_id,
649                           gesture.time,
650                           gesture.x,
651                           gesture.y,
652                           gesture.details.touch_points(),
653                           gesture.details.bounding_box()));
654      touch_scroll_in_progress_ = false;
655      break;
656    case ET_GESTURE_PINCH_BEGIN:
657      DCHECK(!pinch_in_progress_);
658      if (!touch_scroll_in_progress_)
659        Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN,
660                           gesture.motion_event_id,
661                           gesture.time,
662                           gesture.x,
663                           gesture.y,
664                           gesture.details.touch_points(),
665                           gesture.details.bounding_box()));
666      pinch_in_progress_ = true;
667      break;
668    case ET_GESTURE_PINCH_END:
669      DCHECK(pinch_in_progress_);
670      pinch_in_progress_ = false;
671      break;
672    case ET_GESTURE_SHOW_PRESS:
673      // It's possible that a double-tap drag zoom (from ScaleGestureDetector)
674      // will start before the press gesture fires (from GestureDetector), in
675      // which case the press should simply be dropped.
676      if (pinch_in_progress_ || touch_scroll_in_progress_)
677        return;
678    default:
679      break;
680  };
681
682  client_->OnGestureEvent(gesture);
683}
684
685bool GestureProvider::SendLongTapIfNecessary(const MotionEvent& event) {
686  if (event.GetAction() == MotionEvent::ACTION_UP &&
687      !current_longpress_time_.is_null() &&
688      !scale_gesture_listener_->IsScaleGestureDetectionInProgress()) {
689    GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP, 0, 0);
690    Send(CreateGesture(ET_GESTURE_LONG_TAP, event, long_tap_details));
691    return true;
692  }
693  return false;
694}
695
696void GestureProvider::EndTouchScrollIfNecessary(const MotionEvent& event,
697                                                bool send_scroll_end_event) {
698  if (!touch_scroll_in_progress_)
699    return;
700  if (send_scroll_end_event)
701    Send(CreateGesture(ET_GESTURE_SCROLL_END, event));
702  touch_scroll_in_progress_ = false;
703}
704
705void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent& event) {
706  switch (event.GetAction()) {
707    case MotionEvent::ACTION_DOWN:
708      current_down_event_ = event.Clone();
709      touch_scroll_in_progress_ = false;
710      pinch_in_progress_ = false;
711      current_longpress_time_ = base::TimeTicks();
712      if (gesture_begin_end_types_enabled_)
713        Send(CreateGesture(ET_GESTURE_BEGIN, event));
714      break;
715    case MotionEvent::ACTION_POINTER_DOWN:
716      if (gesture_begin_end_types_enabled_)
717        Send(CreateGesture(ET_GESTURE_BEGIN, event));
718      break;
719    case MotionEvent::ACTION_POINTER_UP:
720    case MotionEvent::ACTION_UP:
721    case MotionEvent::ACTION_CANCEL:
722    case MotionEvent::ACTION_MOVE:
723      break;
724  }
725}
726
727void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent& event) {
728  switch (event.GetAction()) {
729    case MotionEvent::ACTION_UP:
730    case MotionEvent::ACTION_CANCEL:
731      // Note: This call will have no effect if a fling was just generated, as
732      // |Fling()| will have already signalled an end to touch-scrolling.
733      EndTouchScrollIfNecessary(event, true);
734
735      if (gesture_begin_end_types_enabled_)
736        Send(CreateGesture(ET_GESTURE_END, event));
737
738      current_down_event_.reset();
739
740      UpdateDoubleTapDetectionSupport();
741      break;
742    case MotionEvent::ACTION_POINTER_UP:
743      if (gesture_begin_end_types_enabled_)
744        Send(CreateGesture(ET_GESTURE_END, event));
745      break;
746    case MotionEvent::ACTION_DOWN:
747    case MotionEvent::ACTION_POINTER_DOWN:
748    case MotionEvent::ACTION_MOVE:
749      break;
750  }
751}
752
753void GestureProvider::UpdateDoubleTapDetectionSupport() {
754  // The GestureDetector requires that any provided DoubleTapListener remain
755  // attached to it for the duration of a touch sequence. Defer any potential
756  // null'ing of the listener until the sequence has ended.
757  if (current_down_event_)
758    return;
759
760  const bool double_tap_enabled = double_tap_support_for_page_ &&
761                                  double_tap_support_for_platform_;
762  gesture_listener_->SetDoubleTapEnabled(double_tap_enabled);
763  scale_gesture_listener_->SetDoubleTapEnabled(double_tap_enabled);
764}
765
766}  //  namespace ui
767