Rename GestureEventFilter to GestureEventQueue
[chromium-blink-merge.git] / content / browser / renderer_host / input / gesture_event_queue.cc
blob534b7778f71d43b2643f0fd6b7946698a188a074
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/renderer_host/input/gesture_event_queue.h"
7 #include "base/command_line.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "content/browser/renderer_host/input/input_router.h"
10 #include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
11 #include "content/browser/renderer_host/input/touchscreen_tap_suppression_controller.h"
12 #include "content/public/common/content_switches.h"
14 using blink::WebGestureEvent;
15 using blink::WebInputEvent;
17 namespace content {
18 namespace {
20 // Default debouncing interval duration: if a scroll is in progress, non-scroll
21 // events during this interval are deferred to either its end or discarded on
22 // receipt of another GestureScrollUpdate.
23 static const int kDebouncingIntervalTimeMs = 30;
25 } // namespace
27 GestureEventQueue::GestureEventQueue(
28 GestureEventQueueClient* client,
29 TouchpadTapSuppressionControllerClient* touchpad_client)
30 : client_(client),
31 fling_in_progress_(false),
32 scrolling_in_progress_(false),
33 ignore_next_ack_(false),
34 combined_scroll_pinch_(gfx::Transform()),
35 touchpad_tap_suppression_controller_(
36 new TouchpadTapSuppressionController(touchpad_client)),
37 touchscreen_tap_suppression_controller_(
38 new TouchscreenTapSuppressionController(this)),
39 debounce_interval_time_ms_(kDebouncingIntervalTimeMs),
40 debounce_enabled_(true) {
41 DCHECK(client);
42 DCHECK(touchpad_tap_suppression_controller_);
43 if (CommandLine::ForCurrentProcess()->HasSwitch(
44 switches::kDisableGestureDebounce)) {
45 debounce_enabled_ = false;
49 GestureEventQueue::~GestureEventQueue() { }
51 bool GestureEventQueue::ShouldDiscardFlingCancelEvent(
52 const GestureEventWithLatencyInfo& gesture_event) const {
53 if (coalesced_gesture_events_.empty() && fling_in_progress_)
54 return false;
55 GestureQueue::const_reverse_iterator it =
56 coalesced_gesture_events_.rbegin();
57 while (it != coalesced_gesture_events_.rend()) {
58 if (it->event.type == WebInputEvent::GestureFlingStart)
59 return false;
60 if (it->event.type == WebInputEvent::GestureFlingCancel)
61 return true;
62 it++;
64 return true;
67 bool GestureEventQueue::ShouldForwardForBounceReduction(
68 const GestureEventWithLatencyInfo& gesture_event) {
69 if (!debounce_enabled_)
70 return true;
71 switch (gesture_event.event.type) {
72 case WebInputEvent::GestureScrollUpdate:
73 if (!scrolling_in_progress_) {
74 debounce_deferring_timer_.Start(
75 FROM_HERE,
76 base::TimeDelta::FromMilliseconds(debounce_interval_time_ms_),
77 this,
78 &GestureEventQueue::SendScrollEndingEventsNow);
79 } else {
80 // Extend the bounce interval.
81 debounce_deferring_timer_.Reset();
83 scrolling_in_progress_ = true;
84 debouncing_deferral_queue_.clear();
85 return true;
86 case WebInputEvent::GesturePinchBegin:
87 case WebInputEvent::GesturePinchEnd:
88 case WebInputEvent::GesturePinchUpdate:
89 // TODO(rjkroege): Debounce pinch (http://crbug.com/147647)
90 return true;
91 default:
92 if (scrolling_in_progress_) {
93 debouncing_deferral_queue_.push_back(gesture_event);
94 return false;
96 return true;
99 NOTREACHED();
100 return false;
103 // NOTE: The filters are applied successively. This simplifies the change.
104 bool GestureEventQueue::ShouldForward(
105 const GestureEventWithLatencyInfo& gesture_event) {
106 return ShouldForwardForZeroVelocityFlingStart(gesture_event) &&
107 ShouldForwardForBounceReduction(gesture_event) &&
108 ShouldForwardForGFCFiltering(gesture_event) &&
109 ShouldForwardForTapSuppression(gesture_event) &&
110 ShouldForwardForCoalescing(gesture_event);
113 bool GestureEventQueue::ShouldForwardForZeroVelocityFlingStart(
114 const GestureEventWithLatencyInfo& gesture_event) const {
115 return gesture_event.event.type != WebInputEvent::GestureFlingStart ||
116 gesture_event.event.sourceDevice != WebGestureEvent::Touchpad ||
117 gesture_event.event.data.flingStart.velocityX != 0 ||
118 gesture_event.event.data.flingStart.velocityY != 0;
121 bool GestureEventQueue::ShouldForwardForGFCFiltering(
122 const GestureEventWithLatencyInfo& gesture_event) const {
123 return gesture_event.event.type != WebInputEvent::GestureFlingCancel ||
124 !ShouldDiscardFlingCancelEvent(gesture_event);
127 bool GestureEventQueue::ShouldForwardForTapSuppression(
128 const GestureEventWithLatencyInfo& gesture_event) {
129 switch (gesture_event.event.type) {
130 case WebInputEvent::GestureFlingCancel:
131 if (gesture_event.event.sourceDevice == WebGestureEvent::Touchscreen)
132 touchscreen_tap_suppression_controller_->GestureFlingCancel();
133 else
134 touchpad_tap_suppression_controller_->GestureFlingCancel();
135 return true;
136 case WebInputEvent::GestureTapDown:
137 return !touchscreen_tap_suppression_controller_->
138 ShouldDeferGestureTapDown(gesture_event);
139 case WebInputEvent::GestureShowPress:
140 return !touchscreen_tap_suppression_controller_->
141 ShouldDeferGestureShowPress(gesture_event);
142 case WebInputEvent::GestureTapCancel:
143 case WebInputEvent::GestureTap:
144 case WebInputEvent::GestureTapUnconfirmed:
145 case WebInputEvent::GestureDoubleTap:
146 return !touchscreen_tap_suppression_controller_->
147 ShouldSuppressGestureTapEnd();
148 default:
149 return true;
151 NOTREACHED();
152 return false;
155 bool GestureEventQueue::ShouldForwardForCoalescing(
156 const GestureEventWithLatencyInfo& gesture_event) {
157 switch (gesture_event.event.type) {
158 case WebInputEvent::GestureFlingCancel:
159 fling_in_progress_ = false;
160 break;
161 case WebInputEvent::GestureFlingStart:
162 fling_in_progress_ = true;
163 break;
164 case WebInputEvent::GesturePinchUpdate:
165 case WebInputEvent::GestureScrollUpdate:
166 MergeOrInsertScrollAndPinchEvent(gesture_event);
167 return ShouldHandleEventNow();
168 default:
169 break;
171 EnqueueEvent(gesture_event);
172 return ShouldHandleEventNow();
175 void GestureEventQueue::ProcessGestureAck(InputEventAckState ack_result,
176 WebInputEvent::Type type,
177 const ui::LatencyInfo& latency) {
178 if (coalesced_gesture_events_.empty()) {
179 DLOG(ERROR) << "Received unexpected ACK for event type " << type;
180 return;
183 // Ack'ing an event may enqueue additional gesture events. By ack'ing the
184 // event before the forwarding of queued events below, such additional events
185 // can be coalesced with existing queued events prior to dispatch.
186 GestureEventWithLatencyInfo event_with_latency =
187 coalesced_gesture_events_.front();
188 DCHECK_EQ(event_with_latency.event.type, type);
189 event_with_latency.latency.AddNewLatencyFrom(latency);
190 client_->OnGestureEventAck(event_with_latency, ack_result);
192 const bool processed = (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result);
193 if (type == WebInputEvent::GestureFlingCancel) {
194 if (event_with_latency.event.sourceDevice == WebGestureEvent::Touchscreen)
195 touchscreen_tap_suppression_controller_->GestureFlingCancelAck(processed);
196 else
197 touchpad_tap_suppression_controller_->GestureFlingCancelAck(processed);
199 coalesced_gesture_events_.pop_front();
201 if (ignore_next_ack_) {
202 ignore_next_ack_ = false;
203 return;
206 if (coalesced_gesture_events_.empty())
207 return;
209 const GestureEventWithLatencyInfo& first_gesture_event =
210 coalesced_gesture_events_.front();
212 // TODO(yusufo): Introduce GesturePanScroll so that these can be combined
213 // into one gesture and kept inside the queue that way.
214 // Check for the coupled GesturePinchUpdate before sending either event,
215 // handling the case where the first GestureScrollUpdate ack is synchronous.
216 GestureEventWithLatencyInfo second_gesture_event;
217 if (first_gesture_event.event.type == WebInputEvent::GestureScrollUpdate &&
218 coalesced_gesture_events_.size() > 1 &&
219 coalesced_gesture_events_[1].event.type ==
220 WebInputEvent::GesturePinchUpdate) {
221 second_gesture_event = coalesced_gesture_events_[1];
222 ignore_next_ack_ = true;
225 client_->SendGestureEventImmediately(first_gesture_event);
226 if (second_gesture_event.event.type != WebInputEvent::Undefined)
227 client_->SendGestureEventImmediately(second_gesture_event);
230 TouchpadTapSuppressionController*
231 GestureEventQueue::GetTouchpadTapSuppressionController() {
232 return touchpad_tap_suppression_controller_.get();
235 bool GestureEventQueue::HasQueuedGestureEvents() const {
236 return !coalesced_gesture_events_.empty();
239 void GestureEventQueue::FlingHasBeenHalted() {
240 fling_in_progress_ = false;
243 bool GestureEventQueue::ShouldHandleEventNow() const {
244 return coalesced_gesture_events_.size() == 1;
247 void GestureEventQueue::ForwardGestureEvent(
248 const GestureEventWithLatencyInfo& gesture_event) {
249 if (ShouldForwardForCoalescing(gesture_event))
250 client_->SendGestureEventImmediately(gesture_event);
253 void GestureEventQueue::SendScrollEndingEventsNow() {
254 scrolling_in_progress_ = false;
255 if (debouncing_deferral_queue_.empty())
256 return;
257 GestureQueue debouncing_deferral_queue;
258 debouncing_deferral_queue.swap(debouncing_deferral_queue_);
259 for (GestureQueue::const_iterator it = debouncing_deferral_queue.begin();
260 it != debouncing_deferral_queue.end(); it++) {
261 if (ShouldForwardForGFCFiltering(*it) &&
262 ShouldForwardForTapSuppression(*it) &&
263 ShouldForwardForCoalescing(*it)) {
264 client_->SendGestureEventImmediately(*it);
269 void GestureEventQueue::MergeOrInsertScrollAndPinchEvent(
270 const GestureEventWithLatencyInfo& gesture_event) {
271 if (coalesced_gesture_events_.size() <= 1) {
272 EnqueueEvent(gesture_event);
273 return;
275 GestureEventWithLatencyInfo* last_event = &coalesced_gesture_events_.back();
276 if (last_event->CanCoalesceWith(gesture_event)) {
277 last_event->CoalesceWith(gesture_event);
278 if (!combined_scroll_pinch_.IsIdentity()) {
279 combined_scroll_pinch_.ConcatTransform(
280 GetTransformForEvent(gesture_event));
282 return;
284 if (coalesced_gesture_events_.size() == 2 ||
285 (coalesced_gesture_events_.size() == 3 && ignore_next_ack_) ||
286 !ShouldTryMerging(gesture_event, *last_event)) {
287 EnqueueEvent(gesture_event);
288 return;
290 GestureEventWithLatencyInfo scroll_event;
291 GestureEventWithLatencyInfo pinch_event;
292 scroll_event.event.modifiers |= gesture_event.event.modifiers;
293 scroll_event.event.timeStampSeconds = gesture_event.event.timeStampSeconds;
294 // Keep the oldest LatencyInfo.
295 DCHECK_LE(last_event->latency.trace_id, gesture_event.latency.trace_id);
296 scroll_event.latency = last_event->latency;
297 pinch_event = scroll_event;
298 scroll_event.event.type = WebInputEvent::GestureScrollUpdate;
299 pinch_event.event.type = WebInputEvent::GesturePinchUpdate;
300 pinch_event.event.x = gesture_event.event.type ==
301 WebInputEvent::GesturePinchUpdate ?
302 gesture_event.event.x : last_event->event.x;
303 pinch_event.event.y = gesture_event.event.type ==
304 WebInputEvent::GesturePinchUpdate ?
305 gesture_event.event.y : last_event->event.y;
307 combined_scroll_pinch_.ConcatTransform(GetTransformForEvent(gesture_event));
308 GestureEventWithLatencyInfo* second_last_event = &coalesced_gesture_events_
309 [coalesced_gesture_events_.size() - 2];
310 if (ShouldTryMerging(gesture_event, *second_last_event)) {
311 // Keep the oldest LatencyInfo.
312 DCHECK_LE(second_last_event->latency.trace_id,
313 scroll_event.latency.trace_id);
314 scroll_event.latency = second_last_event->latency;
315 pinch_event.latency = second_last_event->latency;
316 coalesced_gesture_events_.pop_back();
317 } else {
318 DCHECK(combined_scroll_pinch_ == GetTransformForEvent(gesture_event));
319 combined_scroll_pinch_.
320 PreconcatTransform(GetTransformForEvent(*last_event));
322 coalesced_gesture_events_.pop_back();
323 float combined_scale =
324 SkMScalarToFloat(combined_scroll_pinch_.matrix().get(0, 0));
325 float combined_scroll_pinch_x =
326 SkMScalarToFloat(combined_scroll_pinch_.matrix().get(0, 3));
327 float combined_scroll_pinch_y =
328 SkMScalarToFloat(combined_scroll_pinch_.matrix().get(1, 3));
329 scroll_event.event.data.scrollUpdate.deltaX =
330 (combined_scroll_pinch_x + pinch_event.event.x) / combined_scale -
331 pinch_event.event.x;
332 scroll_event.event.data.scrollUpdate.deltaY =
333 (combined_scroll_pinch_y + pinch_event.event.y) / combined_scale -
334 pinch_event.event.y;
335 coalesced_gesture_events_.push_back(scroll_event);
336 pinch_event.event.data.pinchUpdate.scale = combined_scale;
337 coalesced_gesture_events_.push_back(pinch_event);
340 bool GestureEventQueue::ShouldTryMerging(
341 const GestureEventWithLatencyInfo& new_event,
342 const GestureEventWithLatencyInfo& event_in_queue) const {
343 DLOG_IF(WARNING,
344 new_event.event.timeStampSeconds <
345 event_in_queue.event.timeStampSeconds)
346 << "Event time not monotonic?\n";
347 return (event_in_queue.event.type == WebInputEvent::GestureScrollUpdate ||
348 event_in_queue.event.type == WebInputEvent::GesturePinchUpdate) &&
349 event_in_queue.event.modifiers == new_event.event.modifiers;
352 gfx::Transform GestureEventQueue::GetTransformForEvent(
353 const GestureEventWithLatencyInfo& gesture_event) const {
354 gfx::Transform gesture_transform = gfx::Transform();
355 if (gesture_event.event.type == WebInputEvent::GestureScrollUpdate) {
356 gesture_transform.Translate(gesture_event.event.data.scrollUpdate.deltaX,
357 gesture_event.event.data.scrollUpdate.deltaY);
358 } else if (gesture_event.event.type == WebInputEvent::GesturePinchUpdate) {
359 float scale = gesture_event.event.data.pinchUpdate.scale;
360 gesture_transform.Translate(-gesture_event.event.x, -gesture_event.event.y);
361 gesture_transform.Scale(scale,scale);
362 gesture_transform.Translate(gesture_event.event.x, gesture_event.event.y);
364 return gesture_transform;
367 void GestureEventQueue::EnqueueEvent(
368 const GestureEventWithLatencyInfo& gesture_event) {
369 coalesced_gesture_events_.push_back(gesture_event);
370 // Scroll and pinch events contributing to |combined_scroll_pinch_| will be
371 // manually added to the queue in |MergeOrInsertScrollAndPinchEvent()|.
372 combined_scroll_pinch_ = gfx::Transform();
375 } // namespace content