Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / events / gesture_detection / gesture_provider.cc
index 4deeba2..21a6b1e 100644 (file)
@@ -30,35 +30,74 @@ const char* GetMotionEventActionName(MotionEvent::Action action) {
   return "";
 }
 
+gfx::RectF GetBoundingBox(const MotionEvent& event) {
+  gfx::RectF bounds;
+  for (size_t i = 0; i < event.GetPointerCount(); ++i) {
+    float diameter = event.GetTouchMajor(i);
+    bounds.Union(gfx::RectF(event.GetX(i) - diameter / 2,
+                            event.GetY(i) - diameter / 2,
+                            diameter,
+                            diameter));
+  }
+  return bounds;
+}
+
 GestureEventData CreateGesture(EventType type,
+                               int motion_event_id,
                                base::TimeTicks time,
                                float x,
                                float y,
+                               size_t touch_point_count,
+                               const gfx::RectF& bounding_box,
                                const GestureEventDetails& details) {
-  return GestureEventData(type, time, x, y, details);
+  return GestureEventData(type,
+                          motion_event_id,
+                          time,
+                          x,
+                          y,
+                          static_cast<int>(touch_point_count),
+                          bounding_box,
+                          details);
 }
 
 GestureEventData CreateGesture(EventType type,
+                               int motion_event_id,
                                base::TimeTicks time,
                                float x,
-                               float y) {
-  return GestureEventData(type, time, x, y);
-  }
+                               float y,
+                               size_t touch_point_count,
+                               const gfx::RectF& bounding_box) {
+  return GestureEventData(type,
+                          motion_event_id,
+                          time,
+                          x,
+                          y,
+                          static_cast<int>(touch_point_count),
+                          bounding_box);
+}
 
 GestureEventData CreateGesture(EventType type,
                                const MotionEvent& event,
                                const GestureEventDetails& details) {
-  return CreateGesture(
-      type, event.GetEventTime(), event.GetX(), event.GetY(), details);
+  return CreateGesture(type,
+                       event.GetId(),
+                       event.GetEventTime(),
+                       event.GetX(),
+                       event.GetY(),
+                       event.GetPointerCount(),
+                       GetBoundingBox(event),
+                       details);
 }
 
 GestureEventData CreateGesture(EventType type,
                                const MotionEvent& event) {
-  return CreateGesture(type, event.GetEventTime(), event.GetX(), event.GetY());
-}
-
-float Round(float f) {
-  return (f > 0.f) ? std::floor(f + 0.5f) : std::ceil(f - 0.5f);
+  return CreateGesture(type,
+                       event.GetId(),
+                       event.GetEventTime(),
+                       event.GetX(),
+                       event.GetY(),
+                       event.GetPointerCount(),
+                       GetBoundingBox(event));
 }
 
 GestureEventDetails CreateTapGestureDetails(EventType type,
@@ -67,8 +106,6 @@ GestureEventDetails CreateTapGestureDetails(EventType type,
   // consistent with double tap behavior on a mobile viewport. See
   // crbug.com/234986 for context.
   GestureEventDetails tap_details(type, 1, 0);
-  tap_details.set_bounding_box(
-      gfx::RectF(event.GetTouchMajor(), event.GetTouchMajor()));
   return tap_details;
 }
 
@@ -76,7 +113,10 @@ GestureEventDetails CreateTapGestureDetails(EventType type,
 
 // GestureProvider:::Config
 
-GestureProvider::Config::Config() : disable_click_delay(false) {}
+GestureProvider::Config::Config()
+    : display(gfx::Display::kInvalidDisplayID, gfx::Rect(1, 1)),
+      disable_click_delay(false),
+      gesture_begin_end_types_enabled(false) {}
 
 GestureProvider::Config::~Config() {}
 
@@ -89,7 +129,7 @@ class GestureProvider::ScaleGestureListenerImpl
                            GestureProvider* provider)
       : scale_gesture_detector_(config, this),
         provider_(provider),
-        ignore_detector_events_(false),
+        ignore_multitouch_events_(false),
         pinch_event_sent_(false) {}
 
   bool OnTouchEvent(const MotionEvent& event) {
@@ -105,61 +145,104 @@ class GestureProvider::ScaleGestureListenerImpl
   }
 
   // ScaleGestureDetector::ScaleGestureListener implementation.
-  virtual bool OnScaleBegin(const ScaleGestureDetector& detector) OVERRIDE {
-    if (ignore_detector_events_)
+  virtual bool OnScaleBegin(const ScaleGestureDetector& detector,
+                            const MotionEvent& e) OVERRIDE {
+    if (ignore_multitouch_events_ && !detector.InDoubleTapMode())
       return false;
     pinch_event_sent_ = false;
     return true;
   }
 
-  virtual void OnScaleEnd(const ScaleGestureDetector& detector) OVERRIDE {
+  virtual void OnScaleEnd(const ScaleGestureDetector& detector,
+                          const MotionEvent& e) OVERRIDE {
     if (!pinch_event_sent_)
       return;
-    provider_->Send(
-        CreateGesture(ET_GESTURE_PINCH_END, detector.GetEventTime(), 0, 0));
+    provider_->Send(CreateGesture(ET_GESTURE_PINCH_END,
+                                  e.GetId(),
+                                  detector.GetEventTime(),
+                                  0,
+                                  0,
+                                  e.GetPointerCount(),
+                                  GetBoundingBox(e)));
     pinch_event_sent_ = false;
   }
 
-  virtual bool OnScale(const ScaleGestureDetector& detector) OVERRIDE {
-    if (ignore_detector_events_)
+  virtual bool OnScale(const ScaleGestureDetector& detector,
+                       const MotionEvent& e) OVERRIDE {
+    if (ignore_multitouch_events_ && !detector.InDoubleTapMode())
       return false;
     if (!pinch_event_sent_) {
       pinch_event_sent_ = true;
       provider_->Send(CreateGesture(ET_GESTURE_PINCH_BEGIN,
+                                    e.GetId(),
                                     detector.GetEventTime(),
                                     detector.GetFocusX(),
-                                    detector.GetFocusY()));
+                                    detector.GetFocusY(),
+                                    e.GetPointerCount(),
+                                    GetBoundingBox(e)));
+    }
+
+    float scale = detector.GetScaleFactor();
+    if (scale == 1)
+      return true;
+
+    if (detector.InDoubleTapMode()) {
+      // Relative changes in the double-tap scale factor computed by |detector|
+      // diminish as the touch moves away from the original double-tap focus.
+      // For historical reasons, Chrome has instead adopted a scale factor
+      // computation that is invariant to the focal distance, where
+      // the scale delta remains constant if the touch velocity is constant.
+      float dy =
+          (detector.GetCurrentSpanY() - detector.GetPreviousSpanY()) * 0.5f;
+      scale = std::pow(scale > 1 ? 1.0f + kDoubleTapDragZoomSpeed
+                                 : 1.0f - kDoubleTapDragZoomSpeed,
+                       std::abs(dy));
     }
-    GestureEventDetails pinch_details(
-        ET_GESTURE_PINCH_UPDATE, detector.GetScaleFactor(), 0);
+    GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0);
     provider_->Send(CreateGesture(ET_GESTURE_PINCH_UPDATE,
+                                  e.GetId(),
                                   detector.GetEventTime(),
                                   detector.GetFocusX(),
                                   detector.GetFocusY(),
+                                  e.GetPointerCount(),
+                                  GetBoundingBox(e),
                                   pinch_details));
     return true;
   }
 
-  bool IsScaleGestureDetectionInProgress() const {
-    return !ignore_detector_events_ && scale_gesture_detector_.IsInProgress();
+  void SetDoubleTapEnabled(bool enabled) {
+    DCHECK(!IsDoubleTapInProgress());
+    scale_gesture_detector_.SetQuickScaleEnabled(enabled);
   }
 
-  void set_ignore_detector_events(bool value) {
+  void SetMultiTouchEnabled(bool enabled) {
     // Note that returning false from OnScaleBegin / OnScale makes the
     // gesture detector not to emit further scaling notifications
     // related to this gesture. Thus, if detector events are enabled in
     // the middle of the gesture, we don't need to do anything.
-    ignore_detector_events_ = value;
+    ignore_multitouch_events_ = !enabled;
+  }
+
+  bool IsDoubleTapInProgress() const {
+    return IsScaleGestureDetectionInProgress() && InDoubleTapMode();
+  }
+
+  bool IsScaleGestureDetectionInProgress() const {
+    return scale_gesture_detector_.IsInProgress();
   }
 
  private:
+  bool InDoubleTapMode() const {
+    return scale_gesture_detector_.InDoubleTapMode();
+  }
+
   ScaleGestureDetector scale_gesture_detector_;
 
   GestureProvider* const provider_;
 
-  // Completely silence scaling events. Used in WebView when zoom support
-  // is turned off.
-  bool ignore_detector_events_;
+  // Completely silence multi-touch (pinch) scaling events. Used in WebView when
+  // zoom support is turned off.
+  bool ignore_multitouch_events_;
 
   // Whether any pinch zoom event has been sent to native.
   bool pinch_event_sent_;
@@ -174,31 +257,18 @@ class GestureProvider::GestureListenerImpl
       public GestureDetector::DoubleTapListener {
  public:
   GestureListenerImpl(
+      const gfx::Display& display,
       const GestureDetector::Config& gesture_detector_config,
-      const SnapScrollController::Config& snap_scroll_controller_config,
       bool disable_click_delay,
       GestureProvider* provider)
       : gesture_detector_(gesture_detector_config, this, this),
-        snap_scroll_controller_(snap_scroll_controller_config),
+        snap_scroll_controller_(display),
         provider_(provider),
-        px_to_dp_(1.0f / snap_scroll_controller_config.device_scale_factor),
         disable_click_delay_(disable_click_delay),
-        scaled_touch_slop_(gesture_detector_config.scaled_touch_slop),
-        scaled_touch_slop_square_(scaled_touch_slop_ * scaled_touch_slop_),
+        touch_slop_(gesture_detector_config.touch_slop),
         double_tap_timeout_(gesture_detector_config.double_tap_timeout),
         ignore_single_tap_(false),
-        seen_first_scroll_event_(false),
-        double_tap_mode_(DOUBLE_TAP_MODE_NONE),
-        double_tap_y_(0),
-        double_tap_support_enabled_(true),
-        double_tap_drag_zoom_anchor_x_(0),
-        double_tap_drag_zoom_anchor_y_(0),
-        last_raw_x_(0),
-        last_raw_y_(0),
-        accumulated_scroll_error_x_(0),
-        accumulated_scroll_error_y_(0) {
-    UpdateDoubleTapListener();
-  }
+        seen_first_scroll_event_(false) {}
 
   virtual ~GestureListenerImpl() {}
 
@@ -210,12 +280,8 @@ class GestureProvider::GestureListenerImpl
     if (is_scale_gesture_detection_in_progress)
       SetIgnoreSingleTap(true);
 
-    if (e.GetAction() == MotionEvent::ACTION_POINTER_DOWN ||
-        e.GetAction() == MotionEvent::ACTION_CANCEL) {
-      EndDoubleTapDragIfNecessary(e);
-    } else if (e.GetAction() == MotionEvent::ACTION_DOWN) {
-      gesture_detector_.set_is_longpress_enabled(true);
-    }
+    if (e.GetAction() == MotionEvent::ACTION_DOWN)
+      gesture_detector_.set_longpress_enabled(true);
 
     return gesture_detector_.OnTouchEvent(e);
   }
@@ -225,14 +291,8 @@ class GestureProvider::GestureListenerImpl
     current_down_time_ = e.GetEventTime();
     ignore_single_tap_ = false;
     seen_first_scroll_event_ = false;
-    last_raw_x_ = e.GetRawX();
-    last_raw_y_ = e.GetRawY();
-    accumulated_scroll_error_x_ = 0;
-    accumulated_scroll_error_y_ = 0;
 
     GestureEventDetails tap_details(ET_GESTURE_TAP_DOWN, 0, 0);
-    tap_details.set_bounding_box(
-        gfx::RectF(e.GetTouchMajor(), e.GetTouchMajor()));
     provider_->Send(CreateGesture(ET_GESTURE_TAP_DOWN, e, tap_details));
 
     // Return true to indicate that we want to handle touch.
@@ -253,7 +313,7 @@ class GestureProvider::GestureListenerImpl
           std::sqrt(distance_x * distance_x + distance_y * distance_y);
       double epsilon = 1e-3;
       if (distance > epsilon) {
-        double ratio = std::max(0., distance - scaled_touch_slop_) / distance;
+        double ratio = std::max(0., distance - touch_slop_) / distance;
         distance_x *= ratio;
         distance_y *= ratio;
       }
@@ -267,34 +327,27 @@ class GestureProvider::GestureListenerImpl
       }
     }
 
-    last_raw_x_ = e2.GetRawX();
-    last_raw_y_ = e2.GetRawY();
     if (!provider_->IsScrollInProgress()) {
       // Note that scroll start hints are in distance traveled, where
       // scroll deltas are in the opposite direction.
       GestureEventDetails scroll_details(
           ET_GESTURE_SCROLL_BEGIN, -raw_distance_x, -raw_distance_y);
+
+      // Use the co-ordinates from the touch down, as these co-ordinates are
+      // used to determine which layer the scroll should affect.
       provider_->Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN,
+                                    e2.GetId(),
                                     e2.GetEventTime(),
                                     e1.GetX(),
                                     e1.GetY(),
+                                    e2.GetPointerCount(),
+                                    GetBoundingBox(e2),
                                     scroll_details));
     }
 
