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