Home | History | Annotate | Download | only in gesture_detection
      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 void 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 
    495   void SetDoubleTapEnabled(bool enabled) {
    496     DCHECK(!IsDoubleTapInProgress());
    497     gesture_detector_.SetDoubleTapListener(enabled ? this : NULL);
    498   }
    499 
    500   bool IsDoubleTapInProgress() const {
    501     return gesture_detector_.is_double_tapping();
    502   }
    503 
    504  private:
    505   void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; }
    506 
    507   bool IsDoubleTapEnabled() const {
    508     return gesture_detector_.has_doubletap_listener();
    509   }
    510 
    511   GestureDetector gesture_detector_;
    512   SnapScrollController snap_scroll_controller_;
    513 
    514   GestureProvider* const provider_;
    515 
    516   // Whether the click delay should always be disabled by sending clicks for
    517   // double-tap gestures.
    518   const bool disable_click_delay_;
    519 
    520   const float touch_slop_;
    521 
    522   const base::TimeDelta double_tap_timeout_;
    523 
    524   base::TimeTicks current_down_time_;
    525 
    526   // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch,
    527   // always_in_tap_region_ is not reset. So when the last finger is up,
    528   // OnSingleTapUp() will be mistakenly fired.
    529   bool ignore_single_tap_;
    530 
    531   // Used to remove the touch slop from the initial scroll event in a scroll
    532   // gesture.
    533   bool seen_first_scroll_event_;
    534 
    535   DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl);
    536 };
    537 
    538 // GestureProvider
    539 
    540 GestureProvider::GestureProvider(const Config& config,
    541                                  GestureProviderClient* client)
    542     : client_(client),
    543       touch_scroll_in_progress_(false),
    544       pinch_in_progress_(false),
    545       double_tap_support_for_page_(true),
    546       double_tap_support_for_platform_(true),
    547       gesture_begin_end_types_enabled_(config.gesture_begin_end_types_enabled),
    548       min_gesture_bounds_length_(config.min_gesture_bounds_length) {
    549   DCHECK(client);
    550   InitGestureDetectors(config);
    551 }
    552 
    553 GestureProvider::~GestureProvider() {}
    554 
    555 bool GestureProvider::OnTouchEvent(const MotionEvent& event) {
    556   TRACE_EVENT1("input", "GestureProvider::OnTouchEvent",
    557                "action", GetMotionEventActionName(event.GetAction()));
    558 
    559   DCHECK_NE(0u, event.GetPointerCount());
    560 
    561   if (!CanHandle(event))
    562     return false;
    563 
    564   const bool in_scale_gesture =
    565       scale_gesture_listener_->IsScaleGestureDetectionInProgress();
    566 
    567   OnTouchEventHandlingBegin(event);
    568   gesture_listener_->OnTouchEvent(event, in_scale_gesture);
    569   scale_gesture_listener_->OnTouchEvent(event);
    570   OnTouchEventHandlingEnd(event);
    571   return true;
    572 }
    573 
    574 void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) {
    575   scale_gesture_listener_->SetMultiTouchEnabled(enabled);
    576 }
    577 
    578 void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) {
    579   if (double_tap_support_for_platform_ == enabled)
    580     return;
    581   double_tap_support_for_platform_ = enabled;
    582   UpdateDoubleTapDetectionSupport();
    583 }
    584 
    585 void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) {
    586   if (double_tap_support_for_page_ == enabled)
    587     return;
    588   double_tap_support_for_page_ = enabled;
    589   UpdateDoubleTapDetectionSupport();
    590 }
    591 
    592 bool GestureProvider::IsScrollInProgress() const {
    593   // TODO(wangxianzhu): Also return true when fling is active once the UI knows
    594   // exactly when the fling ends.
    595   return touch_scroll_in_progress_;
    596 }
    597 
    598 bool GestureProvider::IsPinchInProgress() const { return pinch_in_progress_; }
    599 
    600 bool GestureProvider::IsDoubleTapInProgress() const {
    601   return gesture_listener_->IsDoubleTapInProgress() ||
    602          scale_gesture_listener_->IsDoubleTapInProgress();
    603 }
    604 
    605 void GestureProvider::InitGestureDetectors(const Config& config) {
    606   TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors");
    607   gesture_listener_.reset(
    608       new GestureListenerImpl(config.display,
    609                               config.gesture_detector_config,
    610                               config.disable_click_delay,
    611                               this));
    612 
    613   scale_gesture_listener_.reset(
    614       new ScaleGestureListenerImpl(config.scale_gesture_detector_config, this));
    615 
    616   UpdateDoubleTapDetectionSupport();
    617 }
    618 
    619 bool GestureProvider::CanHandle(const MotionEvent& event) const {
    620   return event.GetAction() == MotionEvent::ACTION_DOWN || current_down_event_;
    621 }
    622 
    623 void GestureProvider::Fling(const MotionEvent& event,
    624                             float velocity_x,
    625                             float velocity_y) {
    626   if (!velocity_x && !velocity_y) {
    627     EndTouchScrollIfNecessary(event, true);
    628     return;
    629   }
    630 
    631   if (!touch_scroll_in_progress_) {
    632     // The native side needs a ET_GESTURE_SCROLL_BEGIN before
    633     // ET_SCROLL_FLING_START to send the fling to the correct target. Send if it
    634     // has not sent.  The distance traveled in one second is a reasonable scroll
    635     // start hint.
    636     GestureEventDetails scroll_details(
    637         ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y);
    638     Send(CreateGesture(scroll_details, event));
    639   }
    640   EndTouchScrollIfNecessary(event, false);
    641 
    642   GestureEventDetails fling_details(
    643       ET_SCROLL_FLING_START, velocity_x, velocity_y);
    644   Send(CreateGesture(fling_details, event));
    645 }
    646 
    647 void GestureProvider::Send(GestureEventData gesture) {
    648   DCHECK(!gesture.time.is_null());
    649   // The only valid events that should be sent without an active touch sequence
    650   // are SHOW_PRESS and TAP, potentially triggered by the double-tap
    651   // delay timing out.
    652   DCHECK(current_down_event_ || gesture.type() == ET_GESTURE_TAP ||
    653          gesture.type() == ET_GESTURE_SHOW_PRESS);
    654 
    655   // TODO(jdduke): Provide a way of skipping this clamping for stylus and/or
    656   // mouse-based input, perhaps by exposing the source type on MotionEvent.
    657   const gfx::RectF& gesture_bounds = gesture.details.bounding_box_f();
    658   gesture.details.set_bounding_box(gfx::RectF(
    659       gesture_bounds.x(),
    660       gesture_bounds.y(),
    661       std::max(min_gesture_bounds_length_, gesture_bounds.width()),
    662       std::max(min_gesture_bounds_length_, gesture_bounds.height())));
    663 
    664   switch (gesture.type()) {
    665     case ET_GESTURE_LONG_PRESS:
    666       DCHECK(!scale_gesture_listener_->IsScaleGestureDetectionInProgress());
    667       current_longpress_time_ = gesture.time;
    668       break;
    669     case ET_GESTURE_LONG_TAP:
    670       current_longpress_time_ = base::TimeTicks();
    671       break;
    672     case ET_GESTURE_SCROLL_BEGIN:
    673       DCHECK(!touch_scroll_in_progress_);
    674       touch_scroll_in_progress_ = true;
    675       break;
    676     case ET_GESTURE_SCROLL_END:
    677       DCHECK(touch_scroll_in_progress_);
    678       if (pinch_in_progress_)
    679         Send(GestureEventData(ET_GESTURE_PINCH_END, gesture));
    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(GestureEventData(ET_GESTURE_SCROLL_BEGIN, gesture));
    686       pinch_in_progress_ = true;
    687       break;
    688     case ET_GESTURE_PINCH_END:
    689       DCHECK(pinch_in_progress_);
    690       pinch_in_progress_ = false;
    691       break;
    692     case ET_GESTURE_SHOW_PRESS:
    693       // It's possible that a double-tap drag zoom (from ScaleGestureDetector)
    694       // will start before the press gesture fires (from GestureDetector), in
    695       // which case the press should simply be dropped.
    696       if (pinch_in_progress_ || touch_scroll_in_progress_)
    697         return;
    698     default:
    699       break;
    700   };
    701 
    702   client_->OnGestureEvent(gesture);
    703 }
    704 
    705 bool GestureProvider::SendLongTapIfNecessary(const MotionEvent& event) {
    706   if (event.GetAction() == MotionEvent::ACTION_UP &&
    707       !current_longpress_time_.is_null() &&
    708       !scale_gesture_listener_->IsScaleGestureDetectionInProgress()) {
    709     GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP, 0, 0);
    710     Send(CreateGesture(long_tap_details, event));
    711     return true;
    712   }
    713   return false;
    714 }
    715 
    716 void GestureProvider::EndTouchScrollIfNecessary(const MotionEvent& event,
    717                                                 bool send_scroll_end_event) {
    718   if (!touch_scroll_in_progress_)
    719     return;
    720   if (send_scroll_end_event)
    721     Send(CreateGesture(ET_GESTURE_SCROLL_END, event));
    722   touch_scroll_in_progress_ = false;
    723 }
    724 
    725 void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent& event) {
    726   switch (event.GetAction()) {
    727     case MotionEvent::ACTION_DOWN:
    728       current_down_event_ = event.Clone();
    729       touch_scroll_in_progress_ = false;
    730       pinch_in_progress_ = false;
    731       current_longpress_time_ = base::TimeTicks();
    732       if (gesture_begin_end_types_enabled_)
    733         Send(CreateGesture(ET_GESTURE_BEGIN, event));
    734       break;
    735     case MotionEvent::ACTION_POINTER_DOWN:
    736       if (gesture_begin_end_types_enabled_) {
    737         const int action_index = event.GetActionIndex();
    738         Send(CreateGesture(ET_GESTURE_BEGIN,
    739                            event.GetId(),
    740                            event.GetEventTime(),
    741                            event.GetX(action_index),
    742                            event.GetY(action_index),
    743                            event.GetRawX(action_index),
    744                            event.GetRawY(action_index),
    745                            event.GetPointerCount(),
    746                            GetBoundingBox(event)));
    747       }
    748       break;
    749     case MotionEvent::ACTION_POINTER_UP:
    750     case MotionEvent::ACTION_UP:
    751     case MotionEvent::ACTION_CANCEL:
    752     case MotionEvent::ACTION_MOVE:
    753       break;
    754   }
    755 }
    756 
    757 void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent& event) {
    758   switch (event.GetAction()) {
    759     case MotionEvent::ACTION_UP:
    760     case MotionEvent::ACTION_CANCEL: {
    761       // Note: This call will have no effect if a fling was just generated, as
    762       // |Fling()| will have already signalled an end to touch-scrolling.
    763       EndTouchScrollIfNecessary(event, true);
    764 
    765       const gfx::RectF bounding_box = GetBoundingBox(event);
    766 
    767       if (gesture_begin_end_types_enabled_) {
    768         for (size_t i = 0; i < event.GetPointerCount(); ++i) {
    769           Send(CreateGesture(ET_GESTURE_END,
    770                              event.GetId(),
    771                              event.GetEventTime(),
    772                              event.GetX(i),
    773                              event.GetY(i),
    774                              event.GetRawX(i),
    775                              event.GetRawY(i),
    776                              event.GetPointerCount() - i,
    777                              bounding_box));
    778         }
    779       }
    780 
    781       current_down_event_.reset();
    782 
    783       UpdateDoubleTapDetectionSupport();
    784       break;
    785     }
    786     case MotionEvent::ACTION_POINTER_UP:
    787       if (gesture_begin_end_types_enabled_)
    788         Send(CreateGesture(ET_GESTURE_END, event));
    789       break;
    790     case MotionEvent::ACTION_DOWN:
    791     case MotionEvent::ACTION_POINTER_DOWN:
    792     case MotionEvent::ACTION_MOVE:
    793       break;
    794   }
    795 }
    796 
    797 void GestureProvider::UpdateDoubleTapDetectionSupport() {
    798   // The GestureDetector requires that any provided DoubleTapListener remain
    799   // attached to it for the duration of a touch sequence. Defer any potential
    800   // null'ing of the listener until the sequence has ended.
    801   if (current_down_event_)
    802     return;
    803 
    804   const bool double_tap_enabled = double_tap_support_for_page_ &&
    805                                   double_tap_support_for_platform_;
    806   gesture_listener_->SetDoubleTapEnabled(double_tap_enabled);
    807   scale_gesture_listener_->SetDoubleTapEnabled(double_tap_enabled);
    808 }
    809 
    810 }  //  namespace ui
    811