blob: c9f2f2d03cf32dac0ffa24c4c619b0a8042197b7 [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 "content/browser/renderer_host/input/fling_controller.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/simple_test_tick_clock.h"
#include "build/build_config.h"
#include "content/browser/renderer_host/input/gesture_event_queue.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/blink/fling_booster.h"
using blink::WebGestureEvent;
using blink::WebInputEvent;
using blink::WebMouseWheelEvent;
namespace {
constexpr double kFrameDelta = 1000.0 / 60.0;
} // namespace
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:
// testing::Test
FlingControllerTest()
: needs_begin_frame_for_fling_progress_(GetParam()),
scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
~FlingControllerTest() override {}
void SetUp() override {
fling_controller_ = std::make_unique<FakeFlingController>(
this, this, FlingController::Config());
fling_controller_->set_clock_for_testing(&mock_clock_);
AdvanceTime();
}
// FlingControllerEventSenderClient
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 {
sent_scroll_gesture_count_++;
last_sent_gesture_ = gesture_event.event;
}
// FlingControllerSchedulerClient
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::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) {
// Wait for up to one frame before processing the event.
AdvanceTime(base::RandInt(0, static_cast<int>(kFrameDelta)));
}
fling_controller_->ObserveAndMaybeConsumeGestureEvent(
fling_start_with_latency);
}
void SimulateScrollUpdate(blink::WebGestureDevice source_device,
const gfx::Vector2dF& delta) {
WebGestureEvent scroll_update(WebInputEvent::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::input_types::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::kGestureFlingCancel, 0,
NowTicks(), source_device);
// autoscroll fling cancel doesn't allow fling boosting.
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::TimeDelta::FromMillisecondsD(time_delta_ms));
}
base::TimeTicks NowTicks() const { return mock_clock_.NowTicks(); }
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;
private:
base::SimpleTestTickClock mock_clock_;
bool needs_begin_frame_for_fling_progress_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
DISALLOW_COPY_AND_ASSIGN(FlingControllerTest);
};
INSTANTIATE_TEST_SUITE_P(, FlingControllerTest, testing::Bool());
TEST_P(FlingControllerTest,
ControllerSendsWheelEndOnTouchpadFlingWithZeroVelocity) {
SimulateFlingStart(blink::WebGestureDevice::kTouchpad, gfx::Vector2dF());
// The controller doesn't start a fling and sends a wheel end event
// immediately.
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());
// The controller doesn't start a fling and sends a GSE immediately.
EXPECT_FALSE(FlingInProgress());
EXPECT_EQ(WebInputEvent::kGestureScrollEnd, last_sent_gesture_.GetType());
}
TEST_P(FlingControllerTest, ControllerHandlesTouchpadGestureFling) {
SimulateFlingStart(blink::WebGestureDevice::kTouchpad,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
// Processing GFS will send the first fling progress event if the time delta
// between the timestamp of the GFS and the time that ProcessGestureFlingStart
// is called is large enough.
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);
// The rest of the wheel events must have momentum_phase == KPhaseChanged.
AdvanceTime();
ProgressFling(NowTicks());
EXPECT_EQ(WebMouseWheelEvent::kPhaseChanged, last_sent_wheel_.momentum_phase);
EXPECT_GT(last_sent_wheel_.delta_x, 0.f);
// Now cancel the fling.
SimulateFlingCancel(blink::WebGestureDevice::kTouchpad);
EXPECT_FALSE(FlingInProgress());
}
// Ensure that the start time of a fling is measured from the last received
// GSU. This ensures that the first progress fling during FlingStart should
// send significant delta. If we're using the FlingStart as the start time, we
// would send none or very little delta.
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), /*wait_before_processing=*/false);
EXPECT_TRUE(FlingInProgress());
if (NeedsBeginFrameForFlingProgress())
ProgressFling(NowTicks());
// We haven't advanced time since the FlingStart. Ensure we still send a
// significant amount of delta (~0.030sec * 1000pixels/sec) since we should
// be measuring the time since the last GSU.
EXPECT_EQ(1, sent_scroll_gesture_count_);
ASSERT_EQ(WebInputEvent::kGestureScrollUpdate, last_sent_gesture_.GetType());
EXPECT_NEAR(last_sent_gesture_.data.scroll_update.delta_x, 30.0, 5);
}
TEST_P(FlingControllerTest, ControllerHandlesTouchscreenGestureFling) {
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
// The fling progress will generate and send GSU events with inertial state.
AdvanceTime();
ProgressFling(NowTicks());
ASSERT_EQ(WebInputEvent::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);
// Now cancel the fling.
SimulateFlingCancel(blink::WebGestureDevice::kTouchscreen);
EXPECT_FALSE(FlingInProgress());
// Cancellation should send a GSE.
EXPECT_FALSE(FlingInProgress());
EXPECT_EQ(WebInputEvent::kGestureScrollEnd, last_sent_gesture_.GetType());
}
TEST_P(FlingControllerTest, ControllerSendsWheelEndWhenTouchpadFlingIsOver) {
SimulateFlingStart(blink::WebGestureDevice::kTouchpad,
gfx::Vector2dF(100, 0));
EXPECT_TRUE(FlingInProgress());
// Processing GFS will send the first fling progress event if the time delta
// between the timestamp of the GFS and the time that ProcessGestureFlingStart
// is called is large enough.
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::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::kGestureScrollEnd, last_sent_gesture_.GetType());
}
TEST_P(FlingControllerTest, EarlyTouchpadFlingCancelationOnFlingStop) {
SimulateFlingStart(blink::WebGestureDevice::kTouchpad,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
// Processing GFS will send the first fling progress event if the time delta
// between the timestamp of the GFS and the time that ProcessGestureFlingStart
// is called is large enough.
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());
// progress fling must send GSU events.
AdvanceTime();
ProgressFling(NowTicks());
ASSERT_EQ(WebInputEvent::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::kGestureScrollEnd, last_sent_gesture_.GetType());
}
TEST_P(FlingControllerTest, GestureFlingCancelOutsideFling) {
// FlingCancel without a FlingStart doesn't cause issues, doesn't send any
// events.
{
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_);
}
// Do a fling and cancel it. Make sure another cancel is also a no-op.
{
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_;
// If we the first progress tick happens too close to the fling_start time,
// the controller won't send any GSU events, but the fling is still active.
ProgressFling(NowTicks());
EXPECT_EQ(current_sent_scroll_gesture_count, sent_scroll_gesture_count_);
EXPECT_TRUE(FlingInProgress());
// The rest of the progress flings must advance the fling normally.
AdvanceTime();
ProgressFling(NowTicks());
EXPECT_EQ(blink::WebGestureDevice::kTouchscreen,
last_sent_gesture_.SourceDevice());
ASSERT_EQ(WebInputEvent::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_;
// If we get a negative time delta, that is, the Progress tick time happens
// before the fling's start time then we should *not* try progressing the
// fling.
ProgressFling(initial_time);
EXPECT_EQ(current_sent_scroll_gesture_count, sent_scroll_gesture_count_);
// The rest of the progress flings must advance the fling normally.
AdvanceTime();
ProgressFling(NowTicks());
EXPECT_EQ(blink::WebGestureDevice::kTouchscreen,
last_sent_gesture_.SourceDevice());
ASSERT_EQ(WebInputEvent::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);
}
// Regression test for https://crbug.com/924279
TEST_P(FlingControllerTest, TouchpadFlingWithOldEvent) {
// Only the code path that uses compositor animation observers is affected.
if (NeedsBeginFrameForFlingProgress())
return;
// Create a fling start event.
base::TimeTicks event_time = NowTicks();
WebGestureEvent fling_start(WebInputEvent::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);
// Move time forward. Assume a frame occurs here.
AdvanceTime(1.f);
base::TimeTicks last_frame_time = NowTicks();
// Start the fling animation later, as if there was a delay in event dispatch.
AdvanceTime(1.f);
fling_controller_->ProcessGestureFlingStart(fling_start_with_latency);
EXPECT_TRUE(FlingInProgress());
// Initial scroll was sent.
EXPECT_EQ(1, wheel_event_count_);
wheel_event_count_ = 0;
// Move time forward a little.
AdvanceTime(1.f);
// Simulate the compositor animation observer calling ProgressFling with the
// last frame time. That frame time is after the event time, but before the
// animation start time.
ProgressFling(last_frame_time);
// No scrolls were sent.
EXPECT_EQ(0, wheel_event_count_);
// Additional ProgressFling calls generate scroll events as normal.
AdvanceTime();
ProgressFling(NowTicks());
EXPECT_GT(wheel_event_count_, 0);
}
TEST_P(FlingControllerTest, ControllerBoostsTouchpadFling) {
SimulateFlingStart(blink::WebGestureDevice::kTouchpad,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
// Processing GFS will send the first fling progress event if the time delta
// between the timestamp of the GFS and the time that ProcessGestureFlingStart
// is called is large enough.
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);
// The rest of the wheel events must have momentum_phase == KPhaseChanged.
AdvanceTime();
ProgressFling(NowTicks());
EXPECT_EQ(WebMouseWheelEvent::kPhaseChanged, last_sent_wheel_.momentum_phase);
EXPECT_GT(last_sent_wheel_.delta_x, 0.f);
// Now cancel the fling.
SimulateFlingCancel(blink::WebGestureDevice::kTouchpad);
EXPECT_FALSE(FlingInProgress());
// The second GFS will boost the current active fling.
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());
// Fling progress must send GSU events.
AdvanceTime();
ProgressFling(NowTicks());
ASSERT_EQ(WebInputEvent::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);
// Now cancel the fling.
SimulateFlingCancel(blink::WebGestureDevice::kTouchscreen);
EXPECT_FALSE(FlingInProgress());
// The second GFS can be boosted so it should boost the just deactivated
// fling.
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
EXPECT_GT(fling_controller_->CurrentFlingVelocity().x(), 1000);
}
TEST_P(FlingControllerTest, ControllerNotifiesTheClientAfterFlingStart) {
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0));
EXPECT_TRUE(FlingInProgress());
// Now cancel the fling.
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::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);
// Now send a new fling with different velocity and without sending a fling
// cancel event, the new fling should always replace the old one even when
// they are in the same direction.
SimulateFlingStart(blink::WebGestureDevice::kSyntheticAutoscroll,
gfx::Vector2dF(2000, 0));
EXPECT_TRUE(FlingInProgress());
EXPECT_EQ(fling_controller_->CurrentFlingVelocity().x(), 2000);
// Now cancel the fling. The GFC won't get suppressed by fling booster since
// autoscroll fling doesn't have boosting.
SimulateFlingCancel(blink::WebGestureDevice::kSyntheticAutoscroll);
EXPECT_FALSE(FlingInProgress());
}
} // namespace content