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