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