| Index: content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc
|
| diff --git a/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc b/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc
|
| index b2fb4bb7eb3bf82487025edf9b3e0b3e3d6bfda1..f70ba9be7b12ead8dc457f96ba24b806b338f6c4 100644
|
| --- a/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc
|
| +++ b/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc
|
| @@ -35,7 +35,7 @@ SyntheticSmoothScrollGesture::SyntheticSmoothScrollGesture(
|
| SyntheticSmoothScrollGesture::~SyntheticSmoothScrollGesture() {}
|
|
|
| SyntheticGesture::Result SyntheticSmoothScrollGesture::ForwardInputEvents(
|
| - const base::TimeDelta& interval, SyntheticGestureTarget* target) {
|
| + const base::TimeTicks& timestamp, SyntheticGestureTarget* target) {
|
| if (state_ == SETUP) {
|
| gesture_source_type_ = params_.gesture_source_type;
|
| if (gesture_source_type_ == SyntheticGestureParams::DEFAULT_INPUT)
|
| @@ -45,13 +45,14 @@ SyntheticGesture::Result SyntheticSmoothScrollGesture::ForwardInputEvents(
|
| return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_SUPPORTED_BY_PLATFORM;
|
|
|
| state_ = STARTED;
|
| + start_time_ = timestamp;
|
| }
|
|
|
| DCHECK_NE(gesture_source_type_, SyntheticGestureParams::DEFAULT_INPUT);
|
| if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT)
|
| - ForwardTouchInputEvents(interval, target);
|
| + ForwardTouchInputEvents(timestamp, target);
|
| else if (gesture_source_type_ == SyntheticGestureParams::MOUSE_INPUT)
|
| - ForwardMouseInputEvents(interval, target);
|
| + ForwardMouseInputEvents(timestamp, target);
|
| else
|
| return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED;
|
|
|
| @@ -60,7 +61,8 @@ SyntheticGesture::Result SyntheticSmoothScrollGesture::ForwardInputEvents(
|
| }
|
|
|
| void SyntheticSmoothScrollGesture::ForwardTouchInputEvents(
|
| - const base::TimeDelta& interval, SyntheticGestureTarget* target) {
|
| + const base::TimeTicks& timestamp, SyntheticGestureTarget* target) {
|
| + base::TimeTicks event_timestamp = timestamp;
|
| switch (state_) {
|
| case STARTED:
|
| // Check for an early finish.
|
| @@ -69,29 +71,33 @@ void SyntheticSmoothScrollGesture::ForwardTouchInputEvents(
|
| break;
|
| }
|
| AddTouchSlopToDistance(target);
|
| - PressTouchPoint(target);
|
| + ComputeAndSetStopScrollingTime();
|
| + PressTouchPoint(target, event_timestamp);
|
| state_ = MOVING;
|
| break;
|
| - case MOVING:
|
| - total_delta_ += GetPositionDelta(interval);
|
| - MoveTouchPoint(target);
|
| + case MOVING: {
|
| + event_timestamp = ClampTimestamp(timestamp);
|
| + gfx::Vector2dF delta = GetPositionDeltaAtTime(event_timestamp);
|
| + MoveTouchPoint(target, delta, event_timestamp);
|
|
|
| - if (HasScrolledEntireDistance()) {
|
| + if (HasScrolledEntireDistance(event_timestamp)) {
|
| if (params_.prevent_fling) {
|
| state_ = STOPPING;
|
| } else {
|
| - ReleaseTouchPoint(target);
|
| + ReleaseTouchPoint(target, event_timestamp);
|
| state_ = DONE;
|
| }
|
| }
|
| - break;
|
| + } break;
|
| case STOPPING:
|
| - total_stopping_wait_time_ += interval;
|
| - if (total_stopping_wait_time_ >= target->PointerAssumedStoppedTime()) {
|
| + if (timestamp - stop_scrolling_time_ >=
|
| + target->PointerAssumedStoppedTime()) {
|
| + event_timestamp =
|
| + stop_scrolling_time_ + target->PointerAssumedStoppedTime();
|
| // Send one last move event, but don't change the location. Without this
|
| // we'd still sometimes cause a fling on Android.
|
| - MoveTouchPoint(target);
|
| - ReleaseTouchPoint(target);
|
| + ForwardTouchEvent(target, event_timestamp);
|
| + ReleaseTouchPoint(target, event_timestamp);
|
| state_ = DONE;
|
| }
|
| break;
|
| @@ -105,7 +111,7 @@ void SyntheticSmoothScrollGesture::ForwardTouchInputEvents(
|
| }
|
|
|
| void SyntheticSmoothScrollGesture::ForwardMouseInputEvents(
|
| - const base::TimeDelta& interval, SyntheticGestureTarget* target) {
|
| + const base::TimeTicks& timestamp, SyntheticGestureTarget* target) {
|
| switch (state_) {
|
| case STARTED:
|
| // Check for an early finish.
|
| @@ -113,24 +119,25 @@ void SyntheticSmoothScrollGesture::ForwardMouseInputEvents(
|
| state_ = DONE;
|
| break;
|
| }
|
| + ComputeAndSetStopScrollingTime();
|
| state_ = MOVING;
|
| // Fall through to forward the first event.
|
| - case MOVING:
|
| - {
|
| - // Even though WebMouseWheelEvents take floating point deltas,
|
| - // internally the scroll position is stored as an integer. We therefore
|
| - // keep track of the discrete delta which is consistent with the
|
| - // internal scrolling state. This ensures that when the gesture has
|
| - // finished we've scrolled exactly the specified distance.
|
| - total_delta_ += GetPositionDelta(interval);
|
| - gfx::Vector2d delta_discrete =
|
| - FloorTowardZero(total_delta_ - total_delta_discrete_);
|
| - ForwardMouseWheelEvent(target, delta_discrete);
|
| - total_delta_discrete_ += delta_discrete;
|
| - }
|
| - if (HasScrolledEntireDistance())
|
| + case MOVING: {
|
| + // Even though WebMouseWheelEvents take floating point deltas,
|
| + // internally the scroll position is stored as an integer. We therefore
|
| + // keep track of the discrete delta which is consistent with the
|
| + // internal scrolling state. This ensures that when the gesture has
|
| + // finished we've scrolled exactly the specified distance.
|
| + base::TimeTicks event_timestamp = ClampTimestamp(timestamp);
|
| + gfx::Vector2dF total_delta = GetPositionDeltaAtTime(event_timestamp);
|
| + gfx::Vector2d delta_discrete =
|
| + FloorTowardZero(total_delta - total_delta_discrete_);
|
| + ForwardMouseWheelEvent(target, delta_discrete, event_timestamp);
|
| + total_delta_discrete_ += delta_discrete;
|
| +
|
| + if (HasScrolledEntireDistance(event_timestamp))
|
| state_ = DONE;
|
| - break;
|
| + } break;
|
| case SETUP:
|
| NOTREACHED()
|
| << "State STARTED invalid for synthetic scroll using touch input.";
|
| @@ -140,44 +147,52 @@ void SyntheticSmoothScrollGesture::ForwardMouseInputEvents(
|
| case DONE:
|
| NOTREACHED()
|
| << "State DONE invalid for synthetic scroll using touch input.";
|
| - }
|
| + }
|
| }
|
|
|
| void SyntheticSmoothScrollGesture::ForwardTouchEvent(
|
| - SyntheticGestureTarget* target) const {
|
| + SyntheticGestureTarget* target, const base::TimeTicks& timestamp) {
|
| + touch_event_.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
|
| +
|
| target->DispatchInputEventToPlatform(
|
| InputEvent(touch_event_, ui::LatencyInfo(), false));
|
| }
|
|
|
| void SyntheticSmoothScrollGesture::ForwardMouseWheelEvent(
|
| - SyntheticGestureTarget* target, const gfx::Vector2dF& delta) const {
|
| + SyntheticGestureTarget* target,
|
| + const gfx::Vector2dF& delta,
|
| + const base::TimeTicks& timestamp) const {
|
| blink::WebMouseWheelEvent mouse_wheel_event =
|
| SyntheticWebMouseWheelEventBuilder::Build(delta.x(), delta.y(), 0, false);
|
|
|
| mouse_wheel_event.x = params_.anchor.x();
|
| mouse_wheel_event.y = params_.anchor.y();
|
|
|
| + mouse_wheel_event.timeStampSeconds = ConvertTimestampToSeconds(timestamp);
|
| +
|
| target->DispatchInputEventToPlatform(
|
| InputEvent(mouse_wheel_event, ui::LatencyInfo(), false));
|
| }
|
|
|
| void SyntheticSmoothScrollGesture::PressTouchPoint(
|
| - SyntheticGestureTarget* target) {
|
| + SyntheticGestureTarget* target, const base::TimeTicks& timestamp) {
|
| touch_event_.PressPoint(params_.anchor.x(), params_.anchor.y());
|
| - ForwardTouchEvent(target);
|
| + ForwardTouchEvent(target, timestamp);
|
| }
|
|
|
| void SyntheticSmoothScrollGesture::MoveTouchPoint(
|
| - SyntheticGestureTarget* target) {
|
| - gfx::PointF touch_position = params_.anchor + total_delta_;
|
| + SyntheticGestureTarget* target,
|
| + const gfx::Vector2dF& delta,
|
| + const base::TimeTicks& timestamp) {
|
| + gfx::PointF touch_position = params_.anchor + delta;
|
| touch_event_.MovePoint(0, touch_position.x(), touch_position.y());
|
| - ForwardTouchEvent(target);
|
| + ForwardTouchEvent(target, timestamp);
|
| }
|
|
|
| void SyntheticSmoothScrollGesture::ReleaseTouchPoint(
|
| - SyntheticGestureTarget* target) {
|
| + SyntheticGestureTarget* target, const base::TimeTicks& timestamp) {
|
| touch_event_.ReleasePoint(0);
|
| - ForwardTouchEvent(target);
|
| + ForwardTouchEvent(target, timestamp);
|
| }
|
|
|
| void SyntheticSmoothScrollGesture::AddTouchSlopToDistance(
|
| @@ -187,23 +202,21 @@ void SyntheticSmoothScrollGesture::AddTouchSlopToDistance(
|
| // distance and round up to the nearest integer.
|
| // For vertical and horizontal scrolls (the common case), both methods produce
|
| // the same result.
|
| - gfx::Vector2dF touch_slop_delta = ProjectLengthOntoScrollDirection(
|
| - target->GetTouchSlopInDips());
|
| + gfx::Vector2dF touch_slop_delta =
|
| + ProjectLengthOntoScrollDirection(target->GetTouchSlopInDips());
|
| params_.distance += CeilFromZero(touch_slop_delta);
|
| }
|
|
|
| -gfx::Vector2dF SyntheticSmoothScrollGesture::GetPositionDelta(
|
| - const base::TimeDelta& interval) const {
|
| - float delta_length = params_.speed_in_pixels_s * interval.InSecondsF();
|
| +gfx::Vector2dF SyntheticSmoothScrollGesture::GetPositionDeltaAtTime(
|
| + const base::TimeTicks& timestamp) const {
|
| + // Make sure the final delta is correct. Using the computation below can lead
|
| + // to issues with floating point precision.
|
| + if (HasScrolledEntireDistance(timestamp))
|
| + return -params_.distance;
|
|
|
| - // Make sure we're not scrolling too far.
|
| - gfx::Vector2dF remaining_delta = ComputeRemainingDelta();
|
| - if (delta_length > remaining_delta.Length())
|
| - // In order to scroll in a certain direction we need to move the
|
| - // touch pointer/mouse wheel in the opposite direction.
|
| - return -remaining_delta;
|
| - else
|
| - return -ProjectLengthOntoScrollDirection(delta_length);
|
| + float delta_length =
|
| + params_.speed_in_pixels_s * (timestamp - start_time_).InSecondsF();
|
| + return -ProjectLengthOntoScrollDirection(delta_length);
|
| }
|
|
|
| gfx::Vector2dF SyntheticSmoothScrollGesture::ProjectLengthOntoScrollDirection(
|
| @@ -212,12 +225,22 @@ gfx::Vector2dF SyntheticSmoothScrollGesture::ProjectLengthOntoScrollDirection(
|
| return ScaleVector2d(params_.distance, delta_length / kTotalLength);
|
| }
|
|
|
| -gfx::Vector2dF SyntheticSmoothScrollGesture::ComputeRemainingDelta() const {
|
| - return params_.distance + total_delta_;
|
| +void SyntheticSmoothScrollGesture::ComputeAndSetStopScrollingTime() {
|
| + int64 total_duration_in_us = static_cast<int64>(
|
| + 1e6 * (params_.distance.Length() / params_.speed_in_pixels_s));
|
| + DCHECK_GT(total_duration_in_us, 0);
|
| + stop_scrolling_time_ =
|
| + start_time_ + base::TimeDelta::FromMicroseconds(total_duration_in_us);
|
| +}
|
| +
|
| +base::TimeTicks SyntheticSmoothScrollGesture::ClampTimestamp(
|
| + const base::TimeTicks& timestamp) const {
|
| + return std::min(timestamp, stop_scrolling_time_);
|
| }
|
|
|
| -bool SyntheticSmoothScrollGesture::HasScrolledEntireDistance() const {
|
| - return ComputeRemainingDelta().IsZero();
|
| +bool SyntheticSmoothScrollGesture::HasScrolledEntireDistance(
|
| + const base::TimeTicks& timestamp) const {
|
| + return timestamp >= stop_scrolling_time_;
|
| }
|
|
|
| } // namespace content
|
|
|