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