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 |