ca68e3cf917d10775cc4fc1fffa3f1c7969b8422
[platform/framework/web/crosswalk.git] / src / ui / events / gesture_detection / gesture_provider.cc
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
15 namespace ui {
16 namespace {
17
18 // Double-tap drag zoom sensitivity (speed).
19 const float kDoubleTapDragZoomSpeed = 0.005f;
20
21 const 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
33 gfx::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
45 GestureEventData CreateGesture(const GestureEventDetails& details,
46                                int motion_event_id,
47                                base::TimeTicks time,
48                                float x,
49                                float y,
50                                float raw_x,
51                                float raw_y,
52                                size_t touch_point_count,
53                                const gfx::RectF& bounding_box) {
54   return GestureEventData(details,
55                           motion_event_id,
56                           time,
57                           x,
58                           y,
59                           raw_x,
60                           raw_y,
61                           touch_point_count,
62                           bounding_box);
63 }
64
65 GestureEventData CreateGesture(EventType type,
66                                int motion_event_id,
67                                base::TimeTicks time,
68                                float x,
69                                float y,
70                                float raw_x,
71                                float raw_y,
72                                size_t touch_point_count,
73                                const gfx::RectF& bounding_box) {
74   return GestureEventData(GestureEventDetails(type, 0, 0),
75                           motion_event_id,
76                           time,
77                           x,
78                           y,
79                           raw_x,
80                           raw_y,
81                           touch_point_count,
82                           bounding_box);
83 }
84
85 GestureEventData CreateGesture(const GestureEventDetails& details,
86                                const MotionEvent& event) {
87   return GestureEventData(details,
88                           event.GetId(),
89                           event.GetEventTime(),
90                           event.GetX(),
91                           event.GetY(),
92                           event.GetRawX(),
93                           event.GetRawY(),
94                           event.GetPointerCount(),
95                           GetBoundingBox(event));
96 }
97
98 GestureEventData CreateGesture(EventType type, const MotionEvent& event) {
99   return CreateGesture(GestureEventDetails(type, 0, 0), event);
100 }
101
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);
107   return tap_details;
108 }
109
110 }  // namespace
111
112 // GestureProvider:::Config
113
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) {}
119
120 GestureProvider::Config::~Config() {}
121
122 // GestureProvider::ScaleGestureListener
123
124 class GestureProvider::ScaleGestureListenerImpl
125     : public ScaleGestureDetector::ScaleGestureListener {
126  public:
127   ScaleGestureListenerImpl(const ScaleGestureDetector::Config& config,
128                            GestureProvider* provider)
129       : scale_gesture_detector_(config, this),
130         provider_(provider),
131         ignore_multitouch_events_(false),
132         pinch_event_sent_(false),
133         min_pinch_update_span_delta_(config.min_pinch_update_span_delta) {}
134
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)) {
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, e));
161     pinch_event_sent_ = false;
162   }
163
164   virtual bool OnScale(const ScaleGestureDetector& detector,
165                        const MotionEvent& e) OVERRIDE {
166     if (ignore_multitouch_events_ && !detector.InDoubleTapMode())
167       return false;
168     if (!pinch_event_sent_) {
169       pinch_event_sent_ = true;
170       provider_->Send(CreateGesture(ET_GESTURE_PINCH_BEGIN,
171                                     e.GetId(),
172                                     detector.GetEventTime(),
173                                     detector.GetFocusX(),
174                                     detector.GetFocusY(),
175                                     detector.GetFocusX() + e.GetRawOffsetX(),
176                                     detector.GetFocusY() + e.GetRawOffsetY(),
177                                     e.GetPointerCount(),
178                                     GetBoundingBox(e)));
179     }
180
181     if (std::abs(detector.GetCurrentSpan() - detector.GetPreviousSpan()) <
182         min_pinch_update_span_delta_) {
183       return false;
184     }
185
186     float scale = detector.GetScaleFactor();
187     if (scale == 1)
188       return true;
189
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.
196       float dy =
197           (detector.GetCurrentSpanY() - detector.GetPreviousSpanY()) * 0.5f;
198       scale = std::pow(scale > 1 ? 1.0f + kDoubleTapDragZoomSpeed
199                                  : 1.0f - kDoubleTapDragZoomSpeed,
200                        std::abs(dy));
201     }
202     GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0);
203     provider_->Send(CreateGesture(pinch_details,
204                                   e.GetId(),
205                                   detector.GetEventTime(),
206                                   detector.GetFocusX(),
207                                   detector.GetFocusY(),
208                                   detector.GetFocusX() + e.GetRawOffsetX(),
209                                   detector.GetFocusY() + e.GetRawOffsetY(),
210                                   e.GetPointerCount(),
211                                   GetBoundingBox(e)));
212     return true;
213   }
214
215   void SetDoubleTapEnabled(bool enabled) {
216     DCHECK(!IsDoubleTapInProgress());
217     scale_gesture_detector_.SetQuickScaleEnabled(enabled);
218   }
219
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;
226   }
227
228   bool IsDoubleTapInProgress() const {
229     return IsScaleGestureDetectionInProgress() && InDoubleTapMode();
230   }
231
232   bool IsScaleGestureDetectionInProgress() const {
233     return scale_gesture_detector_.IsInProgress();
234   }
235
236  private:
237   bool InDoubleTapMode() const {
238     return scale_gesture_detector_.InDoubleTapMode();
239   }
240
241   ScaleGestureDetector scale_gesture_detector_;
242
243   GestureProvider* const provider_;
244
245   // Completely silence multi-touch (pinch) scaling events. Used in WebView when
246   // zoom support is turned off.
247   bool ignore_multitouch_events_;
248
249   // Whether any pinch zoom event has been sent to native.
250   bool pinch_event_sent_;
251
252   // The minimum change in span required before this is considered a pinch. See
253   // crbug.com/373318.
254   float min_pinch_update_span_delta_;
255
256   DISALLOW_COPY_AND_ASSIGN(ScaleGestureListenerImpl);
257 };
258
259 // GestureProvider::GestureListener
260
261 class GestureProvider::GestureListenerImpl
262     : public GestureDetector::GestureListener,
263       public GestureDetector::DoubleTapListener {
264  public:
265   GestureListenerImpl(
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),
272         provider_(provider),
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) {}
278
279   virtual ~GestureListenerImpl() {}
280
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);
285
286     if (is_scale_gesture_detection_in_progress)
287       SetIgnoreSingleTap(true);
288
289     if (e.GetAction() == MotionEvent::ACTION_DOWN)
290       gesture_detector_.set_longpress_enabled(true);
291
292     return gesture_detector_.OnTouchEvent(e);
293   }
294
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;
300
301     GestureEventDetails tap_details(ET_GESTURE_TAP_DOWN, 0, 0);
302     provider_->Send(CreateGesture(tap_details, e));
303
304     // Return true to indicate that we want to handle touch.
305     return true;
306   }
307
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
316       // jump.
317       seen_first_scroll_event_ = true;
318       double distance =
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;
323         distance_x *= ratio;
324         distance_y *= ratio;
325       }
326     }
327     snap_scroll_controller_.UpdateSnapScrollMode(distance_x, distance_y);
328     if (snap_scroll_controller_.IsSnappingScrolls()) {
329       if (snap_scroll_controller_.IsSnapHorizontal()) {
330         distance_y = 0;
331       } else {
332         distance_x = 0;
333       }
334     }
335
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);
341
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,
345                                     e2.GetId(),
346                                     e2.GetEventTime(),
347                                     e1.GetX(),
348                                     e1.GetY(),
349                                     e1.GetRawX(),
350                                     e1.GetRawY(),
351                                     e2.GetPointerCount(),
352                                     GetBoundingBox(e2)));
353     }
354
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,
363                                     e2.GetId(),
364                                     e2.GetEventTime(),
365                                     center.x(),
366                                     center.y(),
367                                     raw_center.x(),
368                                     raw_center.y(),
369                                     e2.GetPointerCount(),
370                                     bounding_box));
371     }
372
373     return true;
374   }
375
376   virtual bool OnFling(const MotionEvent& e1,
377                        const MotionEvent& e2,
378                        float velocity_x,
379                        float velocity_y) OVERRIDE {
380     if (snap_scroll_controller_.IsSnappingScrolls()) {
381       if (snap_scroll_controller_.IsSnapHorizontal()) {
382         velocity_y = 0;
383       } else {
384         velocity_x = 0;
385       }
386     }
387
388     provider_->Fling(e2, velocity_x, velocity_y);
389     return true;
390   }
391
392   virtual bool OnSwipe(const MotionEvent& e1,
393                        const MotionEvent& e2,
394                        float velocity_x,
395                        float velocity_y) OVERRIDE {
396     GestureEventDetails swipe_details(ET_GESTURE_SWIPE, velocity_x, velocity_y);
397     provider_->Send(CreateGesture(swipe_details, e2));
398     return true;
399   }
400
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
404     // primary pointer.
405     GestureEventDetails two_finger_tap_details(ET_GESTURE_TWO_FINGER_TAP,
406                                                e1.GetTouchMajor(),
407                                                e1.GetTouchMajor());
408     provider_->Send(CreateGesture(two_finger_tap_details,
409                                   e2.GetId(),
410                                   e2.GetEventTime(),
411                                   e1.GetX(),
412                                   e1.GetY(),
413                                   e1.GetRawX(),
414                                   e1.GetRawY(),
415                                   e2.GetPointerCount(),
416                                   GetBoundingBox(e2)));
417     return true;
418   }
419
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));
423   }
424
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);
439       } else {
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));
444       }
445     }
446
447     return provider_->SendLongTapIfNecessary(e);
448   }
449
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_)
457       return true;
458
459     ignore_single_tap_ = true;
460
461     provider_->Send(CreateGesture(CreateTapGestureDetails(ET_GESTURE_TAP), e));
462     return true;
463   }
464
465   virtual bool OnDoubleTap(const MotionEvent& e) OVERRIDE { return false; }
466
467   virtual bool OnDoubleTapEvent(const MotionEvent& e) OVERRIDE {
468     switch (e.GetAction()) {
469       case MotionEvent::ACTION_DOWN:
470         gesture_detector_.set_longpress_enabled(false);
471         break;
472
473       case MotionEvent::ACTION_UP:
474         if (!provider_->IsPinchInProgress() &&
475             !provider_->IsScrollInProgress()) {
476           provider_->Send(
477               CreateGesture(CreateTapGestureDetails(ET_GESTURE_DOUBLE_TAP), e));
478           return true;
479         }
480         break;
481       default:
482         break;
483     }
484     return false;
485   }
486
487   virtual bool OnLongPress(const MotionEvent& e) OVERRIDE {
488     DCHECK(!IsDoubleTapInProgress());
489     SetIgnoreSingleTap(true);
490
491     GestureEventDetails long_press_details(ET_GESTURE_LONG_PRESS, 0, 0);
492     provider_->Send(CreateGesture(long_press_details, e));
493
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.
497     return false;
498   }
499
500   void SetDoubleTapEnabled(bool enabled) {
501     DCHECK(!IsDoubleTapInProgress());
502     gesture_detector_.SetDoubleTapListener(enabled ? this : NULL);
503   }
504
505   bool IsDoubleTapInProgress() const {
506     return gesture_detector_.is_double_tapping();
507   }
508
509  private:
510   void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; }
511
512   bool IsDoubleTapEnabled() const {
513     return gesture_detector_.has_doubletap_listener();
514   }
515
516   GestureDetector gesture_detector_;
517   SnapScrollController snap_scroll_controller_;
518
519   GestureProvider* const provider_;
520
521   // Whether the click delay should always be disabled by sending clicks for
522   // double-tap gestures.
523   const bool disable_click_delay_;
524
525   const float touch_slop_;
526
527   const base::TimeDelta double_tap_timeout_;
528
529   base::TimeTicks current_down_time_;
530
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_;
535
536   // Used to remove the touch slop from the initial scroll event in a scroll
537   // gesture.
538   bool seen_first_scroll_event_;
539
540   DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl);
541 };
542
543 // GestureProvider
544
545 GestureProvider::GestureProvider(const Config& config,
546                                  GestureProviderClient* client)
547     : client_(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) {
554   DCHECK(client);
555   InitGestureDetectors(config);
556 }
557
558 GestureProvider::~GestureProvider() {}
559
560 bool GestureProvider::OnTouchEvent(const MotionEvent& event) {
561   TRACE_EVENT1("input", "GestureProvider::OnTouchEvent",
562                "action", GetMotionEventActionName(event.GetAction()));
563
564   DCHECK_NE(0u, event.GetPointerCount());
565
566   if (!CanHandle(event))
567     return false;
568
569   const bool in_scale_gesture =
570       scale_gesture_listener_->IsScaleGestureDetectionInProgress();
571
572   OnTouchEventHandlingBegin(event);
573   gesture_listener_->OnTouchEvent(event, in_scale_gesture);
574   scale_gesture_listener_->OnTouchEvent(event);
575   OnTouchEventHandlingEnd(event);
576   return true;
577 }
578
579 void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) {
580   scale_gesture_listener_->SetMultiTouchEnabled(enabled);
581 }
582
583 void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) {
584   if (double_tap_support_for_platform_ == enabled)
585     return;
586   double_tap_support_for_platform_ = enabled;
587   UpdateDoubleTapDetectionSupport();
588 }
589
590 void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) {
591   if (double_tap_support_for_page_ == enabled)
592     return;
593   double_tap_support_for_page_ = enabled;
594   UpdateDoubleTapDetectionSupport();
595 }
596
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_;
601 }
602
603 bool GestureProvider::IsPinchInProgress() const { return pinch_in_progress_; }
604
605 bool GestureProvider::IsDoubleTapInProgress() const {
606   return gesture_listener_->IsDoubleTapInProgress() ||
607          scale_gesture_listener_->IsDoubleTapInProgress();
608 }
609
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,
616                               this));
617
618   scale_gesture_listener_.reset(
619       new ScaleGestureListenerImpl(config.scale_gesture_detector_config, this));
620
621   UpdateDoubleTapDetectionSupport();
622 }
623
624 bool GestureProvider::CanHandle(const MotionEvent& event) const {
625   return event.GetAction() == MotionEvent::ACTION_DOWN || current_down_event_;
626 }
627
628 void GestureProvider::Fling(const MotionEvent& event,
629                             float velocity_x,
630                             float velocity_y) {
631   if (!velocity_x && !velocity_y) {
632     EndTouchScrollIfNecessary(event, true);
633     return;
634   }
635
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
640     // start hint.
641     GestureEventDetails scroll_details(
642         ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y);
643     Send(CreateGesture(scroll_details, event));
644   }
645   EndTouchScrollIfNecessary(event, false);
646
647   GestureEventDetails fling_details(
648       ET_SCROLL_FLING_START, velocity_x, velocity_y);
649   Send(CreateGesture(fling_details, event));
650 }
651
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
656   // delay timing out.
657   DCHECK(current_down_event_ || gesture.type() == ET_GESTURE_TAP ||
658          gesture.type() == ET_GESTURE_SHOW_PRESS);
659
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(
664       gesture_bounds.x(),
665       gesture_bounds.y(),
666       std::max(min_gesture_bounds_length_, gesture_bounds.width()),
667       std::max(min_gesture_bounds_length_, gesture_bounds.height())));
668
669   switch (gesture.type()) {
670     case ET_GESTURE_LONG_PRESS:
671       DCHECK(!scale_gesture_listener_->IsScaleGestureDetectionInProgress());
672       current_longpress_time_ = gesture.time;
673       break;
674     case ET_GESTURE_LONG_TAP:
675       current_longpress_time_ = base::TimeTicks();
676       break;
677     case ET_GESTURE_SCROLL_BEGIN:
678       DCHECK(!touch_scroll_in_progress_);
679       touch_scroll_in_progress_ = true;
680       break;
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;
686       break;
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;
692       break;
693     case ET_GESTURE_PINCH_END:
694       DCHECK(pinch_in_progress_);
695       pinch_in_progress_ = false;
696       break;
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_)
702         return;
703     default:
704       break;
705   };
706
707   client_->OnGestureEvent(gesture);
708 }
709
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));
716     return true;
717   }
718   return false;
719 }
720
721 void GestureProvider::EndTouchScrollIfNecessary(const MotionEvent& event,
722                                                 bool send_scroll_end_event) {
723   if (!touch_scroll_in_progress_)
724     return;
725   if (send_scroll_end_event)
726     Send(CreateGesture(ET_GESTURE_SCROLL_END, event));
727   touch_scroll_in_progress_ = false;
728 }
729
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));
739       break;
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,
744                            event.GetId(),
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)));
752       }
753       break;
754     case MotionEvent::ACTION_POINTER_UP:
755     case MotionEvent::ACTION_UP:
756     case MotionEvent::ACTION_CANCEL:
757     case MotionEvent::ACTION_MOVE:
758       break;
759   }
760 }
761
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);
769
770       const gfx::RectF bounding_box = GetBoundingBox(event);
771
772       if (gesture_begin_end_types_enabled_) {
773         for (size_t i = 0; i < event.GetPointerCount(); ++i) {
774           Send(CreateGesture(ET_GESTURE_END,
775                              event.GetId(),
776                              event.GetEventTime(),
777                              event.GetX(i),
778                              event.GetY(i),
779                              event.GetRawX(i),
780                              event.GetRawY(i),
781                              event.GetPointerCount() - i,
782                              bounding_box));
783         }
784       }
785
786       current_down_event_.reset();
787
788       UpdateDoubleTapDetectionSupport();
789       break;
790     }
791     case MotionEvent::ACTION_POINTER_UP:
792       if (gesture_begin_end_types_enabled_)
793         Send(CreateGesture(ET_GESTURE_END, event));
794       break;
795     case MotionEvent::ACTION_DOWN:
796     case MotionEvent::ACTION_POINTER_DOWN:
797     case MotionEvent::ACTION_MOVE:
798       break;
799   }
800 }
801
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_)
807     return;
808
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);
813 }
814
815 }  //  namespace ui