-    // distance_x and distance_y is the scrolling offset since last OnScroll.
-    // Because we are passing integers to Blink, this could introduce
-    // rounding errors. The rounding errors will accumulate overtime.
-    // To solve this, we should be adding back the rounding errors each time
-    // when we calculate the new offset.
-    // TODO(jdduke): Determine if we can simpy use floating point deltas, as
-    // WebGestureEvent also takes floating point deltas for GestureScrollUpdate.
-    int dx = (int)(distance_x + accumulated_scroll_error_x_);
-    int dy = (int)(distance_y + accumulated_scroll_error_y_);
-    accumulated_scroll_error_x_ += (distance_x - dx);
-    accumulated_scroll_error_y_ += (distance_y - dy);
-
-    if (dx || dy) {
-      GestureEventDetails scroll_details(ET_GESTURE_SCROLL_UPDATE, -dx, -dy);
+    if (distance_x || distance_y) {
+      GestureEventDetails scroll_details(
+          ET_GESTURE_SCROLL_UPDATE, -distance_x, -distance_y);
       provider_->Send(
           CreateGesture(ET_GESTURE_SCROLL_UPDATE, e2, scroll_details));
     }
@@ -314,26 +367,28 @@ class GestureProvider::GestureListenerImpl
       }
     }
 
