summaryrefslogtreecommitdiffstats
path: root/content/browser/renderer_host/input
diff options
context:
space:
mode:
authorrbyers@chromium.org <rbyers@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-10 00:35:51 +0000
committerrbyers@chromium.org <rbyers@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-10 00:35:51 +0000
commit5d0bbdfa9c671b9e7b537693f27b62f2fa81ba0e (patch)
tree1af55c52b355916e7f8c9b41e4046996e679c63f /content/browser/renderer_host/input
parentf96555c2b2e2590476bae50683a168b101c137f4 (diff)
downloadchromium_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')
-rw-r--r--content/browser/renderer_host/input/input_router_impl.cc14
-rw-r--r--content/browser/renderer_host/input/input_router_impl.h3
-rw-r--r--content/browser/renderer_host/input/touch_action_filter.cc63
-rw-r--r--content/browser/renderer_host/input/touch_action_filter.h45
-rw-r--r--content/browser/renderer_host/input/touch_action_filter_unittest.cc100
-rw-r--r--content/browser/renderer_host/input/touch_event_queue.cc10
-rw-r--r--content/browser/renderer_host/input/touch_event_queue.h4
-rw-r--r--content/browser/renderer_host/input/touch_event_queue_unittest.cc54
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