touch_disposition_gesture_filter.cc revision 010d83a9304c5a91596085d917d248abff47903a
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 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 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_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 111int 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 117bool 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 126TouchDispositionGestureFilter::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 136TouchDispositionGestureFilter::~TouchDispositionGestureFilter() {} 137 138TouchDispositionGestureFilter::PacketResult 139TouchDispositionGestureFilter::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 163void 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 202bool TouchDispositionGestureFilter::IsEmpty() const { 203 return sequences_.empty(); 204} 205 206void 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 235void 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 298void 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 308void 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 318void 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 328void TouchDispositionGestureFilter::PopGestureSequence() { 329 DCHECK(Head().empty()); 330 state_ = GestureHandlingState(); 331 sequences_.pop(); 332} 333 334TouchDispositionGestureFilter::GestureSequence& 335TouchDispositionGestureFilter::Head() { 336 DCHECK(!sequences_.empty()); 337 return sequences_.front(); 338} 339 340TouchDispositionGestureFilter::GestureSequence& 341TouchDispositionGestureFilter::Tail() { 342 DCHECK(!sequences_.empty()); 343 return sequences_.back(); 344} 345 346// TouchDispositionGestureFilter::GestureHandlingState 347 348TouchDispositionGestureFilter::GestureHandlingState::GestureHandlingState() 349 : start_touch_consumed_(false), 350 current_touch_consumed_(false) {} 351 352void 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 360bool 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 381