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

Disable the touch ack timeout for touch-action:none

When the touch-action for the current touch sequence is updated, also update
whether the touch ack time is disabled based on the allowed touch-action. This
provides a straightforward knob for developers to disable the timeout on desktop
sites.

BUG=340060

Review URL: https://codereview.chromium.org/183923034

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@255304 0039d316-1c4b-4281-b951-d872f2087c98
parent e91b0473
...@@ -142,8 +142,9 @@ InputRouterImpl::InputRouterImpl(IPC::Sender* sender, ...@@ -142,8 +142,9 @@ InputRouterImpl::InputRouterImpl(IPC::Sender* sender,
move_caret_pending_(false), move_caret_pending_(false),
mouse_move_pending_(false), mouse_move_pending_(false),
mouse_wheel_pending_(false), mouse_wheel_pending_(false),
touch_ack_timeout_enabled_(false), touch_ack_timeout_supported_(false),
touch_ack_timeout_delay_ms_(std::numeric_limits<size_t>::max()), touch_ack_timeout_delay_ms_(std::numeric_limits<size_t>::max()),
current_view_flags_(0),
current_ack_source_(ACK_SOURCE_NONE), current_ack_source_(ACK_SOURCE_NONE),
gesture_event_queue_(new GestureEventQueue(this, this)) { gesture_event_queue_(new GestureEventQueue(this, this)) {
DCHECK(sender); DCHECK(sender);
...@@ -151,10 +152,9 @@ InputRouterImpl::InputRouterImpl(IPC::Sender* sender, ...@@ -151,10 +152,9 @@ InputRouterImpl::InputRouterImpl(IPC::Sender* sender,
DCHECK(ack_handler); DCHECK(ack_handler);
touch_event_queue_.reset(new TouchEventQueue( touch_event_queue_.reset(new TouchEventQueue(
this, GetTouchScrollingMode(), GetTouchMoveSlopSuppressionLengthDips())); this, GetTouchScrollingMode(), GetTouchMoveSlopSuppressionLengthDips()));
touch_ack_timeout_enabled_ = touch_ack_timeout_supported_ =
GetTouchAckTimeoutDelayMs(&touch_ack_timeout_delay_ms_); GetTouchAckTimeoutDelayMs(&touch_ack_timeout_delay_ms_);
touch_event_queue_->SetAckTimeoutEnabled(touch_ack_timeout_enabled_, UpdateTouchAckTimeoutEnabled();
touch_ack_timeout_delay_ms_);
} }
InputRouterImpl::~InputRouterImpl() {} InputRouterImpl::~InputRouterImpl() {}
...@@ -287,8 +287,14 @@ void InputRouterImpl::SendMouseEventImmediately( ...@@ -287,8 +287,14 @@ void InputRouterImpl::SendMouseEventImmediately(
void InputRouterImpl::SendTouchEventImmediately( void InputRouterImpl::SendTouchEventImmediately(
const TouchEventWithLatencyInfo& touch_event) { const TouchEventWithLatencyInfo& touch_event) {
if (WebTouchEventTraits::IsTouchSequenceStart(touch_event.event)) if (WebTouchEventTraits::IsTouchSequenceStart(touch_event.event)) {
touch_action_filter_.ResetTouchAction(); touch_action_filter_.ResetTouchAction();
// Note that if the previous touch-action was TOUCH_ACTION_NONE, enabling
// the timeout here will not take effect until the *following* touch
// sequence. This is a desirable side-effect, giving the renderer a chance
// to send a touch-action response without racing against the ack timeout.
UpdateTouchAckTimeoutEnabled();
}
FilterAndSendWebInputEvent(touch_event.event, touch_event.latency, false); FilterAndSendWebInputEvent(touch_event.event, touch_event.latency, false);
} }
...@@ -310,11 +316,10 @@ bool InputRouterImpl::ShouldForwardTouchEvent() const { ...@@ -310,11 +316,10 @@ bool InputRouterImpl::ShouldForwardTouchEvent() const {
} }
void InputRouterImpl::OnViewUpdated(int view_flags) { void InputRouterImpl::OnViewUpdated(int view_flags) {
bool fixed_page_scale = (view_flags & FIXED_PAGE_SCALE) != 0; current_view_flags_ = view_flags;
bool mobile_viewport = (view_flags & MOBILE_VIEWPORT) != 0;
touch_event_queue_->SetAckTimeoutEnabled( // A fixed page scale or mobile viewport should disable the touch ack timeout.
touch_ack_timeout_enabled_ && !(fixed_page_scale || mobile_viewport), UpdateTouchAckTimeoutEnabled();
touch_ack_timeout_delay_ms_);
} }
bool InputRouterImpl::OnMessageReceived(const IPC::Message& message) { bool InputRouterImpl::OnMessageReceived(const IPC::Message& message) {
...@@ -344,6 +349,7 @@ void InputRouterImpl::OnTouchEventAck(const TouchEventWithLatencyInfo& event, ...@@ -344,6 +349,7 @@ void InputRouterImpl::OnTouchEventAck(const TouchEventWithLatencyInfo& event,
if (WebTouchEventTraits::IsTouchSequenceStart(event.event) && if (WebTouchEventTraits::IsTouchSequenceStart(event.event) &&
ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) { ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) {
touch_action_filter_.ResetTouchAction(); touch_action_filter_.ResetTouchAction();
UpdateTouchAckTimeoutEnabled();
} }
ack_handler_->OnTouchEventAck(event, ack_result); ack_handler_->OnTouchEventAck(event, ack_result);
} }
...@@ -559,12 +565,14 @@ void InputRouterImpl::OnHasTouchEventHandlers(bool has_handlers) { ...@@ -559,12 +565,14 @@ void InputRouterImpl::OnHasTouchEventHandlers(bool has_handlers) {
client_->OnHasTouchEventHandlers(has_handlers); client_->OnHasTouchEventHandlers(has_handlers);
} }
void InputRouterImpl::OnSetTouchAction( void InputRouterImpl::OnSetTouchAction(TouchAction touch_action) {
content::TouchAction touch_action) {
// Synthetic touchstart events should get filtered out in RenderWidget. // Synthetic touchstart events should get filtered out in RenderWidget.
DCHECK(touch_event_queue_->IsPendingAckTouchStart()); DCHECK(touch_event_queue_->IsPendingAckTouchStart());
touch_action_filter_.OnSetTouchAction(touch_action); touch_action_filter_.OnSetTouchAction(touch_action);
// TOUCH_ACTION_NONE should disable the touch ack timeout.
UpdateTouchAckTimeoutEnabled();
} }
void InputRouterImpl::ProcessInputEventAck( void InputRouterImpl::ProcessInputEventAck(
...@@ -774,6 +782,30 @@ void InputRouterImpl::SimulateTouchGestureWithMouse( ...@@ -774,6 +782,30 @@ void InputRouterImpl::SimulateTouchGestureWithMouse(
} }
} }
void InputRouterImpl::UpdateTouchAckTimeoutEnabled() {
if (!touch_ack_timeout_supported_) {
touch_event_queue_->SetAckTimeoutEnabled(false, 0);
return;
}
// Mobile sites tend to be well-behaved with respect to touch handling, so
// they have less need for the touch timeout fallback.
const bool fixed_page_scale = (current_view_flags_ & FIXED_PAGE_SCALE) != 0;
const bool mobile_viewport = (current_view_flags_ & MOBILE_VIEWPORT) != 0;
// TOUCH_ACTION_NONE will prevent scrolling, in which case the timeout serves
// little purpose. It's also a strong signal that touch handling is critical
// to page functionality, so the timeout could do more harm than good.
const bool touch_action_none =
touch_action_filter_.allowed_touch_action() == TOUCH_ACTION_NONE;
const bool touch_ack_timeout_enabled = !fixed_page_scale &&
!mobile_viewport &&
!touch_action_none;
touch_event_queue_->SetAckTimeoutEnabled(touch_ack_timeout_enabled,
touch_ack_timeout_delay_ms_);
}
bool InputRouterImpl::IsInOverscrollGesture() const { bool InputRouterImpl::IsInOverscrollGesture() const {
OverscrollController* controller = client_->GetOverscrollController(); OverscrollController* controller = client_->GetOverscrollController();
return controller && controller->overscroll_mode() != OVERSCROLL_NONE; return controller && controller->overscroll_mode() != OVERSCROLL_NONE;
......
...@@ -125,7 +125,7 @@ private: ...@@ -125,7 +125,7 @@ private:
void OnMsgMoveCaretAck(); void OnMsgMoveCaretAck();
void OnSelectRangeAck(); void OnSelectRangeAck();
void OnHasTouchEventHandlers(bool has_handlers); void OnHasTouchEventHandlers(bool has_handlers);
void OnSetTouchAction(content::TouchAction touch_action); void OnSetTouchAction(TouchAction touch_action);
// Indicates the source of an ack provided to |ProcessInputEventAck()|. // Indicates the source of an ack provided to |ProcessInputEventAck()|.
// The source is tracked by |current_ack_source_|, which aids in ack routing. // The source is tracked by |current_ack_source_|, which aids in ack routing.
...@@ -174,6 +174,12 @@ private: ...@@ -174,6 +174,12 @@ private:
void SimulateTouchGestureWithMouse( void SimulateTouchGestureWithMouse(
const MouseEventWithLatencyInfo& mouse_event); const MouseEventWithLatencyInfo& mouse_event);
// Called when a touch timeout-affecting bit has changed, in turn toggling the
// touch ack timeout feature of the |touch_event_queue_| as appropriate. Input
// to that determination includes current view properties, the allowed touch
// action and the command-line configured |touch_ack_timeout_supported_|.
void UpdateTouchAckTimeoutEnabled();
bool IsInOverscrollGesture() const; bool IsInOverscrollGesture() const;
int routing_id() const { return routing_id_; } int routing_id() const { return routing_id_; }
...@@ -232,9 +238,12 @@ private: ...@@ -232,9 +238,12 @@ private:
KeyQueue key_queue_; KeyQueue key_queue_;
// Whether touch ack timeout handling has been enabled via the command line. // Whether touch ack timeout handling has been enabled via the command line.
bool touch_ack_timeout_enabled_; bool touch_ack_timeout_supported_;
size_t touch_ack_timeout_delay_ms_; size_t touch_ack_timeout_delay_ms_;
// Cached flags from |OnViewUpdated()|, defaults to 0.
int current_view_flags_;
// The source of the ack within the scope of |ProcessInputEventAck()|. // The source of the ack within the scope of |ProcessInputEventAck()|.
// Defaults to ACK_SOURCE_NONE. // Defaults to ACK_SOURCE_NONE.
AckSource current_ack_source_; AckSource current_ack_source_;
......
...@@ -288,6 +288,12 @@ class InputRouterImplTest : public testing::Test { ...@@ -288,6 +288,12 @@ class InputRouterImplTest : public testing::Test {
return count; return count;
} }
static void Wait(base::TimeDelta delay) {
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE, base::MessageLoop::QuitClosure(), delay);
base::MessageLoop::current()->Run();
}
scoped_ptr<MockRenderProcessHost> process_; scoped_ptr<MockRenderProcessHost> process_;
scoped_ptr<MockInputRouterClient> client_; scoped_ptr<MockInputRouterClient> client_;
scoped_ptr<MockInputAckHandler> ack_handler_; scoped_ptr<MockInputAckHandler> ack_handler_;
...@@ -992,14 +998,41 @@ TEST_F(InputRouterImplTest, GestureShowPressIsInOrder) { ...@@ -992,14 +998,41 @@ TEST_F(InputRouterImplTest, GestureShowPressIsInOrder) {
} }
// Test that touch ack timeout behavior is properly configured via the command // Test that touch ack timeout behavior is properly configured via the command
// line, and toggled by the view update flags. // line, and toggled by view update flags and allowed touch actions.
TEST_F(InputRouterImplTest, TouchAckTimeoutConfigured) { TEST_F(InputRouterImplTest, TouchAckTimeoutConfigured) {
// Unless explicitly supported via the command-line, the touch timeout should
// be disabled.
EXPECT_FALSE(TouchEventTimeoutEnabled());
CommandLine::ForCurrentProcess()->AppendSwitchASCII( CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kTouchAckTimeoutDelayMs, "5"); switches::kTouchAckTimeoutDelayMs, "5");
TearDown(); TearDown();
SetUp(); SetUp();
ASSERT_TRUE(TouchEventTimeoutEnabled()); ASSERT_TRUE(TouchEventTimeoutEnabled());
// Verify that the touch ack timeout fires upon the delayed ack.
PressTouchPoint(1, 1);
SendTouchEvent();
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
Wait(base::TimeDelta::FromMilliseconds(15));
// The timed-out event should have been ack'ed.
EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
// Ack'ing the timed-out event should fire a TouchCancel.
SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
// The remainder of the touch sequence should be dropped.
ReleaseTouchPoint(0);
SendTouchEvent();
EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
ASSERT_TRUE(TouchEventTimeoutEnabled());
// A fixed page scale or mobile viewport should disable the touch timeout. // A fixed page scale or mobile viewport should disable the touch timeout.
input_router()->OnViewUpdated(InputRouter::FIXED_PAGE_SCALE); input_router()->OnViewUpdated(InputRouter::FIXED_PAGE_SCALE);
EXPECT_FALSE(TouchEventTimeoutEnabled()); EXPECT_FALSE(TouchEventTimeoutEnabled());
...@@ -1016,6 +1049,99 @@ TEST_F(InputRouterImplTest, TouchAckTimeoutConfigured) { ...@@ -1016,6 +1049,99 @@ TEST_F(InputRouterImplTest, TouchAckTimeoutConfigured) {
input_router()->OnViewUpdated(InputRouter::VIEW_FLAGS_NONE); input_router()->OnViewUpdated(InputRouter::VIEW_FLAGS_NONE);
EXPECT_TRUE(TouchEventTimeoutEnabled()); EXPECT_TRUE(TouchEventTimeoutEnabled());
// TOUCH_ACTION_NONE (and no other touch-action) should disable the timeout.
OnHasTouchEventHandlers(true);
PressTouchPoint(1, 1);
SendTouchEvent();
OnSetTouchAction(TOUCH_ACTION_PAN_Y);
EXPECT_TRUE(TouchEventTimeoutEnabled());
ReleaseTouchPoint(0);
SendTouchEvent();
SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED);
SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED);
PressTouchPoint(1, 1);
SendTouchEvent();
OnSetTouchAction(TOUCH_ACTION_NONE);
EXPECT_FALSE(TouchEventTimeoutEnabled());
ReleaseTouchPoint(0);
SendTouchEvent();
SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED);
SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED);
// As the touch-action is reset by a new touch sequence, the timeout behavior
// should be restored.
PressTouchPoint(1, 1);
SendTouchEvent();
EXPECT_TRUE(TouchEventTimeoutEnabled());
}
// Test that a touch sequenced preceded by TOUCH_ACTION_NONE is not affected by
// the touch timeout.
TEST_F(InputRouterImplTest,
TouchAckTimeoutDisabledForTouchSequenceAfterTouchActionNone) {
CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kTouchAckTimeoutDelayMs, "5");
TearDown();
SetUp();
ASSERT_TRUE(TouchEventTimeoutEnabled());
OnHasTouchEventHandlers(true);
// Start a touch sequence.
PressTouchPoint(1, 1);
SendTouchEvent();
// TOUCH_ACTION_NONE should disable the timeout.
OnSetTouchAction(TOUCH_ACTION_NONE);
SendInputEventACK(WebInputEvent::TouchStart, INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_FALSE(TouchEventTimeoutEnabled());
// End the touch sequence.
ReleaseTouchPoint(0);
SendTouchEvent();
SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_FALSE(TouchEventTimeoutEnabled());
ack_handler_->GetAndResetAckCount();
GetSentMessageCountAndResetSink();
// Start another touch sequence. While this does restore the touch timeout
// the timeout will not apply until the *next* touch sequence. This affords a
// touch-action response from the renderer without racing against the timeout.
PressTouchPoint(1, 1);
SendTouchEvent();
EXPECT_TRUE(TouchEventTimeoutEnabled());
EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
// Delay the ack. The timeout should *not* fire.
Wait(base::TimeDelta::FromMilliseconds(15));
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
// Finally send the ack. The touch sequence should not have been cancelled.
SendInputEventACK(WebInputEvent::TouchStart,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
EXPECT_TRUE(TouchEventTimeoutEnabled());
EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(0U, GetSentMessageCountAndResetSink());
// End the sequence.
ReleaseTouchPoint(0);
SendTouchEvent();
SendInputEventACK(WebInputEvent::TouchEnd, INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
// A new touch sequence should (finally) be subject to the timeout.
PressTouchPoint(1, 1);
SendTouchEvent();
EXPECT_TRUE(TouchEventTimeoutEnabled());
EXPECT_EQ(0U, ack_handler_->GetAndResetAckCount());
EXPECT_EQ(1U, GetSentMessageCountAndResetSink());
// Wait for the touch ack timeout to fire.
Wait(base::TimeDelta::FromMilliseconds(15));
EXPECT_EQ(1U, ack_handler_->GetAndResetAckCount());
} }
// Test that TouchActionFilter::ResetTouchAction is called before the // Test that TouchActionFilter::ResetTouchAction is called before the
......
...@@ -37,6 +37,8 @@ public: ...@@ -37,6 +37,8 @@ public:
// renderer. It may be called multiple times during this interval. // renderer. It may be called multiple times during this interval.
void ResetTouchAction(); void ResetTouchAction();
TouchAction allowed_touch_action() const { return allowed_touch_action_; }
// Return the intersection of two TouchAction values. // Return the intersection of two TouchAction values.
static TouchAction Intersect(TouchAction ta1, TouchAction ta2); static TouchAction Intersect(TouchAction ta1, TouchAction ta2);
...@@ -61,7 +63,7 @@ private: ...@@ -61,7 +63,7 @@ private:
bool allow_current_double_tap_event_; bool allow_current_double_tap_event_;
// What touch actions are currently permitted. // What touch actions are currently permitted.
content::TouchAction allowed_touch_action_; TouchAction allowed_touch_action_;
DISALLOW_COPY_AND_ASSIGN(TouchActionFilter); DISALLOW_COPY_AND_ASSIGN(TouchActionFilter);
}; };
......
...@@ -528,8 +528,10 @@ void TouchEventQueue::FlushQueue() { ...@@ -528,8 +528,10 @@ void TouchEventQueue::FlushQueue() {
DCHECK(!dispatching_touch_); DCHECK(!dispatching_touch_);
if (touch_filtering_state_ != DROP_ALL_TOUCHES) if (touch_filtering_state_ != DROP_ALL_TOUCHES)
touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
while (!touch_queue_.empty()) while (!touch_queue_.empty()) {
PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, LatencyInfo()); PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS,
LatencyInfo());
}
} }
void TouchEventQueue::PopTouchEventToClient( void TouchEventQueue::PopTouchEventToClient(
......
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