| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/common/input/gesture_event_queue.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/location.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/task_environment.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "components/input/touchpad_tap_suppression_controller.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/input/synthetic_web_input_event_builders.h" |
| #include "third_party/blink/public/common/input/web_input_event.h" |
| #include "third_party/blink/public/mojom/input/input_event_result.mojom-shared.h" |
| #include "ui/events/blink/blink_features.h" |
| |
| using blink::WebGestureDevice; |
| using blink::WebGestureEvent; |
| using blink::WebInputEvent; |
| |
| namespace content { |
| |
| class GestureEventQueueTest : public testing::Test, |
| public GestureEventQueueClient, |
| public input::FlingControllerEventSenderClient, |
| public input::FlingControllerSchedulerClient { |
| public: |
| GestureEventQueueTest() |
| : task_environment_( |
| base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME, |
| base::test::SingleThreadTaskEnvironment::MainThreadType::UI), |
| acked_gesture_event_count_(0), |
| sent_gesture_event_count_(0) {} |
| |
| ~GestureEventQueueTest() override {} |
| |
| // testing::Test |
| void SetUp() override { |
| queue_ = |
| std::make_unique<GestureEventQueue>(this, this, this, DefaultConfig()); |
| } |
| |
| void TearDown() override { |
| // Process all pending tasks to avoid leaks. |
| RunUntilIdle(); |
| queue_.reset(); |
| } |
| |
| void SetUpForTapSuppression(int max_cancel_to_down_time_ms) { |
| GestureEventQueue::Config gesture_config; |
| gesture_config.fling_config.touchscreen_tap_suppression_config.enabled = |
| true; |
| gesture_config.fling_config.touchscreen_tap_suppression_config |
| .max_cancel_to_down_time = |
| base::Milliseconds(max_cancel_to_down_time_ms); |
| queue_ = |
| std::make_unique<GestureEventQueue>(this, this, this, gesture_config); |
| } |
| |
| // GestureEventQueueClient |
| void SendGestureEventImmediately( |
| const input::GestureEventWithLatencyInfo& event) override { |
| ++sent_gesture_event_count_; |
| if (sync_ack_result_) { |
| std::unique_ptr<blink::mojom::InputEventResultState> ack_result = |
| std::move(sync_ack_result_); |
| SendInputEventACK(event.event.GetType(), *ack_result); |
| } |
| } |
| |
| void OnGestureEventAck( |
| const input::GestureEventWithLatencyInfo& event, |
| blink::mojom::InputEventResultSource ack_source, |
| blink::mojom::InputEventResultState ack_result) override { |
| ++acked_gesture_event_count_; |
| last_acked_event_ = event.event; |
| if (sync_followup_event_) { |
| auto sync_followup_event = std::move(sync_followup_event_); |
| SimulateGestureEvent(*sync_followup_event); |
| } |
| } |
| |
| // FlingControllerEventSenderClient |
| void SendGeneratedWheelEvent( |
| const input::MouseWheelEventWithLatencyInfo& wheel_event) override {} |
| void SendGeneratedGestureScrollEvents( |
| const input::GestureEventWithLatencyInfo& gesture_event) override {} |
| gfx::Size GetRootWidgetViewportSize() override { |
| return gfx::Size(1920, 1080); |
| } |
| |
| // FlingControllerSchedulerClient |
| void ScheduleFlingProgress( |
| base::WeakPtr<input::FlingController> fling_controller) override {} |
| void DidStopFlingingOnBrowser( |
| base::WeakPtr<input::FlingController> fling_controller) override {} |
| bool NeedsBeginFrameForFlingProgress() override { return false; } |
| bool ShouldUseMobileFlingCurve() override { |
| #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| gfx::Vector2dF GetPixelsPerInch( |
| const gfx::PointF& position_in_screen) override { |
| return gfx::Vector2dF(input::kDefaultPixelsPerInch, |
| input::kDefaultPixelsPerInch); |
| } |
| |
| protected: |
| static GestureEventQueue::Config DefaultConfig() { |
| return GestureEventQueue::Config(); |
| } |
| |
| void SetUpForDebounce(int interval_ms) { |
| queue()->set_debounce_interval_time_ms_for_testing(interval_ms); |
| } |
| |
| void SimulateGestureEvent(const WebGestureEvent& gesture) { |
| input::GestureEventWithLatencyInfo gesture_event(gesture); |
| if (!queue()->PassToFlingController(gesture_event)) { |
| queue()->DebounceOrForwardEvent(gesture_event); |
| } |
| } |
| |
| void SimulateGestureEvent(WebInputEvent::Type type, |
| WebGestureDevice sourceDevice) { |
| SimulateGestureEvent( |
| blink::SyntheticWebGestureEventBuilder::Build(type, sourceDevice)); |
| } |
| |
| void SimulateGSEGeneratedByFlingController(WebGestureDevice sourceDevice) { |
| WebGestureEvent gesture_scroll_end = |
| blink::SyntheticWebGestureEventBuilder::Build( |
| WebInputEvent::Type::kGestureScrollEnd, sourceDevice); |
| gesture_scroll_end.data.scroll_end.generated_by_fling_controller = true; |
| SimulateGestureEvent(gesture_scroll_end); |
| } |
| |
| void SimulateGestureScrollUpdateEvent(float dX, float dY, int modifiers) { |
| SimulateGestureEvent( |
| blink::SyntheticWebGestureEventBuilder::BuildScrollUpdate( |
| dX, dY, modifiers, blink::WebGestureDevice::kTouchscreen)); |
| } |
| |
| void SimulateGesturePinchUpdateEvent(float scale, |
| float anchorX, |
| float anchorY, |
| int modifiers) { |
| SimulateGestureEvent( |
| blink::SyntheticWebGestureEventBuilder::BuildPinchUpdate( |
| scale, anchorX, anchorY, modifiers, |
| blink::WebGestureDevice::kTouchscreen)); |
| } |
| |
| void SimulateGestureFlingStartEvent(float velocityX, |
| float velocityY, |
| WebGestureDevice sourceDevice) { |
| SimulateGestureEvent(blink::SyntheticWebGestureEventBuilder::BuildFling( |
| velocityX, velocityY, sourceDevice)); |
| } |
| |
| void SendInputEventACK(WebInputEvent::Type type, |
| blink::mojom::InputEventResultState ack) { |
| queue()->ProcessGestureAck( |
| blink::mojom::InputEventResultSource::kCompositorThread, ack, type, |
| ui::LatencyInfo()); |
| } |
| |
| void RunUntilIdle() { task_environment_.RunUntilIdle(); } |
| void FastForwardBy(base::TimeDelta delay) { |
| task_environment_.FastForwardBy(delay); |
| } |
| |
| size_t GetAndResetSentGestureEventCount() { |
| size_t count = sent_gesture_event_count_; |
| sent_gesture_event_count_ = 0; |
| return count; |
| } |
| |
| size_t GetAndResetAckedGestureEventCount() { |
| size_t count = acked_gesture_event_count_; |
| acked_gesture_event_count_ = 0; |
| return count; |
| } |
| |
| const WebGestureEvent& last_acked_event() const { return last_acked_event_; } |
| |
| void set_synchronous_ack(blink::mojom::InputEventResultState ack_result) { |
| sync_ack_result_ = |
| std::make_unique<blink::mojom::InputEventResultState>(ack_result); |
| } |
| |
| void set_sync_followup_event(WebInputEvent::Type type, |
| WebGestureDevice sourceDevice) { |
| sync_followup_event_ = std::make_unique<WebGestureEvent>( |
| blink::SyntheticWebGestureEventBuilder::Build(type, sourceDevice)); |
| } |
| |
| unsigned GestureEventQueueSize() { |
| return queue()->sent_events_awaiting_ack_.size(); |
| } |
| |
| WebGestureEvent GestureEventSecondFromLastQueueEvent() { |
| return queue() |
| ->sent_events_awaiting_ack_.at(GestureEventQueueSize() - 2) |
| .event; |
| } |
| |
| WebGestureEvent GestureEventLastQueueEvent() { |
| return queue()->sent_events_awaiting_ack_.back().event; |
| } |
| |
| unsigned GestureEventDebouncingQueueSize() { |
| return queue()->debouncing_deferral_queue_.size(); |
| } |
| |
| WebGestureEvent GestureEventQueueEventAt(int i) { |
| return queue()->sent_events_awaiting_ack_.at(i).event; |
| } |
| |
| bool ScrollingInProgress() { return queue()->scrolling_in_progress_; } |
| |
| bool FlingInProgress() { return queue()->FlingInProgressForTest(); } |
| |
| GestureEventQueue* queue() const { return queue_.get(); } |
| |
| private: |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| std::unique_ptr<GestureEventQueue> queue_; |
| size_t acked_gesture_event_count_; |
| size_t sent_gesture_event_count_; |
| WebGestureEvent last_acked_event_; |
| std::unique_ptr<blink::mojom::InputEventResultState> sync_ack_result_; |
| std::unique_ptr<WebGestureEvent> sync_followup_event_; |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| class GestureEventQueueWithCompositorEventQueueTest |
| : public GestureEventQueueTest {}; |
| |
| // Tests a single event with an synchronous ack. |
| TEST_F(GestureEventQueueTest, SimpleSyncAck) { |
| set_synchronous_ack(blink::mojom::InputEventResultState::kConsumed); |
| SimulateGestureEvent(WebInputEvent::Type::kGestureTapDown, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(1U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(0U, GestureEventQueueSize()); |
| EXPECT_EQ(1U, GetAndResetAckedGestureEventCount()); |
| } |
| |
| // Tests an event with an synchronous ack which enqueues an additional event. |
| TEST_F(GestureEventQueueTest, SyncAckQueuesEvent) { |
| std::unique_ptr<WebGestureEvent> queued_event; |
| set_synchronous_ack(blink::mojom::InputEventResultState::kConsumed); |
| set_sync_followup_event(WebInputEvent::Type::kGestureShowPress, |
| blink::WebGestureDevice::kTouchscreen); |
| // This event enqueues the show press event. |
| SimulateGestureEvent(WebInputEvent::Type::kGestureTapDown, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(2U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(1U, GestureEventQueueSize()); |
| EXPECT_EQ(1U, GetAndResetAckedGestureEventCount()); |
| |
| SendInputEventACK(WebInputEvent::Type::kGestureShowPress, |
| blink::mojom::InputEventResultState::kConsumed); |
| EXPECT_EQ(0U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(0U, GestureEventQueueSize()); |
| EXPECT_EQ(1U, GetAndResetAckedGestureEventCount()); |
| } |
| |
| // Test that a GestureScrollEnd is deferred during the debounce interval, |
| // that Scrolls are not and that the deferred events are sent after that |
| // timer fires. |
| TEST_F(GestureEventQueueTest, DebounceDefersFollowingGestureEvents) { |
| SetUpForDebounce(3); |
| |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollUpdate, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(1U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(1U, GestureEventQueueSize()); |
| EXPECT_EQ(0U, GestureEventDebouncingQueueSize()); |
| EXPECT_TRUE(ScrollingInProgress()); |
| |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollUpdate, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(1U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(2U, GestureEventQueueSize()); |
| EXPECT_EQ(0U, GestureEventDebouncingQueueSize()); |
| EXPECT_TRUE(ScrollingInProgress()); |
| |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollEnd, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(0U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(2U, GestureEventQueueSize()); |
| EXPECT_EQ(1U, GestureEventDebouncingQueueSize()); |
| |
| SimulateGestureEvent(WebInputEvent::Type::kGestureTapDown, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(0U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(2U, GestureEventQueueSize()); |
| EXPECT_EQ(2U, GestureEventDebouncingQueueSize()); |
| |
| FastForwardBy(base::Milliseconds(5)); |
| |
| // The deferred events are correctly queued in coalescing queue. |
| EXPECT_EQ(2U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(4U, GestureEventQueueSize()); |
| EXPECT_EQ(0U, GestureEventDebouncingQueueSize()); |
| EXPECT_FALSE(ScrollingInProgress()); |
| |
| // Verify that the coalescing queue contains the correct events. |
| WebInputEvent::Type expected[] = {WebInputEvent::Type::kGestureScrollUpdate, |
| WebInputEvent::Type::kGestureScrollUpdate, |
| WebInputEvent::Type::kGestureScrollEnd}; |
| |
| for (unsigned i = 0; i < sizeof(expected) / sizeof(WebInputEvent::Type); |
| i++) { |
| WebGestureEvent merged_event = GestureEventQueueEventAt(i); |
| EXPECT_EQ(expected[i], merged_event.GetType()); |
| } |
| } |
| |
| // Tests that GSE events generated by the fling controller are forwarded to the |
| // renderer instead of getting pushed back to the deboucing_deferral_queue_. In |
| // this case the following GSB won't get deferred either. |
| TEST_F(GestureEventQueueTest, |
| DebounceDoesNotDeferGSEsGeneratedByFlingController) { |
| SetUpForDebounce(3); |
| |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollUpdate, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(1U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(1U, GestureEventQueueSize()); |
| EXPECT_EQ(0U, GestureEventDebouncingQueueSize()); |
| EXPECT_TRUE(ScrollingInProgress()); |
| |
| SimulateGSEGeneratedByFlingController(blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(1U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(2U, GestureEventQueueSize()); |
| EXPECT_EQ(0U, GestureEventDebouncingQueueSize()); |
| EXPECT_FALSE(ScrollingInProgress()); |
| |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollBegin, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(1U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(3U, GestureEventQueueSize()); |
| EXPECT_EQ(0U, GestureEventDebouncingQueueSize()); |
| |
| // Verify that the coalescing queue contains the correct events. |
| WebInputEvent::Type expected[] = {WebInputEvent::Type::kGestureScrollUpdate, |
| WebInputEvent::Type::kGestureScrollEnd, |
| WebInputEvent::Type::kGestureScrollBegin}; |
| |
| for (unsigned i = 0; i < sizeof(expected) / sizeof(WebInputEvent::Type); |
| i++) { |
| WebGestureEvent merged_event = GestureEventQueueEventAt(i); |
| EXPECT_EQ(expected[i], merged_event.GetType()); |
| } |
| } |
| |
| TEST_F(GestureEventQueueTest, DebounceDefersGSBIfPreviousGSEDeferred) { |
| SetUpForDebounce(3); |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollUpdate, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(1U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(1U, GestureEventQueueSize()); |
| EXPECT_EQ(0U, GestureEventDebouncingQueueSize()); |
| EXPECT_TRUE(ScrollingInProgress()); |
| |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollEnd, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(0U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(1U, GestureEventQueueSize()); |
| EXPECT_EQ(1U, GestureEventDebouncingQueueSize()); |
| EXPECT_TRUE(ScrollingInProgress()); |
| |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollBegin, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(0U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(1U, GestureEventQueueSize()); |
| EXPECT_EQ(2U, GestureEventDebouncingQueueSize()); |
| EXPECT_TRUE(ScrollingInProgress()); |
| } |
| |
| TEST_F(GestureEventQueueTest, DebounceDefersGSBIfPreviousGSEDropped) { |
| SetUpForDebounce(3); |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollUpdate, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(1U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(1U, GestureEventQueueSize()); |
| EXPECT_EQ(0U, GestureEventDebouncingQueueSize()); |
| EXPECT_TRUE(ScrollingInProgress()); |
| |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollEnd, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(0U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(1U, GestureEventQueueSize()); |
| EXPECT_EQ(1U, GestureEventDebouncingQueueSize()); |
| |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollUpdate, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(1U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(2U, GestureEventQueueSize()); |
| EXPECT_EQ(0U, GestureEventDebouncingQueueSize()); |
| EXPECT_TRUE(ScrollingInProgress()); |
| |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollBegin, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(0U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(2U, GestureEventQueueSize()); |
| EXPECT_EQ(1U, GestureEventDebouncingQueueSize()); |
| EXPECT_TRUE(ScrollingInProgress()); |
| |
| // Verify that the coalescing queue contains the correct events. |
| WebInputEvent::Type expected[] = {WebInputEvent::Type::kGestureScrollUpdate, |
| WebInputEvent::Type::kGestureScrollUpdate}; |
| |
| for (unsigned i = 0; i < sizeof(expected) / sizeof(WebInputEvent::Type); |
| i++) { |
| WebGestureEvent merged_event = GestureEventQueueEventAt(i); |
| EXPECT_EQ(expected[i], merged_event.GetType()); |
| } |
| } |
| |
| // 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(GestureEventQueueTest, DebounceDropsDeferredEvents) { |
| SetUpForDebounce(3); |
| |
| EXPECT_FALSE(ScrollingInProgress()); |
| |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollUpdate, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(1U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(1U, GestureEventQueueSize()); |
| EXPECT_EQ(0U, GestureEventDebouncingQueueSize()); |
| EXPECT_TRUE(ScrollingInProgress()); |
| |
| // This event should get discarded. |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollEnd, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(0U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(1U, GestureEventQueueSize()); |
| EXPECT_EQ(1U, GestureEventDebouncingQueueSize()); |
| |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollUpdate, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(1U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(2U, GestureEventQueueSize()); |
| EXPECT_EQ(0U, GestureEventDebouncingQueueSize()); |
| EXPECT_TRUE(ScrollingInProgress()); |
| |
| // Verify that the coalescing queue contains the correct events. |
| WebInputEvent::Type expected[] = {WebInputEvent::Type::kGestureScrollUpdate, |
| WebInputEvent::Type::kGestureScrollUpdate}; |
| |
| for (unsigned i = 0; i < sizeof(expected) / sizeof(WebInputEvent::Type); |
| i++) { |
| WebGestureEvent merged_event = GestureEventQueueEventAt(i); |
| EXPECT_EQ(expected[i], merged_event.GetType()); |
| } |
| } |
| |
| // Test that the fling cancelling tap down event and its following tap get |
| // suppressed when tap suppression is enabled. |
| TEST_F(GestureEventQueueTest, TapGetsSuppressedAfterTapDownCancelsFling) { |
| SetUpForTapSuppression(400); |
| // The velocity of the event must be large enough to make sure that the fling |
| // is still active when the tap down happens. |
| SimulateGestureFlingStartEvent(0, -1000, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_TRUE(FlingInProgress()); |
| // The fling start event is not sent to the renderer. |
| EXPECT_EQ(0U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedGestureEventCount()); |
| RunUntilIdle(); |
| |
| // Simulate a fling cancel event before sending a gesture tap down event. The |
| // fling cancel event is not sent to the renderer. |
| SimulateGestureEvent(WebInputEvent::Type::kGestureFlingCancel, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(0U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(0U, GestureEventQueueSize()); |
| RunUntilIdle(); |
| |
| // Simulate a fling cancelling tap down. The tap down must get suppressed |
| // since the fling cancel event is processed by the fling controller. |
| SimulateGestureEvent(WebInputEvent::Type::kGestureTapDown, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(0U, GestureEventQueueSize()); |
| |
| // The tap event must get suppressed since its corresponding tap down event |
| // is suppressed. |
| SimulateGestureEvent(WebInputEvent::Type::kGestureTap, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(0U, GestureEventQueueSize()); |
| } |
| |
| TEST_F(GestureEventQueueWithCompositorEventQueueTest, |
| PreserveOrderWithOutOfOrderAck) { |
| // Simulate a scroll sequence, events should be ACKed in original order. |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollBegin, |
| blink::WebGestureDevice::kTouchscreen); |
| SimulateGestureScrollUpdateEvent(8, -4, 1); |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollEnd, |
| blink::WebGestureDevice::kTouchscreen); |
| |
| // All events should have been sent. |
| EXPECT_EQ(3U, GetAndResetSentGestureEventCount()); |
| |
| // Simulate GSB ACK. |
| SendInputEventACK(WebInputEvent::Type::kGestureScrollBegin, |
| blink::mojom::InputEventResultState::kConsumed); |
| EXPECT_EQ(WebInputEvent::Type::kGestureScrollBegin, |
| last_acked_event().GetType()); |
| EXPECT_EQ(2U, GestureEventQueueSize()); |
| |
| // Simulate GSE ACK first since it's usually dispatched non-blocking. |
| SendInputEventACK(WebInputEvent::Type::kGestureScrollEnd, |
| blink::mojom::InputEventResultState::kConsumed); |
| // GSE ACK will be cached in GestureEventQueue since we haven't ACKed GSU yet. |
| EXPECT_EQ(WebInputEvent::Type::kGestureScrollBegin, |
| last_acked_event().GetType()); |
| EXPECT_EQ(2U, GestureEventQueueSize()); |
| |
| // Simulate GSU ACK. |
| SendInputEventACK(WebInputEvent::Type::kGestureScrollUpdate, |
| blink::mojom::InputEventResultState::kConsumed); |
| // Both ACKs should be released in order. |
| EXPECT_EQ(WebInputEvent::Type::kGestureScrollEnd, |
| last_acked_event().GetType()); |
| EXPECT_EQ(0U, GestureEventQueueSize()); |
| } |
| |
| TEST_F(GestureEventQueueWithCompositorEventQueueTest, |
| MultipleGesturesInFlight) { |
| // Simulate a pinch sequence, events should be forwarded immediately. |
| SimulateGestureEvent(WebInputEvent::Type::kGestureScrollBegin, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(1U, GetAndResetSentGestureEventCount()); |
| SimulateGestureEvent(WebInputEvent::Type::kGesturePinchBegin, |
| blink::WebGestureDevice::kTouchscreen); |
| EXPECT_EQ(1U, GetAndResetSentGestureEventCount()); |
| |
| SimulateGestureScrollUpdateEvent(8, -4, 1); |
| EXPECT_EQ(1U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(3U, GestureEventQueueSize()); |
| EXPECT_EQ(WebInputEvent::Type::kGestureScrollUpdate, |
| GestureEventLastQueueEvent().GetType()); |
| |
| // Simulate 2 pinch update events. |
| SimulateGesturePinchUpdateEvent(1.5, 60, 60, 1); |
| EXPECT_EQ(4U, GestureEventQueueSize()); |
| SimulateGesturePinchUpdateEvent(1.3, 60, 60, 1); |
| // Events should be forwarded immediately instead of being coalesced. |
| EXPECT_EQ(5U, GestureEventQueueSize()); |
| EXPECT_EQ(2U, GetAndResetSentGestureEventCount()); |
| EXPECT_EQ(WebInputEvent::Type::kGesturePinchUpdate, |
| GestureEventLastQueueEvent().GetType()); |
| |
| SendInputEventACK(WebInputEvent::Type::kGestureScrollBegin, |
| blink::mojom::InputEventResultState::kConsumed); |
| EXPECT_EQ(4U, GestureEventQueueSize()); |
| |
| SendInputEventACK(WebInputEvent::Type::kGesturePinchBegin, |
| blink::mojom::InputEventResultState::kConsumed); |
| SendInputEventACK(WebInputEvent::Type::kGestureScrollUpdate, |
| blink::mojom::InputEventResultState::kConsumed); |
| |
| // Both GestureScrollUpdate and GesturePinchUpdate should have been sent. |
| EXPECT_EQ(WebInputEvent::Type::kGestureScrollUpdate, |
| last_acked_event().GetType()); |
| EXPECT_EQ(2U, GestureEventQueueSize()); |
| EXPECT_EQ(0U, GetAndResetSentGestureEventCount()); |
| |
| // Ack the last 2 GesturePinchUpdate events. |
| SendInputEventACK(WebInputEvent::Type::kGesturePinchUpdate, |
| blink::mojom::InputEventResultState::kConsumed); |
| SendInputEventACK(WebInputEvent::Type::kGesturePinchUpdate, |
| blink::mojom::InputEventResultState::kConsumed); |
| EXPECT_EQ(WebInputEvent::Type::kGesturePinchUpdate, |
| last_acked_event().GetType()); |
| EXPECT_EQ(0U, GestureEventQueueSize()); |
| EXPECT_EQ(0U, GetAndResetSentGestureEventCount()); |
| } |
| |
| } // namespace content |