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"
12 using blink::WebGestureEvent
;
13 using blink::WebInputEvent
;
14 using blink::WebKeyboardEvent
;
15 using blink::WebMouseEvent
;
16 using blink::WebMouseWheelEvent
;
17 using blink::WebTouchEvent
;
18 using std::numeric_limits
;
23 const int kInvalidTouchIndex
= -1;
25 bool CanCoalesce(const WebKeyboardEvent
& event_to_coalesce
,
26 const WebKeyboardEvent
& event
) {
30 void Coalesce(const WebKeyboardEvent
& event_to_coalesce
,
31 WebKeyboardEvent
* event
) {
32 DCHECK(CanCoalesce(event_to_coalesce
, *event
));
35 bool CanCoalesce(const WebMouseEvent
& event_to_coalesce
,
36 const WebMouseEvent
& event
) {
37 return event
.type
== event_to_coalesce
.type
&&
38 event
.type
== WebInputEvent::MouseMove
;
41 void Coalesce(const WebMouseEvent
& event_to_coalesce
, WebMouseEvent
* event
) {
42 DCHECK(CanCoalesce(event_to_coalesce
, *event
));
43 // Accumulate movement deltas.
44 int x
= event
->movementX
;
45 int y
= event
->movementY
;
46 *event
= event_to_coalesce
;
47 event
->movementX
+= x
;
48 event
->movementY
+= y
;
51 bool CanCoalesce(const WebMouseWheelEvent
& event_to_coalesce
,
52 const WebMouseWheelEvent
& event
) {
53 return event
.modifiers
== event_to_coalesce
.modifiers
&&
54 event
.scrollByPage
== event_to_coalesce
.scrollByPage
&&
55 event
.phase
== event_to_coalesce
.phase
&&
56 event
.momentumPhase
== event_to_coalesce
.momentumPhase
&&
57 event
.hasPreciseScrollingDeltas
==
58 event_to_coalesce
.hasPreciseScrollingDeltas
;
61 float GetUnacceleratedDelta(float accelerated_delta
, float acceleration_ratio
) {
62 return accelerated_delta
* acceleration_ratio
;
65 float GetAccelerationRatio(float accelerated_delta
, float unaccelerated_delta
) {
66 if (unaccelerated_delta
== 0.f
|| accelerated_delta
== 0.f
)
68 return unaccelerated_delta
/ accelerated_delta
;
71 void Coalesce(const WebMouseWheelEvent
& event_to_coalesce
,
72 WebMouseWheelEvent
* event
) {
73 DCHECK(CanCoalesce(event_to_coalesce
, *event
));
74 float unaccelerated_x
=
75 GetUnacceleratedDelta(event
->deltaX
,
76 event
->accelerationRatioX
) +
77 GetUnacceleratedDelta(event_to_coalesce
.deltaX
,
78 event_to_coalesce
.accelerationRatioX
);
79 float unaccelerated_y
=
80 GetUnacceleratedDelta(event
->deltaY
,
81 event
->accelerationRatioY
) +
82 GetUnacceleratedDelta(event_to_coalesce
.deltaY
,
83 event_to_coalesce
.accelerationRatioY
);
84 event
->deltaX
+= event_to_coalesce
.deltaX
;
85 event
->deltaY
+= event_to_coalesce
.deltaY
;
86 event
->wheelTicksX
+= event_to_coalesce
.wheelTicksX
;
87 event
->wheelTicksY
+= event_to_coalesce
.wheelTicksY
;
88 event
->accelerationRatioX
=
89 GetAccelerationRatio(event
->deltaX
, unaccelerated_x
);
90 event
->accelerationRatioY
=
91 GetAccelerationRatio(event
->deltaY
, unaccelerated_y
);
92 DCHECK_GE(event_to_coalesce
.timeStampSeconds
, event
->timeStampSeconds
);
93 event
->timeStampSeconds
= event_to_coalesce
.timeStampSeconds
;
96 // Returns |kInvalidTouchIndex| iff |event| lacks a touch with an ID of |id|.
97 int GetIndexOfTouchID(const WebTouchEvent
& event
, int id
) {
98 for (unsigned i
= 0; i
< event
.touchesLength
; ++i
) {
99 if (event
.touches
[i
].id
== id
)
102 return kInvalidTouchIndex
;
105 bool CanCoalesce(const WebTouchEvent
& event_to_coalesce
,
106 const WebTouchEvent
& event
) {
107 if (event
.type
!= event_to_coalesce
.type
||
108 event
.type
!= WebInputEvent::TouchMove
||
109 event
.modifiers
!= event_to_coalesce
.modifiers
||
110 event
.touchesLength
!= event_to_coalesce
.touchesLength
||
111 event
.touchesLength
> WebTouchEvent::touchesLengthCap
)
114 COMPILE_ASSERT(WebTouchEvent::touchesLengthCap
<= sizeof(int32_t) * 8U,
115 suboptimal_touches_length_cap_size
);
116 // Ensure that we have a 1-to-1 mapping of pointer ids between touches.
117 std::bitset
<WebTouchEvent::touchesLengthCap
> unmatched_event_touches(
118 (1 << event
.touchesLength
) - 1);
119 for (unsigned i
= 0; i
< event_to_coalesce
.touchesLength
; ++i
) {
120 int event_touch_index
=
121 GetIndexOfTouchID(event
, event_to_coalesce
.touches
[i
].id
);
122 if (event_touch_index
== kInvalidTouchIndex
)
124 if (!unmatched_event_touches
[event_touch_index
])
126 unmatched_event_touches
[event_touch_index
] = false;
128 return unmatched_event_touches
.none();
131 void Coalesce(const WebTouchEvent
& event_to_coalesce
, WebTouchEvent
* event
) {
132 DCHECK(CanCoalesce(event_to_coalesce
, *event
));
133 // The WebTouchPoints include absolute position information. So it is
134 // sufficient to simply replace the previous event with the new event->
135 // However, it is necessary to make sure that all the points have the
136 // correct state, i.e. the touch-points that moved in the last event, but
137 // didn't change in the current event, will have Stationary state. It is
138 // necessary to change them back to Moved state.
139 WebTouchEvent old_event
= *event
;
140 *event
= event_to_coalesce
;
141 for (unsigned i
= 0; i
< event
->touchesLength
; ++i
) {
142 int i_old
= GetIndexOfTouchID(old_event
, event
->touches
[i
].id
);
143 if (old_event
.touches
[i_old
].state
== blink::WebTouchPoint::StateMoved
)
144 event
->touches
[i
].state
= blink::WebTouchPoint::StateMoved
;
148 bool CanCoalesce(const WebGestureEvent
& event_to_coalesce
,
149 const WebGestureEvent
& event
) {
150 if (event
.type
!= event_to_coalesce
.type
||
151 event
.sourceDevice
!= event_to_coalesce
.sourceDevice
||
152 event
.modifiers
!= event_to_coalesce
.modifiers
)
155 if (event
.type
== WebInputEvent::GestureScrollUpdate
)
158 // GesturePinchUpdate scales can be combined only if they share a focal point,
159 // e.g., with double-tap drag zoom.
160 if (event
.type
== WebInputEvent::GesturePinchUpdate
&&
161 event
.x
== event_to_coalesce
.x
&&
162 event
.y
== event_to_coalesce
.y
)
168 void Coalesce(const WebGestureEvent
& event_to_coalesce
,
169 WebGestureEvent
* event
) {
170 DCHECK(CanCoalesce(event_to_coalesce
, *event
));
171 if (event
->type
== WebInputEvent::GestureScrollUpdate
) {
172 event
->data
.scrollUpdate
.deltaX
+=
173 event_to_coalesce
.data
.scrollUpdate
.deltaX
;
174 event
->data
.scrollUpdate
.deltaY
+=
175 event_to_coalesce
.data
.scrollUpdate
.deltaY
;
176 } else if (event
->type
== WebInputEvent::GesturePinchUpdate
) {
177 event
->data
.pinchUpdate
.scale
*= event_to_coalesce
.data
.pinchUpdate
.scale
;
178 // Ensure the scale remains bounded above 0 and below Infinity so that
179 // we can reliably perform operations like log on the values.
180 if (event
->data
.pinchUpdate
.scale
< numeric_limits
<float>::min())
181 event
->data
.pinchUpdate
.scale
= numeric_limits
<float>::min();
182 else if (event
->data
.pinchUpdate
.scale
> numeric_limits
<float>::max())
183 event
->data
.pinchUpdate
.scale
= numeric_limits
<float>::max();
187 struct WebInputEventSize
{
188 template <class EventType
>
189 bool Execute(WebInputEvent::Type
/* type */, size_t* type_size
) const {
190 *type_size
= sizeof(EventType
);
195 struct WebInputEventClone
{
196 template <class EventType
>
197 bool Execute(const WebInputEvent
& event
,
198 ScopedWebInputEvent
* scoped_event
) const {
199 DCHECK_EQ(sizeof(EventType
), event
.size
);
200 *scoped_event
= ScopedWebInputEvent(
201 new EventType(static_cast<const EventType
&>(event
)));
206 struct WebInputEventDelete
{
207 template <class EventType
>
208 bool Execute(WebInputEvent
* event
, bool* /* dummy_var */) const {
211 DCHECK_EQ(sizeof(EventType
), event
->size
);
212 delete static_cast<EventType
*>(event
);
217 struct WebInputEventCanCoalesce
{
218 template <class EventType
>
219 bool Execute(const WebInputEvent
& event_to_coalesce
,
220 const WebInputEvent
* event
) const {
221 if (event_to_coalesce
.type
!= event
->type
)
223 DCHECK_EQ(sizeof(EventType
), event
->size
);
224 DCHECK_EQ(sizeof(EventType
), event_to_coalesce
.size
);
225 return CanCoalesce(static_cast<const EventType
&>(event_to_coalesce
),
226 *static_cast<const EventType
*>(event
));
230 struct WebInputEventCoalesce
{
231 template <class EventType
>
232 bool Execute(const WebInputEvent
& event_to_coalesce
,
233 WebInputEvent
* event
) const {
234 Coalesce(static_cast<const EventType
&>(event_to_coalesce
),
235 static_cast<EventType
*>(event
));
240 template <typename Operator
, typename ArgIn
, typename ArgOut
>
241 bool Apply(Operator op
,
242 WebInputEvent::Type type
,
245 if (WebInputEvent::isMouseEventType(type
))
246 return op
.template Execute
<WebMouseEvent
>(arg_in
, arg_out
);
247 else if (type
== WebInputEvent::MouseWheel
)
248 return op
.template Execute
<WebMouseWheelEvent
>(arg_in
, arg_out
);
249 else if (WebInputEvent::isKeyboardEventType(type
))
250 return op
.template Execute
<WebKeyboardEvent
>(arg_in
, arg_out
);
251 else if (WebInputEvent::isTouchEventType(type
))
252 return op
.template Execute
<WebTouchEvent
>(arg_in
, arg_out
);
253 else if (WebInputEvent::isGestureEventType(type
))
254 return op
.template Execute
<WebGestureEvent
>(arg_in
, arg_out
);
256 NOTREACHED() << "Unknown webkit event type " << type
;
262 const char* WebInputEventTraits::GetName(WebInputEvent::Type type
) {
263 #define CASE_TYPE(t) case WebInputEvent::t: return #t
265 CASE_TYPE(Undefined
);
266 CASE_TYPE(MouseDown
);
268 CASE_TYPE(MouseMove
);
269 CASE_TYPE(MouseEnter
);
270 CASE_TYPE(MouseLeave
);
271 CASE_TYPE(ContextMenu
);
272 CASE_TYPE(MouseWheel
);
273 CASE_TYPE(RawKeyDown
);
277 CASE_TYPE(GestureScrollBegin
);
278 CASE_TYPE(GestureScrollEnd
);
279 CASE_TYPE(GestureScrollUpdate
);
280 CASE_TYPE(GestureFlingStart
);
281 CASE_TYPE(GestureFlingCancel
);
282 CASE_TYPE(GestureShowPress
);
283 CASE_TYPE(GestureTap
);
284 CASE_TYPE(GestureTapUnconfirmed
);
285 CASE_TYPE(GestureTapDown
);
286 CASE_TYPE(GestureTapCancel
);
287 CASE_TYPE(GestureDoubleTap
);
288 CASE_TYPE(GestureTwoFingerTap
);
289 CASE_TYPE(GestureLongPress
);
290 CASE_TYPE(GestureLongTap
);
291 CASE_TYPE(GesturePinchBegin
);
292 CASE_TYPE(GesturePinchEnd
);
293 CASE_TYPE(GesturePinchUpdate
);
294 CASE_TYPE(TouchStart
);
295 CASE_TYPE(TouchMove
);
297 CASE_TYPE(TouchCancel
);
299 // Must include default to let blink::WebInputEvent add new event types
300 // before they're added here.
302 "Unhandled WebInputEvent type in WebInputEventTraits::GetName.\n";
309 size_t WebInputEventTraits::GetSize(WebInputEvent::Type type
) {
311 Apply(WebInputEventSize(), type
, type
, &size
);
315 ScopedWebInputEvent
WebInputEventTraits::Clone(const WebInputEvent
& event
) {
316 ScopedWebInputEvent scoped_event
;
317 Apply(WebInputEventClone(), event
.type
, event
, &scoped_event
);
318 return scoped_event
.Pass();
321 void WebInputEventTraits::Delete(WebInputEvent
* event
) {
324 bool dummy_var
= false;
325 Apply(WebInputEventDelete(), event
->type
, event
, &dummy_var
);
328 bool WebInputEventTraits::CanCoalesce(const WebInputEvent
& event_to_coalesce
,
329 const WebInputEvent
& event
) {
330 // Early out before casting.
331 if (event_to_coalesce
.type
!= event
.type
)
333 return Apply(WebInputEventCanCoalesce(),
339 void WebInputEventTraits::Coalesce(const WebInputEvent
& event_to_coalesce
,
340 WebInputEvent
* event
) {
342 Apply(WebInputEventCoalesce(), event
->type
, event_to_coalesce
, event
);
345 bool WebInputEventTraits::IgnoresAckDisposition(const WebInputEvent
& event
) {
346 switch (event
.type
) {
347 case WebInputEvent::MouseDown
:
348 case WebInputEvent::MouseUp
:
349 case WebInputEvent::MouseEnter
:
350 case WebInputEvent::MouseLeave
:
351 case WebInputEvent::ContextMenu
:
352 case WebInputEvent::GestureScrollBegin
:
353 case WebInputEvent::GestureScrollEnd
:
354 case WebInputEvent::GestureShowPress
:
355 case WebInputEvent::GestureTapUnconfirmed
:
356 case WebInputEvent::GestureTapDown
:
357 case WebInputEvent::GestureTapCancel
:
358 case WebInputEvent::GesturePinchBegin
:
359 case WebInputEvent::GesturePinchEnd
:
360 case WebInputEvent::TouchCancel
:
362 case WebInputEvent::TouchStart
:
363 case WebInputEvent::TouchMove
:
364 case WebInputEvent::TouchEnd
:
365 return !static_cast<const WebTouchEvent
&>(event
).cancelable
;
371 } // namespace content