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. 4 5#include "base/auto_reset.h" 6#include "base/command_line.h" 7#include "base/run_loop.h" 8#include "content/browser/gpu/compositor_util.h" 9#include "content/browser/renderer_host/render_widget_host_impl.h" 10#include "content/browser/web_contents/web_contents_impl.h" 11#include "content/common/input/synthetic_web_input_event_builders.h" 12#include "content/common/input_messages.h" 13#include "content/public/browser/browser_message_filter.h" 14#include "content/public/browser/render_view_host.h" 15#include "content/public/browser/render_widget_host_view.h" 16#include "content/public/common/content_switches.h" 17#include "content/public/test/content_browser_test.h" 18#include "content/public/test/content_browser_test_utils.h" 19#include "content/shell/browser/shell.h" 20#include "third_party/WebKit/public/web/WebInputEvent.h" 21#include "ui/events/event_switches.h" 22#include "ui/events/latency_info.h" 23 24using blink::WebInputEvent; 25 26namespace { 27 28void GiveItSomeTime() { 29 base::RunLoop run_loop; 30 base::MessageLoop::current()->PostDelayedTask( 31 FROM_HERE, 32 run_loop.QuitClosure(), 33 base::TimeDelta::FromMilliseconds(10)); 34 run_loop.Run(); 35} 36 37const char kTouchEventDataURL[] = 38 "data:text/html;charset=utf-8," 39 "<body onload='setup();'>" 40 "<div id='first'></div><div id='second'></div><div id='third'></div>" 41 "<style>" 42 " #first {" 43 " position: absolute;" 44 " width: 100px;" 45 " height: 100px;" 46 " top: 0px;" 47 " left: 0px;" 48 " background-color: green;" 49 " -webkit-transform: translate3d(0, 0, 0);" 50 " }" 51 " #second {" 52 " position: absolute;" 53 " width: 100px;" 54 " height: 100px;" 55 " top: 0px;" 56 " left: 110px;" 57 " background-color: blue;" 58 " -webkit-transform: translate3d(0, 0, 0);" 59 " }" 60 " #third {" 61 " position: absolute;" 62 " width: 100px;" 63 " height: 100px;" 64 " top: 110px;" 65 " left: 0px;" 66 " background-color: yellow;" 67 " -webkit-transform: translate3d(0, 0, 0);" 68 " }" 69 "</style>" 70 "<script>" 71 " function setup() {" 72 " second.ontouchstart = function() {};" 73 " third.ontouchstart = function(e) {" 74 " e.preventDefault();" 75 " };" 76 " }" 77 "</script>"; 78 79} // namespace 80 81namespace content { 82 83class InputEventMessageFilter : public BrowserMessageFilter { 84 public: 85 InputEventMessageFilter() 86 : BrowserMessageFilter(InputMsgStart), 87 type_(WebInputEvent::Undefined), 88 state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {} 89 90 void WaitForAck(WebInputEvent::Type type) { 91 base::RunLoop run_loop; 92 base::AutoReset<base::Closure> reset_quit(&quit_, run_loop.QuitClosure()); 93 base::AutoReset<WebInputEvent::Type> reset_type(&type_, type); 94 run_loop.Run(); 95 } 96 97 InputEventAckState last_ack_state() const { return state_; } 98 99 protected: 100 virtual ~InputEventMessageFilter() {} 101 102 private: 103 void ReceivedEventAck(WebInputEvent::Type type, InputEventAckState state) { 104 if (type_ == type) { 105 state_ = state; 106 quit_.Run(); 107 } 108 } 109 110 // BrowserMessageFilter: 111 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { 112 if (message.type() == InputHostMsg_HandleInputEvent_ACK::ID) { 113 InputHostMsg_HandleInputEvent_ACK::Param params; 114 InputHostMsg_HandleInputEvent_ACK::Read(&message, ¶ms); 115 WebInputEvent::Type type = params.a.type; 116 InputEventAckState ack = params.a.state; 117 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 118 base::Bind(&InputEventMessageFilter::ReceivedEventAck, 119 this, type, ack)); 120 } 121 return false; 122 } 123 124 base::Closure quit_; 125 WebInputEvent::Type type_; 126 InputEventAckState state_; 127 128 DISALLOW_COPY_AND_ASSIGN(InputEventMessageFilter); 129}; 130 131class TouchInputBrowserTest : public ContentBrowserTest { 132 public: 133 TouchInputBrowserTest() {} 134 virtual ~TouchInputBrowserTest() {} 135 136 RenderWidgetHostImpl* GetWidgetHost() { 137 return RenderWidgetHostImpl::From(shell()->web_contents()-> 138 GetRenderViewHost()); 139 } 140 141 InputEventMessageFilter* filter() { return filter_.get(); } 142 143 protected: 144 void LoadURLAndAddFilter() { 145 const GURL data_url(kTouchEventDataURL); 146 NavigateToURL(shell(), data_url); 147 148 WebContentsImpl* web_contents = 149 static_cast<WebContentsImpl*>(shell()->web_contents()); 150 RenderWidgetHostImpl* host = 151 RenderWidgetHostImpl::From(web_contents->GetRenderViewHost()); 152 host->GetView()->SetSize(gfx::Size(400, 400)); 153 154 // The page is loaded in the renderer, wait for a new frame to arrive. 155 while (!host->ScheduleComposite()) 156 GiveItSomeTime(); 157 158 filter_ = new InputEventMessageFilter(); 159 host->GetProcess()->AddFilter(filter_.get()); 160 } 161 162 virtual void SetUpCommandLine(CommandLine* cmd) OVERRIDE { 163 cmd->AppendSwitchASCII(switches::kTouchEvents, 164 switches::kTouchEventsEnabled); 165 } 166 167 scoped_refptr<InputEventMessageFilter> filter_; 168}; 169 170#if defined(OS_MACOSX) 171// TODO(ccameron): Failing on mac: crbug.com/346363 172#define MAYBE_TouchNoHandler DISABLED_TouchNoHandler 173#else 174#define MAYBE_TouchNoHandler TouchNoHandler 175#endif 176IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MAYBE_TouchNoHandler) { 177 LoadURLAndAddFilter(); 178 SyntheticWebTouchEvent touch; 179 180 // A press on |first| should be acked with NO_CONSUMER_EXISTS since there is 181 // no touch-handler on it. 182 touch.PressPoint(25, 25); 183 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); 184 filter()->WaitForAck(WebInputEvent::TouchStart); 185 186 EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, 187 filter()->last_ack_state()); 188 189 // If a touch-press is acked with NO_CONSUMER_EXISTS, then subsequent 190 // touch-points don't need to be dispatched until the touch point is released. 191 touch.ReleasePoint(0); 192 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); 193 touch.ResetPoints(); 194} 195 196IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, TouchHandlerNoConsume) { 197 LoadURLAndAddFilter(); 198 SyntheticWebTouchEvent touch; 199 200 // Press on |second| should be acked with NOT_CONSUMED since there is a 201 // touch-handler on |second|, but it doesn't consume the event. 202 touch.PressPoint(125, 25); 203 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); 204 filter()->WaitForAck(WebInputEvent::TouchStart); 205 EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, filter()->last_ack_state()); 206 207 touch.ReleasePoint(0); 208 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); 209 filter()->WaitForAck(WebInputEvent::TouchEnd); 210 touch.ResetPoints(); 211} 212 213IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, TouchHandlerConsume) { 214 LoadURLAndAddFilter(); 215 SyntheticWebTouchEvent touch; 216 217 // Press on |third| should be acked with CONSUMED since the touch-handler on 218 // |third| consimes the event. 219 touch.PressPoint(25, 125); 220 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); 221 filter()->WaitForAck(WebInputEvent::TouchStart); 222 EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, filter()->last_ack_state()); 223 224 touch.ReleasePoint(0); 225 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); 226 filter()->WaitForAck(WebInputEvent::TouchEnd); 227} 228 229#if defined(OS_MACOSX) 230// TODO(ccameron): Failing on mac: crbug.com/346363 231#define MAYBE_MultiPointTouchPress DISABLED_MultiPointTouchPress 232#else 233#define MAYBE_MultiPointTouchPress MultiPointTouchPress 234#endif 235IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MAYBE_MultiPointTouchPress) { 236 LoadURLAndAddFilter(); 237 SyntheticWebTouchEvent touch; 238 239 // Press on |first|, which sould be acked with NO_CONSUMER_EXISTS. Then press 240 // on |third|. That point should be acked with CONSUMED. 241 touch.PressPoint(25, 25); 242 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); 243 filter()->WaitForAck(WebInputEvent::TouchStart); 244 EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, 245 filter()->last_ack_state()); 246 247 touch.PressPoint(25, 125); 248 GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); 249 filter()->WaitForAck(WebInputEvent::TouchStart); 250 EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, filter()->last_ack_state()); 251} 252 253} // namespace content 254