blob: b87871d6905bed5828275e007c18c77f6300d398 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/input/legacy_touch_event_queue.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/browser/renderer_host/input/timeout_monitor.h"
#include "content/common/input/synthetic_web_input_event_builders.h"
#include "content/common/input/web_touch_event_traits.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebInputEvent.h"
#include "ui/events/base_event_utils.h"
using blink::WebGestureEvent;
using blink::WebInputEvent;
using blink::WebTouchEvent;
using blink::WebTouchPoint;
namespace content {
namespace {
const double kMinSecondsBetweenThrottledTouchmoves = 0.2;
const float kSlopLengthDips = 10;
const float kHalfSlopLengthDips = kSlopLengthDips / 2;
base::TimeDelta DefaultTouchTimeoutDelay() {
return base::TimeDelta::FromMilliseconds(1);
}
} // namespace
class LegacyTouchEventQueueTest : public testing::Test,
public TouchEventQueueClient {
public:
LegacyTouchEventQueueTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::UI),
acked_event_count_(0),
last_acked_event_state_(INPUT_EVENT_ACK_STATE_UNKNOWN),
slop_length_dips_(0) {}
~LegacyTouchEventQueueTest() override {}
// testing::Test
void SetUp() override {
ResetQueueWithConfig(TouchEventQueue::Config());
sent_events_ids_.clear();
}
void TearDown() override { queue_.reset(); }
// TouchEventQueueClient
void SendTouchEventImmediately(
const TouchEventWithLatencyInfo& event) override {
sent_events_.push_back(event.event);
sent_events_ids_.push_back(event.event.unique_touch_event_id);
if (sync_ack_result_) {
auto sync_ack_result = std::move(sync_ack_result_);
SendTouchEventAck(*sync_ack_result);
}
}
void OnTouchEventAck(const TouchEventWithLatencyInfo& event,
InputEventAckState ack_result) override {
++acked_event_count_;
last_acked_event_ = event.event;
last_acked_event_state_ = ack_result;
if (followup_touch_event_) {
std::unique_ptr<WebTouchEvent> followup_touch_event =
std::move(followup_touch_event_);
SendTouchEvent(*followup_touch_event);
}
if (followup_gesture_event_) {
std::unique_ptr<WebGestureEvent> followup_gesture_event =
std::move(followup_gesture_event_);
queue_->OnGestureScrollEvent(GestureEventWithLatencyInfo(
*followup_gesture_event, ui::LatencyInfo()));
}
}
void OnFilteringTouchEvent(const blink::WebTouchEvent& touch_event) override {
}
protected:
void SetUpForTouchMoveSlopTesting(double slop_length_dips) {
slop_length_dips_ = slop_length_dips;
}
void SetUpForTimeoutTesting(base::TimeDelta desktop_timeout_delay,
base::TimeDelta mobile_timeout_delay) {
TouchEventQueue::Config config;
config.desktop_touch_ack_timeout_delay = desktop_timeout_delay;
config.mobile_touch_ack_timeout_delay = mobile_timeout_delay;
config.touch_ack_timeout_supported = true;
ResetQueueWithConfig(config);
}
void SetUpForTimeoutTesting() {
SetUpForTimeoutTesting(DefaultTouchTimeoutDelay(),
DefaultTouchTimeoutDelay());
}
void SendTouchEvent(WebTouchEvent event) {
if (slop_length_dips_) {
event.moved_beyond_slop_region = false;
if (WebTouchEventTraits::IsTouchSequenceStart(event))
anchor_ = event.touches[0].position;
if (event.GetType() == WebInputEvent::kTouchMove) {
gfx::Vector2dF delta = anchor_ - event.touches[0].position;
if (delta.LengthSquared() > slop_length_dips_ * slop_length_dips_)
event.moved_beyond_slop_region = true;
}
} else {
event.moved_beyond_slop_region =
event.GetType() == WebInputEvent::kTouchMove;
}
queue_->QueueEvent(TouchEventWithLatencyInfo(event, ui::LatencyInfo()));
}
void SendGestureEvent(WebInputEvent::Type type) {
WebGestureEvent event(type, WebInputEvent::kNoModifiers,
ui::EventTimeStampToSeconds(ui::EventTimeForNow()));
queue_->OnGestureScrollEvent(
GestureEventWithLatencyInfo(event, ui::LatencyInfo()));
}
void SendTouchEventAck(InputEventAckState ack_result) {
DCHECK(!sent_events_ids_.empty());
queue_->ProcessTouchAck(ack_result, ui::LatencyInfo(),
sent_events_ids_.front());
sent_events_ids_.pop_front();
}
void SendTouchEventAckWithID(InputEventAckState ack_result,
int unique_event_id) {
queue_->ProcessTouchAck(ack_result, ui::LatencyInfo(), unique_event_id);
sent_events_ids_.erase(std::remove(sent_events_ids_.begin(),
sent_events_ids_.end(), unique_event_id),
sent_events_ids_.end());
}
void SendGestureEventAck(WebInputEvent::Type type,
InputEventAckState ack_result) {
GestureEventWithLatencyInfo event(
type, blink::WebInputEvent::kNoModifiers,
ui::EventTimeStampToSeconds(ui::EventTimeForNow()), ui::LatencyInfo());
queue_->OnGestureEventAck(event, ack_result);
}
void SetFollowupEvent(const WebTouchEvent& event) {
followup_touch_event_.reset(new WebTouchEvent(event));
}
void SetFollowupEvent(const WebGestureEvent& event) {
followup_gesture_event_.reset(new WebGestureEvent(event));
}
void SetSyncAckResult(InputEventAckState sync_ack_result) {
sync_ack_result_.reset(new InputEventAckState(sync_ack_result));
}
void PressTouchPoint(float x, float y) {
touch_event_.PressPoint(x, y);
SendTouchEvent();
}
void MoveTouchPoint(int index, float x, float y) {
touch_event_.MovePoint(index, x, y);
SendTouchEvent();
}
void MoveTouchPoints(int index0,
float x0,
float y0,
int index1,
float x1,
float y1) {
touch_event_.MovePoint(index0, x0, y0);
touch_event_.MovePoint(index1, x1, y1);
SendTouchEvent();
}
void ChangeTouchPointRadius(int index, float radius_x, float radius_y) {
CHECK_GE(index, 0);
CHECK_LT(index, touch_event_.kTouchesLengthCap);
WebTouchPoint& point = touch_event_.touches[index];
point.radius_x = radius_x;
point.radius_y = radius_y;
touch_event_.touches[index].state = WebTouchPoint::kStateMoved;
touch_event_.moved_beyond_slop_region = true;
WebTouchEventTraits::ResetType(WebInputEvent::kTouchMove,
touch_event_.TimeStampSeconds(),
&touch_event_);
SendTouchEvent();
}
void ChangeTouchPointRotationAngle(int index, float rotation_angle) {
CHECK_GE(index, 0);
CHECK_LT(index, touch_event_.kTouchesLengthCap);
WebTouchPoint& point = touch_event_.touches[index];
point.rotation_angle = rotation_angle;
touch_event_.touches[index].state = WebTouchPoint::kStateMoved;
touch_event_.moved_beyond_slop_region = true;
WebTouchEventTraits::ResetType(WebInputEvent::kTouchMove,
touch_event_.TimeStampSeconds(),
&touch_event_);
SendTouchEvent();
}
void ChangeTouchPointForce(int index, float force) {
CHECK_GE(index, 0);
CHECK_LT(index, touch_event_.kTouchesLengthCap);
WebTouchPoint& point = touch_event_.touches[index];
point.force = force;
touch_event_.touches[index].state = WebTouchPoint::kStateMoved;
touch_event_.moved_beyond_slop_region = true;
WebTouchEventTraits::ResetType(WebInputEvent::kTouchMove,
touch_event_.TimeStampSeconds(),
&touch_event_);
SendTouchEvent();
}
void ReleaseTouchPoint(int index) {
touch_event_.ReleasePoint(index);
SendTouchEvent();
}
void CancelTouchPoint(int index) {
touch_event_.CancelPoint(index);
SendTouchEvent();
}
void PrependTouchScrollNotification() {
queue_->PrependTouchScrollNotification();
}
void AdvanceTouchTime(double seconds) {
touch_event_.SetTimeStampSeconds(touch_event_.TimeStampSeconds() + seconds);
}
void ResetTouchEvent() { touch_event_ = SyntheticWebTouchEvent(); }
size_t GetAndResetAckedEventCount() {
size_t count = acked_event_count_;
acked_event_count_ = 0;
return count;
}
size_t GetAndResetSentEventCount() {
size_t count = sent_events_.size();
sent_events_.clear();
return count;
}
bool IsPendingAckTouchStart() const {
return queue_->IsPendingAckTouchStart();
}
void OnHasTouchEventHandlers(bool has_handlers) {
queue_->OnHasTouchEventHandlers(has_handlers);
}
void SetAckTimeoutDisabled() { queue_->SetAckTimeoutEnabled(false); }
void SetIsMobileOptimizedSite(bool is_mobile_optimized) {
queue_->SetIsMobileOptimizedSite(is_mobile_optimized);
}
bool IsTimeoutRunning() const { return queue_->IsTimeoutRunningForTesting(); }
bool HasPendingAsyncTouchMove() const {
return queue_->HasPendingAsyncTouchMoveForTesting();
}
size_t queued_event_count() const { return queue_->size(); }
const WebTouchEvent& latest_event() const {
return queue_->GetLatestEventForTesting().event;
}
const WebTouchEvent& acked_event() const { return last_acked_event_; }
const WebTouchEvent& sent_event() const {
DCHECK(!sent_events_.empty());
return sent_events_.back();
}
const std::vector<WebTouchEvent>& all_sent_events() const {
return sent_events_;
}
InputEventAckState acked_event_state() const {
return last_acked_event_state_;
}
static void RunTasksAndWait(base::TimeDelta delay) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), delay);
base::RunLoop().Run();
}
size_t uncancelable_touch_moves_pending_ack_count() const {
return queue_->uncancelable_touch_moves_pending_ack_count();
}
int GetUniqueTouchEventID() { return sent_events_ids_.back(); }
private:
void SendTouchEvent() {
SendTouchEvent(touch_event_);
touch_event_.ResetPoints();
}
void ResetQueueWithConfig(const TouchEventQueue::Config& config) {
queue_.reset(new LegacyTouchEventQueue(this, config));
queue_->OnHasTouchEventHandlers(true);
}
base::test::ScopedTaskEnvironment scoped_task_environment_;
std::unique_ptr<LegacyTouchEventQueue> queue_;
size_t acked_event_count_;
WebTouchEvent last_acked_event_;
std::vector<WebTouchEvent> sent_events_;
InputEventAckState last_acked_event_state_;
SyntheticWebTouchEvent touch_event_;
std::unique_ptr<WebTouchEvent> followup_touch_event_;
std::unique_ptr<WebGestureEvent> followup_gesture_event_;
std::unique_ptr<InputEventAckState> sync_ack_result_;
double slop_length_dips_;
gfx::PointF anchor_;
std::deque<int> sent_events_ids_;
};
// Tests that touch-events are queued properly.
TEST_F(LegacyTouchEventQueueTest, Basic) {
PressTouchPoint(1, 1);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// The second touch should not be sent since one is already in queue.
MoveTouchPoint(0, 5, 5);
EXPECT_EQ(2U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
// Receive an ACK for the first touch-event.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(WebInputEvent::kTouchStart, acked_event().GetType());
EXPECT_EQ(WebInputEvent::kBlocking, acked_event().dispatch_type);
// Receive an ACK for the second touch-event.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(WebInputEvent::kTouchMove, acked_event().GetType());
EXPECT_EQ(WebInputEvent::kBlocking, acked_event().dispatch_type);
}
// Tests that touch-events with multiple points are queued properly.
TEST_F(LegacyTouchEventQueueTest, BasicMultiTouch) {
const size_t kPointerCount = 10;
for (float i = 0; i < kPointerCount; ++i)
PressTouchPoint(i, i);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(kPointerCount, queued_event_count());
for (int i = 0; i < static_cast<int>(kPointerCount); ++i)
MoveTouchPoint(i, 1.f + i, 2.f + i);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
// All moves should coalesce.
EXPECT_EQ(kPointerCount + 1, queued_event_count());
for (int i = 0; i < static_cast<int>(kPointerCount); ++i)
ReleaseTouchPoint(kPointerCount - 1 - i);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(kPointerCount * 2 + 1, queued_event_count());
// Ack all presses.
for (size_t i = 0; i < kPointerCount; ++i)
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(kPointerCount, GetAndResetAckedEventCount());
EXPECT_EQ(kPointerCount, GetAndResetSentEventCount());
// Ack the coalesced move.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(kPointerCount, GetAndResetAckedEventCount());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Ack all releases.
for (size_t i = 0; i < kPointerCount; ++i)
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(kPointerCount, GetAndResetAckedEventCount());
EXPECT_EQ(kPointerCount - 1, GetAndResetSentEventCount());
}
// Tests that the touch-queue continues delivering events for an active touch
// sequence after all handlers are removed.
TEST_F(LegacyTouchEventQueueTest,
TouchesForwardedIfHandlerRemovedDuringSequence) {
OnHasTouchEventHandlers(true);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
// Send a touch-press event.
PressTouchPoint(1, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, queued_event_count());
// Signal that all touch handlers have been removed.
OnHasTouchEventHandlers(false);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, queued_event_count());
// Process the ack for the sent touch, ensuring that it is honored (despite
// the touch handler having been removed).
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, acked_event_state());
// Try forwarding a new pointer. It should be forwarded as usual.
PressTouchPoint(2, 2);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, queued_event_count());
// Further events for any pointer should be forwarded, even for pointers that
// reported no consumer.
MoveTouchPoint(1, 3, 3);
ReleaseTouchPoint(1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Events for the first pointer, that had a handler, should be forwarded.
MoveTouchPoint(0, 4, 4);
ReleaseTouchPoint(0);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(2U, queued_event_count());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, acked_event_state());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, acked_event_state());
}
// Tests that addition of a touch handler during a touch sequence will not cause
// the remaining sequence to be forwarded.
TEST_F(LegacyTouchEventQueueTest, ActiveSequenceNotForwardedWhenHandlersAdded) {
OnHasTouchEventHandlers(false);
// Send a touch-press event while there is no handler.
PressTouchPoint(1, 1);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, queued_event_count());
OnHasTouchEventHandlers(true);
// The remaining touch sequence should not be forwarded.
MoveTouchPoint(0, 5, 5);
ReleaseTouchPoint(0);
EXPECT_EQ(2U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, queued_event_count());
// A new touch sequence should resume forwarding.
PressTouchPoint(1, 1);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
}
// Tests that removal of a touch handler during a touch sequence will prevent
// the remaining sequence from being forwarded, even if another touch handler is
// registered during the same touch sequence.
TEST_F(LegacyTouchEventQueueTest, ActiveSequenceDroppedWhenHandlersRemoved) {
// Send a touch-press event.
PressTouchPoint(1, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, queued_event_count());
// Queue a touch-move event.
MoveTouchPoint(0, 5, 5);
EXPECT_EQ(2U, queued_event_count());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
// Unregister all touch handlers.
OnHasTouchEventHandlers(false);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(2U, queued_event_count());
// Repeated registration/unregstration of handlers should have no effect as
// we're still awaiting the ack arrival.
OnHasTouchEventHandlers(true);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(2U, queued_event_count());
OnHasTouchEventHandlers(false);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(2U, queued_event_count());
// The ack should be flush the queue.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(2U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, queued_event_count());
// Events should be dropped while there is no touch handler.
MoveTouchPoint(0, 10, 10);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
// Simulate touch handler registration in the middle of a touch sequence.
OnHasTouchEventHandlers(true);
// The touch end for the interrupted sequence should be dropped.
ReleaseTouchPoint(0);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
// A new touch sequence should be forwarded properly.
PressTouchPoint(1, 1);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
}
// Tests that removal/addition of a touch handler without any intervening
// touch activity has no affect on touch forwarding.
TEST_F(LegacyTouchEventQueueTest,
ActiveSequenceUnaffectedByRepeatedHandlerRemovalAndAddition) {
// Send a touch-press event.
PressTouchPoint(1, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, queued_event_count());
// Simulate the case where the touchstart handler removes itself, and adds a
// touchmove handler.
OnHasTouchEventHandlers(false);
OnHasTouchEventHandlers(true);
// Queue a touch-move event.
MoveTouchPoint(0, 5, 5);
EXPECT_EQ(2U, queued_event_count());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
// The ack should trigger forwarding of the touchmove, as if no touch
// handler registration changes have occurred.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, queued_event_count());
}
// Tests that touch-events are coalesced properly in the queue.
TEST_F(LegacyTouchEventQueueTest, Coalesce) {
// Send a touch-press event.
PressTouchPoint(1, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Send a few touch-move events, followed by a touch-release event. All the
// touch-move events should be coalesced into a single event.
for (float i = 5; i < 15; ++i)
MoveTouchPoint(0, i, i);
EXPECT_EQ(0U, GetAndResetSentEventCount());
ReleaseTouchPoint(0);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(3U, queued_event_count());
// ACK the press. Coalesced touch-move events should be sent.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(2U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(WebInputEvent::kTouchStart, acked_event().GetType());
EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, acked_event_state());
// ACK the moves.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(10U, GetAndResetAckedEventCount());
EXPECT_EQ(WebInputEvent::kTouchMove, acked_event().GetType());
// ACK the release.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(WebInputEvent::kTouchEnd, acked_event().GetType());
}
// Tests that an event that has already been sent but hasn't been ack'ed yet
// doesn't get coalesced with newer events.
TEST_F(LegacyTouchEventQueueTest, SentTouchEventDoesNotCoalesce) {
// Send a touch-press event.
PressTouchPoint(1, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Send a few touch-move events, followed by a touch-release event. All the
// touch-move events should be coalesced into a single event.
for (float i = 5; i < 15; ++i)
MoveTouchPoint(0, i, i);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(2U, queued_event_count());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, queued_event_count());
// The coalesced touch-move event has been sent to the renderer. Any new
// touch-move event should not be coalesced with the sent event.
MoveTouchPoint(0, 5, 5);
EXPECT_EQ(2U, queued_event_count());
MoveTouchPoint(0, 7, 7);
EXPECT_EQ(2U, queued_event_count());
}
// Tests that coalescing works correctly for multi-touch events.
TEST_F(LegacyTouchEventQueueTest, MultiTouch) {
// Press the first finger.
PressTouchPoint(1, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Move the finger.
MoveTouchPoint(0, 5, 5);
EXPECT_EQ(2U, queued_event_count());
// Now press a second finger.
PressTouchPoint(2, 2);
EXPECT_EQ(3U, queued_event_count());
// Move both fingers.
MoveTouchPoints(0, 10, 10, 1, 20, 20);
MoveTouchPoint(1, 20, 20);
EXPECT_EQ(4U, queued_event_count());
// Move only one finger now.
MoveTouchPoint(0, 15, 15);
EXPECT_EQ(4U, queued_event_count());
// Move the other finger.
MoveTouchPoint(1, 25, 25);
EXPECT_EQ(4U, queued_event_count());
// Make sure both fingers are marked as having been moved in the coalesced
// event.
const WebTouchEvent& event = latest_event();
EXPECT_EQ(WebTouchPoint::kStateMoved, event.touches[0].state);
EXPECT_EQ(WebTouchPoint::kStateMoved, event.touches[1].state);
}
// Tests that the touch-event queue is robust to redundant acks.
TEST_F(LegacyTouchEventQueueTest, SpuriousAcksIgnored) {
// Trigger a spurious ack.
SendTouchEventAckWithID(INPUT_EVENT_ACK_STATE_CONSUMED, 0);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
// Send and ack a touch press.
PressTouchPoint(1, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, queued_event_count());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, queued_event_count());
// Trigger a spurious ack.
SendTouchEventAckWithID(INPUT_EVENT_ACK_STATE_CONSUMED, 3);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
}
// Tests that touch-move events are not sent to the renderer if the preceding
// touch-press event did not have a consumer (and consequently, did not hit the
// main thread in the renderer). Also tests that all queued/coalesced touch
// events are flushed immediately when the ACK for the touch-press comes back
// with NO_CONSUMER status.
TEST_F(LegacyTouchEventQueueTest, NoConsumer) {
// The first touch-press should reach the renderer.
PressTouchPoint(1, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
// The second touch should not be sent since one is already in queue.
MoveTouchPoint(0, 5, 5);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(2U, queued_event_count());
// Receive an ACK for the first touch-event. This should release the queued
// touch-event, but it should not be sent to the renderer.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(WebInputEvent::kTouchMove, acked_event().GetType());
EXPECT_EQ(2U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
// Send a release event. This should not reach the renderer.
ReleaseTouchPoint(0);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(WebInputEvent::kTouchEnd, acked_event().GetType());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Send a press-event, followed by move and release events, and another press
// event, before the ACK for the first press event comes back. All of the
// events should be queued first. After the NO_CONSUMER ack for the first
// touch-press, all events upto the second touch-press should be flushed.
PressTouchPoint(10, 10);
EXPECT_EQ(1U, GetAndResetSentEventCount());
MoveTouchPoint(0, 5, 5);
MoveTouchPoint(0, 6, 5);
ReleaseTouchPoint(0);
PressTouchPoint(6, 5);
EXPECT_EQ(0U, GetAndResetSentEventCount());
// The queue should hold the first sent touch-press event, the coalesced
// touch-move event, the touch-end event and the second touch-press event.
EXPECT_EQ(4U, queued_event_count());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(WebInputEvent::kTouchEnd, acked_event().GetType());
EXPECT_EQ(4U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, queued_event_count());
// ACK the second press event as NO_CONSUMER too.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(WebInputEvent::kTouchStart, acked_event().GetType());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, queued_event_count());
// Send a second press event. Even though the first touch press had
// NO_CONSUMER, this press event should reach the renderer.
PressTouchPoint(1, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, queued_event_count());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(WebInputEvent::kTouchStart, acked_event().GetType());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
}
TEST_F(LegacyTouchEventQueueTest, ConsumerIgnoreMultiFinger) {
// Interleave three pointer press, move and release events.
PressTouchPoint(1, 1);
MoveTouchPoint(0, 5, 5);
PressTouchPoint(10, 10);
MoveTouchPoint(1, 15, 15);
PressTouchPoint(20, 20);
MoveTouchPoint(2, 25, 25);
ReleaseTouchPoint(2);
MoveTouchPoint(1, 20, 20);
ReleaseTouchPoint(1);
MoveTouchPoint(0, 10, 10);
ReleaseTouchPoint(0);
// Since the first touch-press is still pending ACK, no other event should
// have been sent to the renderer.
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(11U, queued_event_count());
// ACK the first press as CONSUMED. This should cause the first touch-move of
// the first touch-point to be dispatched.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(10U, queued_event_count());
// ACK the first move as CONSUMED.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(9U, queued_event_count());
// ACK the second press as NO_CONSUMER_EXISTS. The second pointer's touchmove
// should still be forwarded, despite lacking a direct consumer.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(8U, queued_event_count());
// ACK the coalesced move as NOT_CONSUMED.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(7U, queued_event_count());
// All remaining touch events should be forwarded, even if the third pointer
// press also reports no consumer.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(6U, queued_event_count());
while (queued_event_count())
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(6U, GetAndResetSentEventCount());
}
// Tests that touch-event's enqueued via a touch ack are properly handled.
TEST_F(LegacyTouchEventQueueTest, AckWithFollowupEvents) {
// Queue a touch down.
PressTouchPoint(1, 1);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
// Create a touch event that will be queued synchronously by a touch ack.
// Note, this will be triggered by all subsequent touch acks.
WebTouchEvent followup_event(
WebInputEvent::kTouchMove, WebInputEvent::kNoModifiers,
ui::EventTimeStampToSeconds(ui::EventTimeForNow()));
followup_event.touches_length = 1;
followup_event.touches[0].id = 0;
followup_event.touches[0].state = WebTouchPoint::kStateMoved;
SetFollowupEvent(followup_event);
// Receive an ACK for the press. This should cause the followup touch-move to
// be sent to the renderer.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, acked_event_state());
EXPECT_EQ(WebInputEvent::kTouchStart, acked_event().GetType());
// Queue another event.
MoveTouchPoint(0, 2, 2);
EXPECT_EQ(2U, queued_event_count());
// Receive an ACK for the touch-move followup event. This should cause the
// subsequent touch move event be sent to the renderer.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
}
// Tests that touch-events can be synchronously ack'ed.
TEST_F(LegacyTouchEventQueueTest, SynchronousAcks) {
// TouchStart
SetSyncAckResult(INPUT_EVENT_ACK_STATE_CONSUMED);
PressTouchPoint(1, 1);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// TouchMove
SetSyncAckResult(INPUT_EVENT_ACK_STATE_CONSUMED);
MoveTouchPoint(0, 2, 2);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// TouchEnd
SetSyncAckResult(INPUT_EVENT_ACK_STATE_CONSUMED);
ReleaseTouchPoint(0);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// TouchCancel (first inserting a TouchStart so the TouchCancel will be sent)
PressTouchPoint(1, 1);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
SetSyncAckResult(INPUT_EVENT_ACK_STATE_CONSUMED);
CancelTouchPoint(0);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
}
// Tests that followup events triggered by an immediate ack from
// TouchEventQueue::QueueEvent() are properly handled.
TEST_F(LegacyTouchEventQueueTest, ImmediateAckWithFollowupEvents) {
// Create a touch event that will be queued synchronously by a touch ack.
WebTouchEvent followup_event(
WebInputEvent::kTouchStart, WebInputEvent::kNoModifiers,
ui::EventTimeStampToSeconds(ui::EventTimeForNow()));
followup_event.touches_length = 1;
followup_event.touches[0].id = 1;
followup_event.touches[0].state = WebTouchPoint::kStatePressed;
SetFollowupEvent(followup_event);
// Now, enqueue a stationary touch that will not be forwarded. This should be
// immediately ack'ed with "NO_CONSUMER_EXISTS". The followup event should
// then be enqueued and immediately sent to the renderer.
WebTouchEvent stationary_event(
WebInputEvent::kTouchMove, WebInputEvent::kNoModifiers,
ui::EventTimeStampToSeconds(ui::EventTimeForNow()));
;
stationary_event.touches_length = 1;
stationary_event.touches[0].id = 1;
stationary_event.touches[0].state = WebTouchPoint::kStateStationary;
SendTouchEvent(stationary_event);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state());
EXPECT_EQ(WebInputEvent::kTouchMove, acked_event().GetType());
}
// Tests basic TouchEvent forwarding suppression.
TEST_F(LegacyTouchEventQueueTest, NoTouchBasic) {
// Disable TouchEvent forwarding.
OnHasTouchEventHandlers(false);
PressTouchPoint(30, 5);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// TouchMove should not be sent to renderer.
MoveTouchPoint(0, 65, 10);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// TouchEnd should not be sent to renderer.
ReleaseTouchPoint(0);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Enable TouchEvent forwarding.
OnHasTouchEventHandlers(true);
PressTouchPoint(80, 10);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
MoveTouchPoint(0, 80, 20);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
ReleaseTouchPoint(0);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
}
// Tests that IsTouchStartPendingAck works correctly.
TEST_F(LegacyTouchEventQueueTest, PendingStart) {
EXPECT_FALSE(IsPendingAckTouchStart());
// Send the touchstart for one point (#1).
PressTouchPoint(1, 1);
EXPECT_EQ(1U, queued_event_count());
EXPECT_TRUE(IsPendingAckTouchStart());
// Send a touchmove for that point (#2).
MoveTouchPoint(0, 5, 5);
EXPECT_EQ(2U, queued_event_count());
EXPECT_TRUE(IsPendingAckTouchStart());
// Ack the touchstart (#1).
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, queued_event_count());
EXPECT_FALSE(IsPendingAckTouchStart());
// Send a touchstart for another point (#3).
PressTouchPoint(10, 10);
EXPECT_EQ(2U, queued_event_count());
EXPECT_FALSE(IsPendingAckTouchStart());
// Ack the touchmove (#2).
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, queued_event_count());
EXPECT_TRUE(IsPendingAckTouchStart());
// Send a touchstart for a third point (#4).
PressTouchPoint(15, 15);
EXPECT_EQ(2U, queued_event_count());
EXPECT_TRUE(IsPendingAckTouchStart());
// Ack the touchstart for the second point (#3).
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, queued_event_count());
EXPECT_TRUE(IsPendingAckTouchStart());
// Ack the touchstart for the third point (#4).
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_FALSE(IsPendingAckTouchStart());
}
// Tests that the touch timeout is started when sending certain touch types.
TEST_F(LegacyTouchEventQueueTest, TouchTimeoutTypes) {
SetUpForTimeoutTesting();
// Sending a TouchStart will start the timeout.
PressTouchPoint(0, 1);
EXPECT_TRUE(IsTimeoutRunning());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
// A TouchMove should start the timeout.
MoveTouchPoint(0, 5, 5);
EXPECT_TRUE(IsTimeoutRunning());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
// A TouchEnd should not start the timeout.
ReleaseTouchPoint(0);
EXPECT_FALSE(IsTimeoutRunning());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
// A TouchCancel should not start the timeout.
PressTouchPoint(0, 1);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
ASSERT_FALSE(IsTimeoutRunning());
CancelTouchPoint(0);
EXPECT_FALSE(IsTimeoutRunning());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
}
// Tests that a delayed TouchEvent ack will trigger a TouchCancel timeout,
// disabling touch forwarding until the next TouchStart is received after
// the timeout events are ack'ed.
TEST_F(LegacyTouchEventQueueTest, TouchTimeoutBasic) {
SetUpForTimeoutTesting();
// Queue a TouchStart.
GetAndResetSentEventCount();
GetAndResetAckedEventCount();
PressTouchPoint(0, 1);
ASSERT_EQ(1U, GetAndResetSentEventCount());
ASSERT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_TRUE(IsTimeoutRunning());
// Delay the ack.
RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
// The timeout should have fired, synthetically ack'ing the timed-out event.
// TouchEvent forwarding is disabled until the ack is received for the
// timed-out event and the future cancel event.
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Ack'ing the original event should trigger a cancel event.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(WebInputEvent::kTouchCancel, sent_event().GetType());
EXPECT_NE(WebInputEvent::kBlocking, sent_event().dispatch_type);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Touch events should not be forwarded until we receive the cancel acks.
MoveTouchPoint(0, 1, 1);
ASSERT_EQ(0U, GetAndResetSentEventCount());
ASSERT_EQ(1U, GetAndResetAckedEventCount());
ReleaseTouchPoint(0);
ASSERT_EQ(0U, GetAndResetSentEventCount());
ASSERT_EQ(1U, GetAndResetAckedEventCount());
// The synthetic TouchCancel ack should not reach the client, but should
// resume touch forwarding.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
// Subsequent events should be handled normally.
PressTouchPoint(0, 1);
EXPECT_EQ(WebInputEvent::kTouchStart, sent_event().GetType());
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
}
// Tests that the timeout is never started if the renderer consumes
// a TouchEvent from the current touch sequence.
TEST_F(LegacyTouchEventQueueTest, NoTouchTimeoutIfRendererIsConsumingGesture) {
SetUpForTimeoutTesting();
// Queue a TouchStart.
PressTouchPoint(0, 1);
ASSERT_TRUE(IsTimeoutRunning());
// Mark the event as consumed. This should prevent the timeout from
// being activated on subsequent TouchEvents in this gesture.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
// A TouchMove should not start the timeout.
MoveTouchPoint(0, 5, 5);
EXPECT_FALSE(IsTimeoutRunning());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// A secondary TouchStart should not start the timeout.
PressTouchPoint(1, 0);
EXPECT_FALSE(IsTimeoutRunning());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// A TouchEnd should not start the timeout.
ReleaseTouchPoint(1);
EXPECT_FALSE(IsTimeoutRunning());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// A TouchCancel should not start the timeout.
CancelTouchPoint(0);
EXPECT_FALSE(IsTimeoutRunning());
}
// Tests that the timeout is never started if the renderer consumes
// a TouchEvent from the current touch sequence.
TEST_F(LegacyTouchEventQueueTest, NoTouchTimeoutIfDisabledAfterTouchStart) {
SetUpForTimeoutTesting();
// Queue a TouchStart.
PressTouchPoint(0, 1);
ASSERT_TRUE(IsTimeoutRunning());
// Send the ack immediately. The timeout should not have fired.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Now explicitly disable the timeout.
SetAckTimeoutDisabled();
EXPECT_FALSE(IsTimeoutRunning());
// A TouchMove should not start or trigger the timeout.
MoveTouchPoint(0, 5, 5);
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(1U, GetAndResetSentEventCount());
RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
}
// Tests that the timeout is never started if the ack is synchronous.
TEST_F(LegacyTouchEventQueueTest, NoTouchTimeoutIfAckIsSynchronous) {
SetUpForTimeoutTesting();
// Queue a TouchStart.
SetSyncAckResult(INPUT_EVENT_ACK_STATE_CONSUMED);
ASSERT_FALSE(IsTimeoutRunning());
PressTouchPoint(0, 1);
EXPECT_FALSE(IsTimeoutRunning());
}
// Tests that the timeout does not fire if explicitly disabled while an event
// is in-flight.
TEST_F(LegacyTouchEventQueueTest, NoTouchTimeoutIfDisabledWhileTimerIsActive) {
SetUpForTimeoutTesting();
// Queue a TouchStart.
PressTouchPoint(0, 1);
ASSERT_TRUE(IsTimeoutRunning());
// Verify that disabling the timeout also turns off the timer.
SetAckTimeoutDisabled();
EXPECT_FALSE(IsTimeoutRunning());
RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
}
// Tests that the timeout does not fire if the delay is zero.
TEST_F(LegacyTouchEventQueueTest, NoTouchTimeoutIfTimeoutDelayIsZero) {
SetUpForTimeoutTesting(base::TimeDelta(), base::TimeDelta());
// As the delay is zero, timeout behavior should be disabled.
PressTouchPoint(0, 1);
EXPECT_FALSE(IsTimeoutRunning());
RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
}
// Tests that timeout delays for mobile sites take effect when appropriate.
TEST_F(LegacyTouchEventQueueTest, TouchTimeoutConfiguredForMobile) {
base::TimeDelta desktop_delay = DefaultTouchTimeoutDelay();
base::TimeDelta mobile_delay = base::TimeDelta();
SetUpForTimeoutTesting(desktop_delay, mobile_delay);
// The desktop delay is non-zero, allowing timeout behavior.
SetIsMobileOptimizedSite(false);
PressTouchPoint(0, 1);
ASSERT_TRUE(IsTimeoutRunning());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
ReleaseTouchPoint(0);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(2U, GetAndResetAckedEventCount());
ASSERT_FALSE(IsTimeoutRunning());
// The mobile delay is zero, preventing timeout behavior.
SetIsMobileOptimizedSite(true);
PressTouchPoint(0, 1);
EXPECT_FALSE(IsTimeoutRunning());
RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
}
// Tests that a TouchCancel timeout plays nice when the timed out touch stream
// turns into a scroll gesture sequence.
TEST_F(LegacyTouchEventQueueTest, TouchTimeoutWithFollowupGesture) {
SetUpForTimeoutTesting();
// Queue a TouchStart.
PressTouchPoint(0, 1);
EXPECT_TRUE(IsTimeoutRunning());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// The cancelled sequence may turn into a scroll gesture.
WebGestureEvent followup_scroll(
WebInputEvent::kGestureScrollBegin, WebInputEvent::kNoModifiers,
ui::EventTimeStampToSeconds(ui::EventTimeForNow()));
SetFollowupEvent(followup_scroll);
// Delay the ack.
RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
// The timeout should have fired, disabling touch forwarding until both acks
// are received, acking the timed out event.
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Ack the original event, triggering a TouchCancel.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
// Ack the cancel event. Normally, this would resume touch forwarding,
// but we're still within a scroll gesture so it remains disabled.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
// Try to forward touch events for the current sequence.
GetAndResetSentEventCount();
GetAndResetAckedEventCount();
MoveTouchPoint(0, 1, 1);
ReleaseTouchPoint(0);
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(2U, GetAndResetAckedEventCount());
// Now end the scroll sequence, resuming touch handling.
SendGestureEvent(blink::WebInputEvent::kGestureScrollEnd);
PressTouchPoint(0, 1);
EXPECT_TRUE(IsTimeoutRunning());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
}
// Tests that a TouchCancel timeout plays nice when the timed out touch stream
// turns into a scroll gesture sequence, but the original event acks are
// significantly delayed.
TEST_F(LegacyTouchEventQueueTest,
TouchTimeoutWithFollowupGestureAndDelayedAck) {
SetUpForTimeoutTesting();
// Queue a TouchStart.
PressTouchPoint(0, 1);
EXPECT_TRUE(IsTimeoutRunning());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// The cancelled sequence may turn into a scroll gesture.
WebGestureEvent followup_scroll(
WebInputEvent::kGestureScrollBegin, WebInputEvent::kNoModifiers,
ui::EventTimeStampToSeconds(ui::EventTimeForNow()));
SetFollowupEvent(followup_scroll);
// Delay the ack.
RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
// The timeout should have fired, disabling touch forwarding until both acks
// are received and acking the timed out event.
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Try to forward a touch event.
GetAndResetSentEventCount();
GetAndResetAckedEventCount();
MoveTouchPoint(0, 1, 1);
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Now end the scroll sequence. Events will not be forwarded until the two
// outstanding touch acks are received.
SendGestureEvent(blink::WebInputEvent::kGestureScrollEnd);
MoveTouchPoint(0, 2, 2);
ReleaseTouchPoint(0);
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(2U, GetAndResetAckedEventCount());
// Ack the original event, triggering a cancel.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
// Ack the cancel event, resuming touch forwarding.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
PressTouchPoint(0, 1);
EXPECT_TRUE(IsTimeoutRunning());
EXPECT_EQ(1U, GetAndResetSentEventCount());
}
// Tests that a delayed TouchEvent ack will not trigger a TouchCancel timeout if
// the timed-out event had no consumer.
TEST_F(LegacyTouchEventQueueTest, NoCancelOnTouchTimeoutWithoutConsumer) {
SetUpForTimeoutTesting();
// Queue a TouchStart.
PressTouchPoint(0, 1);
ASSERT_EQ(1U, GetAndResetSentEventCount());
ASSERT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_TRUE(IsTimeoutRunning());
// Delay the ack.
RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
// The timeout should have fired, synthetically ack'ing the timed out event.
// TouchEvent forwarding is disabled until the original ack is received.
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Touch events should not be forwarded until we receive the original ack.
MoveTouchPoint(0, 1, 1);
ReleaseTouchPoint(0);
ASSERT_EQ(0U, GetAndResetSentEventCount());
ASSERT_EQ(2U, GetAndResetAckedEventCount());
// Ack'ing the original event should not trigger a cancel event, as the
// TouchStart had no consumer. However, it should re-enable touch forwarding.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
// Subsequent events should be handled normally.
PressTouchPoint(0, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
}
// Tests that TouchMove's movedBeyondSlopRegion is set to false if within the
// boundary-inclusive slop region for an unconsumed TouchStart.
TEST_F(LegacyTouchEventQueueTest, TouchMovedBeyondSlopRegionCheck) {
SetUpForTouchMoveSlopTesting(kSlopLengthDips);
// Queue a TouchStart.
PressTouchPoint(0, 0);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
ASSERT_EQ(1U, GetAndResetSentEventCount());
ASSERT_EQ(1U, GetAndResetAckedEventCount());
// TouchMove's movedBeyondSlopRegion within the slop region is set to false.
MoveTouchPoint(0, 0, kHalfSlopLengthDips);
EXPECT_EQ(1U, queued_event_count());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_FALSE(acked_event().moved_beyond_slop_region);
MoveTouchPoint(0, kHalfSlopLengthDips, 0);
EXPECT_EQ(1U, queued_event_count());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_FALSE(acked_event().moved_beyond_slop_region);
MoveTouchPoint(0, -kHalfSlopLengthDips, 0);
EXPECT_EQ(1U, queued_event_count());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_FALSE(acked_event().moved_beyond_slop_region);
MoveTouchPoint(0, -kSlopLengthDips, 0);
EXPECT_EQ(1U, queued_event_count());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_FALSE(acked_event().moved_beyond_slop_region);
MoveTouchPoint(0, 0, kSlopLengthDips);
EXPECT_EQ(1U, queued_event_count());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_FALSE(acked_event().moved_beyond_slop_region);
// When a TouchMove exceeds the (Euclidean) distance, the TouchMove's
// movedBeyondSlopRegion is set to true.
const float kFortyFiveDegreeSlopLengthXY =
kSlopLengthDips * std::sqrt(2.f) / 2;
MoveTouchPoint(0, kFortyFiveDegreeSlopLengthXY + .2f,
kFortyFiveDegreeSlopLengthXY + .2f);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_TRUE(acked_event().moved_beyond_slop_region);
}
// Tests that even very small TouchMove's movedBeyondSlopRegion is set to true
// when the slop region's dimension is 0.
TEST_F(LegacyTouchEventQueueTest,
MovedBeyondSlopRegionAlwaysTrueIfDimensionZero) {
// Queue a TouchStart.
PressTouchPoint(0, 0);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
ASSERT_EQ(1U, GetAndResetSentEventCount());
ASSERT_EQ(1U, GetAndResetAckedEventCount());
// Small TouchMove's movedBeyondSlopRegion is set to true.
MoveTouchPoint(0, 0.001f, 0.001f);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_TRUE(acked_event().moved_beyond_slop_region);
}
// Tests that secondary touch points can be forwarded even if the primary touch
// point had no consumer.
TEST_F(LegacyTouchEventQueueTest,
SecondaryTouchForwardedAfterPrimaryHadNoConsumer) {
// Queue a TouchStart.
PressTouchPoint(0, 0);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
ASSERT_EQ(1U, GetAndResetSentEventCount());
ASSERT_EQ(1U, GetAndResetAckedEventCount());
// Events should not be forwarded, as the point had no consumer.
MoveTouchPoint(0, 0, 15);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Simulate a secondary pointer press.
PressTouchPoint(20, 0);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// TouchMove with a secondary pointer should not be suppressed.
MoveTouchPoint(1, 25, 0);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
}
// Tests that secondary touch points can be forwarded after scrolling begins
// while first touch point has no consumer.
TEST_F(LegacyTouchEventQueueTest, NoForwardingAfterScrollWithNoTouchConsumers) {
// Queue a TouchStart.
PressTouchPoint(0, 0);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
ASSERT_EQ(1U, GetAndResetSentEventCount());
ASSERT_EQ(1U, GetAndResetAckedEventCount());
WebGestureEvent followup_scroll(WebInputEvent::kGestureScrollBegin,
WebInputEvent::kNoModifiers,
WebInputEvent::kTimeStampForTesting);
SetFollowupEvent(followup_scroll);
MoveTouchPoint(0, 20, 5);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state());
// The secondary pointer press should be forwarded.
PressTouchPoint(20, 0);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// TouchMove with a secondary pointer should also be forwarded.
MoveTouchPoint(1, 25, 0);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
}
TEST_F(LegacyTouchEventQueueTest, AsyncTouch) {
// Queue a TouchStart.
PressTouchPoint(0, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
for (int i = 0; i < 3; ++i) {
SendGestureEventAck(WebInputEvent::kGestureScrollUpdate,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
MoveTouchPoint(0, 10, 5 + i);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Consuming a scroll event will throttle subsequent touchmoves.
SendGestureEventAck(WebInputEvent::kGestureScrollUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED);
MoveTouchPoint(0, 10, 7 + i);
EXPECT_TRUE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
}
}
// Ensure that touchmove's are appropriately throttled during a typical
// scroll sequences that transitions between scrolls consumed and unconsumed.
TEST_F(LegacyTouchEventQueueTest, AsyncTouchThrottledAfterScroll) {
// Process a TouchStart
PressTouchPoint(0, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Now send the first touch move and associated GestureScrollBegin.
MoveTouchPoint(0, 0, 5);
WebGestureEvent followup_scroll(WebInputEvent::kGestureScrollBegin,
WebInputEvent::kNoModifiers,
WebInputEvent::kTimeStampForTesting);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
SendGestureEventAck(WebInputEvent::kGestureScrollBegin,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// Send the second touch move and associated GestureScrollUpdate, but don't
// ACK the gesture event yet.
MoveTouchPoint(0, 0, 50);
followup_scroll.SetType(WebInputEvent::kGestureScrollUpdate);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Now queue a second touchmove and verify it's not (yet) dispatched.
MoveTouchPoint(0, 0, 100);
SetFollowupEvent(followup_scroll);
EXPECT_TRUE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Queuing the final touchend should flush the pending async touchmove. In
// this case, we will first dispatch an async touchmove and then a touchend.
// For the async touchmove, we will not send ack again.
ReleaseTouchPoint(0);
followup_scroll.SetType(WebInputEvent::kGestureScrollEnd);
SetFollowupEvent(followup_scroll);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(2U, all_sent_events().size());
EXPECT_EQ(WebInputEvent::kTouchMove, all_sent_events()[0].GetType());
EXPECT_NE(WebInputEvent::kBlocking, all_sent_events()[0].dispatch_type);
EXPECT_EQ(WebInputEvent::kTouchEnd, all_sent_events()[1].GetType());
EXPECT_NE(WebInputEvent::kBlocking, all_sent_events()[1].dispatch_type);
EXPECT_EQ(2U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, queued_event_count());
// Ack the touchend.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Now mark the scrolls as not consumed (which would cause future touchmoves
// in the active sequence to be sent if there was one).
SendGestureEventAck(WebInputEvent::kGestureScrollUpdate,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
SendGestureEventAck(WebInputEvent::kGestureScrollUpdate,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// Start a new touch sequence and verify that throttling has been reset.
// Touch moves after the start of scrolling will again be throttled.
PressTouchPoint(0, 0);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
MoveTouchPoint(0, 0, 5);
followup_scroll.SetType(WebInputEvent::kGestureScrollBegin);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
MoveTouchPoint(0, 0, 6);
followup_scroll.SetType(WebInputEvent::kGestureScrollUpdate);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
MoveTouchPoint(0, 0, 10);
EXPECT_TRUE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Subsequent touchmove's should be deferred.
MoveTouchPoint(0, 0, 25);
EXPECT_TRUE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, uncancelable_touch_moves_pending_ack_count());
// The pending touchmove should be flushed with the the new touchmove if
// sufficient time has passed and ack to the client.
AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1);
MoveTouchPoint(0, 0, 15);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_NE(WebInputEvent::kBlocking, sent_event().dispatch_type);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Non-touchmove events should always flush any pending touchmove events. In
// this case, we will first dispatch an async touchmove and then a
// touchstart. For the async touchmove, we will not send ack again.
MoveTouchPoint(0, 0, 25);
EXPECT_TRUE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
PressTouchPoint(30, 30);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(2U, all_sent_events().size());
EXPECT_EQ(WebInputEvent::kTouchMove, all_sent_events()[0].GetType());
EXPECT_NE(WebInputEvent::kBlocking, all_sent_events()[0].dispatch_type);
EXPECT_EQ(WebInputEvent::kTouchStart, all_sent_events()[1].GetType());
EXPECT_EQ(WebInputEvent::kBlocking, all_sent_events()[1].dispatch_type);
EXPECT_EQ(2U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, queued_event_count());
// Ack the touchstart.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Send a secondary touchmove.
MoveTouchPoint(1, 0, 25);
EXPECT_TRUE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// An unconsumed scroll should resume synchronous touch handling.
SendGestureEventAck(WebInputEvent::kGestureScrollUpdate,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// The pending touchmove should be coalesced with the next (now synchronous)
// touchmove.
MoveTouchPoint(0, 0, 26);
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(WebInputEvent::kTouchMove, sent_event().GetType());
EXPECT_EQ(WebTouchPoint::kStateMoved, sent_event().touches[0].state);
EXPECT_EQ(WebTouchPoint::kStateMoved, sent_event().touches[1].state);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
// Subsequent touches will queue until the preceding, synchronous touches are
// ack'ed.
ReleaseTouchPoint(1);
EXPECT_EQ(2U, queued_event_count());
ReleaseTouchPoint(0);
EXPECT_EQ(3U, queued_event_count());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
EXPECT_EQ(WebInputEvent::kTouchEnd, sent_event().GetType());
EXPECT_EQ(2U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
EXPECT_EQ(WebInputEvent::kTouchEnd, sent_event().GetType());
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
}
TEST_F(LegacyTouchEventQueueTest, AsyncTouchFlushedByTouchEnd) {
PressTouchPoint(0, 0);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Initiate async touchmove dispatch after the start of a scroll sequence.
MoveTouchPoint(0, 0, 5);
WebGestureEvent followup_scroll(WebInputEvent::kGestureScrollBegin,
WebInputEvent::kNoModifiers,
WebInputEvent::kTimeStampForTesting);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
MoveTouchPoint(0, 0, 10);
followup_scroll.SetType(WebInputEvent::kGestureScrollUpdate);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Now queue a second touchmove and verify it's not (yet) dispatched.
MoveTouchPoint(0, 0, 100);
EXPECT_TRUE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Return the touch sequence to the original touchstart position. Note that
// this (0, 0) touchmove will coalesce with the previous (0, 100) touchmove.
MoveTouchPoint(0, 0, 0);
EXPECT_TRUE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Queuing the final touchend should flush the pending, async touchmove. In
// this case, we will first dispatch an async touchmove and then a touchend.
// For the async touchmove, we will not send ack again.
ReleaseTouchPoint(0);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(2U, all_sent_events().size());
EXPECT_EQ(WebInputEvent::kTouchMove, all_sent_events()[0].GetType());
EXPECT_NE(WebInputEvent::kBlocking, all_sent_events()[0].dispatch_type);
EXPECT_EQ(0, all_sent_events()[0].touches[0].position.x);
EXPECT_EQ(0, all_sent_events()[0].touches[0].position.y);
EXPECT_EQ(WebInputEvent::kTouchEnd, all_sent_events()[1].GetType());
EXPECT_NE(WebInputEvent::kBlocking, all_sent_events()[1].dispatch_type);
EXPECT_EQ(2U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
}
// Ensure that async touch dispatch and touch ack timeout interactions work
// appropriately.
TEST_F(LegacyTouchEventQueueTest, AsyncTouchWithAckTimeout) {
SetUpForTimeoutTesting();
// The touchstart should start the timeout.
PressTouchPoint(0, 0);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_TRUE(IsTimeoutRunning());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_FALSE(IsTimeoutRunning());
// The start of a scroll gesture should trigger async touch event dispatch.
MoveTouchPoint(0, 1, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_TRUE(IsTimeoutRunning());
WebGestureEvent followup_scroll(WebInputEvent::kGestureScrollBegin,
WebInputEvent::kNoModifiers,
WebInputEvent::kTimeStampForTesting);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
SendGestureEventAck(WebInputEvent::kGestureScrollUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED);
// An async touch should fire after the throttling interval has expired, but
// it should not start the touch ack timeout.
MoveTouchPoint(0, 5, 5);
EXPECT_TRUE(HasPendingAsyncTouchMove());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1);
MoveTouchPoint(0, 5, 5);
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_NE(WebInputEvent::kBlocking, sent_event().dispatch_type);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// An unconsumed scroll event will resume synchronous touchmoves, which are
// subject to the ack timeout.
SendGestureEventAck(WebInputEvent::kGestureScrollUpdate,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
MoveTouchPoint(0, 20, 5);
EXPECT_TRUE(IsTimeoutRunning());
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
EXPECT_EQ(1U, GetAndResetSentEventCount());
// The timeout should fire, disabling touch forwarding until both acks are
// received and acking the timed out event.
RunTasksAndWait(DefaultTouchTimeoutDelay() * 2);
EXPECT_FALSE(IsTimeoutRunning());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
// Ack'ing the original event should trigger a cancel event.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_NE(WebInputEvent::kBlocking, sent_event().dispatch_type);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Subsequent touchmove's should not be forwarded, even as the scroll gesture
// goes from unconsumed to consumed.
SendGestureEventAck(WebInputEvent::kGestureScrollUpdate,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
MoveTouchPoint(0, 20, 5);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
SendGestureEventAck(WebInputEvent::kGestureScrollUpdate,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
MoveTouchPoint(0, 25, 5);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
}
// Ensure that if the touch ack for an async touchmove triggers a follow-up
// touch event, that follow-up touch will be forwarded appropriately.
TEST_F(LegacyTouchEventQueueTest, AsyncTouchWithTouchCancelAfterAck) {
PressTouchPoint(0, 0);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// The start of a scroll gesture should trigger async touch event dispatch.
MoveTouchPoint(0, 1, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
WebGestureEvent followup_scroll(WebInputEvent::kGestureScrollBegin,
WebInputEvent::kNoModifiers,
WebInputEvent::kTimeStampForTesting);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, queued_event_count());
SendGestureEvent(WebInputEvent::kGestureScrollUpdate);
// The async touchmove should be ack'ed immediately, but not forwarded.
// However, because the ack triggers a touchcancel, both the pending touch and
// the queued touchcancel should be flushed.
WebTouchEvent followup_cancel(WebInputEvent::kTouchCancel,
WebInputEvent::kNoModifiers,
WebInputEvent::kTimeStampForTesting);
followup_cancel.touches_length = 1;
followup_cancel.touches[0].state = WebTouchPoint::kStateCancelled;
SetFollowupEvent(followup_cancel);
MoveTouchPoint(0, 5, 5);
EXPECT_EQ(1U, queued_event_count());
EXPECT_EQ(2U, all_sent_events().size());
EXPECT_EQ(WebInputEvent::kTouchMove, all_sent_events()[0].GetType());
EXPECT_NE(WebInputEvent::kBlocking, all_sent_events()[0].dispatch_type);
EXPECT_EQ(WebInputEvent::kTouchCancel, all_sent_events()[1].GetType());
EXPECT_NE(WebInputEvent::kBlocking, all_sent_events()[1].dispatch_type);
EXPECT_EQ(2U, GetAndResetSentEventCount());
// Sending the ack is because the async touchmove is not ready for
// dispatching send the ack immediately.
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(WebInputEvent::kTouchMove, acked_event().GetType());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(WebInputEvent::kTouchCancel, acked_event().GetType());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, GetAndResetSentEventCount());
}
// Ensure that the async touch is fully reset if the touch sequence restarts
// without properly terminating.
TEST_F(LegacyTouchEventQueueTest, AsyncTouchWithHardTouchStartReset) {
PressTouchPoint(0, 0);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Trigger async touchmove dispatch.
MoveTouchPoint(0, 1, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
WebGestureEvent followup_scroll(WebInputEvent::kGestureScrollBegin,
WebInputEvent::kNoModifiers,
WebInputEvent::kTimeStampForTesting);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, queued_event_count());
SendGestureEvent(WebInputEvent::kGestureScrollUpdate);
// The async touchmove should be immediately ack'ed but delivery is deferred.
MoveTouchPoint(0, 2, 2);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(WebInputEvent::kTouchMove, acked_event().GetType());
// The queue should be robust to hard touch restarts with a new touch
// sequence. In this case, the deferred async touch should not be flushed
// by the new touch sequence.
SendGestureEvent(WebInputEvent::kGestureScrollEnd);
ResetTouchEvent();
PressTouchPoint(0, 0);
EXPECT_EQ(WebInputEvent::kTouchStart, sent_event().GetType());
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
}
// Ensure that even when the interval expires, we still need to wait for the
// ack sent back from render to send the next async touchmove once the scroll
// starts.
TEST_F(LegacyTouchEventQueueTest, SendNextThrottledAsyncTouchMoveAfterAck) {
// Process a TouchStart
PressTouchPoint(0, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Initiate async touchmove dispatch after the start of a scroll sequence.
MoveTouchPoint(0, 0, 5);
WebGestureEvent followup_scroll(WebInputEvent::kGestureScrollBegin,
WebInputEvent::kNoModifiers,
WebInputEvent::kTimeStampForTesting);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
MoveTouchPoint(0, 0, 10);
followup_scroll.SetType(WebInputEvent::kGestureScrollUpdate);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// We set the next touch event time to be after the throttled interval.
AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1);
// Dispatch the touch move event when sufficient time has passed.
MoveTouchPoint(0, 0, 40);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_NE(WebInputEvent::kBlocking, sent_event().dispatch_type);
// When we dispatch an async touchmove, we do not put it back to the queue
// any more and we will ack to client right away.
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, uncancelable_touch_moves_pending_ack_count());
// Do not dispatch the event until throttledTouchmoves intervals expires and
// receive an ack from render.
AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1);
MoveTouchPoint(0, 0, 50);
EXPECT_TRUE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, uncancelable_touch_moves_pending_ack_count());
// Send pending_async_touch_move_ when we receive an ack back from render,
// but we will not send an ack for pending_async_touch_move_ becasue it is
// been acked before.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, uncancelable_touch_moves_pending_ack_count());
}
// Ensure that even when we receive the ack from render, we still need to wait
// for the interval expires to send the next async touchmove once the scroll
// starts.
TEST_F(LegacyTouchEventQueueTest, SendNextAsyncTouchMoveAfterAckAndTimeExpire) {
// Process a TouchStart
PressTouchPoint(0, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Initiate async touchmove dispatch after the start of a scroll sequence.
MoveTouchPoint(0, 0, 5);
WebGestureEvent followup_scroll(WebInputEvent::kGestureScrollBegin,
WebInputEvent::kNoModifiers,
WebInputEvent::kTimeStampForTesting);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
MoveTouchPoint(0, 0, 10);
followup_scroll.SetType(WebInputEvent::kGestureScrollUpdate);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Dispatch the touch move event when sufficient time has passed.
AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1);
MoveTouchPoint(0, 0, 40);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_NE(WebInputEvent::kBlocking, sent_event().dispatch_type);
// When we dispatch an async touchmove, we do not put it back to the queue
// any more and we will ack to client right away.
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, uncancelable_touch_moves_pending_ack_count());
// We receive an ack back from render but the time interval is not expired,
// so we do not dispatch the touch move event.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(0U, uncancelable_touch_moves_pending_ack_count());
MoveTouchPoint(0, 0, 50);
EXPECT_TRUE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Dispatch the touch move when sufficient time has passed.
AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1);
MoveTouchPoint(0, 0, 50);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(WebInputEvent::kTouchMove, sent_event().GetType());
EXPECT_NE(WebInputEvent::kBlocking, sent_event().dispatch_type);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, uncancelable_touch_moves_pending_ack_count());
}
TEST_F(LegacyTouchEventQueueTest, AsyncTouchFlushedByNonTouchMove) {
// Process a TouchStart
PressTouchPoint(0, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Initiate async touchmove dispatch after the start of a scroll sequence.
MoveTouchPoint(0, 0, 5);
WebGestureEvent followup_scroll(WebInputEvent::kGestureScrollBegin,
WebInputEvent::kNoModifiers,
WebInputEvent::kTimeStampForTesting);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
MoveTouchPoint(0, 0, 10);
followup_scroll.SetType(WebInputEvent::kGestureScrollUpdate);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Dispatch the touch move when sufficient time has passed.
AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1);
MoveTouchPoint(0, 0, 40);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_NE(WebInputEvent::kBlocking, sent_event().dispatch_type);
// When we dispatch an async touchmove, we do not put it back to the queue
// any more and we will ack to client right away.
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, uncancelable_touch_moves_pending_ack_count());
for (int i = 0; i < 3; ++i) {
// We throttle the touchmoves, put it in the pending_async_touch_move_,
// do not dispatch it.
MoveTouchPoint(0, 10 + 10 * i, 10 + 10 * i);
EXPECT_TRUE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(static_cast<size_t>(i + 1),
uncancelable_touch_moves_pending_ack_count());
// Send touchstart will flush pending_async_touch_move_, and increase the
// count. In this case, we will first dispatch an async touchmove and
// then a touchstart. For the async touchmove, we will not send ack again.
PressTouchPoint(30, 30);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(2U, all_sent_events().size());
EXPECT_EQ(WebInputEvent::kTouchMove, all_sent_events()[0].GetType());
EXPECT_NE(WebInputEvent::kBlocking, all_sent_events()[0].dispatch_type);
EXPECT_EQ(10 + 10 * i, all_sent_events()[0].touches[0].position.x);
EXPECT_EQ(10 + 10 * i, all_sent_events()[0].touches[0].position.y);
EXPECT_EQ(static_cast<size_t>(i + 2),
uncancelable_touch_moves_pending_ack_count());
EXPECT_EQ(WebInputEvent::kTouchStart, all_sent_events()[1].GetType());
EXPECT_EQ(WebInputEvent::kBlocking, all_sent_events()[1].dispatch_type);
EXPECT_EQ(2U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
SendTouchEventAckWithID(INPUT_EVENT_ACK_STATE_NOT_CONSUMED,
GetUniqueTouchEventID());
EXPECT_EQ(0U, queued_event_count());
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
}
EXPECT_EQ(4U, uncancelable_touch_moves_pending_ack_count());
// When we receive an ack from render we decrease the count.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(3U, uncancelable_touch_moves_pending_ack_count());
EXPECT_EQ(0U, queued_event_count());
EXPECT_FALSE(HasPendingAsyncTouchMove());
// Do not dispatch the next uncancelable touchmove when we have not received
// all the acks back from render.
AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1);
MoveTouchPoint(0, 20, 30);
EXPECT_TRUE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(3U, uncancelable_touch_moves_pending_ack_count());
// Once we receive the ack from render, we do not dispatch the
// pending_async_touchmove_ until the count is 0.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(2U, uncancelable_touch_moves_pending_ack_count());
EXPECT_EQ(0U, queued_event_count());
EXPECT_TRUE(HasPendingAsyncTouchMove());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// When we receive this ack from render, and the count is 0, so we can
// dispatch the pending_async_touchmove_.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, uncancelable_touch_moves_pending_ack_count());
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(WebInputEvent::kTouchMove, sent_event().GetType());
EXPECT_NE(WebInputEvent::kBlocking, sent_event().dispatch_type);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
}
// Ensure that even when we receive the ack from render, we still need to wait
// for the interval expires to send the next async touchmove once the scroll
// starts.
TEST_F(LegacyTouchEventQueueTest, DoNotIncreaseIfClientConsumeAsyncTouchMove) {
// Process a TouchStart
PressTouchPoint(0, 1);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Initiate async touchmove dispatch after the start of a scroll sequence.
MoveTouchPoint(0, 0, 5);
WebGestureEvent followup_scroll(WebInputEvent::kGestureScrollBegin,
WebInputEvent::kNoModifiers,
WebInputEvent::kTimeStampForTesting);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
MoveTouchPoint(0, 0, 10);
followup_scroll.SetType(WebInputEvent::kGestureScrollUpdate);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Dispatch the touch move event when sufficient time has passed.
AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1);
MoveTouchPoint(0, 0, 40);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_NE(WebInputEvent::kBlocking, sent_event().dispatch_type);
// When we dispatch an async touchmove, we do not put it back to the queue
// any more and we will ack to client right away.
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, uncancelable_touch_moves_pending_ack_count());
// We receive an ack back from render but the time interval is not expired,
// so we do not dispatch the touch move event.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(0U, uncancelable_touch_moves_pending_ack_count());
MoveTouchPoint(0, 0, 50);
EXPECT_TRUE(HasPendingAsyncTouchMove());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Dispatch the touch move when sufficient time has passed. Becasue the event
// is consumed by client already, we would not increase the count and ack to
// client again.
AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1);
SetSyncAckResult(INPUT_EVENT_ACK_STATE_CONSUMED);
MoveTouchPoint(0, 0, 50);
EXPECT_FALSE(HasPendingAsyncTouchMove());
EXPECT_EQ(WebInputEvent::kTouchMove, sent_event().GetType());
EXPECT_NE(WebInputEvent::kBlocking, sent_event().dispatch_type);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, uncancelable_touch_moves_pending_ack_count());
}
TEST_F(LegacyTouchEventQueueTest, TouchAbsorptionWithConsumedFirstMove) {
// Queue a TouchStart.
PressTouchPoint(0, 1);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
MoveTouchPoint(0, 20, 5);
SendGestureEvent(blink::WebInputEvent::kGestureScrollBegin);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(2U, GetAndResetSentEventCount());
// Even if the first touchmove event was consumed, subsequent unconsumed
// touchmove events should trigger scrolling.
MoveTouchPoint(0, 60, 5);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
EXPECT_EQ(1U, GetAndResetSentEventCount());
MoveTouchPoint(0, 20, 5);
WebGestureEvent followup_scroll(WebInputEvent::kGestureScrollUpdate,
WebInputEvent::kNoModifiers,
WebInputEvent::kTimeStampForTesting);
SetFollowupEvent(followup_scroll);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
SendGestureEventAck(WebInputEvent::kGestureScrollUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Touch move event is throttled.
MoveTouchPoint(0, 60, 5);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
}
TEST_F(LegacyTouchEventQueueTest, TouchStartCancelableDuringScroll) {
// Queue a touchstart and touchmove that go unconsumed, transitioning to an
// active scroll sequence.
PressTouchPoint(0, 1);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
ASSERT_EQ(1U, GetAndResetSentEventCount());
MoveTouchPoint(0, 20, 5);
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
SendGestureEvent(blink::WebInputEvent::kGestureScrollBegin);
SendGestureEvent(blink::WebInputEvent::kGestureScrollUpdate);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
ASSERT_EQ(1U, GetAndResetSentEventCount());
// Even though scrolling has begun, touchstart events should be cancelable,
// allowing, for example, customized pinch processing.
PressTouchPoint(10, 11);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
ASSERT_EQ(1U, GetAndResetSentEventCount());
// As the touch start was consumed, touchmoves should no longer be throttled.
MoveTouchPoint(1, 11, 11);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
ASSERT_EQ(1U, GetAndResetSentEventCount());
// With throttling disabled, touchend and touchmove events should also be
// cancelable.
MoveTouchPoint(1, 12, 12);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
ASSERT_EQ(1U, GetAndResetSentEventCount());
ReleaseTouchPoint(1);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
ASSERT_EQ(1U, GetAndResetSentEventCount());
// If subsequent touchmoves aren't consumed, the generated scroll events
// will restore async touch dispatch.
MoveTouchPoint(0, 25, 5);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
SendGestureEvent(blink::WebInputEvent::kGestureScrollUpdate);
EXPECT_EQ(WebInputEvent::kBlocking, sent_event().dispatch_type);
ASSERT_EQ(1U, GetAndResetSentEventCount());
AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1);
MoveTouchPoint(0, 30, 5);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_NE(WebInputEvent::kBlocking, sent_event().dispatch_type);
ASSERT_EQ(1U, GetAndResetSentEventCount());
// The touchend will be uncancelable during an active scroll sequence.
ReleaseTouchPoint(0);
EXPECT_NE(WebInputEvent::kBlocking, sent_event().dispatch_type);
ASSERT_EQ(1U, GetAndResetSentEventCount());
}
TEST_F(LegacyTouchEventQueueTest, UnseenTouchPointerIdsNotForwarded) {
SyntheticWebTouchEvent event;
event.PressPoint(0, 0);
SendTouchEvent(event);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Give the touchmove a previously unseen pointer id; it should not be sent.
int press_id = event.touches[0].id;
event.MovePoint(0, 1, 1);
event.touches[0].id = 7;
SendTouchEvent(event);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Give the touchmove a valid id; it should be sent.
event.touches[0].id = press_id;
SendTouchEvent(event);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Do the same for release.
event.ReleasePoint(0);
event.touches[0].id = 11;
SendTouchEvent(event);
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
// Give the touchmove a valid id; it should be sent.
event.touches[0].id = press_id;
SendTouchEvent(event);
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
}
// Tests that touch points states are correct in TouchMove events.
TEST_F(LegacyTouchEventQueueTest, PointerStatesInTouchMove) {
PressTouchPoint(1, 1);
PressTouchPoint(2, 2);
PressTouchPoint(3, 3);
PressTouchPoint(4, 4);
EXPECT_EQ(4U, queued_event_count());
EXPECT_EQ(1U, GetAndResetSentEventCount());
// Receive ACK for the first three touch-events.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, queued_event_count());
// Test current touches state before sending TouchMoves.
const WebTouchEvent& event1 = sent_event();
EXPECT_EQ(WebInputEvent::kTouchStart, event1.GetType());
EXPECT_EQ(WebTouchPoint::kStateStationary, event1.touches[0].state);
EXPECT_EQ(WebTouchPoint::kStateStationary, event1.touches[1].state);
EXPECT_EQ(WebTouchPoint::kStateStationary, event1.touches[2].state);
EXPECT_EQ(WebTouchPoint::kStatePressed, event1.touches[3].state);
// Move x-position for 1st touch, y-position for 2nd touch
// and do not move other touches.
MoveTouchPoints(0, 1.1f, 1.f, 1, 2.f, 20.001f);
MoveTouchPoints(2, 3.f, 3.f, 3, 4.f, 4.f);
EXPECT_EQ(2U, queued_event_count());
// Receive an ACK for the last TouchPress event.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
// 1st TouchMove is sent. Test for touches state.
const WebTouchEvent& event2 = sent_event();
EXPECT_EQ(WebInputEvent::kTouchMove, event2.GetType());
EXPECT_EQ(WebTouchPoint::kStateMoved, event2.touches[0].state);
EXPECT_EQ(WebTouchPoint::kStateMoved, event2.touches[1].state);
EXPECT_EQ(WebTouchPoint::kStateStationary, event2.touches[2].state);
EXPECT_EQ(WebTouchPoint::kStateStationary, event2.touches[3].state);
// Move only 4th touch but not others.
MoveTouchPoints(0, 1.1f, 1.f, 1, 2.f, 20.001f);
MoveTouchPoints(2, 3.f, 3.f, 3, 4.1f, 4.1f);
// Receive an ACK for previous (1st) TouchMove.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
// 2nd TouchMove is sent. Test for touches state.
const WebTouchEvent& event3 = sent_event();
EXPECT_EQ(WebInputEvent::kTouchMove, event3.GetType());
EXPECT_EQ(WebTouchPoint::kStateStationary, event3.touches[0].state);
EXPECT_EQ(WebTouchPoint::kStateStationary, event3.touches[1].state);
EXPECT_EQ(WebTouchPoint::kStateStationary, event3.touches[2].state);
EXPECT_EQ(WebTouchPoint::kStateMoved, event3.touches[3].state);
}
// Tests that touch point state is correct in TouchMove events
// when point properties other than position changed.
TEST_F(LegacyTouchEventQueueTest, PointerStatesWhenOtherThanPositionChanged) {
PressTouchPoint(1, 1);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
// Default initial radiusX/Y is (1.f, 1.f).
// Default initial rotationAngle is 1.f.
// Default initial force is 1.f.
// Change touch point radius only.
ChangeTouchPointRadius(0, 1.5f, 1.f);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
// TouchMove is sent. Test for pointer state.
const WebTouchEvent& event1 = sent_event();
EXPECT_EQ(WebInputEvent::kTouchMove, event1.GetType());
EXPECT_EQ(WebTouchPoint::kStateMoved, event1.touches[0].state);
// Change touch point force.
ChangeTouchPointForce(0, 0.9f);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
// TouchMove is sent. Test for pointer state.
const WebTouchEvent& event2 = sent_event();
EXPECT_EQ(WebInputEvent::kTouchMove, event2.GetType());
EXPECT_EQ(WebTouchPoint::kStateMoved, event2.touches[0].state);
// Change touch point rotationAngle.
ChangeTouchPointRotationAngle(0, 1.1f);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
// TouchMove is sent. Test for pointer state.
const WebTouchEvent& event3 = sent_event();
EXPECT_EQ(WebInputEvent::kTouchMove, event3.GetType());
EXPECT_EQ(WebTouchPoint::kStateMoved, event3.touches[0].state);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(4U, GetAndResetSentEventCount());
EXPECT_EQ(4U, GetAndResetAckedEventCount());
}
// Tests that TouchMoves are filtered when none of the points are changed.
TEST_F(LegacyTouchEventQueueTest, FilterTouchMovesWhenNoPointerChanged) {
PressTouchPoint(1, 1);
PressTouchPoint(2, 2);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(2U, GetAndResetSentEventCount());
EXPECT_EQ(2U, GetAndResetAckedEventCount());
// Move 1st touch point.
MoveTouchPoint(0, 10, 10);
EXPECT_EQ(1U, queued_event_count());
// TouchMove should be allowed and test for touches state.
const WebTouchEvent& event1 = sent_event();
EXPECT_EQ(WebInputEvent::kTouchMove, event1.GetType());
EXPECT_EQ(WebTouchPoint::kStateMoved, event1.touches[0].state);
EXPECT_EQ(WebTouchPoint::kStateStationary, event1.touches[1].state);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(0U, GetAndResetAckedEventCount());
// Do not really move any touch points, but use previous values.
MoveTouchPoint(0, 10, 10);
ChangeTouchPointRadius(1, 1, 1);
MoveTouchPoint(1, 2, 2);
EXPECT_EQ(2U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
// Receive an ACK for 1st TouchMove.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
// Tries to forward TouchMove but should be filtered
// when none of the touch points have changed.
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
EXPECT_EQ(4U, GetAndResetAckedEventCount());
EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state());
// Move 2nd touch point.
MoveTouchPoint(1, 3, 3);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, queued_event_count());
// TouchMove should be allowed and test for touches state.
const WebTouchEvent& event2 = sent_event();
EXPECT_EQ(WebInputEvent::kTouchMove, event2.GetType());
EXPECT_EQ(WebTouchPoint::kStateStationary, event2.touches[0].state);
EXPECT_EQ(WebTouchPoint::kStateMoved, event2.touches[1].state);
EXPECT_EQ(1U, GetAndResetSentEventCount());
EXPECT_EQ(1U, GetAndResetAckedEventCount());
}
// Tests that touch-scroll-notification is not pushed into an empty queue.
TEST_F(LegacyTouchEventQueueTest, TouchScrollNotificationOrder_EmptyQueue) {
PrependTouchScrollNotification();
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(0U, GetAndResetSentEventCount());
}
// Tests touch-scroll-notification firing order when the event is placed at the
// end of touch queue because of a pending ack for the head of the queue.
TEST_F(LegacyTouchEventQueueTest, TouchScrollNotificationOrder_EndOfQueue) {
PressTouchPoint(1, 1);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(1U, queued_event_count());
// Send the touch-scroll-notification when 3 events are in the queue.
PrependTouchScrollNotification();
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(2U, queued_event_count());
// Receive an ACK for the touchstart.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(WebInputEvent::kTouchStart, acked_event().GetType());
EXPECT_EQ(1U, queued_event_count());
// Receive an ACK for the touch-scroll-notification.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_IGNORED);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(WebInputEvent::kTouchStart, all_sent_events()[0].GetType());
EXPECT_EQ(WebInputEvent::kTouchScrollStarted, all_sent_events()[1].GetType());
EXPECT_EQ(2U, GetAndResetSentEventCount());
}
// Tests touch-scroll-notification firing order when the event is placed in the
// 2nd position in the touch queue between two events.
TEST_F(LegacyTouchEventQueueTest, TouchScrollNotificationOrder_SecondPosition) {
PressTouchPoint(1, 1);
MoveTouchPoint(0, 5, 5);
ReleaseTouchPoint(0);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(3U, queued_event_count());
// Send the touch-scroll-notification when 3 events are in the queue.
PrependTouchScrollNotification();
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(4U, queued_event_count());
// Receive an ACK for the touchstart.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(WebInputEvent::kTouchStart, acked_event().GetType());
EXPECT_EQ(3U, queued_event_count());
// Receive an ACK for the touch-scroll-notification.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_IGNORED);
EXPECT_EQ(0U, GetAndResetAckedEventCount());
EXPECT_EQ(2U, queued_event_count());
// Receive an ACK for the touchmove.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(WebInputEvent::kTouchMove, acked_event().GetType());
EXPECT_EQ(1U, queued_event_count());
// Receive an ACK for the touchend.
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(WebInputEvent::kTouchEnd, acked_event().GetType());
EXPECT_EQ(0U, queued_event_count());
EXPECT_EQ(WebInputEvent::kTouchStart, all_sent_events()[0].GetType());
EXPECT_EQ(WebInputEvent::kTouchScrollStarted, all_sent_events()[1].GetType());
EXPECT_EQ(WebInputEvent::kTouchMove, all_sent_events()[2].GetType());
EXPECT_EQ(WebInputEvent::kTouchEnd, all_sent_events()[3].GetType());
EXPECT_EQ(4U, GetAndResetSentEventCount());
}
// Tests that if touchStartOrFirstTouchMove is correctly set up for touch
// events.
TEST_F(LegacyTouchEventQueueTest, TouchStartOrFirstTouchMove) {
PressTouchPoint(1, 1);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(WebInputEvent::kTouchStart, sent_event().GetType());
EXPECT_TRUE(sent_event().touch_start_or_first_touch_move);
MoveTouchPoint(0, 5, 5);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(WebInputEvent::kTouchMove, sent_event().GetType());
EXPECT_TRUE(sent_event().touch_start_or_first_touch_move);
MoveTouchPoint(0, 15, 15);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(WebInputEvent::kTouchMove, sent_event().GetType());
EXPECT_FALSE(sent_event().touch_start_or_first_touch_move);
ReleaseTouchPoint(0);
SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(WebInputEvent::kTouchEnd, sent_event().GetType());
EXPECT_FALSE(sent_event().touch_start_or_first_touch_move);
}
} // namespace content