touch_disposition_gesture_filter.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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 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 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); 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 DCHECK(client_); 136} 137 138TouchDispositionGestureFilter::~TouchDispositionGestureFilter() { 139} 140 141TouchDispositionGestureFilter::PacketResult 142TouchDispositionGestureFilter::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 166void 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 205bool TouchDispositionGestureFilter::IsEmpty() const { 206 return sequences_.empty(); 207} 208 209void 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 247void 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 312void 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 324void 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 336void 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 348void TouchDispositionGestureFilter::PopGestureSequence() { 349 DCHECK(Head().empty()); 350 state_ = GestureHandlingState(); 351 sequences_.pop(); 352} 353 354TouchDispositionGestureFilter::GestureSequence& 355TouchDispositionGestureFilter::Head() { 356 DCHECK(!sequences_.empty()); 357 return sequences_.front(); 358} 359 360TouchDispositionGestureFilter::GestureSequence& 361TouchDispositionGestureFilter::Tail() { 362 DCHECK(!sequences_.empty()); 363 return sequences_.back(); 364} 365 366// TouchDispositionGestureFilter::GestureHandlingState 367 368TouchDispositionGestureFilter::GestureHandlingState::GestureHandlingState() 369 : start_touch_consumed_(false), 370 current_touch_consumed_(false) {} 371 372void 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 380bool 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