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 5921521e8ce0b2115106fad05b9561420f0aba6f..ee1c7c242ef3faae6f696a758241c2a3557443f8 100644 |
--- a/content/browser/renderer_host/input/touch_event_queue.cc |
+++ b/content/browser/renderer_host/input/touch_event_queue.cc |
@@ -33,7 +33,7 @@ TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent( |
return event; |
} |
-bool IsNewTouchGesture(const WebTouchEvent& event) { |
+bool IsNewTouchSequence(const WebTouchEvent& event) { |
if (event.type != WebInputEvent::TouchStart) |
return false; |
if (!event.touchesLength) |
@@ -80,8 +80,6 @@ class TouchEventQueue::TouchTimeoutHandler { |
SetPendingAckState(PENDING_ACK_CANCEL_EVENT); |
TouchEventWithLatencyInfo cancel_event = |
ObtainCancelEventForTouchEvent(timeout_event_); |
- touch_queue_->UpdateTouchAckStates( |
- cancel_event.event, kDefaultNotForwardedAck); |
touch_queue_->client_->SendTouchEventImmediately(cancel_event); |
} else { |
SetPendingAckState(PENDING_ACK_NONE); |
@@ -126,7 +124,7 @@ class TouchEventQueue::TouchTimeoutHandler { |
DCHECK(HasTimeoutEvent()); |
if (ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) |
return true; |
- return !IsNewTouchGesture(timeout_event_.event); |
+ return !IsNewTouchSequence(timeout_event_.event); |
} |
void SetPendingAckState(PendingAckState new_pending_ack_state) { |
@@ -241,9 +239,7 @@ TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client) |
: client_(client), |
dispatching_touch_ack_(NULL), |
dispatching_touch_(false), |
- has_handlers_(false), |
- scroll_in_progress_(false), |
- renderer_is_consuming_touch_gesture_(false), |
+ touch_filtering_state_(TOUCH_FILTERING_STATE_DEFAULT), |
ack_timeout_enabled_(false) { |
DCHECK(client); |
} |
@@ -254,17 +250,18 @@ TouchEventQueue::~TouchEventQueue() { |
} |
void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) { |
- // Optimization of the case without touch handlers. Removing this path |
- // yields identical results, but this avoids unnecessary allocations. |
- if (!has_handlers_) { |
- DCHECK(touch_queue_.empty()); |
- client_->OnTouchEventAck(event, kDefaultNotForwardedAck); |
- return; |
- } |
- |
// If the queueing of |event| was triggered by an ack dispatch, defer |
// processing the event until the dispatch has finished. |
if (touch_queue_.empty() && !dispatching_touch_ack_) { |
+ // Optimization of the case without touch handlers. Removing this path |
+ // yields identical results, but this avoids unnecessary allocations. |
+ if (touch_filtering_state_ == DROP_ALL_TOUCHES || |
+ (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE && |
+ !IsNewTouchSequence(event.event))) { |
+ client_->OnTouchEventAck(event, kDefaultNotForwardedAck); |
+ return; |
+ } |
+ |
// There is no touch event in the queue. Forward it to the renderer |
// immediately. |
touch_queue_.push_back(new CoalescedWebTouchEvent(event, false)); |
@@ -293,8 +290,9 @@ void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result, |
if (touch_queue_.empty()) |
return; |
- if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) |
- renderer_is_consuming_touch_gesture_ = true; |
+ if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED && |
+ touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT) |
+ touch_filtering_state_ = FORWARD_ALL_TOUCHES; |
const WebTouchEvent& acked_event = |
touch_queue_.front()->coalesced_event().event; |
@@ -310,10 +308,6 @@ void TouchEventQueue::TryForwardNextEventToRenderer() { |
while (!touch_queue_.empty()) { |
const TouchEventWithLatencyInfo& touch = |
touch_queue_.front()->coalesced_event(); |
- if (IsNewTouchGesture(touch.event)) { |
- touch_ack_states_.clear(); |
- renderer_is_consuming_touch_gesture_ = false; |
- } |
if (ShouldForwardToRenderer(touch.event)) { |
ForwardToRenderer(touch); |
break; |
@@ -325,13 +319,21 @@ void TouchEventQueue::TryForwardNextEventToRenderer() { |
void TouchEventQueue::ForwardToRenderer( |
const TouchEventWithLatencyInfo& touch) { |
DCHECK(!dispatching_touch_); |
+ DCHECK_NE(touch_filtering_state_, DROP_ALL_TOUCHES); |
+ |
+ if (IsNewTouchSequence(touch.event)) { |
+ touch_filtering_state_ = |
+ ack_timeout_enabled_ ? FORWARD_TOUCHES_UNTIL_TIMEOUT |
+ : FORWARD_ALL_TOUCHES; |
+ touch_ack_states_.clear(); |
+ } |
+ |
// A synchronous ack will reset |dispatching_touch_|, in which case |
// the touch timeout should not be started. |
base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true); |
client_->SendTouchEventImmediately(touch); |
- if (ack_timeout_enabled_ && |
- dispatching_touch_ && |
- !renderer_is_consuming_touch_gesture_ && |
+ if (dispatching_touch_ && |
+ touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT && |
ShouldTouchTypeTriggerTimeout(touch.event.type)) { |
DCHECK(timeout_handler_); |
timeout_handler_->Start(touch); |
@@ -340,59 +342,53 @@ void TouchEventQueue::ForwardToRenderer( |
void TouchEventQueue::OnGestureScrollEvent( |
const GestureEventWithLatencyInfo& gesture_event) { |
- blink::WebInputEvent::Type type = gesture_event.event.type; |
- if (type == blink::WebInputEvent::GestureScrollBegin) { |
- // We assume the scroll event are generated synchronously from |
- // dispatching a touch event ack, so that we can fake a cancel |
- // event that has the correct touch ids as the touch event that |
- // is being acked. If not, we don't do the touch-cancel optimization. |
- if (scroll_in_progress_ || !dispatching_touch_ack_) |
- return; |
- scroll_in_progress_ = true; |
+ if (gesture_event.event.type != blink::WebInputEvent::GestureScrollBegin) |
+ return; |
- // If we have a timeout event, a cancel has already been dispatched |
- // for the current touch stream. |
- if (HasTimeoutEvent()) |
- return; |
+ // We assume that scroll events are generated synchronously from |
+ // dispatching a touch event ack. This allows us to generate a synthetic |
+ // cancel event that has the same touch ids as the touch event that |
+ // is being acked. Otherwise, we don't perform the touch-cancel optimization. |
+ if (!dispatching_touch_ack_) |
+ return; |
- // Fake a TouchCancel to cancel the touch points of the touch event |
- // that is currently being acked. |
- // Note: |dispatching_touch_ack_| is non-null when we reach here, meaning we |
- // are in the scope of PopTouchEventToClient() and that no touch event |
- // in the queue is waiting for ack from renderer. So we can just insert |
- // the touch cancel at the beginning of the queue. |
- touch_queue_.push_front(new CoalescedWebTouchEvent( |
- ObtainCancelEventForTouchEvent( |
- dispatching_touch_ack_->coalesced_event()), true)); |
- } else if (type == blink::WebInputEvent::GestureScrollEnd || |
- type == blink::WebInputEvent::GestureFlingStart) { |
- scroll_in_progress_ = false; |
- } |
+ if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE) |
+ return; |
+ |
+ touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; |
+ |
+ // Fake a TouchCancel to cancel the touch points of the touch event |
+ // that is currently being acked. |
+ // Note: |dispatching_touch_ack_| is non-null when we reach here, meaning we |
+ // are in the scope of PopTouchEventToClient() and that no touch event |
+ // in the queue is waiting for ack from renderer. So we can just insert |
+ // the touch cancel at the beginning of the queue. |
+ touch_queue_.push_front(new CoalescedWebTouchEvent( |
+ ObtainCancelEventForTouchEvent( |
+ dispatching_touch_ack_->coalesced_event()), true)); |
} |
void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { |
DCHECK(!dispatching_touch_ack_); |
DCHECK(!dispatching_touch_); |
- if (has_handlers_ == has_handlers) |
- return; |
- |
- has_handlers_ = has_handlers; |
- if (!has_handlers_) { |
+ 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. |
+ DCHECK(touch_queue_.empty()); |
+ touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; |
+ } |
+ } else { |
// TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch |
- // state tracking. |
+ // state tracking (e.g., if the touch handler was removed mid-sequence). |
+ touch_filtering_state_ = DROP_ALL_TOUCHES; |
if (timeout_handler_) |
timeout_handler_->Reset(); |
if (!touch_queue_.empty()) |
ProcessTouchAck(kDefaultNotForwardedAck, ui::LatencyInfo()); |
// As there is no touch handler, ack'ing the event should flush the queue. |
DCHECK(touch_queue_.empty()); |
- } else { |
- DCHECK(touch_queue_.empty()); |
- // Prevent a partial sequence from being sent to the renderer. |
- TouchPointAckStates::iterator ack_it = touch_ack_states_.begin(); |
- for (; ack_it != touch_ack_states_.end(); ++ack_it) |
- ack_it->second = kDefaultNotForwardedAck; |
} |
} |
@@ -436,6 +432,8 @@ TouchEventQueue::GetLatestEventForTesting() const { |
void TouchEventQueue::FlushQueue() { |
DCHECK(!dispatching_touch_ack_); |
DCHECK(!dispatching_touch_); |
+ if (touch_filtering_state_ != DROP_ALL_TOUCHES) |
+ touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; |
while (!touch_queue_.empty()) |
PopTouchEventToClient(kDefaultNotForwardedAck, ui::LatencyInfo()); |
} |
@@ -470,12 +468,15 @@ bool TouchEventQueue::ShouldForwardToRenderer( |
if (HasTimeoutEvent()) |
return false; |
- if (!has_handlers_) |
+ if (touch_filtering_state_ == DROP_ALL_TOUCHES) |
return false; |
- if (scroll_in_progress_ && |
- event.type != blink::WebInputEvent::TouchCancel) |
+ if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE && |
+ event.type != WebInputEvent::TouchCancel) { |
+ if (IsNewTouchSequence(event)) |
+ return true; |
return false; |
+ } |
// Touch press events should always be forwarded to the renderer. |
if (event.type == WebInputEvent::TouchStart) |