-    provider_->Fling(
-        e2.GetEventTime(), e1.GetX(), e1.GetY(), velocity_x, velocity_y);
+    provider_->Fling(e2, velocity_x, velocity_y);
+    return true;
+  }
+
+  virtual bool OnSwipe(const MotionEvent& e1,
+                       const MotionEvent& e2,
+                       float velocity_x,
+                       float velocity_y) OVERRIDE {
+    GestureEventDetails swipe_details(
+        ET_GESTURE_MULTIFINGER_SWIPE, velocity_x, velocity_y);
+    provider_->Send(
+        CreateGesture(ET_GESTURE_MULTIFINGER_SWIPE, e2, swipe_details));
     return true;
   }
 
   virtual void OnShowPress(const MotionEvent& e) OVERRIDE {
     GestureEventDetails show_press_details(ET_GESTURE_SHOW_PRESS, 0, 0);
-    // TODO(jdduke): Expose minor axis length and rotation in |MotionEvent|.
-    show_press_details.set_bounding_box(
-        gfx::RectF(e.GetTouchMajor(), e.GetTouchMajor()));
     provider_->Send(
         CreateGesture(ET_GESTURE_SHOW_PRESS, e, show_press_details));
   }
 
   virtual bool OnSingleTapUp(const MotionEvent& e) OVERRIDE {
-    if (IsPointOutsideCurrentSlopRegion(e.GetRawX(), e.GetRawY())) {
-      provider_->SendTapCancelIfNecessary(e);
-      ignore_single_tap_ = true;
-      return true;
-    }
     // This is a hack to address the issue where user hovers
     // over a link for longer than double_tap_timeout_, then
     // OnSingleTapConfirmed() is not triggered. But we still
@@ -343,7 +398,7 @@ class GestureProvider::GestureListenerImpl
     if (!ignore_single_tap_) {
       if (e.GetEventTime() - current_down_time_ > double_tap_timeout_) {
         return OnSingleTapConfirmed(e);
-      } else if (IsDoubleTapDisabled() || disable_click_delay_) {
+      } else if (!IsDoubleTapEnabled() || disable_click_delay_) {
         // If double-tap has been disabled, there is no need to wait
         // for the double-tap timeout.
         return OnSingleTapConfirmed(e);
@@ -381,71 +436,23 @@ class GestureProvider::GestureListenerImpl
   virtual bool OnDoubleTapEvent(const MotionEvent& e) OVERRIDE {
     switch (e.GetAction()) {
       case MotionEvent::ACTION_DOWN:
-        // Note that this will be called before the corresponding |onDown()|
-        // of the same ACTION_DOWN event.  Thus, the preceding TAP_DOWN
-        // should be cancelled prior to sending a new one (in |onDown()|).
-        double_tap_drag_zoom_anchor_x_ = e.GetX();
-        double_tap_drag_zoom_anchor_y_ = e.GetY();
-        double_tap_mode_ = DOUBLE_TAP_MODE_DRAG_DETECTION_IN_PROGRESS;
-        // If a long-press fires during a double-tap, the GestureDetector
-        // will stop feeding MotionEvents to |onDoubleTapEvent()|,
-        // preventing double-tap drag zoom. Long press detection will be
-        // re-enabled on the next ACTION_DOWN.
-        gesture_detector_.set_is_longpress_enabled(false);
-        break;
-      case MotionEvent::ACTION_MOVE:
-        if (double_tap_mode_ == DOUBLE_TAP_MODE_DRAG_DETECTION_IN_PROGRESS) {
-          float distance_x = double_tap_drag_zoom_anchor_x_ - e.GetX();
-          float distance_y = double_tap_drag_zoom_anchor_y_ - e.GetY();
-
-          // Begin double-tap drag zoom mode if the move distance is
-          // further than the threshold.
-          if (IsDistanceGreaterThanTouchSlop(distance_x, distance_y)) {
-            GestureEventDetails scroll_details(
-                ET_GESTURE_SCROLL_BEGIN, -distance_x, -distance_y);
-            provider_->Send(
-                CreateGesture(ET_GESTURE_SCROLL_BEGIN, e, scroll_details));
-            provider_->Send(
-                CreateGesture(ET_GESTURE_PINCH_BEGIN,
-                              e.GetEventTime(),
-                              Round(double_tap_drag_zoom_anchor_x_),
-                              Round(double_tap_drag_zoom_anchor_y_)));
-            double_tap_mode_ = DOUBLE_TAP_MODE_DRAG_ZOOM;
-          }
-        } else if (double_tap_mode_ == DOUBLE_TAP_MODE_DRAG_ZOOM) {
-          provider_->Send(CreateGesture(ET_GESTURE_SCROLL_UPDATE, e));
-
-          float dy = double_tap_y_ - e.GetY();
-          float scale = std::pow(dy > 0 ? 1.0f - kDoubleTapDragZoomSpeed
-                                        : 1.0f + kDoubleTapDragZoomSpeed,
-                                 std::abs(dy * px_to_dp_));
-          GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0);
-          provider_->Send(CreateGesture(ET_GESTURE_PINCH_UPDATE,
-                                        e.GetEventTime(),
-                                        Round(double_tap_drag_zoom_anchor_x_),
-                                        Round(double_tap_drag_zoom_anchor_y_),
-                                        pinch_details));
-        }
+        gesture_detector_.set_longpress_enabled(false);
         break;
+
       case MotionEvent::ACTION_UP:
-        if (double_tap_mode_ != DOUBLE_TAP_MODE_DRAG_ZOOM) {
-          // Normal double-tap gesture.
+        if (!provider_->IsPinchInProgress() &&
+            !provider_->IsScrollInProgress()) {
           provider_->Send(
               CreateGesture(ET_GESTURE_DOUBLE_TAP,
                             e,
                             CreateTapGestureDetails(ET_GESTURE_DOUBLE_TAP, e)));
+          return true;
         }
-        EndDoubleTapDragIfNecessary(e);
-        break;
-      case MotionEvent::ACTION_CANCEL:
-        EndDoubleTapDragIfNecessary(e);
         break;
       default:
-        NOTREACHED() << "Invalid double-tap event.";
         break;
     }
-    double_tap_y_ = e.GetY();
-    return true;
+    return false;
   }
 
   virtual bool OnLongPress(const MotionEvent& e) OVERRIDE {
@@ -453,8 +460,6 @@ class GestureProvider::GestureListenerImpl
     SetIgnoreSingleTap(true);
 
     GestureEventDetails long_press_details(ET_GESTURE_LONG_PRESS, 0, 0);
-    long_press_details.set_bounding_box(
-        gfx::RectF(e.GetTouchMajor(), e.GetTouchMajor()));
     provider_->Send(
         CreateGesture(ET_GESTURE_LONG_PRESS, e, long_press_details));
 
@@ -464,76 +469,20 @@ class GestureProvider::GestureListenerImpl
     return false;
   }
 
-  void SetDoubleTapSupportForPlatformEnabled(bool enabled) {
+  void SetDoubleTapEnabled(bool enabled) {
     DCHECK(!IsDoubleTapInProgress());
-    DoubleTapMode double_tap_mode =
-        enabled ? DOUBLE_TAP_MODE_NONE : DOUBLE_TAP_MODE_DISABLED;
-    if (double_tap_mode_ == double_tap_mode)
-      return;
-    double_tap_mode_ = double_tap_mode;
-    UpdateDoubleTapListener();
-  }
-
-  void SetDoubleTapSupportForPageEnabled(bool enabled) {
-    if (double_tap_support_enabled_ == enabled)
-      return;
-    double_tap_support_enabled_ = enabled;
-    UpdateDoubleTapListener();
+    gesture_detector_.SetDoubleTapListener(enabled ? this : NULL);
   }
 
-  bool IsDoubleTapDisabled() const {
-    return double_tap_mode_ == DOUBLE_TAP_MODE_DISABLED ||
-           !double_tap_support_enabled_;
-  }
-
-  bool IsClickDelayDisabled() const { return disable_click_delay_; }
-
   bool IsDoubleTapInProgress() const {
-    return double_tap_mode_ != DOUBLE_TAP_MODE_DISABLED &&
-           double_tap_mode_ != DOUBLE_TAP_MODE_NONE;
+    return gesture_detector_.is_double_tapping();
   }
 
  private:
-  enum DoubleTapMode {
-    DOUBLE_TAP_MODE_NONE,
-    DOUBLE_TAP_MODE_DRAG_DETECTION_IN_PROGRESS,
-    DOUBLE_TAP_MODE_DRAG_ZOOM,
-    DOUBLE_TAP_MODE_DISABLED
-  };
-
-  bool IsPointOutsideCurrentSlopRegion(float x, float y) const {
-    return IsDistanceGreaterThanTouchSlop(last_raw_x_ - x, last_raw_y_ - y);
-  }
-
-  bool IsDistanceGreaterThanTouchSlop(float distance_x,
-                                      float distance_y) const {
-    return distance_x * distance_x + distance_y * distance_y >
-           scaled_touch_slop_square_;
-  }
-
   void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; }
 
-  void EndDoubleTapDragIfNecessary(const MotionEvent& event) {
-    if (!IsDoubleTapInProgress())
-      return;
-    if (double_tap_mode_ == DOUBLE_TAP_MODE_DRAG_ZOOM) {
-      provider_->Send(CreateGesture(ET_GESTURE_PINCH_END, event));
-      provider_->Send(CreateGesture(ET_GESTURE_SCROLL_END, event));
-    }
-    double_tap_mode_ = DOUBLE_TAP_MODE_NONE;
-    UpdateDoubleTapListener();
-  }
-
-  void UpdateDoubleTapListener() {
-    if (IsDoubleTapDisabled()) {
-      // Defer nulling the DoubleTapListener until the double-tap gesture is
-      // complete.
-      if (IsDoubleTapInProgress())
-        return;
-      gesture_detector_.set_doubletap_listener(NULL);
-    } else {
-      gesture_detector_.set_doubletap_listener(this);
-    }
+  bool IsDoubleTapEnabled() const {
+    return gesture_detector_.has_doubletap_listener();
   }
 
   GestureDetector gesture_detector_;
@@ -541,17 +490,11 @@ class GestureProvider::GestureListenerImpl
 
   GestureProvider* const provider_;
 
-  const float px_to_dp_;
-
   // Whether the click delay should always be disabled by sending clicks for
   // double-tap gestures.
   const bool disable_click_delay_;
 
-  const int scaled_touch_slop_;
-
-  // Cache of square of the scaled touch slop so we don't have to calculate it
-  // on every touch.
-  const int scaled_touch_slop_square_;
+  const float touch_slop_;
 
   const base::TimeDelta double_tap_timeout_;
 
@@ -566,33 +509,6 @@ class GestureProvider::GestureListenerImpl
   // gesture.
   bool seen_first_scroll_event_;
 
-  // Indicate current double-tap mode state.
-  int double_tap_mode_;
-
-  // On double-tap this will store the y coordinates of the touch.
-  float double_tap_y_;
-
-  // The page's viewport and scale sometimes allow us to disable double-tap
-  // gesture detection,
-  // according to the logic in ContentViewCore.onRenderCoordinatesUpdated().
-  bool double_tap_support_enabled_;
-
-  // x, y coordinates for an Anchor on double-tap drag zoom.
-  float double_tap_drag_zoom_anchor_x_;
-  float double_tap_drag_zoom_anchor_y_;
-
-  // Used to track the last rawX/Y coordinates for moves.  This gives absolute
-  // scroll distance.
-  // Useful for full screen tracking.
-  float last_raw_x_;
-  float last_raw_y_;
-
-  // Used to track the accumulated scroll error over time. This is used to
-  // remove the
-  // rounding error we introduced by passing integers to webkit.
-  float accumulated_scroll_error_x_;
-  float accumulated_scroll_error_y_;
-
   DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl);
 };
 
@@ -601,10 +517,11 @@ class GestureProvider::GestureListenerImpl
 GestureProvider::GestureProvider(const Config& config,
                                  GestureProviderClient* client)
     : client_(client),
-      needs_show_press_event_(false),
-      needs_tap_ending_event_(false),
       touch_scroll_in_progress_(false),
-      pinch_in_progress_(false) {
+      pinch_in_progress_(false),
+      double_tap_support_for_page_(true),
+      double_tap_support_for_platform_(true),
+      gesture_begin_end_types_enabled_(config.gesture_begin_end_types_enabled) {
   DCHECK(client);
   InitGestureDetectors(config);
 }
@@ -614,60 +531,38 @@ GestureProvider::~GestureProvider() {}
 bool GestureProvider::OnTouchEvent(const MotionEvent& event) {
   TRACE_EVENT1("input", "GestureProvider::OnTouchEvent",
                "action", GetMotionEventActionName(event.GetAction()));
+
+  DCHECK_NE(0u, event.GetPointerCount());
+
   if (!CanHandle(event))
     return false;
 
-  const bool was_touch_scrolling_ = touch_scroll_in_progress_;
   const bool in_scale_gesture =
       scale_gesture_listener_->IsScaleGestureDetectionInProgress();
 
-  if (event.GetAction() == MotionEvent::ACTION_DOWN) {
-    current_down_event_ = event.Clone();
-    touch_scroll_in_progress_ = false;
-    needs_show_press_event_ = true;
-    current_longpress_time_ = base::TimeTicks();
-    SendTapCancelIfNecessary(event);
-  }
-
-  bool handled = gesture_listener_->OnTouchEvent(event, in_scale_gesture);
-  handled |= scale_gesture_listener_->OnTouchEvent(event);
-
-  if (event.GetAction() == MotionEvent::ACTION_UP ||
-      event.GetAction() == MotionEvent::ACTION_CANCEL) {
-    // "Last finger raised" could be an end to movement, but it should
-    // only terminate scrolling if the event did not cause a fling.
-    if (was_touch_scrolling_ && !handled)
-      EndTouchScrollIfNecessary(event.GetEventTime(), true);
-
-    // We shouldn't necessarily cancel a tap on ACTION_UP, as the double-tap
-    // timeout may yet trigger a SINGLE_TAP.
-    if (event.GetAction() == MotionEvent::ACTION_CANCEL)
-      SendTapCancelIfNecessary(event);
-
-    current_down_event_.reset();
-  }
-
+  OnTouchEventHandlingBegin(event);
+  gesture_listener_->OnTouchEvent(event, in_scale_gesture);
+  scale_gesture_listener_->OnTouchEvent(event);
+  OnTouchEventHandlingEnd(event);
   return true;
 }
 
-void GestureProvider::ResetGestureDetectors() {
-  if (!current_down_event_)
-    return;
-  scoped_ptr<MotionEvent> cancel_event = current_down_event_->Cancel();
-  gesture_listener_->OnTouchEvent(*cancel_event, false);
-  scale_gesture_listener_->OnTouchEvent(*cancel_event);
-}
-
-void GestureProvider::SetMultiTouchSupportEnabled(bool enabled) {
-  scale_gesture_listener_->set_ignore_detector_events(!enabled);
+void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) {
+  scale_gesture_listener_->SetMultiTouchEnabled(enabled);
 }
 
 void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) {
-  gesture_listener_->SetDoubleTapSupportForPlatformEnabled(enabled);
+  if (double_tap_support_for_platform_ == enabled)
+    return;
+  double_tap_support_for_platform_ = enabled;
+  UpdateDoubleTapDetectionSupport();
 }
 
 void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) {
-  gesture_listener_->SetDoubleTapSupportForPageEnabled(enabled);
+  if (double_tap_support_for_page_ == enabled)
+    return;
+  double_tap_support_for_page_ = enabled;
+  UpdateDoubleTapDetectionSupport();
 }
 
 bool GestureProvider::IsScrollInProgress() const {
@@ -679,36 +574,33 @@ bool GestureProvider::IsScrollInProgress() const {
 bool GestureProvider::IsPinchInProgress() const { return pinch_in_progress_; }
 
 bool GestureProvider::IsDoubleTapInProgress() const {
-  return gesture_listener_->IsDoubleTapInProgress();
-}
-
-bool GestureProvider::IsClickDelayDisabled() const {
-  return gesture_listener_->IsClickDelayDisabled();
+  return gesture_listener_->IsDoubleTapInProgress() ||
+         scale_gesture_listener_->IsDoubleTapInProgress();
 }
 
 void GestureProvider::InitGestureDetectors(const Config& config) {
   TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors");
   gesture_listener_.reset(
-      new GestureListenerImpl(config.gesture_detector_config,
-                              config.snap_scroll_controller_config,
+      new GestureListenerImpl(config.display,
+                              config.gesture_detector_config,
                               config.disable_click_delay,
                               this));
 
   scale_gesture_listener_.reset(
       new ScaleGestureListenerImpl(config.scale_gesture_detector_config, this));
+
+  UpdateDoubleTapDetectionSupport();
 }
 
 bool GestureProvider::CanHandle(const MotionEvent& event) const {
   return event.GetAction() == MotionEvent::ACTION_DOWN || current_down_event_;
 }
 
-void GestureProvider::Fling(base::TimeTicks time,
-                            float x,
-                            float y,
+void GestureProvider::Fling(const MotionEvent& event,
                             float velocity_x,
                             float velocity_y) {
   if (!velocity_x && !velocity_y) {
-    EndTouchScrollIfNecessary(time, true);
+    EndTouchScrollIfNecessary(event, true);
     return;
   }
 
@@ -719,13 +611,14 @@ void GestureProvider::Fling(base::TimeTicks time,
     // start hint.
     GestureEventDetails scroll_details(
         ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y);
-    Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN, time, x, y, scroll_details));
+    Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN, event, scroll_details));
   }
-  EndTouchScrollIfNecessary(time, false);
+  EndTouchScrollIfNecessary(event, false);
 
   GestureEventDetails fling_details(
       ET_SCROLL_FLING_START, velocity_x, velocity_y);
-  Send(CreateGesture(ET_SCROLL_FLING_START, time, x, y, fling_details));
+  Send(CreateGesture(
+      ET_SCROLL_FLING_START, event, fling_details));
 }
 
 void GestureProvider::Send(const GestureEventData& gesture) {
@@ -737,50 +630,51 @@ void GestureProvider::Send(const GestureEventData& gesture) {
          gesture.type == ET_GESTURE_SHOW_PRESS);
 
   switch (gesture.type) {
-    case ET_GESTURE_TAP_DOWN:
-      needs_tap_ending_event_ = true;
-      break;
-    case ET_GESTURE_TAP_UNCONFIRMED:
-      needs_show_press_event_ = false;
-      break;
-    case ET_GESTURE_TAP:
-      if (needs_show_press_event_)
-        Send(CreateGesture(
-            ET_GESTURE_SHOW_PRESS, gesture.time, gesture.x, gesture.y));
-      needs_tap_ending_event_ = false;
-      break;
-    case ET_GESTURE_DOUBLE_TAP:
-      needs_tap_ending_event_ = false;
-      break;
-    case ET_GESTURE_TAP_CANCEL:
-      if (!needs_tap_ending_event_)
-        return;
-      needs_tap_ending_event_ = false;
-      break;
-    case ET_GESTURE_SHOW_PRESS:
-      needs_show_press_event_ = false;
-      break;
     case ET_GESTURE_LONG_PRESS:
       DCHECK(!scale_gesture_listener_->IsScaleGestureDetectionInProgress());
       current_longpress_time_ = gesture.time;
       break;
     case ET_GESTURE_LONG_TAP:
-      needs_tap_ending_event_ = false;
       current_longpress_time_ = base::TimeTicks();
       break;
     case ET_GESTURE_SCROLL_BEGIN:
+      DCHECK(!touch_scroll_in_progress_);
       touch_scroll_in_progress_ = true;
-      SendTapCancelIfNecessary(*current_down_event_);
       break;
     case ET_GESTURE_SCROLL_END:
+      DCHECK(touch_scroll_in_progress_);
+      if (pinch_in_progress_)
+        Send(CreateGesture(ET_GESTURE_PINCH_END,
+                           gesture.motion_event_id,
+                           gesture.time,
+                           gesture.x,
+                           gesture.y,
+                           gesture.details.touch_points(),
+                           gesture.details.bounding_box()));
       touch_scroll_in_progress_ = false;
       break;
     case ET_GESTURE_PINCH_BEGIN:
+      DCHECK(!pinch_in_progress_);
+      if (!touch_scroll_in_progress_)
+        Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN,
+                           gesture.motion_event_id,
+                           gesture.time,
+                           gesture.x,
+                           gesture.y,
+                           gesture.details.touch_points(),
+                           gesture.details.bounding_box()));
       pinch_in_progress_ = true;
       break;
     case ET_GESTURE_PINCH_END:
+      DCHECK(pinch_in_progress_);
       pinch_in_progress_ = false;
       break;
+    case ET_GESTURE_SHOW_PRESS:
+      // It's possible that a double-tap drag zoom (from ScaleGestureDetector)
+      // will start before the press gesture fires (from GestureDetector), in
+      // which case the press should simply be dropped.
+      if (pinch_in_progress_ || touch_scroll_in_progress_)
+        return;
     default:
       break;
   };
