diff options
author | rbyers@chromium.org <rbyers@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-10 00:35:51 +0000 |
---|---|---|
committer | rbyers@chromium.org <rbyers@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-10 00:35:51 +0000 |
commit | 5d0bbdfa9c671b9e7b537693f27b62f2fa81ba0e (patch) | |
tree | 1af55c52b355916e7f8c9b41e4046996e679c63f /content/browser/renderer_host/input | |
parent | f96555c2b2e2590476bae50683a168b101c137f4 (diff) | |
download | chromium_src-5d0bbdfa9c671b9e7b537693f27b62f2fa81ba0e.zip chromium_src-5d0bbdfa9c671b9e7b537693f27b62f2fa81ba0e.tar.gz chromium_src-5d0bbdfa9c671b9e7b537693f27b62f2fa81ba0e.tar.bz2 |
Initial browser-side implementation for touch-action
Receive SetTouchAction messages and filter GestureEvents in the browser
based on them.
The logic here so far is pretty trivial, but will get more complex, eg:
- addition of pinch and double-tap gesture handling
- support for pan-x, pan-y, pan-x/y and potentially other touch actions
- more sophisticated handling of multiple fingers (pending WG discussion)
See touch-action design doc at http://goo.gl/KcKbxQ for more details.
Depends on blink-side change in https://codereview.chromium.org/16507017/
BUG=316735
Review URL: https://codereview.chromium.org/67383002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@239611 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser/renderer_host/input')
8 files changed, 293 insertions, 0 deletions
diff --git a/content/browser/renderer_host/input/input_router_impl.cc b/content/browser/renderer_host/input/input_router_impl.cc index 3f3c16a..13888f7 100644 --- a/content/browser/renderer_host/input/input_router_impl.cc +++ b/content/browser/renderer_host/input/input_router_impl.cc @@ -15,6 +15,7 @@ #include "content/browser/renderer_host/overscroll_controller.h" #include "content/common/content_constants_internal.h" #include "content/common/edit_command.h" +#include "content/common/input/touch_action.h" #include "content/common/input/web_input_event_traits.h" #include "content/common/input_messages.h" #include "content/common/view_messages.h" @@ -175,6 +176,9 @@ void InputRouterImpl::SendKeyboardEvent(const NativeWebKeyboardEvent& key_event, void InputRouterImpl::SendGestureEvent( const GestureEventWithLatencyInfo& gesture_event) { + if (touch_action_filter_.FilterGestureEvent(gesture_event.event)) + return; + HandleGestureScroll(gesture_event); if (!IsInOverscrollGesture() && @@ -249,6 +253,8 @@ bool InputRouterImpl::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(ViewHostMsg_SelectRange_ACK, OnSelectRangeAck) IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers, OnHasTouchEventHandlers) + IPC_MESSAGE_HANDLER(InputHostMsg_SetTouchAction, + OnSetTouchAction) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -468,6 +474,14 @@ void InputRouterImpl::OnHasTouchEventHandlers(bool has_handlers) { client_->OnHasTouchEventHandlers(has_handlers); } +void InputRouterImpl::OnSetTouchAction( + content::TouchAction touch_action) { + // Synthetic touchstart events should get filtered out in RenderWidget. + DCHECK(touch_event_queue_->IsPendingAckTouchStart()); + + touch_action_filter_.OnSetTouchAction(touch_action); +} + void InputRouterImpl::ProcessInputEventAck( WebInputEvent::Type event_type, InputEventAckState ack_result, diff --git a/content/browser/renderer_host/input/input_router_impl.h b/content/browser/renderer_host/input/input_router_impl.h index 481422a..10d4473 100644 --- a/content/browser/renderer_host/input/input_router_impl.h +++ b/content/browser/renderer_host/input/input_router_impl.h @@ -12,6 +12,7 @@ #include "base/time/time.h" #include "content/browser/renderer_host/input/gesture_event_filter.h" #include "content/browser/renderer_host/input/input_router.h" +#include "content/browser/renderer_host/input/touch_action_filter.h" #include "content/browser/renderer_host/input/touch_event_queue.h" #include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h" #include "content/public/browser/native_web_keyboard_event.h" @@ -122,6 +123,7 @@ private: void OnMsgMoveCaretAck(); void OnSelectRangeAck(); void OnHasTouchEventHandlers(bool has_handlers); + void OnSetTouchAction(content::TouchAction touch_action); // Indicates the source of an ack provided to |ProcessInputEventAck()|. // The source is tracked by |current_ack_source_|, which aids in ack routing. @@ -240,6 +242,7 @@ private: scoped_ptr<TouchEventQueue> touch_event_queue_; scoped_ptr<GestureEventFilter> gesture_event_filter_; + TouchActionFilter touch_action_filter_; DISALLOW_COPY_AND_ASSIGN(InputRouterImpl); }; diff --git a/content/browser/renderer_host/input/touch_action_filter.cc b/content/browser/renderer_host/input/touch_action_filter.cc new file mode 100644 index 0000000..665ff15 --- /dev/null +++ b/content/browser/renderer_host/input/touch_action_filter.cc @@ -0,0 +1,63 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/renderer_host/input/touch_action_filter.h" + +#include "third_party/WebKit/public/web/WebInputEvent.h" + +using blink::WebInputEvent; +using blink::WebGestureEvent; + +namespace content { + +TouchActionFilter::TouchActionFilter() : + drop_scroll_gesture_events_(false), + allowed_touch_action_(TOUCH_ACTION_AUTO) { +} + +bool TouchActionFilter::FilterGestureEvent( + const WebGestureEvent& gesture_event) { + // Filter for allowable touch actions first (eg. before the TouchEventQueue + // can decide to send a touch cancel event). + // TODO(rbyers): Add touch-action control over for pinch. crbug.com/247566. + switch(gesture_event.type) { + case WebInputEvent::GestureScrollBegin: + if (allowed_touch_action_ == TOUCH_ACTION_NONE) + drop_scroll_gesture_events_ = true; + // FALL THROUGH + case WebInputEvent::GestureScrollUpdate: + if (drop_scroll_gesture_events_) + return true; + break; + + case WebInputEvent::GestureScrollEnd: + case WebInputEvent::GestureFlingStart: + allowed_touch_action_ = content::TOUCH_ACTION_AUTO; + if (drop_scroll_gesture_events_) { + drop_scroll_gesture_events_ = false; + return true; + } + break; + + default: + // Gesture events unrelated to touch actions (panning/zooming) are left + // alone. + break; + } + + return false; +} + +void TouchActionFilter::OnSetTouchAction( + content::TouchAction touch_action) { + // For multiple fingers, we take the intersection of the touch actions for + // all fingers that have gone down during this action. + // TODO(rbyers): What exact multi-finger semantic do we want? This is left + // as implementation-defined in the pointer events specification. + // crbug.com/247566. + if (touch_action == content::TOUCH_ACTION_NONE) + allowed_touch_action_ = content::TOUCH_ACTION_NONE; +} + +} diff --git a/content/browser/renderer_host/input/touch_action_filter.h b/content/browser/renderer_host/input/touch_action_filter.h new file mode 100644 index 0000000..6485c45 --- /dev/null +++ b/content/browser/renderer_host/input/touch_action_filter.h @@ -0,0 +1,45 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_ACTION_FILTER_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_ACTION_FILTER_H_ + +#include "base/basictypes.h" +#include "content/common/content_export.h" +#include "content/common/input/touch_action.h" + +namespace blink { +class WebGestureEvent; +} + +namespace content { + +// The TouchActionFilter is responsible for filtering scroll and pinch gesture +// events according to the CSS touch-action values the renderer has sent for +// each touch point. +// For details see the touch-action design doc at http://goo.gl/KcKbxQ. +class CONTENT_EXPORT TouchActionFilter { +public: + TouchActionFilter(); + + // Returns true if the supplied gesture event should be dropped based on + // the current touch-action state. + bool FilterGestureEvent(const blink::WebGestureEvent& gesture_event); + + // Called when a set-touch-action message is received from the renderer + // for a touch start event that is currently in flight. + void OnSetTouchAction(content::TouchAction touch_action); + +private: + // Whether GestureScroll events should be discarded due to touch-action. + bool drop_scroll_gesture_events_; + + // What touch actions are currently permitted. + content::TouchAction allowed_touch_action_; + + DISALLOW_COPY_AND_ASSIGN(TouchActionFilter); +}; + +} +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_ACTION_FILTER_H_ diff --git a/content/browser/renderer_host/input/touch_action_filter_unittest.cc b/content/browser/renderer_host/input/touch_action_filter_unittest.cc new file mode 100644 index 0000000..a07ef09 --- /dev/null +++ b/content/browser/renderer_host/input/touch_action_filter_unittest.cc @@ -0,0 +1,100 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/renderer_host/input/touch_action_filter.h" +#include "content/common/input/synthetic_web_input_event_builders.h" +#include "content/port/browser/event_with_latency_info.h" +#include "content/port/common/input_event_ack_state.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" + +using blink::WebGestureEvent; +using blink::WebInputEvent; + +namespace content { + +TEST(TouchActionFilterTest, SimpleFilter) { + TouchActionFilter filter; + + const WebGestureEvent scroll_begin = SyntheticWebGestureEventBuilder::Build( + WebInputEvent::GestureScrollBegin, WebGestureEvent::Touchscreen); + const WebGestureEvent scroll_update = + SyntheticWebGestureEventBuilder::BuildScrollUpdate(0, 10, 0); + const WebGestureEvent scroll_end = SyntheticWebGestureEventBuilder::Build( + WebInputEvent::GestureScrollEnd, WebGestureEvent::Touchscreen); + const WebGestureEvent tap = SyntheticWebGestureEventBuilder::Build( + WebInputEvent::GestureTap, WebGestureEvent::Touchscreen); + + // No events filtered by default. + EXPECT_FALSE(filter.FilterGestureEvent(scroll_begin)); + EXPECT_FALSE(filter.FilterGestureEvent(scroll_update)); + EXPECT_FALSE(filter.FilterGestureEvent(scroll_end)); + EXPECT_FALSE(filter.FilterGestureEvent(tap)); + + // TOUCH_ACTION_AUTO doesn't cause any filtering. + filter.OnSetTouchAction(TOUCH_ACTION_AUTO); + EXPECT_FALSE(filter.FilterGestureEvent(scroll_begin)); + EXPECT_FALSE(filter.FilterGestureEvent(scroll_update)); + EXPECT_FALSE(filter.FilterGestureEvent(scroll_end)); + + // TOUCH_ACTION_NONE filters out all scroll events, but no other events. + filter.OnSetTouchAction(TOUCH_ACTION_NONE); + EXPECT_FALSE(filter.FilterGestureEvent(tap)); + EXPECT_TRUE(filter.FilterGestureEvent(scroll_begin)); + EXPECT_TRUE(filter.FilterGestureEvent(scroll_update)); + EXPECT_TRUE(filter.FilterGestureEvent(scroll_update)); + EXPECT_TRUE(filter.FilterGestureEvent(scroll_end)); + + // After the end of a gesture the state is reset. + EXPECT_FALSE(filter.FilterGestureEvent(scroll_begin)); + EXPECT_FALSE(filter.FilterGestureEvent(scroll_update)); + EXPECT_FALSE(filter.FilterGestureEvent(scroll_end)); + + // Setting touch action doesn't impact any in-progress gestures. + EXPECT_FALSE(filter.FilterGestureEvent(scroll_begin)); + filter.OnSetTouchAction(TOUCH_ACTION_NONE); + EXPECT_FALSE(filter.FilterGestureEvent(scroll_update)); + EXPECT_FALSE(filter.FilterGestureEvent(scroll_end)); + + // And the state is still cleared for the next gesture. + EXPECT_FALSE(filter.FilterGestureEvent(scroll_begin)); + EXPECT_FALSE(filter.FilterGestureEvent(scroll_end)); + + // Changing the touch action during a gesture has no effect. + filter.OnSetTouchAction(TOUCH_ACTION_NONE); + EXPECT_TRUE(filter.FilterGestureEvent(scroll_begin)); + filter.OnSetTouchAction(TOUCH_ACTION_AUTO); + EXPECT_TRUE(filter.FilterGestureEvent(scroll_update)); + EXPECT_TRUE(filter.FilterGestureEvent(scroll_update)); + EXPECT_TRUE(filter.FilterGestureEvent(scroll_end)); +} + +TEST(TouchActionFilterTest, MultiTouch) { + TouchActionFilter filter; + + const WebGestureEvent scroll_begin = SyntheticWebGestureEventBuilder::Build( + WebInputEvent::GestureScrollBegin, WebGestureEvent::Touchscreen); + const WebGestureEvent scroll_update = + SyntheticWebGestureEventBuilder::BuildScrollUpdate(0, 10, 0); + const WebGestureEvent scrollEnd = SyntheticWebGestureEventBuilder::Build( + WebInputEvent::GestureScrollEnd, WebGestureEvent::Touchscreen); + + // For multiple points, the intersection is what matters. + filter.OnSetTouchAction(TOUCH_ACTION_NONE); + filter.OnSetTouchAction(TOUCH_ACTION_AUTO); + EXPECT_TRUE(filter.FilterGestureEvent(scroll_begin)); + EXPECT_TRUE(filter.FilterGestureEvent(scroll_update)); + EXPECT_TRUE(filter.FilterGestureEvent(scroll_update)); + EXPECT_TRUE(filter.FilterGestureEvent(scrollEnd)); + + filter.OnSetTouchAction(TOUCH_ACTION_AUTO); + filter.OnSetTouchAction(TOUCH_ACTION_NONE); + filter.OnSetTouchAction(TOUCH_ACTION_AUTO); + EXPECT_TRUE(filter.FilterGestureEvent(scroll_begin)); + EXPECT_TRUE(filter.FilterGestureEvent(scroll_update)); + EXPECT_TRUE(filter.FilterGestureEvent(scroll_update)); + EXPECT_TRUE(filter.FilterGestureEvent(scrollEnd)); +} + +} // namespace content diff --git a/content/browser/renderer_host/input/touch_event_queue.cc b/content/browser/renderer_host/input/touch_event_queue.cc index ecc12056..4ea8be0 100644 --- a/content/browser/renderer_host/input/touch_event_queue.cc +++ b/content/browser/renderer_host/input/touch_event_queue.cc @@ -201,6 +201,16 @@ void TouchEventQueue::FlushQueue() { ui::LatencyInfo()); } +bool TouchEventQueue::IsPendingAckTouchStart() const { + DCHECK(!dispatching_touch_ack_); + if (touch_queue_.empty()) + return false; + + const blink::WebTouchEvent& event = + touch_queue_.front()->coalesced_event().event; + return (event.type == blink::WebInputEvent::TouchStart); +} + size_t TouchEventQueue::GetQueueSize() const { return touch_queue_.size(); } diff --git a/content/browser/renderer_host/input/touch_event_queue.h b/content/browser/renderer_host/input/touch_event_queue.h index a3053c4..d1daa15 100644 --- a/content/browser/renderer_host/input/touch_event_queue.h +++ b/content/browser/renderer_host/input/touch_event_queue.h @@ -62,6 +62,10 @@ class CONTENT_EXPORT TouchEventQueue { // events being sent to the renderer. void FlushQueue(); + // Returns whether the currently pending touch event (waiting ACK) is for + // a touch start event. + bool IsPendingAckTouchStart() const; + // Returns whether the event-queue is empty. bool empty() const WARN_UNUSED_RESULT { return touch_queue_.empty(); 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 3e75656..3780ae8 100644 --- a/content/browser/renderer_host/input/touch_event_queue_unittest.cc +++ b/content/browser/renderer_host/input/touch_event_queue_unittest.cc @@ -127,6 +127,10 @@ class TouchEventQueueTest : public testing::Test, return count; } + bool IsPendingAckTouchStart() const { + return queue_->IsPendingAckTouchStart(); + } + void Flush() { queue_->FlushQueue(); } @@ -836,4 +840,54 @@ TEST_F(TouchEventQueueTest, NoTouchOnScroll) { EXPECT_EQ(1U, GetAndResetAckedEventCount()); } +// Tests that IsTouchStartPendingAck works correctly. +TEST_F(TouchEventQueueTest, PendingStart) { + + EXPECT_FALSE(IsPendingAckTouchStart()); + + // Send the touchstart for one point (#1). + PressTouchPoint(1, 1); + SendTouchEvent(); + EXPECT_EQ(1U, queued_event_count()); + EXPECT_TRUE(IsPendingAckTouchStart()); + + // Send a touchmove for that point (#2). + MoveTouchPoint(0, 5, 5); + SendTouchEvent(); + EXPECT_EQ(2U, queued_event_count()); + EXPECT_TRUE(IsPendingAckTouchStart()); + + // Ack the touchstart (#1). + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(1U, queued_event_count()); + EXPECT_FALSE(IsPendingAckTouchStart()); + + // Send a touchstart for another point (#3). + PressTouchPoint(10, 10); + SendTouchEvent(); + EXPECT_EQ(2U, queued_event_count()); + EXPECT_FALSE(IsPendingAckTouchStart()); + + // Ack the touchmove (#2). + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(1U, queued_event_count()); + EXPECT_TRUE(IsPendingAckTouchStart()); + + // Send a touchstart for a third point (#4). + PressTouchPoint(15, 15); + SendTouchEvent(); + EXPECT_EQ(2U, queued_event_count()); + EXPECT_TRUE(IsPendingAckTouchStart()); + + // Ack the touchstart for the second point (#3). + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(1U, queued_event_count()); + EXPECT_TRUE(IsPendingAckTouchStart()); + + // Ack the touchstart for the third point (#4). + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(0U, queued_event_count()); + EXPECT_FALSE(IsPendingAckTouchStart()); +} + } // namespace content |