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