• 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 "ui/events/gesture_detection/touch_disposition_gesture_filter.h"
6 
7 #include "base/auto_reset.h"
8 #include "base/logging.h"
9 #include "ui/events/gesture_event_details.h"
10 
11 namespace ui {
12 namespace {
13 
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);
17 
CreateGesture(EventType type,int motion_event_id,const GestureEventDataPacket & packet)18 GestureEventData CreateGesture(EventType type,
19                                int motion_event_id,
20                                const GestureEventDataPacket& packet) {
21   return GestureEventData(GestureEventDetails(type, 0, 0),
22                           motion_event_id,
23                           packet.timestamp(),
24                           packet.touch_location().x(),
25                           packet.touch_location().y(),
26                           packet.raw_touch_location().x(),
27                           packet.raw_touch_location().y(),
28                           1,
29                           gfx::RectF(packet.touch_location(), gfx::SizeF()));
30 }
31 
32 enum RequiredTouches {
33   RT_NONE = 0,
34   RT_START = 1 << 0,
35   RT_CURRENT = 1 << 1,
36 };
37 
38 struct DispositionHandlingInfo {
39   // A bitwise-OR of |RequiredTouches|.
40   int required_touches;
41   EventType antecedent_event_type;
42 
DispositionHandlingInfoui::__anon957c65da0111::DispositionHandlingInfo43   explicit DispositionHandlingInfo(int required_touches)
44       : required_touches(required_touches), antecedent_event_type(ET_UNKNOWN) {}
45 
DispositionHandlingInfoui::__anon957c65da0111::DispositionHandlingInfo46   DispositionHandlingInfo(int required_touches,
47                           EventType antecedent_event_type)
48       : required_touches(required_touches),
49         antecedent_event_type(antecedent_event_type) {}
50 };
51 
Info(int required_touches)52 DispositionHandlingInfo Info(int required_touches) {
53   return DispositionHandlingInfo(required_touches);
54 }
55 
Info(int required_touches,EventType antecedent_event_type)56 DispositionHandlingInfo Info(int required_touches,
57                              EventType antecedent_event_type) {
58   return DispositionHandlingInfo(required_touches, antecedent_event_type);
59 }
60 
61 // This approach to disposition handling is described at http://goo.gl/5G8PWJ.
GetDispositionHandlingInfo(EventType type)62 DispositionHandlingInfo GetDispositionHandlingInfo(EventType type) {
63   switch (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);
74     case ET_GESTURE_TAP:
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);
100     case ET_GESTURE_END:
101       return Info(RT_NONE, ET_GESTURE_BEGIN);
102     case ET_GESTURE_SWIPE:
103       return Info(RT_START, ET_GESTURE_SCROLL_BEGIN);
104     case ET_GESTURE_TWO_FINGER_TAP:
105       return Info(RT_START);
106     default:
107       break;
108   }
109   NOTREACHED();
110   return Info(RT_NONE);
111 }
112 
GetGestureTypeIndex(EventType type)113 int GetGestureTypeIndex(EventType type) {
114   DCHECK_GE(type, ET_GESTURE_TYPE_START);
115   DCHECK_LE(type, ET_GESTURE_TYPE_END);
116   return type - ET_GESTURE_TYPE_START;
117 }
118 
IsTouchStartEvent(GestureEventDataPacket::GestureSource gesture_source)119 bool IsTouchStartEvent(GestureEventDataPacket::GestureSource gesture_source) {
120   return gesture_source == GestureEventDataPacket::TOUCH_SEQUENCE_START ||
121          gesture_source == GestureEventDataPacket::TOUCH_START;
122 }
123 
124 }  // namespace
125 
126 // TouchDispositionGestureFilter
127 
TouchDispositionGestureFilter(TouchDispositionGestureFilterClient * client)128 TouchDispositionGestureFilter::TouchDispositionGestureFilter(
129     TouchDispositionGestureFilterClient* client)
130     : client_(client),
131       needs_tap_ending_event_(false),
132       needs_show_press_event_(false),
133       needs_fling_ending_event_(false),
134       needs_scroll_ending_event_(false) {
135   DCHECK(client_);
136 }
137 
~TouchDispositionGestureFilter()138 TouchDispositionGestureFilter::~TouchDispositionGestureFilter() {
139 }
140 
141 TouchDispositionGestureFilter::PacketResult
OnGesturePacket(const GestureEventDataPacket & packet)142 TouchDispositionGestureFilter::OnGesturePacket(
143     const GestureEventDataPacket& packet) {
144   if (packet.gesture_source() == GestureEventDataPacket::UNDEFINED ||
145       packet.gesture_source() == GestureEventDataPacket::INVALID)
146     return INVALID_PACKET_TYPE;
147 
148   if (packet.gesture_source() == GestureEventDataPacket::TOUCH_SEQUENCE_START)
149     sequences_.push(GestureSequence());
150 
151   if (IsEmpty())
152     return INVALID_PACKET_ORDER;
153 
154   if (packet.gesture_source() == GestureEventDataPacket::TOUCH_TIMEOUT &&
155       Tail().empty()) {
156     // Handle the timeout packet immediately if the packet preceding the timeout
157     // has already been dispatched.
158     FilterAndSendPacket(packet);
159     return SUCCESS;
160   }
161 
162   Tail().push(packet);
163   return SUCCESS;
164 }
165 
OnTouchEventAck(bool event_consumed)166 void TouchDispositionGestureFilter::OnTouchEventAck(bool event_consumed) {
167   // Spurious touch acks from the renderer should not trigger a crash.
168   if (IsEmpty() || (Head().empty() && sequences_.size() == 1))
169     return;
170 
171   if (Head().empty())
172     PopGestureSequence();
173 
174   GestureSequence& sequence = Head();
175 
176   // Dispatch the packet corresponding to the ack'ed touch, as well as any
177   // additional timeout-based packets queued before the ack was received.
178   bool touch_packet_for_current_ack_handled = false;
179   while (!sequence.empty()) {
180     DCHECK_NE(sequence.front().gesture_source(),
181               GestureEventDataPacket::UNDEFINED);
182     DCHECK_NE(sequence.front().gesture_source(),
183               GestureEventDataPacket::INVALID);
184 
185     GestureEventDataPacket::GestureSource source =
186         sequence.front().gesture_source();
187     if (source != GestureEventDataPacket::TOUCH_TIMEOUT) {
188       // We should handle at most one non-timeout based packet.
189       if (touch_packet_for_current_ack_handled)
190         break;
191       state_.OnTouchEventAck(event_consumed, IsTouchStartEvent(source));
192       touch_packet_for_current_ack_handled = true;
193     }
194     // We need to pop the current sequence before sending the packet, because
195     // sending the packet could result in this method being re-entered (e.g. on
196     // Aura, we could trigger a touch-cancel). As popping the sequence destroys
197     // the packet, we copy the packet before popping it.
198     const GestureEventDataPacket packet = sequence.front();
199     sequence.pop();
200     FilterAndSendPacket(packet);
201   }
202   DCHECK(touch_packet_for_current_ack_handled);
203 }
204 
IsEmpty() const205 bool TouchDispositionGestureFilter::IsEmpty() const {
206   return sequences_.empty();
207 }
208 
FilterAndSendPacket(const GestureEventDataPacket & packet)209 void TouchDispositionGestureFilter::FilterAndSendPacket(
210     const GestureEventDataPacket& packet) {
211   if (packet.gesture_source() == GestureEventDataPacket::TOUCH_SEQUENCE_START) {
212     CancelTapIfNecessary(packet);
213     EndScrollIfNecessary(packet);
214     CancelFlingIfNecessary(packet);
215   } else if (packet.gesture_source() == GestureEventDataPacket::TOUCH_START) {
216     CancelTapIfNecessary(packet);
217   }
218 
219   for (size_t i = 0; i < packet.gesture_count(); ++i) {
220     const GestureEventData& gesture = packet.gesture(i);
221     DCHECK_GE(gesture.details.type(), ET_GESTURE_TYPE_START);
222     DCHECK_LE(gesture.details.type(), ET_GESTURE_TYPE_END);
223     if (state_.Filter(gesture.details.type())) {
224       CancelTapIfNecessary(packet);
225       continue;
226     }
227     if (packet.gesture_source() == GestureEventDataPacket::TOUCH_TIMEOUT) {
228       // Sending a timed gesture could delete |this|, so we need to return
229       // directly after the |SendGesture| call.
230       SendGesture(gesture, packet);
231       return;
232     }
233 
234     SendGesture(gesture, packet);
235   }
236 
237   if (packet.gesture_source() ==
238       GestureEventDataPacket::TOUCH_SEQUENCE_CANCEL) {
239     EndScrollIfNecessary(packet);
240     CancelTapIfNecessary(packet);
241   } else if (packet.gesture_source() ==
242              GestureEventDataPacket::TOUCH_SEQUENCE_END) {
243     EndScrollIfNecessary(packet);
244   }
245 }
246 
SendGesture(const GestureEventData & event,const GestureEventDataPacket & packet_being_sent)247 void TouchDispositionGestureFilter::SendGesture(
248     const GestureEventData& event,
249     const GestureEventDataPacket& packet_being_sent) {
250   // TODO(jdduke): Factor out gesture stream reparation code into a standalone
251   // utility class.
252   switch (event.type()) {
253     case ET_GESTURE_LONG_TAP:
254       if (!needs_tap_ending_event_)
255         return;
256       CancelTapIfNecessary(packet_being_sent);
257       CancelFlingIfNecessary(packet_being_sent);
258       break;
259     case ET_GESTURE_TAP_DOWN:
260       DCHECK(!needs_tap_ending_event_);
261       ending_event_motion_event_id_ = event.motion_event_id;
262       needs_show_press_event_ = true;
263       needs_tap_ending_event_ = true;
264       break;
265     case ET_GESTURE_SHOW_PRESS:
266       if (!needs_show_press_event_)
267         return;
268       needs_show_press_event_ = false;
269       break;
270     case ET_GESTURE_DOUBLE_TAP:
271       CancelTapIfNecessary(packet_being_sent);
272       needs_show_press_event_ = false;
273       break;
274     case ET_GESTURE_TAP:
275       DCHECK(needs_tap_ending_event_);
276       if (needs_show_press_event_) {
277         SendGesture(GestureEventData(ET_GESTURE_SHOW_PRESS, event),
278                     packet_being_sent);
279         DCHECK(!needs_show_press_event_);
280       }
281       needs_tap_ending_event_ = false;
282       break;
283     case ET_GESTURE_TAP_CANCEL:
284       needs_show_press_event_ = false;
285       needs_tap_ending_event_ = false;
286       break;
287     case ET_GESTURE_SCROLL_BEGIN:
288       CancelTapIfNecessary(packet_being_sent);
289       CancelFlingIfNecessary(packet_being_sent);
290       EndScrollIfNecessary(packet_being_sent);
291       ending_event_motion_event_id_ = event.motion_event_id;
292       needs_scroll_ending_event_ = true;
293       break;
294     case ET_GESTURE_SCROLL_END:
295       needs_scroll_ending_event_ = false;
296       break;
297     case ET_SCROLL_FLING_START:
298       CancelFlingIfNecessary(packet_being_sent);
299       ending_event_motion_event_id_ = event.motion_event_id;
300       needs_fling_ending_event_ = true;
301       needs_scroll_ending_event_ = false;
302       break;
303     case ET_SCROLL_FLING_CANCEL:
304       needs_fling_ending_event_ = false;
305       break;
306     default:
307       break;
308   }
309   client_->ForwardGestureEvent(event);
310 }
311 
CancelTapIfNecessary(const GestureEventDataPacket & packet_being_sent)312 void TouchDispositionGestureFilter::CancelTapIfNecessary(
313     const GestureEventDataPacket& packet_being_sent) {
314   if (!needs_tap_ending_event_)
315     return;
316 
317   SendGesture(CreateGesture(ET_GESTURE_TAP_CANCEL,
318                             ending_event_motion_event_id_,
319                             packet_being_sent),
320               packet_being_sent);
321   DCHECK(!needs_tap_ending_event_);
322 }
323 
CancelFlingIfNecessary(const GestureEventDataPacket & packet_being_sent)324 void TouchDispositionGestureFilter::CancelFlingIfNecessary(
325     const GestureEventDataPacket& packet_being_sent) {
326   if (!needs_fling_ending_event_)
327     return;
328 
329   SendGesture(CreateGesture(ET_SCROLL_FLING_CANCEL,
330                             ending_event_motion_event_id_,
331                             packet_being_sent),
332               packet_being_sent);
333   DCHECK(!needs_fling_ending_event_);
334 }
335 
EndScrollIfNecessary(const GestureEventDataPacket & packet_being_sent)336 void TouchDispositionGestureFilter::EndScrollIfNecessary(
337     const GestureEventDataPacket& packet_being_sent) {
338   if (!needs_scroll_ending_event_)
339     return;
340 
341   SendGesture(CreateGesture(ET_GESTURE_SCROLL_END,
342                             ending_event_motion_event_id_,
343                             packet_being_sent),
344               packet_being_sent);
345   DCHECK(!needs_scroll_ending_event_);
346 }
347 
PopGestureSequence()348 void TouchDispositionGestureFilter::PopGestureSequence() {
349   DCHECK(Head().empty());
350   state_ = GestureHandlingState();
351   sequences_.pop();
352 }
353 
354 TouchDispositionGestureFilter::GestureSequence&
Head()355 TouchDispositionGestureFilter::Head() {
356   DCHECK(!sequences_.empty());
357   return sequences_.front();
358 }
359 
360 TouchDispositionGestureFilter::GestureSequence&
Tail()361 TouchDispositionGestureFilter::Tail() {
362   DCHECK(!sequences_.empty());
363   return sequences_.back();
364 }
365 
366 // TouchDispositionGestureFilter::GestureHandlingState
367 
GestureHandlingState()368 TouchDispositionGestureFilter::GestureHandlingState::GestureHandlingState()
369     : start_touch_consumed_(false),
370       current_touch_consumed_(false) {}
371 
OnTouchEventAck(bool event_consumed,bool is_touch_start_event)372 void TouchDispositionGestureFilter::GestureHandlingState::OnTouchEventAck(
373     bool event_consumed,
374     bool is_touch_start_event) {
375   current_touch_consumed_ = event_consumed;
376   if (event_consumed && is_touch_start_event)
377     start_touch_consumed_ = true;
378 }
379 
Filter(EventType gesture_type)380 bool TouchDispositionGestureFilter::GestureHandlingState::Filter(
381     EventType gesture_type) {
382   DispositionHandlingInfo disposition_handling_info =
383       GetDispositionHandlingInfo(gesture_type);
384 
385   int required_touches = disposition_handling_info.required_touches;
386   EventType antecedent_event_type =
387       disposition_handling_info.antecedent_event_type;
388   if ((required_touches & RT_START && start_touch_consumed_) ||
389       (required_touches & RT_CURRENT && current_touch_consumed_) ||
390       (antecedent_event_type != ET_UNKNOWN &&
391        last_gesture_of_type_dropped_.has_bit(
392            GetGestureTypeIndex(antecedent_event_type)))) {
393     last_gesture_of_type_dropped_.mark_bit(GetGestureTypeIndex(gesture_type));
394     return true;
395   }
396   last_gesture_of_type_dropped_.clear_bit(GetGestureTypeIndex(gesture_type));
397   return false;
398 }
399 
400 }  // namespace content
401