// 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 #include "base/basictypes.h" #include "base/command_line.h" #include "base/memory/scoped_ptr.h" #include "base/strings/utf_string_conversions.h" #include "content/browser/renderer_host/input/gesture_event_queue.h" #include "content/browser/renderer_host/input/input_router_client.h" #include "content/browser/renderer_host/input/input_router_impl.h" #include "content/browser/renderer_host/input/mock_input_ack_handler.h" #include "content/browser/renderer_host/input/mock_input_router_client.h" #include "content/common/content_constants_internal.h" #include "content/common/edit_command.h" #include "content/common/input/synthetic_web_input_event_builders.h" #include "content/common/input/touch_action.h" #include "content/common/input/web_input_event_traits.h" #include "content/common/input_messages.h" #include "content/common/view_messages.h" #include "content/public/common/content_switches.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/events/keycodes/keyboard_codes.h" #if defined(USE_AURA) #include "content/browser/renderer_host/ui_events_helper.h" #include "ui/events/event.h" #endif using base::TimeDelta; using blink::WebGestureDevice; using blink::WebGestureEvent; using blink::WebKeyboardEvent; using blink::WebInputEvent; using blink::WebMouseEvent; using blink::WebMouseWheelEvent; using blink::WebTouchEvent; using blink::WebTouchPoint; namespace content { namespace { const WebInputEvent* GetInputEventFromMessage(const IPC::Message& message) { PickleIterator iter(message); const char* data; int data_length; if (!iter.ReadData(&data, &data_length)) return NULL; return reinterpret_cast(data); } WebInputEvent& GetEventWithType(WebInputEvent::Type type) { WebInputEvent* event = NULL; if (WebInputEvent::isMouseEventType(type)) { static WebMouseEvent mouse; event = &mouse; } else if (WebInputEvent::isTouchEventType(type)) { static WebTouchEvent touch; event = &touch; } else if (WebInputEvent::isKeyboardEventType(type)) { static WebKeyboardEvent key; event = &key; } else if (WebInputEvent::isGestureEventType(type)) { static WebGestureEvent gesture; event = &gesture; } else if (type == WebInputEvent::MouseWheel) { static WebMouseWheelEvent wheel; event = &wheel; } CHECK(event); event->type = type; return *event; } bool GetIsShortcutFromHandleInputEventMessage(const IPC::Message* msg) { InputMsg_HandleInputEvent::Schema::Param param; InputMsg_HandleInputEvent::Read(msg, ¶m); return get<2>(param); } template 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, ¶m)); EXPECT_EQ(arg1, get<0>(param)); } template 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, ¶m)); EXPECT_EQ(arg1, get<0>(param)); EXPECT_EQ(arg2, get<1>(param)); } #if 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& subset, const ScopedVector& 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(USE_AURA) } // namespace class InputRouterImplTest : public testing::Test { public: InputRouterImplTest() {} ~InputRouterImplTest() override {} protected: // testing::Test void SetUp() override { browser_context_.reset(new TestBrowserContext()); process_.reset(new MockRenderProcessHost(browser_context_.get())); client_.reset(new MockInputRouterClient()); ack_handler_.reset(new MockInputAckHandler()); base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); command_line->AppendSwitch(switches::kValidateInputEventStream); input_router_.reset(new InputRouterImpl(process_.get(), client_.get(), ack_handler_.get(), MSG_ROUTING_NONE, config_)); client_->set_input_router(input_router()); ack_handler_->set_input_router(input_router()); } void TearDown() override { // Process all pending tasks to avoid leaks. base::MessageLoop::current()->RunUntilIdle(); input_router_.reset(); client_.reset(); process_.reset(); browser_context_.reset(); } void SetUpForTouchAckTimeoutTest(int timeout_ms) { config_.touch_config.touch_ack_timeout_delay = base::TimeDelta::FromMilliseconds(timeout_ms); config_.touch_config.touch_ack_timeout_supported = true; TearDown(); SetUp(); } void SimulateKeyboardEvent(WebInputEvent::Type type, bool is_shortcut) { WebKeyboardEvent event = SyntheticWebKeyboardEventBuilder::Build(type); NativeWebKeyboardEvent native_event; memcpy(&native_event, &event, sizeof(event)); input_router_->SendKeyboardEvent( native_event, ui::LatencyInfo(), is_shortcut); } void SimulateWheelEvent(float dX, float dY, int modifiers, bool precise) { input_router_->SendWheelEvent(MouseWheelEventWithLatencyInfo( SyntheticWebMouseWheelEventBuilder::Build(dX, dY, modifiers, precise))); } void SimulateMouseEvent(WebInputEvent::Type type, int x, int y) { input_router_->SendMouseEvent(MouseEventWithLatencyInfo( SyntheticWebMouseEventBuilder::Build(type, x, y, 0))); } void SimulateWheelEventWithPhase(WebMouseWheelEvent::Phase phase) { input_router_->SendWheelEvent(MouseWheelEventWithLatencyInfo( SyntheticWebMouseWheelEventBuilder::Build(phase))); } void SimulateGestureEvent(WebGestureEvent gesture) { // Ensure non-zero touchscreen fling velocities, as the router will // validate aganst such. if (gesture.type == WebInputEvent::GestureFlingStart && gesture.sourceDevice == blink::WebGestureDeviceTouchscreen && !gesture.data.flingStart.velocityX && !gesture.data.flingStart.velocityY) { gesture.data.flingStart.velocityX = 5.f; } input_router_->SendGestureEvent(GestureEventWithLatencyInfo(gesture)); } void SimulateGestureEvent(WebInputEvent::Type type, WebGestureDevice source_device) { SimulateGestureEvent( SyntheticWebGestureEventBuilder::Build(type, source_device)); } void SimulateGestureScrollUpdateEvent(float dX, float dY, int modifiers, WebGestureDevice source_device) { SimulateGestureEvent(SyntheticWebGestureEventBuilder::BuildScrollUpdate( dX, dY, modifiers, source_device)); } void SimulateGesturePinchUpdateEvent(float scale, float anchor_x, float anchor_y, int modifiers, WebGestureDevice source_device) { SimulateGestureEvent(SyntheticWebGestureEventBuilder::BuildPinchUpdate( scale, anchor_x, anchor_y, modifiers, source_device)); } void SimulateGestureFlingStartEvent(float velocity_x, float velocity_y, WebGestureDevice source_device) { SimulateGestureEvent(SyntheticWebGestureEventBuilder::BuildFling( velocity_x, velocity_y, source_device)); } void SetTouchTimestamp(base::TimeDelta timestamp) { touch_event_.SetTimestamp(timestamp); } void SendTouchEvent() { input_router_->SendTouchEvent(TouchEventWithLatencyInfo(touch_event_)); touch_event_.ResetPoints(); } int PressTouchPoint(int x, int y) { return touch_event_.PressPoint(x, y); } void MoveTouchPoint(int index, int x, int y) { touch_event_.MovePoint(index, x, y); } void ReleaseTouchPoint(int index) { touch_event_.ReleasePoint(index); } void CancelTouchPoint(int index) { touch_event_.CancelPoint(index); } void SendInputEventACK(blink::WebInputEvent::Type type, InputEventAckState ack_result) { InputHostMsg_HandleInputEvent_ACK_Params ack; ack.type = type; ack.state = ack_result; input_router_->OnMessageReceived(InputHostMsg_HandleInputEvent_ACK(0, ack)); } InputRouterImpl* input_router() const { return input_router_.get(); } bool TouchEventQueueEmpty() const { return input_router()->touch_event_queue_.empty(); } bool TouchEventTimeoutEnabled() const { return input_router()->touch_event_queue_.IsAckTimeoutEnabled(); } void RequestNotificationWhenFlushed() const { return input_router_->RequestNotificationWhenFlushed(); } size_t GetAndResetDidFlushCount() { return client_->GetAndResetDidFlushCount(); } bool HasPendingEvents() const { return input_router_->HasPendingEvents(); } void OnHasTouchEventHandlers(bool has_handlers) { input_router_->OnMessageReceived( ViewHostMsg_HasTouchEventHandlers(0, has_handlers)); } void OnSetTouchAction(content::TouchAction touch_action) { input_router_->OnMessageReceived( InputHostMsg_SetTouchAction(0, touch_action)); } size_t GetSentMessageCountAndResetSink() { size_t count = process_->sink().message_count(); process_->sink().ClearMessages(); return count; } static void RunTasksAndWait(base::TimeDelta delay) { base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::MessageLoop::QuitClosure(), delay); base::MessageLoop::current()->Run(); } InputRouterImpl::Config config_; scoped_ptr process_; scoped_ptr client_; scoped_ptr ack_handler_; scoped_ptr input_router_; private: base::MessageLoopForUI message_loop_; SyntheticWebTouchEvent touch_event_; scoped_ptr browser_context_; }; TEST_F(InputRouterImplTest, CoalescesRangeSelection) { input_router_->SendInput(scoped_ptr( new InputMsg_SelectRange(0, gfx::Point(1, 2), gfx::Point(3, 4)))); ExpectIPCMessageWithArg2( process_->sink().GetMessageAt(0), gfx::Point(1, 2), gfx::Point(3, 4)); EXPECT_EQ(1u, GetSentMessageCountAndResetSink()); // Send two more messages without acking. input_router_->SendInput(scoped_ptr( new InputMsg_SelectRange(0, gfx::Point(5, 6), gfx::Point(7, 8)))); EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); input_router_->SendInput(scoped_ptr( new InputMsg_SelectRange(0, gfx::Point(9, 10), gfx::Point(11, 12)))); EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); // Now ack the first message. { scoped_ptr response(new InputHostMsg_SelectRange_ACK(0)); input_router_->OnMessageReceived(*response); } // Verify that the two messages are coalesced into one message. ExpectIPCMessageWithArg2( process_->sink().GetMessageAt(0), gfx::Point(9, 10), gfx::Point(11, 12)); EXPECT_EQ(1u, GetSentMessageCountAndResetSink()); // Acking the coalesced msg should not send any more msg. { scoped_ptr response(new InputHostMsg_SelectRange_ACK(0)); input_router_->OnMessageReceived(*response); } EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); } TEST_F(InputRouterImplTest, CoalescesMoveRangeSelectionExtent) { input_router_->SendInput(scoped_ptr( new InputMsg_MoveRangeSelectionExtent(0, gfx::Point(1, 2)))); ExpectIPCMessageWithArg1( process_->sink().GetMessageAt(0), gfx::Point(1, 2)); EXPECT_EQ(1u, GetSentMessageCountAndResetSink()); // Send two more messages without acking. input_router_->SendInput(scoped_ptr( new InputMsg_MoveRangeSelectionExtent(0, gfx::Point(3, 4)))); EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); input_router_->SendInput(scoped_ptr( new InputMsg_MoveRangeSelectionExtent(0, gfx::Point(5, 6)))); EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); // Now ack the first message. { scoped_ptr response( new InputHostMsg_MoveRangeSelectionExtent_ACK(0)); input_router_->OnMessageReceived(*response); } // Verify that the two messages are coalesced into one message. ExpectIPCMessageWithArg1( process_->sink().GetMessageAt(0), gfx::Point(5, 6)); EXPECT_EQ(1u, GetSentMessageCountAndResetSink()); // Acking the coalesced msg should not send any more msg. { scoped_ptr response( new InputHostMsg_MoveRangeSelectionExtent_ACK(0)); input_router_->OnMessageReceived(*response); } EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); } TEST_F(InputRouterImplTest, InterleaveSelectRangeAndMoveRangeSelectionExtent) { // Send first message: SelectRange. input_router_->SendInput(scoped_ptr( new InputMsg_SelectRange(0, gfx::Point(1, 2), gfx::Point(3, 4)))); ExpectIPCMessageWithArg2( process_->sink().GetMessageAt(0), gfx::Point(1, 2), gfx::Point(3, 4)); EXPECT_EQ(1u, GetSentMessageCountAndResetSink()); // Send second message: MoveRangeSelectionExtent. input_router_->SendInput(scoped_ptr( new InputMsg_MoveRangeSelectionExtent(0, gfx::Point(5, 6)))); EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); // Send third message: SelectRange. input_router_->SendInput(scoped_ptr( new InputMsg_SelectRange(0, gfx::Point(7, 8), gfx::Point(9, 10)))); EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); // Ack the messages and verify that they're not coalesced and that they're in // correct order. // Ack the first message. { scoped_ptr response( new InputHostMsg_SelectRange_ACK(0)); input_router_->OnMessageReceived(*response); } ExpectIPCMessageWithArg1( process_->sink().GetMessageAt(0), gfx::Point(5, 6)); EXPECT_EQ(1u, GetSentMessageCountAndResetSink()); // Ack the second message. { scoped_ptr response( new InputHostMsg_MoveRangeSelectionExtent_ACK(0)); input_router_->OnMessageReceived(*response); } ExpectIPCMessageWithArg2( process_->sink().GetMessageAt(0), gfx::Point(7, 8), gfx::Point(9, 10)); EXPECT_EQ(1u, GetSentMessageCountAndResetSink()); // Ack the third message. { scoped_ptr response( new InputHostMsg_SelectRange_ACK(0)); input_router_->OnMessageReceived(*response); } EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); } TEST_F(InputRouterImplTest, CoalescesInterleavedSelectRangeAndMoveRangeSelectionExtent) { // Send interleaved SelectRange and MoveRangeSelectionExtent messages. They // should be coalesced as shown by the arrows. // > SelectRange // MoveRangeSelectionExtent // MoveRangeSelectionExtent // > MoveRangeSelectionExtent // SelectRange // > SelectRange // > MoveRangeSelectionExtent input_router_->SendInput(scoped_ptr( new InputMsg_SelectRange(0, gfx::Point(1, 2), gfx::Point(3, 4)))); ExpectIPCMessageWithArg2( process_->sink().GetMessageAt(0), gfx::Point(1, 2), gfx::Point(3, 4)); EXPECT_EQ(1u, GetSentMessageCountAndResetSink()); input_router_->SendInput(scoped_ptr( new InputMsg_MoveRangeSelectionExtent(0, gfx::Point(5, 6)))); EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); input_router_->SendInput(scoped_ptr( new InputMsg_MoveRangeSelectionExtent(0, gfx::Point(7, 8)))); EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); input_router_->SendInput(scoped_ptr( new InputMsg_MoveRangeSelectionExtent(0, gfx::Point(9, 10)))); EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); input_router_->SendInput(scoped_ptr( new InputMsg_SelectRange(0, gfx::Point(11, 12), gfx::Point(13, 14)))); EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); input_router_->SendInput(scoped_ptr( new InputMsg_SelectRange(0, gfx::Point(15, 16), gfx::Point(17, 18)))); EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); input_router_->SendInput(scoped_ptr( new InputMsg_MoveRangeSelectionExtent(0, gfx::Point(19, 20)))); EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); // Ack the first message. { scoped_ptr response( new InputHostMsg_SelectRange_ACK(0)); input_router_->OnMessageReceived(*response); } // Verify that the three MoveRangeSelectionExtent messages are coalesced into // one message. ExpectIPCMessageWithArg1( process_->sink().GetMessageAt(0), gfx::Point(9, 10)); EXPECT_EQ(1u, GetSentMessageCountAndResetSink()); // Ack the second message. { scoped_ptr response( new InputHostMsg_MoveRangeSelectionExtent_ACK(0)); input_router_->OnMessageReceived(*response); } // Verify that the two SelectRange messages are coalesced into one message. ExpectIPCMessageWithArg2( process_->sink().GetMessageAt(0), gfx::Point(15, 16), gfx::Point(17, 18)); EXPECT_EQ(1u, GetSentMessageCountAndResetSink()); // Ack the third message. { scoped_ptr response( new InputHostMsg_SelectRange_ACK(0)); input_router_->OnMessageReceived(*response); } // Verify the fourth message. ExpectIPCMessageWithArg1( process_->sink().GetMessageAt(0), gfx::Point(19, 20)); EXPECT_EQ(1u, GetSentMessageCountAndResetSink()); // Ack the fourth message. { scoped_ptr response( new InputHostMsg_MoveRangeSelectionExtent_ACK(0)); input_router_->OnMessageReceived(*response); } EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); } TEST_F(InputRouterImplTest, CoalescesCaretMove) { input_router_->SendInput( scoped_ptr(new InputMsg_MoveCaret(0, gfx::Point(1, 2)))); ExpectIPCMessageWithArg1( process_->sink().GetMessageAt(0), gfx::Point(1, 2)); EXPECT_EQ(1u, GetSentMessageCountAndResetSink()); // Send two more messages without acking. input_router_->SendInput( scoped_ptr(new InputMsg_MoveCaret(0, gfx::Point(5, 6)))); EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); input_router_->SendInput( scoped_ptr(new InputMsg_MoveCaret(0, gfx::Point(9, 10)))); EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); // Now ack the first message. { scoped_ptr response(new InputHostMsg_MoveCaret_ACK(0)); input_router_->OnMessageReceived(*response); } // Verify that the two messages are coalesced into one message. ExpectIPCMessageWithArg1( process_->sink().GetMessageAt(0), gfx::Point(9, 10)); EXPECT_EQ(1u, GetSentMessageCountAndResetSink()); // Acking the coalesced msg should not send any more msg. { scoped_ptr response(new InputHostMsg_MoveCaret_ACK(0)); input_router_->OnMessageReceived(*response); } EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); } TEST_F(InputRouterImplTest, HandledInputEvent) { client_->set_filter_state(INPUT_EVENT_ACK_STATE_CONSUMED); // Simulate a keyboard event. SimulateKeyboardEvent(WebInputEvent::RawKeyDown, false); // Make sure no input event is sent to the renderer. EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); // OnKeyboardEventAck should be triggered without actual ack. EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); // As the event was acked already, keyboard event queue should be // empty. ASSERT_EQ(NULL, input_router_->GetLastKeyboardEvent()); } TEST_F(InputRouterImplTest, ClientCanceledKeyboardEvent) { client_->set_filter_state(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); // Simulate a keyboard event that has no consumer. SimulateKeyboardEvent(WebInputEvent::RawKeyDown, false); // Make sure no input event is sent to the renderer. EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); // Simulate a keyboard event that should be dropped. client_->set_filter_state(INPUT_EVENT_ACK_STATE_UNKNOWN); SimulateKeyboardEvent(WebInputEvent::RawKeyDown, false); // Make sure no input event is sent to the renderer, and no ack is sent. EXPECT_EQ(0u, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); } TEST_F(InputRouterImplTest, ShortcutKeyboardEvent) { SimulateKeyboardEvent(WebInputEvent::RawKeyDown, true); EXPECT_TRUE(GetIsShortcutFromHandleInputEventMessage( process_->sink().GetMessageAt(0))); process_->sink().ClearMessages(); SimulateKeyboardEvent(WebInputEvent::RawKeyDown, false); EXPECT_FALSE(GetIsShortcutFromHandleInputEventMessage( process_->sink().GetMessageAt(0))); } TEST_F(InputRouterImplTest, NoncorrespondingKeyEvents) { SimulateKeyboardEvent(WebInputEvent::RawKeyDown, false); SendInputEventACK(WebInputEvent::KeyUp, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_TRUE(ack_handler_->unexpected_event_ack_called()); } // Tests ported from RenderWidgetHostTest -------------------------------------- TEST_F(InputRouterImplTest, HandleKeyEventsWeSent) { // Simulate a keyboard event. SimulateKeyboardEvent(WebInputEvent::RawKeyDown, false); 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); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(WebInputEvent::RawKeyDown, ack_handler_->acked_keyboard_event().type); } TEST_F(InputRouterImplTest, IgnoreKeyEventsWeDidntSend) { // Send a simulated, unrequested key response. We should ignore this. SendInputEventACK(WebInputEvent::RawKeyDown, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); } TEST_F(InputRouterImplTest, 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 SimulateWheelEvent(0, -10, 0, false); // enqueued, different modifiers // Explicitly verify that PhaseEnd isn't coalesced to avoid bugs like // https://crbug.com/154740. SimulateWheelEventWithPhase(WebMouseWheelEvent::PhaseEnded); // enqueued // Check that only the first event was sent. EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( InputMsg_HandleInputEvent::ID)); const WebInputEvent* input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(0)); ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type); const WebMouseWheelEvent* wheel_event = static_cast(input_event); EXPECT_EQ(0, wheel_event->deltaX); EXPECT_EQ(-5, wheel_event->deltaY); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // Check that the ACK sends the second message immediately. 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(); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( InputMsg_HandleInputEvent::ID)); input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(0)); ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type); wheel_event = static_cast(input_event); EXPECT_EQ(8, wheel_event->deltaX); EXPECT_EQ(-10 + -6, wheel_event->deltaY); // coalesced EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // Ack the second event (which had the third coalesced into it). SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( InputMsg_HandleInputEvent::ID)); input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(0)); ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type); wheel_event = static_cast(input_event); EXPECT_EQ(9, wheel_event->deltaX); EXPECT_EQ(-7, wheel_event->deltaY); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // Ack the fourth event. SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_TRUE( process_->sink().GetUniqueMessageMatching(InputMsg_HandleInputEvent::ID)); input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(0)); ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type); wheel_event = static_cast(input_event); EXPECT_EQ(0, wheel_event->deltaX); EXPECT_EQ(-10, wheel_event->deltaY); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // Ack the fifth event. SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_TRUE( process_->sink().GetUniqueMessageMatching(InputMsg_HandleInputEvent::ID)); input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(0)); ASSERT_EQ(WebInputEvent::MouseWheel, input_event->type); wheel_event = static_cast(input_event); EXPECT_EQ(0, wheel_event->deltaX); EXPECT_EQ(0, wheel_event->deltaY); EXPECT_EQ(WebMouseWheelEvent::PhaseEnded, wheel_event->phase); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // After the final ack, the queue should be empty. SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED); base::MessageLoop::current()->RunUntilIdle(); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); } // Tests that touch-events are queued properly. TEST_F(InputRouterImplTest, TouchEventQueue) { OnHasTouchEventHandlers(true); PressTouchPoint(1, 1); SendTouchEvent(); EXPECT_TRUE(client_->GetAndResetFilterEventCalled()); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_FALSE(TouchEventQueueEmpty()); // The second touch should not be sent since one is already in queue. MoveTouchPoint(0, 5, 5); SendTouchEvent(); EXPECT_FALSE(client_->GetAndResetFilterEventCalled()); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_FALSE(TouchEventQueueEmpty()); // Receive an ACK for the first touch-event. SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_FALSE(TouchEventQueueEmpty()); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(WebInputEvent::TouchStart, ack_handler_->acked_touch_event().event.type); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); SendInputEventACK(WebInputEvent::TouchMove, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_TRUE(TouchEventQueueEmpty()); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(WebInputEvent::TouchMove, ack_handler_->acked_touch_event().event.type); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); } // Tests that the touch-queue is emptied after a page stops listening for touch // events and the outstanding ack is received. TEST_F(InputRouterImplTest, TouchEventQueueFlush) { OnHasTouchEventHandlers(true); EXPECT_TRUE(client_->has_touch_handler()); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_TRUE(TouchEventQueueEmpty()); // Send a touch-press event. PressTouchPoint(1, 1); SendTouchEvent(); MoveTouchPoint(0, 2, 2); MoveTouchPoint(0, 3, 3); EXPECT_FALSE(TouchEventQueueEmpty()); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // The page stops listening for touch-events. Note that flushing is deferred // until the outstanding ack is received. OnHasTouchEventHandlers(false); EXPECT_FALSE(client_->has_touch_handler()); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_FALSE(TouchEventQueueEmpty()); // After the ack, the touch-event queue should be empty, and none of the // flushed touch-events should have been sent to the renderer. SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_TRUE(TouchEventQueueEmpty()); } #if defined(USE_AURA) // Tests that the acked events have correct state. (ui::Events are used only on // windows and aura) TEST_F(InputRouterImplTest, AckedTouchEventState) { input_router_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true)); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_TRUE(TouchEventQueueEmpty()); // Send a bunch of events, and make sure the ACKed events are correct. ScopedVector 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, GetSentMessageCountAndResetSink()); 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, 500, 500); SetTouchTimestamp(timestamp); SendTouchEvent(); EXPECT_FALSE(TouchEventQueueEmpty()); expected_events.push_back(new ui::TouchEvent(ui::ET_TOUCH_MOVED, gfx::Point(500, 500), 0, timestamp)); // Now press a second finger. timestamp += base::TimeDelta::FromSeconds(10); PressTouchPoint(2, 2); SetTouchTimestamp(timestamp); SendTouchEvent(); EXPECT_FALSE(TouchEventQueueEmpty()); 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_FALSE(TouchEventQueueEmpty()); 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], ack_handler_->acked_touch_event().event.type); ScopedVector acked; MakeUITouchEventsFromWebTouchEvents( ack_handler_->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_TRUE(TouchEventQueueEmpty()); EXPECT_EQ(0U, expected_events.size()); } #endif // defined(USE_AURA) TEST_F(InputRouterImplTest, 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_TRUE(process_->sink().GetUniqueMessageMatching( InputMsg_HandleInputEvent::ID)); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // 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. EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, ack_handler_->ack_state()); EXPECT_EQ(ack_handler_->acked_wheel_event().deltaY, -5); // Check that the second event was sent. EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( InputMsg_HandleInputEvent::ID)); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // Check that the correct unhandled wheel event was received. EXPECT_EQ(ack_handler_->acked_wheel_event().deltaY, -5); } TEST_F(InputRouterImplTest, TouchTypesIgnoringAck) { OnHasTouchEventHandlers(true); // Only acks for TouchCancel should always be ignored. ASSERT_FALSE(WebInputEventTraits::IgnoresAckDisposition( GetEventWithType(WebInputEvent::TouchStart))); ASSERT_FALSE(WebInputEventTraits::IgnoresAckDisposition( GetEventWithType(WebInputEvent::TouchMove))); ASSERT_FALSE(WebInputEventTraits::IgnoresAckDisposition( GetEventWithType(WebInputEvent::TouchEnd))); // Precede the TouchCancel with an appropriate TouchStart; PressTouchPoint(1, 1); SendTouchEvent(); SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED); ASSERT_EQ(1U, GetSentMessageCountAndResetSink()); ASSERT_EQ(1U, ack_handler_->GetAndResetAckCount()); ASSERT_EQ(0, client_->in_flight_event_count()); // The TouchCancel ack is always ignored. CancelTouchPoint(0); SendTouchEvent(); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0, client_->in_flight_event_count()); EXPECT_FALSE(HasPendingEvents()); SendInputEventACK(WebInputEvent::TouchCancel, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); EXPECT_FALSE(HasPendingEvents()); } TEST_F(InputRouterImplTest, GestureTypesIgnoringAck) { // We test every gesture type, ensuring that the stream of gestures is valid. const WebInputEvent::Type eventTypes[] = { WebInputEvent::GestureTapDown, WebInputEvent::GestureShowPress, WebInputEvent::GestureTapCancel, WebInputEvent::GestureScrollBegin, WebInputEvent::GestureFlingStart, WebInputEvent::GestureFlingCancel, WebInputEvent::GestureTapDown, WebInputEvent::GestureTap, WebInputEvent::GestureTapDown, WebInputEvent::GestureLongPress, WebInputEvent::GestureTapCancel, WebInputEvent::GestureLongTap, WebInputEvent::GestureTapDown, WebInputEvent::GestureTapUnconfirmed, WebInputEvent::GestureTapCancel, WebInputEvent::GestureTapDown, WebInputEvent::GestureDoubleTap, WebInputEvent::GestureTapDown, WebInputEvent::GestureTapCancel, WebInputEvent::GestureTwoFingerTap, WebInputEvent::GestureTapDown, WebInputEvent::GestureTapCancel, WebInputEvent::GestureScrollBegin, WebInputEvent::GestureScrollUpdate, WebInputEvent::GesturePinchBegin, WebInputEvent::GesturePinchUpdate, WebInputEvent::GesturePinchEnd, WebInputEvent::GestureScrollEnd}; for (size_t i = 0; i < arraysize(eventTypes); ++i) { WebInputEvent::Type type = eventTypes[i]; if (!WebInputEventTraits::IgnoresAckDisposition(GetEventWithType(type))) { SimulateGestureEvent(type, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(1, client_->in_flight_event_count()); EXPECT_TRUE(HasPendingEvents()); SendInputEventACK(type, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0, client_->in_flight_event_count()); EXPECT_FALSE(HasPendingEvents()); continue; } SimulateGestureEvent(type, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0, client_->in_flight_event_count()); EXPECT_FALSE(HasPendingEvents()); } } TEST_F(InputRouterImplTest, MouseTypesIgnoringAck) { int start_type = static_cast(WebInputEvent::MouseDown); int end_type = static_cast(WebInputEvent::ContextMenu); ASSERT_LT(start_type, end_type); for (int i = start_type; i <= end_type; ++i) { WebInputEvent::Type type = static_cast(i); int expected_in_flight_event_count = WebInputEventTraits::IgnoresAckDisposition(GetEventWithType(type)) ? 0 : 1; // Note: Mouse event acks are never forwarded to the ack handler, so the key // result here is that ignored ack types don't affect the in-flight count. SimulateMouseEvent(type, 0, 0); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(expected_in_flight_event_count, client_->in_flight_event_count()); if (expected_in_flight_event_count) { SendInputEventACK(type, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0, client_->in_flight_event_count()); } } } // Guard against breaking changes to the list of ignored event ack types in // |WebInputEventTraits::IgnoresAckDisposition|. TEST_F(InputRouterImplTest, RequiredEventAckTypes) { const WebInputEvent::Type kRequiredEventAckTypes[] = { WebInputEvent::MouseMove, WebInputEvent::MouseWheel, WebInputEvent::RawKeyDown, WebInputEvent::KeyDown, WebInputEvent::KeyUp, WebInputEvent::Char, WebInputEvent::GestureScrollUpdate, WebInputEvent::GestureFlingStart, WebInputEvent::GestureFlingCancel, WebInputEvent::GesturePinchUpdate, WebInputEvent::TouchStart, WebInputEvent::TouchMove }; for (size_t i = 0; i < arraysize(kRequiredEventAckTypes); ++i) { const WebInputEvent::Type required_ack_type = kRequiredEventAckTypes[i]; EXPECT_FALSE(WebInputEventTraits::IgnoresAckDisposition( GetEventWithType(required_ack_type))); } } // Test that GestureShowPress, GestureTapDown and GestureTapCancel events don't // wait for ACKs. TEST_F(InputRouterImplTest, GestureTypesIgnoringAckInterleaved) { // Interleave a few events that do and do not ignore acks, ensuring that // ack-ignoring events aren't dispatched until all prior events which observe // their ack disposition have been dispatched. SimulateGestureEvent(WebInputEvent::GestureScrollBegin, blink::WebGestureDeviceTouchscreen); ASSERT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0, client_->in_flight_event_count()); SimulateGestureEvent(WebInputEvent::GestureScrollUpdate, blink::WebGestureDeviceTouchscreen); ASSERT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(1, client_->in_flight_event_count()); SimulateGestureEvent(WebInputEvent::GestureTapDown, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(1, client_->in_flight_event_count()); SimulateGestureEvent(WebInputEvent::GestureScrollUpdate, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); SimulateGestureEvent(WebInputEvent::GestureShowPress, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); SimulateGestureEvent(WebInputEvent::GestureScrollUpdate, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); SimulateGestureEvent(WebInputEvent::GestureTapCancel, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); // Now ack each ack-respecting event. Ack-ignoring events should not be // dispatched until all prior events which observe ack disposition have been // fired, at which point they should be sent immediately. They should also // have no effect on the in-flight event count. SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(2U, GetSentMessageCountAndResetSink()); EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(1, client_->in_flight_event_count()); SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(2U, GetSentMessageCountAndResetSink()); EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(1, client_->in_flight_event_count()); SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0, client_->in_flight_event_count()); } // Test that GestureShowPress events don't get out of order due to // ignoring their acks. TEST_F(InputRouterImplTest, GestureShowPressIsInOrder) { SimulateGestureEvent(WebInputEvent::GestureScrollBegin, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); // GesturePinchBegin ignores its ack. SimulateGestureEvent(WebInputEvent::GesturePinchBegin, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); // GesturePinchUpdate waits for an ack. // This also verifies that GesturePinchUpdates for touchscreen are sent // to the renderer (in contrast to the TrackpadPinchUpdate test). SimulateGestureEvent(WebInputEvent::GesturePinchUpdate, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); SimulateGestureEvent(WebInputEvent::GestureShowPress, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); // The ShowPress, though it ignores ack, is still stuck in the queue // behind the PinchUpdate which requires an ack. EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); SimulateGestureEvent(WebInputEvent::GestureShowPress, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); // ShowPress has entered the queue. EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); SendInputEventACK(WebInputEvent::GesturePinchUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); // Now that the Tap has been ACKed, the ShowPress events should receive // synthetic acks, and fire immediately. EXPECT_EQ(2U, GetSentMessageCountAndResetSink()); EXPECT_EQ(3U, ack_handler_->GetAndResetAckCount()); } // Test that touch ack timeout behavior is properly toggled by view update flags // and allowed touch actions. TEST_F(InputRouterImplTest, TouchAckTimeoutConfigured) { const int timeout_ms = 1; SetUpForTouchAckTimeoutTest(timeout_ms); ASSERT_TRUE(TouchEventTimeoutEnabled()); // Verify that the touch ack timeout fires upon the delayed ack. PressTouchPoint(1, 1); SendTouchEvent(); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); RunTasksAndWait(base::TimeDelta::FromMilliseconds(timeout_ms + 1)); // The timed-out event should have been ack'ed. EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); // Ack'ing the timed-out event should fire a TouchCancel. SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // The remainder of the touch sequence should be dropped. ReleaseTouchPoint(0); SendTouchEvent(); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); ASSERT_TRUE(TouchEventTimeoutEnabled()); // A fixed page scale or mobile viewport should disable the touch timeout. input_router()->OnViewUpdated(InputRouter::FIXED_PAGE_SCALE); EXPECT_FALSE(TouchEventTimeoutEnabled()); input_router()->OnViewUpdated(InputRouter::VIEW_FLAGS_NONE); EXPECT_TRUE(TouchEventTimeoutEnabled()); input_router()->OnViewUpdated(InputRouter::MOBILE_VIEWPORT); EXPECT_FALSE(TouchEventTimeoutEnabled()); input_router()->OnViewUpdated(InputRouter::MOBILE_VIEWPORT | InputRouter::FIXED_PAGE_SCALE); EXPECT_FALSE(TouchEventTimeoutEnabled()); input_router()->OnViewUpdated(InputRouter::VIEW_FLAGS_NONE); EXPECT_TRUE(TouchEventTimeoutEnabled()); // TOUCH_ACTION_NONE (and no other touch-action) should disable the timeout. OnHasTouchEventHandlers(true); PressTouchPoint(1, 1); SendTouchEvent(); OnSetTouchAction(TOUCH_ACTION_PAN_Y); EXPECT_TRUE(TouchEventTimeoutEnabled()); ReleaseTouchPoint(0); SendTouchEvent(); SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED); SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED); PressTouchPoint(1, 1); SendTouchEvent(); OnSetTouchAction(TOUCH_ACTION_NONE); EXPECT_FALSE(TouchEventTimeoutEnabled()); ReleaseTouchPoint(0); SendTouchEvent(); SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED); SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED); // As the touch-action is reset by a new touch sequence, the timeout behavior // should be restored. PressTouchPoint(1, 1); SendTouchEvent(); EXPECT_TRUE(TouchEventTimeoutEnabled()); } // Test that a touch sequenced preceded by TOUCH_ACTION_NONE is not affected by // the touch timeout. TEST_F(InputRouterImplTest, TouchAckTimeoutDisabledForTouchSequenceAfterTouchActionNone) { const int timeout_ms = 1; SetUpForTouchAckTimeoutTest(timeout_ms); ASSERT_TRUE(TouchEventTimeoutEnabled()); OnHasTouchEventHandlers(true); // Start a touch sequence. PressTouchPoint(1, 1); SendTouchEvent(); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // TOUCH_ACTION_NONE should disable the timeout. OnSetTouchAction(TOUCH_ACTION_NONE); SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_FALSE(TouchEventTimeoutEnabled()); MoveTouchPoint(0, 1, 2); SendTouchEvent(); EXPECT_FALSE(TouchEventTimeoutEnabled()); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // Delay the move ack. The timeout should not fire. RunTasksAndWait(base::TimeDelta::FromMilliseconds(timeout_ms + 1)); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); // End the touch sequence. ReleaseTouchPoint(0); SendTouchEvent(); SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_FALSE(TouchEventTimeoutEnabled()); ack_handler_->GetAndResetAckCount(); GetSentMessageCountAndResetSink(); // Start another touch sequence. This should restore the touch timeout. PressTouchPoint(1, 1); SendTouchEvent(); EXPECT_TRUE(TouchEventTimeoutEnabled()); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); // Wait for the touch ack timeout to fire. RunTasksAndWait(base::TimeDelta::FromMilliseconds(timeout_ms + 1)); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); } // Test that TouchActionFilter::ResetTouchAction is called before the // first touch event for a touch sequence reaches the renderer. TEST_F(InputRouterImplTest, TouchActionResetBeforeEventReachesRenderer) { OnHasTouchEventHandlers(true); // Sequence 1. PressTouchPoint(1, 1); SendTouchEvent(); OnSetTouchAction(TOUCH_ACTION_NONE); MoveTouchPoint(0, 50, 50); SendTouchEvent(); ReleaseTouchPoint(0); SendTouchEvent(); // Sequence 2. PressTouchPoint(1, 1); SendTouchEvent(); MoveTouchPoint(0, 50, 50); SendTouchEvent(); ReleaseTouchPoint(0); SendTouchEvent(); SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED); SendInputEventACK(WebInputEvent::TouchMove, INPUT_EVENT_ACK_STATE_CONSUMED); // Ensure touch action is still none, as the next touch start hasn't been // acked yet. ScrollBegin and ScrollEnd don't require acks. EXPECT_EQ(3U, GetSentMessageCountAndResetSink()); SimulateGestureEvent(WebInputEvent::GestureScrollBegin, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); SimulateGestureEvent(WebInputEvent::GestureScrollEnd, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); // This allows the next touch sequence to start. SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED); // Ensure touch action has been set to auto, as a new touch sequence has // started. SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED); SendInputEventACK(WebInputEvent::TouchMove, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(3U, GetSentMessageCountAndResetSink()); SimulateGestureEvent(WebInputEvent::GestureScrollBegin, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); SimulateGestureEvent(WebInputEvent::GestureScrollEnd, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED); } // Test that TouchActionFilter::ResetTouchAction is called when a new touch // sequence has no consumer. TEST_F(InputRouterImplTest, TouchActionResetWhenTouchHasNoConsumer) { OnHasTouchEventHandlers(true); // Sequence 1. PressTouchPoint(1, 1); SendTouchEvent(); MoveTouchPoint(0, 50, 50); SendTouchEvent(); OnSetTouchAction(TOUCH_ACTION_NONE); SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED); SendInputEventACK(WebInputEvent::TouchMove, INPUT_EVENT_ACK_STATE_CONSUMED); ReleaseTouchPoint(0); SendTouchEvent(); // Sequence 2 PressTouchPoint(1, 1); SendTouchEvent(); MoveTouchPoint(0, 50, 50); SendTouchEvent(); ReleaseTouchPoint(0); SendTouchEvent(); // Ensure we have touch-action:none. ScrollBegin and ScrollEnd don't require // acks. EXPECT_EQ(3U, GetSentMessageCountAndResetSink()); SimulateGestureEvent(WebInputEvent::GestureScrollBegin, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); SimulateGestureEvent(WebInputEvent::GestureScrollEnd, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED); SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); // Ensure touch action has been set to auto, as the touch had no consumer. EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); SimulateGestureEvent(WebInputEvent::GestureScrollBegin, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); SimulateGestureEvent(WebInputEvent::GestureScrollEnd, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); } // Test that TouchActionFilter::ResetTouchAction is called when the touch // handler is removed. TEST_F(InputRouterImplTest, TouchActionResetWhenTouchHandlerRemoved) { // Touch sequence with touch handler. OnHasTouchEventHandlers(true); PressTouchPoint(1, 1); SendTouchEvent(); MoveTouchPoint(0, 50, 50); SendTouchEvent(); OnSetTouchAction(TOUCH_ACTION_NONE); ReleaseTouchPoint(0); SendTouchEvent(); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // Ensure we have touch-action:none, suppressing scroll events. SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); SendInputEventACK(WebInputEvent::TouchMove, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); SimulateGestureEvent(WebInputEvent::GestureScrollBegin, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); SimulateGestureEvent(WebInputEvent::GestureScrollEnd, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); // Sequence without a touch handler. Note that in this case, the view may not // necessarily forward touches to the router (as no touch handler exists). OnHasTouchEventHandlers(false); // Ensure touch action has been set to auto, as the touch handler has been // removed. SimulateGestureEvent(WebInputEvent::GestureScrollBegin, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); SimulateGestureEvent(WebInputEvent::GestureScrollEnd, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); } // Test that the double tap gesture depends on the touch action of the first // tap. TEST_F(InputRouterImplTest, DoubleTapGestureDependsOnFirstTap) { OnHasTouchEventHandlers(true); // Sequence 1. PressTouchPoint(1, 1); SendTouchEvent(); OnSetTouchAction(TOUCH_ACTION_NONE); SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED); ReleaseTouchPoint(0); SendTouchEvent(); // Sequence 2 PressTouchPoint(1, 1); SendTouchEvent(); // First tap. EXPECT_EQ(2U, GetSentMessageCountAndResetSink()); SimulateGestureEvent(WebInputEvent::GestureTapDown, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // The GestureTapUnconfirmed is converted into a tap, as the touch action is // none. SimulateGestureEvent(WebInputEvent::GestureTapUnconfirmed, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // This test will become invalid if GestureTap stops requiring an ack. ASSERT_FALSE(WebInputEventTraits::IgnoresAckDisposition( GetEventWithType(WebInputEvent::GestureTap))); EXPECT_EQ(2, client_->in_flight_event_count()); SendInputEventACK(WebInputEvent::GestureTap, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(1, client_->in_flight_event_count()); // This tap gesture is dropped, since the GestureTapUnconfirmed was turned // into a tap. SimulateGestureEvent(WebInputEvent::GestureTap, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED); SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); // Second Tap. EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); SimulateGestureEvent(WebInputEvent::GestureTapDown, blink::WebGestureDeviceTouchscreen); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // Although the touch-action is now auto, the double tap still won't be // dispatched, because the first tap occured when the touch-action was none. SimulateGestureEvent(WebInputEvent::GestureDoubleTap, blink::WebGestureDeviceTouchscreen); // This test will become invalid if GestureDoubleTap stops requiring an ack. ASSERT_FALSE(WebInputEventTraits::IgnoresAckDisposition( GetEventWithType(WebInputEvent::GestureDoubleTap))); EXPECT_EQ(1, client_->in_flight_event_count()); SendInputEventACK(WebInputEvent::GestureTap, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0, client_->in_flight_event_count()); } // Test that the router will call the client's |DidFlush| after all events have // been dispatched following a call to |Flush|. TEST_F(InputRouterImplTest, InputFlush) { EXPECT_FALSE(HasPendingEvents()); // Flushing an empty router should immediately trigger DidFlush. RequestNotificationWhenFlushed(); EXPECT_EQ(1U, GetAndResetDidFlushCount()); EXPECT_FALSE(HasPendingEvents()); // Queue a TouchStart. OnHasTouchEventHandlers(true); PressTouchPoint(1, 1); SendTouchEvent(); EXPECT_TRUE(HasPendingEvents()); // DidFlush should be called only after the event is ack'ed. RequestNotificationWhenFlushed(); EXPECT_EQ(0U, GetAndResetDidFlushCount()); SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(1U, GetAndResetDidFlushCount()); // Ensure different types of enqueued events will prevent the DidFlush call // until all such events have been fully dispatched. MoveTouchPoint(0, 50, 50); SendTouchEvent(); ASSERT_TRUE(HasPendingEvents()); SimulateGestureEvent(WebInputEvent::GestureScrollBegin, blink::WebGestureDeviceTouchscreen); SimulateGestureEvent(WebInputEvent::GestureScrollUpdate, blink::WebGestureDeviceTouchscreen); SimulateGestureEvent(WebInputEvent::GesturePinchBegin, blink::WebGestureDeviceTouchscreen); SimulateGestureEvent(WebInputEvent::GesturePinchUpdate, blink::WebGestureDeviceTouchscreen); RequestNotificationWhenFlushed(); EXPECT_EQ(0U, GetAndResetDidFlushCount()); // Repeated flush calls should have no effect. RequestNotificationWhenFlushed(); EXPECT_EQ(0U, GetAndResetDidFlushCount()); // There are still pending gestures. SendInputEventACK(WebInputEvent::TouchMove, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(0U, GetAndResetDidFlushCount()); EXPECT_TRUE(HasPendingEvents()); // One more gesture to go. SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, GetAndResetDidFlushCount()); EXPECT_TRUE(HasPendingEvents()); // The final ack'ed gesture should trigger the DidFlush. SendInputEventACK(WebInputEvent::GesturePinchUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(1U, GetAndResetDidFlushCount()); EXPECT_FALSE(HasPendingEvents()); } // Test that the router will call the client's |DidFlush| after all fling // animations have completed. TEST_F(InputRouterImplTest, InputFlushAfterFling) { EXPECT_FALSE(HasPendingEvents()); // Simulate a fling. SimulateGestureEvent(WebInputEvent::GestureScrollBegin, blink::WebGestureDeviceTouchscreen); SimulateGestureEvent(WebInputEvent::GestureFlingStart, blink::WebGestureDeviceTouchscreen); EXPECT_TRUE(HasPendingEvents()); // If the fling is unconsumed, the flush is complete. RequestNotificationWhenFlushed(); EXPECT_EQ(0U, GetAndResetDidFlushCount()); SimulateGestureEvent(WebInputEvent::GestureScrollBegin, blink::WebGestureDeviceTouchscreen); SendInputEventACK(WebInputEvent::GestureFlingStart, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_FALSE(HasPendingEvents()); EXPECT_EQ(1U, GetAndResetDidFlushCount()); // Simulate a second fling. SimulateGestureEvent(WebInputEvent::GestureFlingStart, blink::WebGestureDeviceTouchscreen); EXPECT_TRUE(HasPendingEvents()); // If the fling is consumed, the flush is complete only when the renderer // reports that is has ended. RequestNotificationWhenFlushed(); EXPECT_EQ(0U, GetAndResetDidFlushCount()); SendInputEventACK(WebInputEvent::GestureFlingStart, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_TRUE(HasPendingEvents()); EXPECT_EQ(0U, GetAndResetDidFlushCount()); // The fling end notification should signal that the router is flushed. input_router()->OnMessageReceived(InputHostMsg_DidStopFlinging(0)); EXPECT_EQ(1U, GetAndResetDidFlushCount()); } // Test that GesturePinchUpdate is handled specially for trackpad TEST_F(InputRouterImplTest, TouchpadPinchUpdate) { // GesturePinchUpdate for trackpad sends synthetic wheel events. // Note that the Touchscreen case is verified as NOT doing this as // part of the ShowPressIsInOrder test. SimulateGesturePinchUpdateEvent( 1.5f, 20, 25, 0, blink::WebGestureDeviceTouchpad); // Verify we actually sent a special wheel event to the renderer. const WebInputEvent* input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(0)); ASSERT_EQ(WebInputEvent::GesturePinchUpdate, input_event->type); const WebGestureEvent* gesture_event = static_cast(input_event); EXPECT_EQ(20, gesture_event->x); EXPECT_EQ(25, gesture_event->y); EXPECT_EQ(20, gesture_event->globalX); EXPECT_EQ(25, gesture_event->globalY); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // Indicate that the wheel event was unhandled. SendInputEventACK(WebInputEvent::GesturePinchUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); // Check that the correct unhandled pinch event was received. EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); ASSERT_EQ(WebInputEvent::GesturePinchUpdate, ack_handler_->ack_event_type()); EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, ack_handler_->ack_state()); EXPECT_EQ(1.5f, ack_handler_->acked_gesture_event().data.pinchUpdate.scale); EXPECT_EQ(0, client_->in_flight_event_count()); // Second a second pinch event. SimulateGesturePinchUpdateEvent( 0.3f, 20, 25, 0, blink::WebGestureDeviceTouchpad); input_event = GetInputEventFromMessage(*process_->sink().GetMessageAt(0)); ASSERT_EQ(WebInputEvent::GesturePinchUpdate, input_event->type); gesture_event = static_cast(input_event); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // Indicate that the wheel event was handled this time. SendInputEventACK(WebInputEvent::GesturePinchUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); // Check that the correct HANDLED pinch event was received. EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(WebInputEvent::GesturePinchUpdate, ack_handler_->ack_event_type()); EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, ack_handler_->ack_state()); EXPECT_FLOAT_EQ(0.3f, ack_handler_->acked_gesture_event().data.pinchUpdate.scale); } // Test proper handling of touchpad Gesture{Pinch,Scroll}Update sequences. TEST_F(InputRouterImplTest, TouchpadPinchAndScrollUpdate) { // The first scroll should be sent immediately. SimulateGestureScrollUpdateEvent(1.5f, 0.f, 0, blink::WebGestureDeviceTouchpad); SimulateGestureEvent(WebInputEvent::GestureScrollUpdate, blink::WebGestureDeviceTouchpad); ASSERT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1, client_->in_flight_event_count()); // Subsequent scroll and pinch events should remain queued, coalescing as // more trackpad events arrive. SimulateGesturePinchUpdateEvent(1.5f, 20, 25, 0, blink::WebGestureDeviceTouchpad); ASSERT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1, client_->in_flight_event_count()); SimulateGestureScrollUpdateEvent(1.5f, 1.5f, 0, blink::WebGestureDeviceTouchpad); ASSERT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1, client_->in_flight_event_count()); SimulateGesturePinchUpdateEvent(1.5f, 20, 25, 0, blink::WebGestureDeviceTouchpad); ASSERT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1, client_->in_flight_event_count()); SimulateGestureScrollUpdateEvent(0.f, 1.5f, 0, blink::WebGestureDeviceTouchpad); ASSERT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1, client_->in_flight_event_count()); // Ack'ing the first scroll should trigger both the coalesced scroll and the // coalesced pinch events (which is sent to the renderer as a wheel event). SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(2U, GetSentMessageCountAndResetSink()); EXPECT_EQ(2, client_->in_flight_event_count()); // Ack the second scroll. SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(1, client_->in_flight_event_count()); // Ack the wheel event. SendInputEventACK(WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0, client_->in_flight_event_count()); } // Test proper routing of overscroll notifications received either from // event acks or from |DidOverscroll| IPC messages. TEST_F(InputRouterImplTest, OverscrollDispatch) { DidOverscrollParams overscroll; overscroll.accumulated_overscroll = gfx::Vector2dF(-14, 14); overscroll.latest_overscroll_delta = gfx::Vector2dF(-7, 0); overscroll.current_fling_velocity = gfx::Vector2dF(-1, 0); input_router_->OnMessageReceived(InputHostMsg_DidOverscroll(0, overscroll)); DidOverscrollParams client_overscroll = client_->GetAndResetOverscroll(); EXPECT_EQ(overscroll.accumulated_overscroll, client_overscroll.accumulated_overscroll); EXPECT_EQ(overscroll.latest_overscroll_delta, client_overscroll.latest_overscroll_delta); EXPECT_EQ(overscroll.current_fling_velocity, client_overscroll.current_fling_velocity); DidOverscrollParams wheel_overscroll; wheel_overscroll.accumulated_overscroll = gfx::Vector2dF(7, -7); wheel_overscroll.latest_overscroll_delta = gfx::Vector2dF(3, 0); wheel_overscroll.current_fling_velocity = gfx::Vector2dF(1, 0); SimulateWheelEvent(3, 0, 0, false); InputHostMsg_HandleInputEvent_ACK_Params ack; ack.type = WebInputEvent::MouseWheel; ack.state = INPUT_EVENT_ACK_STATE_NOT_CONSUMED; ack.overscroll.reset(new DidOverscrollParams(wheel_overscroll)); input_router_->OnMessageReceived(InputHostMsg_HandleInputEvent_ACK(0, ack)); client_overscroll = client_->GetAndResetOverscroll(); EXPECT_EQ(wheel_overscroll.accumulated_overscroll, client_overscroll.accumulated_overscroll); EXPECT_EQ(wheel_overscroll.latest_overscroll_delta, client_overscroll.latest_overscroll_delta); EXPECT_EQ(wheel_overscroll.current_fling_velocity, client_overscroll.current_fling_velocity); } } // namespace content