blob: d162ca0da61c4afe6628ca8ad9504f168e989adb [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/macros.h"
#include "base/metrics/field_trial.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/test/histogram_tester.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/WebKit/public/platform/scheduler/test/mock_renderer_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 {
const unsigned kRafAlignedEnabledTouch = 1;
const unsigned kRafAlignedEnabledMouse = 1 << 1;
// Simulate a 16ms frame signal.
const base::TimeDelta kFrameInterval = base::TimeDelta::FromMilliseconds(16);
const char* kCoalescedCountHistogram =
"Event.MainThreadEventQueue.CoalescedCount";
} // 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()) {}
~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_;
};
class MainThreadEventQueueTest : public testing::TestWithParam<unsigned>,
public MainThreadEventQueueClient {
public:
MainThreadEventQueueTest()
: main_task_runner_(new base::TestSimpleTaskRunner()),
raf_aligned_input_setting_(GetParam()),
needs_main_frame_(false),
closure_count_(0) {
std::vector<base::StringPiece> features;
std::vector<base::StringPiece> disabled_features;
if (raf_aligned_input_setting_ & kRafAlignedEnabledTouch) {
features.push_back(features::kRafAlignedTouchInputEvents.name);
} else {
disabled_features.push_back(features::kRafAlignedTouchInputEvents.name);
}
if (raf_aligned_input_setting_ & kRafAlignedEnabledMouse) {
features.push_back(features::kRafAlignedMouseInputEvents.name);
} else {
disabled_features.push_back(features::kRafAlignedMouseInputEvents.name);
}
feature_list_.InitFromCommandLine(base::JoinString(features, ","),
base::JoinString(disabled_features, ","));
}
void SetUp() override {
queue_ = new MainThreadEventQueue(this, main_task_runner_,
&renderer_scheduler_, true);
queue_->set_use_raf_fallback_timer(false);
}
bool HandleEvent(WebInputEvent& event, InputEventAckState ack_result) {
return queue_->HandleEvent(ui::WebInputEventTraits::Clone(event),
ui::LatencyInfo(), DISPATCH_TYPE_BLOCKING,
ack_result);
}
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::Bind(&MainThreadEventQueueTest::RunClosure,
base::Unretained(this), closure_id));
}
MainThreadEventQueueTaskList& event_queue() {
return queue_->shared_state_.events_;
}
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_);
}
}
InputEventAckState HandleInputEvent(
const blink::WebCoalescedInputEvent& event,
const ui::LatencyInfo& latency,
InputEventDispatchType dispatch_type) override {
std::unique_ptr<HandledTask> handled_event(new HandledEvent(event));
handled_tasks_.push_back(std::move(handled_event));
return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
}
void SendInputEventAck(blink::WebInputEvent::Type type,
InputEventAckState ack_result,
uint32_t touch_event_id) override {
additional_acked_events_.push_back(touch_event_id);
}
void SetNeedsMainFrame() override { needs_main_frame_ = true; }
protected:
base::test::ScopedFeatureList feature_list_;
scoped_refptr<base::TestSimpleTaskRunner> main_task_runner_;
blink::scheduler::MockRendererScheduler renderer_scheduler_;
scoped_refptr<MainThreadEventQueue> queue_;
std::vector<std::unique_ptr<HandledTask>> handled_tasks_;
std::vector<uint32_t> additional_acked_events_;
int raf_aligned_input_setting_;
bool needs_main_frame_;
base::TimeTicks frame_time_;
unsigned closure_count_;
};
TEST_P(MainThreadEventQueueTest, NonBlockingWheel) {
base::HistogramTester histogram_tester;
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());
for (WebMouseWheelEvent& event : kEvents)
HandleEvent(event, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING);
EXPECT_EQ(2u, event_queue().size());
EXPECT_EQ((raf_aligned_input_setting_ & kRafAlignedEnabledMouse) == 0,
main_task_runner_->HasPendingTask());
RunPendingTasksWithSimulatedRaf();
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);
}
histogram_tester.ExpectUniqueSample(kCoalescedCountHistogram, 1, 2);
}
TEST_P(MainThreadEventQueueTest, NonBlockingTouch) {
base::HistogramTester histogram_tester;
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_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);
}
histogram_tester.ExpectBucketCount(kCoalescedCountHistogram, 0, 1);
histogram_tester.ExpectBucketCount(kCoalescedCountHistogram, 1, 1);
}
TEST_P(MainThreadEventQueueTest, BlockingTouch) {
base::HistogramTester histogram_tester;
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(renderer_scheduler_,
DidHandleInputEventOnMainThread(testing::_, testing::_))
.Times(2);
// 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_EQ(0u, event_queue().size());
EXPECT_EQ(2u, additional_acked_events_.size());
EXPECT_EQ(kEvents[2].unique_touch_event_id, additional_acked_events_.at(0));
EXPECT_EQ(kEvents[3].unique_touch_event_id, additional_acked_events_.at(1));
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();
histogram_tester.ExpectUniqueSample(kCoalescedCountHistogram, 2, 2);
}
TEST_P(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_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_EQ(raf_aligned_input_setting_ !=
(kRafAlignedEnabledMouse | kRafAlignedEnabledTouch),
main_task_runner_->HasPendingTask());
RunPendingTasksWithSimulatedRaf();
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_P(MainThreadEventQueueTest, RafAlignedMouseInput) {
// Don't run the test when we aren't supporting rAF aligned input.
if ((raf_aligned_input_setting_ & kRafAlignedEnabledMouse) == 0)
return;
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());
// 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();
// 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();
// 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_EQ(wheelEvents[2].GetModifiers(),
handled_tasks_.at(3)->taskAsEvent()->Event().GetModifiers());
EXPECT_EQ(wheelEvents[0].GetModifiers(),
handled_tasks_.at(4)->taskAsEvent()->Event().GetModifiers());
}
TEST_P(MainThreadEventQueueTest, RafAlignedTouchInput) {
// Don't run the test when we aren't supporting rAF aligned input.
if ((raf_aligned_input_setting_ & kRafAlignedEnabledTouch) == 0)
return;
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());
// 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();
// 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();
// 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();
// 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();
}
TEST_P(MainThreadEventQueueTest, RafAlignedTouchInputCoalescedMoves) {
// Don't run the test when we aren't supporting rAF aligned input.
if ((raf_aligned_input_setting_ & kRafAlignedEnabledTouch) == 0)
return;
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_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_EQ(1u, additional_acked_events_.size());
additional_acked_events_.clear();
// Send a non-cancelable ack required event, and then a non-ack
// required event they should be coalesced together.
EXPECT_TRUE(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_);
EXPECT_TRUE(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_EQ(0u, additional_acked_events_.size());
// Send a non-ack required event, and then a non-cancelable ack
// required event they should be coalesced together.
EXPECT_TRUE(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_);
EXPECT_TRUE(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_EQ(0u, additional_acked_events_.size());
}
TEST_P(MainThreadEventQueueTest, RafAlignedTouchInputThrottlingMoves) {
// Don't run the test when we aren't supporting rAF aligned input.
if ((raf_aligned_input_setting_ & kRafAlignedEnabledTouch) == 0)
return;
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.
EXPECT_TRUE(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_TRUE(HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED));
EXPECT_TRUE(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_EQ(0u, event_queue().size());
EXPECT_EQ(0u, additional_acked_events_.size());
}
TEST_P(MainThreadEventQueueTest, BlockingTouchesDuringFling) {
SyntheticWebTouchEvent kEvents;
kEvents.PressPoint(10, 10);
kEvents.touch_start_or_first_touch_move = true;
set_enable_fling_passive_listener_flag(true);
EXPECT_FALSE(last_touch_start_forced_nonblocking_due_to_fling());
HandleEvent(kEvents, INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING_DUE_TO_FLING);
RunPendingTasksWithSimulatedRaf();
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_EQ((raf_aligned_input_setting_ & kRafAlignedEnabledTouch) == 0,
main_task_runner_->HasPendingTask());
RunPendingTasksWithSimulatedRaf();
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_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_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_P(MainThreadEventQueueTest, BlockingTouchesOutsideFling) {
SyntheticWebTouchEvent kEvents;
kEvents.PressPoint(10, 10);
kEvents.touch_start_or_first_touch_move = true;
set_enable_fling_passive_listener_flag(false);
HandleEvent(kEvents, INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
RunPendingTasksWithSimulatedRaf();
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_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_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_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);
}
// The boolean parameterized test varies whether rAF aligned input
// is enabled or not.
INSTANTIATE_TEST_CASE_P(
MainThreadEventQueueTests,
MainThreadEventQueueTest,
testing::Range(0u,
(kRafAlignedEnabledTouch | kRafAlignedEnabledMouse) + 1));
class MainThreadEventQueueInitializationTest
: public testing::Test,
public MainThreadEventQueueClient {
public:
MainThreadEventQueueInitializationTest()
: field_trial_list_(new base::FieldTrialList(nullptr)) {}
base::TimeDelta main_thread_responsiveness_threshold() {
return queue_->main_thread_responsiveness_threshold_;
}
bool enable_non_blocking_due_to_main_thread_responsiveness_flag() {
return queue_->enable_non_blocking_due_to_main_thread_responsiveness_flag_;
}
InputEventAckState HandleInputEvent(
const blink::WebCoalescedInputEvent& event,
const ui::LatencyInfo& latency,
InputEventDispatchType dispatch_type) override {
return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
}
void SendInputEventAck(blink::WebInputEvent::Type type,
InputEventAckState ack_result,
uint32_t touch_event_id) override {}
void SetNeedsMainFrame() override {}
protected:
scoped_refptr<MainThreadEventQueue> queue_;
base::test::ScopedFeatureList feature_list_;
blink::scheduler::MockRendererScheduler renderer_scheduler_;
scoped_refptr<base::TestSimpleTaskRunner> main_task_runner_;
std::unique_ptr<base::FieldTrialList> field_trial_list_;
};
TEST_F(MainThreadEventQueueInitializationTest,
MainThreadResponsivenessThresholdEnabled) {
feature_list_.InitFromCommandLine(
features::kMainThreadBusyScrollIntervention.name, "");
base::FieldTrialList::CreateFieldTrial(
"MainThreadResponsivenessScrollIntervention", "Enabled123");
queue_ = new MainThreadEventQueue(this, main_task_runner_,
&renderer_scheduler_, true);
EXPECT_TRUE(enable_non_blocking_due_to_main_thread_responsiveness_flag());
EXPECT_EQ(base::TimeDelta::FromMilliseconds(123),
main_thread_responsiveness_threshold());
}
TEST_F(MainThreadEventQueueInitializationTest,
MainThreadResponsivenessThresholdDisabled) {
base::FieldTrialList::CreateFieldTrial(
"MainThreadResponsivenessScrollIntervention", "Control");
queue_ = new MainThreadEventQueue(this, main_task_runner_,
&renderer_scheduler_, true);
EXPECT_FALSE(enable_non_blocking_due_to_main_thread_responsiveness_flag());
EXPECT_EQ(base::TimeDelta::FromMilliseconds(0),
main_thread_responsiveness_threshold());
}
TEST_P(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_P(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_FALSE(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_);
EXPECT_FALSE(HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_NOT_CONSUMED));
EXPECT_EQ(4u, event_queue().size());
if ((raf_aligned_input_setting_ & kRafAlignedEnabledTouch) != 0) {
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();
} else {
EXPECT_FALSE(needs_main_frame_);
main_task_runner_->RunUntilIdle();
}
EXPECT_EQ(0u, event_queue().size());
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_P(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_FALSE(HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED));
QueueClosure();
QueueClosure();
EXPECT_FALSE(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_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_P(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::kTimeStampForTesting);
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_EQ(WebInputEvent::kBlocking, kEvents[0].dispatch_type);
EXPECT_EQ(WebInputEvent::kEventNonBlocking, kEvents[1].dispatch_type);
EXPECT_FALSE(HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED));
EXPECT_TRUE(HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_NOT_CONSUMED));
EXPECT_FALSE(HandleEvent(scroll_start, INPUT_EVENT_ACK_STATE_NOT_CONSUMED));
EXPECT_EQ(3u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
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_P(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::kTimeStampForTesting);
EXPECT_FALSE(main_task_runner_->HasPendingTask());
EXPECT_EQ(0u, event_queue().size());
EXPECT_EQ(WebInputEvent::kBlocking, kEvents[0].dispatch_type);
EXPECT_EQ(WebInputEvent::kBlocking, kEvents[1].dispatch_type);
EXPECT_FALSE(HandleEvent(kEvents[0], INPUT_EVENT_ACK_STATE_NOT_CONSUMED));
EXPECT_FALSE(HandleEvent(kEvents[1], INPUT_EVENT_ACK_STATE_NOT_CONSUMED));
EXPECT_FALSE(HandleEvent(scroll_start, INPUT_EVENT_ACK_STATE_NOT_CONSUMED));
EXPECT_EQ(3u, event_queue().size());
RunPendingTasksWithSimulatedRaf();
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);
}
} // namespace content