blob: 1868ceb2ed81c507a6f9ecc375da598da377a80b [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/events/blink/fling_booster.h"
#include <memory>
#include "base/test/simple_test_tick_clock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event_modifiers.h"
using base::TimeDelta;
using blink::WebGestureDevice;
using blink::WebGestureEvent;
using blink::WebInputEvent;
using gfx::Vector2dF;
namespace ui {
namespace test {
static constexpr TimeDelta kEventDelta = TimeDelta::FromMilliseconds(10);
// Constants from fling_booster.cc
static constexpr double kMinBoostScrollSpeed = 150.;
static constexpr double kMinBoostFlingSpeed = 350.;
static constexpr base::TimeDelta kFlingBoostTimeoutDelay =
base::TimeDelta::FromSecondsD(0.05);
class FlingBoosterTest : public testing::Test {
public:
FlingBoosterTest() = default;
WebGestureEvent CreateFlingStart(
const gfx::Vector2dF& velocity,
int modifiers = 0,
WebGestureDevice source_device = WebGestureDevice::kTouchscreen) {
WebGestureEvent fling_start(WebInputEvent::kGestureFlingStart, modifiers,
event_time_, source_device);
fling_start.data.fling_start.velocity_x = velocity.x();
fling_start.data.fling_start.velocity_y = velocity.y();
return fling_start;
}
WebGestureEvent CreateFlingCancel(
WebGestureDevice source_device = WebGestureDevice::kTouchscreen) {
WebGestureEvent fling_cancel(WebInputEvent::kGestureFlingCancel, 0,
event_time_, source_device);
return fling_cancel;
}
WebGestureEvent CreateScrollBegin(
gfx::Vector2dF delta,
WebGestureDevice source_device = WebGestureDevice::kTouchscreen) {
WebGestureEvent scroll_begin(WebInputEvent::kGestureScrollBegin, 0,
event_time_, source_device);
scroll_begin.data.scroll_begin.delta_x_hint = delta.x();
scroll_begin.data.scroll_begin.delta_y_hint = delta.y();
scroll_begin.data.scroll_begin.delta_hint_units =
ui::input_types::ScrollGranularity::kScrollByPrecisePixel;
return scroll_begin;
}
WebGestureEvent CreateScrollUpdate(
gfx::Vector2dF delta,
WebGestureDevice source_device = WebGestureDevice::kTouchscreen) {
WebGestureEvent scroll_update(WebInputEvent::kGestureScrollUpdate, 0,
event_time_, source_device);
scroll_update.data.scroll_update.delta_x = delta.x();
scroll_update.data.scroll_update.delta_y = delta.y();
scroll_update.data.scroll_update.delta_units =
ui::input_types::ScrollGranularity::kScrollByPrecisePixel;
return scroll_update;
}
WebGestureEvent CreateScrollEnd(
WebGestureDevice source_device = WebGestureDevice::kTouchscreen) {
return WebGestureEvent(WebInputEvent::kGestureScrollEnd, 0, event_time_,
source_device);
}
Vector2dF DeltaFromVelocity(Vector2dF velocity, TimeDelta delta) {
float delta_seconds = static_cast<float>(delta.InSecondsF());
Vector2dF out = velocity;
out.Scale(1.f / delta_seconds);
return out;
}
Vector2dF SendFlingStart(WebGestureEvent event) {
DCHECK_EQ(WebInputEvent::kGestureFlingStart, event.GetType());
// The event will first be observed, then the FlingController will request
// a possibly boosted velocity.
fling_booster_.ObserveGestureEvent(event);
return fling_booster_.GetVelocityForFlingStart(event);
}
// Simulates the gesture scroll stream for a scroll that should create a
// boost.
void SimulateBoostingScroll() {
event_time_ += kEventDelta;
fling_booster_.ObserveGestureEvent(CreateFlingCancel());
fling_booster_.ObserveGestureEvent(CreateScrollEnd());
fling_booster_.ObserveGestureEvent(CreateScrollBegin(Vector2dF(0, 1)));
// GestureScrollUpdates in the same direction and at sufficient speed should
// be considered boosting. First GSU speed is ignored since we need 2 to
// determine velocity.
event_time_ += kEventDelta;
fling_booster_.ObserveGestureEvent(CreateScrollUpdate(Vector2dF(0, 1)));
event_time_ += kEventDelta;
fling_booster_.ObserveGestureEvent(CreateScrollUpdate(
DeltaFromVelocity(Vector2dF(0, kMinBoostScrollSpeed), kEventDelta)));
}
void ProgressFling(const Vector2dF& current_velocity,
const Vector2dF& delta) {
fling_booster_.ObserveGestureEvent(CreateScrollUpdate(delta));
fling_booster_.ObserveProgressFling(current_velocity);
}
protected:
base::TimeTicks event_time_ =
base::TimeTicks() + TimeDelta::FromSeconds(100000);
FlingBooster fling_booster_;
};
TEST_F(FlingBoosterTest, FlingBoostBasic) {
Vector2dF fling_velocity;
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 1000)));
EXPECT_EQ(Vector2dF(0, 1000), fling_velocity)
<< "First fling shouldn't be boosted";
SimulateBoostingScroll();
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 2000)));
EXPECT_EQ(Vector2dF(0, 3000), fling_velocity)
<< "FlingStart with ongoing fling should be boosted";
}
TEST_F(FlingBoosterTest, FlingBoostProgressedFling) {
Vector2dF fling_velocity;
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 1000)));
event_time_ += kEventDelta;
ProgressFling(Vector2dF(0, kMinBoostFlingSpeed), Vector2dF(0, 20));
SimulateBoostingScroll();
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 1100)));
EXPECT_EQ(Vector2dF(0, 2100), fling_velocity) << "Fling should be boosted";
}
TEST_F(FlingBoosterTest, NoFlingBoostIfScrollDelayed) {
Vector2dF fling_velocity;
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 1000)));
SimulateBoostingScroll();
// Delay longer than the timeout and ensure we don't boost.
event_time_ += kFlingBoostTimeoutDelay + TimeDelta::FromMilliseconds(1);
fling_booster_.ObserveGestureEvent(CreateScrollUpdate(Vector2dF(0, 10000)));
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 2000)));
EXPECT_EQ(Vector2dF(0, 2000), fling_velocity)
<< "ScrollUpdate delayed longer than boosting timeout; fling shouldn't "
"be boosted.";
}
TEST_F(FlingBoosterTest, NoFlingBoostIfBoostTooSlow) {
Vector2dF fling_velocity;
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 1000)));
SimulateBoostingScroll();
auto new_velocity = Vector2dF(0, kMinBoostFlingSpeed - 1);
fling_velocity = SendFlingStart(CreateFlingStart(new_velocity));
EXPECT_EQ(new_velocity, fling_velocity)
<< "Boosting FlingStart too slow; fling shouldn't be boosted.";
}
TEST_F(FlingBoosterTest, NoFlingBoostIfPreviousStartVelocityTooSlow) {
Vector2dF fling_velocity;
fling_velocity =
SendFlingStart(CreateFlingStart(Vector2dF(0, kMinBoostFlingSpeed - 1)));
SimulateBoostingScroll();
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 2000)));
EXPECT_EQ(Vector2dF(0, 2000), fling_velocity)
<< "Previous fling too slow and shouldn't be boosted.";
}
TEST_F(FlingBoosterTest, NoFlingBoostIfCurrentVelocityTooSlow) {
Vector2dF fling_velocity;
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 1000)));
event_time_ += kEventDelta;
ProgressFling(Vector2dF(0, kMinBoostFlingSpeed - 1), Vector2dF(0, 20));
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 1100)));
EXPECT_EQ(Vector2dF(0, 1100), fling_velocity)
<< "Existing fling too slow and shouldn't be boosted.";
}
TEST_F(FlingBoosterTest, NoFlingBoostIfFlingInDifferentDirection) {
Vector2dF fling_velocity;
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 1000)));
SimulateBoostingScroll();
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(1000, 0)));
EXPECT_EQ(Vector2dF(1000, 0), fling_velocity)
<< "Fling isn't in same direction, shouldn't boost.";
}
TEST_F(FlingBoosterTest, NoFlingBoostIfScrollInDifferentDirection) {
Vector2dF fling_velocity;
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 1000)));
SimulateBoostingScroll();
// Start a new scroll in an orthogonal direction and fling in the direction
// of the original fling.
event_time_ += kEventDelta;
fling_booster_.ObserveGestureEvent(CreateScrollUpdate(Vector2dF(1000, 0)));
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 2000)));
EXPECT_EQ(Vector2dF(0, 2000), fling_velocity)
<< "Scrolling in an orthogonal direction should prevent boosting, even "
"if the fling is in the original direction.";
}
TEST_F(FlingBoosterTest, NoFlingBoostIfPreventBoostingFlagIsSet) {
WebGestureEvent fling_start = CreateFlingStart(Vector2dF(0, 1000));
Vector2dF fling_velocity = SendFlingStart(fling_start);
// Start a new scroll.
event_time_ += kEventDelta;
WebGestureEvent cancel_event = CreateFlingCancel();
cancel_event.data.fling_cancel.prevent_boosting = true;
fling_booster_.ObserveGestureEvent(cancel_event);
fling_booster_.ObserveGestureEvent(CreateScrollBegin(Vector2dF(0, 1)));
// GestureScrollUpdates in the same direction and at sufficient speed should
// be considered boosting. However, since the prevent_boosting flag was set,
// we shouldn't boost.
event_time_ += kEventDelta;
fling_booster_.ObserveGestureEvent(CreateScrollUpdate(Vector2dF(0, 10000)));
event_time_ += kEventDelta;
fling_booster_.ObserveGestureEvent(CreateScrollUpdate(Vector2dF(0, 10000)));
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 2000)));
EXPECT_EQ(Vector2dF(0, 2000), fling_velocity)
<< "prevent_boosting on FlingCancel should avoid boosting a subsequent "
"FlingStart";
}
TEST_F(FlingBoosterTest, NoFlingBoostIfDifferentFlingModifiers) {
Vector2dF fling_velocity;
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 1000)));
SimulateBoostingScroll();
fling_velocity =
SendFlingStart(CreateFlingStart(Vector2dF(0, 2000), MODIFIER_SHIFT));
EXPECT_EQ(Vector2dF(0, 2000), fling_velocity)
<< "Changed modifier keys should prevent boost.";
}
TEST_F(FlingBoosterTest, NoFlingBoostIfDifferentFlingSourceDevices) {
Vector2dF fling_velocity;
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 1000)));
SimulateBoostingScroll();
fling_velocity = SendFlingStart(
CreateFlingStart(Vector2dF(0, 1000), 0, WebGestureDevice::kTouchpad));
EXPECT_EQ(Vector2dF(0, 1000), fling_velocity)
<< "Changed modifier keys should prevent boost.";
}
// Ensure a scroll starting after the boosting cutoff time doesn't extend the
// timeout.
TEST_F(FlingBoosterTest, NoFlingBoostIfScrollBeginPastCutoffTime) {
WebGestureEvent fling_start = CreateFlingStart(Vector2dF(0, 1000));
Vector2dF fling_velocity = SendFlingStart(fling_start);
ASSERT_EQ(Vector2dF(0, 1000), fling_velocity);
// Simulate a new fling boost delayed by more than the timeout.
{
event_time_ += kEventDelta;
fling_booster_.ObserveGestureEvent(CreateFlingCancel());
fling_booster_.ObserveGestureEvent(CreateScrollEnd());
event_time_ += kFlingBoostTimeoutDelay + TimeDelta::FromMilliseconds(1);
fling_booster_.ObserveGestureEvent(CreateScrollBegin(Vector2dF(0, 1)));
// GestureScrollUpdates in the same direction and at sufficient speed should
// be considered boosting. First GSU speed is ignored since we need 2 to
// determine velocity.
event_time_ += kEventDelta;
fling_booster_.ObserveGestureEvent(CreateScrollUpdate(Vector2dF(0, 1)));
event_time_ += kEventDelta;
fling_booster_.ObserveGestureEvent(CreateScrollUpdate(
DeltaFromVelocity(Vector2dF(0, kMinBoostScrollSpeed), kEventDelta)));
}
fling_velocity = SendFlingStart(CreateFlingStart(Vector2dF(0, 2000)));
EXPECT_EQ(Vector2dF(0, 2000), fling_velocity)
<< "Scroll must not be boosted since it occured after the boost timeout "
"delay.";
}
} // namespace test
} // namespace ui