blob: 4549e50dab18e8ca9aba279d1503a06f26b95c31 [file] [log] [blame]
// Copyright 2016 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 <stddef.h>
#include <new>
#include <utility>
#include <vector>
#include "base/auto_reset.h"
#include "base/macros.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_simple_task_runner.h"
#include "build/build_config.h"
#include "content/common/input/synthetic_web_input_event_builders.h"
#include "content/renderer/input/main_thread_event_queue.h"
#include "content/renderer/render_thread_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/scheduler/test/web_mock_thread_scheduler.h"
using blink::WebInputEvent;
using blink::WebMouseEvent;
using blink::WebMouseWheelEvent;
using blink::WebTouchEvent;
namespace blink {
bool operator==(const WebMouseWheelEvent& lhs, const WebMouseWheelEvent& rhs) {
return memcmp(&lhs, &rhs, lhs.size()) == 0;
}
bool operator==(const WebTouchEvent& lhs, const WebTouchEvent& rhs) {
return memcmp(&lhs, &rhs, lhs.size()) == 0;
}
} // namespace blink
namespace content {
namespace {
// Simulate a 16ms frame signal.
const base::TimeDelta kFrameInterval = base::TimeDelta::FromMilliseconds(16);
} // namespace
class HandledTask {
public:
virtual ~HandledTask() {}
virtual blink::WebCoalescedInputEvent* taskAsEvent() = 0;
virtual unsigned taskAsClosure() const = 0;
};
class HandledEvent : public HandledTask {
public:
explicit HandledEvent(const blink::WebCoalescedInputEvent& event)
: event_(event.Event(),
event.GetCoalescedEventsPointers(),
event.GetPredictedEventsPointers()) {}
~HandledEvent() override {}
blink::WebCoalescedInputEvent* taskAsEvent() override { return &event_; }
unsigned taskAsClosure() const override {
NOTREACHED();
return 0;
}
private:
blink::WebCoalescedInputEvent event_;
};
class HandledClosure : public HandledTask {
public:
explicit HandledClosure(unsigned closure_id) : closure_id_(closure_id) {}
~HandledClosure() override {}
blink::WebCoalescedInputEvent* taskAsEvent() override {
NOTREACHED();
return nullptr;
}
unsigned taskAsClosure() const override { return closure_id_; }
private:
unsigned closure_id_;
};
enum class CallbackReceivedState {
kPending,
kCalledWhileHandlingEvent,
kCalledAfterHandleEvent,
};
class ReceivedCallback {
public:
ReceivedCallback()
: ReceivedCallback(CallbackReceivedState::kPending, false) {}
ReceivedCallback(CallbackReceivedState state, bool coalesced_latency)
: state_(state), coalesced_latency_(coalesced_latency) {}
bool operator==(const ReceivedCallback& other) const {
return state_ == other.state_ &&
coalesced_latency_ == other.coalesced_latency_;
}
private:
CallbackReceivedState state_;
bool coalesced_latency_;
};
class HandledEventCallbackTracker {
public:
HandledEventCallbackTracker()
: handling_event_(false), weak_ptr_factory_(this) {
weak_this_ = weak_ptr_factory_.GetWeakPtr();
}
HandledEventCallback GetCallback() {
callbacks_received_.push_back(ReceivedCallback());
HandledEventCallback callback =
base::BindOnce(&HandledEventCallbackTracker::DidHandleEvent, weak_this_,
callbacks_received_.size() - 1);
return callback;
}
void DidHandleEvent(size_t index,
InputEventAckState ack_result,
const ui::LatencyInfo& latency,
std::unique_ptr<ui::DidOverscrollParams> params,
base::Optional<cc::TouchAction> touch_action) {
callbacks_received_[index] = ReceivedCallback(
handling_event_ ? CallbackReceivedState::kCalledWhileHandlingEvent
: CallbackReceivedState::kCalledAfterHandleEvent,
latency.coalesced());
}
const std::vector<ReceivedCallback>& GetReceivedCallbacks() const {
return callbacks_received_;
}
bool handling_event_;
private:
std::vector<ReceivedCallback> callbacks_received_;
base::WeakPtr<HandledEventCallbackTracker> weak_this_;
base::WeakPtrFactory<HandledEventCallbackTracker> weak_ptr_factory_;
};
class MainThreadEventQueueTest : public testing::Test,
public MainThreadEventQueueClient {
public:
MainThreadEventQueueTest()
: main_task_runner_(new base::TestSimpleTaskRunner()),
needs_main_frame_(false),
closure_count_(0) {
handler_callback_ = std::make_unique<HandledEventCallbackTracker>();
}
void SetUp() override {
queue_ = new MainThreadEventQueue(this, main_task_runner_,
&thread_scheduler_, true);
queue_->set_use_raf_fallback_timer(false);
}
void HandleEvent(WebInputEvent& event, InputEventAckState ack_result) {
base::AutoReset<bool> in_handle_event(&handler_callback_->handling_event_,
true);
queue_->HandleEvent(ui::WebInputEventTraits::Clone(event),
ui::LatencyInfo(), DISPATCH_TYPE_BLOCKING, ack_result,
handler_callback_->GetCallback());
}
void RunClosure(unsigned closure_id) {
std::unique_ptr<HandledTask> closure(new HandledClosure(closure_id));
handled_tasks_.push_back(std::move(closure));
}
void QueueClosure() {
unsigned closure_id = ++closure_count_;
queue_->QueueClosure(base::BindOnce(&MainThreadEventQueueTest::RunClosure,
base::Unretained(this), closure_id));
}
MainThreadEventQueueTaskList& event_queue() {
return queue_->shared_state_.events_;
}
bool needs_low_latency_until_pointer_up() {
return queue_->needs_low_latency_until_pointer_up_;
}
bool last_touch_start_forced_nonblocking_due_to_fling() {
return queue_->last_touch_start_forced_nonblocking_due_to_fling_;
}
void set_enable_fling_passive_listener_flag(bool enable_flag) {
queue_->enable_fling_passive_listener_flag_ = enable_flag;
}
void RunPendingTasksWithSimulatedRaf() {
while (needs_main_frame_ || main_task_runner_->HasPendingTask()) {
main_task_runner_->RunUntilIdle();
needs_main_frame_ = false;
frame_time_ += kFrameInterval;
queue_->DispatchRafAlignedInput(frame_time_);
}
}
void RunSimulatedRafOnce() {
if (needs_main_frame_) {
needs_main_frame_ = false;
frame_time_ += kFrameInterval;
queue_->DispatchRafAlignedInput(frame_time_);
}
}
void HandleInputEvent(const blink::WebCoalescedInputEvent& event,
const ui::LatencyInfo& latency,
HandledEventCallback callback) override {
std::unique_ptr<HandledTask> handled_event(new HandledEvent(event));
handled_tasks_.push_back(std::move(handled_event));
std::move(callback).Run(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, latency,
nullptr, base::nullopt);
}
void SetNeedsMainFrame() override { needs_main_frame_ = true; }
std::vector<ReceivedCallback> GetAndResetCallbackResults() {
std::unique_ptr<HandledEventCallbackTracker> callback =
std::make_unique<HandledEventCallbackTracker>();
handler_callback_.swap(callback);
return callback->GetReceivedCallbacks();
}
protected:
scoped_refptr<base::TestSimpleTaskRunner> main_task_runner_;
blink::scheduler::WebMockThreadScheduler thread_scheduler_;
scoped_refptr<MainThreadEventQueue> queue_;
std::vector<std::unique_ptr<HandledTask>> handled_tasks_;
std::unique_ptr<HandledEventCallbackTracker> handler_callback_;
int raf_aligned_input_setting_;
bool needs_main_frame_;
base::TimeTicks frame_time_;
unsigned closure_count_;
};
TEST_F(MainThreadEventQueueTest, NonBlockingWheel) {
WebMouseWheelEvent kEvents[4] = {
SyntheticWebMouseWheelEventBuilder::Build(10, 10, 0, 53, 0, false),
SyntheticWebMouseWheelEventBuilder::Build(20, 20, 0, 53, 0, false),
SyntheticWebMouseWheelEventBuilder::Build(30, 30, 0, 53, 1, false),
SyntheticWebMouseWheelEventBuilder::Build(30, 30, 0, 53, 1, false),
};
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(0);
for (WebMouseWheelEvent& event : kEvents)
HandleEvent(event, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(2u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_EQ(2u, handled_tasks_.size());
for (const auto& task : handled_tasks_) {
EXPECT_EQ(2u, task->taskAsEvent()->CoalescedEventSize());
}
{
EXPECT_EQ(kEvents[0].size(),
handled_tasks_.at(0)->taskAsEvent()->Event().size());
EXPECT_EQ(kEvents[0].GetType(),
handled_tasks_.at(0)->taskAsEvent()->Event().GetType());
const WebMouseWheelEvent* last_wheel_event =
static_cast<const WebMouseWheelEvent*>(
handled_tasks_.at(0)->taskAsEvent()->EventPointer());
EXPECT_EQ(WebInputEvent::DispatchType::kListenersNonBlockingPassive,
last_wheel_event->dispatch_type);
WebMouseWheelEvent coalesced_event = kEvents[0];
ui::Coalesce(kEvents[1], &coalesced_event);
coalesced_event.dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
EXPECT_EQ(coalesced_event, *last_wheel_event);
}
{
WebMouseWheelEvent coalesced_event = kEvents[0];
std::vector<const WebInputEvent*> coalesced_events =
handled_tasks_[0]->taskAsEvent()->GetCoalescedEventsPointers();
const WebMouseWheelEvent* coalesced_wheel_event0 =
static_cast<const WebMouseWheelEvent*>(coalesced_events[0]);
coalesced_event.dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
EXPECT_EQ(coalesced_event, *coalesced_wheel_event0);
coalesced_event = kEvents[1];
const WebMouseWheelEvent* coalesced_wheel_event1 =
static_cast<const WebMouseWheelEvent*>(coalesced_events[1]);
coalesced_event.dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
EXPECT_EQ(coalesced_event, *coalesced_wheel_event1);
}
{
const WebMouseWheelEvent* last_wheel_event =
static_cast<const WebMouseWheelEvent*>(
handled_tasks_.at(1)->taskAsEvent()->EventPointer());
WebMouseWheelEvent coalesced_event = kEvents[2];
ui::Coalesce(kEvents[3], &coalesced_event);
coalesced_event.dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
EXPECT_EQ(coalesced_event, *last_wheel_event);
}
{
WebMouseWheelEvent coalesced_event = kEvents[2];
std::vector<const WebInputEvent*> coalesced_events =
handled_tasks_[1]->taskAsEvent()->GetCoalescedEventsPointers();
const WebMouseWheelEvent* coalesced_wheel_event0 =
static_cast<const WebMouseWheelEvent*>(coalesced_events[0]);
coalesced_event.dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
EXPECT_EQ(coalesced_event, *coalesced_wheel_event0);
coalesced_event = kEvents[3];
const WebMouseWheelEvent* coalesced_wheel_event1 =
static_cast<const WebMouseWheelEvent*>(coalesced_events[1]);
coalesced_event.dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
EXPECT_EQ(coalesced_event, *coalesced_wheel_event1);
}
}
TEST_F(MainThreadEventQueueTest, NonBlockingTouch) {
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(0);
SyntheticWebTouchEvent kEvents[4];
kEvents[0].PressPoint(10, 10);
kEvents[1].PressPoint(10, 10);
kEvents[1].SetModifiers(1);
kEvents[1].MovePoint(0, 20, 20);
kEvents[2].PressPoint(10, 10);
kEvents[2].MovePoint(0, 30, 30);
kEvents[3].PressPoint(10, 10);
kEvents[3].MovePoint(0, 35, 35);
for (SyntheticWebTouchEvent& event : kEvents)
HandleEvent(event, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(3u, event_queue().size());
EXPECT_TRUE(main_task_runner_->HasPendingTask());
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_EQ(3u, handled_tasks_.size());
EXPECT_EQ(kEvents[0].size(),
handled_tasks_.at(0)->taskAsEvent()->Event().size());
EXPECT_EQ(kEvents[0].GetType(),
handled_tasks_.at(0)->taskAsEvent()->Event().GetType());
const WebTouchEvent* last_touch_event = static_cast<const WebTouchEvent*>(
handled_tasks_.at(0)->taskAsEvent()->EventPointer());
kEvents[0].dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
EXPECT_EQ(kEvents[0], *last_touch_event);
{
EXPECT_EQ(1u, handled_tasks_[0]->taskAsEvent()->CoalescedEventSize());
const WebTouchEvent* coalesced_touch_event =
static_cast<const WebTouchEvent*>(
handled_tasks_[0]->taskAsEvent()->GetCoalescedEventsPointers()[0]);
EXPECT_EQ(kEvents[0], *coalesced_touch_event);
}
EXPECT_EQ(kEvents[1].size(),
handled_tasks_.at(1)->taskAsEvent()->Event().size());
EXPECT_EQ(kEvents[1].GetType(),
handled_tasks_.at(1)->taskAsEvent()->Event().GetType());
last_touch_event = static_cast<const WebTouchEvent*>(
handled_tasks_.at(1)->taskAsEvent()->EventPointer());
kEvents[1].dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
EXPECT_EQ(kEvents[1], *last_touch_event);
{
EXPECT_EQ(1u, handled_tasks_[1]->taskAsEvent()->CoalescedEventSize());
const WebTouchEvent* coalesced_touch_event =
static_cast<const WebTouchEvent*>(
handled_tasks_[1]->taskAsEvent()->GetCoalescedEventsPointers()[0]);
EXPECT_EQ(kEvents[1], *coalesced_touch_event);
}
EXPECT_EQ(kEvents[2].size(),
handled_tasks_.at(1)->taskAsEvent()->Event().size());
EXPECT_EQ(kEvents[2].GetType(),
handled_tasks_.at(2)->taskAsEvent()->Event().GetType());
last_touch_event = static_cast<const WebTouchEvent*>(
handled_tasks_.at(2)->taskAsEvent()->EventPointer());
WebTouchEvent coalesced_event = kEvents[2];
ui::Coalesce(kEvents[3], &coalesced_event);
coalesced_event.dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
EXPECT_EQ(coalesced_event, *last_touch_event);
{
EXPECT_EQ(2u, handled_tasks_[2]->taskAsEvent()->CoalescedEventSize());
WebTouchEvent coalesced_event = kEvents[2];
std::vector<const WebInputEvent*> coalesced_events =
handled_tasks_[2]->taskAsEvent()->GetCoalescedEventsPointers();
const WebTouchEvent* coalesced_touch_event0 =
static_cast<const WebTouchEvent*>(coalesced_events[0]);
coalesced_event.dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
EXPECT_EQ(coalesced_event, *coalesced_touch_event0);
coalesced_event = kEvents[3];
const WebTouchEvent* coalesced_touch_event1 =
static_cast<const WebTouchEvent*>(coalesced_events[1]);
coalesced_event.dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
EXPECT_EQ(coalesced_event, *coalesced_touch_event1);
}
}
TEST_F(MainThreadEventQueueTest, BlockingTouch) {
SyntheticWebTouchEvent kEvents[4];
kEvents[0].PressPoint(10, 10);
kEvents[1].PressPoint(10, 10);
kEvents[1].MovePoint(0, 20, 20);
kEvents[2].PressPoint(10, 10);
kEvents[2].MovePoint(0, 30, 30);
kEvents[3].PressPoint(10, 10);
kEvents[3].MovePoint(0, 35, 35);
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(3);
{
// Ensure that coalescing takes place.
HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
HandleEvent(kEvents[2], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
HandleEvent(kEvents[3], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(2u, event_queue().size());
EXPECT_TRUE(main_task_runner_->HasPendingTask());
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(
GetAndResetCallbackResults(),
testing::ElementsAre(
ReceivedCallback(CallbackReceivedState::kCalledWhileHandlingEvent,
false),
ReceivedCallback(CallbackReceivedState::kCalledAfterHandleEvent,
false),
ReceivedCallback(CallbackReceivedState::kCalledAfterHandleEvent,
true),
ReceivedCallback(CallbackReceivedState::kCalledAfterHandleEvent,
true)));
EXPECT_EQ(0u, event_queue().size());
const WebTouchEvent* last_touch_event = static_cast<const WebTouchEvent*>(
handled_tasks_.at(1)->taskAsEvent()->EventPointer());
EXPECT_EQ(kEvents[1].unique_touch_event_id,
last_touch_event->unique_touch_event_id);
}
HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(kEvents[2], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(kEvents[3], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(1u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
}
TEST_F(MainThreadEventQueueTest, InterleavedEvents) {
WebMouseWheelEvent kWheelEvents[2] = {
SyntheticWebMouseWheelEventBuilder::Build(10, 10, 0, 53, 0, false),
SyntheticWebMouseWheelEventBuilder::Build(20, 20, 0, 53, 0, false),
};
SyntheticWebTouchEvent kTouchEvents[2];
kTouchEvents[0].PressPoint(10, 10);
kTouchEvents[0].MovePoint(0, 20, 20);
kTouchEvents[1].PressPoint(10, 10);
kTouchEvents[1].MovePoint(0, 30, 30);
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(0);
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
HandleEvent(kWheelEvents[0], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(kTouchEvents[0], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(kWheelEvents[1], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(kTouchEvents[1], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(2u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_EQ(2u, handled_tasks_.size());
{
EXPECT_EQ(kWheelEvents[0].size(),
handled_tasks_.at(0)->taskAsEvent()->Event().size());
EXPECT_EQ(kWheelEvents[0].GetType(),
handled_tasks_.at(0)->taskAsEvent()->Event().GetType());
const WebMouseWheelEvent* last_wheel_event =
static_cast<const WebMouseWheelEvent*>(
handled_tasks_.at(0)->taskAsEvent()->EventPointer());
EXPECT_EQ(WebInputEvent::DispatchType::kListenersNonBlockingPassive,
last_wheel_event->dispatch_type);
WebMouseWheelEvent coalesced_event = kWheelEvents[0];
ui::Coalesce(kWheelEvents[1], &coalesced_event);
coalesced_event.dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
EXPECT_EQ(coalesced_event, *last_wheel_event);
}
{
EXPECT_EQ(kTouchEvents[0].size(),
handled_tasks_.at(1)->taskAsEvent()->Event().size());
EXPECT_EQ(kTouchEvents[0].GetType(),
handled_tasks_.at(1)->taskAsEvent()->Event().GetType());
const WebTouchEvent* last_touch_event = static_cast<const WebTouchEvent*>(
handled_tasks_.at(1)->taskAsEvent()->EventPointer());
WebTouchEvent coalesced_event = kTouchEvents[0];
ui::Coalesce(kTouchEvents[1], &coalesced_event);
coalesced_event.dispatch_type =
WebInputEvent::DispatchType::kListenersNonBlockingPassive;
EXPECT_EQ(coalesced_event, *last_touch_event);
}
}
TEST_F(MainThreadEventQueueTest, RafAlignedMouseInput) {
WebMouseEvent mouseDown = SyntheticWebMouseEventBuilder::Build(
WebInputEvent::kMouseDown, 10, 10, 0);
WebMouseEvent mouseMove = SyntheticWebMouseEventBuilder::Build(
WebInputEvent::kMouseMove, 10, 10, 0);
WebMouseEvent mouseUp =
SyntheticWebMouseEventBuilder::Build(WebInputEvent::kMouseUp, 10, 10, 0);
WebMouseWheelEvent wheelEvents[3] = {
SyntheticWebMouseWheelEventBuilder::Build(10, 10, 0, 53, 0, false),
SyntheticWebMouseWheelEventBuilder::Build(20, 20, 0, 53, 0, false),
SyntheticWebMouseWheelEventBuilder::Build(20, 20, 0, 53, 1, false),
};
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(0);
// Simulate enqueing a discrete event, followed by continuous events and
// then a discrete event. The last discrete event should flush the
// continuous events so the aren't aligned to rAF and are processed
// immediately.
HandleEvent(mouseDown, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(mouseMove, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(wheelEvents[0], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(wheelEvents[1], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(mouseUp, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(4u, event_queue().size());
EXPECT_TRUE(main_task_runner_->HasPendingTask());
EXPECT_TRUE(needs_main_frame_);
main_task_runner_->RunUntilIdle();
EXPECT_EQ(0u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
// Simulate the rAF running before the PostTask occurs. The rAF
// will consume everything.
HandleEvent(mouseDown, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(wheelEvents[0], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(2u, event_queue().size());
EXPECT_TRUE(needs_main_frame_);
RunSimulatedRafOnce();
EXPECT_FALSE(needs_main_frame_);
EXPECT_EQ(0u, event_queue().size());
main_task_runner_->RunUntilIdle();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
// Simulate event consumption but no rAF signal. The mouse wheel events
// should still be in the queue.
handled_tasks_.clear();
HandleEvent(mouseDown, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(wheelEvents[0], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(mouseUp, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(wheelEvents[2], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(wheelEvents[0], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(5u, event_queue().size());
EXPECT_TRUE(needs_main_frame_);
main_task_runner_->RunUntilIdle();
EXPECT_TRUE(needs_main_frame_);
EXPECT_EQ(2u, event_queue().size());
RunSimulatedRafOnce();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
EXPECT_EQ(wheelEvents[2].GetModifiers(),
handled_tasks_.at(3)->taskAsEvent()->Event().GetModifiers());
EXPECT_EQ(wheelEvents[0].GetModifiers(),
handled_tasks_.at(4)->taskAsEvent()->Event().GetModifiers());
}
TEST_F(MainThreadEventQueueTest, RafAlignedTouchInput) {
SyntheticWebTouchEvent kEvents[3];
kEvents[0].PressPoint(10, 10);
kEvents[1].PressPoint(10, 10);
kEvents[1].MovePoint(0, 50, 50);
kEvents[2].PressPoint(10, 10);
kEvents[2].ReleasePoint(0);
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(3);
// Simulate enqueing a discrete event, followed by continuous events and
// then a discrete event. The last discrete event should flush the
// continuous events so the aren't aligned to rAF and are processed
// immediately.
for (SyntheticWebTouchEvent& event : kEvents)
HandleEvent(event, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(3u, event_queue().size());
EXPECT_TRUE(main_task_runner_->HasPendingTask());
EXPECT_TRUE(needs_main_frame_);
main_task_runner_->RunUntilIdle();
EXPECT_EQ(0u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
// Simulate the rAF running before the PostTask occurs. The rAF
// will consume everything.
HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(2u, event_queue().size());
EXPECT_TRUE(needs_main_frame_);
RunSimulatedRafOnce();
EXPECT_FALSE(needs_main_frame_);
EXPECT_EQ(0u, event_queue().size());
main_task_runner_->RunUntilIdle();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
// Simulate event consumption but no rAF signal. The touch events
// should still be in the queue.
handled_tasks_.clear();
HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(2u, event_queue().size());
EXPECT_TRUE(needs_main_frame_);
main_task_runner_->RunUntilIdle();
EXPECT_TRUE(needs_main_frame_);
EXPECT_EQ(1u, event_queue().size());
RunSimulatedRafOnce();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
// Simulate the touch move being discrete
kEvents[0].touch_start_or_first_touch_move = true;
kEvents[1].touch_start_or_first_touch_move = true;
for (SyntheticWebTouchEvent& event : kEvents)
HandleEvent(event, INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(3u, event_queue().size());
EXPECT_TRUE(main_task_runner_->HasPendingTask());
EXPECT_TRUE(needs_main_frame_);
main_task_runner_->RunUntilIdle();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledAfterHandleEvent, false)));
}
TEST_F(MainThreadEventQueueTest, RafAlignedTouchInputCoalescedMoves) {
SyntheticWebTouchEvent kEvents[2];
kEvents[0].PressPoint(10, 10);
kEvents[0].MovePoint(0, 50, 50);
kEvents[1].PressPoint(10, 10);
kEvents[1].MovePoint(0, 20, 20);
kEvents[0].dispatch_type = WebInputEvent::kEventNonBlocking;
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(4);
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
{
// Send a non-blocking input event and then blocking event.
// The events should coalesce together.
HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_TRUE(needs_main_frame_);
HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_TRUE(needs_main_frame_);
RunPendingTasksWithSimulatedRaf();
EXPECT_EQ(0u, event_queue().size());
EXPECT_THAT(
GetAndResetCallbackResults(),
testing::ElementsAre(
ReceivedCallback(CallbackReceivedState::kCalledWhileHandlingEvent,
false),
ReceivedCallback(CallbackReceivedState::kCalledAfterHandleEvent,
true)));
}
// Send a non-cancelable ack required event, and then a non-ack
// required event they should be coalesced together.
HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_TRUE(needs_main_frame_);
HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(1u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_TRUE(needs_main_frame_);
RunPendingTasksWithSimulatedRaf();
EXPECT_EQ(0u, event_queue().size());
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
// Send a non-ack required event, and then a non-cancelable ack
// required event they should be coalesced together.
HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(1u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_TRUE(needs_main_frame_);
HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_TRUE(needs_main_frame_);
RunPendingTasksWithSimulatedRaf();
EXPECT_EQ(0u, event_queue().size());
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
}
TEST_F(MainThreadEventQueueTest, RafAlignedTouchInputThrottlingMoves) {
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(3);
SyntheticWebTouchEvent kEvents[2];
kEvents[0].PressPoint(10, 10);
kEvents[0].MovePoint(0, 50, 50);
kEvents[0].dispatch_type = WebInputEvent::kEventNonBlocking;
kEvents[1].PressPoint(10, 10);
kEvents[1].MovePoint(0, 20, 20);
kEvents[1].dispatch_type = WebInputEvent::kEventNonBlocking;
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
// Send a non-cancelable touch move and then send it another one. The
// second one shouldn't go out with the next rAF call and should be throttled.
HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_TRUE(needs_main_frame_);
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_TRUE(needs_main_frame_);
// Event should still be in queue after handling a single rAF call.
RunSimulatedRafOnce();
EXPECT_EQ(1u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_TRUE(needs_main_frame_);
// And should eventually flush.
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
EXPECT_EQ(0u, event_queue().size());
}
TEST_F(MainThreadEventQueueTest, LowLatency) {
SyntheticWebTouchEvent kEvents[2];
kEvents[0].PressPoint(10, 10);
kEvents[1].PressPoint(10, 10);
kEvents[1].MovePoint(0, 50, 50);
queue_->SetNeedsLowLatency(true);
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(0);
for (SyntheticWebTouchEvent& event : kEvents)
HandleEvent(event, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(2u, event_queue().size());
EXPECT_TRUE(main_task_runner_->HasPendingTask());
EXPECT_FALSE(needs_main_frame_);
main_task_runner_->RunUntilIdle();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
EXPECT_EQ(0u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
WebMouseEvent mouse_move = SyntheticWebMouseEventBuilder::Build(
WebInputEvent::kMouseMove, 10, 10, 0);
WebMouseWheelEvent mouse_wheel =
SyntheticWebMouseWheelEventBuilder::Build(10, 10, 0, 53, 0, false);
HandleEvent(mouse_move, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(mouse_wheel, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(2u, event_queue().size());
EXPECT_TRUE(main_task_runner_->HasPendingTask());
EXPECT_FALSE(needs_main_frame_);
main_task_runner_->RunUntilIdle();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
EXPECT_EQ(0u, event_queue().size());
// Now turn off low latency mode.
queue_->SetNeedsLowLatency(false);
for (SyntheticWebTouchEvent& event : kEvents)
HandleEvent(event, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(2u, event_queue().size());
EXPECT_TRUE(main_task_runner_->HasPendingTask());
EXPECT_TRUE(needs_main_frame_);
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
EXPECT_EQ(0u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
HandleEvent(mouse_move, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(mouse_wheel, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(2u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_TRUE(needs_main_frame_);
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
EXPECT_EQ(0u, event_queue().size());
}
TEST_F(MainThreadEventQueueTest, BlockingTouchesDuringFling) {
SyntheticWebTouchEvent kEvents;
kEvents.PressPoint(10, 10);
kEvents.touch_start_or_first_touch_move = true;
set_enable_fling_passive_listener_flag(true);
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(4);
EXPECT_FALSE(last_touch_start_forced_nonblocking_due_to_fling());
HandleEvent(kEvents, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING_DUE_TO_FLING);
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_EQ(1u, handled_tasks_.size());
EXPECT_EQ(kEvents.size(),
handled_tasks_.at(0)->taskAsEvent()->Event().size());
EXPECT_EQ(kEvents.GetType(),
handled_tasks_.at(0)->taskAsEvent()->Event().GetType());
EXPECT_TRUE(last_touch_start_forced_nonblocking_due_to_fling());
const WebTouchEvent* last_touch_event = static_cast<const WebTouchEvent*>(
handled_tasks_.at(0)->taskAsEvent()->EventPointer());
kEvents.dispatch_type = WebInputEvent::kListenersForcedNonBlockingDueToFling;
EXPECT_EQ(kEvents, *last_touch_event);
kEvents.MovePoint(0, 30, 30);
EXPECT_FALSE(main_task_runner_->HasPendingTask());
HandleEvent(kEvents, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING_DUE_TO_FLING);
EXPECT_FALSE(main_task_runner_->HasPendingTask());
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false)));
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_EQ(2u, handled_tasks_.size());
EXPECT_EQ(kEvents.size(),
handled_tasks_.at(1)->taskAsEvent()->Event().size());
EXPECT_EQ(kEvents.GetType(),
handled_tasks_.at(1)->taskAsEvent()->Event().GetType());
EXPECT_TRUE(last_touch_start_forced_nonblocking_due_to_fling());
last_touch_event = static_cast<const WebTouchEvent*>(
handled_tasks_.at(1)->taskAsEvent()->EventPointer());
kEvents.dispatch_type = WebInputEvent::kListenersForcedNonBlockingDueToFling;
EXPECT_EQ(kEvents, *last_touch_event);
kEvents.MovePoint(0, 50, 50);
kEvents.touch_start_or_first_touch_move = false;
HandleEvent(kEvents, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING_DUE_TO_FLING);
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledAfterHandleEvent, false)));
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_EQ(3u, handled_tasks_.size());
EXPECT_EQ(kEvents.size(),
handled_tasks_.at(2)->taskAsEvent()->Event().size());
EXPECT_EQ(kEvents.GetType(),
handled_tasks_.at(2)->taskAsEvent()->Event().GetType());
EXPECT_EQ(kEvents.dispatch_type, WebInputEvent::kBlocking);
last_touch_event = static_cast<const WebTouchEvent*>(
handled_tasks_.at(2)->taskAsEvent()->EventPointer());
EXPECT_EQ(kEvents, *last_touch_event);
kEvents.ReleasePoint(0);
HandleEvent(kEvents, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING_DUE_TO_FLING);
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledAfterHandleEvent, false)));
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_EQ(4u, handled_tasks_.size());
EXPECT_EQ(kEvents.size(),
handled_tasks_.at(3)->taskAsEvent()->Event().size());
EXPECT_EQ(kEvents.GetType(),
handled_tasks_.at(3)->taskAsEvent()->Event().GetType());
EXPECT_EQ(kEvents.dispatch_type, WebInputEvent::kBlocking);
last_touch_event = static_cast<const WebTouchEvent*>(
handled_tasks_.at(3)->taskAsEvent()->EventPointer());
EXPECT_EQ(kEvents, *last_touch_event);
}
TEST_F(MainThreadEventQueueTest, BlockingTouchesOutsideFling) {
SyntheticWebTouchEvent kEvents;
kEvents.PressPoint(10, 10);
kEvents.touch_start_or_first_touch_move = true;
set_enable_fling_passive_listener_flag(false);
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(4);
HandleEvent(kEvents, INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledAfterHandleEvent, false)));
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_EQ(1u, handled_tasks_.size());
EXPECT_EQ(kEvents.size(),
handled_tasks_.at(0)->taskAsEvent()->Event().size());
EXPECT_EQ(kEvents.GetType(),
handled_tasks_.at(0)->taskAsEvent()->Event().GetType());
EXPECT_EQ(kEvents.dispatch_type, WebInputEvent::kBlocking);
EXPECT_FALSE(last_touch_start_forced_nonblocking_due_to_fling());
const WebTouchEvent* last_touch_event = static_cast<const WebTouchEvent*>(
handled_tasks_.at(0)->taskAsEvent()->EventPointer());
EXPECT_EQ(kEvents, *last_touch_event);
set_enable_fling_passive_listener_flag(false);
HandleEvent(kEvents, INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledAfterHandleEvent, false)));
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_EQ(2u, handled_tasks_.size());
EXPECT_EQ(kEvents.size(),
handled_tasks_.at(1)->taskAsEvent()->Event().size());
EXPECT_EQ(kEvents.GetType(),
handled_tasks_.at(1)->taskAsEvent()->Event().GetType());
EXPECT_EQ(kEvents.dispatch_type, WebInputEvent::kBlocking);
EXPECT_FALSE(last_touch_start_forced_nonblocking_due_to_fling());
last_touch_event = static_cast<const WebTouchEvent*>(
handled_tasks_.at(1)->taskAsEvent()->EventPointer());
EXPECT_EQ(kEvents, *last_touch_event);
set_enable_fling_passive_listener_flag(true);
HandleEvent(kEvents, INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledAfterHandleEvent, false)));
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_EQ(3u, handled_tasks_.size());
EXPECT_EQ(kEvents.size(),
handled_tasks_.at(2)->taskAsEvent()->Event().size());
EXPECT_EQ(kEvents.GetType(),
handled_tasks_.at(2)->taskAsEvent()->Event().GetType());
EXPECT_EQ(kEvents.dispatch_type, WebInputEvent::kBlocking);
EXPECT_FALSE(last_touch_start_forced_nonblocking_due_to_fling());
last_touch_event = static_cast<const WebTouchEvent*>(
handled_tasks_.at(2)->taskAsEvent()->EventPointer());
EXPECT_EQ(kEvents, *last_touch_event);
kEvents.MovePoint(0, 30, 30);
HandleEvent(kEvents, INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledAfterHandleEvent, false)));
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_EQ(4u, handled_tasks_.size());
EXPECT_EQ(kEvents.size(),
handled_tasks_.at(3)->taskAsEvent()->Event().size());
EXPECT_EQ(kEvents.GetType(),
handled_tasks_.at(3)->taskAsEvent()->Event().GetType());
EXPECT_EQ(kEvents.dispatch_type, WebInputEvent::kBlocking);
EXPECT_FALSE(last_touch_start_forced_nonblocking_due_to_fling());
last_touch_event = static_cast<const WebTouchEvent*>(
handled_tasks_.at(3)->taskAsEvent()->EventPointer());
EXPECT_EQ(kEvents, *last_touch_event);
}
class MainThreadEventQueueInitializationTest
: public testing::Test,
public MainThreadEventQueueClient {
public:
MainThreadEventQueueInitializationTest() {}
void HandleInputEvent(const blink::WebCoalescedInputEvent& event,
const ui::LatencyInfo& latency,
HandledEventCallback callback) override {
std::move(callback).Run(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, latency,
nullptr, base::nullopt);
}
void SetNeedsMainFrame() override {}
protected:
scoped_refptr<MainThreadEventQueue> queue_;
blink::scheduler::WebMockThreadScheduler thread_scheduler_;
scoped_refptr<base::TestSimpleTaskRunner> main_task_runner_;
};
TEST_F(MainThreadEventQueueTest, QueuingTwoClosures) {
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
QueueClosure();
QueueClosure();
EXPECT_EQ(2u, event_queue().size());
EXPECT_TRUE(main_task_runner_->HasPendingTask());
EXPECT_FALSE(needs_main_frame_);
main_task_runner_->RunUntilIdle();
EXPECT_EQ(1u, handled_tasks_.at(0)->taskAsClosure());
EXPECT_EQ(2u, handled_tasks_.at(1)->taskAsClosure());
}
TEST_F(MainThreadEventQueueTest, QueuingClosureWithRafEvent) {
SyntheticWebTouchEvent kEvents[2];
kEvents[0].PressPoint(10, 10);
kEvents[1].PressPoint(10, 10);
kEvents[1].MovePoint(0, 20, 20);
// Simulate queueuing closure, event, closure, raf aligned event.
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
QueueClosure();
EXPECT_EQ(1u, event_queue().size());
EXPECT_TRUE(main_task_runner_->HasPendingTask());
EXPECT_FALSE(needs_main_frame_);
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(2);
HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
QueueClosure();
EXPECT_EQ(3u, event_queue().size());
EXPECT_TRUE(main_task_runner_->HasPendingTask());
EXPECT_FALSE(needs_main_frame_);
HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(4u, event_queue().size());
EXPECT_TRUE(needs_main_frame_);
main_task_runner_->RunUntilIdle();
// The queue should still have the rAF event.
EXPECT_TRUE(needs_main_frame_);
EXPECT_EQ(1u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_EQ(0u, event_queue().size());
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledAfterHandleEvent, false)));
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_FALSE(needs_main_frame_);
EXPECT_EQ(1u, handled_tasks_.at(0)->taskAsClosure());
EXPECT_EQ(kEvents[0].GetType(),
handled_tasks_.at(1)->taskAsEvent()->Event().GetType());
EXPECT_EQ(2u, handled_tasks_.at(2)->taskAsClosure());
EXPECT_EQ(kEvents[1].GetType(),
handled_tasks_.at(3)->taskAsEvent()->Event().GetType());
}
TEST_F(MainThreadEventQueueTest, QueuingClosuresBetweenEvents) {
SyntheticWebTouchEvent kEvents[2];
kEvents[0].PressPoint(10, 10);
kEvents[1].PressPoint(10, 10);
kEvents[1].ReleasePoint(0);
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(2);
HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
QueueClosure();
QueueClosure();
HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(4u, event_queue().size());
EXPECT_FALSE(needs_main_frame_);
main_task_runner_->RunUntilIdle();
EXPECT_EQ(0u, event_queue().size());
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledAfterHandleEvent, false)));
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_FALSE(needs_main_frame_);
EXPECT_EQ(kEvents[0].GetType(),
handled_tasks_.at(0)->taskAsEvent()->Event().GetType());
EXPECT_EQ(1u, handled_tasks_.at(1)->taskAsClosure());
EXPECT_EQ(2u, handled_tasks_.at(2)->taskAsClosure());
EXPECT_EQ(kEvents[1].GetType(),
handled_tasks_.at(3)->taskAsEvent()->Event().GetType());
}
TEST_F(MainThreadEventQueueTest, BlockingTouchMoveBecomesNonBlocking) {
SyntheticWebTouchEvent kEvents[2];
kEvents[0].PressPoint(10, 10);
kEvents[0].MovePoint(0, 20, 20);
kEvents[1].SetModifiers(1);
kEvents[1].PressPoint(10, 10);
kEvents[1].MovePoint(0, 20, 30);
kEvents[1].dispatch_type = WebInputEvent::kEventNonBlocking;
WebTouchEvent scroll_start(WebInputEvent::kTouchScrollStarted,
WebInputEvent::kNoModifiers,
WebInputEvent::GetStaticTimeStampForTests());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(3);
EXPECT_EQ(WebInputEvent::kBlocking, kEvents[0].dispatch_type);
EXPECT_EQ(WebInputEvent::kEventNonBlocking, kEvents[1].dispatch_type);
HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
HandleEvent(scroll_start, INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(3u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::ElementsAre(
ReceivedCallback(
CallbackReceivedState::kCalledAfterHandleEvent, false),
ReceivedCallback(
CallbackReceivedState::kCalledWhileHandlingEvent, false),
ReceivedCallback(
CallbackReceivedState::kCalledAfterHandleEvent, false)));
EXPECT_EQ(0u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_FALSE(needs_main_frame_);
EXPECT_EQ(WebInputEvent::kEventNonBlocking,
static_cast<const WebTouchEvent&>(
handled_tasks_.at(0)->taskAsEvent()->Event())
.dispatch_type);
EXPECT_EQ(WebInputEvent::kEventNonBlocking,
static_cast<const WebTouchEvent&>(
handled_tasks_.at(1)->taskAsEvent()->Event())
.dispatch_type);
}
TEST_F(MainThreadEventQueueTest, BlockingTouchMoveWithTouchEnd) {
SyntheticWebTouchEvent kEvents[2];
kEvents[0].PressPoint(10, 10);
kEvents[0].MovePoint(0, 20, 20);
kEvents[1].PressPoint(10, 10);
kEvents[1].ReleasePoint(0);
WebTouchEvent scroll_start(WebInputEvent::kTouchScrollStarted,
WebInputEvent::kNoModifiers,
WebInputEvent::GetStaticTimeStampForTests());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(3);
EXPECT_EQ(WebInputEvent::kBlocking, kEvents[0].dispatch_type);
EXPECT_EQ(WebInputEvent::kBlocking, kEvents[1].dispatch_type);
HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
HandleEvent(scroll_start, INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(3u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_THAT(GetAndResetCallbackResults(),
testing::Each(ReceivedCallback(
CallbackReceivedState::kCalledAfterHandleEvent, false)));
EXPECT_EQ(0u, event_queue().size());
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_FALSE(needs_main_frame_);
EXPECT_EQ(WebInputEvent::kBlocking,
static_cast<const WebTouchEvent&>(
handled_tasks_.at(0)->taskAsEvent()->Event())
.dispatch_type);
EXPECT_EQ(WebInputEvent::kBlocking,
static_cast<const WebTouchEvent&>(
handled_tasks_.at(1)->taskAsEvent()->Event())
.dispatch_type);
}
TEST_F(MainThreadEventQueueTest, UnbufferedDispatchTouchEvent) {
SyntheticWebTouchEvent kEvents[3];
kEvents[0].PressPoint(10, 10);
kEvents[1].PressPoint(10, 10);
kEvents[1].MovePoint(0, 20, 20);
kEvents[2].PressPoint(10, 10);
kEvents[2].ReleasePoint(0);
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(3);
EXPECT_EQ(WebInputEvent::kBlocking, kEvents[0].dispatch_type);
EXPECT_EQ(WebInputEvent::kBlocking, kEvents[1].dispatch_type);
HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
queue_->RequestUnbufferedInputEvents();
EXPECT_EQ(1u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_TRUE(needs_low_latency_until_pointer_up());
EXPECT_FALSE(needs_main_frame_);
HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_TRUE(needs_low_latency_until_pointer_up());
EXPECT_FALSE(needs_main_frame_);
HandleEvent(kEvents[2], INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_FALSE(needs_low_latency_until_pointer_up());
EXPECT_FALSE(needs_main_frame_);
}
TEST_F(MainThreadEventQueueTest, PointerEventsCoalescing) {
queue_->HasPointerRawMoveEventHandlers(true);
WebMouseEvent mouse_move = SyntheticWebMouseEventBuilder::Build(
WebInputEvent::kMouseMove, 10, 10, 0);
SyntheticWebTouchEvent touch_move;
touch_move.PressPoint(10, 10);
touch_move.MovePoint(0, 50, 50);
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
HandleEvent(mouse_move, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(touch_move, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(4u, event_queue().size());
HandleEvent(mouse_move, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(touch_move, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(mouse_move, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
HandleEvent(touch_move, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(4u, event_queue().size());
main_task_runner_->RunUntilIdle();
EXPECT_EQ(2u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_EQ(0u, event_queue().size());
EXPECT_FALSE(needs_main_frame_);
}
TEST_F(MainThreadEventQueueTest, PointerRawMoveEvents) {
WebMouseEvent mouse_move = SyntheticWebMouseEventBuilder::Build(
WebInputEvent::kMouseMove, 10, 10, 0);
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(0);
HandleEvent(mouse_move, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(1u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_EQ(0u, event_queue().size());
EXPECT_FALSE(needs_main_frame_);
queue_->HasPointerRawMoveEventHandlers(true);
HandleEvent(mouse_move, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(2u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_EQ(0u, event_queue().size());
EXPECT_FALSE(needs_main_frame_);
queue_->HasPointerRawMoveEventHandlers(false);
SyntheticWebTouchEvent touch_move;
touch_move.PressPoint(10, 10);
touch_move.MovePoint(0, 50, 50);
HandleEvent(touch_move, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(1u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_EQ(0u, event_queue().size());
EXPECT_FALSE(needs_main_frame_);
queue_->HasPointerRawMoveEventHandlers(true);
HandleEvent(touch_move, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(2u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_EQ(0u, event_queue().size());
EXPECT_FALSE(needs_main_frame_);
}
TEST_F(MainThreadEventQueueTest, UnbufferedDispatchMouseEvent) {
WebMouseEvent mouse_down = SyntheticWebMouseEventBuilder::Build(
WebInputEvent::kMouseDown, 10, 10, 0);
WebMouseEvent mouse_move = SyntheticWebMouseEventBuilder::Build(
WebInputEvent::kMouseMove, 10, 10, 0);
WebMouseEvent mouse_up =
SyntheticWebMouseEventBuilder::Build(WebInputEvent::kMouseUp, 10, 10, 0);
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_CALL(thread_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(0);
HandleEvent(mouse_down, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
queue_->RequestUnbufferedInputEvents();
EXPECT_EQ(1u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_TRUE(needs_low_latency_until_pointer_up());
EXPECT_FALSE(needs_main_frame_);
HandleEvent(mouse_move, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
queue_->RequestUnbufferedInputEvents();
EXPECT_EQ(1u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_TRUE(needs_low_latency_until_pointer_up());
EXPECT_FALSE(needs_main_frame_);
HandleEvent(mouse_up, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
queue_->RequestUnbufferedInputEvents();
EXPECT_EQ(1u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
EXPECT_FALSE(needs_low_latency_until_pointer_up());
EXPECT_FALSE(needs_main_frame_);
}
} // namespace content