1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/auto_reset.h"
6 #include "base/command_line.h"
7 #include "base/run_loop.h"
8 #include "content/browser/renderer_host/render_widget_host_impl.h"
9 #include "content/browser/web_contents/web_contents_impl.h"
10 #include "content/common/input/synthetic_web_input_event_builders.h"
11 #include "content/common/input_messages.h"
12 #include "content/public/browser/browser_message_filter.h"
13 #include "content/public/browser/render_view_host.h"
14 #include "content/public/browser/render_widget_host_view.h"
15 #include "content/public/common/content_switches.h"
16 #include "content/public/test/content_browser_test.h"
17 #include "content/public/test/content_browser_test_utils.h"
18 #include "content/shell/browser/shell.h"
19 #include "third_party/WebKit/public/web/WebInputEvent.h"
20 #include "ui/events/event_switches.h"
21 #include "ui/events/latency_info.h"
23 using blink::WebInputEvent;
27 void GiveItSomeTime() {
28 base::RunLoop run_loop;
29 base::MessageLoop::current()->PostDelayedTask(
31 run_loop.QuitClosure(),
32 base::TimeDelta::FromMilliseconds(10));
36 const char kTouchEventDataURL[] =
37 "data:text/html;charset=utf-8,"
38 "<body onload='setup();'>"
39 "<div id='first'></div><div id='second'></div><div id='third'></div>"
42 " position: absolute;"
47 " background-color: green;"
48 " -webkit-transform: translate3d(0, 0, 0);"
51 " position: absolute;"
56 " background-color: blue;"
57 " -webkit-transform: translate3d(0, 0, 0);"
60 " position: absolute;"
65 " background-color: yellow;"
66 " -webkit-transform: translate3d(0, 0, 0);"
71 " second.ontouchstart = function() {};"
72 " third.ontouchstart = function(e) {"
73 " e.preventDefault();"
82 class InputEventMessageFilter : public BrowserMessageFilter {
84 InputEventMessageFilter()
85 : BrowserMessageFilter(InputMsgStart),
86 type_(WebInputEvent::Undefined),
87 state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {}
89 void WaitForAck(WebInputEvent::Type type) {
90 base::RunLoop run_loop;
91 base::AutoReset<base::Closure> reset_quit(&quit_, run_loop.QuitClosure());
92 base::AutoReset<WebInputEvent::Type> reset_type(&type_, type);
96 InputEventAckState last_ack_state() const { return state_; }
99 virtual ~InputEventMessageFilter() {}
102 void ReceivedEventAck(WebInputEvent::Type type, InputEventAckState state) {
109 // BrowserMessageFilter:
110 virtual bool OnMessageReceived(const IPC::Message& message,
111 bool* message_was_ok) OVERRIDE {
112 if (message.type() == InputHostMsg_HandleInputEvent_ACK::ID) {
113 ui::LatencyInfo latency;
114 WebInputEvent::Type type = WebInputEvent::Undefined;
115 InputEventAckState ack = INPUT_EVENT_ACK_STATE_UNKNOWN;
116 InputHostMsg_HandleInputEvent_ACK::Read(&message, &type, &ack, &latency);
117 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
118 base::Bind(&InputEventMessageFilter::ReceivedEventAck,
125 WebInputEvent::Type type_;
126 InputEventAckState state_;
128 DISALLOW_COPY_AND_ASSIGN(InputEventMessageFilter);
131 class TouchInputBrowserTest : public ContentBrowserTest,
132 public testing::WithParamInterface<std::string> {
134 TouchInputBrowserTest() {}
135 virtual ~TouchInputBrowserTest() {}
137 RenderWidgetHostImpl* GetWidgetHost() {
138 return RenderWidgetHostImpl::From(shell()->web_contents()->
139 GetRenderViewHost());
142 InputEventMessageFilter* filter() { return filter_.get(); }
145 void LoadURLAndAddFilter() {
146 const GURL data_url(kTouchEventDataURL);
147 NavigateToURL(shell(), data_url);
149 WebContentsImpl* web_contents =
150 static_cast<WebContentsImpl*>(shell()->web_contents());
151 RenderWidgetHostImpl* host =
152 RenderWidgetHostImpl::From(web_contents->GetRenderViewHost());
153 host->GetView()->SetSize(gfx::Size(400, 400));
155 // The page is loaded in the renderer, wait for a new frame to arrive.
156 while (!host->ScheduleComposite())
159 filter_ = new InputEventMessageFilter();
160 host->GetProcess()->AddFilter(filter_);
163 virtual void SetUpCommandLine(CommandLine* cmd) OVERRIDE {
164 cmd->AppendSwitchASCII(switches::kTouchEvents,
165 switches::kTouchEventsEnabled);
166 cmd->AppendSwitch(GetParam());
169 scoped_refptr<InputEventMessageFilter> filter_;
172 #if defined(OS_MACOSX)
173 // TODO(ccameron): Failing on mac: crbug.com/346363
174 #define MAYBE_TouchNoHandler DISABLED_TouchNoHandler
176 #define MAYBE_TouchNoHandler TouchNoHandler
178 IN_PROC_BROWSER_TEST_P(TouchInputBrowserTest, MAYBE_TouchNoHandler) {
179 LoadURLAndAddFilter();
180 SyntheticWebTouchEvent touch;
182 // A press on |first| should be acked with NO_CONSUMER_EXISTS since there is
183 // no touch-handler on it.
184 touch.PressPoint(25, 25);
185 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
186 filter()->WaitForAck(WebInputEvent::TouchStart);
188 if (GetParam() == std::string(switches::kEnableThreadedCompositing)) {
189 EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS,
190 filter()->last_ack_state());
192 // http://crbug.com/326232: This should be NO_CONSUMER_EXISTS once
193 // WebViewImpl::hasTouchEventHandlersAt() is implemented.
194 EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, filter()->last_ack_state());
197 // If a touch-press is acked with NO_CONSUMER_EXISTS, then subsequent
198 // touch-points don't need to be dispatched until the touch point is released.
199 touch.ReleasePoint(0);
200 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
204 IN_PROC_BROWSER_TEST_P(TouchInputBrowserTest, TouchHandlerNoConsume) {
205 LoadURLAndAddFilter();
206 SyntheticWebTouchEvent touch;
208 // Press on |second| should be acked with NOT_CONSUMED since there is a
209 // touch-handler on |second|, but it doesn't consume the event.
210 touch.PressPoint(125, 25);
211 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
212 filter()->WaitForAck(WebInputEvent::TouchStart);
213 EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, filter()->last_ack_state());
215 touch.ReleasePoint(0);
216 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
217 filter()->WaitForAck(WebInputEvent::TouchEnd);
221 IN_PROC_BROWSER_TEST_P(TouchInputBrowserTest, TouchHandlerConsume) {
222 LoadURLAndAddFilter();
223 SyntheticWebTouchEvent touch;
225 // Press on |third| should be acked with CONSUMED since the touch-handler on
226 // |third| consimes the event.
227 touch.PressPoint(25, 125);
228 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
229 filter()->WaitForAck(WebInputEvent::TouchStart);
230 EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, filter()->last_ack_state());
232 touch.ReleasePoint(0);
233 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
234 filter()->WaitForAck(WebInputEvent::TouchEnd);
237 #if defined(OS_MACOSX)
238 // TODO(ccameron): Failing on mac: crbug.com/346363
239 #define MAYBE_MultiPointTouchPress DISABLED_MultiPointTouchPress
241 #define MAYBE_MultiPointTouchPress MultiPointTouchPress
243 IN_PROC_BROWSER_TEST_P(TouchInputBrowserTest, MAYBE_MultiPointTouchPress) {
244 LoadURLAndAddFilter();
245 SyntheticWebTouchEvent touch;
247 // Press on |first|, which sould be acked with NO_CONSUMER_EXISTS. Then press
248 // on |third|. That point should be acked with CONSUMED.
249 touch.PressPoint(25, 25);
250 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
251 filter()->WaitForAck(WebInputEvent::TouchStart);
252 if (GetParam() == std::string(switches::kEnableThreadedCompositing)) {
253 EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS,
254 filter()->last_ack_state());
256 // http://crbug.com/326232: This should be NO_CONSUMER_EXISTS once
257 // WebViewImpl::hasTouchEventHandlersAt() is implemented.
258 EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, filter()->last_ack_state());
261 touch.PressPoint(25, 125);
262 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
263 filter()->WaitForAck(WebInputEvent::TouchStart);
264 EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, filter()->last_ack_state());
267 // Threaded compositing is always enabled on Aura and Mac.
268 #if !defined(USE_AURA) && !defined(OS_MACOSX)
269 INSTANTIATE_TEST_CASE_P(WithoutInputHandlerProxy, TouchInputBrowserTest,
270 ::testing::Values(std::string(switches::kDisableThreadedCompositing)));
273 #if defined(USE_AURA) || defined(OS_MACOSX)
274 INSTANTIATE_TEST_CASE_P(WithInputHandlerProxy, TouchInputBrowserTest,
275 ::testing::Values(std::string(switches::kEnableThreadedCompositing)));
278 } // namespace content