OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/renderer_host/input/touch_event_queue.h" | 5 #include "content/browser/renderer_host/input/touch_event_queue.h" |
6 | 6 |
7 #include "base/auto_reset.h" | 7 #include "base/auto_reset.h" |
8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
9 #include "base/debug/trace_event.h" | 9 #include "base/debug/trace_event.h" |
10 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
11 #include "content/browser/renderer_host/input/timeout_monitor.h" | 11 #include "content/browser/renderer_host/input/timeout_monitor.h" |
12 #include "content/common/input/web_touch_event_traits.h" | 12 #include "content/common/input/web_touch_event_traits.h" |
13 #include "content/public/common/content_switches.h" | 13 #include "content/public/common/content_switches.h" |
14 #include "ui/gfx/geometry/point_f.h" | 14 #include "ui/gfx/geometry/point_f.h" |
15 | 15 |
16 using blink::WebInputEvent; | 16 using blink::WebInputEvent; |
17 using blink::WebTouchEvent; | 17 using blink::WebTouchEvent; |
18 using blink::WebTouchPoint; | 18 using blink::WebTouchPoint; |
19 using ui::LatencyInfo; | 19 using ui::LatencyInfo; |
20 | 20 |
21 namespace content { | 21 namespace content { |
22 namespace { | 22 namespace { |
23 | 23 |
| 24 // Time interval at which touchmove events will be forwarded to the client while |
| 25 // scrolling is active and possible. |
| 26 const double kAsyncTouchMoveIntervalSec = .2; |
| 27 |
| 28 // A slop region just larger than that used by many web applications. When |
| 29 // touchmove's are being sent asynchronously, movement outside this region will |
| 30 // trigger an immediate async touchmove to cancel potential tap-related logic. |
| 31 const double kApplicationSlopRegionLengthDipsSqared = 15. * 15.; |
| 32 |
24 // Using a small epsilon when comparing slop distances allows pixel perfect | 33 // Using a small epsilon when comparing slop distances allows pixel perfect |
25 // slop determination when using fractional DIP coordinates (assuming the slop | 34 // slop determination when using fractional DIP coordinates (assuming the slop |
26 // region and DPI scale are reasonably proportioned). | 35 // region and DPI scale are reasonably proportioned). |
27 const float kSlopEpsilon = .05f; | 36 const float kSlopEpsilon = .05f; |
28 | 37 |
29 typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList; | 38 typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList; |
30 | 39 |
31 TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent( | 40 TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent( |
32 const TouchEventWithLatencyInfo& event_to_cancel) { | 41 const TouchEventWithLatencyInfo& event_to_cancel) { |
33 TouchEventWithLatencyInfo event = event_to_cancel; | 42 TouchEventWithLatencyInfo event = event_to_cancel; |
34 WebTouchEventTraits::ResetTypeAndTouchStates( | 43 WebTouchEventTraits::ResetTypeAndTouchStates( |
35 WebInputEvent::TouchCancel, | 44 WebInputEvent::TouchCancel, |
36 // TODO(rbyers): Shouldn't we use a fresh timestamp? | 45 // TODO(rbyers): Shouldn't we use a fresh timestamp? |
37 event.event.timeStampSeconds, | 46 event.event.timeStampSeconds, |
38 &event.event); | 47 &event.event); |
39 return event; | 48 return event; |
40 } | 49 } |
41 | 50 |
42 bool ShouldTouchTypeTriggerTimeout(WebInputEvent::Type type) { | 51 bool ShouldTouchTriggerTimeout(const WebTouchEvent& event) { |
43 return type == WebInputEvent::TouchStart || | 52 return (event.type == WebInputEvent::TouchStart || |
44 type == WebInputEvent::TouchMove; | 53 event.type == WebInputEvent::TouchMove) && |
| 54 !WebInputEventTraits::IgnoresAckDisposition(event); |
| 55 } |
| 56 |
| 57 bool OutsideApplicationSlopRegion(const WebTouchEvent& event, |
| 58 const gfx::PointF& anchor) { |
| 59 return (gfx::PointF(event.touches[0].position) - anchor).LengthSquared() > |
| 60 kApplicationSlopRegionLengthDipsSqared; |
45 } | 61 } |
46 | 62 |
47 } // namespace | 63 } // namespace |
48 | 64 |
49 | 65 |
50 // Cancels a touch sequence if a touchstart or touchmove ack response is | 66 // Cancels a touch sequence if a touchstart or touchmove ack response is |
51 // sufficiently delayed. | 67 // sufficiently delayed. |
52 class TouchEventQueue::TouchTimeoutHandler { | 68 class TouchEventQueue::TouchTimeoutHandler { |
53 public: | 69 public: |
54 TouchTimeoutHandler(TouchEventQueue* touch_queue, | 70 TouchTimeoutHandler(TouchEventQueue* touch_queue, |
55 base::TimeDelta timeout_delay) | 71 base::TimeDelta timeout_delay) |
56 : touch_queue_(touch_queue), | 72 : touch_queue_(touch_queue), |
57 timeout_delay_(timeout_delay), | 73 timeout_delay_(timeout_delay), |
58 pending_ack_state_(PENDING_ACK_NONE), | 74 pending_ack_state_(PENDING_ACK_NONE), |
59 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut, | 75 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut, |
60 base::Unretained(this))) {} | 76 base::Unretained(this))) {} |
61 | 77 |
62 ~TouchTimeoutHandler() {} | 78 ~TouchTimeoutHandler() {} |
63 | 79 |
64 void Start(const TouchEventWithLatencyInfo& event) { | 80 void Start(const TouchEventWithLatencyInfo& event) { |
65 DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE); | 81 DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE); |
66 DCHECK(ShouldTouchTypeTriggerTimeout(event.event.type)); | 82 DCHECK(ShouldTouchTriggerTimeout(event.event)); |
67 timeout_event_ = event; | 83 timeout_event_ = event; |
68 timeout_monitor_.Restart(timeout_delay_); | 84 timeout_monitor_.Restart(timeout_delay_); |
69 } | 85 } |
70 | 86 |
71 bool ConfirmTouchEvent(InputEventAckState ack_result) { | 87 bool ConfirmTouchEvent(InputEventAckState ack_result) { |
72 switch (pending_ack_state_) { | 88 switch (pending_ack_state_) { |
73 case PENDING_ACK_NONE: | 89 case PENDING_ACK_NONE: |
74 timeout_monitor_.Stop(); | 90 timeout_monitor_.Stop(); |
75 return false; | 91 return false; |
76 case PENDING_ACK_ORIGINAL_EVENT: | 92 case PENDING_ACK_ORIGINAL_EVENT: |
77 if (AckedTimeoutEventRequiresCancel(ack_result)) { | 93 if (AckedTimeoutEventRequiresCancel(ack_result)) { |
78 SetPendingAckState(PENDING_ACK_CANCEL_EVENT); | 94 SetPendingAckState(PENDING_ACK_CANCEL_EVENT); |
79 TouchEventWithLatencyInfo cancel_event = | 95 TouchEventWithLatencyInfo cancel_event = |
80 ObtainCancelEventForTouchEvent(timeout_event_); | 96 ObtainCancelEventForTouchEvent(timeout_event_); |
81 touch_queue_->client_->SendTouchEventImmediately(cancel_event); | 97 touch_queue_->SendTouchEventImmediately(cancel_event); |
82 } else { | 98 } else { |
83 SetPendingAckState(PENDING_ACK_NONE); | 99 SetPendingAckState(PENDING_ACK_NONE); |
84 touch_queue_->UpdateTouchAckStates(timeout_event_.event, ack_result); | 100 touch_queue_->UpdateTouchAckStates(timeout_event_.event, ack_result); |
85 } | 101 } |
86 return true; | 102 return true; |
87 case PENDING_ACK_CANCEL_EVENT: | 103 case PENDING_ACK_CANCEL_EVENT: |
88 SetPendingAckState(PENDING_ACK_NONE); | 104 SetPendingAckState(PENDING_ACK_NONE); |
89 return true; | 105 return true; |
90 } | 106 } |
91 return false; | 107 return false; |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
221 DISALLOW_COPY_AND_ASSIGN(TouchMoveSlopSuppressor); | 237 DISALLOW_COPY_AND_ASSIGN(TouchMoveSlopSuppressor); |
222 }; | 238 }; |
223 | 239 |
224 // This class represents a single coalesced touch event. However, it also keeps | 240 // This class represents a single coalesced touch event. However, it also keeps |
225 // track of all the original touch-events that were coalesced into a single | 241 // track of all the original touch-events that were coalesced into a single |
226 // event. The coalesced event is forwarded to the renderer, while the original | 242 // event. The coalesced event is forwarded to the renderer, while the original |
227 // touch-events are sent to the Client (on ACK for the coalesced event) so that | 243 // touch-events are sent to the Client (on ACK for the coalesced event) so that |
228 // the Client receives the event with their original timestamp. | 244 // the Client receives the event with their original timestamp. |
229 class CoalescedWebTouchEvent { | 245 class CoalescedWebTouchEvent { |
230 public: | 246 public: |
231 CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event, | 247 // Events for which |async| is true will not be ack'ed to the client after the |
232 bool ignore_ack) | 248 // corresponding ack is received following dispatch. |
233 : coalesced_event_(event), | 249 CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event, bool async) |
234 ignore_ack_(ignore_ack) { | 250 : coalesced_event_(event) { |
235 events_.push_back(event); | 251 if (async) |
236 TRACE_EVENT_ASYNC_BEGIN0( | 252 coalesced_event_.event.cancelable = false; |
237 "input", "TouchEventQueue::QueueEvent", this); | 253 else |
| 254 events_to_ack_.push_back(event); |
| 255 |
| 256 TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventQueue::QueueEvent", this); |
238 } | 257 } |
239 | 258 |
240 ~CoalescedWebTouchEvent() { | 259 ~CoalescedWebTouchEvent() { |
241 TRACE_EVENT_ASYNC_END0( | 260 TRACE_EVENT_ASYNC_END0("input", "TouchEventQueue::QueueEvent", this); |
242 "input", "TouchEventQueue::QueueEvent", this); | |
243 } | 261 } |
244 | 262 |
245 // Coalesces the event with the existing event if possible. Returns whether | 263 // Coalesces the event with the existing event if possible. Returns whether |
246 // the event was coalesced. | 264 // the event was coalesced. |
247 bool CoalesceEventIfPossible( | 265 bool CoalesceEventIfPossible( |
248 const TouchEventWithLatencyInfo& event_with_latency) { | 266 const TouchEventWithLatencyInfo& event_with_latency) { |
249 if (ignore_ack_) | 267 if (!WillDispatchAckToClient()) |
250 return false; | 268 return false; |
251 | 269 |
252 if (!coalesced_event_.CanCoalesceWith(event_with_latency)) | 270 if (!coalesced_event_.CanCoalesceWith(event_with_latency)) |
253 return false; | 271 return false; |
254 | 272 |
255 TRACE_EVENT_INSTANT0( | 273 TRACE_EVENT_INSTANT0( |
256 "input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD); | 274 "input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD); |
257 coalesced_event_.CoalesceWith(event_with_latency); | 275 coalesced_event_.CoalesceWith(event_with_latency); |
258 events_.push_back(event_with_latency); | 276 events_to_ack_.push_back(event_with_latency); |
259 return true; | 277 return true; |
260 } | 278 } |
261 | 279 |
| 280 void UpdateLatencyInfoForAck(const ui::LatencyInfo& renderer_latency_info) { |
| 281 if (!WillDispatchAckToClient()) |
| 282 return; |
| 283 |
| 284 for (WebTouchEventWithLatencyList::iterator iter = events_to_ack_.begin(), |
| 285 end = events_to_ack_.end(); |
| 286 iter != end; |
| 287 ++iter) { |
| 288 iter->latency.AddNewLatencyFrom(renderer_latency_info); |
| 289 } |
| 290 } |
| 291 |
| 292 void DispatchAckToClient(InputEventAckState ack_result, |
| 293 TouchEventQueueClient* client) { |
| 294 DCHECK(client); |
| 295 if (!WillDispatchAckToClient()) |
| 296 return; |
| 297 |
| 298 for (WebTouchEventWithLatencyList::const_iterator |
| 299 iter = events_to_ack_.begin(), |
| 300 end = events_to_ack_.end(); |
| 301 iter != end; |
| 302 ++iter) { |
| 303 client->OnTouchEventAck(*iter, ack_result); |
| 304 } |
| 305 } |
| 306 |
262 const TouchEventWithLatencyInfo& coalesced_event() const { | 307 const TouchEventWithLatencyInfo& coalesced_event() const { |
263 return coalesced_event_; | 308 return coalesced_event_; |
264 } | 309 } |
265 | 310 |
266 WebTouchEventWithLatencyList::iterator begin() { | 311 private: |
267 return events_.begin(); | 312 bool WillDispatchAckToClient() const { return !events_to_ack_.empty(); } |
268 } | |
269 | 313 |
270 WebTouchEventWithLatencyList::iterator end() { | |
271 return events_.end(); | |
272 } | |
273 | |
274 size_t size() const { return events_.size(); } | |
275 | |
276 bool ignore_ack() const { return ignore_ack_; } | |
277 | |
278 private: | |
279 // This is the event that is forwarded to the renderer. | 314 // This is the event that is forwarded to the renderer. |
280 TouchEventWithLatencyInfo coalesced_event_; | 315 TouchEventWithLatencyInfo coalesced_event_; |
281 | 316 |
282 // This is the list of the original events that were coalesced. | 317 // This is the list of the original events that were coalesced, each requiring |
283 WebTouchEventWithLatencyList events_; | 318 // future ack dispatch to the client. |
284 | 319 WebTouchEventWithLatencyList events_to_ack_; |
285 // If |ignore_ack_| is true, don't send this touch event to client | |
286 // when the event is acked. | |
287 bool ignore_ack_; | |
288 | 320 |
289 DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent); | 321 DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent); |
290 }; | 322 }; |
291 | 323 |
292 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client, | 324 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client, |
293 TouchScrollingMode mode, | 325 TouchScrollingMode mode, |
294 double touchmove_suppression_length_dips) | 326 double touchmove_suppression_length_dips) |
295 : client_(client), | 327 : client_(client), |
296 dispatching_touch_ack_(NULL), | 328 dispatching_touch_ack_(NULL), |
297 dispatching_touch_(false), | 329 dispatching_touch_(false), |
298 touch_filtering_state_(TOUCH_FILTERING_STATE_DEFAULT), | 330 touch_filtering_state_(TOUCH_FILTERING_STATE_DEFAULT), |
299 ack_timeout_enabled_(false), | 331 ack_timeout_enabled_(false), |
300 touchmove_slop_suppressor_(new TouchMoveSlopSuppressor( | 332 touchmove_slop_suppressor_(new TouchMoveSlopSuppressor( |
301 touchmove_suppression_length_dips + kSlopEpsilon)), | 333 touchmove_suppression_length_dips + kSlopEpsilon)), |
302 absorbing_touch_moves_(false), | 334 send_touch_events_async_(false), |
| 335 needs_async_touch_move_for_outer_slop_region_(false), |
| 336 last_sent_touch_timestamp_sec_(0), |
303 touch_scrolling_mode_(mode) { | 337 touch_scrolling_mode_(mode) { |
304 DCHECK(client); | 338 DCHECK(client); |
305 } | 339 } |
306 | 340 |
307 TouchEventQueue::~TouchEventQueue() { | 341 TouchEventQueue::~TouchEventQueue() { |
308 if (!touch_queue_.empty()) | 342 if (!touch_queue_.empty()) |
309 STLDeleteElements(&touch_queue_); | 343 STLDeleteElements(&touch_queue_); |
310 } | 344 } |
311 | 345 |
312 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) { | 346 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) { |
313 TRACE_EVENT0("input", "TouchEventQueue::QueueEvent"); | 347 TRACE_EVENT0("input", "TouchEventQueue::QueueEvent"); |
314 | 348 |
315 // If the queueing of |event| was triggered by an ack dispatch, defer | 349 // If the queueing of |event| was triggered by an ack dispatch, defer |
316 // processing the event until the dispatch has finished. | 350 // processing the event until the dispatch has finished. |
317 if (touch_queue_.empty() && !dispatching_touch_ack_) { | 351 if (touch_queue_.empty() && !dispatching_touch_ack_) { |
318 // Optimization of the case without touch handlers. Removing this path | 352 // Optimization of the case without touch handlers. Removing this path |
319 // yields identical results, but this avoids unnecessary allocations. | 353 // yields identical results, but this avoids unnecessary allocations. |
320 if (touch_filtering_state_ == DROP_ALL_TOUCHES || | 354 PreFilterResult filter_result = FilterBeforeForwarding(event.event); |
321 (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE && | 355 if (filter_result != FORWARD_TO_RENDERER) { |
322 !WebTouchEventTraits::IsTouchSequenceStart(event.event))) { | 356 client_->OnTouchEventAck(event, |
323 client_->OnTouchEventAck(event, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); | 357 filter_result == ACK_WITH_NO_CONSUMER_EXISTS |
| 358 ? INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS |
| 359 : INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
324 return; | 360 return; |
325 } | 361 } |
326 | 362 |
327 // There is no touch event in the queue. Forward it to the renderer | 363 // There is no touch event in the queue. Forward it to the renderer |
328 // immediately. | 364 // immediately. |
329 touch_queue_.push_back(new CoalescedWebTouchEvent(event, false)); | 365 touch_queue_.push_back(new CoalescedWebTouchEvent(event, false)); |
330 TryForwardNextEventToRenderer(); | 366 ForwardNextEventToRenderer(); |
331 return; | 367 return; |
332 } | 368 } |
333 | 369 |
334 // If the last queued touch-event was a touch-move, and the current event is | 370 // If the last queued touch-event was a touch-move, and the current event is |
335 // also a touch-move, then the events can be coalesced into a single event. | 371 // also a touch-move, then the events can be coalesced into a single event. |
336 if (touch_queue_.size() > 1) { | 372 if (touch_queue_.size() > 1) { |
337 CoalescedWebTouchEvent* last_event = touch_queue_.back(); | 373 CoalescedWebTouchEvent* last_event = touch_queue_.back(); |
338 if (last_event->CoalesceEventIfPossible(event)) | 374 if (last_event->CoalesceEventIfPossible(event)) |
339 return; | 375 return; |
340 } | 376 } |
(...skipping 22 matching lines...) Expand all Loading... |
363 touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT) { | 399 touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT) { |
364 touch_filtering_state_ = FORWARD_ALL_TOUCHES; | 400 touch_filtering_state_ = FORWARD_ALL_TOUCHES; |
365 } | 401 } |
366 | 402 |
367 if (ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS && | 403 if (ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS && |
368 touch_filtering_state_ != DROP_ALL_TOUCHES && | 404 touch_filtering_state_ != DROP_ALL_TOUCHES && |
369 WebTouchEventTraits::IsTouchSequenceStart(acked_event)) { | 405 WebTouchEventTraits::IsTouchSequenceStart(acked_event)) { |
370 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; | 406 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; |
371 } | 407 } |
372 | 408 |
373 UpdateTouchAckStates(acked_event, ack_result); | |
374 PopTouchEventToClient(ack_result, latency_info); | 409 PopTouchEventToClient(ack_result, latency_info); |
375 TryForwardNextEventToRenderer(); | 410 TryForwardNextEventToRenderer(); |
376 } | 411 } |
377 | 412 |
378 void TouchEventQueue::TryForwardNextEventToRenderer() { | 413 void TouchEventQueue::TryForwardNextEventToRenderer() { |
379 DCHECK(!dispatching_touch_ack_); | 414 DCHECK(!dispatching_touch_ack_); |
380 // If there are queued touch events, then try to forward them to the renderer | 415 // If there are queued touch events, then try to forward them to the renderer |
381 // immediately, or ACK the events back to the client if appropriate. | 416 // immediately, or ACK the events back to the client if appropriate. |
382 while (!touch_queue_.empty()) { | 417 while (!touch_queue_.empty()) { |
383 const TouchEventWithLatencyInfo& touch = | 418 PreFilterResult filter_result = |
384 touch_queue_.front()->coalesced_event(); | 419 FilterBeforeForwarding(touch_queue_.front()->coalesced_event().event); |
385 PreFilterResult result = FilterBeforeForwarding(touch.event); | 420 switch (filter_result) { |
386 switch (result) { | |
387 case ACK_WITH_NO_CONSUMER_EXISTS: | 421 case ACK_WITH_NO_CONSUMER_EXISTS: |
388 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, | 422 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
389 LatencyInfo()); | |
390 break; | 423 break; |
391 case ACK_WITH_NOT_CONSUMED: | 424 case ACK_WITH_NOT_CONSUMED: |
392 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, | 425 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
393 LatencyInfo()); | |
394 break; | 426 break; |
395 case FORWARD_TO_RENDERER: | 427 case FORWARD_TO_RENDERER: |
396 ForwardToRenderer(touch); | 428 ForwardNextEventToRenderer(); |
397 return; | 429 return; |
398 } | 430 } |
399 } | 431 } |
400 } | 432 } |
401 | 433 |
402 void TouchEventQueue::ForwardToRenderer( | 434 void TouchEventQueue::ForwardNextEventToRenderer() { |
403 const TouchEventWithLatencyInfo& touch) { | 435 TRACE_EVENT0("input", "TouchEventQueue::ForwardNextEventToRenderer"); |
404 TRACE_EVENT0("input", "TouchEventQueue::ForwardToRenderer"); | |
405 | 436 |
| 437 DCHECK(!empty()); |
406 DCHECK(!dispatching_touch_); | 438 DCHECK(!dispatching_touch_); |
407 DCHECK_NE(touch_filtering_state_, DROP_ALL_TOUCHES); | 439 DCHECK_NE(touch_filtering_state_, DROP_ALL_TOUCHES); |
| 440 TouchEventWithLatencyInfo touch = touch_queue_.front()->coalesced_event(); |
408 | 441 |
409 if (WebTouchEventTraits::IsTouchSequenceStart(touch.event)) { | 442 if (WebTouchEventTraits::IsTouchSequenceStart(touch.event)) { |
410 touch_filtering_state_ = | 443 touch_filtering_state_ = |
411 ack_timeout_enabled_ ? FORWARD_TOUCHES_UNTIL_TIMEOUT | 444 ack_timeout_enabled_ ? FORWARD_TOUCHES_UNTIL_TIMEOUT |
412 : FORWARD_ALL_TOUCHES; | 445 : FORWARD_ALL_TOUCHES; |
413 touch_ack_states_.clear(); | 446 touch_ack_states_.clear(); |
414 absorbing_touch_moves_ = false; | 447 send_touch_events_async_ = false; |
| 448 touch_sequence_start_position_ = |
| 449 gfx::PointF(touch.event.touches[0].position); |
415 } | 450 } |
416 | 451 |
| 452 if (send_touch_events_async_ && |
| 453 touch.event.type == WebInputEvent::TouchMove) { |
| 454 // Throttling touchmove's in a continuous touchmove stream while scrolling |
| 455 // reduces the risk of jank. However, it's still important that the web |
| 456 // application be sent touches at key points in the gesture stream, |
| 457 // e.g., when the application slop region is exceeded or touchmove |
| 458 // coalescing fails because of different modifiers. |
| 459 const bool send_touch_move_now = |
| 460 size() > 1 || |
| 461 (touch.event.timeStampSeconds >= |
| 462 last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec) || |
| 463 (needs_async_touch_move_for_outer_slop_region_ && |
| 464 OutsideApplicationSlopRegion(touch.event, |
| 465 touch_sequence_start_position_)) || |
| 466 (pending_async_touch_move_ && |
| 467 !pending_async_touch_move_->CanCoalesceWith(touch)); |
| 468 |
| 469 if (!send_touch_move_now) { |
| 470 if (!pending_async_touch_move_) { |
| 471 pending_async_touch_move_.reset(new TouchEventWithLatencyInfo(touch)); |
| 472 } else { |
| 473 DCHECK(pending_async_touch_move_->CanCoalesceWith(touch)); |
| 474 pending_async_touch_move_->CoalesceWith(touch); |
| 475 } |
| 476 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| 477 return; |
| 478 } |
| 479 } |
| 480 |
| 481 last_sent_touch_timestamp_sec_ = touch.event.timeStampSeconds; |
| 482 |
| 483 // Flush any pending async touch move. If it can be combined with the current |
| 484 // (touchmove) event, great, otherwise send it immediately but separately. Its |
| 485 // ack will trigger forwarding of the original |touch| event. |
| 486 if (pending_async_touch_move_) { |
| 487 if (pending_async_touch_move_->CanCoalesceWith(touch)) { |
| 488 pending_async_touch_move_->CoalesceWith(touch); |
| 489 pending_async_touch_move_->event.cancelable = !send_touch_events_async_; |
| 490 touch = *pending_async_touch_move_.Pass(); |
| 491 } else { |
| 492 scoped_ptr<TouchEventWithLatencyInfo> async_move = |
| 493 pending_async_touch_move_.Pass(); |
| 494 async_move->event.cancelable = false; |
| 495 touch_queue_.push_front(new CoalescedWebTouchEvent(*async_move, true)); |
| 496 SendTouchEventImmediately(*async_move); |
| 497 return; |
| 498 } |
| 499 } |
| 500 |
| 501 // Note: Marking touchstart events as not-cancelable prevents them from |
| 502 // blocking subsequent gestures, but it may not be the best long term solution |
| 503 // for tracking touch point dispatch. |
| 504 if (send_touch_events_async_) |
| 505 touch.event.cancelable = false; |
| 506 |
417 // A synchronous ack will reset |dispatching_touch_|, in which case | 507 // A synchronous ack will reset |dispatching_touch_|, in which case |
418 // the touch timeout should not be started. | 508 // the touch timeout should not be started. |
419 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true); | 509 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true); |
420 client_->SendTouchEventImmediately(touch); | 510 SendTouchEventImmediately(touch); |
421 if (dispatching_touch_ && | 511 if (dispatching_touch_ && |
422 touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT && | 512 touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT && |
423 ShouldTouchTypeTriggerTimeout(touch.event.type)) { | 513 ShouldTouchTriggerTimeout(touch.event)) { |
424 DCHECK(timeout_handler_); | 514 DCHECK(timeout_handler_); |
425 timeout_handler_->Start(touch); | 515 timeout_handler_->Start(touch); |
426 } | 516 } |
427 } | 517 } |
428 | 518 |
429 void TouchEventQueue::OnGestureScrollEvent( | 519 void TouchEventQueue::OnGestureScrollEvent( |
430 const GestureEventWithLatencyInfo& gesture_event) { | 520 const GestureEventWithLatencyInfo& gesture_event) { |
431 if (gesture_event.event.type != blink::WebInputEvent::GestureScrollBegin) | 521 if (gesture_event.event.type != blink::WebInputEvent::GestureScrollBegin) |
432 return; | 522 return; |
433 | 523 |
434 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE) | 524 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) { |
435 absorbing_touch_moves_ = true; | 525 pending_async_touch_move_.reset(); |
| 526 send_touch_events_async_ = true; |
| 527 needs_async_touch_move_for_outer_slop_region_ = true; |
| 528 return; |
| 529 } |
436 | 530 |
437 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_TOUCHCANCEL) | 531 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_TOUCHCANCEL) |
438 return; | 532 return; |
439 | 533 |
440 // We assume that scroll events are generated synchronously from | 534 // We assume that scroll events are generated synchronously from |
441 // dispatching a touch event ack. This allows us to generate a synthetic | 535 // dispatching a touch event ack. This allows us to generate a synthetic |
442 // cancel event that has the same touch ids as the touch event that | 536 // cancel event that has the same touch ids as the touch event that |
443 // is being acked. Otherwise, we don't perform the touch-cancel optimization. | 537 // is being acked. Otherwise, we don't perform the touch-cancel optimization. |
444 if (!dispatching_touch_ack_) | 538 if (!dispatching_touch_ack_) |
445 return; | 539 return; |
(...skipping 10 matching lines...) Expand all Loading... |
456 // in the queue is waiting for ack from renderer. So we can just insert | 550 // in the queue is waiting for ack from renderer. So we can just insert |
457 // the touch cancel at the beginning of the queue. | 551 // the touch cancel at the beginning of the queue. |
458 touch_queue_.push_front(new CoalescedWebTouchEvent( | 552 touch_queue_.push_front(new CoalescedWebTouchEvent( |
459 ObtainCancelEventForTouchEvent( | 553 ObtainCancelEventForTouchEvent( |
460 dispatching_touch_ack_->coalesced_event()), true)); | 554 dispatching_touch_ack_->coalesced_event()), true)); |
461 } | 555 } |
462 | 556 |
463 void TouchEventQueue::OnGestureEventAck( | 557 void TouchEventQueue::OnGestureEventAck( |
464 const GestureEventWithLatencyInfo& event, | 558 const GestureEventWithLatencyInfo& event, |
465 InputEventAckState ack_result) { | 559 InputEventAckState ack_result) { |
466 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE) | 560 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) |
467 return; | 561 return; |
468 | 562 |
469 if (event.event.type != blink::WebInputEvent::GestureScrollUpdate) | 563 if (event.event.type != blink::WebInputEvent::GestureScrollUpdate) |
470 return; | 564 return; |
471 | 565 |
472 // Suspend sending touchmove events as long as the scroll events are handled. | 566 // Throttle sending touchmove events as long as the scroll events are handled. |
473 // Note that there's no guarantee that this ACK is for the most recent | 567 // Note that there's no guarantee that this ACK is for the most recent |
474 // gesture event (or even part of the current sequence). Worst case, the | 568 // gesture event (or even part of the current sequence). Worst case, the |
475 // delay in updating the absorption state should only result in minor UI | 569 // delay in updating the absorption state will result in minor UI glitches. |
476 // glitches. | 570 // A valid |pending_async_touch_move_| will be flushed when the next event is |
477 absorbing_touch_moves_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED); | 571 // forwarded. |
| 572 send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED); |
| 573 if (!send_touch_events_async_) |
| 574 needs_async_touch_move_for_outer_slop_region_ = false; |
478 } | 575 } |
479 | 576 |
480 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { | 577 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { |
481 DCHECK(!dispatching_touch_ack_); | 578 DCHECK(!dispatching_touch_ack_); |
482 DCHECK(!dispatching_touch_); | 579 DCHECK(!dispatching_touch_); |
483 | 580 |
484 if (has_handlers) { | 581 if (has_handlers) { |
485 if (touch_filtering_state_ == DROP_ALL_TOUCHES) { | 582 if (touch_filtering_state_ == DROP_ALL_TOUCHES) { |
486 // If no touch handler was previously registered, ensure that we don't | 583 // If no touch handler was previously registered, ensure that we don't |
487 // send a partial touch sequence to the renderer. | 584 // send a partial touch sequence to the renderer. |
488 DCHECK(touch_queue_.empty()); | 585 DCHECK(touch_queue_.empty()); |
489 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; | 586 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; |
490 } | 587 } |
491 } else { | 588 } else { |
492 // TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch | 589 // TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch |
493 // state tracking (e.g., if the touch handler was removed mid-sequence). | 590 // state tracking (e.g., if the touch handler was removed mid-sequence). |
494 touch_filtering_state_ = DROP_ALL_TOUCHES; | 591 touch_filtering_state_ = DROP_ALL_TOUCHES; |
| 592 pending_async_touch_move_.reset(); |
495 if (timeout_handler_) | 593 if (timeout_handler_) |
496 timeout_handler_->Reset(); | 594 timeout_handler_->Reset(); |
497 if (!touch_queue_.empty()) | 595 if (!touch_queue_.empty()) |
498 ProcessTouchAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, LatencyInfo()); | 596 ProcessTouchAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, LatencyInfo()); |
499 // As there is no touch handler, ack'ing the event should flush the queue. | 597 // As there is no touch handler, ack'ing the event should flush the queue. |
500 DCHECK(touch_queue_.empty()); | 598 DCHECK(touch_queue_.empty()); |
501 } | 599 } |
502 } | 600 } |
503 | 601 |
504 bool TouchEventQueue::IsPendingAckTouchStart() const { | 602 bool TouchEventQueue::IsPendingAckTouchStart() const { |
(...skipping 20 matching lines...) Expand all Loading... |
525 return; | 623 return; |
526 } | 624 } |
527 | 625 |
528 ack_timeout_enabled_ = true; | 626 ack_timeout_enabled_ = true; |
529 if (!timeout_handler_) | 627 if (!timeout_handler_) |
530 timeout_handler_.reset(new TouchTimeoutHandler(this, ack_timeout_delay)); | 628 timeout_handler_.reset(new TouchTimeoutHandler(this, ack_timeout_delay)); |
531 else | 629 else |
532 timeout_handler_->set_timeout_delay(ack_timeout_delay); | 630 timeout_handler_->set_timeout_delay(ack_timeout_delay); |
533 } | 631 } |
534 | 632 |
| 633 bool TouchEventQueue::HasPendingAsyncTouchMoveForTesting() const { |
| 634 return pending_async_touch_move_; |
| 635 } |
| 636 |
535 bool TouchEventQueue::IsTimeoutRunningForTesting() const { | 637 bool TouchEventQueue::IsTimeoutRunningForTesting() const { |
536 return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning(); | 638 return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning(); |
537 } | 639 } |
538 | 640 |
539 const TouchEventWithLatencyInfo& | 641 const TouchEventWithLatencyInfo& |
540 TouchEventQueue::GetLatestEventForTesting() const { | 642 TouchEventQueue::GetLatestEventForTesting() const { |
541 return touch_queue_.back()->coalesced_event(); | 643 return touch_queue_.back()->coalesced_event(); |
542 } | 644 } |
543 | 645 |
544 void TouchEventQueue::FlushQueue() { | 646 void TouchEventQueue::FlushQueue() { |
545 DCHECK(!dispatching_touch_ack_); | 647 DCHECK(!dispatching_touch_ack_); |
546 DCHECK(!dispatching_touch_); | 648 DCHECK(!dispatching_touch_); |
| 649 pending_async_touch_move_.reset(); |
547 if (touch_filtering_state_ != DROP_ALL_TOUCHES) | 650 if (touch_filtering_state_ != DROP_ALL_TOUCHES) |
548 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; | 651 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; |
549 while (!touch_queue_.empty()) { | 652 while (!touch_queue_.empty()) |
550 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, | 653 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
551 LatencyInfo()); | 654 } |
552 } | 655 |
| 656 void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result) { |
| 657 AckTouchEventToClient(ack_result, PopTouchEvent()); |
553 } | 658 } |
554 | 659 |
555 void TouchEventQueue::PopTouchEventToClient( | 660 void TouchEventQueue::PopTouchEventToClient( |
556 InputEventAckState ack_result, | 661 InputEventAckState ack_result, |
557 const LatencyInfo& renderer_latency_info) { | 662 const LatencyInfo& renderer_latency_info) { |
| 663 scoped_ptr<CoalescedWebTouchEvent> acked_event = PopTouchEvent(); |
| 664 acked_event->UpdateLatencyInfoForAck(renderer_latency_info); |
| 665 AckTouchEventToClient(ack_result, acked_event.Pass()); |
| 666 } |
| 667 |
| 668 void TouchEventQueue::AckTouchEventToClient( |
| 669 InputEventAckState ack_result, |
| 670 scoped_ptr<CoalescedWebTouchEvent> acked_event) { |
| 671 DCHECK(acked_event); |
558 DCHECK(!dispatching_touch_ack_); | 672 DCHECK(!dispatching_touch_ack_); |
559 if (touch_queue_.empty()) | 673 UpdateTouchAckStates(acked_event->coalesced_event().event, ack_result); |
560 return; | |
561 scoped_ptr<CoalescedWebTouchEvent> acked_event(touch_queue_.front()); | |
562 touch_queue_.pop_front(); | |
563 | |
564 if (acked_event->ignore_ack()) | |
565 return; | |
566 | 674 |
567 // Note that acking the touch-event may result in multiple gestures being sent | 675 // Note that acking the touch-event may result in multiple gestures being sent |
568 // to the renderer, or touch-events being queued. | 676 // to the renderer, or touch-events being queued. |
569 base::AutoReset<CoalescedWebTouchEvent*> | 677 base::AutoReset<const CoalescedWebTouchEvent*> dispatching_touch_ack( |
570 dispatching_touch_ack(&dispatching_touch_ack_, acked_event.get()); | 678 &dispatching_touch_ack_, acked_event.get()); |
| 679 acked_event->DispatchAckToClient(ack_result, client_); |
| 680 } |
571 | 681 |
572 for (WebTouchEventWithLatencyList::iterator iter = acked_event->begin(), | 682 scoped_ptr<CoalescedWebTouchEvent> TouchEventQueue::PopTouchEvent() { |
573 end = acked_event->end(); | 683 DCHECK(!touch_queue_.empty()); |
574 iter != end; ++iter) { | 684 scoped_ptr<CoalescedWebTouchEvent> event(touch_queue_.front()); |
575 iter->latency.AddNewLatencyFrom(renderer_latency_info); | 685 touch_queue_.pop_front(); |
576 client_->OnTouchEventAck((*iter), ack_result); | 686 return event.Pass(); |
| 687 } |
| 688 |
| 689 void TouchEventQueue::SendTouchEventImmediately( |
| 690 const TouchEventWithLatencyInfo& touch) { |
| 691 if (needs_async_touch_move_for_outer_slop_region_) { |
| 692 // Any event other than a touchmove (e.g., touchcancel or secondary |
| 693 // touchstart) after a scroll has started will interrupt the need to send a |
| 694 // an outer slop-region exceeding touchmove. |
| 695 if (touch.event.type != WebInputEvent::TouchMove || |
| 696 OutsideApplicationSlopRegion(touch.event, |
| 697 touch_sequence_start_position_)) |
| 698 needs_async_touch_move_for_outer_slop_region_ = false; |
577 } | 699 } |
| 700 |
| 701 client_->SendTouchEventImmediately(touch); |
578 } | 702 } |
579 | 703 |
580 TouchEventQueue::PreFilterResult | 704 TouchEventQueue::PreFilterResult |
581 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { | 705 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { |
582 if (timeout_handler_ && timeout_handler_->FilterEvent(event)) | 706 if (timeout_handler_ && timeout_handler_->FilterEvent(event)) |
583 return ACK_WITH_NO_CONSUMER_EXISTS; | 707 return ACK_WITH_NO_CONSUMER_EXISTS; |
584 | 708 |
585 if (touchmove_slop_suppressor_->FilterEvent(event)) | 709 if (touchmove_slop_suppressor_->FilterEvent(event)) |
586 return ACK_WITH_NOT_CONSUMED; | 710 return ACK_WITH_NOT_CONSUMED; |
587 | 711 |
588 if (touch_filtering_state_ == DROP_ALL_TOUCHES) | 712 if (touch_filtering_state_ == DROP_ALL_TOUCHES) |
589 return ACK_WITH_NO_CONSUMER_EXISTS; | 713 return ACK_WITH_NO_CONSUMER_EXISTS; |
590 | 714 |
591 if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE && | 715 if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE && |
592 event.type != WebInputEvent::TouchCancel) { | 716 event.type != WebInputEvent::TouchCancel) { |
593 if (WebTouchEventTraits::IsTouchSequenceStart(event)) | 717 if (WebTouchEventTraits::IsTouchSequenceStart(event)) |
594 return FORWARD_TO_RENDERER; | 718 return FORWARD_TO_RENDERER; |
595 return ACK_WITH_NOT_CONSUMED; | 719 return ACK_WITH_NO_CONSUMER_EXISTS; |
596 } | 720 } |
597 | 721 |
598 if (absorbing_touch_moves_ && event.type == WebInputEvent::TouchMove) | |
599 return ACK_WITH_NOT_CONSUMED; | |
600 | |
601 // Touch press events should always be forwarded to the renderer. | 722 // Touch press events should always be forwarded to the renderer. |
602 if (event.type == WebInputEvent::TouchStart) | 723 if (event.type == WebInputEvent::TouchStart) |
603 return FORWARD_TO_RENDERER; | 724 return FORWARD_TO_RENDERER; |
604 | 725 |
605 for (unsigned int i = 0; i < event.touchesLength; ++i) { | 726 for (unsigned int i = 0; i < event.touchesLength; ++i) { |
606 const WebTouchPoint& point = event.touches[i]; | 727 const WebTouchPoint& point = event.touches[i]; |
607 // If a point has been stationary, then don't take it into account. | 728 // If a point has been stationary, then don't take it into account. |
608 if (point.state == WebTouchPoint::StateStationary) | 729 if (point.state == WebTouchPoint::StateStationary) |
609 continue; | 730 continue; |
610 | 731 |
(...skipping 26 matching lines...) Expand all Loading... |
637 } else if (event.type == WebInputEvent::TouchStart) { | 758 } else if (event.type == WebInputEvent::TouchStart) { |
638 for (unsigned i = 0; i < event.touchesLength; ++i) { | 759 for (unsigned i = 0; i < event.touchesLength; ++i) { |
639 const WebTouchPoint& point = event.touches[i]; | 760 const WebTouchPoint& point = event.touches[i]; |
640 if (point.state == WebTouchPoint::StatePressed) | 761 if (point.state == WebTouchPoint::StatePressed) |
641 touch_ack_states_[point.id] = ack_result; | 762 touch_ack_states_[point.id] = ack_result; |
642 } | 763 } |
643 } | 764 } |
644 } | 765 } |
645 | 766 |
646 } // namespace content | 767 } // namespace content |
OLD | NEW |