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/debug/trace_event.h" | 9 #include "base/debug/trace_event.h" |
9 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
10 #include "content/browser/renderer_host/input/timeout_monitor.h" | 11 #include "content/browser/renderer_host/input/timeout_monitor.h" |
11 #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" |
12 #include "ui/gfx/geometry/point_f.h" | 14 #include "ui/gfx/geometry/point_f.h" |
13 | 15 |
14 using blink::WebInputEvent; | 16 using blink::WebInputEvent; |
15 using blink::WebTouchEvent; | 17 using blink::WebTouchEvent; |
16 using blink::WebTouchPoint; | 18 using blink::WebTouchPoint; |
17 using ui::LatencyInfo; | 19 using ui::LatencyInfo; |
18 | 20 |
19 namespace content { | 21 namespace content { |
20 namespace { | 22 namespace { |
21 | 23 |
22 // Time interval at which touchmove events will be forwarded to the client while | 24 // Time interval at which touchmove events will be forwarded to the client while |
23 // scrolling is active and possible. | 25 // scrolling is active and possible. |
24 const double kAsyncTouchMoveIntervalSec = .2; | 26 const double kAsyncTouchMoveIntervalSec = .2; |
25 | 27 |
26 // A slop region just larger than that used by many web applications. When | 28 // A slop region just larger than that used by many web applications. When |
27 // touchmove's are being sent asynchronously, movement outside this region will | 29 // touchmove's are being sent asynchronously, movement outside this region will |
28 // trigger an immediate async touchmove to cancel potential tap-related logic. | 30 // trigger an immediate async touchmove to cancel potential tap-related logic. |
29 const double kApplicationSlopRegionLengthDipsSqared = 15. * 15.; | 31 const double kApplicationSlopRegionLengthDipsSqared = 15. * 15.; |
30 | 32 |
31 // Using a small epsilon when comparing slop distances allows pixel perfect | 33 // Using a small epsilon when comparing slop distances allows pixel perfect |
32 // slop determination when using fractional DIP coordinates (assuming the slop | 34 // slop determination when using fractional DIP coordinates (assuming the slop |
33 // region and DPI scale are reasonably proportioned). | 35 // region and DPI scale are reasonably proportioned). |
34 const float kSlopEpsilon = .05f; | 36 const float kSlopEpsilon = .05f; |
35 | 37 |
| 38 typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList; |
| 39 |
36 TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent( | 40 TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent( |
37 const TouchEventWithLatencyInfo& event_to_cancel) { | 41 const TouchEventWithLatencyInfo& event_to_cancel) { |
38 TouchEventWithLatencyInfo event = event_to_cancel; | 42 TouchEventWithLatencyInfo event = event_to_cancel; |
39 WebTouchEventTraits::ResetTypeAndTouchStates( | 43 WebTouchEventTraits::ResetTypeAndTouchStates( |
40 WebInputEvent::TouchCancel, | 44 WebInputEvent::TouchCancel, |
41 // TODO(rbyers): Shouldn't we use a fresh timestamp? | 45 // TODO(rbyers): Shouldn't we use a fresh timestamp? |
42 event.event.timeStampSeconds, | 46 event.event.timeStampSeconds, |
43 &event.event); | 47 &event.event); |
44 return event; | 48 return event; |
45 } | 49 } |
(...skipping 16 matching lines...) Expand all Loading... |
62 // 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 |
63 // sufficiently delayed. | 67 // sufficiently delayed. |
64 class TouchEventQueue::TouchTimeoutHandler { | 68 class TouchEventQueue::TouchTimeoutHandler { |
65 public: | 69 public: |
66 TouchTimeoutHandler(TouchEventQueue* touch_queue, | 70 TouchTimeoutHandler(TouchEventQueue* touch_queue, |
67 base::TimeDelta timeout_delay) | 71 base::TimeDelta timeout_delay) |
68 : touch_queue_(touch_queue), | 72 : touch_queue_(touch_queue), |
69 timeout_delay_(timeout_delay), | 73 timeout_delay_(timeout_delay), |
70 pending_ack_state_(PENDING_ACK_NONE), | 74 pending_ack_state_(PENDING_ACK_NONE), |
71 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut, | 75 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut, |
72 base::Unretained(this))) { | 76 base::Unretained(this))) {} |
73 DCHECK(timeout_delay != base::TimeDelta()); | |
74 } | |
75 | 77 |
76 ~TouchTimeoutHandler() {} | 78 ~TouchTimeoutHandler() {} |
77 | 79 |
78 void Start(const TouchEventWithLatencyInfo& event) { | 80 void Start(const TouchEventWithLatencyInfo& event) { |
79 DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE); | 81 DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE); |
80 DCHECK(ShouldTouchTriggerTimeout(event.event)); | 82 DCHECK(ShouldTouchTriggerTimeout(event.event)); |
81 timeout_event_ = event; | 83 timeout_event_ = event; |
82 timeout_monitor_.Restart(timeout_delay_); | 84 timeout_monitor_.Restart(timeout_delay_); |
83 } | 85 } |
84 | 86 |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
188 | 190 |
189 // Provides touchmove slop suppression for a single touch that remains within | 191 // Provides touchmove slop suppression for a single touch that remains within |
190 // a given slop region, unless the touchstart is preventDefault'ed. | 192 // a given slop region, unless the touchstart is preventDefault'ed. |
191 // TODO(jdduke): Use a flag bundled with each TouchEvent declaring whether it | 193 // TODO(jdduke): Use a flag bundled with each TouchEvent declaring whether it |
192 // has exceeded the slop region, removing duplicated slop determination logic. | 194 // has exceeded the slop region, removing duplicated slop determination logic. |
193 class TouchEventQueue::TouchMoveSlopSuppressor { | 195 class TouchEventQueue::TouchMoveSlopSuppressor { |
194 public: | 196 public: |
195 TouchMoveSlopSuppressor(double slop_suppression_length_dips) | 197 TouchMoveSlopSuppressor(double slop_suppression_length_dips) |
196 : slop_suppression_length_dips_squared_(slop_suppression_length_dips * | 198 : slop_suppression_length_dips_squared_(slop_suppression_length_dips * |
197 slop_suppression_length_dips), | 199 slop_suppression_length_dips), |
198 suppressing_touchmoves_(false) {} | 200 suppressing_touch_moves_(false) {} |
199 | 201 |
200 bool FilterEvent(const WebTouchEvent& event) { | 202 bool FilterEvent(const WebTouchEvent& event) { |
201 if (WebTouchEventTraits::IsTouchSequenceStart(event)) { | 203 if (WebTouchEventTraits::IsTouchSequenceStart(event)) { |
202 touch_sequence_start_position_ = | 204 touch_sequence_start_position_ = |
203 gfx::PointF(event.touches[0].position); | 205 gfx::PointF(event.touches[0].position); |
204 suppressing_touchmoves_ = slop_suppression_length_dips_squared_ != 0; | 206 suppressing_touch_moves_ = slop_suppression_length_dips_squared_ != 0; |
205 } | 207 } |
206 | 208 |
207 if (event.type != WebInputEvent::TouchMove) | 209 if (event.type != WebInputEvent::TouchMove) |
208 return false; | 210 return false; |
209 | 211 |
210 if (suppressing_touchmoves_) { | 212 if (suppressing_touch_moves_) { |
211 // Movement with a secondary pointer should terminate suppression. | 213 // Movement with a secondary pointer should terminate suppression. |
212 if (event.touchesLength > 1) { | 214 if (event.touchesLength > 1) { |
213 suppressing_touchmoves_ = false; | 215 suppressing_touch_moves_ = false; |
214 } else if (event.touchesLength == 1) { | 216 } else if (event.touchesLength == 1) { |
215 // Movement outside of the slop region should terminate suppression. | 217 // Movement outside of the slop region should terminate suppression. |
216 gfx::PointF position(event.touches[0].position); | 218 gfx::PointF position(event.touches[0].position); |
217 if ((position - touch_sequence_start_position_).LengthSquared() > | 219 if ((position - touch_sequence_start_position_).LengthSquared() > |
218 slop_suppression_length_dips_squared_) | 220 slop_suppression_length_dips_squared_) |
219 suppressing_touchmoves_ = false; | 221 suppressing_touch_moves_ = false; |
220 } | 222 } |
221 } | 223 } |
222 return suppressing_touchmoves_; | 224 return suppressing_touch_moves_; |
223 } | 225 } |
224 | 226 |
225 void ConfirmTouchEvent(InputEventAckState ack_result) { | 227 void ConfirmTouchEvent(InputEventAckState ack_result) { |
226 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) | 228 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) |
227 suppressing_touchmoves_ = false; | 229 suppressing_touch_moves_ = false; |
228 } | 230 } |
229 | 231 |
230 private: | 232 private: |
231 double slop_suppression_length_dips_squared_; | 233 double slop_suppression_length_dips_squared_; |
232 gfx::PointF touch_sequence_start_position_; | 234 gfx::PointF touch_sequence_start_position_; |
233 bool suppressing_touchmoves_; | 235 bool suppressing_touch_moves_; |
234 | 236 |
235 DISALLOW_COPY_AND_ASSIGN(TouchMoveSlopSuppressor); | 237 DISALLOW_COPY_AND_ASSIGN(TouchMoveSlopSuppressor); |
236 }; | 238 }; |
237 | 239 |
238 // 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 |
239 // 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 |
240 // 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 |
241 // 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 |
242 // the Client receives the event with their original timestamp. | 244 // the Client receives the event with their original timestamp. |
243 class CoalescedWebTouchEvent { | 245 class CoalescedWebTouchEvent { |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
307 } | 309 } |
308 | 310 |
309 private: | 311 private: |
310 bool WillDispatchAckToClient() const { return !events_to_ack_.empty(); } | 312 bool WillDispatchAckToClient() const { return !events_to_ack_.empty(); } |
311 | 313 |
312 // This is the event that is forwarded to the renderer. | 314 // This is the event that is forwarded to the renderer. |
313 TouchEventWithLatencyInfo coalesced_event_; | 315 TouchEventWithLatencyInfo coalesced_event_; |
314 | 316 |
315 // This is the list of the original events that were coalesced, each requiring | 317 // This is the list of the original events that were coalesced, each requiring |
316 // future ack dispatch to the client. | 318 // future ack dispatch to the client. |
317 typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList; | |
318 WebTouchEventWithLatencyList events_to_ack_; | 319 WebTouchEventWithLatencyList events_to_ack_; |
319 | 320 |
320 DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent); | 321 DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent); |
321 }; | 322 }; |
322 | 323 |
323 TouchEventQueue::Config::Config() | |
324 : touchmove_slop_suppression_length_dips(0), | |
325 touch_scrolling_mode(TOUCH_SCROLLING_MODE_DEFAULT), | |
326 touch_ack_timeout_delay(base::TimeDelta::FromMilliseconds(200)), | |
327 touch_ack_timeout_supported(false) { | |
328 } | |
329 | |
330 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client, | 324 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client, |
331 const Config& config) | 325 TouchScrollingMode mode, |
| 326 double touchmove_suppression_length_dips) |
332 : client_(client), | 327 : client_(client), |
333 dispatching_touch_ack_(NULL), | 328 dispatching_touch_ack_(NULL), |
334 dispatching_touch_(false), | 329 dispatching_touch_(false), |
335 touch_filtering_state_(TOUCH_FILTERING_STATE_DEFAULT), | 330 touch_filtering_state_(TOUCH_FILTERING_STATE_DEFAULT), |
336 ack_timeout_enabled_(config.touch_ack_timeout_supported), | 331 ack_timeout_enabled_(false), |
337 touchmove_slop_suppressor_(new TouchMoveSlopSuppressor( | 332 touchmove_slop_suppressor_(new TouchMoveSlopSuppressor( |
338 config.touchmove_slop_suppression_length_dips + kSlopEpsilon)), | 333 touchmove_suppression_length_dips + kSlopEpsilon)), |
339 send_touch_events_async_(false), | 334 send_touch_events_async_(false), |
340 needs_async_touchmove_for_outer_slop_region_(false), | 335 needs_async_touch_move_for_outer_slop_region_(false), |
341 last_sent_touch_timestamp_sec_(0), | 336 last_sent_touch_timestamp_sec_(0), |
342 touch_scrolling_mode_(config.touch_scrolling_mode) { | 337 touch_scrolling_mode_(mode) { |
343 DCHECK(client); | 338 DCHECK(client); |
344 if (ack_timeout_enabled_) { | |
345 timeout_handler_.reset( | |
346 new TouchTimeoutHandler(this, config.touch_ack_timeout_delay)); | |
347 } | |
348 } | 339 } |
349 | 340 |
350 TouchEventQueue::~TouchEventQueue() { | 341 TouchEventQueue::~TouchEventQueue() { |
351 if (!touch_queue_.empty()) | 342 if (!touch_queue_.empty()) |
352 STLDeleteElements(&touch_queue_); | 343 STLDeleteElements(&touch_queue_); |
353 } | 344 } |
354 | 345 |
355 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) { | 346 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) { |
356 TRACE_EVENT0("input", "TouchEventQueue::QueueEvent"); | 347 TRACE_EVENT0("input", "TouchEventQueue::QueueEvent"); |
357 | 348 |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
449 gfx::PointF(touch.event.touches[0].position); | 440 gfx::PointF(touch.event.touches[0].position); |
450 } | 441 } |
451 | 442 |
452 if (send_touch_events_async_ && | 443 if (send_touch_events_async_ && |
453 touch.event.type == WebInputEvent::TouchMove) { | 444 touch.event.type == WebInputEvent::TouchMove) { |
454 // Throttling touchmove's in a continuous touchmove stream while scrolling | 445 // Throttling touchmove's in a continuous touchmove stream while scrolling |
455 // reduces the risk of jank. However, it's still important that the web | 446 // 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, | 447 // application be sent touches at key points in the gesture stream, |
457 // e.g., when the application slop region is exceeded or touchmove | 448 // e.g., when the application slop region is exceeded or touchmove |
458 // coalescing fails because of different modifiers. | 449 // coalescing fails because of different modifiers. |
459 const bool send_touchmove_now = | 450 const bool send_touch_move_now = |
460 size() > 1 || | 451 size() > 1 || |
461 (touch.event.timeStampSeconds >= | 452 (touch.event.timeStampSeconds >= |
462 last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec) || | 453 last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec) || |
463 (needs_async_touchmove_for_outer_slop_region_ && | 454 (needs_async_touch_move_for_outer_slop_region_ && |
464 OutsideApplicationSlopRegion(touch.event, | 455 OutsideApplicationSlopRegion(touch.event, |
465 touch_sequence_start_position_)) || | 456 touch_sequence_start_position_)) || |
466 (pending_async_touchmove_ && | 457 (pending_async_touch_move_ && |
467 !pending_async_touchmove_->CanCoalesceWith(touch)); | 458 !pending_async_touch_move_->CanCoalesceWith(touch)); |
468 | 459 |
469 if (!send_touchmove_now) { | 460 if (!send_touch_move_now) { |
470 if (!pending_async_touchmove_) { | 461 if (!pending_async_touch_move_) { |
471 pending_async_touchmove_.reset(new TouchEventWithLatencyInfo(touch)); | 462 pending_async_touch_move_.reset(new TouchEventWithLatencyInfo(touch)); |
472 } else { | 463 } else { |
473 DCHECK(pending_async_touchmove_->CanCoalesceWith(touch)); | 464 DCHECK(pending_async_touch_move_->CanCoalesceWith(touch)); |
474 pending_async_touchmove_->CoalesceWith(touch); | 465 pending_async_touch_move_->CoalesceWith(touch); |
475 } | 466 } |
476 DCHECK_EQ(1U, size()); | 467 DCHECK_EQ(1U, size()); |
477 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); | 468 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
478 // It's possible (though unlikely) that ack'ing the current touch will | 469 // It's possible (though unlikely) that ack'ing the current touch will |
479 // trigger the queueing of another touch event (e.g., a touchcancel). As | 470 // trigger the queueing of another touch event (e.g., a touchcancel). As |
480 // forwarding of the queued event will be deferred while the ack is being | 471 // forwarding of the queued event will be deferred while the ack is being |
481 // dispatched (see |OnTouchEvent()|), try forwarding it now. | 472 // dispatched (see |OnTouchEvent()|), try forwarding it now. |
482 TryForwardNextEventToRenderer(); | 473 TryForwardNextEventToRenderer(); |
483 return; | 474 return; |
484 } | 475 } |
485 } | 476 } |
486 | 477 |
487 last_sent_touch_timestamp_sec_ = touch.event.timeStampSeconds; | 478 last_sent_touch_timestamp_sec_ = touch.event.timeStampSeconds; |
488 | 479 |
489 // Flush any pending async touch move. If it can be combined with the current | 480 // Flush any pending async touch move. If it can be combined with the current |
490 // (touchmove) event, great, otherwise send it immediately but separately. Its | 481 // (touchmove) event, great, otherwise send it immediately but separately. Its |
491 // ack will trigger forwarding of the original |touch| event. | 482 // ack will trigger forwarding of the original |touch| event. |
492 if (pending_async_touchmove_) { | 483 if (pending_async_touch_move_) { |
493 if (pending_async_touchmove_->CanCoalesceWith(touch)) { | 484 if (pending_async_touch_move_->CanCoalesceWith(touch)) { |
494 pending_async_touchmove_->CoalesceWith(touch); | 485 pending_async_touch_move_->CoalesceWith(touch); |
495 pending_async_touchmove_->event.cancelable = !send_touch_events_async_; | 486 pending_async_touch_move_->event.cancelable = !send_touch_events_async_; |
496 touch = *pending_async_touchmove_.Pass(); | 487 touch = *pending_async_touch_move_.Pass(); |
497 } else { | 488 } else { |
498 scoped_ptr<TouchEventWithLatencyInfo> async_move = | 489 scoped_ptr<TouchEventWithLatencyInfo> async_move = |
499 pending_async_touchmove_.Pass(); | 490 pending_async_touch_move_.Pass(); |
500 async_move->event.cancelable = false; | 491 async_move->event.cancelable = false; |
501 touch_queue_.push_front(new CoalescedWebTouchEvent(*async_move, true)); | 492 touch_queue_.push_front(new CoalescedWebTouchEvent(*async_move, true)); |
502 SendTouchEventImmediately(*async_move); | 493 SendTouchEventImmediately(*async_move); |
503 return; | 494 return; |
504 } | 495 } |
505 } | 496 } |
506 | 497 |
507 // Note: Marking touchstart events as not-cancelable prevents them from | 498 // Note: Marking touchstart events as not-cancelable prevents them from |
508 // blocking subsequent gestures, but it may not be the best long term solution | 499 // blocking subsequent gestures, but it may not be the best long term solution |
509 // for tracking touch point dispatch. | 500 // for tracking touch point dispatch. |
(...skipping 26 matching lines...) Expand all Loading... |
536 // always be preventDefault'ed (cancelable == true). | 527 // always be preventDefault'ed (cancelable == true). |
537 // TODO(jdduke): Revisit if touchstarts during scroll are made cancelable. | 528 // TODO(jdduke): Revisit if touchstarts during scroll are made cancelable. |
538 if (touch_ack_states_.empty() || | 529 if (touch_ack_states_.empty() || |
539 AllTouchAckStatesHaveState( | 530 AllTouchAckStatesHaveState( |
540 INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)) { | 531 INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)) { |
541 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; | 532 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; |
542 return; | 533 return; |
543 } | 534 } |
544 } | 535 } |
545 | 536 |
546 pending_async_touchmove_.reset(); | 537 pending_async_touch_move_.reset(); |
547 send_touch_events_async_ = true; | 538 send_touch_events_async_ = true; |
548 needs_async_touchmove_for_outer_slop_region_ = true; | 539 needs_async_touch_move_for_outer_slop_region_ = true; |
549 return; | 540 return; |
550 } | 541 } |
551 | 542 |
552 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_TOUCHCANCEL) | 543 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_TOUCHCANCEL) |
553 return; | 544 return; |
554 | 545 |
555 // We assume that scroll events are generated synchronously from | 546 // We assume that scroll events are generated synchronously from |
556 // dispatching a touch event ack. This allows us to generate a synthetic | 547 // dispatching a touch event ack. This allows us to generate a synthetic |
557 // cancel event that has the same touch ids as the touch event that | 548 // cancel event that has the same touch ids as the touch event that |
558 // is being acked. Otherwise, we don't perform the touch-cancel optimization. | 549 // is being acked. Otherwise, we don't perform the touch-cancel optimization. |
(...skipping 22 matching lines...) Expand all Loading... |
581 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) | 572 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) |
582 return; | 573 return; |
583 | 574 |
584 if (event.event.type != blink::WebInputEvent::GestureScrollUpdate) | 575 if (event.event.type != blink::WebInputEvent::GestureScrollUpdate) |
585 return; | 576 return; |
586 | 577 |
587 // Throttle sending touchmove events as long as the scroll events are handled. | 578 // Throttle sending touchmove events as long as the scroll events are handled. |
588 // Note that there's no guarantee that this ACK is for the most recent | 579 // Note that there's no guarantee that this ACK is for the most recent |
589 // gesture event (or even part of the current sequence). Worst case, the | 580 // gesture event (or even part of the current sequence). Worst case, the |
590 // delay in updating the absorption state will result in minor UI glitches. | 581 // delay in updating the absorption state will result in minor UI glitches. |
591 // A valid |pending_async_touchmove_| will be flushed when the next event is | 582 // A valid |pending_async_touch_move_| will be flushed when the next event is |
592 // forwarded. | 583 // forwarded. |
593 send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED); | 584 send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED); |
594 if (!send_touch_events_async_) | 585 if (!send_touch_events_async_) |
595 needs_async_touchmove_for_outer_slop_region_ = false; | 586 needs_async_touch_move_for_outer_slop_region_ = false; |
596 } | 587 } |
597 | 588 |
598 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { | 589 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { |
599 DCHECK(!dispatching_touch_ack_); | 590 DCHECK(!dispatching_touch_ack_); |
600 DCHECK(!dispatching_touch_); | 591 DCHECK(!dispatching_touch_); |
601 | 592 |
602 if (has_handlers) { | 593 if (has_handlers) { |
603 if (touch_filtering_state_ == DROP_ALL_TOUCHES) { | 594 if (touch_filtering_state_ == DROP_ALL_TOUCHES) { |
604 // If no touch handler was previously registered, ensure that we don't | 595 // If no touch handler was previously registered, ensure that we don't |
605 // send a partial touch sequence to the renderer. | 596 // send a partial touch sequence to the renderer. |
606 DCHECK(touch_queue_.empty()); | 597 DCHECK(touch_queue_.empty()); |
607 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; | 598 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; |
608 } | 599 } |
609 } else { | 600 } else { |
610 // TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch | 601 // TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch |
611 // state tracking (e.g., if the touch handler was removed mid-sequence). | 602 // state tracking (e.g., if the touch handler was removed mid-sequence). |
612 touch_filtering_state_ = DROP_ALL_TOUCHES; | 603 touch_filtering_state_ = DROP_ALL_TOUCHES; |
613 pending_async_touchmove_.reset(); | 604 pending_async_touch_move_.reset(); |
614 if (timeout_handler_) | 605 if (timeout_handler_) |
615 timeout_handler_->Reset(); | 606 timeout_handler_->Reset(); |
616 if (!touch_queue_.empty()) | 607 if (!touch_queue_.empty()) |
617 ProcessTouchAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, LatencyInfo()); | 608 ProcessTouchAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, LatencyInfo()); |
618 // As there is no touch handler, ack'ing the event should flush the queue. | 609 // As there is no touch handler, ack'ing the event should flush the queue. |
619 DCHECK(touch_queue_.empty()); | 610 DCHECK(touch_queue_.empty()); |
620 } | 611 } |
621 } | 612 } |
622 | 613 |
623 bool TouchEventQueue::IsPendingAckTouchStart() const { | 614 bool TouchEventQueue::IsPendingAckTouchStart() const { |
624 DCHECK(!dispatching_touch_ack_); | 615 DCHECK(!dispatching_touch_ack_); |
625 if (touch_queue_.empty()) | 616 if (touch_queue_.empty()) |
626 return false; | 617 return false; |
627 | 618 |
628 const blink::WebTouchEvent& event = | 619 const blink::WebTouchEvent& event = |
629 touch_queue_.front()->coalesced_event().event; | 620 touch_queue_.front()->coalesced_event().event; |
630 return (event.type == WebInputEvent::TouchStart); | 621 return (event.type == WebInputEvent::TouchStart); |
631 } | 622 } |
632 | 623 |
633 void TouchEventQueue::SetAckTimeoutEnabled(bool enabled) { | 624 void TouchEventQueue::SetAckTimeoutEnabled(bool enabled, |
634 // The timeout handler is valid only if explicitly supported in the config. | 625 base::TimeDelta ack_timeout_delay) { |
| 626 if (!enabled) { |
| 627 ack_timeout_enabled_ = false; |
| 628 if (touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT) |
| 629 touch_filtering_state_ = FORWARD_ALL_TOUCHES; |
| 630 // Only reset the |timeout_handler_| if the timer is running and has not yet |
| 631 // timed out. This ensures that an already timed out sequence is properly |
| 632 // flushed by the handler. |
| 633 if (timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning()) |
| 634 timeout_handler_->Reset(); |
| 635 return; |
| 636 } |
| 637 |
| 638 ack_timeout_enabled_ = true; |
635 if (!timeout_handler_) | 639 if (!timeout_handler_) |
636 return; | 640 timeout_handler_.reset(new TouchTimeoutHandler(this, ack_timeout_delay)); |
637 | 641 else |
638 if (ack_timeout_enabled_ == enabled) | 642 timeout_handler_->set_timeout_delay(ack_timeout_delay); |
639 return; | |
640 | |
641 ack_timeout_enabled_ = enabled; | |
642 | |
643 if (enabled) | |
644 return; | |
645 | |
646 if (touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT) | |
647 touch_filtering_state_ = FORWARD_ALL_TOUCHES; | |
648 // Only reset the |timeout_handler_| if the timer is running and has not yet | |
649 // timed out. This ensures that an already timed out sequence is properly | |
650 // flushed by the handler. | |
651 if (timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning()) | |
652 timeout_handler_->Reset(); | |
653 } | 643 } |
654 | 644 |
655 bool TouchEventQueue::HasPendingAsyncTouchMoveForTesting() const { | 645 bool TouchEventQueue::HasPendingAsyncTouchMoveForTesting() const { |
656 return pending_async_touchmove_; | 646 return pending_async_touch_move_; |
657 } | 647 } |
658 | 648 |
659 bool TouchEventQueue::IsTimeoutRunningForTesting() const { | 649 bool TouchEventQueue::IsTimeoutRunningForTesting() const { |
660 return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning(); | 650 return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning(); |
661 } | 651 } |
662 | 652 |
663 const TouchEventWithLatencyInfo& | 653 const TouchEventWithLatencyInfo& |
664 TouchEventQueue::GetLatestEventForTesting() const { | 654 TouchEventQueue::GetLatestEventForTesting() const { |
665 return touch_queue_.back()->coalesced_event(); | 655 return touch_queue_.back()->coalesced_event(); |
666 } | 656 } |
667 | 657 |
668 void TouchEventQueue::FlushQueue() { | 658 void TouchEventQueue::FlushQueue() { |
669 DCHECK(!dispatching_touch_ack_); | 659 DCHECK(!dispatching_touch_ack_); |
670 DCHECK(!dispatching_touch_); | 660 DCHECK(!dispatching_touch_); |
671 pending_async_touchmove_.reset(); | 661 pending_async_touch_move_.reset(); |
672 if (touch_filtering_state_ != DROP_ALL_TOUCHES) | 662 if (touch_filtering_state_ != DROP_ALL_TOUCHES) |
673 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; | 663 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; |
674 while (!touch_queue_.empty()) | 664 while (!touch_queue_.empty()) |
675 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); | 665 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
676 } | 666 } |
677 | 667 |
678 void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result) { | 668 void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result) { |
679 AckTouchEventToClient(ack_result, PopTouchEvent()); | 669 AckTouchEventToClient(ack_result, PopTouchEvent()); |
680 } | 670 } |
681 | 671 |
(...skipping 21 matching lines...) Expand all Loading... |
703 | 693 |
704 scoped_ptr<CoalescedWebTouchEvent> TouchEventQueue::PopTouchEvent() { | 694 scoped_ptr<CoalescedWebTouchEvent> TouchEventQueue::PopTouchEvent() { |
705 DCHECK(!touch_queue_.empty()); | 695 DCHECK(!touch_queue_.empty()); |
706 scoped_ptr<CoalescedWebTouchEvent> event(touch_queue_.front()); | 696 scoped_ptr<CoalescedWebTouchEvent> event(touch_queue_.front()); |
707 touch_queue_.pop_front(); | 697 touch_queue_.pop_front(); |
708 return event.Pass(); | 698 return event.Pass(); |
709 } | 699 } |
710 | 700 |
711 void TouchEventQueue::SendTouchEventImmediately( | 701 void TouchEventQueue::SendTouchEventImmediately( |
712 const TouchEventWithLatencyInfo& touch) { | 702 const TouchEventWithLatencyInfo& touch) { |
713 if (needs_async_touchmove_for_outer_slop_region_) { | 703 if (needs_async_touch_move_for_outer_slop_region_) { |
714 // Any event other than a touchmove (e.g., touchcancel or secondary | 704 // Any event other than a touchmove (e.g., touchcancel or secondary |
715 // touchstart) after a scroll has started will interrupt the need to send a | 705 // touchstart) after a scroll has started will interrupt the need to send a |
716 // an outer slop-region exceeding touchmove. | 706 // an outer slop-region exceeding touchmove. |
717 if (touch.event.type != WebInputEvent::TouchMove || | 707 if (touch.event.type != WebInputEvent::TouchMove || |
718 OutsideApplicationSlopRegion(touch.event, | 708 OutsideApplicationSlopRegion(touch.event, |
719 touch_sequence_start_position_)) | 709 touch_sequence_start_position_)) |
720 needs_async_touchmove_for_outer_slop_region_ = false; | 710 needs_async_touch_move_for_outer_slop_region_ = false; |
721 } | 711 } |
722 | 712 |
723 client_->SendTouchEventImmediately(touch); | 713 client_->SendTouchEventImmediately(touch); |
724 } | 714 } |
725 | 715 |
726 TouchEventQueue::PreFilterResult | 716 TouchEventQueue::PreFilterResult |
727 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { | 717 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { |
728 if (timeout_handler_ && timeout_handler_->FilterEvent(event)) | 718 if (timeout_handler_ && timeout_handler_->FilterEvent(event)) |
729 return ACK_WITH_NO_CONSUMER_EXISTS; | 719 return ACK_WITH_NO_CONSUMER_EXISTS; |
730 | 720 |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
796 iter != end; | 786 iter != end; |
797 ++iter) { | 787 ++iter) { |
798 if (iter->second != ack_state) | 788 if (iter->second != ack_state) |
799 return false; | 789 return false; |
800 } | 790 } |
801 | 791 |
802 return true; | 792 return true; |
803 } | 793 } |
804 | 794 |
805 } // namespace content | 795 } // namespace content |
OLD | NEW |