OLD | NEW |
| (Empty) |
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 "content/common/input/web_input_event_traits.h" | |
6 | |
7 #include <bitset> | |
8 #include <limits> | |
9 | |
10 #include "base/logging.h" | |
11 #include "base/strings/stringprintf.h" | |
12 | |
13 using base::StringAppendF; | |
14 using base::SStringPrintf; | |
15 using blink::WebGestureEvent; | |
16 using blink::WebInputEvent; | |
17 using blink::WebKeyboardEvent; | |
18 using blink::WebMouseEvent; | |
19 using blink::WebMouseWheelEvent; | |
20 using blink::WebTouchEvent; | |
21 using blink::WebTouchPoint; | |
22 using std::numeric_limits; | |
23 | |
24 namespace content { | |
25 namespace { | |
26 | |
27 const int kInvalidTouchIndex = -1; | |
28 | |
29 void ApppendEventDetails(const WebKeyboardEvent& event, std::string* result) { | |
30 StringAppendF(result, | |
31 "{\n WinCode: %d\n NativeCode: %d\n IsSystem: %d\n" | |
32 " Text: %s\n UnmodifiedText: %s\n KeyIdentifier: %s\n}", | |
33 event.windowsKeyCode, | |
34 event.nativeKeyCode, | |
35 event.isSystemKey, | |
36 reinterpret_cast<const char*>(event.text), | |
37 reinterpret_cast<const char*>(event.unmodifiedText), | |
38 reinterpret_cast<const char*>(event.keyIdentifier)); | |
39 } | |
40 | |
41 void ApppendEventDetails(const WebMouseEvent& event, std::string* result) { | |
42 StringAppendF(result, | |
43 "{\n Button: %d\n Pos: (%d, %d)\n WindowPos: (%d, %d)\n" | |
44 " GlobalPos: (%d, %d)\n Movement: (%d, %d)\n Clicks: %d\n}", | |
45 event.button, | |
46 event.x, | |
47 event.y, | |
48 event.windowX, | |
49 event.windowY, | |
50 event.globalX, | |
51 event.globalY, | |
52 event.movementX, | |
53 event.movementY, | |
54 event.clickCount); | |
55 } | |
56 | |
57 void ApppendEventDetails(const WebMouseWheelEvent& event, std::string* result) { | |
58 StringAppendF(result, | |
59 "{\n Delta: (%f, %f)\n WheelTicks: (%f, %f)\n Accel: (%f, %f)\n" | |
60 " ScrollByPage: %d\n HasPreciseScrollingDeltas: %d\n" | |
61 " Phase: (%d, %d)\n CanRubberband: (%d, %d)\n CanScroll: %d\n}", | |
62 event.deltaX, | |
63 event.deltaY, | |
64 event.wheelTicksX, | |
65 event.wheelTicksY, | |
66 event.accelerationRatioX, | |
67 event.accelerationRatioY, | |
68 event.scrollByPage, | |
69 event.hasPreciseScrollingDeltas, | |
70 event.phase, | |
71 event.momentumPhase, | |
72 event.canRubberbandLeft, | |
73 event.canRubberbandRight, | |
74 event.canScroll); | |
75 } | |
76 | |
77 void ApppendEventDetails(const WebGestureEvent& event, std::string* result) { | |
78 StringAppendF(result, | |
79 "{\n Pos: (%d, %d)\n GlobalPos: (%d, %d)\n SourceDevice: %d\n" | |
80 " RawData: (%f, %f, %f, %f, %d)\n}", | |
81 event.x, | |
82 event.y, | |
83 event.globalX, | |
84 event.globalY, | |
85 event.sourceDevice, | |
86 event.data.scrollUpdate.deltaX, | |
87 event.data.scrollUpdate.deltaY, | |
88 event.data.scrollUpdate.velocityX, | |
89 event.data.scrollUpdate.velocityY, | |
90 event.data.scrollUpdate.previousUpdateInSequencePrevented); | |
91 } | |
92 | |
93 void ApppendTouchPointDetails(const WebTouchPoint& point, std::string* result) { | |
94 StringAppendF(result, | |
95 " (ID: %d, State: %d, ScreenPos: (%f, %f), Pos: (%f, %f)," | |
96 " Radius: (%f, %f), Rot: %f, Force: %f," | |
97 " Tilt: (%d, %d)),\n", | |
98 point.id, | |
99 point.state, | |
100 point.screenPosition.x, | |
101 point.screenPosition.y, | |
102 point.position.x, | |
103 point.position.y, | |
104 point.radiusX, | |
105 point.radiusY, | |
106 point.rotationAngle, | |
107 point.force, | |
108 point.tiltX, | |
109 point.tiltY); | |
110 } | |
111 | |
112 void ApppendEventDetails(const WebTouchEvent& event, std::string* result) { | |
113 StringAppendF(result, | |
114 "{\n Touches: %u, Cancelable: %d, CausesScrolling: %d," | |
115 " uniqueTouchEventId: %u\n[\n", | |
116 event.touchesLength, event.cancelable, | |
117 event.causesScrollingIfUncanceled, event.uniqueTouchEventId); | |
118 for (unsigned i = 0; i < event.touchesLength; ++i) | |
119 ApppendTouchPointDetails(event.touches[i], result); | |
120 result->append(" ]\n}"); | |
121 } | |
122 | |
123 bool CanCoalesce(const WebKeyboardEvent& event_to_coalesce, | |
124 const WebKeyboardEvent& event) { | |
125 return false; | |
126 } | |
127 | |
128 void Coalesce(const WebKeyboardEvent& event_to_coalesce, | |
129 WebKeyboardEvent* event) { | |
130 DCHECK(CanCoalesce(event_to_coalesce, *event)); | |
131 } | |
132 | |
133 bool CanCoalesce(const WebMouseEvent& event_to_coalesce, | |
134 const WebMouseEvent& event) { | |
135 return event.type == event_to_coalesce.type && | |
136 event.type == WebInputEvent::MouseMove; | |
137 } | |
138 | |
139 void Coalesce(const WebMouseEvent& event_to_coalesce, WebMouseEvent* event) { | |
140 DCHECK(CanCoalesce(event_to_coalesce, *event)); | |
141 // Accumulate movement deltas. | |
142 int x = event->movementX; | |
143 int y = event->movementY; | |
144 *event = event_to_coalesce; | |
145 event->movementX += x; | |
146 event->movementY += y; | |
147 } | |
148 | |
149 bool CanCoalesce(const WebMouseWheelEvent& event_to_coalesce, | |
150 const WebMouseWheelEvent& event) { | |
151 return event.modifiers == event_to_coalesce.modifiers && | |
152 event.scrollByPage == event_to_coalesce.scrollByPage && | |
153 event.phase == event_to_coalesce.phase && | |
154 event.momentumPhase == event_to_coalesce.momentumPhase && | |
155 event.hasPreciseScrollingDeltas == | |
156 event_to_coalesce.hasPreciseScrollingDeltas && | |
157 event.canScroll == event_to_coalesce.canScroll; | |
158 } | |
159 | |
160 float GetUnacceleratedDelta(float accelerated_delta, float acceleration_ratio) { | |
161 return accelerated_delta * acceleration_ratio; | |
162 } | |
163 | |
164 float GetAccelerationRatio(float accelerated_delta, float unaccelerated_delta) { | |
165 if (unaccelerated_delta == 0.f || accelerated_delta == 0.f) | |
166 return 1.f; | |
167 return unaccelerated_delta / accelerated_delta; | |
168 } | |
169 | |
170 void Coalesce(const WebMouseWheelEvent& event_to_coalesce, | |
171 WebMouseWheelEvent* event) { | |
172 DCHECK(CanCoalesce(event_to_coalesce, *event)); | |
173 float unaccelerated_x = | |
174 GetUnacceleratedDelta(event->deltaX, | |
175 event->accelerationRatioX) + | |
176 GetUnacceleratedDelta(event_to_coalesce.deltaX, | |
177 event_to_coalesce.accelerationRatioX); | |
178 float unaccelerated_y = | |
179 GetUnacceleratedDelta(event->deltaY, | |
180 event->accelerationRatioY) + | |
181 GetUnacceleratedDelta(event_to_coalesce.deltaY, | |
182 event_to_coalesce.accelerationRatioY); | |
183 event->deltaX += event_to_coalesce.deltaX; | |
184 event->deltaY += event_to_coalesce.deltaY; | |
185 event->wheelTicksX += event_to_coalesce.wheelTicksX; | |
186 event->wheelTicksY += event_to_coalesce.wheelTicksY; | |
187 event->accelerationRatioX = | |
188 GetAccelerationRatio(event->deltaX, unaccelerated_x); | |
189 event->accelerationRatioY = | |
190 GetAccelerationRatio(event->deltaY, unaccelerated_y); | |
191 } | |
192 | |
193 // Returns |kInvalidTouchIndex| iff |event| lacks a touch with an ID of |id|. | |
194 int GetIndexOfTouchID(const WebTouchEvent& event, int id) { | |
195 for (unsigned i = 0; i < event.touchesLength; ++i) { | |
196 if (event.touches[i].id == id) | |
197 return i; | |
198 } | |
199 return kInvalidTouchIndex; | |
200 } | |
201 | |
202 bool CanCoalesce(const WebTouchEvent& event_to_coalesce, | |
203 const WebTouchEvent& event) { | |
204 if (event.type != event_to_coalesce.type || | |
205 event.type != WebInputEvent::TouchMove || | |
206 event.modifiers != event_to_coalesce.modifiers || | |
207 event.touchesLength != event_to_coalesce.touchesLength || | |
208 event.touchesLength > WebTouchEvent::touchesLengthCap) | |
209 return false; | |
210 | |
211 static_assert(WebTouchEvent::touchesLengthCap <= sizeof(int32_t) * 8U, | |
212 "suboptimal touchesLengthCap size"); | |
213 // Ensure that we have a 1-to-1 mapping of pointer ids between touches. | |
214 std::bitset<WebTouchEvent::touchesLengthCap> unmatched_event_touches( | |
215 (1 << event.touchesLength) - 1); | |
216 for (unsigned i = 0; i < event_to_coalesce.touchesLength; ++i) { | |
217 int event_touch_index = | |
218 GetIndexOfTouchID(event, event_to_coalesce.touches[i].id); | |
219 if (event_touch_index == kInvalidTouchIndex) | |
220 return false; | |
221 if (!unmatched_event_touches[event_touch_index]) | |
222 return false; | |
223 unmatched_event_touches[event_touch_index] = false; | |
224 } | |
225 return unmatched_event_touches.none(); | |
226 } | |
227 | |
228 void Coalesce(const WebTouchEvent& event_to_coalesce, WebTouchEvent* event) { | |
229 DCHECK(CanCoalesce(event_to_coalesce, *event)); | |
230 // The WebTouchPoints include absolute position information. So it is | |
231 // sufficient to simply replace the previous event with the new event-> | |
232 // However, it is necessary to make sure that all the points have the | |
233 // correct state, i.e. the touch-points that moved in the last event, but | |
234 // didn't change in the current event, will have Stationary state. It is | |
235 // necessary to change them back to Moved state. | |
236 WebTouchEvent old_event = *event; | |
237 *event = event_to_coalesce; | |
238 for (unsigned i = 0; i < event->touchesLength; ++i) { | |
239 int i_old = GetIndexOfTouchID(old_event, event->touches[i].id); | |
240 if (old_event.touches[i_old].state == blink::WebTouchPoint::StateMoved) | |
241 event->touches[i].state = blink::WebTouchPoint::StateMoved; | |
242 } | |
243 event->causesScrollingIfUncanceled |= old_event.causesScrollingIfUncanceled; | |
244 } | |
245 | |
246 bool CanCoalesce(const WebGestureEvent& event_to_coalesce, | |
247 const WebGestureEvent& event) { | |
248 if (event.type != event_to_coalesce.type || | |
249 event.sourceDevice != event_to_coalesce.sourceDevice || | |
250 event.modifiers != event_to_coalesce.modifiers) | |
251 return false; | |
252 | |
253 if (event.type == WebInputEvent::GestureScrollUpdate) | |
254 return true; | |
255 | |
256 // GesturePinchUpdate scales can be combined only if they share a focal point, | |
257 // e.g., with double-tap drag zoom. | |
258 if (event.type == WebInputEvent::GesturePinchUpdate && | |
259 event.x == event_to_coalesce.x && | |
260 event.y == event_to_coalesce.y) | |
261 return true; | |
262 | |
263 return false; | |
264 } | |
265 | |
266 void Coalesce(const WebGestureEvent& event_to_coalesce, | |
267 WebGestureEvent* event) { | |
268 DCHECK(CanCoalesce(event_to_coalesce, *event)); | |
269 if (event->type == WebInputEvent::GestureScrollUpdate) { | |
270 event->data.scrollUpdate.deltaX += | |
271 event_to_coalesce.data.scrollUpdate.deltaX; | |
272 event->data.scrollUpdate.deltaY += | |
273 event_to_coalesce.data.scrollUpdate.deltaY; | |
274 DCHECK_EQ( | |
275 event->data.scrollUpdate.previousUpdateInSequencePrevented, | |
276 event_to_coalesce.data.scrollUpdate.previousUpdateInSequencePrevented); | |
277 } else if (event->type == WebInputEvent::GesturePinchUpdate) { | |
278 event->data.pinchUpdate.scale *= event_to_coalesce.data.pinchUpdate.scale; | |
279 // Ensure the scale remains bounded above 0 and below Infinity so that | |
280 // we can reliably perform operations like log on the values. | |
281 if (event->data.pinchUpdate.scale < numeric_limits<float>::min()) | |
282 event->data.pinchUpdate.scale = numeric_limits<float>::min(); | |
283 else if (event->data.pinchUpdate.scale > numeric_limits<float>::max()) | |
284 event->data.pinchUpdate.scale = numeric_limits<float>::max(); | |
285 } | |
286 } | |
287 | |
288 struct WebInputEventToString { | |
289 template <class EventType> | |
290 bool Execute(const WebInputEvent& event, std::string* result) const { | |
291 SStringPrintf(result, "%s (Time: %lf, Modifiers: %d)\n", | |
292 WebInputEventTraits::GetName(event.type), | |
293 event.timeStampSeconds, | |
294 event.modifiers); | |
295 const EventType& typed_event = static_cast<const EventType&>(event); | |
296 ApppendEventDetails(typed_event, result); | |
297 return true; | |
298 } | |
299 }; | |
300 | |
301 struct WebInputEventSize { | |
302 template <class EventType> | |
303 bool Execute(WebInputEvent::Type /* type */, size_t* type_size) const { | |
304 *type_size = sizeof(EventType); | |
305 return true; | |
306 } | |
307 }; | |
308 | |
309 struct WebInputEventClone { | |
310 template <class EventType> | |
311 bool Execute(const WebInputEvent& event, | |
312 ScopedWebInputEvent* scoped_event) const { | |
313 DCHECK_EQ(sizeof(EventType), event.size); | |
314 *scoped_event = ScopedWebInputEvent( | |
315 new EventType(static_cast<const EventType&>(event))); | |
316 return true; | |
317 } | |
318 }; | |
319 | |
320 struct WebInputEventDelete { | |
321 template <class EventType> | |
322 bool Execute(WebInputEvent* event, bool* /* dummy_var */) const { | |
323 if (!event) | |
324 return false; | |
325 DCHECK_EQ(sizeof(EventType), event->size); | |
326 delete static_cast<EventType*>(event); | |
327 return true; | |
328 } | |
329 }; | |
330 | |
331 struct WebInputEventCanCoalesce { | |
332 template <class EventType> | |
333 bool Execute(const WebInputEvent& event_to_coalesce, | |
334 const WebInputEvent* event) const { | |
335 if (event_to_coalesce.type != event->type) | |
336 return false; | |
337 DCHECK_EQ(sizeof(EventType), event->size); | |
338 DCHECK_EQ(sizeof(EventType), event_to_coalesce.size); | |
339 return CanCoalesce(static_cast<const EventType&>(event_to_coalesce), | |
340 *static_cast<const EventType*>(event)); | |
341 } | |
342 }; | |
343 | |
344 struct WebInputEventCoalesce { | |
345 template <class EventType> | |
346 bool Execute(const WebInputEvent& event_to_coalesce, | |
347 WebInputEvent* event) const { | |
348 // New events get coalesced into older events, and the newer timestamp | |
349 // should always be preserved. | |
350 const double time_stamp_seconds = event_to_coalesce.timeStampSeconds; | |
351 Coalesce(static_cast<const EventType&>(event_to_coalesce), | |
352 static_cast<EventType*>(event)); | |
353 event->timeStampSeconds = time_stamp_seconds; | |
354 return true; | |
355 } | |
356 }; | |
357 | |
358 template <typename Operator, typename ArgIn, typename ArgOut> | |
359 bool Apply(Operator op, | |
360 WebInputEvent::Type type, | |
361 const ArgIn& arg_in, | |
362 ArgOut* arg_out) { | |
363 if (WebInputEvent::isMouseEventType(type)) | |
364 return op.template Execute<WebMouseEvent>(arg_in, arg_out); | |
365 else if (type == WebInputEvent::MouseWheel) | |
366 return op.template Execute<WebMouseWheelEvent>(arg_in, arg_out); | |
367 else if (WebInputEvent::isKeyboardEventType(type)) | |
368 return op.template Execute<WebKeyboardEvent>(arg_in, arg_out); | |
369 else if (WebInputEvent::isTouchEventType(type)) | |
370 return op.template Execute<WebTouchEvent>(arg_in, arg_out); | |
371 else if (WebInputEvent::isGestureEventType(type)) | |
372 return op.template Execute<WebGestureEvent>(arg_in, arg_out); | |
373 | |
374 NOTREACHED() << "Unknown webkit event type " << type; | |
375 return false; | |
376 } | |
377 | |
378 } // namespace | |
379 | |
380 const char* WebInputEventTraits::GetName(WebInputEvent::Type type) { | |
381 #define CASE_TYPE(t) case WebInputEvent::t: return #t | |
382 switch(type) { | |
383 CASE_TYPE(Undefined); | |
384 CASE_TYPE(MouseDown); | |
385 CASE_TYPE(MouseUp); | |
386 CASE_TYPE(MouseMove); | |
387 CASE_TYPE(MouseEnter); | |
388 CASE_TYPE(MouseLeave); | |
389 CASE_TYPE(ContextMenu); | |
390 CASE_TYPE(MouseWheel); | |
391 CASE_TYPE(RawKeyDown); | |
392 CASE_TYPE(KeyDown); | |
393 CASE_TYPE(KeyUp); | |
394 CASE_TYPE(Char); | |
395 CASE_TYPE(GestureScrollBegin); | |
396 CASE_TYPE(GestureScrollEnd); | |
397 CASE_TYPE(GestureScrollUpdate); | |
398 CASE_TYPE(GestureFlingStart); | |
399 CASE_TYPE(GestureFlingCancel); | |
400 CASE_TYPE(GestureShowPress); | |
401 CASE_TYPE(GestureTap); | |
402 CASE_TYPE(GestureTapUnconfirmed); | |
403 CASE_TYPE(GestureTapDown); | |
404 CASE_TYPE(GestureTapCancel); | |
405 CASE_TYPE(GestureDoubleTap); | |
406 CASE_TYPE(GestureTwoFingerTap); | |
407 CASE_TYPE(GestureLongPress); | |
408 CASE_TYPE(GestureLongTap); | |
409 CASE_TYPE(GesturePinchBegin); | |
410 CASE_TYPE(GesturePinchEnd); | |
411 CASE_TYPE(GesturePinchUpdate); | |
412 CASE_TYPE(TouchStart); | |
413 CASE_TYPE(TouchMove); | |
414 CASE_TYPE(TouchEnd); | |
415 CASE_TYPE(TouchCancel); | |
416 default: | |
417 // Must include default to let blink::WebInputEvent add new event types | |
418 // before they're added here. | |
419 DLOG(WARNING) << | |
420 "Unhandled WebInputEvent type in WebInputEventTraits::GetName.\n"; | |
421 break; | |
422 } | |
423 #undef CASE_TYPE | |
424 return ""; | |
425 } | |
426 | |
427 std::string WebInputEventTraits::ToString(const WebInputEvent& event) { | |
428 std::string result; | |
429 Apply(WebInputEventToString(), event.type, event, &result); | |
430 return result; | |
431 } | |
432 | |
433 size_t WebInputEventTraits::GetSize(WebInputEvent::Type type) { | |
434 size_t size = 0; | |
435 Apply(WebInputEventSize(), type, type, &size); | |
436 return size; | |
437 } | |
438 | |
439 ScopedWebInputEvent WebInputEventTraits::Clone(const WebInputEvent& event) { | |
440 ScopedWebInputEvent scoped_event; | |
441 Apply(WebInputEventClone(), event.type, event, &scoped_event); | |
442 return scoped_event; | |
443 } | |
444 | |
445 void WebInputEventTraits::Delete(WebInputEvent* event) { | |
446 if (!event) | |
447 return; | |
448 bool dummy_var = false; | |
449 Apply(WebInputEventDelete(), event->type, event, &dummy_var); | |
450 } | |
451 | |
452 bool WebInputEventTraits::CanCoalesce(const WebInputEvent& event_to_coalesce, | |
453 const WebInputEvent& event) { | |
454 // Early out before casting. | |
455 if (event_to_coalesce.type != event.type) | |
456 return false; | |
457 return Apply(WebInputEventCanCoalesce(), | |
458 event.type, | |
459 event_to_coalesce, | |
460 &event); | |
461 } | |
462 | |
463 void WebInputEventTraits::Coalesce(const WebInputEvent& event_to_coalesce, | |
464 WebInputEvent* event) { | |
465 DCHECK(event); | |
466 Apply(WebInputEventCoalesce(), event->type, event_to_coalesce, event); | |
467 } | |
468 | |
469 bool WebInputEventTraits::WillReceiveAckFromRenderer( | |
470 const WebInputEvent& event) { | |
471 switch (event.type) { | |
472 case WebInputEvent::MouseDown: | |
473 case WebInputEvent::MouseUp: | |
474 case WebInputEvent::MouseEnter: | |
475 case WebInputEvent::MouseLeave: | |
476 case WebInputEvent::ContextMenu: | |
477 case WebInputEvent::GestureScrollBegin: | |
478 case WebInputEvent::GestureScrollEnd: | |
479 case WebInputEvent::GestureShowPress: | |
480 case WebInputEvent::GestureTapUnconfirmed: | |
481 case WebInputEvent::GestureTapDown: | |
482 case WebInputEvent::GestureTapCancel: | |
483 case WebInputEvent::GesturePinchBegin: | |
484 case WebInputEvent::GesturePinchEnd: | |
485 case WebInputEvent::TouchCancel: | |
486 return false; | |
487 case WebInputEvent::TouchStart: | |
488 case WebInputEvent::TouchEnd: | |
489 return static_cast<const WebTouchEvent&>(event).cancelable; | |
490 default: | |
491 return true; | |
492 } | |
493 } | |
494 | |
495 uint32_t WebInputEventTraits::GetUniqueTouchEventId( | |
496 const WebInputEvent& event) { | |
497 if (WebInputEvent::isTouchEventType(event.type)) { | |
498 return static_cast<const WebTouchEvent&>(event).uniqueTouchEventId; | |
499 } | |
500 return 0U; | |
501 } | |
502 | |
503 } // namespace content | |
OLD | NEW |