summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjdduke@chromium.org <jdduke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-28 07:24:09 +0000
committerjdduke@chromium.org <jdduke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-28 07:24:09 +0000
commita1710ba470bc04d9bc07ae8c6f0ecbd50d5bd077 (patch)
treeacfe58f114c234609850a7c00461a0f12dcd8714
parentb20f6ac13aa30e15d7c947a4bf8877260387edab (diff)
downloadchromium_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.grd6
-rw-r--r--chrome/browser/about_flags.cc4
-rw-r--r--content/browser/renderer_host/input/input_router_impl.cc24
-rw-r--r--content/browser/renderer_host/input/input_router_impl_unittest.cc98
-rw-r--r--content/browser/renderer_host/input/touch_event_queue.cc286
-rw-r--r--content/browser/renderer_host/input/touch_event_queue.h60
-rw-r--r--content/browser/renderer_host/input/touch_event_queue_unittest.cc255
-rw-r--r--content/browser/renderer_host/render_widget_host_unittest.cc70
-rw-r--r--content/common/input/web_input_event_traits.cc31
-rw-r--r--content/common/input/web_input_event_traits.h2
-rw-r--r--content/public/common/content_switches.cc4
-rw-r--r--content/public/common/content_switches.h2
-rw-r--r--content/renderer/input/input_event_filter.cc2
-rw-r--r--content/renderer/render_widget.cc2
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, &param);
@@ -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,