#include "content/browser/renderer_host/input/fling_controller.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "build/chromecast_buildflags.h"
#include "content/browser/renderer_host/input/gesture_event_queue.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/ui_base_features.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/blink/fling_booster.h"
#include "ui/events/gestures/physics_based_fling_curve.h"
#if BUILDFLAG(IS_WIN)
#include "ui/display/win/test/scoped_screen_win.h"
#endif
using blink::WebGestureEvent;
using blink::WebInputEvent;
using blink::WebMouseWheelEvent;
using ui::PhysicsBasedFlingCurve;
namespace {
constexpr double kFrameDelta = 1000.0 / 60.0;
}
namespace content {
class FakeFlingController : public FlingController {
public:
FakeFlingController(FlingControllerEventSenderClient* event_sender_client,
FlingControllerSchedulerClient* scheduler_client,
const Config& config)
: FlingController(event_sender_client, scheduler_client, config) {}
};
class FlingControllerTest : public FlingControllerEventSenderClient,
public FlingControllerSchedulerClient,
public testing::TestWithParam<bool> {
public:
FlingControllerTest()
: needs_begin_frame_for_fling_progress_(GetParam()),
task_environment_(base::test::TaskEnvironment::MainThreadType::UI) {}
FlingControllerTest(const FlingControllerTest&) = delete;
FlingControllerTest& operator=(const FlingControllerTest&) = delete;
~FlingControllerTest() override {}
void SetUp() override {
fling_controller_ = std::make_unique<FakeFlingController>(
this, this, FlingController::Config());
fling_controller_->set_clock_for_testing(&mock_clock_);
AdvanceTime();
}
void SendGeneratedWheelEvent(
const MouseWheelEventWithLatencyInfo& wheel_event) override {
wheel_event_count_++;
last_sent_wheel_ = wheel_event.event;
first_wheel_event_sent_ = true;
if (wheel_event.event.momentum_phase == WebMouseWheelEvent::kPhaseEnded)
first_wheel_event_sent_ = false;
}
void SendGeneratedGestureScrollEvents(
const GestureEventWithLatencyInfo& gesture_event) override {
fling_controller_->ObserveAndMaybeConsumeGestureEvent(gesture_event);
sent_scroll_gesture_count_++;
last_sent_gesture_ = gesture_event.event;
}
gfx::Size GetRootWidgetViewportSize() override {
return gfx::Size(1920, 1080);
}
void ScheduleFlingProgress(
base::WeakPtr<FlingController> fling_controller) override {
DCHECK(!scheduled_next_fling_progress_);
scheduled_next_fling_progress_ = true;
}
void DidStopFlingingOnBrowser(
base::WeakPtr<FlingController> fling_controller) override {
notified_client_after_fling_stop_ = true;
}
bool NeedsBeginFrameForFlingProgress() override {
return needs_begin_frame_for_fling_progress_;
}
void SimulateFlingStart(blink::WebGestureDevice source_device,
const gfx::Vector2dF& velocity,
bool wait_before_processing = true) {
scheduled_next_fling_progress_ = false;
sent_scroll_gesture_count_ = 0;
WebGestureEvent fling_start(WebInputEvent::Type::kGestureFlingStart, 0,
NowTicks(), source_device);
fling_start.data.fling_start.velocity_x = velocity.x();
fling_start.data.fling_start.velocity_y = velocity.y();
GestureEventWithLatencyInfo fling_start_with_latency(fling_start);
if (wait_before_processing) {
AdvanceTime(base::RandInt(0, static_cast<int>(kFrameDelta)));
}
fling_controller_->ObserveAndMaybeConsumeGestureEvent(
fling_start_with_latency);
}
void SimulateScrollBegin(blink::WebGestureDevice source_device,
const gfx::Vector2dF& delta) {
WebGestureEvent scroll_begin(WebInputEvent::Type::kGestureScrollBegin, 0,
NowTicks(), 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.inertial_phase =
WebGestureEvent::InertialPhaseState::kNonMomentum;
scroll_begin.data.scroll_begin.delta_hint_units =
ui::ScrollGranularity::kScrollByPrecisePixel;
GestureEventWithLatencyInfo scroll_begin_with_latency(scroll_begin);
fling_controller_->ObserveAndMaybeConsumeGestureEvent(
scroll_begin_with_latency);
}
void SimulateScrollUpdate(blink::WebGestureDevice source_device,
const gfx::Vector2dF& delta) {
WebGestureEvent scroll_update(WebInputEvent::Type::kGestureScrollUpdate, 0,
NowTicks(), 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.velocity_x = delta.x();
scroll_update.data.scroll_update.velocity_y = delta.y();
scroll_update.data.scroll_update.inertial_phase =
WebGestureEvent::InertialPhaseState::kNonMomentum;
scroll_update.data.scroll_update.delta_units =
ui::ScrollGranularity::kScrollByPrecisePixel;
GestureEventWithLatencyInfo scroll_update_with_latency(scroll_update);
fling_controller_->ObserveAndMaybeConsumeGestureEvent(
scroll_update_with_latency);
}
void SimulateFlingCancel(blink::WebGestureDevice source_device) {
notified_client_after_fling_stop_ = false;
WebGestureEvent fling_cancel(WebInputEvent::Type::kGestureFlingCancel, 0,
NowTicks(), source_device);
if (source_device == blink::WebGestureDevice::kSyntheticAutoscroll)
fling_cancel.data.fling_cancel.prevent_boosting = true;
GestureEventWithLatencyInfo fling_cancel_with_latency(fling_cancel);
fling_controller_->ObserveAndMaybeConsumeGestureEvent(
fling_cancel_with_latency);
}
void ProgressFling(base::TimeTicks current_time) {
DCHECK(scheduled_next_fling_progress_);
scheduled_next_fling_progress_ = false;
fling_controller_->ProgressFling(current_time);
}
bool FlingInProgress() { return fling_controller_->fling_in_progress(); }
void AdvanceTime(double time_delta_ms = kFrameDelta) {
mock_clock_.Advance(base::Milliseconds(time_delta_ms));
}
base::TimeTicks NowTicks() const { return mock_clock_.NowTicks(); }
float CompleteFlingAndAccumulateScrollDelta() {
float total_scroll_delta = 0.f;
if (last_sent_gesture_.GetType() !=
WebInputEvent::Type::kGestureScrollEnd) {
DCHECK(last_sent_gesture_.GetType() ==
WebInputEvent::Type::kGestureScrollUpdate);
total_scroll_delta += last_sent_gesture_.data.scroll_update.delta_x;
}
while (true) {
AdvanceTime();
ProgressFling(NowTicks());
if (last_sent_gesture_.GetType() ==
WebInputEvent::Type::kGestureScrollEnd) {
break;
} else {
DCHECK(last_sent_gesture_.GetType() ==
WebInputEvent::Type::kGestureScrollUpdate);
total_scroll_delta += last_sent_gesture_.data.scroll_update.delta_x;
}
}
return total_scroll_delta;
}
protected:
std::unique_ptr<FakeFlingController> fling_controller_;
int wheel_event_count_ = 0;
WebMouseWheelEvent last_sent_wheel_;
WebGestureEvent last_sent_gesture_;
bool scheduled_next_fling_progress_ = false;
bool notified_client_after_fling_stop_ = false;
bool first_wheel_event_sent_ = false;
int sent_scroll_gesture_count_ = 0;
#if BUILDFLAG(IS_WIN)
display::win::test::ScopedScreenWin scoped_screen_win_;
#endif
private:
base::SimpleTestTickClock mock_clock_;
bool needs_begin_frame_for_fling_progress_;
base::test::TaskEnvironment task_environment_;
};
INSTANTIATE_TEST_SUITE_P(All, FlingControllerTest, testing::Bool());
TEST_P(FlingControllerTest,
ControllerSendsWheelEndOnTouchpadFlingWithZeroVelocity) {
SimulateFlingStart(blink::WebGestureDevice::kTouchpad, gfx::Vector2dF());
EXPECT_FALSE(FlingInProgress());
EXPECT_EQ(WebMouseWheelEvent::kPhaseEnded, last_sent_wheel_.momentum_phase);
EXPECT_EQ(0.f, last_sent_wheel_.delta_x);
EXPECT_EQ(0.f, last_sent_wheel_.delta_y);
}
TEST_P(FlingControllerTest,
ControllerSendsGSEOnTouchscreenFlingWithZeroVelocity) {
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen, gfx::Vector2dF());
EXPECT_FALSE(FlingInProgress());
EXPECT_EQ(WebInputEvent::Type::kGestureScrollEnd,
last_sent_gesture_.GetType());
}
TEST_P(FlingControllerTest, ControllerHandlesTouchpadGestureFling) {
SimulateFlingStart(blink::WebGestureDevice::kTouchpad,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
bool process_GFS_sent_first_event = first_wheel_event_sent_;
AdvanceTime();
ProgressFling(NowTicks());
if (!process_GFS_sent_first_event) {
EXPECT_EQ(WebMouseWheelEvent::kPhaseBegan, last_sent_wheel_.momentum_phase);
} else {
EXPECT_EQ(WebMouseWheelEvent::kPhaseChanged,
last_sent_wheel_.momentum_phase);
}
EXPECT_GT(last_sent_wheel_.delta_x, 0.f);
AdvanceTime();
ProgressFling(NowTicks());
EXPECT_EQ(WebMouseWheelEvent::kPhaseChanged, last_sent_wheel_.momentum_phase);
EXPECT_GT(last_sent_wheel_.delta_x, 0.f);
SimulateFlingCancel(blink::WebGestureDevice::kTouchpad);
EXPECT_FALSE(FlingInProgress());
}
TEST_P(FlingControllerTest, FlingStartsAtLastScrollUpdate) {
SimulateScrollUpdate(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0));
double time_to_advance_ms = 30.0;
AdvanceTime(time_to_advance_ms);
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0), false);
EXPECT_TRUE(FlingInProgress());
if (NeedsBeginFrameForFlingProgress())
ProgressFling(NowTicks());
EXPECT_EQ(1, sent_scroll_gesture_count_);
ASSERT_EQ(WebInputEvent::Type::kGestureScrollUpdate,
last_sent_gesture_.GetType());
EXPECT_NEAR(last_sent_gesture_.data.scroll_update.delta_x, 30.0, 5);
}
TEST_P(FlingControllerTest, InterruptedFlingIsntBoosted) {
double time_to_advance_ms = 8.0;
{
AdvanceTime(time_to_advance_ms);
SimulateScrollBegin(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(10, 0));
SimulateScrollUpdate(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(10, 0));
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0),
false);
ASSERT_TRUE(FlingInProgress());
if (NeedsBeginFrameForFlingProgress())
ProgressFling(NowTicks());
}
{
ASSERT_EQ(fling_controller_->CurrentFlingVelocity().x(), 1000);
fling_controller_->StopFling();
}
{
AdvanceTime(time_to_advance_ms);
SimulateScrollBegin(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(10, 0));
SimulateScrollUpdate(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(10, 0));
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0),
false);
if (NeedsBeginFrameForFlingProgress())
ProgressFling(NowTicks());
EXPECT_EQ(fling_controller_->CurrentFlingVelocity().x(), 1000)
<< "Fling was boosted but should not have been.";
}
}
TEST_P(FlingControllerTest, ControllerHandlesTouchscreenGestureFling) {
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
AdvanceTime();
ProgressFling(NowTicks());
ASSERT_EQ(WebInputEvent::Type::kGestureScrollUpdate,
last_sent_gesture_.GetType());
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kMomentum,
last_sent_gesture_.data.scroll_update.inertial_phase);
EXPECT_GT(last_sent_gesture_.data.scroll_update.delta_x, 0.f);
SimulateFlingCancel(blink::WebGestureDevice::kTouchscreen);
EXPECT_FALSE(FlingInProgress());
EXPECT_FALSE(FlingInProgress());
EXPECT_EQ(WebInputEvent::Type::kGestureScrollEnd,
last_sent_gesture_.GetType());
}
TEST_P(FlingControllerTest, ControllerSendsWheelEndWhenTouchpadFlingIsOver) {
SimulateFlingStart(blink::WebGestureDevice::kTouchpad,
gfx::Vector2dF(100, 0));
EXPECT_TRUE(FlingInProgress());
bool process_GFS_sent_first_event = first_wheel_event_sent_;
AdvanceTime();
ProgressFling(NowTicks());
if (!process_GFS_sent_first_event) {
EXPECT_EQ(WebMouseWheelEvent::kPhaseBegan, last_sent_wheel_.momentum_phase);
} else {
EXPECT_EQ(WebMouseWheelEvent::kPhaseChanged,
last_sent_wheel_.momentum_phase);
}
EXPECT_GT(last_sent_wheel_.delta_x, 0.f);
AdvanceTime();
ProgressFling(NowTicks());
while (FlingInProgress()) {
EXPECT_EQ(WebMouseWheelEvent::kPhaseChanged,
last_sent_wheel_.momentum_phase);
EXPECT_GT(last_sent_wheel_.delta_x, 0.f);
AdvanceTime();
ProgressFling(NowTicks());
}
EXPECT_EQ(WebMouseWheelEvent::kPhaseEnded, last_sent_wheel_.momentum_phase);
EXPECT_EQ(0.f, last_sent_wheel_.delta_x);
EXPECT_EQ(0.f, last_sent_wheel_.delta_y);
}
TEST_P(FlingControllerTest, ControllerSendsGSEWhenTouchscreenFlingIsOver) {
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(100, 0));
EXPECT_TRUE(FlingInProgress());
AdvanceTime();
ProgressFling(NowTicks());
while (FlingInProgress()) {
ASSERT_EQ(WebInputEvent::Type::kGestureScrollUpdate,
last_sent_gesture_.GetType());
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kMomentum,
last_sent_gesture_.data.scroll_update.inertial_phase);
EXPECT_GT(last_sent_gesture_.data.scroll_update.delta_x, 0.f);
AdvanceTime();
ProgressFling(NowTicks());
}
EXPECT_EQ(WebInputEvent::Type::kGestureScrollEnd,
last_sent_gesture_.GetType());
}
TEST_P(FlingControllerTest, EarlyTouchpadFlingCancelationOnFlingStop) {
SimulateFlingStart(blink::WebGestureDevice::kTouchpad,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
bool process_GFS_sent_first_event = first_wheel_event_sent_;
AdvanceTime();
ProgressFling(NowTicks());
if (!process_GFS_sent_first_event) {
EXPECT_EQ(WebMouseWheelEvent::kPhaseBegan, last_sent_wheel_.momentum_phase);
} else {
EXPECT_EQ(WebMouseWheelEvent::kPhaseChanged,
last_sent_wheel_.momentum_phase);
}
EXPECT_GT(last_sent_wheel_.delta_x, 0.f);
fling_controller_->StopFling();
EXPECT_FALSE(FlingInProgress());
EXPECT_EQ(WebMouseWheelEvent::kPhaseEnded, last_sent_wheel_.momentum_phase);
EXPECT_EQ(0.f, last_sent_wheel_.delta_x);
EXPECT_EQ(0.f, last_sent_wheel_.delta_y);
}
TEST_P(FlingControllerTest, EarlyTouchscreenFlingCancelationOnFlingStop) {
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
AdvanceTime();
ProgressFling(NowTicks());
ASSERT_EQ(WebInputEvent::Type::kGestureScrollUpdate,
last_sent_gesture_.GetType());
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kMomentum,
last_sent_gesture_.data.scroll_update.inertial_phase);
EXPECT_GT(last_sent_gesture_.data.scroll_update.delta_x, 0.f);
fling_controller_->StopFling();
EXPECT_FALSE(FlingInProgress());
EXPECT_EQ(WebInputEvent::Type::kGestureScrollEnd,
last_sent_gesture_.GetType());
}
TEST_P(FlingControllerTest, GestureFlingCancelOutsideFling) {
{
int current_sent_scroll_gesture_count = sent_scroll_gesture_count_;
SimulateFlingCancel(blink::WebGestureDevice::kTouchscreen);
EXPECT_FALSE(FlingInProgress());
EXPECT_EQ(current_sent_scroll_gesture_count, sent_scroll_gesture_count_);
}
{
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0));
AdvanceTime();
ProgressFling(NowTicks());
SimulateFlingCancel(blink::WebGestureDevice::kTouchscreen);
int current_sent_scroll_gesture_count = sent_scroll_gesture_count_;
SimulateFlingCancel(blink::WebGestureDevice::kTouchscreen);
EXPECT_EQ(current_sent_scroll_gesture_count, sent_scroll_gesture_count_);
}
}
TEST_P(FlingControllerTest, GestureFlingNotCancelledBySmallTimeDelta) {
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0), false);
EXPECT_TRUE(FlingInProgress());
int current_sent_scroll_gesture_count = sent_scroll_gesture_count_;
ProgressFling(NowTicks());
EXPECT_EQ(current_sent_scroll_gesture_count, sent_scroll_gesture_count_);
EXPECT_TRUE(FlingInProgress());
AdvanceTime();
ProgressFling(NowTicks());
EXPECT_EQ(blink::WebGestureDevice::kTouchscreen,
last_sent_gesture_.SourceDevice());
ASSERT_EQ(WebInputEvent::Type::kGestureScrollUpdate,
last_sent_gesture_.GetType());
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kMomentum,
last_sent_gesture_.data.scroll_update.inertial_phase);
EXPECT_GT(last_sent_gesture_.data.scroll_update.delta_x, 0.f);
}
TEST_P(FlingControllerTest, GestureFlingWithNegativeTimeDelta) {
base::TimeTicks initial_time = NowTicks();
AdvanceTime();
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
int current_sent_scroll_gesture_count = sent_scroll_gesture_count_;
ProgressFling(initial_time);
EXPECT_EQ(current_sent_scroll_gesture_count, sent_scroll_gesture_count_);
AdvanceTime();
ProgressFling(NowTicks());
EXPECT_EQ(blink::WebGestureDevice::kTouchscreen,
last_sent_gesture_.SourceDevice());
ASSERT_EQ(WebInputEvent::Type::kGestureScrollUpdate,
last_sent_gesture_.GetType());
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kMomentum,
last_sent_gesture_.data.scroll_update.inertial_phase);
EXPECT_GT(last_sent_gesture_.data.scroll_update.delta_x, 0.f);
}
TEST_P(FlingControllerTest, TouchpadFlingWithOldEvent) {
if (NeedsBeginFrameForFlingProgress())
return;
base::TimeTicks event_time = NowTicks();
WebGestureEvent fling_start(WebInputEvent::Type::kGestureFlingStart, 0,
event_time, blink::WebGestureDevice::kTouchpad);
fling_start.data.fling_start.velocity_x = 0.f;
fling_start.data.fling_start.velocity_y = -1000.f;
GestureEventWithLatencyInfo fling_start_with_latency(fling_start);
AdvanceTime(1.f);
base::TimeTicks last_frame_time = NowTicks();
AdvanceTime(1.f);
fling_controller_->ProcessGestureFlingStart(fling_start_with_latency);
EXPECT_TRUE(FlingInProgress());
EXPECT_EQ(1, wheel_event_count_);
wheel_event_count_ = 0;
AdvanceTime(1.f);
ProgressFling(last_frame_time);
EXPECT_EQ(0, wheel_event_count_);
AdvanceTime();
ProgressFling(NowTicks());
EXPECT_GT(wheel_event_count_, 0);
}
TEST_P(FlingControllerTest, ControllerBoostsTouchpadFling) {
SimulateFlingStart(blink::WebGestureDevice::kTouchpad,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
bool process_GFS_sent_first_event = first_wheel_event_sent_;
AdvanceTime();
ProgressFling(NowTicks());
if (!process_GFS_sent_first_event) {
EXPECT_EQ(WebMouseWheelEvent::kPhaseBegan, last_sent_wheel_.momentum_phase);
} else {
EXPECT_EQ(WebMouseWheelEvent::kPhaseChanged,
last_sent_wheel_.momentum_phase);
}
EXPECT_GT(last_sent_wheel_.delta_x, 0.f);
AdvanceTime();
ProgressFling(NowTicks());
EXPECT_EQ(WebMouseWheelEvent::kPhaseChanged, last_sent_wheel_.momentum_phase);
EXPECT_GT(last_sent_wheel_.delta_x, 0.f);
SimulateFlingCancel(blink::WebGestureDevice::kTouchpad);
EXPECT_FALSE(FlingInProgress());
SimulateFlingStart(blink::WebGestureDevice::kTouchpad,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
}
TEST_P(FlingControllerTest, ControllerBoostsTouchscreenFling) {
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
AdvanceTime();
ProgressFling(NowTicks());
ASSERT_EQ(WebInputEvent::Type::kGestureScrollUpdate,
last_sent_gesture_.GetType());
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kMomentum,
last_sent_gesture_.data.scroll_update.inertial_phase);
EXPECT_GT(last_sent_gesture_.data.scroll_update.delta_x, 0.f);
SimulateFlingCancel(blink::WebGestureDevice::kTouchscreen);
EXPECT_FALSE(FlingInProgress());
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
EXPECT_GT(fling_controller_->CurrentFlingVelocity().x(), 1000);
}
TEST_P(FlingControllerTest, ControllerDoesntBoostFinishedFling) {
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
AdvanceTime();
ProgressFling(NowTicks());
double time_to_advance_ms = 1000.0;
AdvanceTime(time_to_advance_ms);
ProgressFling(NowTicks());
ASSERT_EQ(WebInputEvent::Type::kGestureScrollEnd,
last_sent_gesture_.GetType())
<< "Unexpected Last Sent Gesture: "
<< WebInputEvent::GetName(last_sent_gesture_.GetType());
EXPECT_EQ(fling_controller_->CurrentFlingVelocity().x(), 0);
EXPECT_FALSE(FlingInProgress());
AdvanceTime();
SimulateFlingCancel(blink::WebGestureDevice::kTouchscreen);
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0), false);
EXPECT_TRUE(FlingInProgress());
EXPECT_EQ(fling_controller_->CurrentFlingVelocity().x(), 1000);
}
TEST_P(FlingControllerTest, ControllerNotifiesTheClientAfterFlingStart) {
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
SimulateFlingCancel(blink::WebGestureDevice::kTouchscreen);
EXPECT_FALSE(FlingInProgress());
EXPECT_TRUE(notified_client_after_fling_stop_);
}
TEST_P(FlingControllerTest, MiddleClickAutoScrollFling) {
SimulateFlingStart(blink::WebGestureDevice::kSyntheticAutoscroll,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
AdvanceTime();
ProgressFling(NowTicks());
ASSERT_EQ(WebInputEvent::Type::kGestureScrollUpdate,
last_sent_gesture_.GetType());
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kMomentum,
last_sent_gesture_.data.scroll_update.inertial_phase);
EXPECT_GT(last_sent_gesture_.data.scroll_update.delta_x, 0.f);
SimulateFlingStart(blink::WebGestureDevice::kSyntheticAutoscroll,
gfx::Vector2dF(2000, 0));
EXPECT_TRUE(FlingInProgress());
EXPECT_EQ(fling_controller_->CurrentFlingVelocity().x(), 2000);
SimulateFlingCancel(blink::WebGestureDevice::kSyntheticAutoscroll);
EXPECT_FALSE(FlingInProgress());
}
TEST_P(FlingControllerTest, NoFlingStartAfterWheelEventConsumed) {
fling_controller_->OnWheelEventAck(
MouseWheelEventWithLatencyInfo(),
blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kNotConsumed);
SimulateFlingStart(blink::WebGestureDevice::kTouchpad,
gfx::Vector2dF(1000, 0));
ASSERT_TRUE(FlingInProgress());
SimulateFlingCancel(blink::WebGestureDevice::kTouchpad);
EXPECT_FALSE(FlingInProgress());
fling_controller_->OnWheelEventAck(
MouseWheelEventWithLatencyInfo(),
blink::mojom::InputEventResultSource::kCompositorThread,
blink::mojom::InputEventResultState::kConsumed);
SimulateFlingStart(blink::WebGestureDevice::kTouchpad,
gfx::Vector2dF(1000, 0));
EXPECT_FALSE(FlingInProgress());
}
class FlingControllerWithPhysicsBasedFlingTest : public FlingControllerTest {
public:
FlingControllerWithPhysicsBasedFlingTest() {
scoped_feature_list_.InitAndEnableFeature(
features::kExperimentalFlingAnimation);
}
FlingControllerWithPhysicsBasedFlingTest(
const FlingControllerWithPhysicsBasedFlingTest&) = delete;
FlingControllerWithPhysicsBasedFlingTest& operator=(
const FlingControllerWithPhysicsBasedFlingTest&) = delete;
~FlingControllerWithPhysicsBasedFlingTest() override = default;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_TEST_SUITE_P(All,
FlingControllerWithPhysicsBasedFlingTest,
testing::Bool());
TEST_P(FlingControllerWithPhysicsBasedFlingTest,
ControllerBoostsTouchscreenFling) {
bool use_mobile_fling_curve = false;
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_IOS)
use_mobile_fling_curve = true;
#endif
if (use_mobile_fling_curve)
return;
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(4500, 0));
EXPECT_TRUE(FlingInProgress());
AdvanceTime();
ProgressFling(NowTicks());
ASSERT_EQ(WebInputEvent::Type::kGestureScrollUpdate,
last_sent_gesture_.GetType());
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kMomentum,
last_sent_gesture_.data.scroll_update.inertial_phase);
EXPECT_GT(last_sent_gesture_.data.scroll_update.delta_x, 0.f);
SimulateFlingCancel(blink::WebGestureDevice::kTouchscreen);
EXPECT_FALSE(FlingInProgress());
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(4500, 0));
EXPECT_TRUE(FlingInProgress());
if (NeedsBeginFrameForFlingProgress())
ProgressFling(NowTicks());
float total_scroll_delta = CompleteFlingAndAccumulateScrollDelta();
float expected_delta =
2 * PhysicsBasedFlingCurve::default_bounds_multiplier_for_testing() *
GetRootWidgetViewportSize().width();
EXPECT_EQ(ceilf(total_scroll_delta), roundf(expected_delta));
}
TEST_P(FlingControllerWithPhysicsBasedFlingTest,
ControllerDoesntBoostFinishedFling) {
bool use_mobile_fling_curve = false;
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_IOS)
use_mobile_fling_curve = true;
#endif
if (use_mobile_fling_curve)
return;
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0), true);
EXPECT_TRUE(FlingInProgress());
AdvanceTime();
ProgressFling(NowTicks());
double time_to_advance_ms = 1000.0;
AdvanceTime(time_to_advance_ms);
ProgressFling(NowTicks());
ASSERT_EQ(WebInputEvent::Type::kGestureScrollEnd,
last_sent_gesture_.GetType())
<< "Unexpected Last Sent Gesture: "
<< WebInputEvent::GetName(last_sent_gesture_.GetType());
EXPECT_EQ(fling_controller_->CurrentFlingVelocity().x(), 0);
EXPECT_FALSE(FlingInProgress());
AdvanceTime();
SimulateFlingCancel(blink::WebGestureDevice::kTouchscreen);
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(10000, 0));
EXPECT_TRUE(FlingInProgress());
if (NeedsBeginFrameForFlingProgress())
ProgressFling(NowTicks());
float total_scroll_delta = CompleteFlingAndAccumulateScrollDelta();
float expected_delta =
PhysicsBasedFlingCurve::default_bounds_multiplier_for_testing() *
GetRootWidgetViewportSize().width();
EXPECT_EQ(ceilf(total_scroll_delta), roundf(expected_delta));
}
}