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 "ui/events/gesture_detection/touch_disposition_gesture_filter.h"
7 #include "base/auto_reset.h"
8 #include "base/logging.h"
9 #include "ui/events/gesture_event_details.h"
14 // A BitSet32 is used for tracking dropped gesture types.
15 COMPILE_ASSERT(ET_GESTURE_TYPE_END - ET_GESTURE_TYPE_START < 32,
16 gesture_type_count_too_large);
18 GestureEventData CreateGesture(EventType type,
20 const base::TimeTicks& timestamp) {
21 GestureEventDetails details(type, 0, 0);
22 return GestureEventData(type,
28 gfx::RectF(0, 0, 0, 0),
32 enum RequiredTouches {
38 struct DispositionHandlingInfo {
39 // A bitwise-OR of |RequiredTouches|.
41 EventType antecedent_event_type;
43 explicit DispositionHandlingInfo(int required_touches)
44 : required_touches(required_touches), antecedent_event_type(ET_UNKNOWN) {}
46 DispositionHandlingInfo(int required_touches,
47 EventType antecedent_event_type)
48 : required_touches(required_touches),
49 antecedent_event_type(antecedent_event_type) {}
52 DispositionHandlingInfo Info(int required_touches) {
53 return DispositionHandlingInfo(required_touches);
56 DispositionHandlingInfo Info(int required_touches,
57 EventType antecedent_event_type) {
58 return DispositionHandlingInfo(required_touches, antecedent_event_type);
61 // This approach to disposition handling is described at http://goo.gl/5G8PWJ.
62 DispositionHandlingInfo GetDispositionHandlingInfo(EventType type) {
64 case ET_GESTURE_TAP_DOWN:
65 return Info(RT_START);
66 case ET_GESTURE_TAP_CANCEL:
67 return Info(RT_START);
68 case ET_GESTURE_SHOW_PRESS:
69 return Info(RT_START);
70 case ET_GESTURE_LONG_PRESS:
71 return Info(RT_START);
72 case ET_GESTURE_LONG_TAP:
73 return Info(RT_START | RT_CURRENT);
75 return Info(RT_START | RT_CURRENT, ET_GESTURE_TAP_UNCONFIRMED);
76 case ET_GESTURE_TAP_UNCONFIRMED:
77 return Info(RT_START | RT_CURRENT);
78 case ET_GESTURE_DOUBLE_TAP:
79 return Info(RT_START | RT_CURRENT, ET_GESTURE_TAP_UNCONFIRMED);
80 case ET_GESTURE_SCROLL_BEGIN:
81 return Info(RT_START | RT_CURRENT);
82 case ET_GESTURE_SCROLL_UPDATE:
83 return Info(RT_CURRENT, ET_GESTURE_SCROLL_BEGIN);
84 case ET_GESTURE_SCROLL_END:
85 return Info(RT_NONE, ET_GESTURE_SCROLL_BEGIN);
86 case ET_SCROLL_FLING_START:
87 // We rely on |EndScrollGestureIfNecessary| to end the scroll if the fling
88 // start is prevented.
89 return Info(RT_NONE, ET_GESTURE_SCROLL_UPDATE);
90 case ET_SCROLL_FLING_CANCEL:
91 return Info(RT_NONE, ET_SCROLL_FLING_START);
92 case ET_GESTURE_PINCH_BEGIN:
93 return Info(RT_START, ET_GESTURE_SCROLL_BEGIN);
94 case ET_GESTURE_PINCH_UPDATE:
95 return Info(RT_CURRENT, ET_GESTURE_PINCH_BEGIN);
96 case ET_GESTURE_PINCH_END:
97 return Info(RT_NONE, ET_GESTURE_PINCH_BEGIN);
98 case ET_GESTURE_BEGIN:
99 return Info(RT_START);
101 return Info(RT_NONE, ET_GESTURE_BEGIN);
102 case ET_GESTURE_MULTIFINGER_SWIPE:
103 return Info(RT_START, ET_GESTURE_SCROLL_BEGIN);
108 return Info(RT_NONE);
111 int GetGestureTypeIndex(EventType type) {
112 DCHECK_GE(type, ET_GESTURE_TYPE_START);
113 DCHECK_LE(type, ET_GESTURE_TYPE_END);
114 return type - ET_GESTURE_TYPE_START;
117 bool IsTouchStartEvent(GestureEventDataPacket::GestureSource gesture_source) {
118 return gesture_source == GestureEventDataPacket::TOUCH_SEQUENCE_START ||
119 gesture_source == GestureEventDataPacket::TOUCH_START;
124 // TouchDispositionGestureFilter
126 TouchDispositionGestureFilter::TouchDispositionGestureFilter(
127 TouchDispositionGestureFilterClient* client)
129 needs_tap_ending_event_(false),
130 needs_show_press_event_(false),
131 needs_fling_ending_event_(false),
132 needs_scroll_ending_event_(false) {
136 TouchDispositionGestureFilter::~TouchDispositionGestureFilter() {}
138 TouchDispositionGestureFilter::PacketResult
139 TouchDispositionGestureFilter::OnGesturePacket(
140 const GestureEventDataPacket& packet) {
141 if (packet.gesture_source() == GestureEventDataPacket::UNDEFINED ||
142 packet.gesture_source() == GestureEventDataPacket::INVALID)
143 return INVALID_PACKET_TYPE;
145 if (packet.gesture_source() == GestureEventDataPacket::TOUCH_SEQUENCE_START)
146 sequences_.push(GestureSequence());
149 return INVALID_PACKET_ORDER;
151 if (packet.gesture_source() == GestureEventDataPacket::TOUCH_TIMEOUT &&
153 // Handle the timeout packet immediately if the packet preceding the timeout
154 // has already been dispatched.
155 FilterAndSendPacket(packet);
163 void TouchDispositionGestureFilter::OnTouchEventAck(bool event_consumed) {
164 // Spurious touch acks from the renderer should not trigger a crash.
165 if (IsEmpty() || (Head().empty() && sequences_.size() == 1))
169 PopGestureSequence();
171 GestureSequence& sequence = Head();
173 // Dispatch the packet corresponding to the ack'ed touch, as well as any
174 // additional timeout-based packets queued before the ack was received.
175 bool touch_packet_for_current_ack_handled = false;
176 while (!sequence.empty()) {
177 DCHECK_NE(sequence.front().gesture_source(),
178 GestureEventDataPacket::UNDEFINED);
179 DCHECK_NE(sequence.front().gesture_source(),
180 GestureEventDataPacket::INVALID);
182 GestureEventDataPacket::GestureSource source =
183 sequence.front().gesture_source();
184 if (source != GestureEventDataPacket::TOUCH_TIMEOUT) {
185 // We should handle at most one non-timeout based packet.
186 if (touch_packet_for_current_ack_handled)
188 state_.OnTouchEventAck(event_consumed, IsTouchStartEvent(source));
189 touch_packet_for_current_ack_handled = true;
191 // We need to pop the current sequence before sending the packet, because
192 // sending the packet could result in this method being re-entered (e.g. on
193 // Aura, we could trigger a touch-cancel). As popping the sequence destroys
194 // the packet, we copy the packet before popping it.
195 const GestureEventDataPacket packet = sequence.front();
197 FilterAndSendPacket(packet);
199 DCHECK(touch_packet_for_current_ack_handled);
202 bool TouchDispositionGestureFilter::IsEmpty() const {
203 return sequences_.empty();
206 void TouchDispositionGestureFilter::FilterAndSendPacket(
207 const GestureEventDataPacket& packet) {
208 if (packet.gesture_source() == GestureEventDataPacket::TOUCH_SEQUENCE_START) {
209 CancelTapIfNecessary(packet.timestamp());
210 EndScrollIfNecessary(packet.timestamp());
211 CancelFlingIfNecessary(packet.timestamp());
214 for (size_t i = 0; i < packet.gesture_count(); ++i) {
215 const GestureEventData& gesture = packet.gesture(i);
216 DCHECK(ET_GESTURE_TYPE_START <= gesture.type &&
217 gesture.type <= ET_GESTURE_TYPE_END);
218 if (state_.Filter(gesture.type)) {
219 CancelTapIfNecessary(gesture.time);
222 SendGesture(gesture);
225 if (packet.gesture_source() ==
226 GestureEventDataPacket::TOUCH_SEQUENCE_CANCEL) {
227 EndScrollIfNecessary(packet.timestamp());
228 CancelTapIfNecessary(packet.timestamp());
229 } else if (packet.gesture_source() ==
230 GestureEventDataPacket::TOUCH_SEQUENCE_END) {
231 EndScrollIfNecessary(packet.timestamp());
235 void TouchDispositionGestureFilter::SendGesture(const GestureEventData& event) {
236 // TODO(jdduke): Factor out gesture stream reparation code into a standalone
238 switch (event.type) {
239 case ET_GESTURE_LONG_TAP:
240 if (!needs_tap_ending_event_)
242 CancelTapIfNecessary(event.time);
243 CancelFlingIfNecessary(event.time);
245 case ET_GESTURE_TAP_DOWN:
246 DCHECK(!needs_tap_ending_event_);
247 ending_event_motion_event_id_ = event.motion_event_id;
248 needs_show_press_event_ = true;
249 needs_tap_ending_event_ = true;
251 case ET_GESTURE_SHOW_PRESS:
252 if (!needs_show_press_event_)
254 needs_show_press_event_ = false;
256 case ET_GESTURE_DOUBLE_TAP:
257 CancelTapIfNecessary(event.time);
258 needs_show_press_event_ = false;
261 if (needs_show_press_event_) {
262 GestureEventData show_press_event(event);
263 show_press_event.type = ET_GESTURE_SHOW_PRESS;
264 SendGesture(show_press_event);
265 DCHECK(!needs_show_press_event_);
267 needs_tap_ending_event_ = false;
269 case ET_GESTURE_TAP_CANCEL:
270 needs_show_press_event_ = false;
271 needs_tap_ending_event_ = false;
273 case ET_GESTURE_SCROLL_BEGIN:
274 CancelTapIfNecessary(event.time);
275 CancelFlingIfNecessary(event.time);
276 EndScrollIfNecessary(event.time);
277 ending_event_motion_event_id_ = event.motion_event_id;
278 needs_scroll_ending_event_ = true;
280 case ET_GESTURE_SCROLL_END:
281 needs_scroll_ending_event_ = false;
283 case ET_SCROLL_FLING_START:
284 CancelFlingIfNecessary(event.time);
285 ending_event_motion_event_id_ = event.motion_event_id;
286 needs_fling_ending_event_ = true;
287 needs_scroll_ending_event_ = false;
289 case ET_SCROLL_FLING_CANCEL:
290 needs_fling_ending_event_ = false;
295 client_->ForwardGestureEvent(event);
298 void TouchDispositionGestureFilter::CancelTapIfNecessary(
299 const base::TimeTicks& timestamp) {
300 if (!needs_tap_ending_event_)
303 SendGesture(CreateGesture(
304 ET_GESTURE_TAP_CANCEL, ending_event_motion_event_id_, timestamp));
305 DCHECK(!needs_tap_ending_event_);
308 void TouchDispositionGestureFilter::CancelFlingIfNecessary(
309 const base::TimeTicks& timestamp) {
310 if (!needs_fling_ending_event_)
313 SendGesture(CreateGesture(
314 ET_SCROLL_FLING_CANCEL, ending_event_motion_event_id_, timestamp));
315 DCHECK(!needs_fling_ending_event_);
318 void TouchDispositionGestureFilter::EndScrollIfNecessary(
319 const base::TimeTicks& timestamp) {
320 if (!needs_scroll_ending_event_)
323 SendGesture(CreateGesture(
324 ET_GESTURE_SCROLL_END, ending_event_motion_event_id_, timestamp));
325 DCHECK(!needs_scroll_ending_event_);
328 void TouchDispositionGestureFilter::PopGestureSequence() {
329 DCHECK(Head().empty());
330 state_ = GestureHandlingState();
334 TouchDispositionGestureFilter::GestureSequence&
335 TouchDispositionGestureFilter::Head() {
336 DCHECK(!sequences_.empty());
337 return sequences_.front();
340 TouchDispositionGestureFilter::GestureSequence&
341 TouchDispositionGestureFilter::Tail() {
342 DCHECK(!sequences_.empty());
343 return sequences_.back();
346 // TouchDispositionGestureFilter::GestureHandlingState
348 TouchDispositionGestureFilter::GestureHandlingState::GestureHandlingState()
349 : start_touch_consumed_(false),
350 current_touch_consumed_(false) {}
352 void TouchDispositionGestureFilter::GestureHandlingState::OnTouchEventAck(
354 bool is_touch_start_event) {
355 current_touch_consumed_ = event_consumed;
356 if (event_consumed && is_touch_start_event)
357 start_touch_consumed_ = true;
360 bool TouchDispositionGestureFilter::GestureHandlingState::Filter(
361 EventType gesture_type) {
362 DispositionHandlingInfo disposition_handling_info =
363 GetDispositionHandlingInfo(gesture_type);
365 int required_touches = disposition_handling_info.required_touches;
366 EventType antecedent_event_type =
367 disposition_handling_info.antecedent_event_type;
368 if ((required_touches & RT_START && start_touch_consumed_) ||
369 (required_touches & RT_CURRENT && current_touch_consumed_) ||
370 (antecedent_event_type != ET_UNKNOWN &&
371 last_gesture_of_type_dropped_.has_bit(
372 GetGestureTypeIndex(antecedent_event_type)))) {
373 last_gesture_of_type_dropped_.mark_bit(GetGestureTypeIndex(gesture_type));
376 last_gesture_of_type_dropped_.clear_bit(GetGestureTypeIndex(gesture_type));
380 } // namespace content