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 "content/common/input/web_input_event_traits.h"
10 #include "base/logging.h"
11 #include "base/strings/stringprintf.h"
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
;
27 const int kInvalidTouchIndex
= -1;
29 void ApppendEventDetails(const WebKeyboardEvent
& event
, std::string
* result
) {
31 "{\n WinCode: %d\n NativeCode: %d\n IsSystem: %d\n"
32 " Text: %s\n UnmodifiedText: %s\n KeyIdentifier: %s\n}",
36 reinterpret_cast<const char*>(event
.text
),
37 reinterpret_cast<const char*>(event
.unmodifiedText
),
38 reinterpret_cast<const char*>(event
.keyIdentifier
));
41 void ApppendEventDetails(const WebMouseEvent
& event
, std::string
* 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}",
57 void ApppendEventDetails(const WebMouseWheelEvent
& event
, std::string
* 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}",
66 event
.accelerationRatioX
,
67 event
.accelerationRatioY
,
69 event
.hasPreciseScrollingDeltas
,
72 event
.canRubberbandLeft
,
73 event
.canRubberbandRight
,
77 void ApppendEventDetails(const WebGestureEvent
& event
, std::string
* result
) {
79 "{\n Pos: (%d, %d)\n GlobalPos: (%d, %d)\n SourceDevice: %d\n"
80 " RawData: (%f, %f, %f, %f, %d)\n}",
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
);
93 void ApppendTouchPointDetails(const WebTouchPoint
& point
, std::string
* result
) {
95 " (ID: %d, State: %d, ScreenPos: (%f, %f), Pos: (%f, %f),"
96 " Radius: (%f, %f), Rot: %f, Force: %f),\n",
99 point
.screenPosition
.x
,
100 point
.screenPosition
.y
,
109 void ApppendEventDetails(const WebTouchEvent
& event
, std::string
* result
) {
110 StringAppendF(result
,
111 "{\n Touches: %u, Cancelable: %d, CausesScrolling: %d,"
112 " uniqueTouchEventId: %u\n[\n",
113 event
.touchesLength
, event
.cancelable
,
114 event
.causesScrollingIfUncanceled
, event
.uniqueTouchEventId
);
115 for (unsigned i
= 0; i
< event
.touchesLength
; ++i
)
116 ApppendTouchPointDetails(event
.touches
[i
], result
);
117 result
->append(" ]\n}");
120 bool CanCoalesce(const WebKeyboardEvent
& event_to_coalesce
,
121 const WebKeyboardEvent
& event
) {
125 void Coalesce(const WebKeyboardEvent
& event_to_coalesce
,
126 WebKeyboardEvent
* event
) {
127 DCHECK(CanCoalesce(event_to_coalesce
, *event
));
130 bool CanCoalesce(const WebMouseEvent
& event_to_coalesce
,
131 const WebMouseEvent
& event
) {
132 return event
.type
== event_to_coalesce
.type
&&
133 event
.type
== WebInputEvent::MouseMove
;
136 void Coalesce(const WebMouseEvent
& event_to_coalesce
, WebMouseEvent
* event
) {
137 DCHECK(CanCoalesce(event_to_coalesce
, *event
));
138 // Accumulate movement deltas.
139 int x
= event
->movementX
;
140 int y
= event
->movementY
;
141 *event
= event_to_coalesce
;
142 event
->movementX
+= x
;
143 event
->movementY
+= y
;
146 bool CanCoalesce(const WebMouseWheelEvent
& event_to_coalesce
,
147 const WebMouseWheelEvent
& event
) {
148 return event
.modifiers
== event_to_coalesce
.modifiers
&&
149 event
.scrollByPage
== event_to_coalesce
.scrollByPage
&&
150 event
.phase
== event_to_coalesce
.phase
&&
151 event
.momentumPhase
== event_to_coalesce
.momentumPhase
&&
152 event
.hasPreciseScrollingDeltas
==
153 event_to_coalesce
.hasPreciseScrollingDeltas
&&
154 event
.canScroll
== event_to_coalesce
.canScroll
;
157 float GetUnacceleratedDelta(float accelerated_delta
, float acceleration_ratio
) {
158 return accelerated_delta
* acceleration_ratio
;
161 float GetAccelerationRatio(float accelerated_delta
, float unaccelerated_delta
) {
162 if (unaccelerated_delta
== 0.f
|| accelerated_delta
== 0.f
)
164 return unaccelerated_delta
/ accelerated_delta
;
167 void Coalesce(const WebMouseWheelEvent
& event_to_coalesce
,
168 WebMouseWheelEvent
* event
) {
169 DCHECK(CanCoalesce(event_to_coalesce
, *event
));
170 float unaccelerated_x
=
171 GetUnacceleratedDelta(event
->deltaX
,
172 event
->accelerationRatioX
) +
173 GetUnacceleratedDelta(event_to_coalesce
.deltaX
,
174 event_to_coalesce
.accelerationRatioX
);
175 float unaccelerated_y
=
176 GetUnacceleratedDelta(event
->deltaY
,
177 event
->accelerationRatioY
) +
178 GetUnacceleratedDelta(event_to_coalesce
.deltaY
,
179 event_to_coalesce
.accelerationRatioY
);
180 event
->deltaX
+= event_to_coalesce
.deltaX
;
181 event
->deltaY
+= event_to_coalesce
.deltaY
;
182 event
->wheelTicksX
+= event_to_coalesce
.wheelTicksX
;
183 event
->wheelTicksY
+= event_to_coalesce
.wheelTicksY
;
184 event
->accelerationRatioX
=
185 GetAccelerationRatio(event
->deltaX
, unaccelerated_x
);
186 event
->accelerationRatioY
=
187 GetAccelerationRatio(event
->deltaY
, unaccelerated_y
);
190 // Returns |kInvalidTouchIndex| iff |event| lacks a touch with an ID of |id|.
191 int GetIndexOfTouchID(const WebTouchEvent
& event
, int id
) {
192 for (unsigned i
= 0; i
< event
.touchesLength
; ++i
) {
193 if (event
.touches
[i
].id
== id
)
196 return kInvalidTouchIndex
;
199 bool CanCoalesce(const WebTouchEvent
& event_to_coalesce
,
200 const WebTouchEvent
& event
) {
201 if (event
.type
!= event_to_coalesce
.type
||
202 event
.type
!= WebInputEvent::TouchMove
||
203 event
.modifiers
!= event_to_coalesce
.modifiers
||
204 event
.touchesLength
!= event_to_coalesce
.touchesLength
||
205 event
.touchesLength
> WebTouchEvent::touchesLengthCap
)
208 static_assert(WebTouchEvent::touchesLengthCap
<= sizeof(int32_t) * 8U,
209 "suboptimal touchesLengthCap size");
210 // Ensure that we have a 1-to-1 mapping of pointer ids between touches.
211 std::bitset
<WebTouchEvent::touchesLengthCap
> unmatched_event_touches(
212 (1 << event
.touchesLength
) - 1);
213 for (unsigned i
= 0; i
< event_to_coalesce
.touchesLength
; ++i
) {
214 int event_touch_index
=
215 GetIndexOfTouchID(event
, event_to_coalesce
.touches
[i
].id
);
216 if (event_touch_index
== kInvalidTouchIndex
)
218 if (!unmatched_event_touches
[event_touch_index
])
220 unmatched_event_touches
[event_touch_index
] = false;
222 return unmatched_event_touches
.none();
225 void Coalesce(const WebTouchEvent
& event_to_coalesce
, WebTouchEvent
* event
) {
226 DCHECK(CanCoalesce(event_to_coalesce
, *event
));
227 // The WebTouchPoints include absolute position information. So it is
228 // sufficient to simply replace the previous event with the new event->
229 // However, it is necessary to make sure that all the points have the
230 // correct state, i.e. the touch-points that moved in the last event, but
231 // didn't change in the current event, will have Stationary state. It is
232 // necessary to change them back to Moved state.
233 WebTouchEvent old_event
= *event
;
234 *event
= event_to_coalesce
;
235 for (unsigned i
= 0; i
< event
->touchesLength
; ++i
) {
236 int i_old
= GetIndexOfTouchID(old_event
, event
->touches
[i
].id
);
237 if (old_event
.touches
[i_old
].state
== blink::WebTouchPoint::StateMoved
)
238 event
->touches
[i
].state
= blink::WebTouchPoint::StateMoved
;
240 event
->causesScrollingIfUncanceled
|= old_event
.causesScrollingIfUncanceled
;
243 bool CanCoalesce(const WebGestureEvent
& event_to_coalesce
,
244 const WebGestureEvent
& event
) {
245 if (event
.type
!= event_to_coalesce
.type
||
246 event
.sourceDevice
!= event_to_coalesce
.sourceDevice
||
247 event
.modifiers
!= event_to_coalesce
.modifiers
)
250 if (event
.type
== WebInputEvent::GestureScrollUpdate
)
253 // GesturePinchUpdate scales can be combined only if they share a focal point,
254 // e.g., with double-tap drag zoom.
255 if (event
.type
== WebInputEvent::GesturePinchUpdate
&&
256 event
.x
== event_to_coalesce
.x
&&
257 event
.y
== event_to_coalesce
.y
)
263 void Coalesce(const WebGestureEvent
& event_to_coalesce
,
264 WebGestureEvent
* event
) {
265 DCHECK(CanCoalesce(event_to_coalesce
, *event
));
266 if (event
->type
== WebInputEvent::GestureScrollUpdate
) {
267 event
->data
.scrollUpdate
.deltaX
+=
268 event_to_coalesce
.data
.scrollUpdate
.deltaX
;
269 event
->data
.scrollUpdate
.deltaY
+=
270 event_to_coalesce
.data
.scrollUpdate
.deltaY
;
272 event
->data
.scrollUpdate
.previousUpdateInSequencePrevented
,
273 event_to_coalesce
.data
.scrollUpdate
.previousUpdateInSequencePrevented
);
274 } else if (event
->type
== WebInputEvent::GesturePinchUpdate
) {
275 event
->data
.pinchUpdate
.scale
*= event_to_coalesce
.data
.pinchUpdate
.scale
;
276 // Ensure the scale remains bounded above 0 and below Infinity so that
277 // we can reliably perform operations like log on the values.
278 if (event
->data
.pinchUpdate
.scale
< numeric_limits
<float>::min())
279 event
->data
.pinchUpdate
.scale
= numeric_limits
<float>::min();
280 else if (event
->data
.pinchUpdate
.scale
> numeric_limits
<float>::max())
281 event
->data
.pinchUpdate
.scale
= numeric_limits
<float>::max();
285 struct WebInputEventToString
{
286 template <class EventType
>
287 bool Execute(const WebInputEvent
& event
, std::string
* result
) const {
288 SStringPrintf(result
, "%s (Time: %lf, Modifiers: %d)\n",
289 WebInputEventTraits::GetName(event
.type
),
290 event
.timeStampSeconds
,
292 const EventType
& typed_event
= static_cast<const EventType
&>(event
);
293 ApppendEventDetails(typed_event
, result
);
298 struct WebInputEventSize
{
299 template <class EventType
>
300 bool Execute(WebInputEvent::Type
/* type */, size_t* type_size
) const {
301 *type_size
= sizeof(EventType
);
306 struct WebInputEventClone
{
307 template <class EventType
>
308 bool Execute(const WebInputEvent
& event
,
309 ScopedWebInputEvent
* scoped_event
) const {
310 DCHECK_EQ(sizeof(EventType
), event
.size
);
311 *scoped_event
= ScopedWebInputEvent(
312 new EventType(static_cast<const EventType
&>(event
)));
317 struct WebInputEventDelete
{
318 template <class EventType
>
319 bool Execute(WebInputEvent
* event
, bool* /* dummy_var */) const {
322 DCHECK_EQ(sizeof(EventType
), event
->size
);
323 delete static_cast<EventType
*>(event
);
328 struct WebInputEventCanCoalesce
{
329 template <class EventType
>
330 bool Execute(const WebInputEvent
& event_to_coalesce
,
331 const WebInputEvent
* event
) const {
332 if (event_to_coalesce
.type
!= event
->type
)
334 DCHECK_EQ(sizeof(EventType
), event
->size
);
335 DCHECK_EQ(sizeof(EventType
), event_to_coalesce
.size
);
336 return CanCoalesce(static_cast<const EventType
&>(event_to_coalesce
),
337 *static_cast<const EventType
*>(event
));
341 struct WebInputEventCoalesce
{
342 template <class EventType
>
343 bool Execute(const WebInputEvent
& event_to_coalesce
,
344 WebInputEvent
* event
) const {
345 // New events get coalesced into older events, and the newer timestamp
346 // should always be preserved.
347 // On Windows sometimes there could be wild timestamp value, so ignore the
349 // TODO(miletus): Make it work on Windows. crbug.com/517921.
351 // In some test cases, due to clock precision and data conversion loss,
352 // we allow a small epsilon.
353 DCHECK_GE(event_to_coalesce
.timeStampSeconds
- event
->timeStampSeconds
,
355 #endif // !defined(OS_WIN)
357 const double time_stamp_seconds
= event_to_coalesce
.timeStampSeconds
;
358 Coalesce(static_cast<const EventType
&>(event_to_coalesce
),
359 static_cast<EventType
*>(event
));
360 event
->timeStampSeconds
= time_stamp_seconds
;
365 template <typename Operator
, typename ArgIn
, typename ArgOut
>
366 bool Apply(Operator op
,
367 WebInputEvent::Type type
,
370 if (WebInputEvent::isMouseEventType(type
))
371 return op
.template Execute
<WebMouseEvent
>(arg_in
, arg_out
);
372 else if (type
== WebInputEvent::MouseWheel
)
373 return op
.template Execute
<WebMouseWheelEvent
>(arg_in
, arg_out
);
374 else if (WebInputEvent::isKeyboardEventType(type
))
375 return op
.template Execute
<WebKeyboardEvent
>(arg_in
, arg_out
);
376 else if (WebInputEvent::isTouchEventType(type
))
377 return op
.template Execute
<WebTouchEvent
>(arg_in
, arg_out
);
378 else if (WebInputEvent::isGestureEventType(type
))
379 return op
.template Execute
<WebGestureEvent
>(arg_in
, arg_out
);
381 NOTREACHED() << "Unknown webkit event type " << type
;
387 const char* WebInputEventTraits::GetName(WebInputEvent::Type type
) {
388 #define CASE_TYPE(t) case WebInputEvent::t: return #t
390 CASE_TYPE(Undefined
);
391 CASE_TYPE(MouseDown
);
393 CASE_TYPE(MouseMove
);
394 CASE_TYPE(MouseEnter
);
395 CASE_TYPE(MouseLeave
);
396 CASE_TYPE(ContextMenu
);
397 CASE_TYPE(MouseWheel
);
398 CASE_TYPE(RawKeyDown
);
402 CASE_TYPE(GestureScrollBegin
);
403 CASE_TYPE(GestureScrollEnd
);
404 CASE_TYPE(GestureScrollUpdate
);
405 CASE_TYPE(GestureFlingStart
);
406 CASE_TYPE(GestureFlingCancel
);
407 CASE_TYPE(GestureShowPress
);
408 CASE_TYPE(GestureTap
);
409 CASE_TYPE(GestureTapUnconfirmed
);
410 CASE_TYPE(GestureTapDown
);
411 CASE_TYPE(GestureTapCancel
);
412 CASE_TYPE(GestureDoubleTap
);
413 CASE_TYPE(GestureTwoFingerTap
);
414 CASE_TYPE(GestureLongPress
);
415 CASE_TYPE(GestureLongTap
);
416 CASE_TYPE(GesturePinchBegin
);
417 CASE_TYPE(GesturePinchEnd
);
418 CASE_TYPE(GesturePinchUpdate
);
419 CASE_TYPE(TouchStart
);
420 CASE_TYPE(TouchMove
);
422 CASE_TYPE(TouchCancel
);
424 // Must include default to let blink::WebInputEvent add new event types
425 // before they're added here.
427 "Unhandled WebInputEvent type in WebInputEventTraits::GetName.\n";
434 std::string
WebInputEventTraits::ToString(const WebInputEvent
& event
) {
436 Apply(WebInputEventToString(), event
.type
, event
, &result
);
440 size_t WebInputEventTraits::GetSize(WebInputEvent::Type type
) {
442 Apply(WebInputEventSize(), type
, type
, &size
);
446 ScopedWebInputEvent
WebInputEventTraits::Clone(const WebInputEvent
& event
) {
447 ScopedWebInputEvent scoped_event
;
448 Apply(WebInputEventClone(), event
.type
, event
, &scoped_event
);
449 return scoped_event
.Pass();
452 void WebInputEventTraits::Delete(WebInputEvent
* event
) {
455 bool dummy_var
= false;
456 Apply(WebInputEventDelete(), event
->type
, event
, &dummy_var
);
459 bool WebInputEventTraits::CanCoalesce(const WebInputEvent
& event_to_coalesce
,
460 const WebInputEvent
& event
) {
461 // Early out before casting.
462 if (event_to_coalesce
.type
!= event
.type
)
464 return Apply(WebInputEventCanCoalesce(),
470 void WebInputEventTraits::Coalesce(const WebInputEvent
& event_to_coalesce
,
471 WebInputEvent
* event
) {
473 Apply(WebInputEventCoalesce(), event
->type
, event_to_coalesce
, event
);
476 bool WebInputEventTraits::WillReceiveAckFromRenderer(
477 const WebInputEvent
& event
) {
478 switch (event
.type
) {
479 case WebInputEvent::MouseDown
:
480 case WebInputEvent::MouseUp
:
481 case WebInputEvent::MouseEnter
:
482 case WebInputEvent::MouseLeave
:
483 case WebInputEvent::ContextMenu
:
484 case WebInputEvent::GestureScrollBegin
:
485 case WebInputEvent::GestureScrollEnd
:
486 case WebInputEvent::GestureShowPress
:
487 case WebInputEvent::GestureTapUnconfirmed
:
488 case WebInputEvent::GestureTapDown
:
489 case WebInputEvent::GestureTapCancel
:
490 case WebInputEvent::GesturePinchBegin
:
491 case WebInputEvent::GesturePinchEnd
:
492 case WebInputEvent::TouchCancel
:
494 case WebInputEvent::TouchStart
:
495 case WebInputEvent::TouchEnd
:
496 return static_cast<const WebTouchEvent
&>(event
).cancelable
;
502 uint32
WebInputEventTraits::GetUniqueTouchEventId(const WebInputEvent
& event
) {
503 if (WebInputEvent::isTouchEventType(event
.type
)) {
504 return static_cast<const WebTouchEvent
&>(event
).uniqueTouchEventId
;
509 } // namespace content