#include "content/browser/renderer_host/input/gesture_event_queue.h"
-#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/strings/string_number_conversions.h"
#include "content/browser/renderer_host/input/input_router.h"
using blink::WebInputEvent;
namespace content {
-namespace {
-// Default debouncing interval duration: if a scroll is in progress, non-scroll
-// events during this interval are deferred to either its end or discarded on
-// receipt of another GestureScrollUpdate.
-static const int kDebouncingIntervalTimeMs = 30;
-
-} // namespace
+GestureEventQueue::Config::Config() {
+}
GestureEventQueue::GestureEventQueue(
GestureEventQueueClient* client,
- TouchpadTapSuppressionControllerClient* touchpad_client)
- : client_(client),
- fling_in_progress_(false),
- scrolling_in_progress_(false),
- ignore_next_ack_(false),
- combined_scroll_pinch_(gfx::Transform()),
- touchpad_tap_suppression_controller_(
- new TouchpadTapSuppressionController(touchpad_client)),
- touchscreen_tap_suppression_controller_(
- new TouchscreenTapSuppressionController(this)),
- debounce_interval_time_ms_(kDebouncingIntervalTimeMs),
- debounce_enabled_(true) {
+ TouchpadTapSuppressionControllerClient* touchpad_client,
+ const Config& config)
+ : client_(client),
+ fling_in_progress_(false),
+ scrolling_in_progress_(false),
+ ignore_next_ack_(false),
+ touchpad_tap_suppression_controller_(
+ touchpad_client,
+ config.touchpad_tap_suppression_config),
+ touchscreen_tap_suppression_controller_(
+ this,
+ config.touchscreen_tap_suppression_config),
+ debounce_interval_(config.debounce_interval) {
DCHECK(client);
- DCHECK(touchpad_tap_suppression_controller_);
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableGestureDebounce)) {
- debounce_enabled_ = false;
- }
+ DCHECK(touchpad_client);
}
GestureEventQueue::~GestureEventQueue() { }
bool GestureEventQueue::ShouldForwardForBounceReduction(
const GestureEventWithLatencyInfo& gesture_event) {
- if (!debounce_enabled_)
+ if (debounce_interval_ <= base::TimeDelta())
return true;
switch (gesture_event.event.type) {
case WebInputEvent::GestureScrollUpdate:
if (!scrolling_in_progress_) {
debounce_deferring_timer_.Start(
FROM_HERE,
- base::TimeDelta::FromMilliseconds(debounce_interval_time_ms_),
+ debounce_interval_,
this,
&GestureEventQueue::SendScrollEndingEventsNow);
} else {
switch (gesture_event.event.type) {
case WebInputEvent::GestureFlingCancel:
if (gesture_event.event.sourceDevice == WebGestureEvent::Touchscreen)
- touchscreen_tap_suppression_controller_->GestureFlingCancel();
+ touchscreen_tap_suppression_controller_.GestureFlingCancel();
else
- touchpad_tap_suppression_controller_->GestureFlingCancel();
+ touchpad_tap_suppression_controller_.GestureFlingCancel();
return true;
case WebInputEvent::GestureTapDown:
- return !touchscreen_tap_suppression_controller_->
- ShouldDeferGestureTapDown(gesture_event);
case WebInputEvent::GestureShowPress:
- return !touchscreen_tap_suppression_controller_->
- ShouldDeferGestureShowPress(gesture_event);
+ case WebInputEvent::GestureTapUnconfirmed:
case WebInputEvent::GestureTapCancel:
case WebInputEvent::GestureTap:
- case WebInputEvent::GestureTapUnconfirmed:
case WebInputEvent::GestureDoubleTap:
- return !touchscreen_tap_suppression_controller_->
- ShouldSuppressGestureTapEnd();
+ if (gesture_event.event.sourceDevice == WebGestureEvent::Touchscreen) {
+ return !touchscreen_tap_suppression_controller_.FilterTapEvent(
+ gesture_event);
+ }
+ return true;
default:
return true;
}
default:
break;
}
- EnqueueEvent(gesture_event);
+ coalesced_gesture_events_.push_back(gesture_event);
return ShouldHandleEventNow();
}
return;
}
- // Ack'ing an event may enqueue additional gesture events. By ack'ing the
- // event before the forwarding of queued events below, such additional events
- // can be coalesced with existing queued events prior to dispatch.
+ // It's possible that the ack for the second event in an in-flight, coalesced
+ // Gesture{Scroll,Pinch}Update pair is received prior to the first event ack.
+ // TODO(jdduke): Unify GSU/GPU pairs into a single event, crbug.com/359115.
+ size_t event_index = 0;
+ if (ignore_next_ack_ &&
+ coalesced_gesture_events_.size() > 1 &&
+ coalesced_gesture_events_[0].event.type != type &&
+ coalesced_gesture_events_[1].event.type == type) {
+ event_index = 1;
+ }
GestureEventWithLatencyInfo event_with_latency =
- coalesced_gesture_events_.front();
+ coalesced_gesture_events_[event_index];
DCHECK_EQ(event_with_latency.event.type, type);
event_with_latency.latency.AddNewLatencyFrom(latency);
+
+ // Ack'ing an event may enqueue additional gesture events. By ack'ing the
+ // event before the forwarding of queued events below, such additional events
+ // can be coalesced with existing queued events prior to dispatch.
client_->OnGestureEventAck(event_with_latency, ack_result);
const bool processed = (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result);
if (type == WebInputEvent::GestureFlingCancel) {
if (event_with_latency.event.sourceDevice == WebGestureEvent::Touchscreen)
- touchscreen_tap_suppression_controller_->GestureFlingCancelAck(processed);
+ touchscreen_tap_suppression_controller_.GestureFlingCancelAck(processed);
else
- touchpad_tap_suppression_controller_->GestureFlingCancelAck(processed);
+ touchpad_tap_suppression_controller_.GestureFlingCancelAck(processed);
}
- coalesced_gesture_events_.pop_front();
+ DCHECK_LT(event_index, coalesced_gesture_events_.size());
+ coalesced_gesture_events_.erase(coalesced_gesture_events_.begin() +
+ event_index);
if (ignore_next_ack_) {
ignore_next_ack_ = false;
const GestureEventWithLatencyInfo& first_gesture_event =
coalesced_gesture_events_.front();
- // TODO(yusufo): Introduce GesturePanScroll so that these can be combined
- // into one gesture and kept inside the queue that way.
+ // TODO(jdduke): Unify GSU/GPU pairs into a single event, crbug.com/359115.
// Check for the coupled GesturePinchUpdate before sending either event,
// handling the case where the first GestureScrollUpdate ack is synchronous.
GestureEventWithLatencyInfo second_gesture_event;
TouchpadTapSuppressionController*
GestureEventQueue::GetTouchpadTapSuppressionController() {
- return touchpad_tap_suppression_controller_.get();
+ return &touchpad_tap_suppression_controller_;
}
-bool GestureEventQueue::HasQueuedGestureEvents() const {
+bool GestureEventQueue::ExpectingGestureAck() const {
return !coalesced_gesture_events_.empty();
}
void GestureEventQueue::MergeOrInsertScrollAndPinchEvent(
const GestureEventWithLatencyInfo& gesture_event) {
- if (coalesced_gesture_events_.size() <= 1) {
- EnqueueEvent(gesture_event);
+ const size_t unsent_events_count =
+ coalesced_gesture_events_.size() - EventsInFlightCount();
+ if (!unsent_events_count) {
+ coalesced_gesture_events_.push_back(gesture_event);
return;
}
+
GestureEventWithLatencyInfo* last_event = &coalesced_gesture_events_.back();
if (last_event->CanCoalesceWith(gesture_event)) {
last_event->CoalesceWith(gesture_event);
- if (!combined_scroll_pinch_.IsIdentity()) {
- combined_scroll_pinch_.ConcatTransform(
- GetTransformForEvent(gesture_event));
- }
return;
}
- if (coalesced_gesture_events_.size() == 2 ||
- (coalesced_gesture_events_.size() == 3 && ignore_next_ack_) ||
- !ShouldTryMerging(gesture_event, *last_event)) {
- EnqueueEvent(gesture_event);
+
+ if (!ShouldTryMerging(gesture_event, *last_event)) {
+ coalesced_gesture_events_.push_back(gesture_event);
return;
}
+
GestureEventWithLatencyInfo scroll_event;
GestureEventWithLatencyInfo pinch_event;
scroll_event.event.modifiers |= gesture_event.event.modifiers;
+ scroll_event.event.sourceDevice = gesture_event.event.sourceDevice;
scroll_event.event.timeStampSeconds = gesture_event.event.timeStampSeconds;
// Keep the oldest LatencyInfo.
DCHECK_LE(last_event->latency.trace_id, gesture_event.latency.trace_id);
WebInputEvent::GesturePinchUpdate ?
gesture_event.event.y : last_event->event.y;
- combined_scroll_pinch_.ConcatTransform(GetTransformForEvent(gesture_event));
- GestureEventWithLatencyInfo* second_last_event = &coalesced_gesture_events_
- [coalesced_gesture_events_.size() - 2];
- if (ShouldTryMerging(gesture_event, *second_last_event)) {
- // Keep the oldest LatencyInfo.
- DCHECK_LE(second_last_event->latency.trace_id,
- scroll_event.latency.trace_id);
- scroll_event.latency = second_last_event->latency;
- pinch_event.latency = second_last_event->latency;
- coalesced_gesture_events_.pop_back();
- } else {
- DCHECK(combined_scroll_pinch_ == GetTransformForEvent(gesture_event));
- combined_scroll_pinch_.
- PreconcatTransform(GetTransformForEvent(*last_event));
+ gfx::Transform combined_scroll_pinch = GetTransformForEvent(*last_event);
+ // Only include the second-to-last event in the coalesced pair if it exists
+ // and can be combined with the new event.
+ if (unsent_events_count > 1) {
+ const GestureEventWithLatencyInfo& second_last_event =
+ coalesced_gesture_events_[coalesced_gesture_events_.size() - 2];
+ if (ShouldTryMerging(gesture_event, second_last_event)) {
+ // Keep the oldest LatencyInfo.
+ DCHECK_LE(second_last_event.latency.trace_id,
+ scroll_event.latency.trace_id);
+ scroll_event.latency = second_last_event.latency;
+ pinch_event.latency = second_last_event.latency;
+ combined_scroll_pinch.PreconcatTransform(
+ GetTransformForEvent(second_last_event));
+ coalesced_gesture_events_.pop_back();
+ }
}
+ combined_scroll_pinch.ConcatTransform(GetTransformForEvent(gesture_event));
coalesced_gesture_events_.pop_back();
+
float combined_scale =
- SkMScalarToFloat(combined_scroll_pinch_.matrix().get(0, 0));
+ SkMScalarToFloat(combined_scroll_pinch.matrix().get(0, 0));
float combined_scroll_pinch_x =
- SkMScalarToFloat(combined_scroll_pinch_.matrix().get(0, 3));
+ SkMScalarToFloat(combined_scroll_pinch.matrix().get(0, 3));
float combined_scroll_pinch_y =
- SkMScalarToFloat(combined_scroll_pinch_.matrix().get(1, 3));
+ SkMScalarToFloat(combined_scroll_pinch.matrix().get(1, 3));
scroll_event.event.data.scrollUpdate.deltaX =
(combined_scroll_pinch_x + pinch_event.event.x) / combined_scale -
pinch_event.event.x;
<< "Event time not monotonic?\n";
return (event_in_queue.event.type == WebInputEvent::GestureScrollUpdate ||
event_in_queue.event.type == WebInputEvent::GesturePinchUpdate) &&
- event_in_queue.event.modifiers == new_event.event.modifiers;
+ event_in_queue.event.modifiers == new_event.event.modifiers &&
+ event_in_queue.event.sourceDevice == new_event.event.sourceDevice;
}
gfx::Transform GestureEventQueue::GetTransformForEvent(
const GestureEventWithLatencyInfo& gesture_event) const {
- gfx::Transform gesture_transform = gfx::Transform();
+ gfx::Transform gesture_transform;
if (gesture_event.event.type == WebInputEvent::GestureScrollUpdate) {
gesture_transform.Translate(gesture_event.event.data.scrollUpdate.deltaX,
gesture_event.event.data.scrollUpdate.deltaY);
return gesture_transform;
}
-void GestureEventQueue::EnqueueEvent(
- const GestureEventWithLatencyInfo& gesture_event) {
- coalesced_gesture_events_.push_back(gesture_event);
- // Scroll and pinch events contributing to |combined_scroll_pinch_| will be
- // manually added to the queue in |MergeOrInsertScrollAndPinchEvent()|.
- combined_scroll_pinch_ = gfx::Transform();
+size_t GestureEventQueue::EventsInFlightCount() const {
+ if (coalesced_gesture_events_.empty())
+ return 0;
+
+ if (!ignore_next_ack_)
+ return 1;
+
+ DCHECK_GT(coalesced_gesture_events_.size(), 1U);
+ return 2;
}
} // namespace content