Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / events / gesture_detection / touch_disposition_gesture_filter.cc
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
18 GestureEventData CreateGesture(EventType type,
19                                int motion_event_id,
20                                const base::TimeTicks& timestamp) {
21   GestureEventDetails details(type, 0, 0);
22   return GestureEventData(type,
23                           motion_event_id,
24                           timestamp,
25                           0,
26                           0,
27                           1,
28                           gfx::RectF(0, 0, 0, 0),
29                           details);
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
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
52 DispositionHandlingInfo Info(int required_touches) {
53   return DispositionHandlingInfo(required_touches);
54 }
55
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.
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_MULTIFINGER_SWIPE:
103       return Info(RT_START, ET_GESTURE_SCROLL_BEGIN);
104     default:
105       break;
106   }
107   NOTREACHED();
108   return Info(RT_NONE);
109 }
110
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;
115 }
116
117 bool IsTouchStartEvent(GestureEventDataPacket::GestureSource gesture_source) {
118   return gesture_source == GestureEventDataPacket::TOUCH_SEQUENCE_START ||
119          gesture_source == GestureEventDataPacket::TOUCH_START;
120 }
121
122 }  // namespace
123
124 // TouchDispositionGestureFilter
125
126 TouchDispositionGestureFilter::TouchDispositionGestureFilter(
127     TouchDispositionGestureFilterClient* client)
128     : client_(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) {
133   DCHECK(client_);
134 }
135
136 TouchDispositionGestureFilter::~TouchDispositionGestureFilter() {}
137
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;
144
145   if (packet.gesture_source() == GestureEventDataPacket::TOUCH_SEQUENCE_START)
146     sequences_.push(GestureSequence());
147
148   if (IsEmpty())
149     return INVALID_PACKET_ORDER;
150
151   if (packet.gesture_source() == GestureEventDataPacket::TOUCH_TIMEOUT &&
152       Tail().empty()) {
153     // Handle the timeout packet immediately if the packet preceding the timeout
154     // has already been dispatched.
155     FilterAndSendPacket(packet);
156     return SUCCESS;
157   }
158
159   Tail().push(packet);
160   return SUCCESS;
161 }
162
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))
166     return;
167
168   if (Head().empty())
169     PopGestureSequence();
170
171   GestureSequence& sequence = Head();
172
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);
181
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)
187         break;
188       state_.OnTouchEventAck(event_consumed, IsTouchStartEvent(source));
189       touch_packet_for_current_ack_handled = true;
190     }
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();
196     sequence.pop();
197     FilterAndSendPacket(packet);
198   }
199   DCHECK(touch_packet_for_current_ack_handled);
200 }
201
202 bool TouchDispositionGestureFilter::IsEmpty() const {
203   return sequences_.empty();
204 }
205
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());
212   }
213
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);
220       continue;
221     }
222     SendGesture(gesture);
223   }
224
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());
232   }
233 }
234
235 void TouchDispositionGestureFilter::SendGesture(const GestureEventData& event) {
236   // TODO(jdduke): Factor out gesture stream reparation code into a standalone
237   // utility class.
238   switch (event.type) {
239     case ET_GESTURE_LONG_TAP:
240       if (!needs_tap_ending_event_)
241         return;
242       CancelTapIfNecessary(event.time);
243       CancelFlingIfNecessary(event.time);
244       break;
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;
250       break;
251     case ET_GESTURE_SHOW_PRESS:
252       if (!needs_show_press_event_)
253         return;
254       needs_show_press_event_ = false;
255       break;
256     case ET_GESTURE_DOUBLE_TAP:
257       CancelTapIfNecessary(event.time);
258       needs_show_press_event_ = false;
259       break;
260     case ET_GESTURE_TAP:
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_);
266       }
267       needs_tap_ending_event_ = false;
268       break;
269     case ET_GESTURE_TAP_CANCEL:
270       needs_show_press_event_ = false;
271       needs_tap_ending_event_ = false;
272       break;
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;
279       break;
280     case ET_GESTURE_SCROLL_END:
281       needs_scroll_ending_event_ = false;
282       break;
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;
288       break;
289     case ET_SCROLL_FLING_CANCEL:
290       needs_fling_ending_event_ = false;
291       break;
292     default:
293       break;
294   }
295   client_->ForwardGestureEvent(event);
296 }
297
298 void TouchDispositionGestureFilter::CancelTapIfNecessary(
299     const base::TimeTicks& timestamp) {
300   if (!needs_tap_ending_event_)
301     return;
302
303   SendGesture(CreateGesture(
304       ET_GESTURE_TAP_CANCEL, ending_event_motion_event_id_, timestamp));
305   DCHECK(!needs_tap_ending_event_);
306 }
307
308 void TouchDispositionGestureFilter::CancelFlingIfNecessary(
309     const base::TimeTicks& timestamp) {
310   if (!needs_fling_ending_event_)
311     return;
312
313   SendGesture(CreateGesture(
314       ET_SCROLL_FLING_CANCEL, ending_event_motion_event_id_, timestamp));
315   DCHECK(!needs_fling_ending_event_);
316 }
317
318 void TouchDispositionGestureFilter::EndScrollIfNecessary(
319     const base::TimeTicks& timestamp) {
320   if (!needs_scroll_ending_event_)
321     return;
322
323   SendGesture(CreateGesture(
324       ET_GESTURE_SCROLL_END, ending_event_motion_event_id_, timestamp));
325   DCHECK(!needs_scroll_ending_event_);
326 }
327
328 void TouchDispositionGestureFilter::PopGestureSequence() {
329   DCHECK(Head().empty());
330   state_ = GestureHandlingState();
331   sequences_.pop();
332 }
333
334 TouchDispositionGestureFilter::GestureSequence&
335 TouchDispositionGestureFilter::Head() {
336   DCHECK(!sequences_.empty());
337   return sequences_.front();
338 }
339
340 TouchDispositionGestureFilter::GestureSequence&
341 TouchDispositionGestureFilter::Tail() {
342   DCHECK(!sequences_.empty());
343   return sequences_.back();
344 }
345
346 // TouchDispositionGestureFilter::GestureHandlingState
347
348 TouchDispositionGestureFilter::GestureHandlingState::GestureHandlingState()
349     : start_touch_consumed_(false),
350       current_touch_consumed_(false) {}
351
352 void TouchDispositionGestureFilter::GestureHandlingState::OnTouchEventAck(
353     bool event_consumed,
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;
358 }
359
360 bool TouchDispositionGestureFilter::GestureHandlingState::Filter(
361     EventType gesture_type) {
362   DispositionHandlingInfo disposition_handling_info =
363       GetDispositionHandlingInfo(gesture_type);
364
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));
374     return true;
375   }
376   last_gesture_of_type_dropped_.clear_bit(GetGestureTypeIndex(gesture_type));
377   return false;
378 }
379
380 }  // namespace content