Commit 3f1dfec0 authored by skyostil@chromium.org's avatar skyostil@chromium.org

gpu: Avoid flinging beyond content edge

If the user flings down a page while the page is still loading, we may
hit a situation where the fling animation stops at the bottom edge of
the content, pauses while the loading proceeds and then continues once the
content grows taller. This results in a jarring pause in an otherwise
continuous animation.

This patch fixes the issue by preventing the fling animation from trying
to scroll beyond the content edge. In other words, as soon as the
content overscrolls on a specific axis, we prevent the fling animation
from scrolling on that axis again.

BUG=244376
TEST=InputHandlerProxyTest.GestureFlingStopsAtContentEdge

Review URL: https://chromiumcodereview.appspot.com/16156002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@204006 0039d316-1c4b-4281-b951-d872f2087c98
parent eafe4049
......@@ -29,7 +29,9 @@ InputHandlerProxy::InputHandlerProxy(cc::InputHandler* input_handler)
#endif
gesture_scroll_on_impl_thread_(false),
gesture_pinch_on_impl_thread_(false),
fling_may_be_active_on_main_thread_(false) {
fling_may_be_active_on_main_thread_(false),
fling_overscrolled_horizontally_(false),
fling_overscrolled_vertically_(false) {
input_handler_->BindToClient(this);
}
......@@ -222,6 +224,8 @@ InputHandlerProxy::HandleGestureFling(
WebFloatPoint(gesture_event.data.flingStart.velocityX,
gesture_event.data.flingStart.velocityY),
WebKit::WebSize()));
fling_overscrolled_horizontally_ = false;
fling_overscrolled_vertically_ = false;
TRACE_EVENT_ASYNC_BEGIN0(
"renderer",
"InputHandlerProxy::HandleGestureFling::started",
......@@ -291,6 +295,14 @@ void InputHandlerProxy::MainThreadHasStoppedFlinging() {
void InputHandlerProxy::DidOverscroll(gfx::Vector2dF accumulated_overscroll,
gfx::Vector2dF current_fling_velocity) {
DCHECK(client_);
if (fling_curve_) {
static const int kFlingOverscrollThreshold = 1;
fling_overscrolled_horizontally_ |=
std::abs(accumulated_overscroll.x()) >= kFlingOverscrollThreshold;
fling_overscrolled_vertically_ |=
std::abs(accumulated_overscroll.y()) >= kFlingOverscrollThreshold;
}
client_->DidOverscroll(accumulated_overscroll, current_fling_velocity);
}
......@@ -360,31 +372,38 @@ static gfx::Vector2dF ToClientScrollIncrement(const WebFloatSize& increment) {
}
void InputHandlerProxy::scrollBy(const WebFloatSize& increment) {
if (increment == WebFloatSize())
WebFloatSize clipped_increment;
if (!fling_overscrolled_horizontally_)
clipped_increment.width = increment.width;
if (!fling_overscrolled_vertically_)
clipped_increment.height = increment.height;
if (clipped_increment == WebFloatSize())
return;
TRACE_EVENT2("renderer",
"InputHandlerProxy::scrollBy",
"x",
increment.width,
clipped_increment.width,
"y",
increment.height);
clipped_increment.height);
bool did_scroll = false;
switch (fling_parameters_.sourceDevice) {
case WebGestureEvent::Touchpad:
did_scroll = TouchpadFlingScroll(increment);
did_scroll = TouchpadFlingScroll(clipped_increment);
break;
case WebGestureEvent::Touchscreen:
clipped_increment = ToClientScrollIncrement(clipped_increment);
did_scroll = input_handler_->ScrollBy(fling_parameters_.point,
ToClientScrollIncrement(increment));
clipped_increment);
break;
}
if (did_scroll) {
fling_parameters_.cumulativeScroll.width += increment.width;
fling_parameters_.cumulativeScroll.height += increment.height;
fling_parameters_.cumulativeScroll.width += clipped_increment.width;
fling_parameters_.cumulativeScroll.height += clipped_increment.height;
}
}
......
......@@ -87,6 +87,9 @@ class CONTENT_EXPORT InputHandlerProxy
// conservative in the sense that we might not be actually flinging when it is
// true.
bool fling_may_be_active_on_main_thread_;
// The axes on which the current fling has overshot the bounds of the content.
bool fling_overscrolled_horizontally_;
bool fling_overscrolled_vertically_;
DISALLOW_COPY_AND_ASSIGN(InputHandlerProxy);
};
......
......@@ -966,5 +966,61 @@ TEST_F(InputHandlerProxyTest, LastInputEventForVSync) {
EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
}
TEST_F(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) {
// We shouldn't send any events to the widget for this gesture.
expected_disposition_ = InputHandlerProxy::DID_HANDLE;
VERIFY_AND_RESET_MOCKS();
// On the fling start, we should schedule an animation but not actually start
// scrolling.
gesture_.type = WebInputEvent::GestureFlingStart;
WebFloatPoint fling_delta = WebFloatPoint(1000, 1000);
gesture_.data.flingStart.velocityX = fling_delta.x;
gesture_.data.flingStart.velocityY = fling_delta.y;
EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
.WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
EXPECT_CALL(mock_input_handler_, ScrollEnd());
EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
// The first animate doesn't cause any scrolling.
EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10);
input_handler_->Animate(time);
testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
// The second animate starts scrolling in the positive X and Y directions.
EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
.WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
EXPECT_CALL(mock_input_handler_,
ScrollBy(testing::_,
testing::Property(&gfx::Vector2dF::y, testing::Lt(0))))
.WillOnce(testing::Return(true));
EXPECT_CALL(mock_input_handler_, ScrollEnd());
time += base::TimeDelta::FromMilliseconds(100);
input_handler_->Animate(time);
testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
// Simulate hitting the bottom content edge.
gfx::Vector2dF overscroll_amount(0, 100);
gfx::Vector2dF overscroll_velocity(0, 10);
input_handler_->DidOverscroll(overscroll_amount, overscroll_velocity);
// The next call to animate will no longer scroll vertically.
EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
.WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
EXPECT_CALL(mock_input_handler_,
ScrollBy(testing::_,
testing::Property(&gfx::Vector2dF::y, testing::Eq(0))))
.WillOnce(testing::Return(true));
EXPECT_CALL(mock_input_handler_, ScrollEnd());
time += base::TimeDelta::FromMilliseconds(100);
input_handler_->Animate(time);
testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
}
} // namespace
} // namespace content
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment