diff options
author | jdduke@chromium.org <jdduke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-28 07:24:09 +0000 |
---|---|---|
committer | jdduke@chromium.org <jdduke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-28 07:24:09 +0000 |
commit | a1710ba470bc04d9bc07ae8c6f0ecbd50d5bd077 (patch) | |
tree | acfe58f114c234609850a7c00461a0f12dcd8714 | |
parent | b20f6ac13aa30e15d7c947a4bf8877260387edab (diff) | |
download | chromium_src-a1710ba470bc04d9bc07ae8c6f0ecbd50d5bd077.zip chromium_src-a1710ba470bc04d9bc07ae8c6f0ecbd50d5bd077.tar.gz chromium_src-a1710ba470bc04d9bc07ae8c6f0ecbd50d5bd077.tar.bz2 |
Implement async touchmove dispatch during scroll
This implements a touch dispatch model in which touchmove sending is throttled
while a scroll sequence is active and being consumed. Such touchmove's are
marked with |cancelable = false|, indicating to any potential consumers that the
event cannot be prevented from triggering a platform gesture. Throttling limits
the touchmove sending rate during scrolling to 1 event per 200ms.
BUG=346693
Review URL: https://codereview.chromium.org/245833002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@266470 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/app/generated_resources.grd | 6 | ||||
-rw-r--r-- | chrome/browser/about_flags.cc | 4 | ||||
-rw-r--r-- | content/browser/renderer_host/input/input_router_impl.cc | 24 | ||||
-rw-r--r-- | content/browser/renderer_host/input/input_router_impl_unittest.cc | 98 | ||||
-rw-r--r-- | content/browser/renderer_host/input/touch_event_queue.cc | 286 | ||||
-rw-r--r-- | content/browser/renderer_host/input/touch_event_queue.h | 60 | ||||
-rw-r--r-- | content/browser/renderer_host/input/touch_event_queue_unittest.cc | 255 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host_unittest.cc | 70 | ||||
-rw-r--r-- | content/common/input/web_input_event_traits.cc | 31 | ||||
-rw-r--r-- | content/common/input/web_input_event_traits.h | 2 | ||||
-rw-r--r-- | content/public/common/content_switches.cc | 4 | ||||
-rw-r--r-- | content/public/common/content_switches.h | 2 | ||||
-rw-r--r-- | content/renderer/input/input_event_filter.cc | 2 | ||||
-rw-r--r-- | content/renderer/render_widget.cc | 2 |
14 files changed, 562 insertions, 284 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index c0dd7ba..bddbda5 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -6693,13 +6693,13 @@ Keep your key file in a safe place. You will need it to create new versions of y Touch scrolling mode. </message> <message name="IDS_FLAGS_TOUCH_SCROLLING_MODE_DESCRIPTION" desc="Description for the flag to control touch scrolling mode."> - Change the touch event behavior while scrolling. "touchcancel" is what chrome has historically used, and "absorb-touchmove" is the new preferred mode. + Change the touch event behavior while scrolling. "touchcancel" is what chrome has historically used, and "async-touchmove" is the new preferred mode. </message> <message name="IDS_FLAGS_TOUCH_SCROLLING_MODE_TOUCHCANCEL" desc="Name for the touchcancel scrolling mode"> touchcancel </message> - <message name="IDS_FLAGS_TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE" desc="Name for the absorb-touchmove scrolling mode"> - absorb-touchmove + <message name="IDS_FLAGS_TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE" desc="Name for the async-touchmove scrolling mode"> + async-touchmove </message> <message name="IDS_FLAGS_TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE" desc="Name for the sync-touchmove scrolling mode"> sync-touchmove diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 1797663..3ba634e 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -374,9 +374,9 @@ const Experiment::Choice kTouchScrollingModeChoices[] = { { IDS_FLAGS_TOUCH_SCROLLING_MODE_TOUCHCANCEL, switches::kTouchScrollingMode, switches::kTouchScrollingModeTouchcancel }, - { IDS_FLAGS_TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE, + { IDS_FLAGS_TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE, switches::kTouchScrollingMode, - switches::kTouchScrollingModeAbsorbTouchmove }, + switches::kTouchScrollingModeAsyncTouchmove }, { IDS_FLAGS_TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE, switches::kTouchScrollingMode, switches::kTouchScrollingModeSyncTouchmove }, diff --git a/content/browser/renderer_host/input/input_router_impl.cc b/content/browser/renderer_host/input/input_router_impl.cc index 36a7a26..8f1682a 100644 --- a/content/browser/renderer_host/input/input_router_impl.cc +++ b/content/browser/renderer_host/input/input_router_impl.cc @@ -88,10 +88,10 @@ double GetTouchMoveSlopSuppressionLengthDips() { TouchEventQueue::TouchScrollingMode GetTouchScrollingMode() { std::string modeString = CommandLine::ForCurrentProcess()-> GetSwitchValueASCII(switches::kTouchScrollingMode); + if (modeString == switches::kTouchScrollingModeAsyncTouchmove) + return TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE; if (modeString == switches::kTouchScrollingModeSyncTouchmove) return TouchEventQueue::TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE; - if (modeString == switches::kTouchScrollingModeAbsorbTouchmove) - return TouchEventQueue::TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE; if (modeString == switches::kTouchScrollingModeTouchcancel) return TouchEventQueue::TOUCH_SCROLLING_MODE_TOUCHCANCEL; if (modeString != "") @@ -448,15 +448,14 @@ void InputRouterImpl::OfferToHandlers(const WebInputEvent& input_event, // Touch events should always indicate in the event whether they are // cancelable (respect ACK disposition) or not. - bool ignoresAck = - WebInputEventTraits::IgnoresAckDisposition(input_event.type); + bool ignores_ack = WebInputEventTraits::IgnoresAckDisposition(input_event); if (WebInputEvent::isTouchEventType(input_event.type)) { - DCHECK(!ignoresAck == + DCHECK(!ignores_ack == static_cast<const blink::WebTouchEvent&>(input_event).cancelable); } // If we don't care about the ack disposition, send the ack immediately. - if (ignoresAck) { + if (ignores_ack) { ProcessInputEventAck(input_event.type, INPUT_EVENT_ACK_STATE_IGNORED, latency_info, @@ -530,10 +529,10 @@ bool InputRouterImpl::OfferToRenderer(const WebInputEvent& input_event, bool is_keyboard_shortcut) { if (Send(new InputMsg_HandleInputEvent( routing_id(), &input_event, latency_info, is_keyboard_shortcut))) { - // Ack messages for ignored ack event types are not required, and might - // never be sent by the renderer. Consequently, such event types should not - // affect event timing or in-flight event count metrics. - if (!WebInputEventTraits::IgnoresAckDisposition(input_event.type)) { + // Ack messages for ignored ack event types should never be sent by the + // renderer. Consequently, such event types should not affect event time + // or in-flight event count metrics. + if (!WebInputEventTraits::IgnoresAckDisposition(input_event)) { input_event_start_time_ = TimeTicks::Now(); client_->IncrementInFlightEventCount(); } @@ -545,11 +544,6 @@ bool InputRouterImpl::OfferToRenderer(const WebInputEvent& input_event, void InputRouterImpl::OnInputEventAck(WebInputEvent::Type event_type, InputEventAckState ack_result, const ui::LatencyInfo& latency_info) { - // A synthetic ack will already have been sent for this event, and it should - // not affect event timing or in-flight count metrics. - if (WebInputEventTraits::IgnoresAckDisposition(event_type)) - return; - client_->DecrementInFlightEventCount(); // Log the time delta for processing an input event. diff --git a/content/browser/renderer_host/input/input_router_impl_unittest.cc b/content/browser/renderer_host/input/input_router_impl_unittest.cc index 0fbb1b3..69adf74 100644 --- a/content/browser/renderer_host/input/input_router_impl_unittest.cc +++ b/content/browser/renderer_host/input/input_router_impl_unittest.cc @@ -51,6 +51,29 @@ const WebInputEvent* GetInputEventFromMessage(const IPC::Message& message) { return reinterpret_cast<const WebInputEvent*>(data); } +WebInputEvent& GetEventWithType(WebInputEvent::Type type) { + WebInputEvent* event = NULL; + if (WebInputEvent::isMouseEventType(type)) { + static WebMouseEvent mouse; + event = &mouse; + } else if (WebInputEvent::isTouchEventType(type)) { + static WebTouchEvent touch; + event = &touch; + } else if (WebInputEvent::isKeyboardEventType(type)) { + static WebKeyboardEvent key; + event = &key; + } else if (WebInputEvent::isGestureEventType(type)) { + static WebGestureEvent gesture; + event = &gesture; + } else if (type == WebInputEvent::MouseWheel) { + static WebMouseWheelEvent wheel; + event = &wheel; + } + CHECK(event); + event->type = type; + return *event; +} + bool GetIsShortcutFromHandleInputEventMessage(const IPC::Message* msg) { InputMsg_HandleInputEvent::Schema::Param param; InputMsg_HandleInputEvent::Read(msg, ¶m); @@ -252,6 +275,7 @@ class InputRouterImplTest : public testing::Test { void CancelTouchPoint(int index) { touch_event_.CancelPoint(index); } + void SendInputEventACK(blink::WebInputEvent::Type type, InputEventAckState ack_result) { scoped_ptr<IPC::Message> response( @@ -742,7 +766,7 @@ TEST_F(InputRouterImplTest, TouchTypesIgnoringAck) { ASSERT_LT(start_type, end_type); for (int i = start_type; i <= end_type; ++i) { WebInputEvent::Type type = static_cast<WebInputEvent::Type>(i); - if (!WebInputEventTraits::IgnoresAckDisposition(type)) + if (!WebInputEventTraits::IgnoresAckDisposition(GetEventWithType(type))) continue; // The TouchEventQueue requires an initial TouchStart for it to begin @@ -803,7 +827,7 @@ TEST_F(InputRouterImplTest, GestureTypesIgnoringAck) { WebInputEvent::GestureScrollEnd}; for (int i = 0; i < kEventTypesLength; ++i) { WebInputEvent::Type type = eventTypes[i]; - if (!WebInputEventTraits::IgnoresAckDisposition(type)) { + if (!WebInputEventTraits::IgnoresAckDisposition(GetEventWithType(type))) { SimulateGestureEvent(type, WebGestureEvent::Touchscreen); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); @@ -823,10 +847,6 @@ TEST_F(InputRouterImplTest, GestureTypesIgnoringAck) { EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0, client_->in_flight_event_count()); EXPECT_FALSE(HasPendingEvents()); - SendInputEventACK(type, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); - EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); - EXPECT_FALSE(HasPendingEvents()); } } @@ -837,7 +857,8 @@ TEST_F(InputRouterImplTest, MouseTypesIgnoringAck) { for (int i = start_type; i <= end_type; ++i) { WebInputEvent::Type type = static_cast<WebInputEvent::Type>(i); int expected_in_flight_event_count = - WebInputEventTraits::IgnoresAckDisposition(type) ? 0 : 1; + WebInputEventTraits::IgnoresAckDisposition(GetEventWithType(type)) ? 0 + : 1; // Note: Mouse event acks are never forwarded to the ack handler, so the key // result here is that ignored ack types don't affect the in-flight count. @@ -845,10 +866,12 @@ TEST_F(InputRouterImplTest, MouseTypesIgnoringAck) { EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(expected_in_flight_event_count, client_->in_flight_event_count()); - SendInputEventACK(type, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); - EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); - EXPECT_EQ(0, client_->in_flight_event_count()); + if (expected_in_flight_event_count) { + SendInputEventACK(type, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); + EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); + EXPECT_EQ(0, client_->in_flight_event_count()); + } } } @@ -871,7 +894,8 @@ TEST_F(InputRouterImplTest, RequiredEventAckTypes) { }; for (size_t i = 0; i < arraysize(kRequiredEventAckTypes); ++i) { const WebInputEvent::Type required_ack_type = kRequiredEventAckTypes[i]; - EXPECT_FALSE(WebInputEventTraits::IgnoresAckDisposition(required_ack_type)); + EXPECT_FALSE(WebInputEventTraits::IgnoresAckDisposition( + GetEventWithType(required_ack_type))); } } @@ -920,51 +944,27 @@ TEST_F(InputRouterImplTest, GestureTypesIgnoringAckInterleaved) { EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); - // Now ack each event. Ack-ignoring events should not be dispatched until all - // prior events which observe ack disposition have been fired, at which - // point they should be sent immediately. They should also have no effect - // on the in-flight event count. - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - + // Now ack each ack-respecting event. Ack-ignoring events should not be + // dispatched until all prior events which observe ack disposition have been + // fired, at which point they should be sent immediately. They should also + // have no effect on the in-flight event count. SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(2U, GetSentMessageCountAndResetSink()); EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(1, client_->in_flight_event_count()); - // For events which ignore ack disposition, non-synthetic acks are ignored. - SendInputEventACK(WebInputEvent::GestureTapDown, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); - EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); - EXPECT_EQ(1, client_->in_flight_event_count()); - SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(2U, GetSentMessageCountAndResetSink()); EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(1, client_->in_flight_event_count()); - // For events which ignore ack disposition, non-synthetic acks are ignored. - SendInputEventACK(WebInputEvent::GestureShowPress, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); - EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); - EXPECT_EQ(1, client_->in_flight_event_count()); - SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0, client_->in_flight_event_count()); - - // For events which ignore ack disposition, non-synthetic acks are ignored. - SendInputEventACK(WebInputEvent::GestureTapCancel, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); - EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); - EXPECT_EQ(0, client_->in_flight_event_count()); } // Test that GestureShowPress events don't get out of order due to @@ -1003,12 +1003,6 @@ TEST_F(InputRouterImplTest, GestureShowPressIsInOrder) { // ShowPress has entered the queue. EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); - // This ack is ignored. - SendInputEventACK(WebInputEvent::GesturePinchBegin, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); - EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); - SendInputEventACK(WebInputEvent::GesturePinchUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); // Now that the Tap has been ACKed, the ShowPress events should receive @@ -1288,8 +1282,6 @@ TEST_F(InputRouterImplTest, DoubleTapGestureDependsOnFirstTap) { // auto. SimulateGestureEvent(WebInputEvent::GestureTapUnconfirmed, WebGestureEvent::Touchscreen); - SendInputEventACK(WebInputEvent::GestureTap, - INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); // This tap gesture is dropped, since the GestureTapUnconfirmed was turned @@ -1313,7 +1305,7 @@ TEST_F(InputRouterImplTest, DoubleTapGestureDependsOnFirstTap) { WebGestureEvent::Touchscreen); // This test will become invalid if GestureDoubleTap stops requiring an ack. DCHECK(!WebInputEventTraits::IgnoresAckDisposition( - WebInputEvent::GestureDoubleTap)); + GetEventWithType(WebInputEvent::GestureDoubleTap))); EXPECT_EQ(0, client_->in_flight_event_count()); } @@ -1349,6 +1341,10 @@ TEST_F(InputRouterImplTest, InputFlush) { WebGestureEvent::Touchscreen); SimulateGestureEvent(WebInputEvent::GestureScrollUpdate, WebGestureEvent::Touchscreen); + SimulateGestureEvent(WebInputEvent::GesturePinchBegin, + WebGestureEvent::Touchscreen); + SimulateGestureEvent(WebInputEvent::GesturePinchUpdate, + WebGestureEvent::Touchscreen); Flush(); EXPECT_EQ(0U, GetAndResetDidFlushCount()); @@ -1363,13 +1359,13 @@ TEST_F(InputRouterImplTest, InputFlush) { EXPECT_TRUE(HasPendingEvents()); // One more gesture to go. - SendInputEventACK(WebInputEvent::GestureScrollBegin, + SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, GetAndResetDidFlushCount()); EXPECT_TRUE(HasPendingEvents()); // The final ack'ed gesture should trigger the DidFlush. - SendInputEventACK(WebInputEvent::GestureScrollUpdate, + SendInputEventACK(WebInputEvent::GesturePinchUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(1U, GetAndResetDidFlushCount()); EXPECT_FALSE(HasPendingEvents()); diff --git a/content/browser/renderer_host/input/touch_event_queue.cc b/content/browser/renderer_host/input/touch_event_queue.cc index 4f4aa7b..639a025 100644 --- a/content/browser/renderer_host/input/touch_event_queue.cc +++ b/content/browser/renderer_host/input/touch_event_queue.cc @@ -21,6 +21,15 @@ using ui::LatencyInfo; namespace content { namespace { +// Time interval at which touchmove events will be forwarded to the client while +// scrolling is active and possible. +const double kAsyncTouchMoveIntervalSec = .2; + +// A slop region just larger than that used by many web applications. When +// touchmove's are being sent asynchronously, movement outside this region will +// trigger an immediate async touchmove to cancel potential tap-related logic. +const double kApplicationSlopRegionLengthDipsSqared = 15. * 15.; + // Using a small epsilon when comparing slop distances allows pixel perfect // slop determination when using fractional DIP coordinates (assuming the slop // region and DPI scale are reasonably proportioned). @@ -39,9 +48,16 @@ TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent( return event; } -bool ShouldTouchTypeTriggerTimeout(WebInputEvent::Type type) { - return type == WebInputEvent::TouchStart || - type == WebInputEvent::TouchMove; +bool ShouldTouchTriggerTimeout(const WebTouchEvent& event) { + return (event.type == WebInputEvent::TouchStart || + event.type == WebInputEvent::TouchMove) && + !WebInputEventTraits::IgnoresAckDisposition(event); +} + +bool OutsideApplicationSlopRegion(const WebTouchEvent& event, + const gfx::PointF& anchor) { + return (gfx::PointF(event.touches[0].position) - anchor).LengthSquared() > + kApplicationSlopRegionLengthDipsSqared; } } // namespace @@ -63,7 +79,7 @@ class TouchEventQueue::TouchTimeoutHandler { void Start(const TouchEventWithLatencyInfo& event) { DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE); - DCHECK(ShouldTouchTypeTriggerTimeout(event.event.type)); + DCHECK(ShouldTouchTriggerTimeout(event.event)); timeout_event_ = event; timeout_monitor_.Restart(timeout_delay_); } @@ -78,7 +94,7 @@ class TouchEventQueue::TouchTimeoutHandler { SetPendingAckState(PENDING_ACK_CANCEL_EVENT); TouchEventWithLatencyInfo cancel_event = ObtainCancelEventForTouchEvent(timeout_event_); - touch_queue_->client_->SendTouchEventImmediately(cancel_event); + touch_queue_->SendTouchEventImmediately(cancel_event); } else { SetPendingAckState(PENDING_ACK_NONE); touch_queue_->UpdateTouchAckStates(timeout_event_.event, ack_result); @@ -228,25 +244,27 @@ class TouchEventQueue::TouchMoveSlopSuppressor { // the Client receives the event with their original timestamp. class CoalescedWebTouchEvent { public: - CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event, - bool ignore_ack) - : coalesced_event_(event), - ignore_ack_(ignore_ack) { - events_.push_back(event); - TRACE_EVENT_ASYNC_BEGIN0( - "input", "TouchEventQueue::QueueEvent", this); + // Events for which |async| is true will not be ack'ed to the client after the + // corresponding ack is received following dispatch. + CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event, bool async) + : coalesced_event_(event) { + if (async) + coalesced_event_.event.cancelable = false; + else + events_to_ack_.push_back(event); + + TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventQueue::QueueEvent", this); } ~CoalescedWebTouchEvent() { - TRACE_EVENT_ASYNC_END0( - "input", "TouchEventQueue::QueueEvent", this); + TRACE_EVENT_ASYNC_END0("input", "TouchEventQueue::QueueEvent", this); } // Coalesces the event with the existing event if possible. Returns whether // the event was coalesced. bool CoalesceEventIfPossible( const TouchEventWithLatencyInfo& event_with_latency) { - if (ignore_ack_) + if (!WillDispatchAckToClient()) return false; if (!coalesced_event_.CanCoalesceWith(event_with_latency)) @@ -255,36 +273,50 @@ class CoalescedWebTouchEvent { TRACE_EVENT_INSTANT0( "input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD); coalesced_event_.CoalesceWith(event_with_latency); - events_.push_back(event_with_latency); + events_to_ack_.push_back(event_with_latency); return true; } - const TouchEventWithLatencyInfo& coalesced_event() const { - return coalesced_event_; - } + void UpdateLatencyInfoForAck(const ui::LatencyInfo& renderer_latency_info) { + if (!WillDispatchAckToClient()) + return; - WebTouchEventWithLatencyList::iterator begin() { - return events_.begin(); + for (WebTouchEventWithLatencyList::iterator iter = events_to_ack_.begin(), + end = events_to_ack_.end(); + iter != end; + ++iter) { + iter->latency.AddNewLatencyFrom(renderer_latency_info); + } } - WebTouchEventWithLatencyList::iterator end() { - return events_.end(); - } + void DispatchAckToClient(InputEventAckState ack_result, + TouchEventQueueClient* client) { + DCHECK(client); + if (!WillDispatchAckToClient()) + return; - size_t size() const { return events_.size(); } + for (WebTouchEventWithLatencyList::const_iterator + iter = events_to_ack_.begin(), + end = events_to_ack_.end(); + iter != end; + ++iter) { + client->OnTouchEventAck(*iter, ack_result); + } + } - bool ignore_ack() const { return ignore_ack_; } + const TouchEventWithLatencyInfo& coalesced_event() const { + return coalesced_event_; + } private: + bool WillDispatchAckToClient() const { return !events_to_ack_.empty(); } + // This is the event that is forwarded to the renderer. TouchEventWithLatencyInfo coalesced_event_; - // This is the list of the original events that were coalesced. - WebTouchEventWithLatencyList events_; - - // If |ignore_ack_| is true, don't send this touch event to client - // when the event is acked. - bool ignore_ack_; + // This is the list of the original events that were coalesced, each requiring + // future ack dispatch to the client. + WebTouchEventWithLatencyList events_to_ack_; DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent); }; @@ -299,7 +331,8 @@ TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client, ack_timeout_enabled_(false), touchmove_slop_suppressor_(new TouchMoveSlopSuppressor( touchmove_suppression_length_dips + kSlopEpsilon)), - absorbing_touch_moves_(false), + send_touch_events_async_(false), + last_sent_touch_timestamp_sec_(0), touch_scrolling_mode_(mode) { DCHECK(client); } @@ -317,17 +350,19 @@ void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) { 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 && - !WebTouchEventTraits::IsTouchSequenceStart(event.event))) { - client_->OnTouchEventAck(event, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); + PreFilterResult filter_result = FilterBeforeForwarding(event.event); + if (filter_result != FORWARD_TO_RENDERER) { + client_->OnTouchEventAck(event, + filter_result == ACK_WITH_NO_CONSUMER_EXISTS + ? INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS + : INPUT_EVENT_ACK_STATE_NOT_CONSUMED); return; } // There is no touch event in the queue. Forward it to the renderer // immediately. touch_queue_.push_back(new CoalescedWebTouchEvent(event, false)); - TryForwardNextEventToRenderer(); + ForwardNextEventToRenderer(); return; } @@ -370,7 +405,6 @@ void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result, touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; } - UpdateTouchAckStates(acked_event, ack_result); PopTouchEventToClient(ack_result, latency_info); TryForwardNextEventToRenderer(); } @@ -380,47 +414,102 @@ void TouchEventQueue::TryForwardNextEventToRenderer() { // If there are queued touch events, then try to forward them to the renderer // immediately, or ACK the events back to the client if appropriate. while (!touch_queue_.empty()) { - const TouchEventWithLatencyInfo& touch = - touch_queue_.front()->coalesced_event(); - PreFilterResult result = FilterBeforeForwarding(touch.event); - switch (result) { + PreFilterResult filter_result = + FilterBeforeForwarding(touch_queue_.front()->coalesced_event().event); + switch (filter_result) { case ACK_WITH_NO_CONSUMER_EXISTS: - PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, - LatencyInfo()); + PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); break; case ACK_WITH_NOT_CONSUMED: - PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, - LatencyInfo()); + PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); break; case FORWARD_TO_RENDERER: - ForwardToRenderer(touch); + ForwardNextEventToRenderer(); return; } } } -void TouchEventQueue::ForwardToRenderer( - const TouchEventWithLatencyInfo& touch) { - TRACE_EVENT0("input", "TouchEventQueue::ForwardToRenderer"); +void TouchEventQueue::ForwardNextEventToRenderer() { + TRACE_EVENT0("input", "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(); - absorbing_touch_moves_ = false; + send_touch_events_async_ = false; + 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 + // reduces the risk of jank. However, it's still important that the web + // application be sent touches at key points in the gesture stream, + // e.g., when the application slop region is exceeded or touchmove + // coalescing fails because of different modifiers. + const bool send_touch_move_now = + size() > 1 || + (touch.event.timeStampSeconds >= + last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec) || + (needs_async_touch_move_for_outer_slop_region_ && + OutsideApplicationSlopRegion(touch.event, + touch_sequence_start_position_)) || + (pending_async_touch_move_ && + !pending_async_touch_move_->CanCoalesceWith(touch)); + + if (!send_touch_move_now) { + if (!pending_async_touch_move_) { + pending_async_touch_move_.reset(new TouchEventWithLatencyInfo(touch)); + } else { + DCHECK(pending_async_touch_move_->CanCoalesceWith(touch)); + pending_async_touch_move_->CoalesceWith(touch); + } + PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + return; + } + } + + last_sent_touch_timestamp_sec_ = touch.event.timeStampSeconds; + + // Flush any pending async touch move. If it can be combined with the current + // (touchmove) event, great, otherwise send it immediately but separately. Its + // ack will trigger forwarding of the original |touch| event. + if (pending_async_touch_move_) { + if (pending_async_touch_move_->CanCoalesceWith(touch)) { + pending_async_touch_move_->CoalesceWith(touch); + pending_async_touch_move_->event.cancelable = !send_touch_events_async_; + touch = *pending_async_touch_move_.Pass(); + } else { + scoped_ptr<TouchEventWithLatencyInfo> async_move = + pending_async_touch_move_.Pass(); + async_move->event.cancelable = false; + touch_queue_.push_front(new CoalescedWebTouchEvent(*async_move, true)); + SendTouchEventImmediately(*async_move); + return; + } } + // Note: Marking touchstart events as not-cancelable prevents them from + // blocking subsequent gestures, but it may not be the best long term solution + // for tracking touch point dispatch. + if (send_touch_events_async_) + touch.event.cancelable = false; + // 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); + SendTouchEventImmediately(touch); if (dispatching_touch_ && touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT && - ShouldTouchTypeTriggerTimeout(touch.event.type)) { + ShouldTouchTriggerTimeout(touch.event)) { DCHECK(timeout_handler_); timeout_handler_->Start(touch); } @@ -431,8 +520,12 @@ void TouchEventQueue::OnGestureScrollEvent( if (gesture_event.event.type != blink::WebInputEvent::GestureScrollBegin) return; - if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE) - absorbing_touch_moves_ = true; + if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) { + pending_async_touch_move_.reset(); + send_touch_events_async_ = true; + needs_async_touch_move_for_outer_slop_region_ = true; + return; + } if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_TOUCHCANCEL) return; @@ -463,18 +556,21 @@ void TouchEventQueue::OnGestureScrollEvent( void TouchEventQueue::OnGestureEventAck( const GestureEventWithLatencyInfo& event, InputEventAckState ack_result) { - if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE) + if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) return; if (event.event.type != blink::WebInputEvent::GestureScrollUpdate) return; - // Suspend sending touchmove events as long as the scroll events are handled. + // Throttle sending touchmove events as long as the scroll events are handled. // Note that there's no guarantee that this ACK is for the most recent // gesture event (or even part of the current sequence). Worst case, the - // delay in updating the absorption state should only result in minor UI - // glitches. - absorbing_touch_moves_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED); + // delay in updating the absorption state will result in minor UI glitches. + // A valid |pending_async_touch_move_| will be flushed when the next event is + // forwarded. + send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED); + if (!send_touch_events_async_) + needs_async_touch_move_for_outer_slop_region_ = false; } void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { @@ -492,6 +588,7 @@ void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { // TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch // state tracking (e.g., if the touch handler was removed mid-sequence). touch_filtering_state_ = DROP_ALL_TOUCHES; + pending_async_touch_move_.reset(); if (timeout_handler_) timeout_handler_->Reset(); if (!touch_queue_.empty()) @@ -532,6 +629,10 @@ void TouchEventQueue::SetAckTimeoutEnabled(bool enabled, timeout_handler_->set_timeout_delay(ack_timeout_delay); } +bool TouchEventQueue::HasPendingAsyncTouchMoveForTesting() const { + return pending_async_touch_move_; +} + bool TouchEventQueue::IsTimeoutRunningForTesting() const { return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning(); } @@ -544,37 +645,59 @@ TouchEventQueue::GetLatestEventForTesting() const { void TouchEventQueue::FlushQueue() { DCHECK(!dispatching_touch_ack_); DCHECK(!dispatching_touch_); + pending_async_touch_move_.reset(); if (touch_filtering_state_ != DROP_ALL_TOUCHES) touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; - while (!touch_queue_.empty()) { - PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, - LatencyInfo()); - } + while (!touch_queue_.empty()) + PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); +} + +void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result) { + AckTouchEventToClient(ack_result, PopTouchEvent()); } void TouchEventQueue::PopTouchEventToClient( InputEventAckState ack_result, const LatencyInfo& renderer_latency_info) { - DCHECK(!dispatching_touch_ack_); - if (touch_queue_.empty()) - return; - scoped_ptr<CoalescedWebTouchEvent> acked_event(touch_queue_.front()); - touch_queue_.pop_front(); + scoped_ptr<CoalescedWebTouchEvent> acked_event = PopTouchEvent(); + acked_event->UpdateLatencyInfoForAck(renderer_latency_info); + AckTouchEventToClient(ack_result, acked_event.Pass()); +} - if (acked_event->ignore_ack()) - return; +void TouchEventQueue::AckTouchEventToClient( + InputEventAckState ack_result, + scoped_ptr<CoalescedWebTouchEvent> acked_event) { + DCHECK(acked_event); + DCHECK(!dispatching_touch_ack_); + UpdateTouchAckStates(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. - base::AutoReset<CoalescedWebTouchEvent*> - dispatching_touch_ack(&dispatching_touch_ack_, acked_event.get()); + base::AutoReset<const CoalescedWebTouchEvent*> dispatching_touch_ack( + &dispatching_touch_ack_, acked_event.get()); + acked_event->DispatchAckToClient(ack_result, client_); +} + +scoped_ptr<CoalescedWebTouchEvent> TouchEventQueue::PopTouchEvent() { + DCHECK(!touch_queue_.empty()); + scoped_ptr<CoalescedWebTouchEvent> event(touch_queue_.front()); + touch_queue_.pop_front(); + return event.Pass(); +} - for (WebTouchEventWithLatencyList::iterator iter = acked_event->begin(), - end = acked_event->end(); - iter != end; ++iter) { - iter->latency.AddNewLatencyFrom(renderer_latency_info); - client_->OnTouchEventAck((*iter), ack_result); +void TouchEventQueue::SendTouchEventImmediately( + const TouchEventWithLatencyInfo& touch) { + if (needs_async_touch_move_for_outer_slop_region_) { + // Any event other than a touchmove (e.g., touchcancel or secondary + // touchstart) after a scroll has started will interrupt the need to send a + // an outer slop-region exceeding touchmove. + if (touch.event.type != WebInputEvent::TouchMove || + OutsideApplicationSlopRegion(touch.event, + touch_sequence_start_position_)) + needs_async_touch_move_for_outer_slop_region_ = false; } + + client_->SendTouchEventImmediately(touch); } TouchEventQueue::PreFilterResult @@ -592,12 +715,9 @@ TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { event.type != WebInputEvent::TouchCancel) { if (WebTouchEventTraits::IsTouchSequenceStart(event)) return FORWARD_TO_RENDERER; - return ACK_WITH_NOT_CONSUMED; + return ACK_WITH_NO_CONSUMER_EXISTS; } - if (absorbing_touch_moves_ && event.type == WebInputEvent::TouchMove) - return ACK_WITH_NOT_CONSUMED; - // Touch press events should always be forwarded to the renderer. if (event.type == WebInputEvent::TouchStart) return FORWARD_TO_RENDERER; diff --git a/content/browser/renderer_host/input/touch_event_queue.h b/content/browser/renderer_host/input/touch_event_queue.h index 03d3210..5d2edc2 100644 --- a/content/browser/renderer_host/input/touch_event_queue.h +++ b/content/browser/renderer_host/input/touch_event_queue.h @@ -48,12 +48,11 @@ class CONTENT_EXPORT TouchEventQueue { // using the disposition to determine whether a scroll update should be // sent. Mobile Safari's default overflow scroll behavior. TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE, - // Like sync, except that consumed scroll events cause subsequent touchmove - // events to be suppressed. Unconsumed scroll events return touchmove - // events to being dispatched synchronously (so scrolling may be hijacked - // when a scroll limit is reached, and later resumed). http://goo.gl/RShsdN - TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE, - TOUCH_SCROLLING_MODE_DEFAULT = TOUCH_SCROLLING_MODE_TOUCHCANCEL + // Send touchmove events throughout a scroll, but throttle sending and + // ignore the ACK as long as scrolling remains possible. Unconsumed scroll + // events return touchmove events to being dispatched synchronously. + TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE, + TOUCH_SCROLLING_MODE_DEFAULT = TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE }; // The |client| must outlive the TouchEventQueue. If @@ -120,7 +119,7 @@ class CONTENT_EXPORT TouchEventQueue { friend class TouchTimeoutHandler; friend class TouchEventQueueTest; - bool HasTimeoutEvent() const; + bool HasPendingAsyncTouchMoveForTesting() const; bool IsTimeoutRunningForTesting() const; const TouchEventWithLatencyInfo& GetLatestEventForTesting() const; @@ -128,16 +127,32 @@ class CONTENT_EXPORT TouchEventQueue { // events being sent to the renderer. void FlushQueue(); - // Walks the queue, checking each event for |ShouldForwardToRenderer()|. - // If true, forwards the touch event and stops processing further events. - // If false, acks the event with |INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS|. + // Walks the queue, checking each event with |FilterBeforeForwarding()|. + // If allowed, forwards the touch event and stops processing further events. + // Otherwise, acks the event with |INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS|. void TryForwardNextEventToRenderer(); - // Pops the touch-event from the top of the queue and sends it to the - // TouchEventQueueClient. This reduces the size of the queue by one. + // Forwards the event at the head of the queue to the renderer. + void ForwardNextEventToRenderer(); + + // Pops the touch-event from the head of the queue and acks it to the client. + void PopTouchEventToClient(InputEventAckState ack_result); + + // Pops the touch-event from the top of the queue and acks it to the client, + // updating the event with |renderer_latency_info|. void PopTouchEventToClient(InputEventAckState ack_result, const ui::LatencyInfo& renderer_latency_info); + // Ack all coalesced events in |acked_event| to the client with |ack_result|. + void AckTouchEventToClient(InputEventAckState ack_result, + scoped_ptr<CoalescedWebTouchEvent> acked_event); + + // Safely pop the head of the queue. + scoped_ptr<CoalescedWebTouchEvent> PopTouchEvent(); + + // Dispatch |touch| to the client. + void SendTouchEventImmediately(const TouchEventWithLatencyInfo& touch); + enum PreFilterResult { ACK_WITH_NO_CONSUMER_EXISTS, ACK_WITH_NOT_CONSUMED, @@ -161,10 +176,14 @@ class CONTENT_EXPORT TouchEventQueue { typedef std::map<int, InputEventAckState> TouchPointAckStates; TouchPointAckStates touch_ack_states_; + // Position of the first touch in the most recent sequence forwarded to the + // client. + gfx::PointF touch_sequence_start_position_; + // Used to defer touch forwarding when ack dispatch triggers |QueueEvent()|. // If not NULL, |dispatching_touch_ack_| is the touch event of which the ack // is being dispatched. - CoalescedWebTouchEvent* dispatching_touch_ack_; + const CoalescedWebTouchEvent* dispatching_touch_ack_; // Used to prevent touch timeout scheduling if we receive a synchronous // ack after forwarding a touch event to the client. @@ -188,12 +207,15 @@ class CONTENT_EXPORT TouchEventQueue { // been preventDefaulted. scoped_ptr<TouchMoveSlopSuppressor> touchmove_slop_suppressor_; - // Whether touchmove events should be dropped due to the - // TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE mode. Note that we can't use - // touch_filtering_state_ for this (without adding a few new states and - // complicating the code significantly) because it can occur with and without - // timeout, and shouldn't cause touchend to be dropped. - bool absorbing_touch_moves_; + // Whether touch events should remain buffered and dispatched asynchronously + // while a scroll sequence is active. In this mode, touchmove's are throttled + // and ack'ed immediately, but remain buffered in |pending_async_touch_move_| + // until a sufficient time period has elapsed since the last sent touch event. + // For details see the design doc at http://goo.gl/lVyJAa. + bool send_touch_events_async_; + scoped_ptr<TouchEventWithLatencyInfo> pending_async_touch_move_; + double last_sent_touch_timestamp_sec_; + bool needs_async_touch_move_for_outer_slop_region_; // How touch events are handled during scrolling. For now this is a global // setting for experimentation, but we may evolve it into an app-controlled diff --git a/content/browser/renderer_host/input/touch_event_queue_unittest.cc b/content/browser/renderer_host/input/touch_event_queue_unittest.cc index 99fd921..8c6d68fc 100644 --- a/content/browser/renderer_host/input/touch_event_queue_unittest.cc +++ b/content/browser/renderer_host/input/touch_event_queue_unittest.cc @@ -19,6 +19,9 @@ using blink::WebTouchPoint; namespace content { namespace { + +const double kMinSecondsBetweenThrottledTouchmoves = 0.2; + base::TimeDelta DefaultTouchTimeoutDelay() { return base::TimeDelta::FromMilliseconds(1); } @@ -156,6 +159,10 @@ class TouchEventQueueTest : public testing::Test, SendTouchEvent(); } + void AdvanceTouchTime(double seconds) { + touch_event_.timeStampSeconds += seconds; + } + size_t GetAndResetAckedEventCount() { size_t count = acked_event_count_; acked_event_count_ = 0; @@ -180,12 +187,12 @@ class TouchEventQueueTest : public testing::Test, queue_->SetAckTimeoutEnabled(false, base::TimeDelta()); } - bool IsTimeoutEnabled() { - return queue_->ack_timeout_enabled(); - } + bool IsTimeoutEnabled() const { return queue_->ack_timeout_enabled(); } - bool IsTimeoutRunning() { - return queue_->IsTimeoutRunningForTesting(); + bool IsTimeoutRunning() const { return queue_->IsTimeoutRunningForTesting(); } + + bool HasPendingAsyncTouchMove() const { + return queue_->HasPendingAsyncTouchMoveForTesting(); } size_t queued_event_count() const { @@ -1537,8 +1544,8 @@ TEST_F(TouchEventQueueTest, SyncTouchMoveDoesntCancelTouchOnScroll) { EXPECT_EQ(0U, queued_event_count()); } -TEST_F(TouchEventQueueTest, TouchAbsorption) { - SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE); +TEST_F(TouchEventQueueTest, AsyncTouch) { + SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE); // Queue a TouchStart. PressTouchPoint(0, 1); @@ -1550,24 +1557,28 @@ TEST_F(TouchEventQueueTest, TouchAbsorption) { SendGestureEventAck(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - MoveTouchPoint(0, 20, 5); + MoveTouchPoint(0, 10, 5); SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_FALSE(HasPendingAsyncTouchMove()); + EXPECT_TRUE(sent_event().cancelable); EXPECT_EQ(0U, queued_event_count()); EXPECT_EQ(1U, GetAndResetSentEventCount()); - // Consuming a scroll event prevents the next touch moves from being - // dispatched. + // Consuming a scroll event will throttle subsequent touchmoves. SendGestureEventAck(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); - MoveTouchPoint(0, 20, 5); + MoveTouchPoint(0, 10, 5); SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_TRUE(HasPendingAsyncTouchMove()); EXPECT_EQ(0U, queued_event_count()); EXPECT_EQ(0U, GetAndResetSentEventCount()); } } -TEST_F(TouchEventQueueTest, TouchAbsorptionNoTouchAfterScroll) { - SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE); +// Ensure that touchmove's are appropriately throttled during a typical +// scroll sequences that transitions between scrolls consumed and unconsumed. +TEST_F(TouchEventQueueTest, AsyncTouchThrottledAfterScroll) { + SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE); // Process a TouchStart PressTouchPoint(0, 1); @@ -1585,30 +1596,45 @@ TEST_F(TouchEventQueueTest, TouchAbsorptionNoTouchAfterScroll) { EXPECT_EQ(1U, GetAndResetSentEventCount()); EXPECT_EQ(1U, GetAndResetAckedEventCount()); - // Now queue a second touchmove and verify it's not dispatched. + // Now queue a second touchmove and verify it's not (yet) dispatched. MoveTouchPoint(0, 0, 10); SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_TRUE(HasPendingAsyncTouchMove()); EXPECT_EQ(0U, queued_event_count()); EXPECT_EQ(0U, GetAndResetSentEventCount()); EXPECT_EQ(1U, GetAndResetAckedEventCount()); - // But a final touchend is sent (even before any gesture events - // have been ACKed). + // Queuing the final touchend should flush the pending, async touchmove. ReleaseTouchPoint(0); + EXPECT_EQ(WebInputEvent::TouchMove, sent_event().type); + EXPECT_FALSE(HasPendingAsyncTouchMove()); + EXPECT_FALSE(sent_event().cancelable); + EXPECT_EQ(1U, GetAndResetSentEventCount()); + EXPECT_EQ(2U, queued_event_count()); + + // Ack the flushed, async touchmove. The ack should not reach the client, but + // it should trigger sending of the (now non-cancelable) touchend. SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - EXPECT_EQ(0U, queued_event_count()); + EXPECT_EQ(WebInputEvent::TouchEnd, sent_event().type); + EXPECT_FALSE(sent_event().cancelable); EXPECT_EQ(1U, GetAndResetSentEventCount()); + EXPECT_EQ(0U, GetAndResetAckedEventCount()); + EXPECT_EQ(1U, queued_event_count()); + + // Ack the touchend. + SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(0U, queued_event_count()); + EXPECT_EQ(0U, GetAndResetSentEventCount()); EXPECT_EQ(1U, GetAndResetAckedEventCount()); - EXPECT_EQ(WebInputEvent::TouchEnd, sent_event().type); // Now mark the scroll as not consumed (which would cause future // touchmoves in the active sequence to be sent if there was one). SendGestureEventAck(WebInputEvent::GestureScrollBegin, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - // Start a new touch sequence and verify that absorption has been - // reset so that moves after the start of scrolling are not sent. - PressTouchPoint(0, 1); + // Start a new touch sequence and verify that throttling has been reset. + // Touch moves after the start of scrolling will again be throttled. + PressTouchPoint(0, 0); EXPECT_EQ(1U, GetAndResetSentEventCount()); SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(1U, GetAndResetAckedEventCount()); @@ -1619,8 +1645,195 @@ TEST_F(TouchEventQueueTest, TouchAbsorptionNoTouchAfterScroll) { EXPECT_EQ(1U, GetAndResetAckedEventCount()); MoveTouchPoint(0, 0, 10); SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_TRUE(HasPendingAsyncTouchMove()); + EXPECT_EQ(0U, queued_event_count()); + EXPECT_EQ(0U, GetAndResetSentEventCount()); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + + // As soon as a touchmove exceeds the outer slop region it will be forwarded + // immediately. + MoveTouchPoint(0, 0, 20); + SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_FALSE(HasPendingAsyncTouchMove()); + EXPECT_FALSE(sent_event().cancelable); + EXPECT_EQ(0U, queued_event_count()); + EXPECT_EQ(1U, GetAndResetSentEventCount()); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + + // Subsequent touchmove's should be deferred. + MoveTouchPoint(0, 0, 25); + EXPECT_TRUE(HasPendingAsyncTouchMove()); + EXPECT_EQ(0U, queued_event_count()); + EXPECT_EQ(0U, GetAndResetSentEventCount()); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + + // The pending touchmove should be flushed with the the new touchmove if + // sufficient time has passed. + AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1); + MoveTouchPoint(0, 0, 15); + EXPECT_FALSE(HasPendingAsyncTouchMove()); + EXPECT_FALSE(sent_event().cancelable); + EXPECT_EQ(1U, queued_event_count()); + EXPECT_EQ(1U, GetAndResetSentEventCount()); + EXPECT_EQ(0U, GetAndResetAckedEventCount()); + SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(0U, queued_event_count()); + EXPECT_EQ(0U, GetAndResetSentEventCount()); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + + // Non-touchmove events should always flush any pending touchmove events. + MoveTouchPoint(0, 0, 25); + EXPECT_TRUE(HasPendingAsyncTouchMove()); + EXPECT_EQ(0U, queued_event_count()); + EXPECT_EQ(0U, GetAndResetSentEventCount()); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + PressTouchPoint(30, 30); + EXPECT_FALSE(HasPendingAsyncTouchMove()); + EXPECT_FALSE(sent_event().cancelable); + EXPECT_EQ(WebInputEvent::TouchMove, sent_event().type); + EXPECT_EQ(1U, GetAndResetSentEventCount()); + EXPECT_EQ(2U, queued_event_count()); + + // Ack'ing the flushed, async touchmove will dispatch the touchstart. Note + // that the flushed touchmove's ack will not reach the client (its + // constituent events have already been ack'ed). + SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_FALSE(sent_event().cancelable); + EXPECT_EQ(WebInputEvent::TouchStart, sent_event().type); + EXPECT_EQ(1U, queued_event_count()); + EXPECT_EQ(1U, GetAndResetSentEventCount()); + EXPECT_EQ(0U, GetAndResetAckedEventCount()); + + // Ack the touchstart. + SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(0U, queued_event_count()); + EXPECT_EQ(0U, GetAndResetSentEventCount()); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + + // Send a secondary touchmove. + MoveTouchPoint(1, 0, 25); + EXPECT_TRUE(HasPendingAsyncTouchMove()); EXPECT_EQ(0U, queued_event_count()); EXPECT_EQ(0U, GetAndResetSentEventCount()); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + + // An unconsumed scroll should resume synchronous touch handling. + SendGestureEventAck(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + // The pending touchmove should be coalesced with the next (now synchronous) + // touchmove. + MoveTouchPoint(0, 0, 25); + EXPECT_TRUE(sent_event().cancelable); + EXPECT_FALSE(HasPendingAsyncTouchMove()); + EXPECT_EQ(WebInputEvent::TouchMove, sent_event().type); + EXPECT_EQ(WebTouchPoint::StateMoved, sent_event().touches[0].state); + EXPECT_EQ(WebTouchPoint::StateMoved, sent_event().touches[1].state); + EXPECT_EQ(1U, queued_event_count()); + EXPECT_EQ(1U, GetAndResetSentEventCount()); + EXPECT_EQ(0U, GetAndResetAckedEventCount()); + + // Subsequent touches will queue until the preceding, synchronous touches are + // ack'ed. + ReleaseTouchPoint(1); + EXPECT_EQ(2U, queued_event_count()); + ReleaseTouchPoint(0); + EXPECT_EQ(3U, queued_event_count()); + SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_TRUE(sent_event().cancelable); + EXPECT_EQ(WebInputEvent::TouchEnd, sent_event().type); + EXPECT_EQ(2U, queued_event_count()); + EXPECT_EQ(1U, GetAndResetSentEventCount()); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + + SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_TRUE(sent_event().cancelable); + EXPECT_EQ(WebInputEvent::TouchEnd, sent_event().type); + EXPECT_EQ(1U, queued_event_count()); + EXPECT_EQ(1U, GetAndResetSentEventCount()); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + + SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(0U, queued_event_count()); + EXPECT_EQ(0U, GetAndResetSentEventCount()); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); +} + +// Ensure that async touch dispatch and touch ack timeout interactions work +// appropriately. +TEST_F(TouchEventQueueTest, AsyncTouchWithAckTimeout) { + SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE); + SetUpForTimeoutTesting(DefaultTouchTimeoutDelay()); + + // The touchstart should start the timeout. + PressTouchPoint(0, 0); + EXPECT_EQ(1U, GetAndResetSentEventCount()); + EXPECT_TRUE(IsTimeoutRunning()); + + // The start of a scroll gesture should triger async touch event dispatch. + WebGestureEvent followup_scroll; + followup_scroll.type = WebInputEvent::GestureScrollBegin; + SetFollowupEvent(followup_scroll); + SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_FALSE(IsTimeoutRunning()); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + + SendGestureEventAck(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_CONSUMED); + + // An async touch should fire after the throttling interval has expired, but + // it should not start the touch ack timeout. + MoveTouchPoint(0, 5, 5); + EXPECT_TRUE(HasPendingAsyncTouchMove()); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + + AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1); + MoveTouchPoint(0, 5, 5); + EXPECT_FALSE(IsTimeoutRunning()); + EXPECT_FALSE(HasPendingAsyncTouchMove()); + EXPECT_FALSE(sent_event().cancelable); + SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + EXPECT_EQ(1U, GetAndResetSentEventCount()); + + // An unconsumed scroll event will resume synchronous touchmoves, which are + // subject to the ack timeout. + SendGestureEventAck(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + MoveTouchPoint(0, 20, 5); + EXPECT_TRUE(IsTimeoutRunning()); + EXPECT_TRUE(sent_event().cancelable); + EXPECT_EQ(1U, GetAndResetSentEventCount()); + + // The timeout should fire, disabling touch forwarding until both acks are + // received and acking the timed out event. + RunTasksAndWait(DefaultTouchTimeoutDelay() * 2); + EXPECT_FALSE(IsTimeoutRunning()); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + EXPECT_EQ(0U, GetAndResetSentEventCount()); + + // Ack'ing the original event should trigger a cancel event. + SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_FALSE(sent_event().cancelable); + EXPECT_EQ(0U, GetAndResetAckedEventCount()); + EXPECT_EQ(1U, GetAndResetSentEventCount()); + + // Subsequent touchmove's should not be forwarded, even as the scroll gesture + // goes from unconsumed to consumed. + SendGestureEventAck(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + MoveTouchPoint(0, 20, 5); + EXPECT_FALSE(HasPendingAsyncTouchMove()); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + EXPECT_EQ(0U, GetAndResetSentEventCount()); + + SendGestureEventAck(WebInputEvent::GestureScrollUpdate, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + MoveTouchPoint(0, 25, 5); + EXPECT_FALSE(HasPendingAsyncTouchMove()); + EXPECT_EQ(1U, GetAndResetAckedEventCount()); + EXPECT_EQ(0U, GetAndResetSentEventCount()); } } // namespace content diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc index 1253c7e..d02fede 100644 --- a/content/browser/renderer_host/render_widget_host_unittest.cc +++ b/content/browser/renderer_host/render_widget_host_unittest.cc @@ -1547,8 +1547,6 @@ TEST_F(RenderWidgetHostTest, ReverseFlingCancelsOverscroll) { SimulateGestureEvent(WebInputEvent::GestureScrollBegin, WebGestureEvent::Touchscreen); SimulateGestureScrollUpdateEvent(300, -5, 0); - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode()); @@ -1560,8 +1558,6 @@ TEST_F(RenderWidgetHostTest, ReverseFlingCancelsOverscroll) { EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_delegate()->completed_mode()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode()); EXPECT_EQ(1U, process_->sink().message_count()); - SendInputEventACK(WebInputEvent::GestureScrollEnd, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); } { @@ -1573,8 +1569,6 @@ TEST_F(RenderWidgetHostTest, ReverseFlingCancelsOverscroll) { SimulateGestureEvent(WebInputEvent::GestureScrollBegin, WebGestureEvent::Touchscreen); SimulateGestureScrollUpdateEvent(-300, -5, 0); - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(OVERSCROLL_WEST, host_->overscroll_mode()); @@ -1599,8 +1593,6 @@ TEST_F(RenderWidgetHostTest, GestureScrollOverscrolls) { SimulateGestureEvent(WebInputEvent::GestureScrollBegin, WebGestureEvent::Touchscreen); - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode()); @@ -1658,8 +1650,6 @@ TEST_F(RenderWidgetHostTest, GestureScrollConsumedHorizontal) { SimulateGestureScrollUpdateEvent(10, 0, 0); // Start scrolling on content. ACK both events as being processed. - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode()); @@ -1689,8 +1679,6 @@ TEST_F(RenderWidgetHostTest, GestureScrollDebounceOverscrolls) { WebGestureEvent::Touchscreen); EXPECT_EQ(1U, process_->sink().message_count()); process_->sink().ClearMessages(); - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); // Send update events. SimulateGestureScrollUpdateEvent(25, 0, 0); @@ -1769,8 +1757,6 @@ TEST_F(RenderWidgetHostTest, GestureScrollDebounceTimerOverscroll) { WebGestureEvent::Touchscreen); EXPECT_EQ(1U, process_->sink().message_count()); process_->sink().ClearMessages(); - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); // Send update events. SimulateGestureScrollUpdateEvent(55, 0, 0); @@ -1851,8 +1837,6 @@ TEST_F(RenderWidgetHostTest, OverscrollWithTouchEvents) { SimulateGestureEvent(WebInputEvent::GestureScrollBegin, WebGestureEvent::Touchscreen); SimulateGestureScrollUpdateEvent(20, 0, 0); - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode()); @@ -1967,8 +1951,6 @@ TEST_F(RenderWidgetHostTest, TouchGestureEndDispatchedAfterOverscrollComplete) { // router. EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize()); process_->sink().ClearMessages(); - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize()); EXPECT_EQ(0U, process_->sink().message_count()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode()); @@ -2014,12 +1996,7 @@ TEST_F(RenderWidgetHostTest, TouchGestureEndDispatchedAfterOverscrollComplete) { // router. EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize()); EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize()); - - SendInputEventACK(blink::WebInputEvent::GestureScrollEnd, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(0U, process_->sink().message_count()); - EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize()); - EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize()); // Start scrolling. Receive ACK as it being processed. SimulateGestureEvent(WebInputEvent::GestureScrollBegin, @@ -2029,8 +2006,6 @@ TEST_F(RenderWidgetHostTest, TouchGestureEndDispatchedAfterOverscrollComplete) { // router. EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize()); process_->sink().ClearMessages(); - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize()); EXPECT_EQ(0U, process_->sink().message_count()); EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_mode()); @@ -2077,12 +2052,7 @@ TEST_F(RenderWidgetHostTest, TouchGestureEndDispatchedAfterOverscrollComplete) { // router. EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize()); EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize()); - - SendInputEventACK(blink::WebInputEvent::GestureScrollEnd, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(0U, process_->sink().message_count()); - EXPECT_EQ(0U, host_->GestureEventLastQueueEventSize()); - EXPECT_EQ(0U, host_->GestureEventDebouncingQueueSize()); } TEST_F(RenderWidgetHostTest, OverscrollDirectionChange) { @@ -2095,8 +2065,6 @@ TEST_F(RenderWidgetHostTest, OverscrollDirectionChange) { WebGestureEvent::Touchscreen); EXPECT_EQ(1U, process_->sink().message_count()); process_->sink().ClearMessages(); - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); // Send update events and receive ack as not consumed. SimulateGestureScrollUpdateEvent(125, -5, 0); @@ -2183,8 +2151,6 @@ TEST_F(RenderWidgetHostTest, OverscrollMouseMoveCompletion) { SimulateGestureEvent(WebInputEvent::GestureScrollBegin, WebGestureEvent::Touchscreen); SimulateGestureScrollUpdateEvent(300, -5, 0); - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode()); @@ -2208,8 +2174,6 @@ TEST_F(RenderWidgetHostTest, OverscrollMouseMoveCompletion) { EXPECT_EQ(OVERSCROLL_NONE, host_->overscroll_delegate()->current_mode()); EXPECT_EQ(1U, process_->sink().message_count()); process_->sink().ClearMessages(); - SendInputEventACK(WebInputEvent::GestureScrollEnd, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); // Move mouse some more. The mouse-move events should reach the renderer. SimulateMouseMove(5, 10, 0); @@ -2295,8 +2259,6 @@ TEST_F(RenderWidgetHostTest, OverscrollResetsOnBlur) { SimulateGestureEvent(WebInputEvent::GestureScrollBegin, WebGestureEvent::Touchscreen); SimulateGestureScrollUpdateEvent(300, -5, 0); - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(OVERSCROLL_EAST, host_->overscroll_mode()); @@ -2368,8 +2330,6 @@ TEST_F(RenderWidgetHostTest, TouchEmulator) { SimulateMouseEvent(WebInputEvent::MouseDown, 10, 10, 0, true); EXPECT_EQ(WebInputEvent::TouchStart, host_->acked_touch_event_type()); EXPECT_EQ("GestureTapDown", GetInputMessageTypes(process_)); - SendInputEventACK(WebInputEvent::GestureTapDown, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); // Mouse drag generates touch move, cancels tap and starts scroll. SimulateMouseEvent(WebInputEvent::MouseMove, 10, 30, 0, true); @@ -2377,10 +2337,6 @@ TEST_F(RenderWidgetHostTest, TouchEmulator) { EXPECT_EQ( "GestureTapCancel GestureScrollBegin GestureScrollUpdate", GetInputMessageTypes(process_)); - SendInputEventACK(WebInputEvent::GestureTapCancel, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, process_->sink().message_count()); @@ -2391,8 +2347,6 @@ TEST_F(RenderWidgetHostTest, TouchEmulator) { EXPECT_EQ(WebInputEvent::TouchMove, host_->acked_touch_event_type()); EXPECT_EQ("GesturePinchBegin", GetInputMessageTypes(process_)); - SendInputEventACK(WebInputEvent::GesturePinchBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, process_->sink().message_count()); SimulateMouseEvent( @@ -2424,8 +2378,6 @@ TEST_F(RenderWidgetHostTest, TouchEmulator) { SimulateMouseEvent(WebInputEvent::MouseUp, 10, 70, 0, true); EXPECT_EQ(WebInputEvent::TouchEnd, host_->acked_touch_event_type()); EXPECT_EQ("GestureScrollEnd", GetInputMessageTypes(process_)); - SendInputEventACK(WebInputEvent::GestureScrollEnd, - INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, process_->sink().message_count()); // Mouse move does nothing. @@ -2436,8 +2388,6 @@ TEST_F(RenderWidgetHostTest, TouchEmulator) { SimulateMouseEvent(WebInputEvent::MouseDown, 10, 80, 0, true); EXPECT_EQ(WebInputEvent::TouchStart, host_->acked_touch_event_type()); EXPECT_EQ("GestureTapDown", GetInputMessageTypes(process_)); - SendInputEventACK(WebInputEvent::GestureTapDown, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(0U, process_->sink().message_count()); SimulateMouseEvent(WebInputEvent::MouseMove, 10, 100, 0, true); @@ -2445,10 +2395,6 @@ TEST_F(RenderWidgetHostTest, TouchEmulator) { EXPECT_EQ( "GestureTapCancel GestureScrollBegin GestureScrollUpdate", GetInputMessageTypes(process_)); - SendInputEventACK(WebInputEvent::GestureTapCancel, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, process_->sink().message_count()); @@ -2459,8 +2405,6 @@ TEST_F(RenderWidgetHostTest, TouchEmulator) { EXPECT_EQ(WebInputEvent::TouchMove, host_->acked_touch_event_type()); EXPECT_EQ("GesturePinchBegin", GetInputMessageTypes(process_)); - SendInputEventACK(WebInputEvent::GesturePinchBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, process_->sink().message_count()); SimulateMouseEvent( @@ -2478,10 +2422,6 @@ TEST_F(RenderWidgetHostTest, TouchEmulator) { EXPECT_EQ(WebInputEvent::TouchCancel, host_->acked_touch_event_type()); EXPECT_EQ("GesturePinchEnd GestureScrollEnd", GetInputMessageTypes(process_)); - SendInputEventACK(WebInputEvent::GesturePinchEnd, - INPUT_EVENT_ACK_STATE_CONSUMED); - SendInputEventACK(WebInputEvent::GestureScrollEnd, - INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, process_->sink().message_count()); // Mouse event should pass untouched. @@ -2501,8 +2441,6 @@ TEST_F(RenderWidgetHostTest, TouchEmulator) { SimulateMouseEvent(WebInputEvent::MouseDown, 10, 10, 0, true); EXPECT_EQ(WebInputEvent::TouchStart, host_->acked_touch_event_type()); EXPECT_EQ("GestureTapDown", GetInputMessageTypes(process_)); - SendInputEventACK(WebInputEvent::GestureTapDown, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); EXPECT_EQ(0U, process_->sink().message_count()); // Scroll. @@ -2511,10 +2449,6 @@ TEST_F(RenderWidgetHostTest, TouchEmulator) { EXPECT_EQ( "GestureTapCancel GestureScrollBegin GestureScrollUpdate", GetInputMessageTypes(process_)); - SendInputEventACK(WebInputEvent::GestureTapCancel, - INPUT_EVENT_ACK_STATE_NOT_CONSUMED); - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); SendInputEventACK(WebInputEvent::GestureScrollUpdate, INPUT_EVENT_ACK_STATE_CONSUMED); @@ -2524,8 +2458,6 @@ TEST_F(RenderWidgetHostTest, TouchEmulator) { EXPECT_EQ(WebInputEvent::TouchCancel, host_->acked_touch_event_type()); EXPECT_EQ("GestureScrollEnd", GetInputMessageTypes(process_)); - SendInputEventACK(WebInputEvent::GestureScrollEnd, - INPUT_EVENT_ACK_STATE_CONSUMED); EXPECT_EQ(0U, process_->sink().message_count()); } @@ -2748,8 +2680,6 @@ TEST_F(RenderWidgetHostTest, InputEventRWHLatencyComponent) { WebGestureEvent::Touchscreen); CheckLatencyInfoComponentInMessage( process_, GetLatencyComponentId(), WebInputEvent::GestureScrollBegin); - SendInputEventACK(WebInputEvent::GestureScrollBegin, - INPUT_EVENT_ACK_STATE_CONSUMED); // Tests RWHI::ForwardGestureEventWithLatencyInfo(). SimulateGestureEventWithLatencyInfo(WebInputEvent::GestureScrollUpdate, diff --git a/content/common/input/web_input_event_traits.cc b/content/common/input/web_input_event_traits.cc index 7de816a..df4513e 100644 --- a/content/common/input/web_input_event_traits.cc +++ b/content/common/input/web_input_event_traits.cc @@ -334,28 +334,31 @@ void WebInputEventTraits::Coalesce(const WebInputEvent& event_to_coalesce, Apply(WebInputEventCoalesce(), event->type, event_to_coalesce, event); } -bool WebInputEventTraits::IgnoresAckDisposition( - blink::WebInputEvent::Type type) { - switch (type) { - case WebInputEvent::GestureTapDown: - case WebInputEvent::GestureShowPress: - case WebInputEvent::GestureTapCancel: - case WebInputEvent::GestureTap: - case WebInputEvent::GesturePinchBegin: - case WebInputEvent::GesturePinchEnd: - case WebInputEvent::GestureScrollBegin: - case WebInputEvent::GestureScrollEnd: - case WebInputEvent::TouchCancel: +bool WebInputEventTraits::IgnoresAckDisposition(const WebInputEvent& event) { + switch (event.type) { case WebInputEvent::MouseDown: case WebInputEvent::MouseUp: case WebInputEvent::MouseEnter: case WebInputEvent::MouseLeave: case WebInputEvent::ContextMenu: + case WebInputEvent::GestureScrollBegin: + case WebInputEvent::GestureScrollEnd: + case WebInputEvent::GestureShowPress: + case WebInputEvent::GestureTap: + case WebInputEvent::GestureTapUnconfirmed: + case WebInputEvent::GestureTapDown: + case WebInputEvent::GestureTapCancel: + case WebInputEvent::GesturePinchBegin: + case WebInputEvent::GesturePinchEnd: + case WebInputEvent::TouchCancel: return true; + case WebInputEvent::TouchStart: + case WebInputEvent::TouchMove: + case WebInputEvent::TouchEnd: + return !static_cast<const WebTouchEvent&>(event).cancelable; default: - break; + return false; } - return false; } } // namespace content diff --git a/content/common/input/web_input_event_traits.h b/content/common/input/web_input_event_traits.h index 9751fc0..47cd69f 100644 --- a/content/common/input/web_input_event_traits.h +++ b/content/common/input/web_input_event_traits.h @@ -22,7 +22,7 @@ class CONTENT_EXPORT WebInputEventTraits { const blink::WebInputEvent& event); static void Coalesce(const blink::WebInputEvent& event_to_coalesce, blink::WebInputEvent* event); - static bool IgnoresAckDisposition(blink::WebInputEvent::Type type); + static bool IgnoresAckDisposition(const blink::WebInputEvent& event); }; } // namespace content diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc index 1e1cb6d..c4ad822 100644 --- a/content/public/common/content_switches.cc +++ b/content/public/common/content_switches.cc @@ -850,9 +850,9 @@ const char kTestType[] = "test-type"; const char kTouchAckTimeoutDelayMs[] = "touch-ack-timeout-delay-ms"; const char kTouchScrollingMode[] = "touch-scrolling-mode"; -const char kTouchScrollingModeTouchcancel[] = "touchcancel"; +const char kTouchScrollingModeAsyncTouchmove[] = "async-touchmove"; const char kTouchScrollingModeSyncTouchmove[] = "sync-touchmove"; -const char kTouchScrollingModeAbsorbTouchmove[] = "absorb-touchmove"; +const char kTouchScrollingModeTouchcancel[] = "touchcancel"; // Causes TRACE_EVENT flags to be recorded beginning with shutdown. Optionally, // can specify the specific trace categories to include (e.g. diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h index 2dceb67..dfea402 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h @@ -240,7 +240,7 @@ CONTENT_EXPORT extern const char kTestSandbox[]; CONTENT_EXPORT extern const char kTestType[]; CONTENT_EXPORT extern const char kTouchAckTimeoutDelayMs[]; CONTENT_EXPORT extern const char kTouchScrollingMode[]; -CONTENT_EXPORT extern const char kTouchScrollingModeAbsorbTouchmove[]; +CONTENT_EXPORT extern const char kTouchScrollingModeAsyncTouchmove[]; CONTENT_EXPORT extern const char kTouchScrollingModeSyncTouchmove[]; CONTENT_EXPORT extern const char kTouchScrollingModeTouchcancel[]; CONTENT_EXPORT extern const char kTraceShutdown[]; diff --git a/content/renderer/input/input_event_filter.cc b/content/renderer/input/input_event_filter.cc index b59b8d4..1738078 100644 --- a/content/renderer/input/input_event_filter.cc +++ b/content/renderer/input/input_event_filter.cc @@ -173,7 +173,7 @@ void InputEventFilter::ForwardToHandler(const IPC::Message& message) { return; } - if (!WebInputEventTraits::IgnoresAckDisposition(event->type)) + if (!WebInputEventTraits::IgnoresAckDisposition(*event)) SendACK(event->type, ack, latency_info, routing_id); } diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index fd2f5d5..0c4f68e 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc @@ -1077,7 +1077,7 @@ void RenderWidget::OnHandleInputEvent(const blink::WebInputEvent* input_event, kInputHandlingTimeThrottlingThresholdMicroseconds; } - if (!WebInputEventTraits::IgnoresAckDisposition(input_event->type)) { + if (!WebInputEventTraits::IgnoresAckDisposition(*input_event)) { scoped_ptr<IPC::Message> response( new InputHostMsg_HandleInputEvent_ACK(routing_id_, input_event->type, |