Chromium Code Reviews| 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/debug/trace_event.h" | 8 #include "base/debug/trace_event.h" |
| 9 #include "base/stl_util.h" | 9 #include "base/stl_util.h" |
| 10 #include "content/browser/renderer_host/input/timeout_monitor.h" | 10 #include "content/browser/renderer_host/input/timeout_monitor.h" |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 62 // 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 |
| 63 // sufficiently delayed. | 63 // sufficiently delayed. |
| 64 class TouchEventQueue::TouchTimeoutHandler { | 64 class TouchEventQueue::TouchTimeoutHandler { |
| 65 public: | 65 public: |
| 66 TouchTimeoutHandler(TouchEventQueue* touch_queue, | 66 TouchTimeoutHandler(TouchEventQueue* touch_queue, |
| 67 base::TimeDelta timeout_delay) | 67 base::TimeDelta timeout_delay) |
| 68 : touch_queue_(touch_queue), | 68 : touch_queue_(touch_queue), |
| 69 timeout_delay_(timeout_delay), | 69 timeout_delay_(timeout_delay), |
| 70 pending_ack_state_(PENDING_ACK_NONE), | 70 pending_ack_state_(PENDING_ACK_NONE), |
| 71 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut, | 71 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut, |
| 72 base::Unretained(this))) { | 72 base::Unretained(this))), |
| 73 enabled_(true), | |
| 74 enabled_for_current_sequence_(false) { | |
| 73 DCHECK(timeout_delay != base::TimeDelta()); | 75 DCHECK(timeout_delay != base::TimeDelta()); |
| 74 } | 76 } |
| 75 | 77 |
| 76 ~TouchTimeoutHandler() {} | 78 ~TouchTimeoutHandler() {} |
| 77 | 79 |
| 78 void Start(const TouchEventWithLatencyInfo& event) { | 80 void StartIfNecessary(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 if (!enabled_) |
| 83 return; | |
| 84 | |
| 85 if (!ShouldTouchTriggerTimeout(event.event)) | |
| 86 return; | |
| 87 | |
| 88 if (WebTouchEventTraits::IsTouchSequenceStart(event.event)) | |
| 89 enabled_for_current_sequence_ = true; | |
| 90 | |
| 91 if (!enabled_for_current_sequence_) | |
| 92 return; | |
| 93 | |
| 81 timeout_event_ = event; | 94 timeout_event_ = event; |
| 82 timeout_monitor_.Restart(timeout_delay_); | 95 timeout_monitor_.Restart(timeout_delay_); |
| 83 } | 96 } |
| 84 | 97 |
| 85 bool ConfirmTouchEvent(InputEventAckState ack_result) { | 98 bool ConfirmTouchEvent(InputEventAckState ack_result) { |
| 86 switch (pending_ack_state_) { | 99 switch (pending_ack_state_) { |
| 87 case PENDING_ACK_NONE: | 100 case PENDING_ACK_NONE: |
| 101 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) | |
| 102 enabled_for_current_sequence_ = false; | |
| 88 timeout_monitor_.Stop(); | 103 timeout_monitor_.Stop(); |
| 89 return false; | 104 return false; |
| 90 case PENDING_ACK_ORIGINAL_EVENT: | 105 case PENDING_ACK_ORIGINAL_EVENT: |
| 91 if (AckedTimeoutEventRequiresCancel(ack_result)) { | 106 if (AckedTimeoutEventRequiresCancel(ack_result)) { |
| 92 SetPendingAckState(PENDING_ACK_CANCEL_EVENT); | 107 SetPendingAckState(PENDING_ACK_CANCEL_EVENT); |
| 93 TouchEventWithLatencyInfo cancel_event = | 108 TouchEventWithLatencyInfo cancel_event = |
| 94 ObtainCancelEventForTouchEvent(timeout_event_); | 109 ObtainCancelEventForTouchEvent(timeout_event_); |
| 95 touch_queue_->SendTouchEventImmediately(cancel_event); | 110 touch_queue_->SendTouchEventImmediately(cancel_event); |
| 96 } else { | 111 } else { |
| 97 SetPendingAckState(PENDING_ACK_NONE); | 112 SetPendingAckState(PENDING_ACK_NONE); |
| 98 touch_queue_->UpdateTouchAckStates(timeout_event_.event, ack_result); | 113 touch_queue_->UpdateTouchConsumerStates(timeout_event_.event, |
| 114 ack_result); | |
| 99 } | 115 } |
| 100 return true; | 116 return true; |
| 101 case PENDING_ACK_CANCEL_EVENT: | 117 case PENDING_ACK_CANCEL_EVENT: |
| 102 SetPendingAckState(PENDING_ACK_NONE); | 118 SetPendingAckState(PENDING_ACK_NONE); |
| 103 return true; | 119 return true; |
| 104 } | 120 } |
| 105 return false; | 121 return false; |
| 106 } | 122 } |
| 107 | 123 |
| 108 bool FilterEvent(const WebTouchEvent& event) { | 124 bool FilterEvent(const WebTouchEvent& event) { |
| 109 return HasTimeoutEvent(); | 125 return HasTimeoutEvent(); |
| 110 } | 126 } |
| 111 | 127 |
| 112 bool IsTimeoutTimerRunning() const { | 128 void SetEnabled(bool enabled) { |
| 113 return timeout_monitor_.IsRunning(); | 129 if (enabled_ == enabled) |
| 130 return; | |
| 131 | |
| 132 enabled_ = enabled; | |
| 133 | |
| 134 if (enabled_) | |
| 135 return; | |
| 136 | |
| 137 enabled_for_current_sequence_ = false; | |
| 138 // Only reset the |timeout_handler_| if the timer is running and has not | |
| 139 // yet timed out. This ensures that an already timed out sequence is | |
| 140 // properly flushed by the handler. | |
| 141 if (IsTimeoutTimerRunning()) { | |
| 142 pending_ack_state_ = PENDING_ACK_NONE; | |
| 143 timeout_monitor_.Stop(); | |
| 144 } | |
| 114 } | 145 } |
| 115 | 146 |
| 116 void Reset() { | 147 bool IsTimeoutTimerRunning() const { return timeout_monitor_.IsRunning(); } |
| 117 pending_ack_state_ = PENDING_ACK_NONE; | |
| 118 timeout_monitor_.Stop(); | |
| 119 } | |
| 120 | 148 |
| 121 void set_timeout_delay(base::TimeDelta timeout_delay) { | 149 bool enabled() const { return enabled_; } |
| 122 timeout_delay_ = timeout_delay; | |
| 123 } | |
| 124 | 150 |
| 125 private: | 151 private: |
| 126 enum PendingAckState { | 152 enum PendingAckState { |
| 127 PENDING_ACK_NONE, | 153 PENDING_ACK_NONE, |
| 128 PENDING_ACK_ORIGINAL_EVENT, | 154 PENDING_ACK_ORIGINAL_EVENT, |
| 129 PENDING_ACK_CANCEL_EVENT, | 155 PENDING_ACK_CANCEL_EVENT, |
| 130 }; | 156 }; |
| 131 | 157 |
| 132 void OnTimeOut() { | 158 void OnTimeOut() { |
| 133 SetPendingAckState(PENDING_ACK_ORIGINAL_EVENT); | 159 SetPendingAckState(PENDING_ACK_ORIGINAL_EVENT); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 177 base::TimeDelta timeout_delay_; | 203 base::TimeDelta timeout_delay_; |
| 178 | 204 |
| 179 // The touch event source for which we expect the next ack. | 205 // The touch event source for which we expect the next ack. |
| 180 PendingAckState pending_ack_state_; | 206 PendingAckState pending_ack_state_; |
| 181 | 207 |
| 182 // The event for which the ack timeout is triggered. | 208 // The event for which the ack timeout is triggered. |
| 183 TouchEventWithLatencyInfo timeout_event_; | 209 TouchEventWithLatencyInfo timeout_event_; |
| 184 | 210 |
| 185 // Provides timeout-based callback behavior. | 211 // Provides timeout-based callback behavior. |
| 186 TimeoutMonitor timeout_monitor_; | 212 TimeoutMonitor timeout_monitor_; |
| 213 | |
| 214 bool enabled_; | |
| 215 bool enabled_for_current_sequence_; | |
| 187 }; | 216 }; |
| 188 | 217 |
| 189 // Provides touchmove slop suppression for a single touch that remains within | 218 // Provides touchmove slop suppression for a single touch that remains within |
| 190 // a given slop region, unless the touchstart is preventDefault'ed. | 219 // a given slop region, unless the touchstart is preventDefault'ed. |
| 191 // TODO(jdduke): Use a flag bundled with each TouchEvent declaring whether it | 220 // TODO(jdduke): Use a flag bundled with each TouchEvent declaring whether it |
| 192 // has exceeded the slop region, removing duplicated slop determination logic. | 221 // has exceeded the slop region, removing duplicated slop determination logic. |
| 193 class TouchEventQueue::TouchMoveSlopSuppressor { | 222 class TouchEventQueue::TouchMoveSlopSuppressor { |
| 194 public: | 223 public: |
| 195 TouchMoveSlopSuppressor(double slop_suppression_length_dips) | 224 TouchMoveSlopSuppressor(double slop_suppression_length_dips) |
| 196 : slop_suppression_length_dips_squared_(0), | 225 : slop_suppression_length_dips_squared_(0), |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 336 touch_scrolling_mode(TOUCH_SCROLLING_MODE_DEFAULT), | 365 touch_scrolling_mode(TOUCH_SCROLLING_MODE_DEFAULT), |
| 337 touch_ack_timeout_delay(base::TimeDelta::FromMilliseconds(200)), | 366 touch_ack_timeout_delay(base::TimeDelta::FromMilliseconds(200)), |
| 338 touch_ack_timeout_supported(false) { | 367 touch_ack_timeout_supported(false) { |
| 339 } | 368 } |
| 340 | 369 |
| 341 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client, | 370 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client, |
| 342 const Config& config) | 371 const Config& config) |
| 343 : client_(client), | 372 : client_(client), |
| 344 dispatching_touch_ack_(NULL), | 373 dispatching_touch_ack_(NULL), |
| 345 dispatching_touch_(false), | 374 dispatching_touch_(false), |
| 346 touch_filtering_state_(TOUCH_FILTERING_STATE_DEFAULT), | 375 has_handlers_(true), |
| 347 ack_timeout_enabled_(config.touch_ack_timeout_supported), | |
| 348 touchmove_slop_suppressor_(new TouchMoveSlopSuppressor( | 376 touchmove_slop_suppressor_(new TouchMoveSlopSuppressor( |
| 349 config.touchmove_slop_suppression_length_dips)), | 377 config.touchmove_slop_suppression_length_dips)), |
| 350 send_touch_events_async_(false), | 378 send_touch_events_async_(false), |
| 351 needs_async_touchmove_for_outer_slop_region_(false), | 379 needs_async_touchmove_for_outer_slop_region_(false), |
| 352 last_sent_touch_timestamp_sec_(0), | 380 last_sent_touch_timestamp_sec_(0), |
| 353 touch_scrolling_mode_(config.touch_scrolling_mode) { | 381 touch_scrolling_mode_(config.touch_scrolling_mode) { |
| 354 DCHECK(client); | 382 DCHECK(client); |
| 355 if (ack_timeout_enabled_) { | 383 if (config.touch_ack_timeout_supported) { |
| 356 timeout_handler_.reset( | 384 timeout_handler_.reset( |
| 357 new TouchTimeoutHandler(this, config.touch_ack_timeout_delay)); | 385 new TouchTimeoutHandler(this, config.touch_ack_timeout_delay)); |
| 358 } | 386 } |
| 359 } | 387 } |
| 360 | 388 |
| 361 TouchEventQueue::~TouchEventQueue() { | 389 TouchEventQueue::~TouchEventQueue() { |
| 362 if (!touch_queue_.empty()) | 390 if (!touch_queue_.empty()) |
| 363 STLDeleteElements(&touch_queue_); | 391 STLDeleteElements(&touch_queue_); |
| 364 } | 392 } |
| 365 | 393 |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 405 dispatching_touch_ = false; | 433 dispatching_touch_ = false; |
| 406 | 434 |
| 407 if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent(ack_result)) | 435 if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent(ack_result)) |
| 408 return; | 436 return; |
| 409 | 437 |
| 410 touchmove_slop_suppressor_->ConfirmTouchEvent(ack_result); | 438 touchmove_slop_suppressor_->ConfirmTouchEvent(ack_result); |
| 411 | 439 |
| 412 if (touch_queue_.empty()) | 440 if (touch_queue_.empty()) |
| 413 return; | 441 return; |
| 414 | 442 |
| 415 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED && | |
| 416 touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT) { | |
| 417 touch_filtering_state_ = FORWARD_ALL_TOUCHES; | |
| 418 } | |
| 419 | |
| 420 PopTouchEventToClient(ack_result, latency_info); | 443 PopTouchEventToClient(ack_result, latency_info); |
| 421 TryForwardNextEventToRenderer(); | 444 TryForwardNextEventToRenderer(); |
| 422 } | 445 } |
| 423 | 446 |
| 424 void TouchEventQueue::TryForwardNextEventToRenderer() { | 447 void TouchEventQueue::TryForwardNextEventToRenderer() { |
| 425 DCHECK(!dispatching_touch_ack_); | 448 DCHECK(!dispatching_touch_ack_); |
| 426 // If there are queued touch events, then try to forward them to the renderer | 449 // If there are queued touch events, then try to forward them to the renderer |
| 427 // immediately, or ACK the events back to the client if appropriate. | 450 // immediately, or ACK the events back to the client if appropriate. |
| 428 while (!touch_queue_.empty()) { | 451 while (!touch_queue_.empty()) { |
| 429 PreFilterResult filter_result = | 452 PreFilterResult filter_result = |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 440 return; | 463 return; |
| 441 } | 464 } |
| 442 } | 465 } |
| 443 } | 466 } |
| 444 | 467 |
| 445 void TouchEventQueue::ForwardNextEventToRenderer() { | 468 void TouchEventQueue::ForwardNextEventToRenderer() { |
| 446 TRACE_EVENT0("input", "TouchEventQueue::ForwardNextEventToRenderer"); | 469 TRACE_EVENT0("input", "TouchEventQueue::ForwardNextEventToRenderer"); |
| 447 | 470 |
| 448 DCHECK(!empty()); | 471 DCHECK(!empty()); |
| 449 DCHECK(!dispatching_touch_); | 472 DCHECK(!dispatching_touch_); |
| 450 DCHECK_NE(touch_filtering_state_, DROP_ALL_TOUCHES); | |
| 451 TouchEventWithLatencyInfo touch = touch_queue_.front()->coalesced_event(); | 473 TouchEventWithLatencyInfo touch = touch_queue_.front()->coalesced_event(); |
| 452 | 474 |
| 453 if (WebTouchEventTraits::IsTouchSequenceStart(touch.event)) { | |
| 454 touch_filtering_state_ = | |
| 455 ack_timeout_enabled_ ? FORWARD_TOUCHES_UNTIL_TIMEOUT | |
| 456 : FORWARD_ALL_TOUCHES; | |
| 457 touch_ack_states_.clear(); | |
| 458 send_touch_events_async_ = false; | |
| 459 pending_async_touchmove_.reset(); | |
| 460 touch_sequence_start_position_ = | |
| 461 gfx::PointF(touch.event.touches[0].position); | |
| 462 } | |
| 463 | |
| 464 if (send_touch_events_async_ && | 475 if (send_touch_events_async_ && |
| 465 touch.event.type == WebInputEvent::TouchMove) { | 476 touch.event.type == WebInputEvent::TouchMove) { |
| 466 // Throttling touchmove's in a continuous touchmove stream while scrolling | 477 // Throttling touchmove's in a continuous touchmove stream while scrolling |
| 467 // reduces the risk of jank. However, it's still important that the web | 478 // reduces the risk of jank. However, it's still important that the web |
| 468 // application be sent touches at key points in the gesture stream, | 479 // application be sent touches at key points in the gesture stream, |
| 469 // e.g., when the application slop region is exceeded or touchmove | 480 // e.g., when the application slop region is exceeded or touchmove |
| 470 // coalescing fails because of different modifiers. | 481 // coalescing fails because of different modifiers. |
| 471 const bool send_touchmove_now = | 482 const bool send_touchmove_now = |
| 472 size() > 1 || | 483 size() > 1 || |
| 473 (touch.event.timeStampSeconds >= | 484 (touch.event.timeStampSeconds >= |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 519 // Note: Marking touchstart events as not-cancelable prevents them from | 530 // Note: Marking touchstart events as not-cancelable prevents them from |
| 520 // blocking subsequent gestures, but it may not be the best long term solution | 531 // blocking subsequent gestures, but it may not be the best long term solution |
| 521 // for tracking touch point dispatch. | 532 // for tracking touch point dispatch. |
| 522 if (send_touch_events_async_) | 533 if (send_touch_events_async_) |
| 523 touch.event.cancelable = false; | 534 touch.event.cancelable = false; |
| 524 | 535 |
| 525 // A synchronous ack will reset |dispatching_touch_|, in which case | 536 // A synchronous ack will reset |dispatching_touch_|, in which case |
| 526 // the touch timeout should not be started. | 537 // the touch timeout should not be started. |
| 527 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true); | 538 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true); |
| 528 SendTouchEventImmediately(touch); | 539 SendTouchEventImmediately(touch); |
| 529 if (dispatching_touch_ && | 540 if (dispatching_touch_ && timeout_handler_) |
| 530 touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT && | 541 timeout_handler_->StartIfNecessary(touch); |
| 531 ShouldTouchTriggerTimeout(touch.event)) { | |
| 532 DCHECK(timeout_handler_); | |
| 533 timeout_handler_->Start(touch); | |
| 534 } | |
| 535 } | 542 } |
| 536 | 543 |
| 537 void TouchEventQueue::OnGestureScrollEvent( | 544 void TouchEventQueue::OnGestureScrollEvent( |
| 538 const GestureEventWithLatencyInfo& gesture_event) { | 545 const GestureEventWithLatencyInfo& gesture_event) { |
| 539 if (gesture_event.event.type == blink::WebInputEvent::GestureScrollBegin) { | 546 if (gesture_event.event.type == blink::WebInputEvent::GestureScrollBegin) { |
| 540 if (touch_filtering_state_ != DROP_ALL_TOUCHES && | |
| 541 touch_filtering_state_ != DROP_TOUCHES_IN_SEQUENCE) { | |
| 542 DCHECK(!touchmove_slop_suppressor_->suppressing_touchmoves()) | |
|
Rick Byers
2014/09/19 17:54:06
We added this DCHECK because we had at least one n
jdduke (slow)
2014/09/22 16:30:14
Hmm, you're right, I thought it would be tricky bu
| |
| 543 << "The renderer should be offered a touchmove before scrolling " | |
| 544 "begins"; | |
| 545 } | |
| 546 | |
| 547 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE && | 547 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE && |
| 548 touch_filtering_state_ != DROP_ALL_TOUCHES && | 548 !drop_remaining_touches_in_sequence_ && |
| 549 touch_filtering_state_ != DROP_TOUCHES_IN_SEQUENCE && | 549 touch_consumer_states_.is_empty()) { |
| 550 (touch_ack_states_.empty() || | |
| 551 AllTouchAckStatesHaveState( | |
| 552 INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS))) { | |
| 553 // If no touch points have a consumer, prevent all subsequent touch events | 550 // If no touch points have a consumer, prevent all subsequent touch events |
| 554 // received during the scroll from reaching the renderer. This ensures | 551 // received during the scroll from reaching the renderer. This ensures |
| 555 // that the first touchstart the renderer sees in any given sequence can | 552 // that the first touchstart the renderer sees in any given sequence can |
| 556 // always be preventDefault'ed (cancelable == true). | 553 // always be preventDefault'ed (cancelable == true). |
| 557 // TODO(jdduke): Revisit if touchstarts during scroll are made cancelable. | 554 // TODO(jdduke): Revisit if touchstarts during scroll are made cancelable. |
| 558 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; | 555 drop_remaining_touches_in_sequence_ = true; |
| 559 } | 556 } |
| 560 | 557 |
| 561 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) { | 558 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) { |
| 562 needs_async_touchmove_for_outer_slop_region_ = true; | 559 needs_async_touchmove_for_outer_slop_region_ = true; |
| 563 pending_async_touchmove_.reset(); | 560 pending_async_touchmove_.reset(); |
| 564 } | 561 } |
| 565 | 562 |
| 566 return; | 563 return; |
| 567 } | 564 } |
| 568 | 565 |
| 569 if (gesture_event.event.type != blink::WebInputEvent::GestureScrollUpdate) | 566 if (gesture_event.event.type != blink::WebInputEvent::GestureScrollUpdate) |
| 570 return; | 567 return; |
| 571 | 568 |
| 572 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) | 569 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) |
| 573 send_touch_events_async_ = true; | 570 send_touch_events_async_ = true; |
| 574 | 571 |
| 575 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_TOUCHCANCEL) | 572 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_TOUCHCANCEL) |
| 576 return; | 573 return; |
| 577 | 574 |
| 578 // We assume that scroll events are generated synchronously from | 575 // We assume that scroll events are generated synchronously from |
| 579 // dispatching a touch event ack. This allows us to generate a synthetic | 576 // dispatching a touch event ack. This allows us to generate a synthetic |
| 580 // cancel event that has the same touch ids as the touch event that | 577 // cancel event that has the same touch ids as the touch event that |
| 581 // is being acked. Otherwise, we don't perform the touch-cancel optimization. | 578 // is being acked. Otherwise, we don't perform the touch-cancel optimization. |
| 582 if (!dispatching_touch_ack_) | 579 if (!dispatching_touch_ack_) |
| 583 return; | 580 return; |
| 584 | 581 |
| 585 if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE) | 582 if (drop_remaining_touches_in_sequence_) |
| 586 return; | 583 return; |
| 587 | 584 |
| 588 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; | 585 drop_remaining_touches_in_sequence_ = true; |
| 589 | 586 |
| 590 // Fake a TouchCancel to cancel the touch points of the touch event | 587 // Fake a TouchCancel to cancel the touch points of the touch event |
| 591 // that is currently being acked. | 588 // that is currently being acked. |
| 592 // Note: |dispatching_touch_ack_| is non-null when we reach here, meaning we | 589 // Note: |dispatching_touch_ack_| is non-null when we reach here, meaning we |
| 593 // are in the scope of PopTouchEventToClient() and that no touch event | 590 // are in the scope of PopTouchEventToClient() and that no touch event |
| 594 // in the queue is waiting for ack from renderer. So we can just insert | 591 // in the queue is waiting for ack from renderer. So we can just insert |
| 595 // the touch cancel at the beginning of the queue. | 592 // the touch cancel at the beginning of the queue. |
| 596 touch_queue_.push_front(new CoalescedWebTouchEvent( | 593 touch_queue_.push_front(new CoalescedWebTouchEvent( |
| 597 ObtainCancelEventForTouchEvent( | 594 ObtainCancelEventForTouchEvent( |
| 598 dispatching_touch_ack_->coalesced_event()), true)); | 595 dispatching_touch_ack_->coalesced_event()), true)); |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 614 // A valid |pending_async_touchmove_| will be flushed when the next event is | 611 // A valid |pending_async_touchmove_| will be flushed when the next event is |
| 615 // forwarded. | 612 // forwarded. |
| 616 send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED); | 613 send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED); |
| 617 if (!send_touch_events_async_) | 614 if (!send_touch_events_async_) |
| 618 needs_async_touchmove_for_outer_slop_region_ = false; | 615 needs_async_touchmove_for_outer_slop_region_ = false; |
| 619 } | 616 } |
| 620 | 617 |
| 621 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { | 618 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { |
| 622 DCHECK(!dispatching_touch_ack_); | 619 DCHECK(!dispatching_touch_ack_); |
| 623 DCHECK(!dispatching_touch_); | 620 DCHECK(!dispatching_touch_); |
| 624 | 621 has_handlers_ = has_handlers; |
| 625 if (has_handlers) { | |
| 626 if (touch_filtering_state_ == DROP_ALL_TOUCHES) { | |
| 627 // If no touch handler was previously registered, ensure that we don't | |
| 628 // send a partial touch sequence to the renderer. | |
| 629 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; | |
| 630 } | |
| 631 } else { | |
| 632 // TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch | |
| 633 // state tracking and/or touch-action filtering (e.g., if the touch handler | |
| 634 // was removed mid-sequence), crbug.com/375940. | |
| 635 touch_filtering_state_ = DROP_ALL_TOUCHES; | |
| 636 pending_async_touchmove_.reset(); | |
| 637 if (timeout_handler_) | |
| 638 timeout_handler_->Reset(); | |
| 639 } | |
| 640 } | 622 } |
| 641 | 623 |
| 642 bool TouchEventQueue::IsPendingAckTouchStart() const { | 624 bool TouchEventQueue::IsPendingAckTouchStart() const { |
| 643 DCHECK(!dispatching_touch_ack_); | 625 DCHECK(!dispatching_touch_ack_); |
| 644 if (touch_queue_.empty()) | 626 if (touch_queue_.empty()) |
| 645 return false; | 627 return false; |
| 646 | 628 |
| 647 const blink::WebTouchEvent& event = | 629 const blink::WebTouchEvent& event = |
| 648 touch_queue_.front()->coalesced_event().event; | 630 touch_queue_.front()->coalesced_event().event; |
| 649 return (event.type == WebInputEvent::TouchStart); | 631 return (event.type == WebInputEvent::TouchStart); |
| 650 } | 632 } |
| 651 | 633 |
| 652 void TouchEventQueue::SetAckTimeoutEnabled(bool enabled) { | 634 void TouchEventQueue::SetAckTimeoutEnabled(bool enabled) { |
| 653 // The timeout handler is valid only if explicitly supported in the config. | 635 if (timeout_handler_) |
| 654 if (!timeout_handler_) | 636 timeout_handler_->SetEnabled(enabled); |
| 655 return; | 637 } |
| 656 | 638 |
| 657 if (ack_timeout_enabled_ == enabled) | 639 bool TouchEventQueue::IsAckTimeoutEnabled() const { |
| 658 return; | 640 return timeout_handler_ && timeout_handler_->enabled(); |
| 659 | |
| 660 ack_timeout_enabled_ = enabled; | |
| 661 | |
| 662 if (enabled) | |
| 663 return; | |
| 664 | |
| 665 if (touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT) | |
| 666 touch_filtering_state_ = FORWARD_ALL_TOUCHES; | |
| 667 // Only reset the |timeout_handler_| if the timer is running and has not yet | |
| 668 // timed out. This ensures that an already timed out sequence is properly | |
| 669 // flushed by the handler. | |
| 670 if (timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning()) | |
| 671 timeout_handler_->Reset(); | |
| 672 } | 641 } |
| 673 | 642 |
| 674 bool TouchEventQueue::HasPendingAsyncTouchMoveForTesting() const { | 643 bool TouchEventQueue::HasPendingAsyncTouchMoveForTesting() const { |
| 675 return pending_async_touchmove_; | 644 return pending_async_touchmove_; |
| 676 } | 645 } |
| 677 | 646 |
| 678 bool TouchEventQueue::IsTimeoutRunningForTesting() const { | 647 bool TouchEventQueue::IsTimeoutRunningForTesting() const { |
| 679 return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning(); | 648 return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning(); |
| 680 } | 649 } |
| 681 | 650 |
| 682 const TouchEventWithLatencyInfo& | 651 const TouchEventWithLatencyInfo& |
| 683 TouchEventQueue::GetLatestEventForTesting() const { | 652 TouchEventQueue::GetLatestEventForTesting() const { |
| 684 return touch_queue_.back()->coalesced_event(); | 653 return touch_queue_.back()->coalesced_event(); |
| 685 } | 654 } |
| 686 | 655 |
| 687 void TouchEventQueue::FlushQueue() { | 656 void TouchEventQueue::FlushQueue() { |
| 688 DCHECK(!dispatching_touch_ack_); | 657 DCHECK(!dispatching_touch_ack_); |
| 689 DCHECK(!dispatching_touch_); | 658 DCHECK(!dispatching_touch_); |
| 690 pending_async_touchmove_.reset(); | 659 pending_async_touchmove_.reset(); |
| 691 if (touch_filtering_state_ != DROP_ALL_TOUCHES) | 660 drop_remaining_touches_in_sequence_ = true; |
| 692 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; | |
| 693 while (!touch_queue_.empty()) | 661 while (!touch_queue_.empty()) |
| 694 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); | 662 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
| 695 } | 663 } |
| 696 | 664 |
| 697 void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result) { | 665 void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result) { |
| 698 AckTouchEventToClient(ack_result, PopTouchEvent()); | 666 AckTouchEventToClient(ack_result, PopTouchEvent()); |
| 699 } | 667 } |
| 700 | 668 |
| 701 void TouchEventQueue::PopTouchEventToClient( | 669 void TouchEventQueue::PopTouchEventToClient( |
| 702 InputEventAckState ack_result, | 670 InputEventAckState ack_result, |
| 703 const LatencyInfo& renderer_latency_info) { | 671 const LatencyInfo& renderer_latency_info) { |
| 704 scoped_ptr<CoalescedWebTouchEvent> acked_event = PopTouchEvent(); | 672 scoped_ptr<CoalescedWebTouchEvent> acked_event = PopTouchEvent(); |
| 705 acked_event->UpdateLatencyInfoForAck(renderer_latency_info); | 673 acked_event->UpdateLatencyInfoForAck(renderer_latency_info); |
| 706 AckTouchEventToClient(ack_result, acked_event.Pass()); | 674 AckTouchEventToClient(ack_result, acked_event.Pass()); |
| 707 } | 675 } |
| 708 | 676 |
| 709 void TouchEventQueue::AckTouchEventToClient( | 677 void TouchEventQueue::AckTouchEventToClient( |
| 710 InputEventAckState ack_result, | 678 InputEventAckState ack_result, |
| 711 scoped_ptr<CoalescedWebTouchEvent> acked_event) { | 679 scoped_ptr<CoalescedWebTouchEvent> acked_event) { |
| 712 DCHECK(acked_event); | 680 DCHECK(acked_event); |
| 713 DCHECK(!dispatching_touch_ack_); | 681 DCHECK(!dispatching_touch_ack_); |
| 714 UpdateTouchAckStates(acked_event->coalesced_event().event, ack_result); | 682 UpdateTouchConsumerStates(acked_event->coalesced_event().event, ack_result); |
| 715 | 683 |
| 716 // Note that acking the touch-event may result in multiple gestures being sent | 684 // Note that acking the touch-event may result in multiple gestures being sent |
| 717 // to the renderer, or touch-events being queued. | 685 // to the renderer, or touch-events being queued. |
| 718 base::AutoReset<const CoalescedWebTouchEvent*> dispatching_touch_ack( | 686 base::AutoReset<const CoalescedWebTouchEvent*> dispatching_touch_ack( |
| 719 &dispatching_touch_ack_, acked_event.get()); | 687 &dispatching_touch_ack_, acked_event.get()); |
| 720 acked_event->DispatchAckToClient(ack_result, client_); | 688 acked_event->DispatchAckToClient(ack_result, client_); |
| 721 } | 689 } |
| 722 | 690 |
| 723 scoped_ptr<CoalescedWebTouchEvent> TouchEventQueue::PopTouchEvent() { | 691 scoped_ptr<CoalescedWebTouchEvent> TouchEventQueue::PopTouchEvent() { |
| 724 DCHECK(!touch_queue_.empty()); | 692 DCHECK(!touch_queue_.empty()); |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 743 } | 711 } |
| 744 | 712 |
| 745 TouchEventQueue::PreFilterResult | 713 TouchEventQueue::PreFilterResult |
| 746 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { | 714 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { |
| 747 if (timeout_handler_ && timeout_handler_->FilterEvent(event)) | 715 if (timeout_handler_ && timeout_handler_->FilterEvent(event)) |
| 748 return ACK_WITH_NO_CONSUMER_EXISTS; | 716 return ACK_WITH_NO_CONSUMER_EXISTS; |
| 749 | 717 |
| 750 if (touchmove_slop_suppressor_->FilterEvent(event)) | 718 if (touchmove_slop_suppressor_->FilterEvent(event)) |
| 751 return ACK_WITH_NOT_CONSUMED; | 719 return ACK_WITH_NOT_CONSUMED; |
| 752 | 720 |
| 753 if (touch_filtering_state_ == DROP_ALL_TOUCHES) | 721 if (WebTouchEventTraits::IsTouchSequenceStart(event)) { |
| 754 return ACK_WITH_NO_CONSUMER_EXISTS; | 722 touch_consumer_states_.clear(); |
| 723 send_touch_events_async_ = false; | |
| 724 pending_async_touchmove_.reset(); | |
| 725 touch_sequence_start_position_ = gfx::PointF(event.touches[0].position); | |
| 726 drop_remaining_touches_in_sequence_ = false; | |
| 727 if (!has_handlers_) { | |
| 728 drop_remaining_touches_in_sequence_ = true; | |
| 729 return ACK_WITH_NO_CONSUMER_EXISTS; | |
| 730 } | |
| 731 } | |
| 755 | 732 |
| 756 if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE && | 733 if (drop_remaining_touches_in_sequence_ && |
| 757 event.type != WebInputEvent::TouchCancel) { | 734 event.type != WebInputEvent::TouchCancel) { |
| 758 if (WebTouchEventTraits::IsTouchSequenceStart(event)) | |
| 759 return FORWARD_TO_RENDERER; | |
| 760 return ACK_WITH_NO_CONSUMER_EXISTS; | 735 return ACK_WITH_NO_CONSUMER_EXISTS; |
| 761 } | 736 } |
| 762 | 737 |
| 763 // Touch press events should always be forwarded to the renderer. | |
| 764 if (event.type == WebInputEvent::TouchStart) | 738 if (event.type == WebInputEvent::TouchStart) |
| 765 return FORWARD_TO_RENDERER; | 739 return has_handlers_ ? FORWARD_TO_RENDERER : ACK_WITH_NO_CONSUMER_EXISTS; |
| 766 | 740 |
| 767 for (unsigned int i = 0; i < event.touchesLength; ++i) { | 741 for (unsigned int i = 0; i < event.touchesLength; ++i) { |
| 768 const WebTouchPoint& point = event.touches[i]; | 742 const WebTouchPoint& point = event.touches[i]; |
| 769 // If a point has been stationary, then don't take it into account. | 743 // If a point has been stationary, then don't take it into account. |
| 770 if (point.state == WebTouchPoint::StateStationary) | 744 if (point.state == WebTouchPoint::StateStationary) |
| 771 continue; | 745 continue; |
| 772 | 746 |
| 773 if (touch_ack_states_.count(point.id) > 0) { | 747 DCHECK_GE(point.id, 0); |
| 774 if (touch_ack_states_.find(point.id)->second != | 748 DCHECK_LT(point.id, 32); |
| 775 INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) | 749 if (touch_consumer_states_.has_bit(point.id)) |
| 776 return FORWARD_TO_RENDERER; | |
| 777 } else { | |
| 778 // If the ACK status of a point is unknown, then the event should be | |
| 779 // forwarded to the renderer. | |
| 780 return FORWARD_TO_RENDERER; | 750 return FORWARD_TO_RENDERER; |
| 781 } | |
| 782 } | 751 } |
| 783 | 752 |
| 784 return ACK_WITH_NO_CONSUMER_EXISTS; | 753 return ACK_WITH_NO_CONSUMER_EXISTS; |
| 785 } | 754 } |
| 786 | 755 |
| 787 void TouchEventQueue::UpdateTouchAckStates(const WebTouchEvent& event, | 756 void TouchEventQueue::UpdateTouchConsumerStates(const WebTouchEvent& event, |
| 788 InputEventAckState ack_result) { | 757 InputEventAckState ack_result) { |
| 789 // Update the ACK status for each touch point in the ACKed event. | 758 // Update the ACK status for each touch point in the ACKed event. |
| 790 if (event.type == WebInputEvent::TouchEnd || | 759 if (event.type == WebInputEvent::TouchEnd || |
| 791 event.type == WebInputEvent::TouchCancel) { | 760 event.type == WebInputEvent::TouchCancel) { |
| 792 // The points have been released. Erase the ACK states. | 761 // The points have been released. Erase the ACK states. |
| 793 for (unsigned i = 0; i < event.touchesLength; ++i) { | 762 for (unsigned i = 0; i < event.touchesLength; ++i) { |
| 794 const WebTouchPoint& point = event.touches[i]; | 763 const WebTouchPoint& point = event.touches[i]; |
| 764 DCHECK_GE(point.id, 0); | |
|
Rick Byers
2014/09/19 17:54:06
I'd prefer if the DCHECK_LT was in the bitset impl
jdduke (slow)
2014/09/22 16:30:14
That's much better, thanks.
| |
| 765 DCHECK_LT(point.id, 32); | |
| 795 if (point.state == WebTouchPoint::StateReleased || | 766 if (point.state == WebTouchPoint::StateReleased || |
| 796 point.state == WebTouchPoint::StateCancelled) | 767 point.state == WebTouchPoint::StateCancelled) |
| 797 touch_ack_states_.erase(point.id); | 768 touch_consumer_states_.clear_bit(point.id); |
| 798 } | 769 } |
| 799 } else if (event.type == WebInputEvent::TouchStart) { | 770 } else if (event.type == WebInputEvent::TouchStart) { |
| 800 for (unsigned i = 0; i < event.touchesLength; ++i) { | 771 for (unsigned i = 0; i < event.touchesLength; ++i) { |
| 801 const WebTouchPoint& point = event.touches[i]; | 772 const WebTouchPoint& point = event.touches[i]; |
| 802 if (point.state == WebTouchPoint::StatePressed) | 773 DCHECK_GE(point.id, 0); |
| 803 touch_ack_states_[point.id] = ack_result; | 774 DCHECK_LT(point.id, 32); |
| 775 if (point.state == WebTouchPoint::StatePressed) { | |
| 776 if (ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) | |
| 777 touch_consumer_states_.mark_bit(point.id); | |
| 778 else | |
| 779 touch_consumer_states_.clear_bit(point.id); | |
| 780 } | |
| 804 } | 781 } |
| 805 } | 782 } |
| 806 } | 783 } |
| 807 | 784 |
| 808 bool TouchEventQueue::AllTouchAckStatesHaveState( | |
| 809 InputEventAckState ack_state) const { | |
| 810 if (touch_ack_states_.empty()) | |
| 811 return false; | |
| 812 | |
| 813 for (TouchPointAckStates::const_iterator iter = touch_ack_states_.begin(), | |
| 814 end = touch_ack_states_.end(); | |
| 815 iter != end; | |
| 816 ++iter) { | |
| 817 if (iter->second != ack_state) | |
| 818 return false; | |
| 819 } | |
| 820 | |
| 821 return true; | |
| 822 } | |
| 823 | |
| 824 } // namespace content | 785 } // namespace content |
| OLD | NEW |