blob: d1898cac2227f196c4b50a1f48e93322b6833bd7 [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/input/mouse_wheel_event_queue.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include "base/location.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "components/input/timeout_monitor.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/input/synthetic_web_input_event_builders.h"
#include "third_party/blink/public/common/input/web_input_event.h"
#include "ui/events/base_event_utils.h"
using blink::WebGestureEvent;
using blink::WebInputEvent;
using blink::WebMouseWheelEvent;
namespace input {
namespace {
const float kWheelScrollX = 10;
const float kWheelScrollY = 12;
const float kWheelScrollGlobalX = 50;
const float kWheelScrollGlobalY = 72;
#define EXPECT_GESTURE_SCROLL_BEGIN_IMPL(event) \
EXPECT_EQ(WebInputEvent::Type::kGestureScrollBegin, event->GetType()); \
EXPECT_EQ(kWheelScrollX, event->PositionInWidget().x()); \
EXPECT_EQ(kWheelScrollY, event->PositionInWidget().y()); \
EXPECT_EQ(kWheelScrollGlobalX, event->PositionInScreen().x()); \
EXPECT_EQ(kWheelScrollGlobalY, event->PositionInScreen().y()); \
EXPECT_EQ(scroll_units, event->data.scroll_begin.delta_hint_units);
#define EXPECT_GESTURE_SCROLL_BEGIN(event) \
EXPECT_GESTURE_SCROLL_BEGIN_IMPL(event); \
EXPECT_FALSE(event->data.scroll_begin.synthetic); \
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kUnknownMomentum, \
event->data.scroll_begin.inertial_phase);
#define EXPECT_GESTURE_SCROLL_BEGIN_WITH_PHASE(event) \
EXPECT_GESTURE_SCROLL_BEGIN_IMPL(event); \
EXPECT_FALSE(event->data.scroll_begin.synthetic); \
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kNonMomentum, \
event->data.scroll_begin.inertial_phase);
#define EXPECT_SYNTHETIC_GESTURE_SCROLL_BEGIN(event) \
EXPECT_GESTURE_SCROLL_BEGIN_IMPL(event); \
EXPECT_TRUE(event->data.scroll_begin.synthetic); \
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kNonMomentum, \
event->data.scroll_begin.inertial_phase);
#define EXPECT_INERTIAL_GESTURE_SCROLL_BEGIN(event) \
EXPECT_GESTURE_SCROLL_BEGIN_IMPL(event); \
EXPECT_FALSE(event->data.scroll_begin.synthetic); \
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kMomentum, \
event->data.scroll_begin.inertial_phase);
#define EXPECT_SYNTHETIC_INERTIAL_GESTURE_SCROLL_BEGIN(event) \
EXPECT_GESTURE_SCROLL_BEGIN_IMPL(event); \
EXPECT_TRUE(event->data.scroll_begin.synthetic); \
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kMomentum, \
event->data.scroll_begin.inertial_phase);
#define EXPECT_GESTURE_SCROLL_UPDATE_IMPL(event) \
EXPECT_EQ(WebInputEvent::Type::kGestureScrollUpdate, event->GetType()); \
EXPECT_EQ(scroll_units, event->data.scroll_update.delta_units); \
EXPECT_EQ(kWheelScrollX, event->PositionInWidget().x()); \
EXPECT_EQ(kWheelScrollY, event->PositionInWidget().y()); \
EXPECT_EQ(kWheelScrollGlobalX, event->PositionInScreen().x()); \
EXPECT_EQ(kWheelScrollGlobalY, event->PositionInScreen().y());
#define EXPECT_GESTURE_SCROLL_UPDATE(event) \
EXPECT_GESTURE_SCROLL_UPDATE_IMPL(event); \
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kUnknownMomentum, \
event->data.scroll_update.inertial_phase);
#define EXPECT_GESTURE_SCROLL_UPDATE_WITH_PHASE(event) \
EXPECT_GESTURE_SCROLL_UPDATE_IMPL(event); \
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kNonMomentum, \
event->data.scroll_update.inertial_phase);
#define EXPECT_INERTIAL_GESTURE_SCROLL_UPDATE(event) \
EXPECT_GESTURE_SCROLL_UPDATE_IMPL(event); \
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kMomentum, \
event->data.scroll_update.inertial_phase);
#define EXPECT_GESTURE_SCROLL_END_IMPL(event) \
EXPECT_EQ(WebInputEvent::Type::kGestureScrollEnd, event->GetType()); \
EXPECT_EQ(scroll_units, event->data.scroll_end.delta_units); \
EXPECT_EQ(kWheelScrollX, event->PositionInWidget().x()); \
EXPECT_EQ(kWheelScrollY, event->PositionInWidget().y()); \
EXPECT_EQ(kWheelScrollGlobalX, event->PositionInScreen().x()); \
EXPECT_EQ(kWheelScrollGlobalY, event->PositionInScreen().y());
#define EXPECT_GESTURE_SCROLL_END(event) \
EXPECT_GESTURE_SCROLL_END_IMPL(event); \
EXPECT_FALSE(event->data.scroll_end.synthetic); \
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kUnknownMomentum, \
event->data.scroll_end.inertial_phase);
#define EXPECT_GESTURE_SCROLL_END_WITH_PHASE(event) \
EXPECT_GESTURE_SCROLL_END_IMPL(event); \
EXPECT_FALSE(event->data.scroll_end.synthetic); \
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kNonMomentum, \
event->data.scroll_end.inertial_phase);
#define EXPECT_SYNTHETIC_GESTURE_SCROLL_END(event) \
EXPECT_GESTURE_SCROLL_END_IMPL(event); \
EXPECT_TRUE(event->data.scroll_end.synthetic); \
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kNonMomentum, \
event->data.scroll_end.inertial_phase);
#define EXPECT_INERTIAL_GESTURE_SCROLL_END(event) \
EXPECT_GESTURE_SCROLL_END_IMPL(event); \
EXPECT_FALSE(event->data.scroll_end.synthetic); \
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kMomentum, \
event->data.scroll_end.inertial_phase);
#if BUILDFLAG(IS_CHROMEOS)
#define EXPECT_SYNTHETIC_INERTIAL_GESTURE_SCROLL_END(event) \
EXPECT_GESTURE_SCROLL_END_IMPL(event); \
EXPECT_TRUE(event->data.scroll_end.synthetic); \
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kMomentum, \
event->data.scroll_end.inertial_phase); \
EXPECT_TRUE(event->data.scroll_end.generated_by_fling_controller);
#endif
#define EXPECT_MOUSE_WHEEL(event) \
EXPECT_EQ(WebInputEvent::Type::kMouseWheel, event->GetType());
} // namespace
class MouseWheelEventQueueTest : public testing::Test,
public MouseWheelEventQueueClient {
public:
MouseWheelEventQueueTest()
: task_environment_(
base::test::SingleThreadTaskEnvironment::MainThreadType::UI),
acked_event_count_(0),
last_acked_event_state_(blink::mojom::InputEventResultState::kUnknown) {
queue_ = std::make_unique<MouseWheelEventQueue>(this);
}
~MouseWheelEventQueueTest() override = default;
// MouseWheelEventQueueClient
void SendMouseWheelEventImmediately(
const MouseWheelEventWithLatencyInfo& event,
MouseWheelEventHandledCallback callback,
DispatchToRendererCallback& dispatch_callback) override {
WebMouseWheelEvent* cloned_event = new WebMouseWheelEvent();
std::unique_ptr<WebInputEvent> cloned_event_holder(cloned_event);
*cloned_event = event.event;
sent_events_.push_back(std::move(cloned_event_holder));
// Currently the tests assume the events are always dispatched, to simulate
// events not being dispatched the `dispatch_callback` should be run with
// `kNotDispatched`.
std::move(dispatch_callback)
.Run(event.event, DispatchToRendererResult::kDispatched);
callbacks_.emplace_back(base::BindOnce(
[](MouseWheelEventHandledCallback callback,
const MouseWheelEventWithLatencyInfo& event,
blink::mojom::InputEventResultSource ack_source,
blink::mojom::InputEventResultState ack_result) {
std::move(callback).Run(event, ack_source, ack_result);
},
std::move(callback), event));
}
void ForwardGestureEventWithLatencyInfo(
const blink::WebGestureEvent& event,
const ui::LatencyInfo& latency_info) override {
WebGestureEvent* cloned_event = new WebGestureEvent();
std::unique_ptr<WebInputEvent> cloned_event_holder(cloned_event);
*cloned_event = event;
if (event.GetType() == WebInputEvent::Type::kGestureScrollBegin) {
is_wheel_scroll_in_progress_ = true;
} else if (event.GetType() == WebInputEvent::Type::kGestureScrollEnd) {
is_wheel_scroll_in_progress_ = false;
}
sent_events_.push_back(std::move(cloned_event_holder));
}
void OnMouseWheelEventAck(
const MouseWheelEventWithLatencyInfo& event,
blink::mojom::InputEventResultSource ack_source,
blink::mojom::InputEventResultState ack_result) override {
++acked_event_count_;
last_acked_event_ = event.event;
last_acked_event_state_ = ack_result;
}
bool IsWheelScrollInProgress() override {
return is_wheel_scroll_in_progress_;
}
bool IsAutoscrollInProgress() override { return false; }
protected:
using HandleEventCallback =
base::OnceCallback<void(blink::mojom::InputEventResultSource ack_source,
blink::mojom::InputEventResultState ack_result)>;
size_t queued_event_count() const { return queue_->queued_size(); }
bool event_in_flight() const { return queue_->event_in_flight(); }
std::vector<std::unique_ptr<WebInputEvent>>& all_sent_events() {
return sent_events_;
}
const std::unique_ptr<WebInputEvent>& sent_input_event(size_t index) {
return sent_events_[index];
}
const WebGestureEvent* sent_gesture_event(size_t index) {
return static_cast<WebGestureEvent*>(sent_events_[index].get());
}
const WebMouseWheelEvent& acked_event() const { return last_acked_event_; }
size_t GetAndResetSentEventCount() {
size_t count = sent_events_.size();
sent_events_.clear();
return count;
}
size_t GetAndResetAckedEventCount() {
size_t count = acked_event_count_;
acked_event_count_ = 0;
return count;
}
void SendMouseWheelEventAck(blink::mojom::InputEventResultState ack_result) {
std::move(callbacks_.front())
.Run(blink::mojom::InputEventResultSource::kCompositorThread,
ack_result);
callbacks_.pop_front();
}
void SendMouseWheel(float x,
float y,
float global_x,
float global_y,
float dX,
float dY,
int modifiers,
bool high_precision,
blink::WebMouseWheelEvent::Phase phase,
blink::WebMouseWheelEvent::Phase momentum_phase,
WebInputEvent::RailsMode rails_mode,
bool has_synthetic_phase = false) {
WebMouseWheelEvent event = blink::SyntheticWebMouseWheelEventBuilder::Build(
x, y, global_x, global_y, dX, dY, modifiers,
high_precision ? ui::ScrollGranularity::kScrollByPrecisePixel
: ui::ScrollGranularity::kScrollByPixel);
event.phase = phase;
event.momentum_phase = momentum_phase;
event.rails_mode = rails_mode;
event.has_synthetic_phase = has_synthetic_phase;
{
ScopedDispatchToRendererCallback dispatch_callback(base::DoNothing());
queue_->QueueEvent(MouseWheelEventWithLatencyInfo(event),
dispatch_callback.callback);
}
}
void SendMouseWheel(float x,
float y,
float global_x,
float global_y,
float dX,
float dY,
int modifiers,
bool high_precision,
blink::WebMouseWheelEvent::Phase phase,
blink::WebMouseWheelEvent::Phase momentum_phase,
bool has_synthetic_phase = false) {
SendMouseWheel(x, y, global_x, global_y, dX, dY, modifiers, high_precision,
phase, momentum_phase, WebInputEvent::kRailsModeFree,
has_synthetic_phase);
}
void SendGestureEvent(WebInputEvent::Type type) {
WebGestureEvent event(type, WebInputEvent::kNoModifiers,
ui::EventTimeForNow(),
blink::WebGestureDevice::kTouchscreen);
queue_->OnGestureScrollEvent(
GestureEventWithLatencyInfo(event, ui::LatencyInfo()));
}
static void RunTasksAndWait(base::TimeDelta delay) {
base::RunLoop loop;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, loop.QuitWhenIdleClosure(), delay);
loop.Run();
}
void GestureSendingTest(bool high_precision) {
const ui::ScrollGranularity scroll_units =
high_precision ? ui::ScrollGranularity::kScrollByPrecisePixel
: ui::ScrollGranularity::kScrollByPixel;
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 1, 1, 0, high_precision,
WebMouseWheelEvent::kPhaseBegan,
WebMouseWheelEvent::kPhaseNone);
EXPECT_EQ(0U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// The second mouse wheel should not be sent since one is already in
// queue.
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 5, 5, 0, high_precision,
WebMouseWheelEvent::kPhaseChanged,
WebMouseWheelEvent::kPhaseNone);
EXPECT_EQ(1U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(0U, GetAndResetSentEventCount());
// Receive an ACK for the mouse wheel event and release the next
// mouse wheel event.
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
EXPECT_EQ(0U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(WebInputEvent::Type::kMouseWheel, acked_event().GetType());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(3U, all_sent_events().size());
EXPECT_GESTURE_SCROLL_BEGIN_WITH_PHASE(sent_gesture_event(0));
EXPECT_GESTURE_SCROLL_UPDATE_WITH_PHASE(sent_gesture_event(1));
EXPECT_MOUSE_WHEEL(sent_input_event(2));
EXPECT_EQ(3U, GetAndResetSentEventCount());
}
void PhaseGestureSendingTest(bool high_precision) {
const ui::ScrollGranularity scroll_units =
high_precision ? ui::ScrollGranularity::kScrollByPrecisePixel
: ui::ScrollGranularity::kScrollByPixel;
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 1, 1, 0, high_precision,
WebMouseWheelEvent::kPhaseBegan,
WebMouseWheelEvent::kPhaseNone);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
EXPECT_EQ(2U, all_sent_events().size());
EXPECT_GESTURE_SCROLL_BEGIN_WITH_PHASE(sent_gesture_event(0));
EXPECT_GESTURE_SCROLL_UPDATE_WITH_PHASE(sent_gesture_event(1));
EXPECT_EQ(2U, GetAndResetSentEventCount());
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 5, 5, 0, high_precision,
WebMouseWheelEvent::kPhaseChanged,
WebMouseWheelEvent::kPhaseNone);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
EXPECT_EQ(1U, all_sent_events().size());
EXPECT_GESTURE_SCROLL_UPDATE_WITH_PHASE(sent_gesture_event(0));
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 5, 5, 0, high_precision,
WebMouseWheelEvent::kPhaseNone,
WebMouseWheelEvent::kPhaseBegan);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
// A fling has started, no ScrollEnd/ScrollBegin is sent.
EXPECT_EQ(1U, all_sent_events().size());
EXPECT_INERTIAL_GESTURE_SCROLL_UPDATE(sent_gesture_event(0));
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 5, 5, 0, high_precision,
WebMouseWheelEvent::kPhaseNone,
WebMouseWheelEvent::kPhaseChanged);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
EXPECT_EQ(1U, all_sent_events().size());
EXPECT_INERTIAL_GESTURE_SCROLL_UPDATE(sent_gesture_event(0));
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 0, 0, 0, high_precision,
WebMouseWheelEvent::kPhaseNone,
WebMouseWheelEvent::kPhaseEnded);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
// MomentumPhase is ended, the scroll is done, and GSE is sent
// immediately.
EXPECT_EQ(1U, all_sent_events().size());
EXPECT_INERTIAL_GESTURE_SCROLL_END(sent_gesture_event(0));
EXPECT_EQ(1U, GetAndResetSentEventCount());
}
base::test::SingleThreadTaskEnvironment task_environment_;
std::unique_ptr<MouseWheelEventQueue> queue_;
std::vector<std::unique_ptr<WebInputEvent>> sent_events_;
base::circular_deque<HandleEventCallback> callbacks_;
size_t acked_event_count_;
blink::mojom::InputEventResultState last_acked_event_state_;
WebMouseWheelEvent last_acked_event_;
private:
bool is_wheel_scroll_in_progress_ = false;
};
// Tests that mouse wheel events are queued properly.
TEST_F(MouseWheelEventQueueTest, Basic) {
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 1, 1, 0, false,
WebMouseWheelEvent::kPhaseBegan,
WebMouseWheelEvent::kPhaseNone);
EXPECT_EQ(0U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// The second mouse wheel should not be sent since one is already in queue.
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 5, 5, 0, false,
WebMouseWheelEvent::kPhaseChanged,
WebMouseWheelEvent::kPhaseNone);
EXPECT_EQ(1U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(0U, GetAndResetSentEventCount());
// Receive an ACK for the first mouse wheel event.
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kConsumed);
EXPECT_EQ(0U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(WebInputEvent::Type::kMouseWheel, acked_event().GetType());
// Receive an ACK for the second mouse wheel event.
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kConsumed);
EXPECT_EQ(0U, queued_event_count());
EXPECT_FALSE(event_in_flight());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(WebInputEvent::Type::kMouseWheel, acked_event().GetType());
}
TEST_F(MouseWheelEventQueueTest, GestureSending) {
GestureSendingTest(false);
}
TEST_F(MouseWheelEventQueueTest, GestureSendingPrecisePixels) {
GestureSendingTest(true);
}
TEST_F(MouseWheelEventQueueTest, GestureSendingWithPhaseInformation) {
PhaseGestureSendingTest(false);
}
TEST_F(MouseWheelEventQueueTest,
GestureSendingWithPhaseInformationPrecisePixels) {
PhaseGestureSendingTest(true);
}
// Tests that a Wheel event with synthetic momentumn_phase == PhaseEnded that is
// generated by the fling controller properly populates
// scroll_end.data.scroll_end.generated_by_fling_controller.
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(MouseWheelEventQueueTest, WheelEndWithMomentumPhaseEndedInformation) {
const ui::ScrollGranularity scroll_units =
ui::ScrollGranularity::kScrollByPrecisePixel;
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 1, 1, 0, true /* high_precision */,
WebMouseWheelEvent::kPhaseBegan,
WebMouseWheelEvent::kPhaseNone);
EXPECT_EQ(0U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
EXPECT_GESTURE_SCROLL_BEGIN_WITH_PHASE(sent_gesture_event(0));
EXPECT_GESTURE_SCROLL_UPDATE_WITH_PHASE(sent_gesture_event(1));
EXPECT_EQ(2U, GetAndResetSentEventCount());
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 0, 0, 0, true /* high_precision */,
WebMouseWheelEvent::kPhaseNone,
WebMouseWheelEvent::kPhaseEnded, true /*has_synthetic_phase*/);
EXPECT_EQ(0U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
EXPECT_EQ(1U, all_sent_events().size());
EXPECT_SYNTHETIC_INERTIAL_GESTURE_SCROLL_END(sent_gesture_event(0));
EXPECT_EQ(1U, GetAndResetSentEventCount());
}
#endif // BUILDFLAG(IS_CHROMEOS)
TEST_F(MouseWheelEventQueueTest, GestureSendingInterrupted) {
const ui::ScrollGranularity scroll_units =
ui::ScrollGranularity::kScrollByPixel;
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 1, 1, 0, false,
WebMouseWheelEvent::kPhaseBegan,
WebMouseWheelEvent::kPhaseNone);
EXPECT_EQ(0U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Receive an ACK for the mouse wheel event.
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
EXPECT_EQ(0U, queued_event_count());
EXPECT_FALSE(event_in_flight());
EXPECT_EQ(WebInputEvent::Type::kMouseWheel, acked_event().GetType());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(2U, all_sent_events().size());
EXPECT_GESTURE_SCROLL_BEGIN_WITH_PHASE(sent_gesture_event(0));
EXPECT_GESTURE_SCROLL_UPDATE_WITH_PHASE(sent_gesture_event(1));
EXPECT_EQ(2U, GetAndResetSentEventCount());
// When a touch based GSB arrives in the
// middle of wheel scrolling sequence, a synthetic wheel event with zero
// deltas and phase = |kPhaseEnded| will be sent.
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 0, 0, 0, false,
WebMouseWheelEvent::kPhaseEnded,
WebMouseWheelEvent::kPhaseNone);
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Ensure that a gesture scroll begin terminates the current scroll event.
SendGestureEvent(WebInputEvent::Type::kGestureScrollBegin);
EXPECT_EQ(2U, all_sent_events().size());
EXPECT_GESTURE_SCROLL_END_WITH_PHASE(sent_gesture_event(1));
EXPECT_EQ(2U, GetAndResetSentEventCount());
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 1, 1, 0, false,
WebMouseWheelEvent::kPhaseBegan,
WebMouseWheelEvent::kPhaseNone);
EXPECT_EQ(0U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// New mouse wheel events won't cause gestures because a scroll
// is already in progress by another device.
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
EXPECT_EQ(0U, queued_event_count());
EXPECT_FALSE(event_in_flight());
EXPECT_EQ(WebInputEvent::Type::kMouseWheel, acked_event().GetType());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, all_sent_events().size());
SendGestureEvent(WebInputEvent::Type::kGestureScrollEnd);
EXPECT_EQ(0U, all_sent_events().size());
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 1, 1, 0, false,
WebMouseWheelEvent::kPhaseBegan,
WebMouseWheelEvent::kPhaseNone);
EXPECT_EQ(0U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Receive an ACK for the mouse wheel event.
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
EXPECT_EQ(0U, queued_event_count());
EXPECT_FALSE(event_in_flight());
EXPECT_EQ(WebInputEvent::Type::kMouseWheel, acked_event().GetType());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(2U, all_sent_events().size());
EXPECT_GESTURE_SCROLL_BEGIN_WITH_PHASE(sent_gesture_event(0));
EXPECT_GESTURE_SCROLL_UPDATE_WITH_PHASE(sent_gesture_event(1));
EXPECT_EQ(2U, GetAndResetSentEventCount());
}
TEST_F(MouseWheelEventQueueTest, GestureRailScrolling) {
const ui::ScrollGranularity scroll_units =
ui::ScrollGranularity::kScrollByPixel;
SendMouseWheel(
kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX, kWheelScrollGlobalY, 1,
1, 0, false, WebMouseWheelEvent::kPhaseBegan,
WebMouseWheelEvent::kPhaseNone, WebInputEvent::kRailsModeHorizontal);
EXPECT_EQ(0U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Receive an ACK for the mouse wheel event.
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
EXPECT_EQ(0U, queued_event_count());
EXPECT_FALSE(event_in_flight());
EXPECT_EQ(WebInputEvent::Type::kMouseWheel, acked_event().GetType());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(2U, all_sent_events().size());
EXPECT_GESTURE_SCROLL_BEGIN_WITH_PHASE(sent_gesture_event(0));
EXPECT_GESTURE_SCROLL_UPDATE_WITH_PHASE(sent_gesture_event(1));
EXPECT_EQ(1U, sent_gesture_event(1)->data.scroll_update.delta_x);
EXPECT_EQ(0U, sent_gesture_event(1)->data.scroll_update.delta_y);
EXPECT_EQ(2U, GetAndResetSentEventCount());
SendMouseWheel(
kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX, kWheelScrollGlobalY, 1,
1, 0, false, WebMouseWheelEvent::kPhaseChanged,
WebMouseWheelEvent::kPhaseNone, WebInputEvent::kRailsModeVertical);
EXPECT_EQ(0U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Receive an ACK for the mouse wheel event.
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
EXPECT_EQ(0U, queued_event_count());
EXPECT_FALSE(event_in_flight());
EXPECT_EQ(WebInputEvent::Type::kMouseWheel, acked_event().GetType());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
size_t scroll_update_index = 0;
EXPECT_EQ(1U, all_sent_events().size());
EXPECT_GESTURE_SCROLL_UPDATE_WITH_PHASE(sent_gesture_event(0));
EXPECT_EQ(
0U, sent_gesture_event(scroll_update_index)->data.scroll_update.delta_x);
EXPECT_EQ(
1U, sent_gesture_event(scroll_update_index)->data.scroll_update.delta_y);
EXPECT_EQ(1U, GetAndResetSentEventCount());
}
TEST_F(MouseWheelEventQueueTest, WheelScrollLatching) {
const ui::ScrollGranularity scroll_units =
ui::ScrollGranularity::kScrollByPixel;
SendMouseWheel(
kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX, kWheelScrollGlobalY, 1,
1, 0, false, WebMouseWheelEvent::kPhaseBegan,
WebMouseWheelEvent::kPhaseNone, WebInputEvent::kRailsModeVertical);
EXPECT_EQ(0U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Receive an ACK for the mouse wheel event.
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
EXPECT_EQ(0U, queued_event_count());
EXPECT_FALSE(event_in_flight());
EXPECT_EQ(WebInputEvent::Type::kMouseWheel, acked_event().GetType());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(2U, all_sent_events().size());
EXPECT_GESTURE_SCROLL_BEGIN_WITH_PHASE(sent_gesture_event(0));
EXPECT_GESTURE_SCROLL_UPDATE_WITH_PHASE(sent_gesture_event(1));
EXPECT_EQ(0U, sent_gesture_event(1)->data.scroll_update.delta_x);
EXPECT_EQ(1U, sent_gesture_event(1)->data.scroll_update.delta_y);
EXPECT_EQ(2U, GetAndResetSentEventCount());
SendMouseWheel(
kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX, kWheelScrollGlobalY, 1,
1, 0, false, WebMouseWheelEvent::kPhaseChanged,
WebMouseWheelEvent::kPhaseNone, WebInputEvent::kRailsModeVertical);
EXPECT_EQ(0U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Receive an ACK for the mouse wheel event.
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
EXPECT_EQ(0U, queued_event_count());
EXPECT_FALSE(event_in_flight());
EXPECT_EQ(WebInputEvent::Type::kMouseWheel, acked_event().GetType());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Scroll latching: no new scroll begin expected.
EXPECT_EQ(1U, all_sent_events().size());
EXPECT_GESTURE_SCROLL_UPDATE_WITH_PHASE(sent_gesture_event(0));
EXPECT_EQ(0U, sent_gesture_event(0)->data.scroll_update.delta_x);
EXPECT_EQ(1U, sent_gesture_event(0)->data.scroll_update.delta_y);
EXPECT_EQ(1U, GetAndResetSentEventCount());
}
#if BUILDFLAG(IS_MAC)
TEST_F(MouseWheelEventQueueTest, DoNotSwapXYForShiftScroll) {
// Send an event with shift modifier, zero value for delta X, and no direction
// for |rails_mode|. Do not swap the scroll direction.
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 0.0, 1.0, WebInputEvent::kShiftKey, false,
WebMouseWheelEvent::kPhaseBegan,
WebMouseWheelEvent::kPhaseNone, WebInputEvent::kRailsModeFree);
EXPECT_EQ(0U, queued_event_count());
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Receive an ACK for the mouse wheel event.
SendMouseWheelEventAck(blink::mojom::InputEventResultState::kNotConsumed);
EXPECT_EQ(0U, queued_event_count());
EXPECT_FALSE(event_in_flight());
EXPECT_EQ(WebInputEvent::Type::kMouseWheel, acked_event().GetType());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(2U, all_sent_events().size());
EXPECT_EQ(0U, sent_gesture_event(1)->data.scroll_update.delta_x);
EXPECT_EQ(1U, sent_gesture_event(1)->data.scroll_update.delta_y);
EXPECT_EQ(2U, GetAndResetSentEventCount());
}
#endif
} // namespace input