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 "content/browser/renderer_host/input/gesture_event_queue.h" 6 7 #include "base/debug/trace_event.h" 8 #include "base/strings/string_number_conversions.h" 9 #include "content/browser/renderer_host/input/input_router.h" 10 #include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h" 11 #include "content/browser/renderer_host/input/touchscreen_tap_suppression_controller.h" 12 #include "content/public/common/content_switches.h" 13 14 using blink::WebGestureEvent; 15 using blink::WebInputEvent; 16 17 namespace content { 18 19 GestureEventQueue::Config::Config() { 20 } 21 22 GestureEventQueue::GestureEventQueue( 23 GestureEventQueueClient* client, 24 TouchpadTapSuppressionControllerClient* touchpad_client, 25 const Config& config) 26 : client_(client), 27 fling_in_progress_(false), 28 scrolling_in_progress_(false), 29 ignore_next_ack_(false), 30 touchpad_tap_suppression_controller_( 31 touchpad_client, 32 config.touchpad_tap_suppression_config), 33 touchscreen_tap_suppression_controller_( 34 this, 35 config.touchscreen_tap_suppression_config), 36 debounce_interval_(config.debounce_interval) { 37 DCHECK(client); 38 DCHECK(touchpad_client); 39 } 40 41 GestureEventQueue::~GestureEventQueue() { } 42 43 bool GestureEventQueue::ShouldDiscardFlingCancelEvent( 44 const GestureEventWithLatencyInfo& gesture_event) const { 45 if (coalesced_gesture_events_.empty() && fling_in_progress_) 46 return false; 47 GestureQueue::const_reverse_iterator it = 48 coalesced_gesture_events_.rbegin(); 49 while (it != coalesced_gesture_events_.rend()) { 50 if (it->event.type == WebInputEvent::GestureFlingStart) 51 return false; 52 if (it->event.type == WebInputEvent::GestureFlingCancel) 53 return true; 54 it++; 55 } 56 return true; 57 } 58 59 bool GestureEventQueue::ShouldForwardForBounceReduction( 60 const GestureEventWithLatencyInfo& gesture_event) { 61 if (debounce_interval_ <= base::TimeDelta()) 62 return true; 63 switch (gesture_event.event.type) { 64 case WebInputEvent::GestureScrollUpdate: 65 if (!scrolling_in_progress_) { 66 debounce_deferring_timer_.Start( 67 FROM_HERE, 68 debounce_interval_, 69 this, 70 &GestureEventQueue::SendScrollEndingEventsNow); 71 } else { 72 // Extend the bounce interval. 73 debounce_deferring_timer_.Reset(); 74 } 75 scrolling_in_progress_ = true; 76 debouncing_deferral_queue_.clear(); 77 return true; 78 case WebInputEvent::GesturePinchBegin: 79 case WebInputEvent::GesturePinchEnd: 80 case WebInputEvent::GesturePinchUpdate: 81 // TODO(rjkroege): Debounce pinch (http://crbug.com/147647) 82 return true; 83 default: 84 if (scrolling_in_progress_) { 85 debouncing_deferral_queue_.push_back(gesture_event); 86 return false; 87 } 88 return true; 89 } 90 } 91 92 // NOTE: The filters are applied successively. This simplifies the change. 93 bool GestureEventQueue::ShouldForward( 94 const GestureEventWithLatencyInfo& gesture_event) { 95 TRACE_EVENT0("input", "GestureEventQueue::ShouldForward"); 96 return ShouldForwardForBounceReduction(gesture_event) && 97 ShouldForwardForGFCFiltering(gesture_event) && 98 ShouldForwardForTapSuppression(gesture_event) && 99 ShouldForwardForCoalescing(gesture_event); 100 } 101 102 bool GestureEventQueue::ShouldForwardForGFCFiltering( 103 const GestureEventWithLatencyInfo& gesture_event) const { 104 return gesture_event.event.type != WebInputEvent::GestureFlingCancel || 105 !ShouldDiscardFlingCancelEvent(gesture_event); 106 } 107 108 bool GestureEventQueue::ShouldForwardForTapSuppression( 109 const GestureEventWithLatencyInfo& gesture_event) { 110 switch (gesture_event.event.type) { 111 case WebInputEvent::GestureFlingCancel: 112 if (gesture_event.event.sourceDevice == 113 blink::WebGestureDeviceTouchscreen) 114 touchscreen_tap_suppression_controller_.GestureFlingCancel(); 115 else 116 touchpad_tap_suppression_controller_.GestureFlingCancel(); 117 return true; 118 case WebInputEvent::GestureTapDown: 119 case WebInputEvent::GestureShowPress: 120 case WebInputEvent::GestureTapUnconfirmed: 121 case WebInputEvent::GestureTapCancel: 122 case WebInputEvent::GestureTap: 123 case WebInputEvent::GestureDoubleTap: 124 if (gesture_event.event.sourceDevice == 125 blink::WebGestureDeviceTouchscreen) { 126 return !touchscreen_tap_suppression_controller_.FilterTapEvent( 127 gesture_event); 128 } 129 return true; 130 default: 131 return true; 132 } 133 } 134 135 bool GestureEventQueue::ShouldForwardForCoalescing( 136 const GestureEventWithLatencyInfo& gesture_event) { 137 switch (gesture_event.event.type) { 138 case WebInputEvent::GestureFlingCancel: 139 fling_in_progress_ = false; 140 break; 141 case WebInputEvent::GestureFlingStart: 142 fling_in_progress_ = true; 143 break; 144 case WebInputEvent::GesturePinchUpdate: 145 case WebInputEvent::GestureScrollUpdate: 146 MergeOrInsertScrollAndPinchEvent(gesture_event); 147 return ShouldHandleEventNow(); 148 default: 149 break; 150 } 151 coalesced_gesture_events_.push_back(gesture_event); 152 return ShouldHandleEventNow(); 153 } 154 155 void GestureEventQueue::ProcessGestureAck(InputEventAckState ack_result, 156 WebInputEvent::Type type, 157 const ui::LatencyInfo& latency) { 158 TRACE_EVENT0("input", "GestureEventQueue::ProcessGestureAck"); 159 160 if (coalesced_gesture_events_.empty()) { 161 DLOG(ERROR) << "Received unexpected ACK for event type " << type; 162 return; 163 } 164 165 // It's possible that the ack for the second event in an in-flight, coalesced 166 // Gesture{Scroll,Pinch}Update pair is received prior to the first event ack. 167 // TODO(jdduke): Unify GSU/GPU pairs into a single event, crbug.com/359115. 168 size_t event_index = 0; 169 if (ignore_next_ack_ && 170 coalesced_gesture_events_.size() > 1 && 171 coalesced_gesture_events_[0].event.type != type && 172 coalesced_gesture_events_[1].event.type == type) { 173 event_index = 1; 174 } 175 GestureEventWithLatencyInfo event_with_latency = 176 coalesced_gesture_events_[event_index]; 177 DCHECK_EQ(event_with_latency.event.type, type); 178 event_with_latency.latency.AddNewLatencyFrom(latency); 179 180 // Ack'ing an event may enqueue additional gesture events. By ack'ing the 181 // event before the forwarding of queued events below, such additional events 182 // can be coalesced with existing queued events prior to dispatch. 183 client_->OnGestureEventAck(event_with_latency, ack_result); 184 185 const bool processed = (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result); 186 if (type == WebInputEvent::GestureFlingCancel) { 187 if (event_with_latency.event.sourceDevice == 188 blink::WebGestureDeviceTouchscreen) 189 touchscreen_tap_suppression_controller_.GestureFlingCancelAck(processed); 190 else 191 touchpad_tap_suppression_controller_.GestureFlingCancelAck(processed); 192 } 193 DCHECK_LT(event_index, coalesced_gesture_events_.size()); 194 coalesced_gesture_events_.erase(coalesced_gesture_events_.begin() + 195 event_index); 196 197 if (ignore_next_ack_) { 198 ignore_next_ack_ = false; 199 return; 200 } 201 202 if (coalesced_gesture_events_.empty()) 203 return; 204 205 const GestureEventWithLatencyInfo& first_gesture_event = 206 coalesced_gesture_events_.front(); 207 208 // TODO(jdduke): Unify GSU/GPU pairs into a single event, crbug.com/359115. 209 // Check for the coupled GesturePinchUpdate before sending either event, 210 // handling the case where the first GestureScrollUpdate ack is synchronous. 211 GestureEventWithLatencyInfo second_gesture_event; 212 if (first_gesture_event.event.type == WebInputEvent::GestureScrollUpdate && 213 coalesced_gesture_events_.size() > 1 && 214 coalesced_gesture_events_[1].event.type == 215 WebInputEvent::GesturePinchUpdate) { 216 second_gesture_event = coalesced_gesture_events_[1]; 217 ignore_next_ack_ = true; 218 } 219 220 client_->SendGestureEventImmediately(first_gesture_event); 221 if (second_gesture_event.event.type != WebInputEvent::Undefined) 222 client_->SendGestureEventImmediately(second_gesture_event); 223 } 224 225 TouchpadTapSuppressionController* 226 GestureEventQueue::GetTouchpadTapSuppressionController() { 227 return &touchpad_tap_suppression_controller_; 228 } 229 230 bool GestureEventQueue::ExpectingGestureAck() const { 231 return !coalesced_gesture_events_.empty(); 232 } 233 234 void GestureEventQueue::FlingHasBeenHalted() { 235 fling_in_progress_ = false; 236 } 237 238 bool GestureEventQueue::ShouldHandleEventNow() const { 239 return coalesced_gesture_events_.size() == 1; 240 } 241 242 void GestureEventQueue::ForwardGestureEvent( 243 const GestureEventWithLatencyInfo& gesture_event) { 244 if (ShouldForwardForCoalescing(gesture_event)) 245 client_->SendGestureEventImmediately(gesture_event); 246 } 247 248 void GestureEventQueue::SendScrollEndingEventsNow() { 249 scrolling_in_progress_ = false; 250 if (debouncing_deferral_queue_.empty()) 251 return; 252 GestureQueue debouncing_deferral_queue; 253 debouncing_deferral_queue.swap(debouncing_deferral_queue_); 254 for (GestureQueue::const_iterator it = debouncing_deferral_queue.begin(); 255 it != debouncing_deferral_queue.end(); it++) { 256 if (ShouldForwardForGFCFiltering(*it) && 257 ShouldForwardForTapSuppression(*it) && 258 ShouldForwardForCoalescing(*it)) { 259 client_->SendGestureEventImmediately(*it); 260 } 261 } 262 } 263 264 void GestureEventQueue::MergeOrInsertScrollAndPinchEvent( 265 const GestureEventWithLatencyInfo& gesture_event) { 266 const size_t unsent_events_count = 267 coalesced_gesture_events_.size() - EventsInFlightCount(); 268 if (!unsent_events_count) { 269 coalesced_gesture_events_.push_back(gesture_event); 270 return; 271 } 272 273 GestureEventWithLatencyInfo* last_event = &coalesced_gesture_events_.back(); 274 if (last_event->CanCoalesceWith(gesture_event)) { 275 last_event->CoalesceWith(gesture_event); 276 return; 277 } 278 279 if (!ShouldTryMerging(gesture_event, *last_event)) { 280 coalesced_gesture_events_.push_back(gesture_event); 281 return; 282 } 283 284 GestureEventWithLatencyInfo scroll_event; 285 GestureEventWithLatencyInfo pinch_event; 286 scroll_event.event.modifiers |= gesture_event.event.modifiers; 287 scroll_event.event.sourceDevice = gesture_event.event.sourceDevice; 288 scroll_event.event.timeStampSeconds = gesture_event.event.timeStampSeconds; 289 // Keep the oldest LatencyInfo. 290 DCHECK_LE(last_event->latency.trace_id, gesture_event.latency.trace_id); 291 scroll_event.latency = last_event->latency; 292 pinch_event = scroll_event; 293 scroll_event.event.type = WebInputEvent::GestureScrollUpdate; 294 pinch_event.event.type = WebInputEvent::GesturePinchUpdate; 295 pinch_event.event.x = gesture_event.event.type == 296 WebInputEvent::GesturePinchUpdate ? 297 gesture_event.event.x : last_event->event.x; 298 pinch_event.event.y = gesture_event.event.type == 299 WebInputEvent::GesturePinchUpdate ? 300 gesture_event.event.y : last_event->event.y; 301 302 gfx::Transform combined_scroll_pinch = GetTransformForEvent(*last_event); 303 // Only include the second-to-last event in the coalesced pair if it exists 304 // and can be combined with the new event. 305 if (unsent_events_count > 1) { 306 const GestureEventWithLatencyInfo& second_last_event = 307 coalesced_gesture_events_[coalesced_gesture_events_.size() - 2]; 308 if (ShouldTryMerging(gesture_event, second_last_event)) { 309 // Keep the oldest LatencyInfo. 310 DCHECK_LE(second_last_event.latency.trace_id, 311 scroll_event.latency.trace_id); 312 scroll_event.latency = second_last_event.latency; 313 pinch_event.latency = second_last_event.latency; 314 combined_scroll_pinch.PreconcatTransform( 315 GetTransformForEvent(second_last_event)); 316 coalesced_gesture_events_.pop_back(); 317 } 318 } 319 combined_scroll_pinch.ConcatTransform(GetTransformForEvent(gesture_event)); 320 coalesced_gesture_events_.pop_back(); 321 322 float combined_scale = 323 SkMScalarToFloat(combined_scroll_pinch.matrix().get(0, 0)); 324 float combined_scroll_pinch_x = 325 SkMScalarToFloat(combined_scroll_pinch.matrix().get(0, 3)); 326 float combined_scroll_pinch_y = 327 SkMScalarToFloat(combined_scroll_pinch.matrix().get(1, 3)); 328 scroll_event.event.data.scrollUpdate.deltaX = 329 (combined_scroll_pinch_x + pinch_event.event.x) / combined_scale - 330 pinch_event.event.x; 331 scroll_event.event.data.scrollUpdate.deltaY = 332 (combined_scroll_pinch_y + pinch_event.event.y) / combined_scale - 333 pinch_event.event.y; 334 coalesced_gesture_events_.push_back(scroll_event); 335 pinch_event.event.data.pinchUpdate.scale = combined_scale; 336 coalesced_gesture_events_.push_back(pinch_event); 337 } 338 339 bool GestureEventQueue::ShouldTryMerging( 340 const GestureEventWithLatencyInfo& new_event, 341 const GestureEventWithLatencyInfo& event_in_queue) const { 342 DLOG_IF(WARNING, 343 new_event.event.timeStampSeconds < 344 event_in_queue.event.timeStampSeconds) 345 << "Event time not monotonic?\n"; 346 return (event_in_queue.event.type == WebInputEvent::GestureScrollUpdate || 347 event_in_queue.event.type == WebInputEvent::GesturePinchUpdate) && 348 event_in_queue.event.modifiers == new_event.event.modifiers && 349 event_in_queue.event.sourceDevice == new_event.event.sourceDevice; 350 } 351 352 gfx::Transform GestureEventQueue::GetTransformForEvent( 353 const GestureEventWithLatencyInfo& gesture_event) const { 354 gfx::Transform gesture_transform; 355 if (gesture_event.event.type == WebInputEvent::GestureScrollUpdate) { 356 gesture_transform.Translate(gesture_event.event.data.scrollUpdate.deltaX, 357 gesture_event.event.data.scrollUpdate.deltaY); 358 } else if (gesture_event.event.type == WebInputEvent::GesturePinchUpdate) { 359 float scale = gesture_event.event.data.pinchUpdate.scale; 360 gesture_transform.Translate(-gesture_event.event.x, -gesture_event.event.y); 361 gesture_transform.Scale(scale,scale); 362 gesture_transform.Translate(gesture_event.event.x, gesture_event.event.y); 363 } 364 return gesture_transform; 365 } 366 367 size_t GestureEventQueue::EventsInFlightCount() const { 368 if (coalesced_gesture_events_.empty()) 369 return 0; 370 371 if (!ignore_next_ack_) 372 return 1; 373 374 DCHECK_GT(coalesced_gesture_events_.size(), 1U); 375 return 2; 376 } 377 378 } // namespace content 379