|  | // 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. | 
|  |  | 
|  | #ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_GESTURE_EVENT_QUEUE_H_ | 
|  | #define CONTENT_BROWSER_RENDERER_HOST_INPUT_GESTURE_EVENT_QUEUE_H_ | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include "base/containers/circular_deque.h" | 
|  | #include "base/memory/raw_ptr.h" | 
|  | #include "base/time/time.h" | 
|  | #include "base/timer/timer.h" | 
|  | #include "content/browser/renderer_host/event_with_latency_info.h" | 
|  | #include "content/browser/renderer_host/input/fling_controller.h" | 
|  | #include "content/common/content_export.h" | 
|  | #include "third_party/abseil-cpp/absl/types/optional.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 "third_party/blink/public/mojom/input/input_handler.mojom.h" | 
|  |  | 
|  | namespace content { | 
|  | class GestureEventQueueTest; | 
|  | class MockRenderWidgetHost; | 
|  |  | 
|  | // Interface with which the GestureEventQueue can forward gesture events, and | 
|  | // dispatch gesture event responses. | 
|  | class CONTENT_EXPORT GestureEventQueueClient { | 
|  | public: | 
|  | virtual ~GestureEventQueueClient() {} | 
|  |  | 
|  | virtual void SendGestureEventImmediately( | 
|  | const GestureEventWithLatencyInfo& event) = 0; | 
|  |  | 
|  | virtual void OnGestureEventAck( | 
|  | const GestureEventWithLatencyInfo& event, | 
|  | blink::mojom::InputEventResultSource ack_source, | 
|  | blink::mojom::InputEventResultState ack_result, | 
|  | blink::mojom::ScrollResultDataPtr scroll_result_data) = 0; | 
|  | }; | 
|  |  | 
|  | // Despite its name, this class isn't so much one queue as it is a collection | 
|  | // of queues and filters. This class applies logic to determine if an event | 
|  | // should be queued, filtered altogether, or sent immediately; it tracks sent | 
|  | // events and ACKs them to the clilent in the order they were dispatched. This | 
|  | // class applies a series of filters and queues for various scenarios: | 
|  | // 1. The sequence is filtered for bounces. A bounce is when the finger lifts | 
|  | //    from the screen briefly during an in-progress scroll. If this happens, | 
|  | //    non-GestureScrollUpdate events are queued until the de-bounce interval | 
|  | //    passes or another GestureScrollUpdate event occurs. | 
|  | // 2. Unnecessary GestureFlingCancel events are filtered by fling controller. | 
|  | //    These are GestureFlingCancels that have no corresponding GestureFlingStart | 
|  | //    in the queue. GestureFlingStarts are also filtered and translated to | 
|  | //    scroll gestures by the fling controller. | 
|  | // 3. Taps immediately after a GestureFlingCancel (caused by the same tap) are | 
|  | //    filtered by fling controller. | 
|  | // 4. Gesture events are queued while we're waiting to determine the allowed | 
|  | //    touch actions. | 
|  | // Sent events are kept in a queue until a response from the renderer is | 
|  | // received for that event. The client is notified of ACKs in the order the | 
|  | // events were sent, not ACK'd. This means an ACK'd event that was sent after | 
|  | // an event still awaiting an ACK won't notify the client until the earlier | 
|  | // event is ACK'd. | 
|  | class CONTENT_EXPORT GestureEventQueue { | 
|  | public: | 
|  | using GestureQueue = base::circular_deque<GestureEventWithLatencyInfo>; | 
|  | struct CONTENT_EXPORT Config { | 
|  | Config(); | 
|  |  | 
|  | FlingController::Config fling_config; | 
|  |  | 
|  | // Determines whether non-scroll gesture events are "debounced" during an | 
|  | // active scroll sequence, suppressing brief scroll interruptions. | 
|  | // Zero by default (disabled). | 
|  | base::TimeDelta debounce_interval; | 
|  | }; | 
|  |  | 
|  | // Both |client| and |touchpad_client| must outlive the GestureEventQueue. | 
|  | GestureEventQueue(GestureEventQueueClient* client, | 
|  | FlingControllerEventSenderClient* fling_event_sender_client, | 
|  | FlingControllerSchedulerClient* fling_scheduler_client, | 
|  | const Config& config); | 
|  |  | 
|  | GestureEventQueue(const GestureEventQueue&) = delete; | 
|  | GestureEventQueue& operator=(const GestureEventQueue&) = delete; | 
|  |  | 
|  | ~GestureEventQueue(); | 
|  |  | 
|  | // Allow the fling controller to observe the gesture event. Returns true if | 
|  | // the event was filtered by the fling controller and shouldn't be further | 
|  | // forwarded. | 
|  | bool PassToFlingController(const GestureEventWithLatencyInfo&); | 
|  |  | 
|  | // Filter the event for debouncing or forward it to the renderer. Returns | 
|  | // true if the event was forwarded, false if was filtered for debouncing. | 
|  | bool DebounceOrForwardEvent(const GestureEventWithLatencyInfo&); | 
|  |  | 
|  | // Adds a gesture to the queue of events that needs to be deferred until the | 
|  | // touch action is known. | 
|  | void QueueDeferredEvents(const GestureEventWithLatencyInfo&); | 
|  |  | 
|  | // Returns events in the |deferred_gesture_queue_| and empty the queue. | 
|  | GestureQueue TakeDeferredEvents(); | 
|  |  | 
|  | // Indicates that the caller has received an acknowledgement from the renderer | 
|  | // with state |ack_result| and event |type|. | 
|  | void ProcessGestureAck(blink::mojom::InputEventResultSource ack_source, | 
|  | blink::mojom::InputEventResultState ack_result, | 
|  | blink::WebInputEvent::Type type, | 
|  | const ui::LatencyInfo& latency, | 
|  | blink::mojom::ScrollResultDataPtr scroll_result_data); | 
|  |  | 
|  | // Returns the |TouchpadTapSuppressionController| instance. | 
|  | TouchpadTapSuppressionController* GetTouchpadTapSuppressionController(); | 
|  |  | 
|  | // Sends the gesture event to the renderer. Stores the sent event for when | 
|  | // the renderer replies with an ACK. | 
|  | void ForwardGestureEvent(const GestureEventWithLatencyInfo& gesture_event); | 
|  |  | 
|  | bool empty() const { | 
|  | return sent_events_awaiting_ack_.empty() && | 
|  | debouncing_deferral_queue_.empty(); | 
|  | } | 
|  |  | 
|  | // Calls |fling_controller_.StopFling| to halt an active fling if such exists. | 
|  | void StopFling(); | 
|  |  | 
|  | gfx::Vector2dF CurrentFlingVelocity() const; | 
|  |  | 
|  | void set_debounce_interval_time_ms_for_testing(int interval_ms) { | 
|  | debounce_interval_ = base::Milliseconds(interval_ms); | 
|  | } | 
|  |  | 
|  | // TODO(nburris): Wheel event acks shouldn't really go through the gesture | 
|  | // event queue, but this is needed to pass them through to the | 
|  | // FlingController. The FlingController should probably be owned by the | 
|  | // InputRouter instead. | 
|  | void OnWheelEventAck(const MouseWheelEventWithLatencyInfo& event, | 
|  | blink::mojom::InputEventResultSource ack_source, | 
|  | blink::mojom::InputEventResultState ack_result); | 
|  |  | 
|  | bool IsFlingActiveForTest() { return FlingInProgressForTest(); } | 
|  |  | 
|  | private: | 
|  | friend class GestureEventQueueTest; | 
|  | friend class MockRenderWidgetHost; | 
|  |  | 
|  | class GestureEventWithLatencyInfoAckStateAndScrollResultData | 
|  | : public GestureEventWithLatencyInfo { | 
|  | public: | 
|  | GestureEventWithLatencyInfoAckStateAndScrollResultData( | 
|  | const GestureEventWithLatencyInfo&); | 
|  | ~GestureEventWithLatencyInfoAckStateAndScrollResultData() = default; | 
|  | blink::mojom::InputEventResultState ack_state() const { return ack_state_; } | 
|  | void set_ack_info(blink::mojom::InputEventResultSource source, | 
|  | blink::mojom::InputEventResultState state) { | 
|  | ack_source_ = source; | 
|  | ack_state_ = state; | 
|  | } | 
|  | blink::mojom::InputEventResultSource ack_source() const { | 
|  | return ack_source_; | 
|  | } | 
|  | void set_scroll_result_data( | 
|  | blink::mojom::ScrollResultDataPtr scroll_result_data) { | 
|  | // Creating a new instance and setting the field(s) explicitly because | 
|  | // having blink::mojom::ScrollResultDataPtr as a field makes this class | 
|  | // move-only and causes issues pushing into and removing from | 
|  | // sent_events_awaiting_ack_. | 
|  | // TODO(sinansahin): This class can probably be refactored to work with | 
|  | // being move-only. | 
|  | scroll_result_data_ = blink::mojom::ScrollResultData( | 
|  | scroll_result_data ? scroll_result_data->root_scroll_offset | 
|  | : absl::nullopt); | 
|  | } | 
|  | const blink::mojom::ScrollResultData& scroll_result_data() { | 
|  | return scroll_result_data_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | blink::mojom::InputEventResultSource ack_source_ = | 
|  | blink::mojom::InputEventResultSource::kUnknown; | 
|  | blink::mojom::InputEventResultState ack_state_ = | 
|  | blink::mojom::InputEventResultState::kUnknown; | 
|  | blink::mojom::ScrollResultData scroll_result_data_; | 
|  | }; | 
|  |  | 
|  | // Inovked on the expiration of the debounce interval to release | 
|  | // deferred events. | 
|  | void SendScrollEndingEventsNow(); | 
|  |  | 
|  | // Sub-filter for removing bounces from in-progress scrolls. | 
|  | bool ShouldForwardForBounceReduction( | 
|  | const GestureEventWithLatencyInfo& gesture_event); | 
|  |  | 
|  | // ACK completed events in order until we have reached an incomplete event. | 
|  | // Will preserve the FIFO order as events originally arrived. | 
|  | void AckCompletedEvents(); | 
|  | void AckGestureEventToClient( | 
|  | const GestureEventWithLatencyInfo&, | 
|  | blink::mojom::InputEventResultSource, | 
|  | blink::mojom::InputEventResultState, | 
|  | blink::mojom::ScrollResultDataPtr scroll_result_data); | 
|  |  | 
|  | bool FlingInProgressForTest() const; | 
|  |  | 
|  | // The receiver of all forwarded gesture events. | 
|  | raw_ptr<GestureEventQueueClient> client_; | 
|  |  | 
|  | // True if a GestureScrollUpdate sequence is in progress. | 
|  | bool scrolling_in_progress_; | 
|  |  | 
|  | bool processing_acks_ = false; | 
|  |  | 
|  | using GestureQueueWithAckState = base::circular_deque< | 
|  | GestureEventWithLatencyInfoAckStateAndScrollResultData>; | 
|  |  | 
|  | // Stores outstanding events that have been sent to the renderer but not yet | 
|  | // been ACK'd. These are kept in the order they were sent in so that they can | 
|  | // be ACK'd back in order. Note, the renderer can reply to these out-of-order. | 
|  | // This class makes a note of the ACK state but doesn't actually let the | 
|  | // client know about the ACK until all events earlier in the queue have been | 
|  | // ACK'd so that the client sees the ACKs in order. | 
|  | GestureQueueWithAckState sent_events_awaiting_ack_; | 
|  |  | 
|  | // Timer to release a previously deferred gesture event. | 
|  | base::OneShotTimer debounce_deferring_timer_; | 
|  |  | 
|  | // Queue of events that have been deferred for debounce. | 
|  | GestureQueue debouncing_deferral_queue_; | 
|  |  | 
|  | // Queue of gesture events that have been deferred until the main thread touch | 
|  | // action is known. | 
|  | GestureQueue deferred_gesture_queue_; | 
|  |  | 
|  | // Time window in which to debounce scroll/fling ends. Note that an interval | 
|  | // of zero effectively disables debouncing. | 
|  | base::TimeDelta debounce_interval_; | 
|  |  | 
|  | // An object for filtering unnecessary GFC events, as well as gestureTap/mouse | 
|  | // events that happen immediately after touchscreen/touchpad fling canceling | 
|  | // taps. | 
|  | FlingController fling_controller_; | 
|  |  | 
|  | // True when the last GSE event is either in the debouncing_deferral_queue_ or | 
|  | // pushed to the queue and dropped from it later on. | 
|  | bool scroll_end_filtered_by_deboucing_deferral_queue_ = false; | 
|  | }; | 
|  |  | 
|  | }  // namespace content | 
|  |  | 
|  | #endif  // CONTENT_BROWSER_RENDERER_HOST_INPUT_GESTURE_EVENT_QUEUE_H_ |