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