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.
5 #include "ui/events/gesture_detection/gesture_provider.h"
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"
18 // Double-tap drag zoom sensitivity (speed).
19 const float kDoubleTapDragZoomSpeed = 0.005f;
21 const char* GetMotionEventActionName(MotionEvent::Action 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";
33 gfx::RectF GetBoundingBox(const MotionEvent& event) {
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,
45 GestureEventData CreateGesture(const GestureEventDetails& details,
52 size_t touch_point_count,
53 const gfx::RectF& bounding_box) {
54 return GestureEventData(details,
65 GestureEventData CreateGesture(EventType type,
72 size_t touch_point_count,
73 const gfx::RectF& bounding_box) {
74 return GestureEventData(GestureEventDetails(type, 0, 0),
85 GestureEventData CreateGesture(const GestureEventDetails& details,
86 const MotionEvent& event) {
87 return GestureEventData(details,
94 event.GetPointerCount(),
95 GetBoundingBox(event));
98 GestureEventData CreateGesture(EventType type, const MotionEvent& event) {
99 return CreateGesture(GestureEventDetails(type, 0, 0), event);
102 GestureEventDetails CreateTapGestureDetails(EventType type) {
103 // Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be
104 // consistent with double tap behavior on a mobile viewport. See
105 // crbug.com/234986 for context.
106 GestureEventDetails tap_details(type, 1, 0);
112 // GestureProvider:::Config
114 GestureProvider::Config::Config()
115 : display(gfx::Display::kInvalidDisplayID, gfx::Rect(1, 1)),
116 disable_click_delay(false),
117 gesture_begin_end_types_enabled(false),
118 min_gesture_bounds_length(0) {}
120 GestureProvider::Config::~Config() {}
122 // GestureProvider::ScaleGestureListener
124 class GestureProvider::ScaleGestureListenerImpl
125 : public ScaleGestureDetector::ScaleGestureListener {
127 ScaleGestureListenerImpl(const ScaleGestureDetector::Config& config,
128 GestureProvider* provider)
129 : scale_gesture_detector_(config, this),
131 ignore_multitouch_events_(false),
132 pinch_event_sent_(false),
133 min_pinch_update_span_delta_(config.min_pinch_update_span_delta) {}
135 bool OnTouchEvent(const MotionEvent& event) {
136 // 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)) {
147 // ScaleGestureDetector::ScaleGestureListener implementation.
148 virtual bool OnScaleBegin(const ScaleGestureDetector& detector,
149 const MotionEvent& e) OVERRIDE {
150 if (ignore_multitouch_events_ && !detector.InDoubleTapMode())
152 pinch_event_sent_ = false;
156 virtual void OnScaleEnd(const ScaleGestureDetector& detector,
157 const MotionEvent& e) OVERRIDE {
158 if (!pinch_event_sent_)
160 provider_->Send(CreateGesture(ET_GESTURE_PINCH_END, e));
161 pinch_event_sent_ = false;
164 virtual bool OnScale(const ScaleGestureDetector& detector,
165 const MotionEvent& e) OVERRIDE {
166 if (ignore_multitouch_events_ && !detector.InDoubleTapMode())
168 if (!pinch_event_sent_) {
169 pinch_event_sent_ = true;
170 provider_->Send(CreateGesture(ET_GESTURE_PINCH_BEGIN,
172 detector.GetEventTime(),
173 detector.GetFocusX(),
174 detector.GetFocusY(),
175 detector.GetFocusX() + e.GetRawOffsetX(),
176 detector.GetFocusY() + e.GetRawOffsetY(),
181 if (std::abs(detector.GetCurrentSpan() - detector.GetPreviousSpan()) <
182 min_pinch_update_span_delta_) {
186 float scale = detector.GetScaleFactor();
190 if (detector.InDoubleTapMode()) {
191 // Relative changes in the double-tap scale factor computed by |detector|
192 // diminish as the touch moves away from the original double-tap focus.
193 // For historical reasons, Chrome has instead adopted a scale factor
194 // computation that is invariant to the focal distance, where
195 // the scale delta remains constant if the touch velocity is constant.
197 (detector.GetCurrentSpanY() - detector.GetPreviousSpanY()) * 0.5f;
198 scale = std::pow(scale > 1 ? 1.0f + kDoubleTapDragZoomSpeed
199 : 1.0f - kDoubleTapDragZoomSpeed,
202 GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0);
203 provider_->Send(CreateGesture(pinch_details,
205 detector.GetEventTime(),
206 detector.GetFocusX(),
207 detector.GetFocusY(),
208 detector.GetFocusX() + e.GetRawOffsetX(),
209 detector.GetFocusY() + e.GetRawOffsetY(),
215 void SetDoubleTapEnabled(bool enabled) {
216 DCHECK(!IsDoubleTapInProgress());
217 scale_gesture_detector_.SetQuickScaleEnabled(enabled);
220 void SetMultiTouchEnabled(bool enabled) {
221 // Note that returning false from OnScaleBegin / OnScale makes the
222 // gesture detector not to emit further scaling notifications
223 // related to this gesture. Thus, if detector events are enabled in
224 // the middle of the gesture, we don't need to do anything.
225 ignore_multitouch_events_ = !enabled;
228 bool IsDoubleTapInProgress() const {
229 return IsScaleGestureDetectionInProgress() && InDoubleTapMode();
232 bool IsScaleGestureDetectionInProgress() const {
233 return scale_gesture_detector_.IsInProgress();
237 bool InDoubleTapMode() const {
238 return scale_gesture_detector_.InDoubleTapMode();
241 ScaleGestureDetector scale_gesture_detector_;
243 GestureProvider* const provider_;
245 // Completely silence multi-touch (pinch) scaling events. Used in WebView when
246 // zoom support is turned off.
247 bool ignore_multitouch_events_;
249 // Whether any pinch zoom event has been sent to native.
250 bool pinch_event_sent_;
252 // The minimum change in span required before this is considered a pinch. See
254 float min_pinch_update_span_delta_;
256 DISALLOW_COPY_AND_ASSIGN(ScaleGestureListenerImpl);
259 // GestureProvider::GestureListener
261 class GestureProvider::GestureListenerImpl
262 : public GestureDetector::GestureListener,
263 public GestureDetector::DoubleTapListener {
266 const gfx::Display& display,
267 const GestureDetector::Config& gesture_detector_config,
268 bool disable_click_delay,
269 GestureProvider* provider)
270 : gesture_detector_(gesture_detector_config, this, this),
271 snap_scroll_controller_(display),
273 disable_click_delay_(disable_click_delay),
274 touch_slop_(gesture_detector_config.touch_slop),
275 double_tap_timeout_(gesture_detector_config.double_tap_timeout),
276 ignore_single_tap_(false),
277 seen_first_scroll_event_(false) {}
279 virtual ~GestureListenerImpl() {}
281 bool OnTouchEvent(const MotionEvent& e,
282 bool is_scale_gesture_detection_in_progress) {
283 snap_scroll_controller_.SetSnapScrollingMode(
284 e, is_scale_gesture_detection_in_progress);
286 if (is_scale_gesture_detection_in_progress)
287 SetIgnoreSingleTap(true);
289 if (e.GetAction() == MotionEvent::ACTION_DOWN)
290 gesture_detector_.set_longpress_enabled(true);
292 return gesture_detector_.OnTouchEvent(e);
295 // GestureDetector::GestureListener implementation.
296 virtual bool OnDown(const MotionEvent& e) OVERRIDE {
297 current_down_time_ = e.GetEventTime();
298 ignore_single_tap_ = false;
299 seen_first_scroll_event_ = false;
301 GestureEventDetails tap_details(ET_GESTURE_TAP_DOWN, 0, 0);
302 provider_->Send(CreateGesture(tap_details, e));
304 // Return true to indicate that we want to handle touch.
308 virtual bool OnScroll(const MotionEvent& e1,
309 const MotionEvent& e2,
310 float raw_distance_x,
311 float raw_distance_y) OVERRIDE {
312 float distance_x = raw_distance_x;
313 float distance_y = raw_distance_y;
314 if (!seen_first_scroll_event_) {
315 // Remove the touch slop region from the first scroll event to avoid a
317 seen_first_scroll_event_ = true;
319 std::sqrt(distance_x * distance_x + distance_y * distance_y);
320 double epsilon = 1e-3;
321 if (distance > epsilon) {
322 double ratio = std::max(0., distance - touch_slop_) / distance;
327 snap_scroll_controller_.UpdateSnapScrollMode(distance_x, distance_y);
328 if (snap_scroll_controller_.IsSnappingScrolls()) {
329 if (snap_scroll_controller_.IsSnapHorizontal()) {
336 if (!provider_->IsScrollInProgress()) {
337 // Note that scroll start hints are in distance traveled, where
338 // scroll deltas are in the opposite direction.
339 GestureEventDetails scroll_details(
340 ET_GESTURE_SCROLL_BEGIN, -raw_distance_x, -raw_distance_y);
342 // Use the co-ordinates from the touch down, as these co-ordinates are
343 // used to determine which layer the scroll should affect.
344 provider_->Send(CreateGesture(scroll_details,
351 e2.GetPointerCount(),
352 GetBoundingBox(e2)));
355 if (distance_x || distance_y) {
356 const gfx::RectF bounding_box = GetBoundingBox(e2);
357 const gfx::PointF center = bounding_box.CenterPoint();
358 const gfx::PointF raw_center =
359 center + gfx::Vector2dF(e2.GetRawOffsetX(), e2.GetRawOffsetY());
360 GestureEventDetails scroll_details(
361 ET_GESTURE_SCROLL_UPDATE, -distance_x, -distance_y);
362 provider_->Send(CreateGesture(scroll_details,
369 e2.GetPointerCount(),
376 virtual bool OnFling(const MotionEvent& e1,
377 const MotionEvent& e2,
379 float velocity_y) OVERRIDE {
380 if (snap_scroll_controller_.IsSnappingScrolls()) {
381 if (snap_scroll_controller_.IsSnapHorizontal()) {
388 provider_->Fling(e2, velocity_x, velocity_y);
392 virtual bool OnSwipe(const MotionEvent& e1,
393 const MotionEvent& e2,
395 float velocity_y) OVERRIDE {
396 GestureEventDetails swipe_details(ET_GESTURE_SWIPE, velocity_x, velocity_y);
397 provider_->Send(CreateGesture(swipe_details, e2));
401 virtual bool OnTwoFingerTap(const MotionEvent& e1,
402 const MotionEvent& e2) OVERRIDE {
403 // The location of the two finger tap event should be the location of the
405 GestureEventDetails two_finger_tap_details(ET_GESTURE_TWO_FINGER_TAP,
408 provider_->Send(CreateGesture(two_finger_tap_details,
415 e2.GetPointerCount(),
416 GetBoundingBox(e2)));
420 virtual void OnShowPress(const MotionEvent& e) OVERRIDE {
421 GestureEventDetails show_press_details(ET_GESTURE_SHOW_PRESS, 0, 0);
422 provider_->Send(CreateGesture(show_press_details, e));
425 virtual bool OnSingleTapUp(const MotionEvent& e) OVERRIDE {
426 // This is a hack to address the issue where user hovers
427 // over a link for longer than double_tap_timeout_, then
428 // OnSingleTapConfirmed() is not triggered. But we still
429 // want to trigger the tap event at UP. So we override
430 // OnSingleTapUp() in this case. This assumes singleTapUp
431 // gets always called before singleTapConfirmed.
432 if (!ignore_single_tap_) {
433 if (e.GetEventTime() - current_down_time_ > double_tap_timeout_) {
434 return OnSingleTapConfirmed(e);
435 } else if (!IsDoubleTapEnabled() || disable_click_delay_) {
436 // If double-tap has been disabled, there is no need to wait
437 // for the double-tap timeout.
438 return OnSingleTapConfirmed(e);
440 // Notify Blink about this tapUp event anyway, when none of the above
441 // conditions applied.
442 provider_->Send(CreateGesture(
443 CreateTapGestureDetails(ET_GESTURE_TAP_UNCONFIRMED), e));
447 return provider_->SendLongTapIfNecessary(e);
450 // GestureDetector::DoubleTapListener implementation.
451 virtual bool OnSingleTapConfirmed(const MotionEvent& e) OVERRIDE {
452 // Long taps in the edges of the screen have their events delayed by
453 // ContentViewHolder for tab swipe operations. As a consequence of the delay
454 // this method might be called after receiving the up event.
455 // These corner cases should be ignored.
456 if (ignore_single_tap_)
459 ignore_single_tap_ = true;
461 provider_->Send(CreateGesture(CreateTapGestureDetails(ET_GESTURE_TAP), e));
465 virtual bool OnDoubleTap(const MotionEvent& e) OVERRIDE { return false; }
467 virtual bool OnDoubleTapEvent(const MotionEvent& e) OVERRIDE {
468 switch (e.GetAction()) {
469 case MotionEvent::ACTION_DOWN:
470 gesture_detector_.set_longpress_enabled(false);
473 case MotionEvent::ACTION_UP:
474 if (!provider_->IsPinchInProgress() &&
475 !provider_->IsScrollInProgress()) {
477 CreateGesture(CreateTapGestureDetails(ET_GESTURE_DOUBLE_TAP), e));
487 virtual bool OnLongPress(const MotionEvent& e) OVERRIDE {
488 DCHECK(!IsDoubleTapInProgress());
489 SetIgnoreSingleTap(true);
491 GestureEventDetails long_press_details(ET_GESTURE_LONG_PRESS, 0, 0);
492 provider_->Send(CreateGesture(long_press_details, e));
494 // Returning true puts the GestureDetector in "longpress" mode, disabling
495 // further scrolling. This is undesirable, as it is quite common for a
496 // longpress gesture to fire on content that won't trigger a context menu.
500 void SetDoubleTapEnabled(bool enabled) {
501 DCHECK(!IsDoubleTapInProgress());
502 gesture_detector_.SetDoubleTapListener(enabled ? this : NULL);
505 bool IsDoubleTapInProgress() const {
506 return gesture_detector_.is_double_tapping();
510 void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; }
512 bool IsDoubleTapEnabled() const {
513 return gesture_detector_.has_doubletap_listener();
516 GestureDetector gesture_detector_;
517 SnapScrollController snap_scroll_controller_;
519 GestureProvider* const provider_;
521 // Whether the click delay should always be disabled by sending clicks for
522 // double-tap gestures.
523 const bool disable_click_delay_;
525 const float touch_slop_;
527 const base::TimeDelta double_tap_timeout_;
529 base::TimeTicks current_down_time_;
531 // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch,
532 // always_in_tap_region_ is not reset. So when the last finger is up,
533 // OnSingleTapUp() will be mistakenly fired.
534 bool ignore_single_tap_;
536 // Used to remove the touch slop from the initial scroll event in a scroll
538 bool seen_first_scroll_event_;
540 DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl);
545 GestureProvider::GestureProvider(const Config& config,
546 GestureProviderClient* client)
548 touch_scroll_in_progress_(false),
549 pinch_in_progress_(false),
550 double_tap_support_for_page_(true),
551 double_tap_support_for_platform_(true),
552 gesture_begin_end_types_enabled_(config.gesture_begin_end_types_enabled),
553 min_gesture_bounds_length_(config.min_gesture_bounds_length) {
555 InitGestureDetectors(config);
558 GestureProvider::~GestureProvider() {}
560 bool GestureProvider::OnTouchEvent(const MotionEvent& event) {
561 TRACE_EVENT1("input", "GestureProvider::OnTouchEvent",
562 "action", GetMotionEventActionName(event.GetAction()));
564 DCHECK_NE(0u, event.GetPointerCount());
566 if (!CanHandle(event))
569 const bool in_scale_gesture =
570 scale_gesture_listener_->IsScaleGestureDetectionInProgress();
572 OnTouchEventHandlingBegin(event);
573 gesture_listener_->OnTouchEvent(event, in_scale_gesture);
574 scale_gesture_listener_->OnTouchEvent(event);
575 OnTouchEventHandlingEnd(event);
579 void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) {
580 scale_gesture_listener_->SetMultiTouchEnabled(enabled);
583 void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) {
584 if (double_tap_support_for_platform_ == enabled)
586 double_tap_support_for_platform_ = enabled;
587 UpdateDoubleTapDetectionSupport();
590 void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) {
591 if (double_tap_support_for_page_ == enabled)
593 double_tap_support_for_page_ = enabled;
594 UpdateDoubleTapDetectionSupport();
597 bool GestureProvider::IsScrollInProgress() const {
598 // TODO(wangxianzhu): Also return true when fling is active once the UI knows
599 // exactly when the fling ends.
600 return touch_scroll_in_progress_;
603 bool GestureProvider::IsPinchInProgress() const { return pinch_in_progress_; }
605 bool GestureProvider::IsDoubleTapInProgress() const {
606 return gesture_listener_->IsDoubleTapInProgress() ||
607 scale_gesture_listener_->IsDoubleTapInProgress();
610 void GestureProvider::InitGestureDetectors(const Config& config) {
611 TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors");
612 gesture_listener_.reset(
613 new GestureListenerImpl(config.display,
614 config.gesture_detector_config,
615 config.disable_click_delay,
618 scale_gesture_listener_.reset(
619 new ScaleGestureListenerImpl(config.scale_gesture_detector_config, this));
621 UpdateDoubleTapDetectionSupport();
624 bool GestureProvider::CanHandle(const MotionEvent& event) const {
625 return event.GetAction() == MotionEvent::ACTION_DOWN || current_down_event_;
628 void GestureProvider::Fling(const MotionEvent& event,
631 if (!velocity_x && !velocity_y) {
632 EndTouchScrollIfNecessary(event, true);
636 if (!touch_scroll_in_progress_) {
637 // The native side needs a ET_GESTURE_SCROLL_BEGIN before
638 // ET_SCROLL_FLING_START to send the fling to the correct target. Send if it
639 // has not sent. The distance traveled in one second is a reasonable scroll
641 GestureEventDetails scroll_details(
642 ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y);
643 Send(CreateGesture(scroll_details, event));
645 EndTouchScrollIfNecessary(event, false);
647 GestureEventDetails fling_details(
648 ET_SCROLL_FLING_START, velocity_x, velocity_y);
649 Send(CreateGesture(fling_details, event));
652 void GestureProvider::Send(GestureEventData gesture) {
653 DCHECK(!gesture.time.is_null());
654 // The only valid events that should be sent without an active touch sequence
655 // are SHOW_PRESS and TAP, potentially triggered by the double-tap
657 DCHECK(current_down_event_ || gesture.type() == ET_GESTURE_TAP ||
658 gesture.type() == ET_GESTURE_SHOW_PRESS);
660 // TODO(jdduke): Provide a way of skipping this clamping for stylus and/or
661 // mouse-based input, perhaps by exposing the source type on MotionEvent.
662 const gfx::RectF& gesture_bounds = gesture.details.bounding_box_f();
663 gesture.details.set_bounding_box(gfx::RectF(
666 std::max(min_gesture_bounds_length_, gesture_bounds.width()),
667 std::max(min_gesture_bounds_length_, gesture_bounds.height())));
669 switch (gesture.type()) {
670 case ET_GESTURE_LONG_PRESS:
671 DCHECK(!scale_gesture_listener_->IsScaleGestureDetectionInProgress());
672 current_longpress_time_ = gesture.time;
674 case ET_GESTURE_LONG_TAP:
675 current_longpress_time_ = base::TimeTicks();
677 case ET_GESTURE_SCROLL_BEGIN:
678 DCHECK(!touch_scroll_in_progress_);
679 touch_scroll_in_progress_ = true;
681 case ET_GESTURE_SCROLL_END:
682 DCHECK(touch_scroll_in_progress_);
683 if (pinch_in_progress_)
684 Send(GestureEventData(ET_GESTURE_PINCH_END, gesture));
685 touch_scroll_in_progress_ = false;
687 case ET_GESTURE_PINCH_BEGIN:
688 DCHECK(!pinch_in_progress_);
689 if (!touch_scroll_in_progress_)
690 Send(GestureEventData(ET_GESTURE_SCROLL_BEGIN, gesture));
691 pinch_in_progress_ = true;
693 case ET_GESTURE_PINCH_END:
694 DCHECK(pinch_in_progress_);
695 pinch_in_progress_ = false;
697 case ET_GESTURE_SHOW_PRESS:
698 // It's possible that a double-tap drag zoom (from ScaleGestureDetector)
699 // will start before the press gesture fires (from GestureDetector), in
700 // which case the press should simply be dropped.
701 if (pinch_in_progress_ || touch_scroll_in_progress_)
707 client_->OnGestureEvent(gesture);
710 bool GestureProvider::SendLongTapIfNecessary(const MotionEvent& event) {
711 if (event.GetAction() == MotionEvent::ACTION_UP &&
712 !current_longpress_time_.is_null() &&
713 !scale_gesture_listener_->IsScaleGestureDetectionInProgress()) {
714 GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP, 0, 0);
715 Send(CreateGesture(long_tap_details, event));
721 void GestureProvider::EndTouchScrollIfNecessary(const MotionEvent& event,
722 bool send_scroll_end_event) {
723 if (!touch_scroll_in_progress_)
725 if (send_scroll_end_event)
726 Send(CreateGesture(ET_GESTURE_SCROLL_END, event));
727 touch_scroll_in_progress_ = false;
730 void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent& event) {
731 switch (event.GetAction()) {
732 case MotionEvent::ACTION_DOWN:
733 current_down_event_ = event.Clone();
734 touch_scroll_in_progress_ = false;
735 pinch_in_progress_ = false;
736 current_longpress_time_ = base::TimeTicks();
737 if (gesture_begin_end_types_enabled_)
738 Send(CreateGesture(ET_GESTURE_BEGIN, event));
740 case MotionEvent::ACTION_POINTER_DOWN:
741 if (gesture_begin_end_types_enabled_) {
742 const int action_index = event.GetActionIndex();
743 Send(CreateGesture(ET_GESTURE_BEGIN,
745 event.GetEventTime(),
746 event.GetX(action_index),
747 event.GetY(action_index),
748 event.GetRawX(action_index),
749 event.GetRawY(action_index),
750 event.GetPointerCount(),
751 GetBoundingBox(event)));
754 case MotionEvent::ACTION_POINTER_UP:
755 case MotionEvent::ACTION_UP:
756 case MotionEvent::ACTION_CANCEL:
757 case MotionEvent::ACTION_MOVE:
762 void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent& event) {
763 switch (event.GetAction()) {
764 case MotionEvent::ACTION_UP:
765 case MotionEvent::ACTION_CANCEL: {
766 // Note: This call will have no effect if a fling was just generated, as
767 // |Fling()| will have already signalled an end to touch-scrolling.
768 EndTouchScrollIfNecessary(event, true);
770 const gfx::RectF bounding_box = GetBoundingBox(event);
772 if (gesture_begin_end_types_enabled_) {
773 for (size_t i = 0; i < event.GetPointerCount(); ++i) {
774 Send(CreateGesture(ET_GESTURE_END,
776 event.GetEventTime(),
781 event.GetPointerCount() - i,
786 current_down_event_.reset();
788 UpdateDoubleTapDetectionSupport();
791 case MotionEvent::ACTION_POINTER_UP:
792 if (gesture_begin_end_types_enabled_)
793 Send(CreateGesture(ET_GESTURE_END, event));
795 case MotionEvent::ACTION_DOWN:
796 case MotionEvent::ACTION_POINTER_DOWN:
797 case MotionEvent::ACTION_MOVE:
802 void GestureProvider::UpdateDoubleTapDetectionSupport() {
803 // The GestureDetector requires that any provided DoubleTapListener remain
804 // attached to it for the duration of a touch sequence. Defer any potential
805 // null'ing of the listener until the sequence has ended.
806 if (current_down_event_)
809 const bool double_tap_enabled = double_tap_support_for_page_ &&
810 double_tap_support_for_platform_;
811 gesture_listener_->SetDoubleTapEnabled(double_tap_enabled);
812 scale_gesture_listener_->SetDoubleTapEnabled(double_tap_enabled);