blob: ed725ee1a10a7cd75a767bb38ac243fc601da61e [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 "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/renderer_host/input/gesture_event_filter.h"
#include "content/browser/renderer_host/input/immediate_input_router.h"
#include "content/browser/renderer_host/input/input_router_client.h"
#include "content/common/content_constants_internal.h"
#include "content/common/edit_command.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/keycodes/keyboard_codes.h"
#if defined(OS_WIN) || defined(USE_AURA)
#include "content/browser/renderer_host/ui_events_helper.h"
#include "ui/base/events/event.h"
#endif
using base::TimeDelta;
using WebKit::WebGestureEvent;
using WebKit::WebInputEvent;
using WebKit::WebMouseEvent;
using WebKit::WebMouseWheelEvent;
using WebKit::WebTouchEvent;
using WebKit::WebTouchPoint;
namespace content {
namespace {
const WebInputEvent* GetInputEventFromMessage(const IPC::Message& message) {
PickleIterator iter(message);
const char* data;
int data_length;
if (!message.ReadData(&iter, &data, &data_length))
return NULL;
return reinterpret_cast<const WebInputEvent*>(data);
}
bool GetIsShortcutFromHandleInputEventMessage(const IPC::Message* msg) {
InputMsg_HandleInputEvent::Schema::Param param;
InputMsg_HandleInputEvent::Read(msg, &param);
return param.c;
}
template<typename MSG_T, typename ARG_T1>
void ExpectIPCMessageWithArg1(const IPC::Message* msg, const ARG_T1& arg1) {
ASSERT_EQ(MSG_T::ID, msg->type());
typename MSG_T::Schema::Param param;
ASSERT_TRUE(MSG_T::Read(msg, &param));
EXPECT_EQ(arg1, param.a);
}
template<typename MSG_T, typename ARG_T1, typename ARG_T2>
void ExpectIPCMessageWithArg2(const IPC::Message* msg,
const ARG_T1& arg1,
const ARG_T2& arg2) {
ASSERT_EQ(MSG_T::ID, msg->type());
typename MSG_T::Schema::Param param;
ASSERT_TRUE(MSG_T::Read(msg, &param));
EXPECT_EQ(arg1, param.a);
EXPECT_EQ(arg2, param.b);
}
#if defined(OS_WIN) || defined(USE_AURA)
bool TouchEventsAreEquivalent(const ui::TouchEvent& first,
const ui::TouchEvent& second) {
if (first.type() != second.type())
return false;
if (first.location() != second.location())
return false;
if (first.touch_id() != second.touch_id())
return false;
if (second.time_stamp().InSeconds() != first.time_stamp().InSeconds())
return false;
return true;
}
bool EventListIsSubset(const ScopedVector<ui::TouchEvent>& subset,
const ScopedVector<ui::TouchEvent>& set) {
if (subset.size() > set.size())
return false;
for (size_t i = 0; i < subset.size(); ++i) {
const ui::TouchEvent* first = subset[i];
const ui::TouchEvent* second = set[i];
bool equivalent = TouchEventsAreEquivalent(*first, *second);
if (!equivalent)
return false;
}
return true;
}
#endif // defined(OS_WIN) || defined(USE_AURA)
} // namespace
class MockInputRouterClient : public InputRouterClient {
public:
MockInputRouterClient()
: input_router_(NULL),
in_flight_event_count_(0),
has_touch_handler_(false),
ack_count_(0),
unexpected_event_ack_called_(false),
ack_state_(INPUT_EVENT_ACK_STATE_UNKNOWN),
filter_state_(INPUT_EVENT_ACK_STATE_NOT_CONSUMED),
is_shortcut_(false),
allow_send_key_event_(true),
send_called_(false),
send_immediately_called_(false) {
}
virtual ~MockInputRouterClient() {
}
// InputRouterClient
virtual InputEventAckState FilterInputEvent(
const WebInputEvent& input_event,
const ui::LatencyInfo& latency_info) OVERRIDE {
return filter_state_;
}
// Called each time a WebInputEvent IPC is sent.
virtual void IncrementInFlightEventCount() OVERRIDE {
++in_flight_event_count_;
}
// Called each time a WebInputEvent ACK IPC is received.
virtual void DecrementInFlightEventCount() OVERRIDE {
--in_flight_event_count_;
}
// Called when the renderer notifies that it has touch event handlers.
virtual void OnHasTouchEventHandlers(bool has_handlers) OVERRIDE {
has_touch_handler_ = has_handlers;
}
virtual bool OnSendKeyboardEvent(
const NativeWebKeyboardEvent& key_event,
const ui::LatencyInfo& latency_info,
bool* is_shortcut) OVERRIDE {
send_called_ = true;
sent_key_event_ = key_event;
*is_shortcut = is_shortcut_;
return allow_send_key_event_;
}
virtual bool OnSendWheelEvent(
const MouseWheelEventWithLatencyInfo& wheel_event) OVERRIDE {
send_called_ = true;
sent_wheel_event_ = wheel_event;
return true;
}
virtual bool OnSendMouseEvent(
const MouseEventWithLatencyInfo& mouse_event) OVERRIDE {
send_called_ = true;
sent_mouse_event_ = mouse_event;
return true;
}
virtual bool OnSendTouchEvent(
const TouchEventWithLatencyInfo& touch_event) OVERRIDE {
send_called_ = true;
sent_touch_event_ = touch_event;
return true;
}
virtual bool OnSendGestureEvent(
const GestureEventWithLatencyInfo& gesture_event) OVERRIDE {
send_called_ = true;
sent_gesture_event_ = gesture_event;
return input_router_->ShouldForwardGestureEvent(gesture_event);
}
virtual bool OnSendMouseEventImmediately(
const MouseEventWithLatencyInfo& mouse_event) OVERRIDE {
send_immediately_called_ = true;
immediately_sent_mouse_event_ = mouse_event;
return true;
}
virtual bool OnSendTouchEventImmediately(
const TouchEventWithLatencyInfo& touch_event) OVERRIDE {
send_immediately_called_ = true;
immediately_sent_touch_event_ = touch_event;
return true;
}
virtual bool OnSendGestureEventImmediately(
const GestureEventWithLatencyInfo& gesture_event) OVERRIDE {
send_immediately_called_ = true;
immediately_sent_gesture_event_ = gesture_event;
return true;
}
// Called upon event ack receipt from the renderer.
virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event,
InputEventAckState ack_result) OVERRIDE {
VLOG(1) << __FUNCTION__ << " called!";
acked_key_event_ = event;
RecordAckCalled(ack_result);
}
virtual void OnWheelEventAck(const WebMouseWheelEvent& event,
InputEventAckState ack_result) OVERRIDE {
VLOG(1) << __FUNCTION__ << " called!";
acked_wheel_event_ = event;
RecordAckCalled(ack_result);
}
virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event,
InputEventAckState ack_result) OVERRIDE {
VLOG(1) << __FUNCTION__ << " called!";
acked_touch_event_ = event;
RecordAckCalled(ack_result);
}
virtual void OnGestureEventAck(const WebGestureEvent& event,
InputEventAckState ack_result) OVERRIDE {
VLOG(1) << __FUNCTION__ << " called!";
RecordAckCalled(ack_result);
}
virtual void OnUnexpectedEventAck(bool bad_message) OVERRIDE {
VLOG(1) << __FUNCTION__ << " called!";
unexpected_event_ack_called_ = true;
}
void ExpectSendCalled(bool called) {
EXPECT_EQ(called, send_called_);
send_called_ = false;
}
void ExpectSendImmediatelyCalled(bool called) {
EXPECT_EQ(called, send_immediately_called_);
send_immediately_called_ = false;
}
void ExpectAckCalled(int times) {
EXPECT_EQ(times, ack_count_);
ack_count_ = 0;
}
void set_input_router(InputRouter* input_router) {
input_router_ = input_router;
}
bool has_touch_handler() const { return has_touch_handler_; }
InputEventAckState ack_state() const { return ack_state_; }
void set_filter_state(InputEventAckState filter_state) {
filter_state_ = filter_state;
}
bool unexpected_event_ack_called() const {
return unexpected_event_ack_called_;
}
const NativeWebKeyboardEvent& acked_keyboard_event() {
return acked_key_event_;
}
const WebMouseWheelEvent& acked_wheel_event() {
return acked_wheel_event_;
}
const TouchEventWithLatencyInfo& acked_touch_event() {
return acked_touch_event_;
}
void set_is_shortcut(bool is_shortcut) {
is_shortcut_ = is_shortcut;
}
void set_allow_send_key_event(bool allow) {
allow_send_key_event_ = allow;
}
const NativeWebKeyboardEvent& sent_key_event() {
return sent_key_event_;
}
const MouseWheelEventWithLatencyInfo& sent_wheel_event() {
return sent_wheel_event_;
}
const MouseEventWithLatencyInfo& sent_mouse_event() {
return sent_mouse_event_;
}
const GestureEventWithLatencyInfo& sent_gesture_event() {
return sent_gesture_event_;
}
const MouseEventWithLatencyInfo& immediately_sent_mouse_event() {
return immediately_sent_mouse_event_;
}
const TouchEventWithLatencyInfo& immediately_sent_touch_event() {
return immediately_sent_touch_event_;
}
const GestureEventWithLatencyInfo& immediately_sent_gesture_event() {
return immediately_sent_gesture_event_;
}
private:
void RecordAckCalled(InputEventAckState ack_result) {
++ack_count_;
ack_state_ = ack_result;
}
InputRouter* input_router_;
int in_flight_event_count_;
bool has_touch_handler_;
int ack_count_;
bool unexpected_event_ack_called_;
InputEventAckState ack_state_;
InputEventAckState filter_state_;
NativeWebKeyboardEvent acked_key_event_;
WebMouseWheelEvent acked_wheel_event_;
TouchEventWithLatencyInfo acked_touch_event_;
bool is_shortcut_;
bool allow_send_key_event_;
bool send_called_;
NativeWebKeyboardEvent sent_key_event_;
MouseWheelEventWithLatencyInfo sent_wheel_event_;
MouseEventWithLatencyInfo sent_mouse_event_;
TouchEventWithLatencyInfo sent_touch_event_;
GestureEventWithLatencyInfo sent_gesture_event_;
bool send_immediately_called_;
MouseEventWithLatencyInfo immediately_sent_mouse_event_;
TouchEventWithLatencyInfo immediately_sent_touch_event_;
GestureEventWithLatencyInfo immediately_sent_gesture_event_;
};
class ImmediateInputRouterTest : public testing::Test {
public:
ImmediateInputRouterTest() {
}
virtual ~ImmediateInputRouterTest() {
}
protected:
// testing::Test
virtual void SetUp() {
browser_context_.reset(new TestBrowserContext());
process_.reset(new MockRenderProcessHost(browser_context_.get()));
client_.reset(new MockInputRouterClient());
input_router_.reset(new ImmediateInputRouter(
process_.get(),
client_.get(),
MSG_ROUTING_NONE));
client_->set_input_router(input_router_.get());
}
virtual void TearDown() {
// Process all pending tasks to avoid leaks.
base::MessageLoop::current()->RunUntilIdle();
input_router_.reset();
client_.reset();
process_.reset();
browser_context_.reset();
}
void SendInputEventACK(WebInputEvent::Type type,
InputEventAckState ack_result) {
scoped_ptr<IPC::Message> response(
new InputHostMsg_HandleInputEvent_ACK(0, type, ack_result,
ui::LatencyInfo()));
input_router_->OnMessageReceived(*response);
}
void SimulateKeyboardEvent(WebInputEvent::Type type) {
NativeWebKeyboardEvent key_event;
key_event.type = type;
key_event.windowsKeyCode = ui::VKEY_L; // non-null made up value.
input_router_->SendKeyboardEvent(key_event, ui::LatencyInfo());
client_->ExpectSendCalled(true);
EXPECT_EQ(type, client_->sent_key_event().type);
EXPECT_EQ(key_event.windowsKeyCode,
client_->sent_key_event().windowsKeyCode);
}
void SimulateWheelEvent(float dX, float dY, int modifiers, bool precise) {
WebMouseWheelEvent wheel_event;
wheel_event.type = WebInputEvent::MouseWheel;
wheel_event.deltaX = dX;
wheel_event.deltaY = dY;
wheel_event.modifiers = modifiers;
wheel_event.hasPreciseScrollingDeltas = precise;
input_router_->SendWheelEvent(
MouseWheelEventWithLatencyInfo(wheel_event, ui::LatencyInfo()));
client_->ExpectSendCalled(true);
EXPECT_EQ(wheel_event.type, client_->sent_wheel_event().event.type);
EXPECT_EQ(dX, client_->sent_wheel_event().event.deltaX);
}
void SimulateMouseMove(int x, int y, int modifiers) {
WebMouseEvent mouse_event;
mouse_event.type = WebInputEvent::MouseMove;
mouse_event.x = mouse_event.windowX = x;
mouse_event.y = mouse_event.windowY = y;
mouse_event.modifiers = modifiers;
input_router_->SendMouseEvent(
MouseEventWithLatencyInfo(mouse_event, ui::LatencyInfo()));
client_->ExpectSendCalled(true);
client_->ExpectSendImmediatelyCalled(true);
EXPECT_EQ(mouse_event.type, client_->sent_mouse_event().event.type);
EXPECT_EQ(x, client_->sent_mouse_event().event.x);
EXPECT_EQ(mouse_event.type,
client_->immediately_sent_mouse_event().event.type);
EXPECT_EQ(x, client_->immediately_sent_mouse_event().event.x);
}
void SimulateWheelEventWithPhase(WebMouseWheelEvent::Phase phase) {
WebMouseWheelEvent wheel_event;
wheel_event.type = WebInputEvent::MouseWheel;
wheel_event.phase = phase;
input_router_->SendWheelEvent(
MouseWheelEventWithLatencyInfo(wheel_event, ui::LatencyInfo()));
client_->ExpectSendCalled(true);
EXPECT_EQ(wheel_event.type, client_->sent_wheel_event().event.type);
EXPECT_EQ(phase, client_->sent_wheel_event().event.phase);
}
// Inject provided synthetic WebGestureEvent instance.
void SimulateGestureEventCore(WebInputEvent::Type type,
WebGestureEvent::SourceDevice sourceDevice,
WebGestureEvent* gesture_event) {
gesture_event->type = type;
gesture_event->sourceDevice = sourceDevice;
GestureEventWithLatencyInfo gesture_with_latency(
*gesture_event, ui::LatencyInfo());
input_router_->SendGestureEvent(gesture_with_latency);
client_->ExpectSendCalled(true);
EXPECT_EQ(type, client_->sent_gesture_event().event.type);
EXPECT_EQ(sourceDevice, client_->sent_gesture_event().event.sourceDevice);
}
// Inject simple synthetic WebGestureEvent instances.
void SimulateGestureEvent(WebInputEvent::Type type,
WebGestureEvent::SourceDevice sourceDevice) {
WebGestureEvent gesture_event;
SimulateGestureEventCore(type, sourceDevice, &gesture_event);
}
void SimulateGestureScrollUpdateEvent(float dX, float dY, int modifiers) {
WebGestureEvent gesture_event;
gesture_event.data.scrollUpdate.deltaX = dX;
gesture_event.data.scrollUpdate.deltaY = dY;
gesture_event.modifiers = modifiers;
SimulateGestureEventCore(WebInputEvent::GestureScrollUpdate,
WebGestureEvent::Touchscreen, &gesture_event);
}
void SimulateGesturePinchUpdateEvent(float scale,
float anchorX,
float anchorY,
int modifiers) {
WebGestureEvent gesture_event;
gesture_event.data.pinchUpdate.scale = scale;
gesture_event.x = anchorX;
gesture_event.y = anchorY;
gesture_event.modifiers = modifiers;
SimulateGestureEventCore(WebInputEvent::GesturePinchUpdate,
WebGestureEvent::Touchscreen, &gesture_event);
}
// Inject synthetic GestureFlingStart events.
void SimulateGestureFlingStartEvent(
float velocityX,
float velocityY,
WebGestureEvent::SourceDevice sourceDevice) {
WebGestureEvent gesture_event;
gesture_event.data.flingStart.velocityX = velocityX;
gesture_event.data.flingStart.velocityY = velocityY;
SimulateGestureEventCore(WebInputEvent::GestureFlingStart, sourceDevice,
&gesture_event);
}
// Set the timestamp for the touch-event.
void SetTouchTimestamp(base::TimeDelta timestamp) {
touch_event_.timeStampSeconds = timestamp.InSecondsF();
}
// Sends a touch event (irrespective of whether the page has a touch-event
// handler or not).
void SendTouchEvent() {
input_router_->SendTouchEvent(
TouchEventWithLatencyInfo(touch_event_, ui::LatencyInfo()));
// Mark all the points as stationary. And remove the points that have been
// released.
int point = 0;
for (unsigned int i = 0; i < touch_event_.touchesLength; ++i) {
if (touch_event_.touches[i].state == WebTouchPoint::StateReleased)
continue;
touch_event_.touches[point] = touch_event_.touches[i];
touch_event_.touches[point].state =
WebTouchPoint::StateStationary;
++point;
}
touch_event_.touchesLength = point;
touch_event_.type = WebInputEvent::Undefined;
}
int PressTouchPoint(int x, int y) {
if (touch_event_.touchesLength == touch_event_.touchesLengthCap)
return -1;
WebTouchPoint& point =
touch_event_.touches[touch_event_.touchesLength];
point.id = touch_event_.touchesLength;
point.position.x = point.screenPosition.x = x;
point.position.y = point.screenPosition.y = y;
point.state = WebTouchPoint::StatePressed;
point.radiusX = point.radiusY = 1.f;
++touch_event_.touchesLength;
touch_event_.type = WebInputEvent::TouchStart;
return point.id;
}
void MoveTouchPoint(int index, int x, int y) {
CHECK(index >= 0 && index < touch_event_.touchesLengthCap);
WebTouchPoint& point = touch_event_.touches[index];
point.position.x = point.screenPosition.x = x;
point.position.y = point.screenPosition.y = y;
touch_event_.touches[index].state = WebTouchPoint::StateMoved;
touch_event_.type = WebInputEvent::TouchMove;
}
void ReleaseTouchPoint(int index) {
CHECK(index >= 0 && index < touch_event_.touchesLengthCap);
touch_event_.touches[index].state = WebTouchPoint::StateReleased;
touch_event_.type = WebInputEvent::TouchEnd;
}
void set_debounce_interval_time_ms(int ms) {
input_router_->gesture_event_filter()->debounce_interval_time_ms_ = ms;
}
void set_maximum_tap_gap_time_ms(int delay_ms) {
input_router_->gesture_event_filter()->maximum_tap_gap_time_ms_ = delay_ms;
}
size_t TouchEventQueueSize() {
return touch_event_queue()->GetQueueSize();
}
const WebTouchEvent& latest_event() const {
return touch_event_queue()->GetLatestEvent().event;
}
TouchEventQueue* touch_event_queue() const {
return input_router_->touch_event_queue();
}
unsigned GestureEventLastQueueEventSize() {
return gesture_event_filter()->coalesced_gesture_events_.size();
}
WebGestureEvent GestureEventSecondFromLastQueueEvent() {
return gesture_event_filter()->coalesced_gesture_events_.at(
GestureEventLastQueueEventSize() - 2).event;
}
WebGestureEvent GestureEventLastQueueEvent() {
return gesture_event_filter()->coalesced_gesture_events_.back().event;
}
unsigned GestureEventDebouncingQueueSize() {
return gesture_event_filter()->debouncing_deferral_queue_.size();
}
WebGestureEvent GestureEventQueueEventAt(int i) {
return gesture_event_filter()->coalesced_gesture_events_.at(i).event;
}
bool shouldDeferTapDownEvents() {
return gesture_event_filter()->maximum_tap_gap_time_ms_ != 0;
}
bool ScrollingInProgress() {
return gesture_event_filter()->scrolling_in_progress_;
}
bool FlingInProgress() {
return gesture_event_filter()->fling_in_progress_;
}
bool WillIgnoreNextACK() {
return gesture_event_filter()->ignore_next_ack_;
}
GestureEventFilter* gesture_event_filter() const {
return input_router_->gesture_event_filter();
}
scoped_ptr<MockRenderProcessHost> process_;
scoped_ptr<MockInputRouterClient> client_;
scoped_ptr<ImmediateInputRouter> input_router_;
private:
base::MessageLoopForUI message_loop_;
WebTouchEvent touch_event_;
scoped_ptr<TestBrowserContext> browser_context_;
};
#if GTEST_HAS_PARAM_TEST
// This is for tests that are to be run for all source devices.
class ImmediateInputRouterWithSourceTest
: public ImmediateInputRouterTest,
public testing::WithParamInterface<WebGestureEvent::SourceDevice> {
};
#endif // GTEST_HAS_PARAM_TEST
TEST_F(ImmediateInputRouterTest, CoalescesRangeSelection) {
input_router_->SendInput(
new InputMsg_SelectRange(0, gfx::Point(1, 2), gfx::Point(3, 4)));
EXPECT_EQ(1u, process_->sink().message_count());
ExpectIPCMessageWithArg2<InputMsg_SelectRange>(
process_->sink().GetMessageAt(0),
gfx::Point(1, 2),
gfx::Point(3, 4));
process_->sink().ClearMessages();
// Send two more messages without acking.
input_router_->SendInput(
new InputMsg_SelectRange(0, gfx::Point(5, 6), gfx::Point(7, 8)));
EXPECT_EQ(0u, process_->sink().message_count());
input_router_->SendInput(
new InputMsg_SelectRange(0, gfx::Point(9, 10), gfx::Point(11, 12)));
EXPECT_EQ(0u, process_->sink().message_count());
// Now ack the first message.
{
scoped_ptr<IPC::Message> response(new ViewHostMsg_SelectRange_ACK(0));
input_router_->OnMessageReceived(*response);
}
// Verify that the two messages are coalesced into one message.
EXPECT_EQ(1u, process_->sink().message_count());
ExpectIPCMessageWithArg2<InputMsg_SelectRange>(
process_->sink().GetMessageAt(0),
gfx::Point(9, 10),
gfx::Point(11, 12));
process_->sink().ClearMessages();
// Acking the coalesced msg should not send any more msg.
{
scoped_ptr<IPC::Message> response(new ViewHostMsg_SelectRange_ACK(0));
input_router_->OnMessageReceived(*response);
}
EXPECT_EQ(0u, process_->sink().message_count());
}
TEST_F(ImmediateInputRouterTest, CoalescesCaretMove) {
input_router_->SendInput(new InputMsg_MoveCaret(0, gfx::Point(1, 2)));
EXPECT_EQ(1u, process_->sink().message_count());
ExpectIPCMessageWithArg1<InputMsg_MoveCaret>(
process_->sink().GetMessageAt(0), gfx::Point(1, 2));
process_->sink().ClearMessages();
// Send two more messages without acking.
input_router_->SendInput(new InputMsg_MoveCaret(0, gfx::Point(5, 6)));
EXPECT_EQ(0u, process_->sink().message_count());
input_router_->SendInput(new InputMsg_MoveCaret(0, gfx::Point(9, 10)));
EXPECT_EQ(0u, process_->sink().message_count());
// Now ack the first message.
{
scoped_ptr<IPC::Message> response(new ViewHostMsg_MoveCaret_ACK(0));
input_router_->OnMessageReceived(*response);
}
// Verify that the two messages are coalesced into one message.
EXPECT_EQ(1u, process_->sink().message_count());
ExpectIPCMessageWithArg1<InputMsg_MoveCaret>(
process_->sink().GetMessageAt(0), gfx::Point(9, 10));
process_->sink().ClearMessages();
// Acking the coalesced msg should not send any more msg.
{
scoped_ptr<IPC::Message> response(new ViewHostMsg_MoveCaret_ACK(0));
input_router_->OnMessageReceived(*response);
}
EXPECT_EQ(0u, process_->sink().message_count());
}
TEST_F(ImmediateInputRouterTest, HandledInputEvent) {
client_->set_filter_state(INPUT_EVENT_ACK_STATE_CONSUMED);
// Simulate a keyboard event.
SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
// Make sure no input event is sent to the renderer.
EXPECT_EQ(0u, process_->sink().message_count());
// OnKeyboardEventAck should be triggered without actual ack.
client_->ExpectAckCalled(1);
// As the event was acked already, keyboard event queue should be
// empty.
ASSERT_EQ(NULL, input_router_->GetLastKeyboardEvent());
}
TEST_F(ImmediateInputRouterTest, ClientCanceledKeyboardEvent) {
client_->set_allow_send_key_event(false);
// Simulate a keyboard event.
SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
// Make sure no input event is sent to the renderer.
EXPECT_EQ(0u, process_->sink().message_count());
client_->ExpectAckCalled(0);
}
TEST_F(ImmediateInputRouterTest, ShortcutKeyboardEvent) {
client_->set_is_shortcut(true);
SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
EXPECT_TRUE(GetIsShortcutFromHandleInputEventMessage(
process_->sink().GetMessageAt(0)));
process_->sink().ClearMessages();
client_->set_is_shortcut(false);
SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
EXPECT_FALSE(GetIsShortcutFromHandleInputEventMessage(
process_->sink().GetMessageAt(0)));
}
TEST_F(ImmediateInputRouterTest, NoncorrespondingKeyEvents) {
SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
SendInputEventACK(WebInputEvent::KeyUp,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_TRUE(client_->unexpected_event_ack_called());
}
// Tests ported from RenderWidgetHostTest --------------------------------------
TEST_F(ImmediateInputRouterTest, HandleKeyEventsWeSent) {
// Simulate a keyboard event.
SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
ASSERT_TRUE(input_router_->GetLastKeyboardEvent());
EXPECT_EQ(WebInputEvent::RawKeyDown,
input_router_->GetLastKeyboardEvent()->type);
// Make sure we sent the input event to the renderer.
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
process_->sink().ClearMessages();
// Send the simulated response from the renderer back.
SendInputEventACK(WebInputEvent::RawKeyDown,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
client_->ExpectAckCalled(1);
EXPECT_EQ(WebInputEvent::RawKeyDown, client_->acked_keyboard_event().type);
}
TEST_F(ImmediateInputRouterTest, IgnoreKeyEventsWeDidntSend) {
// Send a simulated, unrequested key response. We should ignore this.
SendInputEventACK(WebInputEvent::RawKeyDown,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
client_->ExpectAckCalled(0);
}
TEST_F(ImmediateInputRouterTest, CoalescesWheelEvents) {
// Simulate wheel events.
SimulateWheelEvent(0, -5, 0, false); // sent directly
SimulateWheelEvent(0, -10, 0, false); // enqueued
SimulateWheelEvent(8, -6, 0, false); // coalesced into previous event
SimulateWheelEvent(9, -7, 1, false); // enqueued, different modifiers
// Check that only the first event was sent.
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
process_->sink().ClearMessages();
// Check that the ACK sends the second message via ImmediateInputForwarder
SendInputEventACK(WebInputEvent::MouseWheel,
INPUT_EVENT_ACK_STATE_CONSUMED);
// The coalesced events can queue up a delayed ack
// so that additional input events can be processed before
// we turn off coalescing.
base::MessageLoop::current()->RunUntilIdle();
client_->ExpectAckCalled(1);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
process_->sink().ClearMessages();
// One more time.
SendInputEventACK(WebInputEvent::MouseWheel,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
client_->ExpectAckCalled(1);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
process_->sink().ClearMessages();
// After the final ack, the queue should be empty.
SendInputEventACK(WebInputEvent::MouseWheel,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
client_->ExpectAckCalled(1);
EXPECT_EQ(0U, process_->sink().message_count());
// FIXME(kouhei): Below is testing gesture event filter. Maybe separate test?
{
WebGestureEvent gesture_event;
gesture_event.type = WebInputEvent::GestureFlingStart;
gesture_event.sourceDevice = WebGestureEvent::Touchpad;
gesture_event.data.flingStart.velocityX = 0.f;
gesture_event.data.flingStart.velocityY = 0.f;
EXPECT_FALSE(input_router_->ShouldForwardGestureEvent(
GestureEventWithLatencyInfo(gesture_event, ui::LatencyInfo())));
}
}
TEST_F(ImmediateInputRouterTest,
CoalescesWheelEventsQueuedPhaseEndIsNotDropped) {
// Send an initial gesture begin and ACK it.
SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
WebGestureEvent::Touchpad);
EXPECT_EQ(1U, process_->sink().message_count());
SendInputEventACK(WebInputEvent::GestureScrollBegin,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
// Send a wheel event, should get sent directly.
SimulateWheelEvent(0, -5, 0, false);
EXPECT_EQ(2U, process_->sink().message_count());
// Send a wheel phase end event before an ACK is received for the previous
// wheel event, which should get queued.
SimulateWheelEventWithPhase(WebMouseWheelEvent::PhaseEnded);
EXPECT_EQ(2U, process_->sink().message_count());
// A gesture event should now result in the queued phase ended event being
// transmitted before it.
SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
WebGestureEvent::Touchpad);
ASSERT_EQ(4U, process_->sink().message_count());
// Verify the events that were sent.
const WebInputEvent* input_event =
GetInputEventFromMessage(*process_->sink().GetMessageAt(2));
ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type);
const WebMouseWheelEvent* wheel_event =
static_cast<const WebMouseWheelEvent*>(input_event);
ASSERT_EQ(WebMouseWheelEvent::PhaseEnded, wheel_event->phase);
input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(3));
EXPECT_EQ(WebInputEvent::GestureScrollEnd, input_event->type);
}
TEST_F(ImmediateInputRouterTest, CoalescesScrollGestureEvents) {
// Turn off debounce handling for test isolation.
set_debounce_interval_time_ms(0);
// Test coalescing of only GestureScrollUpdate events.
// Simulate gesture events.
// Sent.
SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
WebGestureEvent::Touchscreen);
// Enqueued.
SimulateGestureScrollUpdateEvent(8, -5, 0);
// Make sure that the queue contains what we think it should.
WebGestureEvent merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(2U, GestureEventLastQueueEventSize());
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
// Coalesced.
SimulateGestureScrollUpdateEvent(8, -6, 0);
// Check that coalescing updated the correct values.
merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(0, merged_event.modifiers);
EXPECT_EQ(16, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-11, merged_event.data.scrollUpdate.deltaY);
// Enqueued.
SimulateGestureScrollUpdateEvent(8, -7, 1);
// Check that we didn't wrongly coalesce.
merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(1, merged_event.modifiers);
// Different.
SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
WebGestureEvent::Touchscreen);
// Check that only the first event was sent.
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
process_->sink().ClearMessages();
// Check that the ACK sends the second message.
SendInputEventACK(WebInputEvent::GestureScrollBegin,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
client_->ExpectAckCalled(1);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
process_->sink().ClearMessages();
// Ack for queued coalesced event.
SendInputEventACK(WebInputEvent::GestureScrollUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
client_->ExpectAckCalled(1);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
process_->sink().ClearMessages();
// Ack for queued uncoalesced event.
SendInputEventACK(WebInputEvent::GestureScrollUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
client_->ExpectAckCalled(1);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
process_->sink().ClearMessages();
// After the final ack, the queue should be empty.
SendInputEventACK(WebInputEvent::GestureScrollEnd,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
client_->ExpectAckCalled(1);
EXPECT_EQ(0U, process_->sink().message_count());
}
TEST_F(ImmediateInputRouterTest, CoalescesScrollAndPinchEvents) {
// Turn off debounce handling for test isolation.
set_debounce_interval_time_ms(0);
// Test coalescing of only GestureScrollUpdate events.
// Simulate gesture events.
// Sent.
SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
WebGestureEvent::Touchscreen);
// Sent.
SimulateGestureEvent(WebInputEvent::GesturePinchBegin,
WebGestureEvent::Touchscreen);
// Enqueued.
SimulateGestureScrollUpdateEvent(8, -4, 1);
// Make sure that the queue contains what we think it should.
WebGestureEvent merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(3U, GestureEventLastQueueEventSize());
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
// Coalesced without changing event order. Note anchor at (60, 60). Anchoring
// from a poinht that is not the origin should still give us the wight scroll.
SimulateGesturePinchUpdateEvent(1.5, 60, 60, 1);
EXPECT_EQ(4U, GestureEventLastQueueEventSize());
merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(1.5, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(8, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-4, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
// Enqueued.
SimulateGestureScrollUpdateEvent(6, -3, 1);
// Check whether coalesced correctly.
EXPECT_EQ(4U, GestureEventLastQueueEventSize());
merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(1.5, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(12, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-6, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
// Enqueued.
SimulateGesturePinchUpdateEvent(2, 60, 60, 1);
// Check whether coalesced correctly.
EXPECT_EQ(4U, GestureEventLastQueueEventSize());
merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(3, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(12, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-6, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
// Enqueued.
SimulateGesturePinchUpdateEvent(2, 60, 60, 1);
// Check whether coalesced correctly.
EXPECT_EQ(4U, GestureEventLastQueueEventSize());
merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(6, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(12, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-6, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
// Check that only the first event was sent.
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
process_->sink().ClearMessages();
// Check that the ACK sends the second message.
SendInputEventACK(WebInputEvent::GestureScrollBegin,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
process_->sink().ClearMessages();
// Enqueued.
SimulateGestureScrollUpdateEvent(6, -6, 1);
// Check whether coalesced correctly.
EXPECT_EQ(3U, GestureEventLastQueueEventSize());
merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(6, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(13, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-7, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
// At this point ACKs shouldn't be getting ignored.
EXPECT_FALSE(WillIgnoreNextACK());
// Check that the ACK sends both scroll and pinch updates.
SendInputEventACK(WebInputEvent::GesturePinchBegin,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
EXPECT_EQ(2U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetFirstMessageMatching(
InputMsg_HandleInputEvent::ID));
EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
process_->sink().ClearMessages();
// The next ACK should be getting ignored.
EXPECT_TRUE(WillIgnoreNextACK());
// Enqueued.
SimulateGestureScrollUpdateEvent(1, -1, 1);
// Check whether coalesced correctly.
EXPECT_EQ(3U, GestureEventLastQueueEventSize());
merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(1, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-1, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(6, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
// Enqueued.
SimulateGestureScrollUpdateEvent(2, -2, 1);
// Coalescing scrolls should still work.
EXPECT_EQ(3U, GestureEventLastQueueEventSize());
merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(3, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-3, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(6, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
// Enqueued.
SimulateGesturePinchUpdateEvent(0.5, 60, 60, 1);
// Check whether coalesced correctly.
EXPECT_EQ(4U, GestureEventLastQueueEventSize());
merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(0.5, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(3, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-3, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(1, merged_event.modifiers);
// Check that the ACK gets ignored.
SendInputEventACK(WebInputEvent::GestureScrollUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
EXPECT_EQ(0U, process_->sink().message_count());
// The flag should have been flipped back to false.
EXPECT_FALSE(WillIgnoreNextACK());
// Enqueued.
SimulateGestureScrollUpdateEvent(2, -2, 2);
// Shouldn't coalesce with different modifiers.
EXPECT_EQ(4U, GestureEventLastQueueEventSize());
merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type);
EXPECT_EQ(2, merged_event.data.scrollUpdate.deltaX);
EXPECT_EQ(-2, merged_event.data.scrollUpdate.deltaY);
EXPECT_EQ(2, merged_event.modifiers);
merged_event = GestureEventSecondFromLastQueueEvent();
EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type);
EXPECT_EQ(0.5, merged_event.data.pinchUpdate.scale);
EXPECT_EQ(1, merged_event.modifiers);
// Check that the ACK sends the next scroll pinch pair.
SendInputEventACK(WebInputEvent::GesturePinchUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
EXPECT_EQ(2U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetFirstMessageMatching(
InputMsg_HandleInputEvent::ID));
EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
process_->sink().ClearMessages();
// Check that the ACK sends the second message.
SendInputEventACK(WebInputEvent::GestureScrollUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
EXPECT_EQ(0U, process_->sink().message_count());
// Check that the ACK sends the second message.
SendInputEventACK(WebInputEvent::GesturePinchUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
process_->sink().ClearMessages();
// Check that the queue is empty after ACK and no messages get sent.
SendInputEventACK(WebInputEvent::GestureScrollUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, GestureEventLastQueueEventSize());
}
#if GTEST_HAS_PARAM_TEST
TEST_P(ImmediateInputRouterWithSourceTest, GestureFlingCancelsFiltered) {
WebGestureEvent::SourceDevice source_device = GetParam();
// Turn off debounce handling for test isolation.
set_debounce_interval_time_ms(0);
// GFC without previous GFS is dropped.
SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, GestureEventLastQueueEventSize());
// GFC after previous GFS is dispatched and acked.
process_->sink().ClearMessages();
SimulateGestureFlingStartEvent(0, -10, source_device);
EXPECT_TRUE(FlingInProgress());
SendInputEventACK(WebInputEvent::GestureFlingStart,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
client_->ExpectAckCalled(1);
SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
EXPECT_FALSE(FlingInProgress());
EXPECT_EQ(2U, process_->sink().message_count());
SendInputEventACK(WebInputEvent::GestureFlingCancel,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
client_->ExpectAckCalled(1);
EXPECT_EQ(0U, GestureEventLastQueueEventSize());
// GFC before previous GFS is acked.
process_->sink().ClearMessages();
SimulateGestureFlingStartEvent(0, -10, source_device);
EXPECT_TRUE(FlingInProgress());
SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
EXPECT_FALSE(FlingInProgress());
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(2U, GestureEventLastQueueEventSize());
// Advance state realistically.
SendInputEventACK(WebInputEvent::GestureFlingStart,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
SendInputEventACK(WebInputEvent::GestureFlingCancel,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
client_->ExpectAckCalled(2);
EXPECT_EQ(0U, GestureEventLastQueueEventSize());
// GFS is added to the queue if another event is pending
process_->sink().ClearMessages();
SimulateGestureScrollUpdateEvent(8, -7, 0);
SimulateGestureFlingStartEvent(0, -10, source_device);
EXPECT_EQ(2U, GestureEventLastQueueEventSize());
EXPECT_EQ(1U, process_->sink().message_count());
WebGestureEvent merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureFlingStart, merged_event.type);
EXPECT_TRUE(FlingInProgress());
EXPECT_EQ(2U, GestureEventLastQueueEventSize());
// GFS in queue means that a GFC is added to the queue
SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
merged_event =GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureFlingCancel, merged_event.type);
EXPECT_FALSE(FlingInProgress());
EXPECT_EQ(3U, GestureEventLastQueueEventSize());
// Adding a second GFC is dropped.
SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
EXPECT_FALSE(FlingInProgress());
EXPECT_EQ(3U, GestureEventLastQueueEventSize());
// Adding another GFS will add it to the queue.
SimulateGestureFlingStartEvent(0, -10, source_device);
merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureFlingStart, merged_event.type);
EXPECT_TRUE(FlingInProgress());
EXPECT_EQ(4U, GestureEventLastQueueEventSize());
// GFS in queue means that a GFC is added to the queue
SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureFlingCancel, merged_event.type);
EXPECT_FALSE(FlingInProgress());
EXPECT_EQ(5U, GestureEventLastQueueEventSize());
// Adding another GFC with a GFC already there is dropped.
SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
merged_event = GestureEventLastQueueEvent();
EXPECT_EQ(WebInputEvent::GestureFlingCancel, merged_event.type);
EXPECT_FALSE(FlingInProgress());
EXPECT_EQ(5U, GestureEventLastQueueEventSize());
}
INSTANTIATE_TEST_CASE_P(AllSources,
ImmediateInputRouterWithSourceTest,
testing::Values(WebGestureEvent::Touchscreen,
WebGestureEvent::Touchpad));
#endif // GTEST_HAS_PARAM_TEST
// Test that GestureTapDown events are deferred.
TEST_F(ImmediateInputRouterTest, DeferredGestureTapDown) {
// Set some sort of short deferral timeout
set_maximum_tap_gap_time_ms(5);
SimulateGestureEvent(WebInputEvent::GestureTapDown,
WebGestureEvent::Touchscreen);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, GestureEventLastQueueEventSize());
// Wait long enough for first timeout and see if it fired.
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::MessageLoop::QuitClosure(),
TimeDelta::FromMilliseconds(10));
base::MessageLoop::current()->Run();
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, GestureEventLastQueueEventSize());
EXPECT_EQ(WebInputEvent::GestureTapDown,
GestureEventLastQueueEvent().type);
}
// Test that GestureTapDown events are sent immediately on GestureTap.
TEST_F(ImmediateInputRouterTest, DeferredGestureTapDownSentOnTap) {
// Set some sort of short deferral timeout
set_maximum_tap_gap_time_ms(5);
SimulateGestureEvent(WebInputEvent::GestureTapDown,
WebGestureEvent::Touchscreen);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, GestureEventLastQueueEventSize());
SimulateGestureEvent(WebInputEvent::GestureTap,
WebGestureEvent::Touchscreen);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(2U, GestureEventLastQueueEventSize());
EXPECT_EQ(WebInputEvent::GestureTap,
GestureEventLastQueueEvent().type);
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::MessageLoop::QuitClosure(),
TimeDelta::FromMilliseconds(10));
base::MessageLoop::current()->Run();
EXPECT_EQ(WebInputEvent::GestureTapDown,
client_->immediately_sent_gesture_event().event.type);
// If the deferral timer incorrectly fired, it sent an extra message.
EXPECT_EQ(1U, process_->sink().message_count());
}
// Test that only a single GestureTapDown event is sent when tap occurs after
// the timeout.
TEST_F(ImmediateInputRouterTest, DeferredGestureTapDownOnlyOnce) {
// Set some sort of short deferral timeout
set_maximum_tap_gap_time_ms(5);
SimulateGestureEvent(WebInputEvent::GestureTapDown,
WebGestureEvent::Touchscreen);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, GestureEventLastQueueEventSize());
// Wait long enough for the timeout and verify it fired.
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::MessageLoop::QuitClosure(),
TimeDelta::FromMilliseconds(10));
base::MessageLoop::current()->Run();
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, GestureEventLastQueueEventSize());
EXPECT_EQ(WebInputEvent::GestureTapDown,
GestureEventLastQueueEvent().type);
// Now send the tap gesture and verify we didn't get an extra TapDown.
SimulateGestureEvent(WebInputEvent::GestureTap,
WebGestureEvent::Touchscreen);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(2U, GestureEventLastQueueEventSize());
EXPECT_EQ(WebInputEvent::GestureTap,
GestureEventLastQueueEvent().type);
}
// Test that scroll events during the deferral interval drop the GestureTapDown.
TEST_F(ImmediateInputRouterTest, DeferredGestureTapDownAnulledOnScroll) {
// Set some sort of short deferral timeout
set_maximum_tap_gap_time_ms(5);
SimulateGestureEvent(WebInputEvent::GestureTapDown,
WebGestureEvent::Touchscreen);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, GestureEventLastQueueEventSize());
SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
WebGestureEvent::Touchscreen);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, GestureEventLastQueueEventSize());
EXPECT_EQ(WebInputEvent::GestureScrollBegin,
GestureEventLastQueueEvent().type);
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::MessageLoop::QuitClosure(),
TimeDelta::FromMilliseconds(10));
base::MessageLoop::current()->Run();
// If the deferral timer incorrectly fired, it will send an extra message.
EXPECT_EQ(1U, process_->sink().message_count());
}
// Test that a tap cancel event during the deferral interval drops the
// GestureTapDown.
TEST_F(ImmediateInputRouterTest, DeferredGestureTapDownAnulledOnTapCancel) {
// Set some sort of short deferral timeout
set_maximum_tap_gap_time_ms(5);
SimulateGestureEvent(WebInputEvent::GestureTapDown,
WebGestureEvent::Touchscreen);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, GestureEventLastQueueEventSize());
SimulateGestureEvent(WebInputEvent::GestureTapCancel,
WebGestureEvent::Touchscreen);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, GestureEventLastQueueEventSize());
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::MessageLoop::QuitClosure(),
TimeDelta::FromMilliseconds(10));
base::MessageLoop::current()->Run();
// If the deferral timer incorrectly fired, it will send an extra message.
EXPECT_EQ(0U, process_->sink().message_count());
}
// Test that if a GestureTapDown gets sent, any corresponding GestureTapCancel
// is also sent.
TEST_F(ImmediateInputRouterTest, DeferredGestureTapDownTapCancel) {
// Set some sort of short deferral timeout
set_maximum_tap_gap_time_ms(5);
SimulateGestureEvent(WebInputEvent::GestureTapDown,
WebGestureEvent::Touchscreen);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, GestureEventLastQueueEventSize());
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::MessageLoop::QuitClosure(),
TimeDelta::FromMilliseconds(10));
base::MessageLoop::current()->Run();
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, GestureEventLastQueueEventSize());
SimulateGestureEvent(WebInputEvent::GestureTapCancel,
WebGestureEvent::Touchscreen);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(2U, GestureEventLastQueueEventSize());
}
// Test that a GestureScrollEnd | GestureFlingStart are deferred during the
// debounce interval, that Scrolls are not and that the deferred events are
// sent after that timer fires.
TEST_F(ImmediateInputRouterTest, DebounceDefersFollowingGestureEvents) {
set_debounce_interval_time_ms(3);
SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
WebGestureEvent::Touchscreen);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, GestureEventLastQueueEventSize());
EXPECT_EQ(0U, GestureEventDebouncingQueueSize());
EXPECT_TRUE(ScrollingInProgress());
SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
WebGestureEvent::Touchscreen);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(2U, GestureEventLastQueueEventSize());
EXPECT_EQ(0U, GestureEventDebouncingQueueSize());
EXPECT_TRUE(ScrollingInProgress());
SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
WebGestureEvent::Touchscreen);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(2U, GestureEventLastQueueEventSize());
EXPECT_EQ(1U, GestureEventDebouncingQueueSize());
SimulateGestureFlingStartEvent(0, 10, WebGestureEvent::Touchscreen);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(2U, GestureEventLastQueueEventSize());
EXPECT_EQ(2U, GestureEventDebouncingQueueSize());
SimulateGestureEvent(WebInputEvent::GestureTapDown,
WebGestureEvent::Touchscreen);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(2U, GestureEventLastQueueEventSize());
EXPECT_EQ(3U, GestureEventDebouncingQueueSize());
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::MessageLoop::QuitClosure(),
TimeDelta::FromMilliseconds(5));
base::MessageLoop::current()->Run();
// The deferred events are correctly queued in coalescing queue.
EXPECT_EQ(1U, process_->sink().message_count());
if (shouldDeferTapDownEvents())
// NOTE: The TapDown is still deferred hence not queued.
EXPECT_EQ(4U, GestureEventLastQueueEventSize());
else
EXPECT_EQ(5U, GestureEventLastQueueEventSize());
EXPECT_EQ(0U, GestureEventDebouncingQueueSize());
EXPECT_FALSE(ScrollingInProgress());
// Verify that the coalescing queue contains the correct events.
WebInputEvent::Type expected[] = {
WebInputEvent::GestureScrollUpdate,
WebInputEvent::GestureScrollUpdate,
WebInputEvent::GestureScrollEnd,
WebInputEvent::GestureFlingStart};
for (unsigned i = 0; i < sizeof(expected) / sizeof(WebInputEvent::Type);
i++) {
WebGestureEvent merged_event = GestureEventQueueEventAt(i);
EXPECT_EQ(expected[i], merged_event.type);
}
}
// Test that non-scroll events are deferred while scrolling during the debounce
// interval and are discarded if a GestureScrollUpdate event arrives before the
// interval end.
TEST_F(ImmediateInputRouterTest, DebounceDropsDeferredEvents) {
set_debounce_interval_time_ms(3);
EXPECT_FALSE(ScrollingInProgress());
SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
WebGestureEvent::Touchscreen);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, GestureEventLastQueueEventSize());
EXPECT_EQ(0U, GestureEventDebouncingQueueSize());
EXPECT_TRUE(ScrollingInProgress());
// This event should get discarded.
SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
WebGestureEvent::Touchscreen);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, GestureEventLastQueueEventSize());
EXPECT_EQ(1U, GestureEventDebouncingQueueSize());
SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
WebGestureEvent::Touchscreen);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(2U, GestureEventLastQueueEventSize());
EXPECT_EQ(0U, GestureEventDebouncingQueueSize());
EXPECT_TRUE(ScrollingInProgress());
// Verify that the coalescing queue contains the correct events.
WebInputEvent::Type expected[] = {
WebInputEvent::GestureScrollUpdate,
WebInputEvent::GestureScrollUpdate};
for (unsigned i = 0; i < sizeof(expected) / sizeof(WebInputEvent::Type);
i++) {
WebGestureEvent merged_event = GestureEventQueueEventAt(i);
EXPECT_EQ(expected[i], merged_event.type);
}
}
// Tests that touch-events are queued properly.
TEST_F(ImmediateInputRouterTest, TouchEventQueue) {
PressTouchPoint(1, 1);
SendTouchEvent();
client_->ExpectSendImmediatelyCalled(true);
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
// The second touch should not be sent since one is already in queue.
MoveTouchPoint(0, 5, 5);
SendTouchEvent();
client_->ExpectSendImmediatelyCalled(false);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(2U, TouchEventQueueSize());
// Receive an ACK for the first touch-event.
SendInputEventACK(WebInputEvent::TouchStart,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, TouchEventQueueSize());
client_->ExpectAckCalled(1);
EXPECT_EQ(WebInputEvent::TouchStart,
client_->acked_touch_event().event.type);
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
SendInputEventACK(WebInputEvent::TouchMove,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, TouchEventQueueSize());
client_->ExpectAckCalled(1);
EXPECT_EQ(WebInputEvent::TouchMove,
client_->acked_touch_event().event.type);
EXPECT_EQ(0U, process_->sink().message_count());
}
// Tests that the touch-queue is emptied if a page stops listening for touch
// events.
TEST_F(ImmediateInputRouterTest, TouchEventQueueFlush) {
input_router_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
EXPECT_TRUE(client_->has_touch_handler());
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
EXPECT_EQ(0U, TouchEventQueueSize());
EXPECT_TRUE(input_router_->ShouldForwardTouchEvent());
// Send a touch-press event.
PressTouchPoint(1, 1);
SendTouchEvent();
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
ReleaseTouchPoint(0);
SendTouchEvent();
for (int i = 5; i < 15; ++i) {
PressTouchPoint(1, 1);
SendTouchEvent();
MoveTouchPoint(0, i, i);
SendTouchEvent();
ReleaseTouchPoint(0);
SendTouchEvent();
}
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(32U, TouchEventQueueSize());
// Receive an ACK for the first touch-event. One of the queued touch-event
// should be forwarded.
SendInputEventACK(WebInputEvent::TouchStart,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(31U, TouchEventQueueSize());
EXPECT_EQ(WebInputEvent::TouchStart,
client_->acked_touch_event().event.type);
client_->ExpectAckCalled(1);
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
// The page stops listening for touch-events. The touch-event queue should now
// be emptied, but none of the queued touch-events should be sent to the
// renderer.
input_router_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, false));
EXPECT_FALSE(client_->has_touch_handler());
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
EXPECT_FALSE(input_router_->ShouldForwardTouchEvent());
}
// Tests that touch-events are coalesced properly in the queue.
TEST_F(ImmediateInputRouterTest, TouchEventQueueCoalesce) {
input_router_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
EXPECT_TRUE(input_router_->ShouldForwardTouchEvent());
// Send a touch-press event.
PressTouchPoint(1, 1);
SendTouchEvent();
client_->ExpectSendImmediatelyCalled(true);
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
// 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 (int i = 5; i < 15; ++i) {
MoveTouchPoint(0, i, i);
SendTouchEvent();
}
client_->ExpectSendImmediatelyCalled(false);
ReleaseTouchPoint(0);
SendTouchEvent();
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(3U, TouchEventQueueSize());
client_->ExpectSendImmediatelyCalled(false);
// ACK the press.
SendInputEventACK(WebInputEvent::TouchStart,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(2U, TouchEventQueueSize());
EXPECT_EQ(WebInputEvent::TouchStart,
client_->acked_touch_event().event.type);
client_->ExpectAckCalled(1);
process_->sink().ClearMessages();
// Coalesced touch-move events should be sent.
client_->ExpectSendImmediatelyCalled(true);
EXPECT_EQ(WebInputEvent::TouchMove,
client_->immediately_sent_touch_event().event.type);
// ACK the moves.
SendInputEventACK(WebInputEvent::TouchMove,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, TouchEventQueueSize());
EXPECT_EQ(WebInputEvent::TouchMove,
client_->acked_touch_event().event.type);
client_->ExpectAckCalled(10);
process_->sink().ClearMessages();
// ACK the release.
SendInputEventACK(WebInputEvent::TouchEnd,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
EXPECT_EQ(WebInputEvent::TouchEnd,
client_->acked_touch_event().event.type);
client_->ExpectAckCalled(1);
}
// 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(ImmediateInputRouterTest, SentTouchEventDoesNotCoalesce) {
input_router_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
EXPECT_TRUE(input_router_->ShouldForwardTouchEvent());
// Send a touch-press event.
PressTouchPoint(1, 1);
SendTouchEvent();
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
// 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 (int i = 5; i < 15; ++i) {
MoveTouchPoint(0, i, i);
SendTouchEvent();
}
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(2U, TouchEventQueueSize());
SendInputEventACK(WebInputEvent::TouchStart,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, TouchEventQueueSize());
process_->sink().ClearMessages();
// 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);
SendTouchEvent();
EXPECT_EQ(2U, TouchEventQueueSize());
MoveTouchPoint(0, 7, 7);
SendTouchEvent();
EXPECT_EQ(2U, TouchEventQueueSize());
}
// Tests that coalescing works correctly for multi-touch events.
TEST_F(ImmediateInputRouterTest, TouchEventQueueMultiTouch) {
input_router_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
EXPECT_TRUE(input_router_->ShouldForwardTouchEvent());
// Press the first finger.
PressTouchPoint(1, 1);
SendTouchEvent();
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
// Move the finger.
MoveTouchPoint(0, 5, 5);
SendTouchEvent();
EXPECT_EQ(2U, TouchEventQueueSize());
// Now press a second finger.
PressTouchPoint(2, 2);
SendTouchEvent();
EXPECT_EQ(3U, TouchEventQueueSize());
// Move both fingers.
MoveTouchPoint(0, 10, 10);
MoveTouchPoint(1, 20, 20);
SendTouchEvent();
EXPECT_EQ(4U, TouchEventQueueSize());
// Move only one finger now.
MoveTouchPoint(0, 15, 15);
SendTouchEvent();
EXPECT_EQ(4U, TouchEventQueueSize());
// Move the other finger.
MoveTouchPoint(1, 25, 25);
SendTouchEvent();
EXPECT_EQ(4U, TouchEventQueueSize());
// Make sure both fingers are marked as having been moved in the coalesced
// event.
const WebTouchEvent& event = latest_event();
EXPECT_EQ(WebTouchPoint::StateMoved, event.touches[0].state);
EXPECT_EQ(WebTouchPoint::StateMoved, event.touches[1].state);
}
// Tests that if a touch-event queue is destroyed in response to a touch-event
// in the renderer, then there is no crash when the ACK for that touch-event
// comes back.
TEST_F(ImmediateInputRouterTest, TouchEventAckAfterQueueFlushed) {
// First, install a touch-event handler and send some touch-events to the
// renderer.
input_router_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
EXPECT_TRUE(input_router_->ShouldForwardTouchEvent());
PressTouchPoint(1, 1);
SendTouchEvent();
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, TouchEventQueueSize());
process_->sink().ClearMessages();
MoveTouchPoint(0, 10, 10);
SendTouchEvent();
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(2U, TouchEventQueueSize());
// Receive an ACK for the press. This should cause the queued touch-move to
// be sent to the renderer.
SendInputEventACK(WebInputEvent::TouchStart,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, TouchEventQueueSize());
process_->sink().ClearMessages();
// Uninstall the touch-event handler. This will cause the queue to be flushed.
input_router_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, false));
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
// Now receive an ACK for the move.
SendInputEventACK(WebInputEvent::TouchMove,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
}
// 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(ImmediateInputRouterTest, TouchEventQueueNoConsumer) {
// The first touch-press should reach the renderer.
PressTouchPoint(1, 1);
SendTouchEvent();
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
// The second touch should not be sent since one is already in queue.
MoveTouchPoint(0, 5, 5);
SendTouchEvent();
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(2U, TouchEventQueueSize());
// Receive an ACK for the first touch-event. This should release the queued
// touch-event, but it should not be sent to the renderer.
SendInputEventACK(WebInputEvent::TouchStart,
INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(0U, TouchEventQueueSize());
EXPECT_EQ(WebInputEvent::TouchMove,
client_->acked_touch_event().event.type);
client_->ExpectAckCalled(2);
EXPECT_EQ(0U, process_->sink().message_count());
process_->sink().ClearMessages();
// Send a release event. This should not reach the renderer.
ReleaseTouchPoint(0);
SendTouchEvent();
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(WebInputEvent::TouchEnd,
client_->acked_touch_event().event.type);
client_->ExpectAckCalled(1);
// 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);
SendTouchEvent();
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
MoveTouchPoint(0, 5, 5);
SendTouchEvent();
MoveTouchPoint(0, 6, 5);
SendTouchEvent();
ReleaseTouchPoint(0);
SendTouchEvent();
PressTouchPoint(6, 5);
SendTouchEvent();
EXPECT_EQ(0U, process_->sink().message_count());
// 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, TouchEventQueueSize());
SendInputEventACK(WebInputEvent::TouchStart,
INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(WebInputEvent::TouchEnd, client_->acked_touch_event().event.type);
client_->ExpectAckCalled(4);
EXPECT_EQ(1U, TouchEventQueueSize());
process_->sink().ClearMessages();
// ACK the second press event as NO_CONSUMER too.
SendInputEventACK(WebInputEvent::TouchStart,
INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(WebInputEvent::TouchStart, client_->acked_touch_event().event.type);
client_->ExpectAckCalled(1);
EXPECT_EQ(0U, TouchEventQueueSize());
// Send a second press event. Even though the first touch had NO_CONSUMER,
// this press event should reach the renderer.
PressTouchPoint(1, 1);
SendTouchEvent();
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, TouchEventQueueSize());
}
TEST_F(ImmediateInputRouterTest, TouchEventQueueConsumerIgnoreMultiFinger) {
// Press two touch points and move them around a bit. The renderer consumes
// the events for the first touch point, but returns NO_CONSUMER_EXISTS for
// the second touch point.
PressTouchPoint(1, 1);
SendTouchEvent();
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
MoveTouchPoint(0, 5, 5);
SendTouchEvent();
PressTouchPoint(10, 10);
SendTouchEvent();
MoveTouchPoint(0, 2, 2);
SendTouchEvent();
MoveTouchPoint(1, 4, 10);
SendTouchEvent();
MoveTouchPoint(0, 10, 10);
MoveTouchPoint(1, 20, 20);
SendTouchEvent();
// Since the first touch-press is still pending ACK, no other event should
// have been sent to the renderer.
EXPECT_EQ(0U, process_->sink().message_count());
// The queue includes the two presses, the first touch-move of the first
// point, and a coalesced touch-move of both points.
EXPECT_EQ(4U, TouchEventQueueSize());
// ACK the first press as CONSUMED. This should cause the first touch-move of
// the first touch-point to be dispatched.
SendInputEventACK(WebInputEvent::TouchStart,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
EXPECT_EQ(3U, TouchEventQueueSize());
// ACK the first move as CONSUMED.
SendInputEventACK(WebInputEvent::TouchMove,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
EXPECT_EQ(2U, TouchEventQueueSize());
// ACK the second press as NO_CONSUMER_EXISTS. This will dequeue the coalesced
// touch-move event (which contains both touch points). Although the second
// touch-point does not need to be sent to the renderer, the first touch-point
// did move, and so the coalesced touch-event will be sent to the renderer.
SendInputEventACK(WebInputEvent::TouchStart,
INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
EXPECT_EQ(1U, TouchEventQueueSize());
// ACK the coalesced move as NOT_CONSUMED.
SendInputEventACK(WebInputEvent::TouchMove,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
// Move just the second touch point. Because the first touch point did not
// move, this event should not reach the renderer.
MoveTouchPoint(1, 30, 30);
SendTouchEvent();
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
// Move just the first touch point. This should reach the renderer.
MoveTouchPoint(0, 10, 10);
SendTouchEvent();
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, TouchEventQueueSize());
process_->sink().ClearMessages();
// Move both fingers. This event should reach the renderer (after the ACK of
// the previous move event is received), because the first touch point did
// move.
MoveTouchPoint(0, 15, 15);
MoveTouchPoint(1, 25, 25);
SendTouchEvent();
EXPECT_EQ(0U, process_->sink().message_count());
SendInputEventACK(WebInputEvent::TouchMove,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, TouchEventQueueSize());
process_->sink().ClearMessages();
SendInputEventACK(WebInputEvent::TouchMove,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
// Release the first finger. Then move the second finger around some, then
// press another finger. Once the release event is ACKed, the move events of
// the second finger should be immediately released to the view, and the
// touch-press event should be dispatched to the renderer.
ReleaseTouchPoint(0);
SendTouchEvent();
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, TouchEventQueueSize());
process_->sink().ClearMessages();
MoveTouchPoint(1, 40, 40);
SendTouchEvent();
MoveTouchPoint(1, 50, 50);
SendTouchEvent();
PressTouchPoint(1, 1);
SendTouchEvent();
MoveTouchPoint(1, 30, 30);
SendTouchEvent();
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(4U, TouchEventQueueSize());
SendInputEventACK(WebInputEvent::TouchEnd,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(2U, TouchEventQueueSize());
EXPECT_EQ(WebInputEvent::TouchMove,
client_->acked_touch_event().event.type);
process_->sink().ClearMessages();
// ACK the press with NO_CONSUMED_EXISTS. This should release the queued
// touch-move events to the view.
SendInputEventACK(WebInputEvent::TouchStart,
INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
EXPECT_EQ(WebInputEvent::TouchMove,
client_->acked_touch_event().event.type);
ReleaseTouchPoint(2);
ReleaseTouchPoint(1);
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
}
#if defined(OS_WIN) || defined(USE_AURA)
// Tests that the acked events have correct state. (ui::Events are used only on
// windows and aura)
TEST_F(ImmediateInputRouterTest, AckedTouchEventState) {
input_router_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
EXPECT_TRUE(input_router_->ShouldForwardTouchEvent());
// Send a bunch of events, and make sure the ACKed events are correct.
ScopedVector<ui::TouchEvent> expected_events;
// Use a custom timestamp for all the events to test that the acked events
// have the same timestamp;
base::TimeDelta timestamp = base::Time::NowFromSystemTime() - base::Time();
timestamp -= base::TimeDelta::FromSeconds(600);
// Press the first finger.
PressTouchPoint(1, 1);
SetTouchTimestamp(timestamp);
SendTouchEvent();
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
expected_events.push_back(new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
gfx::Point(1, 1), 0, timestamp));
// Move the finger.
timestamp += base::TimeDelta::FromSeconds(10);
MoveTouchPoint(0, 5, 5);
SetTouchTimestamp(timestamp);
SendTouchEvent();
EXPECT_EQ(2U, TouchEventQueueSize());
expected_events.push_back(new ui::TouchEvent(ui::ET_TOUCH_MOVED,
gfx::Point(5, 5), 0, timestamp));
// Now press a second finger.
timestamp += base::TimeDelta::FromSeconds(10);
PressTouchPoint(2, 2);
SetTouchTimestamp(timestamp);
SendTouchEvent();
EXPECT_EQ(3U, TouchEventQueueSize());
expected_events.push_back(new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
gfx::Point(2, 2), 1, timestamp));
// Move both fingers.
timestamp += base::TimeDelta::FromSeconds(10);
MoveTouchPoint(0, 10, 10);
MoveTouchPoint(1, 20, 20);
SetTouchTimestamp(timestamp);
SendTouchEvent();
EXPECT_EQ(4U, TouchEventQueueSize());
expected_events.push_back(new ui::TouchEvent(ui::ET_TOUCH_MOVED,
gfx::Point(10, 10), 0, timestamp));
expected_events.push_back(new ui::TouchEvent(ui::ET_TOUCH_MOVED,
gfx::Point(20, 20), 1, timestamp));
// Receive the ACKs and make sure the generated events from the acked events
// are correct.
WebInputEvent::Type acks[] = { WebInputEvent::TouchStart,
WebInputEvent::TouchMove,
WebInputEvent::TouchStart,
WebInputEvent::TouchMove };
TouchEventCoordinateSystem coordinate_system = LOCAL_COORDINATES;
#if !defined(OS_WIN)
coordinate_system = SCREEN_COORDINATES;
#endif
for (size_t i = 0; i < arraysize(acks); ++i) {
SendInputEventACK(acks[i],
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(acks[i], client_->acked_touch_event().event.type);
ScopedVector<ui::TouchEvent> acked;
MakeUITouchEventsFromWebTouchEvents(
client_->acked_touch_event(), &acked, coordinate_system);
bool success = EventListIsSubset(acked, expected_events);
EXPECT_TRUE(success) << "Failed on step: " << i;
if (!success)
break;
expected_events.erase(expected_events.begin(),
expected_events.begin() + acked.size());
}
EXPECT_EQ(0U, expected_events.size());
}
#endif // defined(OS_WIN) || defined(USE_AURA)
TEST_F(ImmediateInputRouterTest, UnhandledWheelEvent) {
// Simulate wheel events.
SimulateWheelEvent(0, -5, 0, false); // sent directly
SimulateWheelEvent(0, -10, 0, false); // enqueued
// Check that only the first event was sent.
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
process_->sink().ClearMessages();
// Indicate that the wheel event was unhandled.
SendInputEventACK(WebInputEvent::MouseWheel,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// Check that the correct unhandled wheel event was received.
client_->ExpectAckCalled(1);
EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, client_->ack_state());
EXPECT_EQ(client_->acked_wheel_event().deltaY, -5);
// Check that the second event was sent.
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
process_->sink().ClearMessages();
// Check that the correct unhandled wheel event was received.
EXPECT_EQ(client_->acked_wheel_event().deltaY, -5);
}
} // namespace content