| Index: content/browser/renderer_host/input/touch_event_queue.cc
|
| diff --git a/content/browser/renderer_host/input/touch_event_queue.cc b/content/browser/renderer_host/input/touch_event_queue.cc
|
| index 8b03c889980329715a5e70dd6a9c9b07788a865d..7edb36356832cad183e651044ba6d5ca3e154218 100644
|
| --- a/content/browser/renderer_host/input/touch_event_queue.cc
|
| +++ b/content/browser/renderer_host/input/touch_event_queue.cc
|
| @@ -69,15 +69,28 @@ class TouchEventQueue::TouchTimeoutHandler {
|
| timeout_delay_(timeout_delay),
|
| pending_ack_state_(PENDING_ACK_NONE),
|
| timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut,
|
| - base::Unretained(this))) {
|
| + base::Unretained(this))),
|
| + enabled_(true),
|
| + enabled_for_current_sequence_(false) {
|
| DCHECK(timeout_delay != base::TimeDelta());
|
| }
|
|
|
| ~TouchTimeoutHandler() {}
|
|
|
| - void Start(const TouchEventWithLatencyInfo& event) {
|
| + void StartIfNecessary(const TouchEventWithLatencyInfo& event) {
|
| DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE);
|
| - DCHECK(ShouldTouchTriggerTimeout(event.event));
|
| + if (!enabled_)
|
| + return;
|
| +
|
| + if (!ShouldTouchTriggerTimeout(event.event))
|
| + return;
|
| +
|
| + if (WebTouchEventTraits::IsTouchSequenceStart(event.event))
|
| + enabled_for_current_sequence_ = true;
|
| +
|
| + if (!enabled_for_current_sequence_)
|
| + return;
|
| +
|
| timeout_event_ = event;
|
| timeout_monitor_.Restart(timeout_delay_);
|
| }
|
| @@ -85,6 +98,8 @@ class TouchEventQueue::TouchTimeoutHandler {
|
| bool ConfirmTouchEvent(InputEventAckState ack_result) {
|
| switch (pending_ack_state_) {
|
| case PENDING_ACK_NONE:
|
| + if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
|
| + enabled_for_current_sequence_ = false;
|
| timeout_monitor_.Stop();
|
| return false;
|
| case PENDING_ACK_ORIGINAL_EVENT:
|
| @@ -95,7 +110,8 @@ class TouchEventQueue::TouchTimeoutHandler {
|
| touch_queue_->SendTouchEventImmediately(cancel_event);
|
| } else {
|
| SetPendingAckState(PENDING_ACK_NONE);
|
| - touch_queue_->UpdateTouchAckStates(timeout_event_.event, ack_result);
|
| + touch_queue_->UpdateTouchConsumerStates(timeout_event_.event,
|
| + ack_result);
|
| }
|
| return true;
|
| case PENDING_ACK_CANCEL_EVENT:
|
| @@ -109,19 +125,29 @@ class TouchEventQueue::TouchTimeoutHandler {
|
| return HasTimeoutEvent();
|
| }
|
|
|
| - bool IsTimeoutTimerRunning() const {
|
| - return timeout_monitor_.IsRunning();
|
| - }
|
| + void SetEnabled(bool enabled) {
|
| + if (enabled_ == enabled)
|
| + return;
|
|
|
| - void Reset() {
|
| - pending_ack_state_ = PENDING_ACK_NONE;
|
| - timeout_monitor_.Stop();
|
| - }
|
| + enabled_ = enabled;
|
|
|
| - void set_timeout_delay(base::TimeDelta timeout_delay) {
|
| - timeout_delay_ = timeout_delay;
|
| + if (enabled_)
|
| + return;
|
| +
|
| + enabled_for_current_sequence_ = false;
|
| + // Only reset the |timeout_handler_| if the timer is running and has not
|
| + // yet timed out. This ensures that an already timed out sequence is
|
| + // properly flushed by the handler.
|
| + if (IsTimeoutTimerRunning()) {
|
| + pending_ack_state_ = PENDING_ACK_NONE;
|
| + timeout_monitor_.Stop();
|
| + }
|
| }
|
|
|
| + bool IsTimeoutTimerRunning() const { return timeout_monitor_.IsRunning(); }
|
| +
|
| + bool enabled() const { return enabled_; }
|
| +
|
| private:
|
| enum PendingAckState {
|
| PENDING_ACK_NONE,
|
| @@ -184,6 +210,9 @@ class TouchEventQueue::TouchTimeoutHandler {
|
|
|
| // Provides timeout-based callback behavior.
|
| TimeoutMonitor timeout_monitor_;
|
| +
|
| + bool enabled_;
|
| + bool enabled_for_current_sequence_;
|
| };
|
|
|
| // Provides touchmove slop suppression for a single touch that remains within
|
| @@ -344,8 +373,7 @@ TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client,
|
| : client_(client),
|
| dispatching_touch_ack_(NULL),
|
| dispatching_touch_(false),
|
| - touch_filtering_state_(TOUCH_FILTERING_STATE_DEFAULT),
|
| - ack_timeout_enabled_(config.touch_ack_timeout_supported),
|
| + has_handlers_(true),
|
| touchmove_slop_suppressor_(new TouchMoveSlopSuppressor(
|
| config.touchmove_slop_suppression_length_dips)),
|
| send_touch_events_async_(false),
|
| @@ -353,7 +381,7 @@ TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client,
|
| last_sent_touch_timestamp_sec_(0),
|
| touch_scrolling_mode_(config.touch_scrolling_mode) {
|
| DCHECK(client);
|
| - if (ack_timeout_enabled_) {
|
| + if (config.touch_ack_timeout_supported) {
|
| timeout_handler_.reset(
|
| new TouchTimeoutHandler(this, config.touch_ack_timeout_delay));
|
| }
|
| @@ -413,11 +441,6 @@ void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result,
|
| if (touch_queue_.empty())
|
| return;
|
|
|
| - if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED &&
|
| - touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT) {
|
| - touch_filtering_state_ = FORWARD_ALL_TOUCHES;
|
| - }
|
| -
|
| PopTouchEventToClient(ack_result, latency_info);
|
| TryForwardNextEventToRenderer();
|
| }
|
| @@ -448,20 +471,8 @@ void TouchEventQueue::ForwardNextEventToRenderer() {
|
|
|
| DCHECK(!empty());
|
| DCHECK(!dispatching_touch_);
|
| - DCHECK_NE(touch_filtering_state_, DROP_ALL_TOUCHES);
|
| TouchEventWithLatencyInfo touch = touch_queue_.front()->coalesced_event();
|
|
|
| - if (WebTouchEventTraits::IsTouchSequenceStart(touch.event)) {
|
| - touch_filtering_state_ =
|
| - ack_timeout_enabled_ ? FORWARD_TOUCHES_UNTIL_TIMEOUT
|
| - : FORWARD_ALL_TOUCHES;
|
| - touch_ack_states_.clear();
|
| - send_touch_events_async_ = false;
|
| - pending_async_touchmove_.reset();
|
| - touch_sequence_start_position_ =
|
| - gfx::PointF(touch.event.touches[0].position);
|
| - }
|
| -
|
| if (send_touch_events_async_ &&
|
| touch.event.type == WebInputEvent::TouchMove) {
|
| // Throttling touchmove's in a continuous touchmove stream while scrolling
|
| @@ -527,36 +538,27 @@ void TouchEventQueue::ForwardNextEventToRenderer() {
|
| // the touch timeout should not be started.
|
| base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true);
|
| SendTouchEventImmediately(touch);
|
| - if (dispatching_touch_ &&
|
| - touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT &&
|
| - ShouldTouchTriggerTimeout(touch.event)) {
|
| - DCHECK(timeout_handler_);
|
| - timeout_handler_->Start(touch);
|
| - }
|
| + if (dispatching_touch_ && timeout_handler_)
|
| + timeout_handler_->StartIfNecessary(touch);
|
| }
|
|
|
| void TouchEventQueue::OnGestureScrollEvent(
|
| const GestureEventWithLatencyInfo& gesture_event) {
|
| if (gesture_event.event.type == blink::WebInputEvent::GestureScrollBegin) {
|
| - if (touch_filtering_state_ != DROP_ALL_TOUCHES &&
|
| - touch_filtering_state_ != DROP_TOUCHES_IN_SEQUENCE) {
|
| + if (!touch_consumer_states_.is_empty() &&
|
| + !drop_remaining_touches_in_sequence_) {
|
| DCHECK(!touchmove_slop_suppressor_->suppressing_touchmoves())
|
| - << "The renderer should be offered a touchmove before scrolling "
|
| - "begins";
|
| + << "A touch handler should be offered a touchmove before scrolling.";
|
| }
|
| -
|
| if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE &&
|
| - touch_filtering_state_ != DROP_ALL_TOUCHES &&
|
| - touch_filtering_state_ != DROP_TOUCHES_IN_SEQUENCE &&
|
| - (touch_ack_states_.empty() ||
|
| - AllTouchAckStatesHaveState(
|
| - INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS))) {
|
| + !drop_remaining_touches_in_sequence_ &&
|
| + touch_consumer_states_.is_empty()) {
|
| // If no touch points have a consumer, prevent all subsequent touch events
|
| // received during the scroll from reaching the renderer. This ensures
|
| // that the first touchstart the renderer sees in any given sequence can
|
| // always be preventDefault'ed (cancelable == true).
|
| // TODO(jdduke): Revisit if touchstarts during scroll are made cancelable.
|
| - touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
|
| + drop_remaining_touches_in_sequence_ = true;
|
| }
|
|
|
| if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) {
|
| @@ -583,10 +585,10 @@ void TouchEventQueue::OnGestureScrollEvent(
|
| if (!dispatching_touch_ack_)
|
| return;
|
|
|
| - if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE)
|
| + if (drop_remaining_touches_in_sequence_)
|
| return;
|
|
|
| - touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
|
| + drop_remaining_touches_in_sequence_ = true;
|
|
|
| // Fake a TouchCancel to cancel the touch points of the touch event
|
| // that is currently being acked.
|
| @@ -622,22 +624,7 @@ void TouchEventQueue::OnGestureEventAck(
|
| void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) {
|
| DCHECK(!dispatching_touch_ack_);
|
| DCHECK(!dispatching_touch_);
|
| -
|
| - if (has_handlers) {
|
| - if (touch_filtering_state_ == DROP_ALL_TOUCHES) {
|
| - // If no touch handler was previously registered, ensure that we don't
|
| - // send a partial touch sequence to the renderer.
|
| - touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
|
| - }
|
| - } else {
|
| - // TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch
|
| - // state tracking and/or touch-action filtering (e.g., if the touch handler
|
| - // was removed mid-sequence), crbug.com/375940.
|
| - touch_filtering_state_ = DROP_ALL_TOUCHES;
|
| - pending_async_touchmove_.reset();
|
| - if (timeout_handler_)
|
| - timeout_handler_->Reset();
|
| - }
|
| + has_handlers_ = has_handlers;
|
| }
|
|
|
| bool TouchEventQueue::IsPendingAckTouchStart() const {
|
| @@ -651,25 +638,12 @@ bool TouchEventQueue::IsPendingAckTouchStart() const {
|
| }
|
|
|
| void TouchEventQueue::SetAckTimeoutEnabled(bool enabled) {
|
| - // The timeout handler is valid only if explicitly supported in the config.
|
| - if (!timeout_handler_)
|
| - return;
|
| -
|
| - if (ack_timeout_enabled_ == enabled)
|
| - return;
|
| -
|
| - ack_timeout_enabled_ = enabled;
|
| -
|
| - if (enabled)
|
| - return;
|
| + if (timeout_handler_)
|
| + timeout_handler_->SetEnabled(enabled);
|
| +}
|
|
|
| - if (touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT)
|
| - touch_filtering_state_ = FORWARD_ALL_TOUCHES;
|
| - // Only reset the |timeout_handler_| if the timer is running and has not yet
|
| - // timed out. This ensures that an already timed out sequence is properly
|
| - // flushed by the handler.
|
| - if (timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning())
|
| - timeout_handler_->Reset();
|
| +bool TouchEventQueue::IsAckTimeoutEnabled() const {
|
| + return timeout_handler_ && timeout_handler_->enabled();
|
| }
|
|
|
| bool TouchEventQueue::HasPendingAsyncTouchMoveForTesting() const {
|
| @@ -689,8 +663,7 @@ void TouchEventQueue::FlushQueue() {
|
| DCHECK(!dispatching_touch_ack_);
|
| DCHECK(!dispatching_touch_);
|
| pending_async_touchmove_.reset();
|
| - if (touch_filtering_state_ != DROP_ALL_TOUCHES)
|
| - touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
|
| + drop_remaining_touches_in_sequence_ = true;
|
| while (!touch_queue_.empty())
|
| PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
|
| }
|
| @@ -711,7 +684,7 @@ void TouchEventQueue::AckTouchEventToClient(
|
| const ui::LatencyInfo* optional_latency_info) {
|
| DCHECK(acked_event);
|
| DCHECK(!dispatching_touch_ack_);
|
| - UpdateTouchAckStates(acked_event->coalesced_event().event, ack_result);
|
| + UpdateTouchConsumerStates(acked_event->coalesced_event().event, ack_result);
|
|
|
| // Note that acking the touch-event may result in multiple gestures being sent
|
| // to the renderer, or touch-events being queued.
|
| @@ -750,19 +723,25 @@ TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) {
|
| if (touchmove_slop_suppressor_->FilterEvent(event))
|
| return ACK_WITH_NOT_CONSUMED;
|
|
|
| - if (touch_filtering_state_ == DROP_ALL_TOUCHES)
|
| - return ACK_WITH_NO_CONSUMER_EXISTS;
|
| + if (WebTouchEventTraits::IsTouchSequenceStart(event)) {
|
| + touch_consumer_states_.clear();
|
| + send_touch_events_async_ = false;
|
| + pending_async_touchmove_.reset();
|
| + touch_sequence_start_position_ = gfx::PointF(event.touches[0].position);
|
| + drop_remaining_touches_in_sequence_ = false;
|
| + if (!has_handlers_) {
|
| + drop_remaining_touches_in_sequence_ = true;
|
| + return ACK_WITH_NO_CONSUMER_EXISTS;
|
| + }
|
| + }
|
|
|
| - if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE &&
|
| + if (drop_remaining_touches_in_sequence_ &&
|
| event.type != WebInputEvent::TouchCancel) {
|
| - if (WebTouchEventTraits::IsTouchSequenceStart(event))
|
| - return FORWARD_TO_RENDERER;
|
| return ACK_WITH_NO_CONSUMER_EXISTS;
|
| }
|
|
|
| - // Touch press events should always be forwarded to the renderer.
|
| if (event.type == WebInputEvent::TouchStart)
|
| - return FORWARD_TO_RENDERER;
|
| + return has_handlers_ ? FORWARD_TO_RENDERER : ACK_WITH_NO_CONSUMER_EXISTS;
|
|
|
| for (unsigned int i = 0; i < event.touchesLength; ++i) {
|
| const WebTouchPoint& point = event.touches[i];
|
| @@ -770,22 +749,15 @@ TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) {
|
| if (point.state == WebTouchPoint::StateStationary)
|
| continue;
|
|
|
| - if (touch_ack_states_.count(point.id) > 0) {
|
| - if (touch_ack_states_.find(point.id)->second !=
|
| - INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)
|
| - return FORWARD_TO_RENDERER;
|
| - } else {
|
| - // If the ACK status of a point is unknown, then the event should be
|
| - // forwarded to the renderer.
|
| + if (touch_consumer_states_.has_bit(point.id))
|
| return FORWARD_TO_RENDERER;
|
| - }
|
| }
|
|
|
| return ACK_WITH_NO_CONSUMER_EXISTS;
|
| }
|
|
|
| -void TouchEventQueue::UpdateTouchAckStates(const WebTouchEvent& event,
|
| - InputEventAckState ack_result) {
|
| +void TouchEventQueue::UpdateTouchConsumerStates(const WebTouchEvent& event,
|
| + InputEventAckState ack_result) {
|
| // Update the ACK status for each touch point in the ACKed event.
|
| if (event.type == WebInputEvent::TouchEnd ||
|
| event.type == WebInputEvent::TouchCancel) {
|
| @@ -794,31 +766,19 @@ void TouchEventQueue::UpdateTouchAckStates(const WebTouchEvent& event,
|
| const WebTouchPoint& point = event.touches[i];
|
| if (point.state == WebTouchPoint::StateReleased ||
|
| point.state == WebTouchPoint::StateCancelled)
|
| - touch_ack_states_.erase(point.id);
|
| + touch_consumer_states_.clear_bit(point.id);
|
| }
|
| } else if (event.type == WebInputEvent::TouchStart) {
|
| for (unsigned i = 0; i < event.touchesLength; ++i) {
|
| const WebTouchPoint& point = event.touches[i];
|
| - if (point.state == WebTouchPoint::StatePressed)
|
| - touch_ack_states_[point.id] = ack_result;
|
| + if (point.state == WebTouchPoint::StatePressed) {
|
| + if (ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)
|
| + touch_consumer_states_.mark_bit(point.id);
|
| + else
|
| + touch_consumer_states_.clear_bit(point.id);
|
| + }
|
| }
|
| }
|
| }
|
|
|
| -bool TouchEventQueue::AllTouchAckStatesHaveState(
|
| - InputEventAckState ack_state) const {
|
| - if (touch_ack_states_.empty())
|
| - return false;
|
| -
|
| - for (TouchPointAckStates::const_iterator iter = touch_ack_states_.begin(),
|
| - end = touch_ack_states_.end();
|
| - iter != end;
|
| - ++iter) {
|
| - if (iter->second != ack_state)
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| } // namespace content
|
|
|