• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "content/browser/renderer_host/input/gesture_event_queue.h"
6 
7 #include "base/debug/trace_event.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"
13 
14 using blink::WebGestureEvent;
15 using blink::WebInputEvent;
16 
17 namespace content {
18 
Config()19 GestureEventQueue::Config::Config() {
20 }
21 
GestureEventQueue(GestureEventQueueClient * client,TouchpadTapSuppressionControllerClient * touchpad_client,const Config & config)22 GestureEventQueue::GestureEventQueue(
23     GestureEventQueueClient* client,
24     TouchpadTapSuppressionControllerClient* touchpad_client,
25     const Config& config)
26     : client_(client),
27       fling_in_progress_(false),
28       scrolling_in_progress_(false),
29       ignore_next_ack_(false),
30       touchpad_tap_suppression_controller_(
31           touchpad_client,
32           config.touchpad_tap_suppression_config),
33       touchscreen_tap_suppression_controller_(
34           this,
35           config.touchscreen_tap_suppression_config),
36       debounce_interval_(config.debounce_interval) {
37   DCHECK(client);
38   DCHECK(touchpad_client);
39 }
40 
~GestureEventQueue()41 GestureEventQueue::~GestureEventQueue() { }
42 
ShouldDiscardFlingCancelEvent(const GestureEventWithLatencyInfo & gesture_event) const43 bool GestureEventQueue::ShouldDiscardFlingCancelEvent(
44     const GestureEventWithLatencyInfo& gesture_event) const {
45   if (coalesced_gesture_events_.empty() && fling_in_progress_)
46     return false;
47   GestureQueue::const_reverse_iterator it =
48       coalesced_gesture_events_.rbegin();
49   while (it != coalesced_gesture_events_.rend()) {
50     if (it->event.type == WebInputEvent::GestureFlingStart)
51       return false;
52     if (it->event.type == WebInputEvent::GestureFlingCancel)
53       return true;
54     it++;
55   }
56   return true;
57 }
58 
ShouldForwardForBounceReduction(const GestureEventWithLatencyInfo & gesture_event)59 bool GestureEventQueue::ShouldForwardForBounceReduction(
60     const GestureEventWithLatencyInfo& gesture_event) {
61   if (debounce_interval_ <= base::TimeDelta())
62     return true;
63   switch (gesture_event.event.type) {
64     case WebInputEvent::GestureScrollUpdate:
65       if (!scrolling_in_progress_) {
66         debounce_deferring_timer_.Start(
67             FROM_HERE,
68             debounce_interval_,
69             this,
70             &GestureEventQueue::SendScrollEndingEventsNow);
71       } else {
72         // Extend the bounce interval.
73         debounce_deferring_timer_.Reset();
74       }
75       scrolling_in_progress_ = true;
76       debouncing_deferral_queue_.clear();
77       return true;
78     case WebInputEvent::GesturePinchBegin:
79     case WebInputEvent::GesturePinchEnd:
80     case WebInputEvent::GesturePinchUpdate:
81       // TODO(rjkroege): Debounce pinch (http://crbug.com/147647)
82       return true;
83     default:
84       if (scrolling_in_progress_) {
85         debouncing_deferral_queue_.push_back(gesture_event);
86         return false;
87       }
88       return true;
89   }
90 }
91 
92 // NOTE: The filters are applied successively. This simplifies the change.
ShouldForward(const GestureEventWithLatencyInfo & gesture_event)93 bool GestureEventQueue::ShouldForward(
94     const GestureEventWithLatencyInfo& gesture_event) {
95   TRACE_EVENT0("input", "GestureEventQueue::ShouldForward");
96   return ShouldForwardForBounceReduction(gesture_event) &&
97          ShouldForwardForGFCFiltering(gesture_event) &&
98          ShouldForwardForTapSuppression(gesture_event) &&
99          ShouldForwardForCoalescing(gesture_event);
100 }
101 
ShouldForwardForGFCFiltering(const GestureEventWithLatencyInfo & gesture_event) const102 bool GestureEventQueue::ShouldForwardForGFCFiltering(
103     const GestureEventWithLatencyInfo& gesture_event) const {
104   return gesture_event.event.type != WebInputEvent::GestureFlingCancel ||
105       !ShouldDiscardFlingCancelEvent(gesture_event);
106 }
107 
ShouldForwardForTapSuppression(const GestureEventWithLatencyInfo & gesture_event)108 bool GestureEventQueue::ShouldForwardForTapSuppression(
109     const GestureEventWithLatencyInfo& gesture_event) {
110   switch (gesture_event.event.type) {
111     case WebInputEvent::GestureFlingCancel:
112       if (gesture_event.event.sourceDevice ==
113           blink::WebGestureDeviceTouchscreen)
114         touchscreen_tap_suppression_controller_.GestureFlingCancel();
115       else
116         touchpad_tap_suppression_controller_.GestureFlingCancel();
117       return true;
118     case WebInputEvent::GestureTapDown:
119     case WebInputEvent::GestureShowPress:
120     case WebInputEvent::GestureTapUnconfirmed:
121     case WebInputEvent::GestureTapCancel:
122     case WebInputEvent::GestureTap:
123     case WebInputEvent::GestureDoubleTap:
124       if (gesture_event.event.sourceDevice ==
125           blink::WebGestureDeviceTouchscreen) {
126         return !touchscreen_tap_suppression_controller_.FilterTapEvent(
127             gesture_event);
128       }
129       return true;
130     default:
131       return true;
132   }
133 }
134 
ShouldForwardForCoalescing(const GestureEventWithLatencyInfo & gesture_event)135 bool GestureEventQueue::ShouldForwardForCoalescing(
136     const GestureEventWithLatencyInfo& gesture_event) {
137   switch (gesture_event.event.type) {
138     case WebInputEvent::GestureFlingCancel:
139       fling_in_progress_ = false;
140       break;
141     case WebInputEvent::GestureFlingStart:
142       fling_in_progress_ = true;
143       break;
144     case WebInputEvent::GesturePinchUpdate:
145     case WebInputEvent::GestureScrollUpdate:
146       MergeOrInsertScrollAndPinchEvent(gesture_event);
147       return ShouldHandleEventNow();
148     default:
149       break;
150   }
151   coalesced_gesture_events_.push_back(gesture_event);
152   return ShouldHandleEventNow();
153 }
154 
ProcessGestureAck(InputEventAckState ack_result,WebInputEvent::Type type,const ui::LatencyInfo & latency)155 void GestureEventQueue::ProcessGestureAck(InputEventAckState ack_result,
156                                            WebInputEvent::Type type,
157                                            const ui::LatencyInfo& latency) {
158   TRACE_EVENT0("input", "GestureEventQueue::ProcessGestureAck");
159 
160   if (coalesced_gesture_events_.empty()) {
161     DLOG(ERROR) << "Received unexpected ACK for event type " << type;
162     return;
163   }
164 
165   // It's possible that the ack for the second event in an in-flight, coalesced
166   // Gesture{Scroll,Pinch}Update pair is received prior to the first event ack.
167   // TODO(jdduke): Unify GSU/GPU pairs into a single event, crbug.com/359115.
168   size_t event_index = 0;
169   if (ignore_next_ack_ &&
170       coalesced_gesture_events_.size() > 1 &&
171       coalesced_gesture_events_[0].event.type != type &&
172       coalesced_gesture_events_[1].event.type == type) {
173     event_index = 1;
174   }
175   GestureEventWithLatencyInfo event_with_latency =
176       coalesced_gesture_events_[event_index];
177   DCHECK_EQ(event_with_latency.event.type, type);
178   event_with_latency.latency.AddNewLatencyFrom(latency);
179 
180   // Ack'ing an event may enqueue additional gesture events.  By ack'ing the
181   // event before the forwarding of queued events below, such additional events
182   // can be coalesced with existing queued events prior to dispatch.
183   client_->OnGestureEventAck(event_with_latency, ack_result);
184 
185   const bool processed = (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result);
186   if (type == WebInputEvent::GestureFlingCancel) {
187     if (event_with_latency.event.sourceDevice ==
188         blink::WebGestureDeviceTouchscreen)
189       touchscreen_tap_suppression_controller_.GestureFlingCancelAck(processed);
190     else
191       touchpad_tap_suppression_controller_.GestureFlingCancelAck(processed);
192   }
193   DCHECK_LT(event_index, coalesced_gesture_events_.size());
194   coalesced_gesture_events_.erase(coalesced_gesture_events_.begin() +
195                                   event_index);
196 
197   if (ignore_next_ack_) {
198     ignore_next_ack_ = false;
199     return;
200   }
201 
202   if (coalesced_gesture_events_.empty())
203     return;
204 
205   const GestureEventWithLatencyInfo& first_gesture_event =
206       coalesced_gesture_events_.front();
207 
208   // TODO(jdduke): Unify GSU/GPU pairs into a single event, crbug.com/359115.
209   // Check for the coupled GesturePinchUpdate before sending either event,
210   // handling the case where the first GestureScrollUpdate ack is synchronous.
211   GestureEventWithLatencyInfo second_gesture_event;
212   if (first_gesture_event.event.type == WebInputEvent::GestureScrollUpdate &&
213       coalesced_gesture_events_.size() > 1 &&
214       coalesced_gesture_events_[1].event.type ==
215           WebInputEvent::GesturePinchUpdate) {
216     second_gesture_event = coalesced_gesture_events_[1];
217     ignore_next_ack_ = true;
218   }
219 
220   client_->SendGestureEventImmediately(first_gesture_event);
221   if (second_gesture_event.event.type != WebInputEvent::Undefined)
222     client_->SendGestureEventImmediately(second_gesture_event);
223 }
224 
225 TouchpadTapSuppressionController*
GetTouchpadTapSuppressionController()226     GestureEventQueue::GetTouchpadTapSuppressionController() {
227   return &touchpad_tap_suppression_controller_;
228 }
229 
ExpectingGestureAck() const230 bool GestureEventQueue::ExpectingGestureAck() const {
231   return !coalesced_gesture_events_.empty();
232 }
233 
FlingHasBeenHalted()234 void GestureEventQueue::FlingHasBeenHalted() {
235   fling_in_progress_ = false;
236 }
237 
ShouldHandleEventNow() const238 bool GestureEventQueue::ShouldHandleEventNow() const {
239   return coalesced_gesture_events_.size() == 1;
240 }
241 
ForwardGestureEvent(const GestureEventWithLatencyInfo & gesture_event)242 void GestureEventQueue::ForwardGestureEvent(
243     const GestureEventWithLatencyInfo& gesture_event) {
244   if (ShouldForwardForCoalescing(gesture_event))
245     client_->SendGestureEventImmediately(gesture_event);
246 }
247 
SendScrollEndingEventsNow()248 void GestureEventQueue::SendScrollEndingEventsNow() {
249   scrolling_in_progress_ = false;
250   if (debouncing_deferral_queue_.empty())
251     return;
252   GestureQueue debouncing_deferral_queue;
253   debouncing_deferral_queue.swap(debouncing_deferral_queue_);
254   for (GestureQueue::const_iterator it = debouncing_deferral_queue.begin();
255        it != debouncing_deferral_queue.end(); it++) {
256     if (ShouldForwardForGFCFiltering(*it) &&
257         ShouldForwardForTapSuppression(*it) &&
258         ShouldForwardForCoalescing(*it)) {
259       client_->SendGestureEventImmediately(*it);
260     }
261   }
262 }
263 
MergeOrInsertScrollAndPinchEvent(const GestureEventWithLatencyInfo & gesture_event)264 void GestureEventQueue::MergeOrInsertScrollAndPinchEvent(
265     const GestureEventWithLatencyInfo& gesture_event) {
266   const size_t unsent_events_count =
267       coalesced_gesture_events_.size() - EventsInFlightCount();
268   if (!unsent_events_count) {
269     coalesced_gesture_events_.push_back(gesture_event);
270     return;
271   }
272 
273   GestureEventWithLatencyInfo* last_event = &coalesced_gesture_events_.back();
274   if (last_event->CanCoalesceWith(gesture_event)) {
275     last_event->CoalesceWith(gesture_event);
276     return;
277   }
278 
279   if (!ShouldTryMerging(gesture_event, *last_event)) {
280     coalesced_gesture_events_.push_back(gesture_event);
281     return;
282   }
283 
284   GestureEventWithLatencyInfo scroll_event;
285   GestureEventWithLatencyInfo pinch_event;
286   scroll_event.event.modifiers |= gesture_event.event.modifiers;
287   scroll_event.event.sourceDevice = gesture_event.event.sourceDevice;
288   scroll_event.event.timeStampSeconds = gesture_event.event.timeStampSeconds;
289   // Keep the oldest LatencyInfo.
290   DCHECK_LE(last_event->latency.trace_id, gesture_event.latency.trace_id);
291   scroll_event.latency = last_event->latency;
292   pinch_event = scroll_event;
293   scroll_event.event.type = WebInputEvent::GestureScrollUpdate;
294   pinch_event.event.type = WebInputEvent::GesturePinchUpdate;
295   pinch_event.event.x = gesture_event.event.type ==
296       WebInputEvent::GesturePinchUpdate ?
297           gesture_event.event.x : last_event->event.x;
298   pinch_event.event.y = gesture_event.event.type ==
299       WebInputEvent::GesturePinchUpdate ?
300           gesture_event.event.y : last_event->event.y;
301 
302   gfx::Transform combined_scroll_pinch = GetTransformForEvent(*last_event);
303   // Only include the second-to-last event in the coalesced pair if it exists
304   // and can be combined with the new event.
305   if (unsent_events_count > 1) {
306     const GestureEventWithLatencyInfo& second_last_event =
307         coalesced_gesture_events_[coalesced_gesture_events_.size() - 2];
308     if (ShouldTryMerging(gesture_event, second_last_event)) {
309       // Keep the oldest LatencyInfo.
310       DCHECK_LE(second_last_event.latency.trace_id,
311                 scroll_event.latency.trace_id);
312       scroll_event.latency = second_last_event.latency;
313       pinch_event.latency = second_last_event.latency;
314       combined_scroll_pinch.PreconcatTransform(
315           GetTransformForEvent(second_last_event));
316       coalesced_gesture_events_.pop_back();
317     }
318   }
319   combined_scroll_pinch.ConcatTransform(GetTransformForEvent(gesture_event));
320   coalesced_gesture_events_.pop_back();
321 
322   float combined_scale =
323       SkMScalarToFloat(combined_scroll_pinch.matrix().get(0, 0));
324   float combined_scroll_pinch_x =
325       SkMScalarToFloat(combined_scroll_pinch.matrix().get(0, 3));
326   float combined_scroll_pinch_y =
327       SkMScalarToFloat(combined_scroll_pinch.matrix().get(1, 3));
328   scroll_event.event.data.scrollUpdate.deltaX =
329       (combined_scroll_pinch_x + pinch_event.event.x) / combined_scale -
330       pinch_event.event.x;
331   scroll_event.event.data.scrollUpdate.deltaY =
332       (combined_scroll_pinch_y + pinch_event.event.y) / combined_scale -
333       pinch_event.event.y;
334   coalesced_gesture_events_.push_back(scroll_event);
335   pinch_event.event.data.pinchUpdate.scale = combined_scale;
336   coalesced_gesture_events_.push_back(pinch_event);
337 }
338 
ShouldTryMerging(const GestureEventWithLatencyInfo & new_event,const GestureEventWithLatencyInfo & event_in_queue) const339 bool GestureEventQueue::ShouldTryMerging(
340     const GestureEventWithLatencyInfo& new_event,
341     const GestureEventWithLatencyInfo& event_in_queue) const {
342   DLOG_IF(WARNING,
343           new_event.event.timeStampSeconds <
344           event_in_queue.event.timeStampSeconds)
345           << "Event time not monotonic?\n";
346   return (event_in_queue.event.type == WebInputEvent::GestureScrollUpdate ||
347       event_in_queue.event.type == WebInputEvent::GesturePinchUpdate) &&
348       event_in_queue.event.modifiers == new_event.event.modifiers &&
349       event_in_queue.event.sourceDevice == new_event.event.sourceDevice;
350 }
351 
GetTransformForEvent(const GestureEventWithLatencyInfo & gesture_event) const352 gfx::Transform GestureEventQueue::GetTransformForEvent(
353     const GestureEventWithLatencyInfo& gesture_event) const {
354   gfx::Transform gesture_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);
363   }
364   return gesture_transform;
365 }
366 
EventsInFlightCount() const367 size_t GestureEventQueue::EventsInFlightCount() const {
368   if (coalesced_gesture_events_.empty())
369     return 0;
370 
371   if (!ignore_next_ack_)
372     return 1;
373 
374   DCHECK_GT(coalesced_gesture_events_.size(), 1U);
375   return 2;
376 }
377 
378 }  // namespace content
379