blob: 2e38b7dbf9b1a0f90373609feb9ac785dd788fc5 [file] [log] [blame]
// 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 "components/input/gesture_event_queue.h"
#include "base/auto_reset.h"
#include "base/trace_event/trace_event.h"
#include "components/input/touchpad_tap_suppression_controller.h"
#include "components/input/touchscreen_tap_suppression_controller.h"
#include "ui/events/blink/blink_features.h"
#include "ui/events/blink/web_input_event_traits.h"
using blink::WebGestureEvent;
using blink::WebInputEvent;
namespace input {
GestureEventQueue::GestureEventWithLatencyInfoAckState::
GestureEventWithLatencyInfoAckState(
const GestureEventWithLatencyInfo& event)
: GestureEventWithLatencyInfo(event) {}
GestureEventWithLatencyInfoAndCallback::GestureEventWithLatencyInfoAndCallback(
const GestureEventWithLatencyInfo& event,
DispatchToRendererCallback callback)
: GestureEventWithLatencyInfo(event),
dispatch_callback(std::move(callback)) {}
GestureEventWithLatencyInfoAndCallback::GestureEventWithLatencyInfoAndCallback(
GestureEventWithLatencyInfoAndCallback&& event)
: GestureEventWithLatencyInfo(event.event, event.latency),
dispatch_callback(std::move(event.dispatch_callback)) {}
GestureEventWithLatencyInfoAndCallback::
~GestureEventWithLatencyInfoAndCallback() = default;
GestureEventQueue::Config::Config() = default;
GestureEventQueue::GestureEventQueue(
GestureEventQueueClient* client,
FlingControllerEventSenderClient* fling_event_sender_client,
FlingControllerSchedulerClient* fling_scheduler_client,
const Config& config)
: client_(client),
scrolling_in_progress_(false),
debounce_interval_(config.debounce_interval),
fling_controller_(fling_event_sender_client,
fling_scheduler_client,
config.fling_config) {
DCHECK(client);
DCHECK(fling_event_sender_client);
DCHECK(fling_scheduler_client);
}
GestureEventQueue::~GestureEventQueue() = default;
bool GestureEventQueue::DebounceOrForwardEvent(
const GestureEventWithLatencyInfo& gesture_event,
DispatchToRendererCallback& dispatch_callback) {
// GFS and GFC should have been filtered in PassToFlingController.
DCHECK_NE(gesture_event.event.GetType(),
WebInputEvent::Type::kGestureFlingStart);
DCHECK_NE(gesture_event.event.GetType(),
WebInputEvent::Type::kGestureFlingCancel);
if (!ShouldForwardForBounceReduction(gesture_event, dispatch_callback)) {
return false;
}
ForwardGestureEvent(gesture_event, dispatch_callback);
return true;
}
bool GestureEventQueue::PassToFlingController(
const GestureEventWithLatencyInfo& gesture_event) {
return fling_controller_.ObserveAndMaybeConsumeGestureEvent(gesture_event);
}
void GestureEventQueue::QueueDeferredEvents(
const GestureEventWithLatencyInfo& gesture_event,
DispatchToRendererCallback& dispatch_callback) {
deferred_gesture_queue_.emplace_back(gesture_event,
std::move(dispatch_callback));
}
GestureEventQueue::GestureWithCallbackQueue
GestureEventQueue::TakeDeferredEvents() {
GestureWithCallbackQueue deferred_gesture_events;
deferred_gesture_events.swap(deferred_gesture_queue_);
return deferred_gesture_events;
}
void GestureEventQueue::StopFling() {
fling_controller_.StopFling();
}
gfx::Vector2dF GestureEventQueue::CurrentFlingVelocity() const {
return fling_controller_.CurrentFlingVelocity();
}
bool GestureEventQueue::FlingInProgressForTest() const {
return fling_controller_.fling_in_progress();
}
bool GestureEventQueue::ShouldForwardForBounceReduction(
const GestureEventWithLatencyInfo& gesture_event,
DispatchToRendererCallback& dispatch_callback) {
if (debounce_interval_ <= base::TimeDelta()) {
return true;
}
// Don't debounce any gesture events while a fling is in progress on the
// browser side. A GSE event in this case ends fling progress and it shouldn't
// get cancelled by its next GSB event.
if (fling_controller_.fling_in_progress()) {
return true;
}
switch (gesture_event.event.GetType()) {
case WebInputEvent::Type::kGestureScrollUpdate:
// This will restart the timer if it is already running.
debounce_deferring_timer_.Start(
FROM_HERE, debounce_interval_, this,
&GestureEventQueue::SendScrollEndingEventsNow);
scrolling_in_progress_ = true;
debouncing_deferral_queue_.clear();
return true;
case WebInputEvent::Type::kGesturePinchBegin:
case WebInputEvent::Type::kGesturePinchEnd:
case WebInputEvent::Type::kGesturePinchUpdate:
return true;
case WebInputEvent::Type::kGestureScrollBegin:
// GSB event should not get filtered by the debouncing_deferral_queue_
// when its previous GSE event is forwarded to the renderer.
if (!scroll_end_filtered_by_deboucing_deferral_queue_) {
return true;
} else {
debouncing_deferral_queue_.emplace_back(gesture_event,
std::move(dispatch_callback));
return false;
}
case WebInputEvent::Type::kGestureScrollEnd:
scroll_end_filtered_by_deboucing_deferral_queue_ = false;
// GSEs generated by the fling controller should not get pushed to the
// debouncing_deferral_queue_.
if (gesture_event.event.data.scroll_end.generated_by_fling_controller) {
scrolling_in_progress_ = false;
return true;
}
if (scrolling_in_progress_) {
debouncing_deferral_queue_.emplace_back(gesture_event,
std::move(dispatch_callback));
scroll_end_filtered_by_deboucing_deferral_queue_ = true;
return false;
}
return true;
default:
if (scrolling_in_progress_) {
debouncing_deferral_queue_.emplace_back(gesture_event,
std::move(dispatch_callback));
return false;
}
return true;
}
}
void GestureEventQueue::ForwardGestureEvent(
const GestureEventWithLatencyInfo& gesture_event,
DispatchToRendererCallback& dispatch_callback) {
// GFS and GFC should have been filtered in PassToFlingController to get
// handled by fling controller.
DCHECK_NE(gesture_event.event.GetType(),
WebInputEvent::Type::kGestureFlingStart);
DCHECK_NE(gesture_event.event.GetType(),
WebInputEvent::Type::kGestureFlingCancel);
sent_events_awaiting_ack_.push_back(gesture_event);
client_->SendGestureEventImmediately(gesture_event, dispatch_callback);
// `ForwardGestureEvent` could be called on enqueued events as well, DCHECK
// ensures the corresponding callback was run.
CHECK(!dispatch_callback);
}
void GestureEventQueue::ProcessGestureAck(
blink::mojom::InputEventResultSource ack_source,
blink::mojom::InputEventResultState ack_result,
WebInputEvent::Type type,
const ui::LatencyInfo& latency) {
TRACE_EVENT0("input", "GestureEventQueue::ProcessGestureAck");
if (sent_events_awaiting_ack_.empty()) {
DLOG(ERROR) << "Received unexpected ACK for event type " << type;
return;
}
// ACKs could come back out of order. We want to cache them to restore the
// original order.
for (auto& outstanding_event : sent_events_awaiting_ack_) {
if (outstanding_event.ack_state() !=
blink::mojom::InputEventResultState::kUnknown) {
continue;
}
if (outstanding_event.event.GetType() == type) {
outstanding_event.latency.AddNewLatencyFrom(latency);
outstanding_event.set_ack_info(ack_source, ack_result);
break;
}
}
AckCompletedEvents();
}
void GestureEventQueue::AckCompletedEvents() {
// Don't allow re-entrancy into this method otherwise
// the ordering of acks won't be preserved.
if (processing_acks_) {
return;
}
base::AutoReset<bool> process_acks(&processing_acks_, true);
while (!sent_events_awaiting_ack_.empty()) {
auto iter = sent_events_awaiting_ack_.begin();
if (iter->ack_state() == blink::mojom::InputEventResultState::kUnknown) {
break;
}
GestureEventWithLatencyInfoAckState event = *iter;
sent_events_awaiting_ack_.erase(iter);
AckGestureEventToClient(event, event.ack_source(), event.ack_state());
}
}
void GestureEventQueue::AckGestureEventToClient(
const GestureEventWithLatencyInfo& event_with_latency,
blink::mojom::InputEventResultSource ack_source,
blink::mojom::InputEventResultState ack_result) {
client_->OnGestureEventAck(event_with_latency, ack_source, ack_result);
}
TouchpadTapSuppressionController*
GestureEventQueue::GetTouchpadTapSuppressionController() {
return fling_controller_.GetTouchpadTapSuppressionController();
}
void GestureEventQueue::SendScrollEndingEventsNow() {
scrolling_in_progress_ = false;
if (debouncing_deferral_queue_.empty()) {
return;
}
GestureWithCallbackQueue debouncing_deferral_queue;
debouncing_deferral_queue.swap(debouncing_deferral_queue_);
for (auto& gesture_with_callback : debouncing_deferral_queue) {
if (!fling_controller_.ObserveAndMaybeConsumeGestureEvent(
gesture_with_callback)) {
if (gesture_with_callback.event.GetType() ==
WebInputEvent::Type::kGestureScrollEnd) {
scroll_end_filtered_by_deboucing_deferral_queue_ = false;
}
ForwardGestureEvent(gesture_with_callback,
gesture_with_callback.dispatch_callback);
} else {
std::move(gesture_with_callback.dispatch_callback)
.Run(gesture_with_callback.event,
DispatchToRendererResult::kNotDispatched);
}
}
}
void GestureEventQueue::OnWheelEventAck(
const MouseWheelEventWithLatencyInfo& event,
blink::mojom::InputEventResultSource ack_source,
blink::mojom::InputEventResultState ack_result) {
fling_controller_.OnWheelEventAck(event, ack_source, ack_result);
}
} // namespace input