Commit e7015d5a authored by Sahel Sharify's avatar Sahel Sharify Committed by Commit Bot

Scroll latching timer timeout is 500ms and Mouse move breaks latching.

With this cl scroll latching timeout is increased to 500ms and the phase
handler ends a timer-based scroll sequence once the difference between
coordinates of the initial wheel event in the sequence and the current
wheel event exceeds some threshold.

       TimerBasedLatchingBreaksWithMouseMove

Bug: 526463
Test: RenderWidgetHostViewAuraWheelScrollLatchingEnabledTest.
Change-Id: Ie6afbf205c42dfce4f3a3f56937c9cb32b2feebf
Reviewed-on: https://chromium-review.googlesource.com/820334
Commit-Queue: Sahel Sharifymoghaddam <sahel@chromium.org>
Reviewed-by: default avatarTimothy Dresser <tdresser@chromium.org>
Cr-Commit-Position: refs/heads/master@{#523809}
parent c566c6f9
......@@ -15,6 +15,7 @@ MouseWheelPhaseHandler::MouseWheelPhaseHandler(
RenderWidgetHostViewBase* const host_view)
: host_(host),
host_view_(host_view),
mouse_wheel_end_dispatch_timeout_(kDefaultMouseWheelLatchingTransaction),
scroll_phase_state_(SCROLL_STATE_UNKNOWN) {}
void MouseWheelPhaseHandler::AddPhaseIfNeededAndScheduleEndEvent(
......@@ -28,7 +29,9 @@ void MouseWheelPhaseHandler::AddPhaseIfNeededAndScheduleEndEvent(
if (mouse_wheel_event.phase == blink::WebMouseWheelEvent::kPhaseEnded) {
// Don't send the wheel end event immediately, start a timer instead to
// see whether momentum phase of the scrolling starts or not.
ScheduleMouseWheelEndDispatching(should_route_event);
ScheduleMouseWheelEndDispatching(
should_route_event,
kMaximumTimeBetweenPhaseEndedAndMomentumPhaseBegan);
} else if (mouse_wheel_event.phase ==
blink::WebMouseWheelEvent::kPhaseBegan) {
// A new scrolling sequence has started, send the pending wheel end
......@@ -45,9 +48,19 @@ void MouseWheelPhaseHandler::AddPhaseIfNeededAndScheduleEndEvent(
switch (scroll_phase_state_) {
case SCROLL_STATE_UNKNOWN: {
mouse_wheel_event.has_synthetic_phase = true;
// Break the latching when the location difference between the current
// and the initial wheel event positions exceeds the maximum allowed
// threshold.
if (!IsWithinSlopRegion(mouse_wheel_event))
DispatchPendingWheelEndEvent();
if (!mouse_wheel_end_dispatch_timer_.IsRunning()) {
mouse_wheel_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
ScheduleMouseWheelEndDispatching(should_route_event);
first_wheel_location_ =
gfx::Vector2dF(mouse_wheel_event.PositionInWidget().x,
mouse_wheel_event.PositionInWidget().y);
ScheduleMouseWheelEndDispatching(should_route_event,
mouse_wheel_end_dispatch_timeout_);
} else { // mouse_wheel_end_dispatch_timer_.IsRunning()
bool non_zero_delta =
mouse_wheel_event.delta_x || mouse_wheel_event.delta_y;
......@@ -129,13 +142,21 @@ void MouseWheelPhaseHandler::SendSyntheticWheelEventWithPhaseEnded(
}
void MouseWheelPhaseHandler::ScheduleMouseWheelEndDispatching(
bool should_route_event) {
bool should_route_event,
const base::TimeDelta timeout) {
mouse_wheel_end_dispatch_timer_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(
kDefaultMouseWheelLatchingTransactionMs),
FROM_HERE, timeout,
base::Bind(&MouseWheelPhaseHandler::SendSyntheticWheelEventWithPhaseEnded,
base::Unretained(this), should_route_event));
}
bool MouseWheelPhaseHandler::IsWithinSlopRegion(
blink::WebMouseWheelEvent wheel_event) const {
DCHECK(scroll_phase_state_ == SCROLL_STATE_UNKNOWN);
gfx::Vector2dF current_wheel_location(wheel_event.PositionInWidget().x,
wheel_event.PositionInWidget().y);
return (current_wheel_location - first_wheel_location_).LengthSquared() <
kWheelLatchingSlopRegion * kWheelLatchingSlopRegion;
}
} // namespace content
......@@ -15,7 +15,18 @@ class RenderWidgetHostViewBase;
// The duration after which a synthetic wheel with zero deltas and
// phase = |kPhaseEnded| will be sent after the last wheel event.
const int64_t kDefaultMouseWheelLatchingTransactionMs = 100;
constexpr base::TimeDelta kDefaultMouseWheelLatchingTransaction =
base::TimeDelta::FromMilliseconds(500);
// Maximum time that the phase handler waits for arrival of a wheel event with
// momentum_phase = kPhaseBegan before sending its previous wheel event with
// phase = kPhaseEnded.
constexpr base::TimeDelta kMaximumTimeBetweenPhaseEndedAndMomentumPhaseBegan =
base::TimeDelta::FromMilliseconds(100);
// Maximum allowed difference between coordinates of two mouse wheel events in
// the same scroll sequence.
const double kWheelLatchingSlopRegion = 10.0;
// On ChromeOS wheel events don't have phase information; However, whenever the
// user puts down or lifts their fingers a GFC or GFS is received.
......@@ -44,6 +55,11 @@ class MouseWheelPhaseHandler {
void SendWheelEndIfNeeded();
void ScrollingMayBegin();
// Used to set the timer timeout for testing.
void set_mouse_wheel_end_dispatch_timeout(base::TimeDelta timeout) {
mouse_wheel_end_dispatch_timeout_ = timeout;
}
bool HasPendingWheelEndEvent() const {
return mouse_wheel_end_dispatch_timer_.IsRunning();
}
......@@ -51,13 +67,21 @@ class MouseWheelPhaseHandler {
private:
void SendSyntheticWheelEventWithPhaseEnded(
bool should_route_event);
void ScheduleMouseWheelEndDispatching(bool should_route_event);
void ScheduleMouseWheelEndDispatching(bool should_route_event,
const base::TimeDelta timeout);
bool IsWithinSlopRegion(blink::WebMouseWheelEvent wheel_event) const;
RenderWidgetHostImpl* const host_;
RenderWidgetHostViewBase* const host_view_;
base::OneShotTimer mouse_wheel_end_dispatch_timer_;
base::TimeDelta mouse_wheel_end_dispatch_timeout_;
blink::WebMouseWheelEvent last_mouse_wheel_event_;
ScrollPhaseState scroll_phase_state_;
// This is used to break the timer based latching when the difference between
// the locations of the first wheel event and the current wheel event is
// larger than some threshold. The variable value is only valid while the
// dispatch timer is running.
gfx::Vector2dF first_wheel_location_;
DISALLOW_COPY_AND_ASSIGN(MouseWheelPhaseHandler);
};
......
......@@ -746,6 +746,9 @@ class RenderWidgetHostViewAuraTest : public testing::Test {
delegates_.back()->set_widget_host(widget_host_);
widget_host_->Init();
view_ = new FakeRenderWidgetHostViewAura(widget_host_, is_guest_view_hack_);
// Set the mouse_wheel_phase_handler_ timer timeout to 100ms.
view_->event_handler()->set_mouse_wheel_wheel_phase_handler_timeout(
base::TimeDelta::FromMilliseconds(100));
base::RunLoop().RunUntilIdle();
}
......@@ -1948,8 +1951,7 @@ TEST_F(RenderWidgetHostViewAuraWheelScrollLatchingEnabledTest,
// synthetic wheel event with zero deltas and kPhaseEnded will be sent.
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(),
base::TimeDelta::FromMilliseconds(
kDefaultMouseWheelLatchingTransactionMs));
base::TimeDelta::FromMilliseconds(100));
base::RunLoop().Run();
events = GetAndResetDispatchedMessages();
......@@ -1970,6 +1972,66 @@ TEST_F(RenderWidgetHostViewAuraWheelScrollLatchingEnabledTest,
EXPECT_TRUE(gesture_event->data.scroll_end.synthetic);
}
// Tests that latching breaks when the difference between location of the first
// wheel event in the sequence and the location of the current wheel event is
// larger than some maximum threshold.
TEST_F(RenderWidgetHostViewAuraWheelScrollLatchingEnabledTest,
TimerBasedLatchingBreaksWithMouseMove) {
view_->InitAsChild(nullptr);
view_->Show();
sink_->ClearMessages();
ui::MouseWheelEvent event(gfx::Vector2d(0, 5), gfx::Point(2, 2),
gfx::Point(2, 2), ui::EventTimeForNow(), 0, 0);
view_->OnMouseEvent(&event);
base::RunLoop().RunUntilIdle();
MockWidgetInputHandler::MessageVector events =
GetAndResetDispatchedMessages();
EXPECT_TRUE(events[0]->ToEvent());
const WebMouseWheelEvent* wheel_event =
static_cast<const WebMouseWheelEvent*>(
events[0]->ToEvent()->Event()->web_event.get());
EXPECT_EQ(WebMouseWheelEvent::kPhaseBegan, wheel_event->phase);
events[0]->ToEvent()->CallCallback(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
events = GetAndResetDispatchedMessages();
// Send the second wheel event with a location within the slop region. The
// second wheel event will still be part of the current scrolling sequence
// since the location difference is less than the allowed threshold.
ui::MouseWheelEvent event2(gfx::Vector2d(0, 5),
gfx::Point(2 + kWheelLatchingSlopRegion / 2, 2),
gfx::Point(2 + kWheelLatchingSlopRegion / 2, 2),
ui::EventTimeForNow(), 0, 0);
view_->OnMouseEvent(&event2);
base::RunLoop().RunUntilIdle();
events = GetAndResetDispatchedMessages();
EXPECT_EQ("MouseWheel", GetMessageNames(events));
wheel_event = static_cast<const WebMouseWheelEvent*>(
events[0]->ToEvent()->Event()->web_event.get());
EXPECT_EQ(WebMouseWheelEvent::kPhaseChanged, wheel_event->phase);
events[0]->ToEvent()->CallCallback(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
events = GetAndResetDispatchedMessages();
// Send the third wheel event with a location outside of the slop region. The
// third wheel event will break the latching since the location difference is
// larger than the allowed threshold.
ui::MouseWheelEvent event3(
gfx::Vector2d(0, 5), gfx::Point(2 + kWheelLatchingSlopRegion, 2),
gfx::Point(2 + kWheelLatchingSlopRegion, 2), ui::EventTimeForNow(), 0, 0);
view_->OnMouseEvent(&event3);
base::RunLoop().RunUntilIdle();
events = GetAndResetDispatchedMessages();
EXPECT_EQ("MouseWheel GestureScrollEnd MouseWheel", GetMessageNames(events));
wheel_event = static_cast<const WebMouseWheelEvent*>(
events[0]->ToEvent()->Event()->web_event.get());
EXPECT_EQ(WebMouseWheelEvent::kPhaseEnded, wheel_event->phase);
wheel_event = static_cast<const WebMouseWheelEvent*>(
events[2]->ToEvent()->Event()->web_event.get());
EXPECT_EQ(WebMouseWheelEvent::kPhaseBegan, wheel_event->phase);
}
// Tests that a gesture fling start with touchpad source resets wheel phase
// state.
TEST_F(RenderWidgetHostViewAuraWheelScrollLatchingEnabledTest,
......@@ -2013,8 +2075,7 @@ TEST_F(RenderWidgetHostViewAuraWheelScrollLatchingEnabledTest,
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(
2 * kDefaultMouseWheelLatchingTransactionMs));
base::TimeDelta::FromMilliseconds(200));
run_loop.Run();
ui::ScrollEvent scroll1(ui::ET_SCROLL, gfx::Point(2, 2),
ui::EventTimeForNow(), 0, 0, 15, 0, 15, 2);
......
......@@ -147,6 +147,11 @@ class CONTENT_EXPORT RenderWidgetHostViewEventHandler
void OnTouchEvent(ui::TouchEvent* event) override;
void OnGestureEvent(ui::GestureEvent* event) override;
// Used to set the mouse_wheel_phase_handler_ timer timeout for testing.
void set_mouse_wheel_wheel_phase_handler_timeout(base::TimeDelta timeout) {
mouse_wheel_phase_handler_.set_mouse_wheel_end_dispatch_timeout(timeout);
}
private:
FRIEND_TEST_ALL_PREFIXES(InputMethodResultAuraTest,
FinishImeCompositionSession);
......
......@@ -471,6 +471,11 @@ class CONTENT_EXPORT RenderWidgetHostViewMac
MouseWheelPhaseHandler mouse_wheel_phase_handler_;
// Used to set the mouse_wheel_phase_handler_ timer timeout for testing.
void set_mouse_wheel_wheel_phase_handler_timeout(base::TimeDelta timeout) {
mouse_wheel_phase_handler_.set_mouse_wheel_end_dispatch_timeout(timeout);
}
NSWindow* pepper_fullscreen_window() const {
return pepper_fullscreen_window_;
}
......
......@@ -1296,6 +1296,9 @@ TEST_F(RenderWidgetHostViewMacWithWheelScrollLatchingEnabledTest,
// generated from this type of devices.
TEST_F(RenderWidgetHostViewMacWithWheelScrollLatchingEnabledTest,
TimerBasedPhaseInfo) {
rwhv_mac_->set_mouse_wheel_wheel_phase_handler_timeout(
base::TimeDelta::FromMilliseconds(100));
// Send a wheel event without phase information for scrolling by 3 lines.
NSEvent* wheelEvent = MockScrollWheelEventWithoutPhase(3);
[rwhv_mac_->cocoa_view() scrollWheel:wheelEvent];
......@@ -1384,7 +1387,7 @@ TEST_F(RenderWidgetHostViewMacWithWheelScrollLatchingEnabledTest,
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(100));
kMaximumTimeBetweenPhaseEndedAndMomentumPhaseBegan);
run_loop.Run();
}
......
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