OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 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 "content/renderer/gpu/input_handler_proxy.h" | |
6 | |
7 #include "base/debug/trace_event.h" | |
8 #include "base/logging.h" | |
9 #include "content/renderer/gpu/input_handler_proxy_client.h" | |
10 #include "third_party/WebKit/Source/Platform/chromium/public/Platform.h" | |
11 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" | |
12 | |
13 using WebKit::WebFloatPoint; | |
14 using WebKit::WebFloatSize; | |
15 using WebKit::WebGestureEvent; | |
16 using WebKit::WebInputEvent; | |
17 using WebKit::WebMouseWheelEvent; | |
18 using WebKit::WebPoint; | |
19 using WebKit::WebScrollbar; | |
20 using WebKit::WebTouchEvent; | |
21 | |
22 namespace content { | |
23 | |
24 InputHandlerProxy::InputHandlerProxy(cc::InputHandler* input_handler) | |
25 : client_(NULL), | |
26 input_handler_(input_handler), | |
27 #ifndef NDEBUG | |
28 expect_scroll_update_end_(false), | |
29 expect_pinch_update_end_(false), | |
30 #endif | |
31 gesture_scroll_on_impl_thread_(false), | |
32 gesture_pinch_on_impl_thread_(false), | |
33 fling_may_be_active_on_main_thread_(false) { | |
34 input_handler_->BindToClient(this); | |
35 } | |
36 | |
37 InputHandlerProxy::~InputHandlerProxy() {} | |
38 | |
39 void InputHandlerProxy::WillShutdown() { | |
40 if (client_) | |
piman
2013/05/15 21:51:03
maybe DCHECK(client_)? Since WillShutdown is what
| |
41 client_->WillShutdown(); | |
42 } | |
43 | |
44 void InputHandlerProxy::SetClient(InputHandlerProxyClient* client) { | |
45 DCHECK(!client_); | |
46 client_ = client; | |
47 } | |
48 | |
49 void InputHandlerProxy::HandleInputEvent(const WebInputEvent& event) { | |
50 DCHECK(client_); | |
51 | |
52 InputHandlerProxy::EventDisposition disposition = | |
53 HandleInputEventInternal(event); | |
54 switch (disposition) { | |
55 case DidHandle: | |
56 client_->DidHandleInputEvent(); | |
57 break; | |
58 case DidNotHandle: | |
59 client_->DidNotHandleInputEvent(true /* send_to_widget */); | |
60 break; | |
61 case DropEvent: | |
62 client_->DidNotHandleInputEvent(false /* send_to_widget */); | |
63 break; | |
64 } | |
65 if (event.modifiers & WebInputEvent::IsLastInputEventForCurrentVSync) { | |
66 input_handler_->DidReceiveLastInputEventForVSync( | |
67 base::TimeTicks::FromInternalValue(event.timeStampSeconds * | |
68 base::Time::kMicrosecondsPerSecond)); | |
69 } | |
70 } | |
71 | |
72 InputHandlerProxy::EventDisposition | |
73 InputHandlerProxy::HandleInputEventInternal(const WebInputEvent& event) { | |
74 if (event.type == WebInputEvent::MouseWheel) { | |
75 const WebMouseWheelEvent& wheel_event = | |
76 *static_cast<const WebMouseWheelEvent*>(&event); | |
77 if (wheel_event.scrollByPage) { | |
78 // TODO(jamesr): We don't properly handle scroll by page in the compositor | |
79 // thread, so punt it to the main thread. http://crbug.com/236639 | |
80 return DidNotHandle; | |
81 } | |
82 cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin( | |
83 gfx::Point(wheel_event.x, wheel_event.y), cc::InputHandler::Wheel); | |
84 switch (scroll_status) { | |
85 case cc::InputHandler::ScrollStarted: { | |
86 TRACE_EVENT_INSTANT2( | |
87 "renderer", | |
88 "InputHandlerProxy::handle_input wheel scroll", | |
89 TRACE_EVENT_SCOPE_THREAD, | |
90 "deltaX", | |
91 -wheel_event.deltaX, | |
92 "deltaY", | |
93 -wheel_event.deltaY); | |
94 bool did_scroll = input_handler_->ScrollBy( | |
95 gfx::Point(wheel_event.x, wheel_event.y), | |
96 gfx::Vector2dF(-wheel_event.deltaX, -wheel_event.deltaY)); | |
97 input_handler_->ScrollEnd(); | |
98 return did_scroll ? DidHandle : DropEvent; | |
99 } | |
100 case cc::InputHandler::ScrollIgnored: | |
101 // TODO(jamesr): This should be DropEvent, but in cases where we fail to | |
102 // properly sync scrollability it's safer to send the | |
103 // event to the main thread. Change back to DropEvent once we have | |
104 // synchronization bugs sorted out. | |
105 return DidNotHandle; | |
106 case cc::InputHandler::ScrollOnMainThread: | |
107 return DidNotHandle; | |
108 } | |
109 } else if (event.type == WebInputEvent::GestureScrollBegin) { | |
110 DCHECK(!gesture_scroll_on_impl_thread_); | |
111 #ifndef NDEBUG | |
112 DCHECK(!expect_scroll_update_end_); | |
113 expect_scroll_update_end_ = true; | |
114 #endif | |
115 const WebGestureEvent& gesture_event = | |
116 *static_cast<const WebGestureEvent*>(&event); | |
117 cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin( | |
118 gfx::Point(gesture_event.x, gesture_event.y), | |
119 cc::InputHandler::Gesture); | |
120 switch (scroll_status) { | |
121 case cc::InputHandler::ScrollStarted: | |
122 gesture_scroll_on_impl_thread_ = true; | |
123 return DidHandle; | |
124 case cc::InputHandler::ScrollOnMainThread: | |
125 return DidNotHandle; | |
126 case cc::InputHandler::ScrollIgnored: | |
127 return DropEvent; | |
128 } | |
129 } else if (event.type == WebInputEvent::GestureScrollUpdate) { | |
130 #ifndef NDEBUG | |
131 DCHECK(expect_scroll_update_end_); | |
132 #endif | |
133 | |
134 if (!gesture_scroll_on_impl_thread_ && !gesture_pinch_on_impl_thread_) | |
135 return DidNotHandle; | |
136 | |
137 const WebGestureEvent& gesture_event = | |
138 *static_cast<const WebGestureEvent*>(&event); | |
139 bool did_scroll = input_handler_->ScrollBy( | |
140 gfx::Point(gesture_event.x, gesture_event.y), | |
141 gfx::Vector2dF(-gesture_event.data.scrollUpdate.deltaX, | |
142 -gesture_event.data.scrollUpdate.deltaY)); | |
143 return did_scroll ? DidHandle : DropEvent; | |
144 } else if (event.type == WebInputEvent::GestureScrollEnd) { | |
145 #ifndef NDEBUG | |
146 DCHECK(expect_scroll_update_end_); | |
147 expect_scroll_update_end_ = false; | |
148 #endif | |
149 if (!gesture_scroll_on_impl_thread_) | |
150 return DidNotHandle; | |
151 | |
152 input_handler_->ScrollEnd(); | |
153 gesture_scroll_on_impl_thread_ = false; | |
154 return DidHandle; | |
155 } else if (event.type == WebInputEvent::GesturePinchBegin) { | |
156 #ifndef NDEBUG | |
157 DCHECK(!expect_pinch_update_end_); | |
158 expect_pinch_update_end_ = true; | |
159 #endif | |
160 input_handler_->PinchGestureBegin(); | |
161 gesture_pinch_on_impl_thread_ = true; | |
162 return DidHandle; | |
163 } else if (event.type == WebInputEvent::GesturePinchEnd) { | |
164 #ifndef NDEBUG | |
165 DCHECK(expect_pinch_update_end_); | |
166 expect_pinch_update_end_ = false; | |
167 #endif | |
168 gesture_pinch_on_impl_thread_ = false; | |
169 input_handler_->PinchGestureEnd(); | |
170 return DidHandle; | |
171 } else if (event.type == WebInputEvent::GesturePinchUpdate) { | |
172 #ifndef NDEBUG | |
173 DCHECK(expect_pinch_update_end_); | |
174 #endif | |
175 const WebGestureEvent& gesture_event = | |
176 *static_cast<const WebGestureEvent*>(&event); | |
177 input_handler_->PinchGestureUpdate( | |
178 gesture_event.data.pinchUpdate.scale, | |
179 gfx::Point(gesture_event.x, gesture_event.y)); | |
180 return DidHandle; | |
181 } else if (event.type == WebInputEvent::GestureFlingStart) { | |
182 const WebGestureEvent& gesture_event = | |
183 *static_cast<const WebGestureEvent*>(&event); | |
184 return HandleGestureFling(gesture_event); | |
185 } else if (event.type == WebInputEvent::GestureFlingCancel) { | |
186 if (CancelCurrentFling()) | |
187 return DidHandle; | |
188 else if (!fling_may_be_active_on_main_thread_) | |
189 return DropEvent; | |
190 } else if (event.type == WebInputEvent::TouchStart) { | |
191 const WebTouchEvent& touch_event = | |
192 *static_cast<const WebTouchEvent*>(&event); | |
193 if (!input_handler_->HaveTouchEventHandlersAt(touch_event.touches[0] | |
194 .position)) | |
195 return DropEvent; | |
196 } else if (WebInputEvent::isKeyboardEventType(event.type)) { | |
197 CancelCurrentFling(); | |
198 } | |
199 | |
200 return DidNotHandle; | |
201 } | |
202 | |
203 InputHandlerProxy::EventDisposition | |
204 InputHandlerProxy::HandleGestureFling( | |
205 const WebGestureEvent& gesture_event) { | |
206 cc::InputHandler::ScrollStatus scroll_status; | |
207 | |
208 if (gesture_event.sourceDevice == WebGestureEvent::Touchpad) { | |
209 scroll_status = input_handler_->ScrollBegin( | |
210 gfx::Point(gesture_event.x, gesture_event.y), | |
211 cc::InputHandler::NonBubblingGesture); | |
212 } else { | |
213 if (!gesture_scroll_on_impl_thread_) | |
214 scroll_status = cc::InputHandler::ScrollOnMainThread; | |
215 else | |
216 scroll_status = input_handler_->FlingScrollBegin(); | |
217 } | |
218 | |
219 #ifndef NDEBUG | |
220 expect_scroll_update_end_ = false; | |
221 #endif | |
222 | |
223 switch (scroll_status) { | |
224 case cc::InputHandler::ScrollStarted: { | |
225 if (gesture_event.sourceDevice == WebGestureEvent::Touchpad) | |
226 input_handler_->ScrollEnd(); | |
227 | |
228 fling_curve_.reset(client_->CreateFlingAnimationCurve( | |
229 gesture_event.sourceDevice, | |
230 WebFloatPoint(gesture_event.data.flingStart.velocityX, | |
231 gesture_event.data.flingStart.velocityY), | |
232 WebKit::WebSize())); | |
233 TRACE_EVENT_ASYNC_BEGIN0( | |
234 "renderer", | |
235 "InputHandlerProxy::HandleGestureFling::started", | |
236 this); | |
237 fling_parameters_.delta = | |
238 WebFloatPoint(gesture_event.data.flingStart.velocityX, | |
239 gesture_event.data.flingStart.velocityY); | |
240 fling_parameters_.point = WebPoint(gesture_event.x, gesture_event.y); | |
241 fling_parameters_.globalPoint = | |
242 WebPoint(gesture_event.globalX, gesture_event.globalY); | |
243 fling_parameters_.modifiers = gesture_event.modifiers; | |
244 fling_parameters_.sourceDevice = gesture_event.sourceDevice; | |
245 input_handler_->ScheduleAnimation(); | |
246 return DidHandle; | |
247 } | |
248 case cc::InputHandler::ScrollOnMainThread: { | |
249 TRACE_EVENT_INSTANT0("renderer", | |
250 "InputHandlerProxy::HandleGestureFling::" | |
251 "scroll_on_main_thread", | |
252 TRACE_EVENT_SCOPE_THREAD); | |
253 fling_may_be_active_on_main_thread_ = true; | |
254 return DidNotHandle; | |
255 } | |
256 case cc::InputHandler::ScrollIgnored: { | |
257 TRACE_EVENT_INSTANT0( | |
258 "renderer", | |
259 "InputHandlerProxy::HandleGestureFling::ignored", | |
260 TRACE_EVENT_SCOPE_THREAD); | |
261 if (gesture_event.sourceDevice == WebGestureEvent::Touchpad) { | |
262 // We still pass the curve to the main thread if there's nothing | |
263 // scrollable, in case something | |
264 // registers a handler before the curve is over. | |
265 return DidNotHandle; | |
266 } | |
267 return DropEvent; | |
268 } | |
269 } | |
270 return DidNotHandle; | |
271 } | |
272 | |
273 void InputHandlerProxy::Animate(base::TimeTicks time) { | |
274 if (!fling_curve_) | |
275 return; | |
276 | |
277 double monotonic_time_sec = (time - base::TimeTicks()).InSecondsF(); | |
278 if (!fling_parameters_.startTime) { | |
279 fling_parameters_.startTime = monotonic_time_sec; | |
280 input_handler_->ScheduleAnimation(); | |
281 return; | |
282 } | |
283 | |
284 if (fling_curve_->apply(monotonic_time_sec - fling_parameters_.startTime, | |
285 this)) { | |
286 input_handler_->ScheduleAnimation(); | |
287 } else { | |
288 TRACE_EVENT_INSTANT0("renderer", | |
289 "InputHandlerProxy::animate::flingOver", | |
290 TRACE_EVENT_SCOPE_THREAD); | |
291 CancelCurrentFling(); | |
292 } | |
293 } | |
294 | |
295 void InputHandlerProxy::MainThreadHasStoppedFlinging() { | |
296 fling_may_be_active_on_main_thread_ = false; | |
297 } | |
298 | |
299 bool InputHandlerProxy::CancelCurrentFling() { | |
300 bool had_fling_animation = fling_curve_; | |
301 if (had_fling_animation && | |
302 fling_parameters_.sourceDevice == WebGestureEvent::Touchscreen) { | |
303 input_handler_->ScrollEnd(); | |
304 TRACE_EVENT_ASYNC_END0( | |
305 "renderer", | |
306 "InputHandlerProxy::HandleGestureFling::started", | |
307 this); | |
308 } | |
309 | |
310 TRACE_EVENT_INSTANT1("renderer", | |
311 "InputHandlerProxy::CancelCurrentFling", | |
312 TRACE_EVENT_SCOPE_THREAD, | |
313 "had_fling_animation", | |
314 had_fling_animation); | |
315 fling_curve_.reset(); | |
316 gesture_scroll_on_impl_thread_ = false; | |
317 fling_parameters_ = WebKit::WebActiveWheelFlingParameters(); | |
318 return had_fling_animation; | |
319 } | |
320 | |
321 bool InputHandlerProxy::TouchpadFlingScroll( | |
322 const WebFloatSize& increment) { | |
323 WebMouseWheelEvent synthetic_wheel; | |
324 synthetic_wheel.type = WebInputEvent::MouseWheel; | |
325 synthetic_wheel.deltaX = increment.width; | |
326 synthetic_wheel.deltaY = increment.height; | |
327 synthetic_wheel.hasPreciseScrollingDeltas = true; | |
328 synthetic_wheel.x = fling_parameters_.point.x; | |
329 synthetic_wheel.y = fling_parameters_.point.y; | |
330 synthetic_wheel.globalX = fling_parameters_.globalPoint.x; | |
331 synthetic_wheel.globalY = fling_parameters_.globalPoint.y; | |
332 synthetic_wheel.modifiers = fling_parameters_.modifiers; | |
333 | |
334 InputHandlerProxy::EventDisposition disposition = | |
335 HandleInputEventInternal(synthetic_wheel); | |
336 switch (disposition) { | |
337 case DidHandle: | |
338 return true; | |
339 case DropEvent: | |
340 break; | |
341 case DidNotHandle: | |
342 TRACE_EVENT_INSTANT0("renderer", | |
343 "InputHandlerProxy::scrollBy::AbortFling", | |
344 TRACE_EVENT_SCOPE_THREAD); | |
345 // If we got a DidNotHandle, that means we need to deliver wheels on the | |
346 // main thread. In this case we need to schedule a commit and transfer the | |
347 // fling curve over to the main thread and run the rest of the wheels from | |
348 // there. This can happen when flinging a page that contains a scrollable | |
349 // subarea that we can't scroll on the thread if the fling starts outside | |
350 // the subarea but then is flung "under" the pointer. | |
351 client_->TransferActiveWheelFlingAnimation(fling_parameters_); | |
352 fling_may_be_active_on_main_thread_ = true; | |
353 CancelCurrentFling(); | |
354 break; | |
355 } | |
356 | |
357 return false; | |
358 } | |
359 | |
360 static gfx::Vector2dF ToClientScrollIncrement(const WebFloatSize& increment) { | |
361 return gfx::Vector2dF(-increment.width, -increment.height); | |
362 } | |
363 | |
364 void InputHandlerProxy::scrollBy(const WebFloatSize& increment) { | |
365 if (increment == WebFloatSize()) | |
366 return; | |
367 | |
368 TRACE_EVENT2("renderer", | |
369 "InputHandlerProxy::scrollBy", | |
370 "x", | |
371 increment.width, | |
372 "y", | |
373 increment.height); | |
374 | |
375 bool did_scroll = false; | |
376 | |
377 switch (fling_parameters_.sourceDevice) { | |
378 case WebGestureEvent::Touchpad: | |
379 did_scroll = TouchpadFlingScroll(increment); | |
380 break; | |
381 case WebGestureEvent::Touchscreen: | |
382 did_scroll = input_handler_->ScrollBy(fling_parameters_.point, | |
383 ToClientScrollIncrement(increment)); | |
384 break; | |
385 } | |
386 | |
387 if (did_scroll) { | |
388 fling_parameters_.cumulativeScroll.width += increment.width; | |
389 fling_parameters_.cumulativeScroll.height += increment.height; | |
390 } | |
391 } | |
392 | |
393 void InputHandlerProxy::notifyCurrentFlingVelocity( | |
394 const WebFloatSize& velocity) { | |
395 TRACE_EVENT2("renderer", | |
396 "InputHandlerProxy::notifyCurrentFlingVelocity", | |
397 "vx", | |
398 velocity.width, | |
399 "vy", | |
400 velocity.height); | |
401 input_handler_->NotifyCurrentFlingVelocity(ToClientScrollIncrement(velocity)); | |
402 } | |
403 | |
404 } // namespace content | |
OLD | NEW |