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