@@ -788,34 +682,85 @@ void GestureProvider::Send(const GestureEventData& gesture) {
   client_->OnGestureEvent(gesture);
 }
 
-void GestureProvider::SendTapCancelIfNecessary(const MotionEvent& event) {
-  if (!needs_tap_ending_event_)
-    return;
-  current_longpress_time_ = base::TimeTicks();
-  Send(CreateGesture(ET_GESTURE_TAP_CANCEL, event));
-}
-
 bool GestureProvider::SendLongTapIfNecessary(const MotionEvent& event) {
   if (event.GetAction() == MotionEvent::ACTION_UP &&
       !current_longpress_time_.is_null() &&
       !scale_gesture_listener_->IsScaleGestureDetectionInProgress()) {
-    SendTapCancelIfNecessary(event);
     GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP, 0, 0);
-    long_tap_details.set_bounding_box(
-        gfx::RectF(event.GetTouchMajor(), event.GetTouchMajor()));
     Send(CreateGesture(ET_GESTURE_LONG_TAP, event, long_tap_details));
     return true;
   }
   return false;
 }
 
-void GestureProvider::EndTouchScrollIfNecessary(base::TimeTicks time,
+void GestureProvider::EndTouchScrollIfNecessary(const MotionEvent& event,
                                                 bool send_scroll_end_event) {
   if (!touch_scroll_in_progress_)
     return;
-  touch_scroll_in_progress_ = false;
   if (send_scroll_end_event)
-    Send(CreateGesture(ET_GESTURE_SCROLL_END, time, 0, 0));
+    Send(CreateGesture(ET_GESTURE_SCROLL_END, event));
+  touch_scroll_in_progress_ = false;
+}
+
+void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent& event) {
+  switch (event.GetAction()) {
+    case MotionEvent::ACTION_DOWN:
+      current_down_event_ = event.Clone();
+      touch_scroll_in_progress_ = false;
+      pinch_in_progress_ = false;
+      current_longpress_time_ = base::TimeTicks();
+      if (gesture_begin_end_types_enabled_)
+        Send(CreateGesture(ET_GESTURE_BEGIN, event));
+      break;
+    case MotionEvent::ACTION_POINTER_DOWN:
+      if (gesture_begin_end_types_enabled_)
+        Send(CreateGesture(ET_GESTURE_BEGIN, event));
+      break;
+    case MotionEvent::ACTION_POINTER_UP:
+    case MotionEvent::ACTION_UP:
+    case MotionEvent::ACTION_CANCEL:
+    case MotionEvent::ACTION_MOVE:
+      break;
+  }
+}
+
+void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent& event) {
+  switch (event.GetAction()) {
+    case MotionEvent::ACTION_UP:
+    case MotionEvent::ACTION_CANCEL:
+      // Note: This call will have no effect if a fling was just generated, as
+      // |Fling()| will have already signalled an end to touch-scrolling.
+      EndTouchScrollIfNecessary(event, true);
+
+      if (gesture_begin_end_types_enabled_)
+        Send(CreateGesture(ET_GESTURE_END, event));
+
+      current_down_event_.reset();
+
+      UpdateDoubleTapDetectionSupport();
+      break;
+    case MotionEvent::ACTION_POINTER_UP:
+      if (gesture_begin_end_types_enabled_)
+        Send(CreateGesture(ET_GESTURE_END, event));
+      break;
+    case MotionEvent::ACTION_DOWN:
+    case MotionEvent::ACTION_POINTER_DOWN:
+    case MotionEvent::ACTION_MOVE:
+      break;
+  }
+}
+
+void GestureProvider::UpdateDoubleTapDetectionSupport() {
+  // The GestureDetector requires that any provided DoubleTapListener remain
+  // attached to it for the duration of a touch sequence. Defer any potential
+  // null'ing of the listener until the sequence has ended.
+  if (current_down_event_)
+    return;
+
+  const bool double_tap_enabled = double_tap_support_for_page_ &&
+                                  double_tap_support_for_platform_;
+  gesture_listener_->SetDoubleTapEnabled(double_tap_enabled);
+  scale_gesture_listener_->SetDoubleTapEnabled(double_tap_enabled);
 }
 
 }  //  namespace ui