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