Commit a1710ba4 authored by jdduke@chromium.org's avatar jdduke@chromium.org

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
parent b20f6ac1
...@@ -6693,13 +6693,13 @@ Keep your key file in a safe place. You will need it to create new versions of y ...@@ -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. Touch scrolling mode.
</message> </message>
<message name="IDS_FLAGS_TOUCH_SCROLLING_MODE_DESCRIPTION" desc="Description for the flag to control touch scrolling mode."> <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>
<message name="IDS_FLAGS_TOUCH_SCROLLING_MODE_TOUCHCANCEL" desc="Name for the touchcancel scrolling mode"> <message name="IDS_FLAGS_TOUCH_SCROLLING_MODE_TOUCHCANCEL" desc="Name for the touchcancel scrolling mode">
touchcancel touchcancel
</message> </message>
<message name="IDS_FLAGS_TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE" desc="Name for the absorb-touchmove scrolling mode"> <message name="IDS_FLAGS_TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE" desc="Name for the async-touchmove scrolling mode">
absorb-touchmove async-touchmove
</message> </message>
<message name="IDS_FLAGS_TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE" desc="Name for the sync-touchmove scrolling mode"> <message name="IDS_FLAGS_TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE" desc="Name for the sync-touchmove scrolling mode">
sync-touchmove sync-touchmove
......
...@@ -374,9 +374,9 @@ const Experiment::Choice kTouchScrollingModeChoices[] = { ...@@ -374,9 +374,9 @@ const Experiment::Choice kTouchScrollingModeChoices[] = {
{ IDS_FLAGS_TOUCH_SCROLLING_MODE_TOUCHCANCEL, { IDS_FLAGS_TOUCH_SCROLLING_MODE_TOUCHCANCEL,
switches::kTouchScrollingMode, switches::kTouchScrollingMode,
switches::kTouchScrollingModeTouchcancel }, switches::kTouchScrollingModeTouchcancel },
{ IDS_FLAGS_TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE, { IDS_FLAGS_TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE,
switches::kTouchScrollingMode, switches::kTouchScrollingMode,
switches::kTouchScrollingModeAbsorbTouchmove }, switches::kTouchScrollingModeAsyncTouchmove },
{ IDS_FLAGS_TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE, { IDS_FLAGS_TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE,
switches::kTouchScrollingMode, switches::kTouchScrollingMode,
switches::kTouchScrollingModeSyncTouchmove }, switches::kTouchScrollingModeSyncTouchmove },
......
...@@ -88,10 +88,10 @@ double GetTouchMoveSlopSuppressionLengthDips() { ...@@ -88,10 +88,10 @@ double GetTouchMoveSlopSuppressionLengthDips() {
TouchEventQueue::TouchScrollingMode GetTouchScrollingMode() { TouchEventQueue::TouchScrollingMode GetTouchScrollingMode() {
std::string modeString = CommandLine::ForCurrentProcess()-> std::string modeString = CommandLine::ForCurrentProcess()->
GetSwitchValueASCII(switches::kTouchScrollingMode); GetSwitchValueASCII(switches::kTouchScrollingMode);
if (modeString == switches::kTouchScrollingModeAsyncTouchmove)
return TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE;
if (modeString == switches::kTouchScrollingModeSyncTouchmove) if (modeString == switches::kTouchScrollingModeSyncTouchmove)
return TouchEventQueue::TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE; return TouchEventQueue::TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE;
if (modeString == switches::kTouchScrollingModeAbsorbTouchmove)
return TouchEventQueue::TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE;
if (modeString == switches::kTouchScrollingModeTouchcancel) if (modeString == switches::kTouchScrollingModeTouchcancel)
return TouchEventQueue::TOUCH_SCROLLING_MODE_TOUCHCANCEL; return TouchEventQueue::TOUCH_SCROLLING_MODE_TOUCHCANCEL;
if (modeString != "") if (modeString != "")
...@@ -448,15 +448,14 @@ void InputRouterImpl::OfferToHandlers(const WebInputEvent& input_event, ...@@ -448,15 +448,14 @@ void InputRouterImpl::OfferToHandlers(const WebInputEvent& input_event,
// Touch events should always indicate in the event whether they are // Touch events should always indicate in the event whether they are
// cancelable (respect ACK disposition) or not. // cancelable (respect ACK disposition) or not.
bool ignoresAck = bool ignores_ack = WebInputEventTraits::IgnoresAckDisposition(input_event);
WebInputEventTraits::IgnoresAckDisposition(input_event.type);
if (WebInputEvent::isTouchEventType(input_event.type)) { if (WebInputEvent::isTouchEventType(input_event.type)) {
DCHECK(!ignoresAck == DCHECK(!ignores_ack ==
static_cast<const blink::WebTouchEvent&>(input_event).cancelable); static_cast<const blink::WebTouchEvent&>(input_event).cancelable);
} }
// If we don't care about the ack disposition, send the ack immediately. // If we don't care about the ack disposition, send the ack immediately.
if (ignoresAck) { if (ignores_ack) {
ProcessInputEventAck(input_event.type, ProcessInputEventAck(input_event.type,
INPUT_EVENT_ACK_STATE_IGNORED, INPUT_EVENT_ACK_STATE_IGNORED,
latency_info, latency_info,
...@@ -530,10 +529,10 @@ bool InputRouterImpl::OfferToRenderer(const WebInputEvent& input_event, ...@@ -530,10 +529,10 @@ bool InputRouterImpl::OfferToRenderer(const WebInputEvent& input_event,
bool is_keyboard_shortcut) { bool is_keyboard_shortcut) {
if (Send(new InputMsg_HandleInputEvent( if (Send(new InputMsg_HandleInputEvent(
routing_id(), &input_event, latency_info, is_keyboard_shortcut))) { routing_id(), &input_event, latency_info, is_keyboard_shortcut))) {
// Ack messages for ignored ack event types are not required, and might // Ack messages for ignored ack event types should never be sent by the
// never be sent by the renderer. Consequently, such event types should not // renderer. Consequently, such event types should not affect event time
// affect event timing or in-flight event count metrics. // or in-flight event count metrics.
if (!WebInputEventTraits::IgnoresAckDisposition(input_event.type)) { if (!WebInputEventTraits::IgnoresAckDisposition(input_event)) {
input_event_start_time_ = TimeTicks::Now(); input_event_start_time_ = TimeTicks::Now();
client_->IncrementInFlightEventCount(); client_->IncrementInFlightEventCount();
} }
...@@ -545,11 +544,6 @@ bool InputRouterImpl::OfferToRenderer(const WebInputEvent& input_event, ...@@ -545,11 +544,6 @@ bool InputRouterImpl::OfferToRenderer(const WebInputEvent& input_event,
void InputRouterImpl::OnInputEventAck(WebInputEvent::Type event_type, void InputRouterImpl::OnInputEventAck(WebInputEvent::Type event_type,
InputEventAckState ack_result, InputEventAckState ack_result,
const ui::LatencyInfo& latency_info) { 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(); client_->DecrementInFlightEventCount();
// Log the time delta for processing an input event. // Log the time delta for processing an input event.
......
...@@ -51,6 +51,29 @@ const WebInputEvent* GetInputEventFromMessage(const IPC::Message& message) { ...@@ -51,6 +51,29 @@ const WebInputEvent* GetInputEventFromMessage(const IPC::Message& message) {
return reinterpret_cast<const WebInputEvent*>(data); 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) { bool GetIsShortcutFromHandleInputEventMessage(const IPC::Message* msg) {
InputMsg_HandleInputEvent::Schema::Param param; InputMsg_HandleInputEvent::Schema::Param param;
InputMsg_HandleInputEvent::Read(msg, &param); InputMsg_HandleInputEvent::Read(msg, &param);
...@@ -252,6 +275,7 @@ class InputRouterImplTest : public testing::Test { ...@@ -252,6 +275,7 @@ class InputRouterImplTest : public testing::Test {
void CancelTouchPoint(int index) { void CancelTouchPoint(int index) {
touch_event_.CancelPoint(index); touch_event_.CancelPoint(index);
} }
void SendInputEventACK(blink::WebInputEvent::Type type, void SendInputEventACK(blink::WebInputEvent::Type type,
InputEventAckState ack_result) { InputEventAckState ack_result) {
scoped_ptr<IPC::Message> response( scoped_ptr<IPC::Message> response(
...@@ -742,7 +766,7 @@ TEST_F(InputRouterImplTest, TouchTypesIgnoringAck) { ...@@ -742,7 +766,7 @@ TEST_F(InputRouterImplTest, TouchTypesIgnoringAck) {
ASSERT_LT(start_type, end_type); ASSERT_LT(start_type, end_type);
for (int i = start_type; i <= end_type; ++i) { for (int i = start_type; i <= end_type; ++i) {
WebInputEvent::Type type = static_cast<WebInputEvent::Type>(i); WebInputEvent::Type type = static_cast<WebInputEvent::Type>(i);
if (!WebInputEventTraits::IgnoresAckDisposition(type)) if (!WebInputEventTraits::IgnoresAckDisposition(GetEventWithType(type)))
continue; continue;
// The TouchEventQueue requires an initial TouchStart for it to begin // The TouchEventQueue requires an initial TouchStart for it to begin
...@@ -803,7 +827,7 @@ TEST_F(InputRouterImplTest, GestureTypesIgnoringAck) { ...@@ -803,7 +827,7 @@ TEST_F(InputRouterImplTest, GestureTypesIgnoringAck) {
WebInputEvent::GestureScrollEnd}; WebInputEvent::GestureScrollEnd};
for (int i = 0; i < kEventTypesLength; ++i) { for (int i = 0; i < kEventTypesLength; ++i) {
WebInputEvent::Type type = eventTypes[i]; WebInputEvent::Type type = eventTypes[i];
if (!WebInputEventTraits::IgnoresAckDisposition(type)) { if (!WebInputEventTraits::IgnoresAckDisposition(GetEventWithType(type))) {
SimulateGestureEvent(type, WebGestureEvent::Touchscreen); SimulateGestureEvent(type, WebGestureEvent::Touchscreen);
EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
...@@ -823,10 +847,6 @@ TEST_F(InputRouterImplTest, GestureTypesIgnoringAck) { ...@@ -823,10 +847,6 @@ TEST_F(InputRouterImplTest, GestureTypesIgnoringAck) {
EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(0, client_->in_flight_event_count()); EXPECT_EQ(0, client_->in_flight_event_count());
EXPECT_FALSE(HasPendingEvents()); 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) { ...@@ -837,7 +857,8 @@ TEST_F(InputRouterImplTest, MouseTypesIgnoringAck) {
for (int i = start_type; i <= end_type; ++i) { for (int i = start_type; i <= end_type; ++i) {
WebInputEvent::Type type = static_cast<WebInputEvent::Type>(i); WebInputEvent::Type type = static_cast<WebInputEvent::Type>(i);
int expected_in_flight_event_count = 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 // 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. // result here is that ignored ack types don't affect the in-flight count.
...@@ -845,10 +866,12 @@ TEST_F(InputRouterImplTest, MouseTypesIgnoringAck) { ...@@ -845,10 +866,12 @@ TEST_F(InputRouterImplTest, MouseTypesIgnoringAck) {
EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(expected_in_flight_event_count, client_->in_flight_event_count()); EXPECT_EQ(expected_in_flight_event_count, client_->in_flight_event_count());
SendInputEventACK(type, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); if (expected_in_flight_event_count) {
EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); SendInputEventACK(type, INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
EXPECT_EQ(0, client_->in_flight_event_count()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(0, client_->in_flight_event_count());
}
} }
} }
...@@ -871,7 +894,8 @@ TEST_F(InputRouterImplTest, RequiredEventAckTypes) { ...@@ -871,7 +894,8 @@ TEST_F(InputRouterImplTest, RequiredEventAckTypes) {
}; };
for (size_t i = 0; i < arraysize(kRequiredEventAckTypes); ++i) { for (size_t i = 0; i < arraysize(kRequiredEventAckTypes); ++i) {
const WebInputEvent::Type required_ack_type = 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) { ...@@ -920,51 +944,27 @@ TEST_F(InputRouterImplTest, GestureTypesIgnoringAckInterleaved) {
EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
// Now ack each event. Ack-ignoring events should not be dispatched until all // Now ack each ack-respecting event. Ack-ignoring events should not be
// prior events which observe ack disposition have been fired, at which // dispatched until all prior events which observe ack disposition have been
// point they should be sent immediately. They should also have no effect // fired, at which point they should be sent immediately. They should also
// on the in-flight event count. // have no effect on the in-flight event count.
SendInputEventACK(WebInputEvent::GestureScrollBegin,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
SendInputEventACK(WebInputEvent::GestureScrollUpdate, SendInputEventACK(WebInputEvent::GestureScrollUpdate,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED); INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(2U, GetSentMessageCountAndResetSink()); EXPECT_EQ(2U, GetSentMessageCountAndResetSink());
EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(1, client_->in_flight_event_count()); 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, SendInputEventACK(WebInputEvent::GestureScrollUpdate,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED); INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(2U, GetSentMessageCountAndResetSink()); EXPECT_EQ(2U, GetSentMessageCountAndResetSink());
EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(1, client_->in_flight_event_count()); 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, SendInputEventACK(WebInputEvent::GestureScrollUpdate,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED); INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount()); EXPECT_EQ(2U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(0, client_->in_flight_event_count()); 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 // Test that GestureShowPress events don't get out of order due to
...@@ -1003,12 +1003,6 @@ TEST_F(InputRouterImplTest, GestureShowPressIsInOrder) { ...@@ -1003,12 +1003,6 @@ TEST_F(InputRouterImplTest, GestureShowPressIsInOrder) {
// ShowPress has entered the queue. // ShowPress has entered the queue.
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount()); 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, SendInputEventACK(WebInputEvent::GesturePinchUpdate,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED); INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// Now that the Tap has been ACKed, the ShowPress events should receive // Now that the Tap has been ACKed, the ShowPress events should receive
...@@ -1288,8 +1282,6 @@ TEST_F(InputRouterImplTest, DoubleTapGestureDependsOnFirstTap) { ...@@ -1288,8 +1282,6 @@ TEST_F(InputRouterImplTest, DoubleTapGestureDependsOnFirstTap) {
// auto. // auto.
SimulateGestureEvent(WebInputEvent::GestureTapUnconfirmed, SimulateGestureEvent(WebInputEvent::GestureTapUnconfirmed,
WebGestureEvent::Touchscreen); WebGestureEvent::Touchscreen);
SendInputEventACK(WebInputEvent::GestureTap,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
// This tap gesture is dropped, since the GestureTapUnconfirmed was turned // This tap gesture is dropped, since the GestureTapUnconfirmed was turned
...@@ -1313,7 +1305,7 @@ TEST_F(InputRouterImplTest, DoubleTapGestureDependsOnFirstTap) { ...@@ -1313,7 +1305,7 @@ TEST_F(InputRouterImplTest, DoubleTapGestureDependsOnFirstTap) {
WebGestureEvent::Touchscreen); WebGestureEvent::Touchscreen);
// This test will become invalid if GestureDoubleTap stops requiring an ack. // This test will become invalid if GestureDoubleTap stops requiring an ack.
DCHECK(!WebInputEventTraits::IgnoresAckDisposition( DCHECK(!WebInputEventTraits::IgnoresAckDisposition(
WebInputEvent::GestureDoubleTap)); GetEventWithType(WebInputEvent::GestureDoubleTap)));
EXPECT_EQ(0, client_->in_flight_event_count()); EXPECT_EQ(0, client_->in_flight_event_count());
} }
...@@ -1349,6 +1341,10 @@ TEST_F(InputRouterImplTest, InputFlush) { ...@@ -1349,6 +1341,10 @@ TEST_F(InputRouterImplTest, InputFlush) {
WebGestureEvent::Touchscreen); WebGestureEvent::Touchscreen);
SimulateGestureEvent(WebInputEvent::GestureScrollUpdate, SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
WebGestureEvent::Touchscreen); WebGestureEvent::Touchscreen);
SimulateGestureEvent(WebInputEvent::GesturePinchBegin,
WebGestureEvent::Touchscreen);
SimulateGestureEvent(WebInputEvent::GesturePinchUpdate,
WebGestureEvent::Touchscreen);
Flush(); Flush();
EXPECT_EQ(0U, GetAndResetDidFlushCount()); EXPECT_EQ(0U, GetAndResetDidFlushCount());
...@@ -1363,13 +1359,13 @@ TEST_F(InputRouterImplTest, InputFlush) { ...@@ -1363,13 +1359,13 @@ TEST_F(InputRouterImplTest, InputFlush) {
EXPECT_TRUE(HasPendingEvents()); EXPECT_TRUE(HasPendingEvents());
// One more gesture to go. // One more gesture to go.
SendInputEventACK(WebInputEvent::GestureScrollBegin, SendInputEventACK(WebInputEvent::GestureScrollUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED); INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, GetAndResetDidFlushCount()); EXPECT_EQ(0U, GetAndResetDidFlushCount());
EXPECT_TRUE(HasPendingEvents()); EXPECT_TRUE(HasPendingEvents());
// The final ack'ed gesture should trigger the DidFlush. // The final ack'ed gesture should trigger the DidFlush.
SendInputEventACK(WebInputEvent::GestureScrollUpdate, SendInputEventACK(WebInputEvent::GesturePinchUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED); INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, GetAndResetDidFlushCount()); EXPECT_EQ(1U, GetAndResetDidFlushCount());
EXPECT_FALSE(HasPendingEvents()); EXPECT_FALSE(HasPendingEvents());
......
...@@ -48,12 +48,11 @@ class CONTENT_EXPORT TouchEventQueue { ...@@ -48,12 +48,11 @@ class CONTENT_EXPORT TouchEventQueue {
// using the disposition to determine whether a scroll update should be // using the disposition to determine whether a scroll update should be
// sent. Mobile Safari's default overflow scroll behavior. // sent. Mobile Safari's default overflow scroll behavior.
TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE, TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE,
// Like sync, except that consumed scroll events cause subsequent touchmove // Send touchmove events throughout a scroll, but throttle sending and
// events to be suppressed. Unconsumed scroll events return touchmove // ignore the ACK as long as scrolling remains possible. Unconsumed scroll
// events to being dispatched synchronously (so scrolling may be hijacked // events return touchmove events to being dispatched synchronously.
// when a scroll limit is reached, and later resumed). http://goo.gl/RShsdN TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE,
TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE, TOUCH_SCROLLING_MODE_DEFAULT = TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE
TOUCH_SCROLLING_MODE_DEFAULT = TOUCH_SCROLLING_MODE_TOUCHCANCEL
}; };
// The |client| must outlive the TouchEventQueue. If // The |client| must outlive the TouchEventQueue. If
...@@ -120,7 +119,7 @@ class CONTENT_EXPORT TouchEventQueue { ...@@ -120,7 +119,7 @@ class CONTENT_EXPORT TouchEventQueue {
friend class TouchTimeoutHandler; friend class TouchTimeoutHandler;
friend class TouchEventQueueTest; friend class TouchEventQueueTest;
bool HasTimeoutEvent() const; bool HasPendingAsyncTouchMoveForTesting() const;
bool IsTimeoutRunningForTesting() const; bool IsTimeoutRunningForTesting() const;
const TouchEventWithLatencyInfo& GetLatestEventForTesting() const; const TouchEventWithLatencyInfo& GetLatestEventForTesting() const;
...@@ -128,16 +127,32 @@ class CONTENT_EXPORT TouchEventQueue { ...@@ -128,16 +127,32 @@ class CONTENT_EXPORT TouchEventQueue {
// events being sent to the renderer. // events being sent to the renderer.
void FlushQueue(); void FlushQueue();
// Walks the queue, checking each event for |ShouldForwardToRenderer()|. // Walks the queue, checking each event with |FilterBeforeForwarding()|.
// If true, forwards the touch event and stops processing further events. // If allowed, forwards the touch event and stops processing further events.
// If false, acks the event with |INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS|. // Otherwise, acks the event with |INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS|.
void TryForwardNextEventToRenderer(); void TryForwardNextEventToRenderer();
// Pops the touch-event from the top of the queue and sends it to the // Forwards the event at the head of the queue to the renderer.
// TouchEventQueueClient. This reduces the size of the queue by one. 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, void PopTouchEventToClient(InputEventAckState ack_result,
const ui::LatencyInfo& renderer_latency_info); 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 { enum PreFilterResult {
ACK_WITH_NO_CONSUMER_EXISTS, ACK_WITH_NO_CONSUMER_EXISTS,
ACK_WITH_NOT_CONSUMED, ACK_WITH_NOT_CONSUMED,
...@@ -161,10 +176,14 @@ class CONTENT_EXPORT TouchEventQueue { ...@@ -161,10 +176,14 @@ class CONTENT_EXPORT TouchEventQueue {
typedef std::map<int, InputEventAckState> TouchPointAckStates; typedef std::map<int, InputEventAckState> TouchPointAckStates;
TouchPointAckStates touch_ack_states_; 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()|. // Used to defer touch forwarding when ack dispatch triggers |QueueEvent()|.
// If not NULL, |dispatching_touch_ack_| is the touch event of which the ack // If not NULL, |dispatching_touch_ack_| is the touch event of which the ack
// is being dispatched. // is being dispatched.
CoalescedWebTouchEvent* dispatching_touch_ack_; const CoalescedWebTouchEvent* dispatching_touch_ack_;
// Used to prevent touch timeout scheduling if we receive a synchronous // Used to prevent touch timeout scheduling if we receive a synchronous
// ack after forwarding a touch event to the client. // ack after forwarding a touch event to the client.
...@@ -188,12 +207,15 @@ class CONTENT_EXPORT TouchEventQueue { ...@@ -188,12 +207,15 @@ class CONTENT_EXPORT TouchEventQueue {
// been preventDefaulted. // been preventDefaulted.
scoped_ptr<TouchMoveSlopSuppressor> touchmove_slop_suppressor_; scoped_ptr<TouchMoveSlopSuppressor> touchmove_slop_suppressor_;
// Whether touchmove events should be dropped due to the // Whether touch events should remain buffered and dispatched asynchronously
// TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE mode. Note that we can't use // while a scroll sequence is active. In this mode, touchmove's are throttled
// touch_filtering_state_ for this (without adding a few new states and // and ack'ed immediately, but remain buffered in |pending_async_touch_move_|
// complicating the code significantly) because it can occur with and without // until a sufficient time period has elapsed since the last sent touch event.
// timeout, and shouldn't cause touchend to be dropped. // For details see the design doc at http://goo.gl/lVyJAa.
bool absorbing_touch_moves_; 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 // 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 // setting for experimentation, but we may evolve it into an app-controlled
......
...@@ -334,28 +334,31 @@ void WebInputEventTraits::Coalesce(const WebInputEvent& event_to_coalesce, ...@@ -334,28 +334,31 @@ void WebInputEventTraits::Coalesce(const WebInputEvent& event_to_coalesce,
Apply(WebInputEventCoalesce(), event->type, event_to_coalesce, event); Apply(WebInputEventCoalesce(), event->type, event_to_coalesce, event);
} }
bool WebInputEventTraits::IgnoresAckDisposition( bool WebInputEventTraits::IgnoresAckDisposition(const WebInputEvent& event) {
blink::WebInputEvent::Type type) { switch (event.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:
case WebInputEvent::MouseDown: case WebInputEvent::MouseDown:
case WebInputEvent::MouseUp: case WebInputEvent::MouseUp:
case WebInputEvent::MouseEnter: case WebInputEvent::MouseEnter:
case WebInputEvent::MouseLeave: case WebInputEvent::MouseLeave:
case WebInputEvent::ContextMenu: 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; return true;
case WebInputEvent::TouchStart:
case WebInputEvent::TouchMove:
case WebInputEvent::TouchEnd:
return !static_cast<const WebTouchEvent&>(event).cancelable;
default: default:
break; return false;
} }
return false;
} }
} // namespace content } // namespace content
...@@ -22,7 +22,7 @@ class CONTENT_EXPORT WebInputEventTraits { ...@@ -22,7 +22,7 @@ class CONTENT_EXPORT WebInputEventTraits {
const blink::WebInputEvent& event); const blink::WebInputEvent& event);
static void Coalesce(const blink::WebInputEvent& event_to_coalesce, static void Coalesce(const blink::WebInputEvent& event_to_coalesce,
blink::WebInputEvent* event); blink::WebInputEvent* event);
static bool IgnoresAckDisposition(blink::WebInputEvent::Type type); static bool IgnoresAckDisposition(const blink::WebInputEvent& event);
}; };
} // namespace content } // namespace content
......
...@@ -850,9 +850,9 @@ const char kTestType[] = "test-type"; ...@@ -850,9 +850,9 @@ const char kTestType[] = "test-type";
const char kTouchAckTimeoutDelayMs[] = "touch-ack-timeout-delay-ms"; const char kTouchAckTimeoutDelayMs[] = "touch-ack-timeout-delay-ms";
const char kTouchScrollingMode[] = "touch-scrolling-mode"; const char kTouchScrollingMode[] = "touch-scrolling-mode";
const char kTouchScrollingModeTouchcancel[] = "touchcancel"; const char kTouchScrollingModeAsyncTouchmove[] = "async-touchmove";
const char kTouchScrollingModeSyncTouchmove[] = "sync-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, // Causes TRACE_EVENT flags to be recorded beginning with shutdown. Optionally,
// can specify the specific trace categories to include (e.g. // can specify the specific trace categories to include (e.g.
......
...@@ -240,7 +240,7 @@ CONTENT_EXPORT extern const char kTestSandbox[]; ...@@ -240,7 +240,7 @@ CONTENT_EXPORT extern const char kTestSandbox[];
CONTENT_EXPORT extern const char kTestType[]; CONTENT_EXPORT extern const char kTestType[];
CONTENT_EXPORT extern const char kTouchAckTimeoutDelayMs[]; CONTENT_EXPORT extern const char kTouchAckTimeoutDelayMs[];
CONTENT_EXPORT extern const char kTouchScrollingMode[]; 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 kTouchScrollingModeSyncTouchmove[];
CONTENT_EXPORT extern const char kTouchScrollingModeTouchcancel[]; CONTENT_EXPORT extern const char kTouchScrollingModeTouchcancel[];
CONTENT_EXPORT extern const char kTraceShutdown[]; CONTENT_EXPORT extern const char kTraceShutdown[];
......
...@@ -173,7 +173,7 @@ void InputEventFilter::ForwardToHandler(const IPC::Message& message) { ...@@ -173,7 +173,7 @@ void InputEventFilter::ForwardToHandler(const IPC::Message& message) {
return; return;
} }
if (!WebInputEventTraits::IgnoresAckDisposition(event->type)) if (!WebInputEventTraits::IgnoresAckDisposition(*event))
SendACK(event->type, ack, latency_info, routing_id); SendACK(event->type, ack, latency_info, routing_id);
} }
......
...@@ -1077,7 +1077,7 @@ void RenderWidget::OnHandleInputEvent(const blink::WebInputEvent* input_event, ...@@ -1077,7 +1077,7 @@ void RenderWidget::OnHandleInputEvent(const blink::WebInputEvent* input_event,
kInputHandlingTimeThrottlingThresholdMicroseconds; kInputHandlingTimeThrottlingThresholdMicroseconds;
} }
if (!WebInputEventTraits::IgnoresAckDisposition(input_event->type)) { if (!WebInputEventTraits::IgnoresAckDisposition(*input_event)) {
scoped_ptr<IPC::Message> response( scoped_ptr<IPC::Message> response(
new InputHostMsg_HandleInputEvent_ACK(routing_id_, new InputHostMsg_HandleInputEvent_ACK(routing_id_,
input_event->type, input_event->type,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment