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}",
66 event.accelerationRatioX,
67 event.accelerationRatioY,
69 event.hasPreciseScrollingDeltas,
72 event.canRubberbandLeft,
73 event.canRubberbandRight);
76 void ApppendEventDetails(const WebGestureEvent& event, std::string* result) {
78 "{\n Pos: (%d, %d)\n GlobalPos: (%d, %d)\n SourceDevice: %d\n"
79 " RawData: (%f, %f, %f, %f)\n}",
85 event.data.scrollUpdate.deltaX,
86 event.data.scrollUpdate.deltaY,
87 event.data.scrollUpdate.velocityX,
88 event.data.scrollUpdate.velocityY);
91 void ApppendTouchPointDetails(const WebTouchPoint& point, std::string* result) {
93 " (ID: %d, State: %d, ScreenPos: (%f, %f), Pos: (%f, %f),"
94 " Radius: (%f, %f), Rot: %f, Force: %f),\n",
97 point.screenPosition.x,
98 point.screenPosition.y,
107 void ApppendEventDetails(const WebTouchEvent& event, std::string* result) {
108 StringAppendF(result,
109 "{\n Touches: %u, Cancelable: %d,\n[\n",
112 for (unsigned i = 0; i < event.touchesLength; ++i)
113 ApppendTouchPointDetails(event.touches[i], result);
114 result->append(" ]\n}");
117 bool CanCoalesce(const WebKeyboardEvent& event_to_coalesce,
118 const WebKeyboardEvent& event) {
122 void Coalesce(const WebKeyboardEvent& event_to_coalesce,
123 WebKeyboardEvent* event) {
124 DCHECK(CanCoalesce(event_to_coalesce, *event));
127 bool CanCoalesce(const WebMouseEvent& event_to_coalesce,
128 const WebMouseEvent& event) {
129 return event.type == event_to_coalesce.type &&
130 event.type == WebInputEvent::MouseMove;
133 void Coalesce(const WebMouseEvent& event_to_coalesce, WebMouseEvent* event) {
134 DCHECK(CanCoalesce(event_to_coalesce, *event));
135 // Accumulate movement deltas.
136 int x = event->movementX;
137 int y = event->movementY;
138 *event = event_to_coalesce;
139 event->movementX += x;
140 event->movementY += y;
143 bool CanCoalesce(const WebMouseWheelEvent& event_to_coalesce,
144 const WebMouseWheelEvent& event) {
145 return event.modifiers == event_to_coalesce.modifiers &&
146 event.scrollByPage == event_to_coalesce.scrollByPage &&
147 event.phase == event_to_coalesce.phase &&
148 event.momentumPhase == event_to_coalesce.momentumPhase &&
149 event.hasPreciseScrollingDeltas ==
150 event_to_coalesce.hasPreciseScrollingDeltas;
153 float GetUnacceleratedDelta(float accelerated_delta, float acceleration_ratio) {
154 return accelerated_delta * acceleration_ratio;
157 float GetAccelerationRatio(float accelerated_delta, float unaccelerated_delta) {
158 if (unaccelerated_delta == 0.f || accelerated_delta == 0.f)
160 return unaccelerated_delta / accelerated_delta;
163 void Coalesce(const WebMouseWheelEvent& event_to_coalesce,
164 WebMouseWheelEvent* event) {
165 DCHECK(CanCoalesce(event_to_coalesce, *event));
166 float unaccelerated_x =
167 GetUnacceleratedDelta(event->deltaX,
168 event->accelerationRatioX) +
169 GetUnacceleratedDelta(event_to_coalesce.deltaX,
170 event_to_coalesce.accelerationRatioX);
171 float unaccelerated_y =
172 GetUnacceleratedDelta(event->deltaY,
173 event->accelerationRatioY) +
174 GetUnacceleratedDelta(event_to_coalesce.deltaY,
175 event_to_coalesce.accelerationRatioY);
176 event->deltaX += event_to_coalesce.deltaX;
177 event->deltaY += event_to_coalesce.deltaY;
178 event->wheelTicksX += event_to_coalesce.wheelTicksX;
179 event->wheelTicksY += event_to_coalesce.wheelTicksY;
180 event->accelerationRatioX =
181 GetAccelerationRatio(event->deltaX, unaccelerated_x);
182 event->accelerationRatioY =
183 GetAccelerationRatio(event->deltaY, unaccelerated_y);
184 DCHECK_GE(event_to_coalesce.timeStampSeconds, event->timeStampSeconds);
185 event->timeStampSeconds = event_to_coalesce.timeStampSeconds;
188 // Returns |kInvalidTouchIndex| iff |event| lacks a touch with an ID of |id|.
189 int GetIndexOfTouchID(const WebTouchEvent& event, int id) {
190 for (unsigned i = 0; i < event.touchesLength; ++i) {
191 if (event.touches[i].id == id)
194 return kInvalidTouchIndex;
197 bool CanCoalesce(const WebTouchEvent& event_to_coalesce,
198 const WebTouchEvent& event) {
199 if (event.type != event_to_coalesce.type ||
200 event.type != WebInputEvent::TouchMove ||
201 event.modifiers != event_to_coalesce.modifiers ||
202 event.touchesLength != event_to_coalesce.touchesLength ||
203 event.touchesLength > WebTouchEvent::touchesLengthCap)
206 COMPILE_ASSERT(WebTouchEvent::touchesLengthCap <= sizeof(int32_t) * 8U,
207 suboptimal_touches_length_cap_size);
208 // Ensure that we have a 1-to-1 mapping of pointer ids between touches.
209 std::bitset<WebTouchEvent::touchesLengthCap> unmatched_event_touches(
210 (1 << event.touchesLength) - 1);
211 for (unsigned i = 0; i < event_to_coalesce.touchesLength; ++i) {
212 int event_touch_index =
213 GetIndexOfTouchID(event, event_to_coalesce.touches[i].id);
214 if (event_touch_index == kInvalidTouchIndex)
216 if (!unmatched_event_touches[event_touch_index])
218 unmatched_event_touches[event_touch_index] = false;
220 return unmatched_event_touches.none();
223 void Coalesce(const WebTouchEvent& event_to_coalesce, WebTouchEvent* event) {
224 DCHECK(CanCoalesce(event_to_coalesce, *event));
225 // The WebTouchPoints include absolute position information. So it is
226 // sufficient to simply replace the previous event with the new event->
227 // However, it is necessary to make sure that all the points have the
228 // correct state, i.e. the touch-points that moved in the last event, but
229 // didn't change in the current event, will have Stationary state. It is
230 // necessary to change them back to Moved state.
231 WebTouchEvent old_event = *event;
232 *event = event_to_coalesce;
233 for (unsigned i = 0; i < event->touchesLength; ++i) {
234 int i_old = GetIndexOfTouchID(old_event, event->touches[i].id);
235 if (old_event.touches[i_old].state == blink::WebTouchPoint::StateMoved)
236 event->touches[i].state = blink::WebTouchPoint::StateMoved;
240 bool CanCoalesce(const WebGestureEvent& event_to_coalesce,
241 const WebGestureEvent& event) {
242 if (event.type != event_to_coalesce.type ||
243 event.sourceDevice != event_to_coalesce.sourceDevice ||
244 event.modifiers != event_to_coalesce.modifiers)
247 if (event.type == WebInputEvent::GestureScrollUpdate)
250 // GesturePinchUpdate scales can be combined only if they share a focal point,
251 // e.g., with double-tap drag zoom.
252 if (event.type == WebInputEvent::GesturePinchUpdate &&
253 event.x == event_to_coalesce.x &&
254 event.y == event_to_coalesce.y)
260 void Coalesce(const WebGestureEvent& event_to_coalesce,
261 WebGestureEvent* event) {
262 DCHECK(CanCoalesce(event_to_coalesce, *event));
263 if (event->type == WebInputEvent::GestureScrollUpdate) {
264 event->data.scrollUpdate.deltaX +=
265 event_to_coalesce.data.scrollUpdate.deltaX;
266 event->data.scrollUpdate.deltaY +=
267 event_to_coalesce.data.scrollUpdate.deltaY;
268 } else if (event->type == WebInputEvent::GesturePinchUpdate) {
269 event->data.pinchUpdate.scale *= event_to_coalesce.data.pinchUpdate.scale;
270 // Ensure the scale remains bounded above 0 and below Infinity so that
271 // we can reliably perform operations like log on the values.
272 if (event->data.pinchUpdate.scale < numeric_limits<float>::min())
273 event->data.pinchUpdate.scale = numeric_limits<float>::min();
274 else if (event->data.pinchUpdate.scale > numeric_limits<float>::max())
275 event->data.pinchUpdate.scale = numeric_limits<float>::max();
279 struct WebInputEventToString {
280 template <class EventType>
281 bool Execute(const WebInputEvent& event, std::string* result) const {
282 SStringPrintf(result, "%s (Time: %lf, Modifiers: %d)\n",
283 WebInputEventTraits::GetName(event.type),
284 event.timeStampSeconds,
286 const EventType& typed_event = static_cast<const EventType&>(event);
287 ApppendEventDetails(typed_event, result);
292 struct WebInputEventSize {
293 template <class EventType>
294 bool Execute(WebInputEvent::Type /* type */, size_t* type_size) const {
295 *type_size = sizeof(EventType);
300 struct WebInputEventClone {
301 template <class EventType>
302 bool Execute(const WebInputEvent& event,
303 ScopedWebInputEvent* scoped_event) const {
304 DCHECK_EQ(sizeof(EventType), event.size);
305 *scoped_event = ScopedWebInputEvent(
306 new EventType(static_cast<const EventType&>(event)));
311 struct WebInputEventDelete {
312 template <class EventType>
313 bool Execute(WebInputEvent* event, bool* /* dummy_var */) const {
316 DCHECK_EQ(sizeof(EventType), event->size);
317 delete static_cast<EventType*>(event);
322 struct WebInputEventCanCoalesce {
323 template <class EventType>
324 bool Execute(const WebInputEvent& event_to_coalesce,
325 const WebInputEvent* event) const {
326 if (event_to_coalesce.type != event->type)
328 DCHECK_EQ(sizeof(EventType), event->size);
329 DCHECK_EQ(sizeof(EventType), event_to_coalesce.size);
330 return CanCoalesce(static_cast<const EventType&>(event_to_coalesce),
331 *static_cast<const EventType*>(event));
335 struct WebInputEventCoalesce {
336 template <class EventType>
337 bool Execute(const WebInputEvent& event_to_coalesce,
338 WebInputEvent* event) const {
339 Coalesce(static_cast<const EventType&>(event_to_coalesce),
340 static_cast<EventType*>(event));
345 template <typename Operator, typename ArgIn, typename ArgOut>
346 bool Apply(Operator op,
347 WebInputEvent::Type type,
350 if (WebInputEvent::isMouseEventType(type))
351 return op.template Execute<WebMouseEvent>(arg_in, arg_out);
352 else if (type == WebInputEvent::MouseWheel)
353 return op.template Execute<WebMouseWheelEvent>(arg_in, arg_out);
354 else if (WebInputEvent::isKeyboardEventType(type))
355 return op.template Execute<WebKeyboardEvent>(arg_in, arg_out);
356 else if (WebInputEvent::isTouchEventType(type))
357 return op.template Execute<WebTouchEvent>(arg_in, arg_out);
358 else if (WebInputEvent::isGestureEventType(type))
359 return op.template Execute<WebGestureEvent>(arg_in, arg_out);
361 NOTREACHED() << "Unknown webkit event type " << type;
367 const char* WebInputEventTraits::GetName(WebInputEvent::Type type) {
368 #define CASE_TYPE(t) case WebInputEvent::t: return #t
370 CASE_TYPE(Undefined);
371 CASE_TYPE(MouseDown);
373 CASE_TYPE(MouseMove);
374 CASE_TYPE(MouseEnter);
375 CASE_TYPE(MouseLeave);
376 CASE_TYPE(ContextMenu);
377 CASE_TYPE(MouseWheel);
378 CASE_TYPE(RawKeyDown);
382 CASE_TYPE(GestureScrollBegin);
383 CASE_TYPE(GestureScrollEnd);
384 CASE_TYPE(GestureScrollUpdate);
385 CASE_TYPE(GestureFlingStart);
386 CASE_TYPE(GestureFlingCancel);
387 CASE_TYPE(GestureShowPress);
388 CASE_TYPE(GestureTap);
389 CASE_TYPE(GestureTapUnconfirmed);
390 CASE_TYPE(GestureTapDown);
391 CASE_TYPE(GestureTapCancel);
392 CASE_TYPE(GestureDoubleTap);
393 CASE_TYPE(GestureTwoFingerTap);
394 CASE_TYPE(GestureLongPress);
395 CASE_TYPE(GestureLongTap);
396 CASE_TYPE(GesturePinchBegin);
397 CASE_TYPE(GesturePinchEnd);
398 CASE_TYPE(GesturePinchUpdate);
399 CASE_TYPE(TouchStart);
400 CASE_TYPE(TouchMove);
402 CASE_TYPE(TouchCancel);
404 // Must include default to let blink::WebInputEvent add new event types
405 // before they're added here.
407 "Unhandled WebInputEvent type in WebInputEventTraits::GetName.\n";
414 std::string WebInputEventTraits::ToString(const WebInputEvent& event) {
416 Apply(WebInputEventToString(), event.type, event, &result);
420 size_t WebInputEventTraits::GetSize(WebInputEvent::Type type) {
422 Apply(WebInputEventSize(), type, type, &size);
426 ScopedWebInputEvent WebInputEventTraits::Clone(const WebInputEvent& event) {
427 ScopedWebInputEvent scoped_event;
428 Apply(WebInputEventClone(), event.type, event, &scoped_event);
429 return scoped_event.Pass();
432 void WebInputEventTraits::Delete(WebInputEvent* event) {
435 bool dummy_var = false;
436 Apply(WebInputEventDelete(), event->type, event, &dummy_var);
439 bool WebInputEventTraits::CanCoalesce(const WebInputEvent& event_to_coalesce,
440 const WebInputEvent& event) {
441 // Early out before casting.
442 if (event_to_coalesce.type != event.type)
444 return Apply(WebInputEventCanCoalesce(),
450 void WebInputEventTraits::Coalesce(const WebInputEvent& event_to_coalesce,
451 WebInputEvent* event) {
453 Apply(WebInputEventCoalesce(), event->type, event_to_coalesce, event);
456 bool WebInputEventTraits::IgnoresAckDisposition(const WebInputEvent& event) {
457 switch (event.type) {
458 case WebInputEvent::MouseDown:
459 case WebInputEvent::MouseUp:
460 case WebInputEvent::MouseEnter:
461 case WebInputEvent::MouseLeave:
462 case WebInputEvent::ContextMenu:
463 case WebInputEvent::GestureScrollBegin:
464 case WebInputEvent::GestureScrollEnd:
465 case WebInputEvent::GestureShowPress:
466 case WebInputEvent::GestureTapUnconfirmed:
467 case WebInputEvent::GestureTapDown:
468 case WebInputEvent::GestureTapCancel:
469 case WebInputEvent::GesturePinchBegin:
470 case WebInputEvent::GesturePinchEnd:
471 case WebInputEvent::TouchCancel:
473 case WebInputEvent::TouchStart:
474 case WebInputEvent::TouchMove:
475 case WebInputEvent::TouchEnd:
476 return !static_cast<const WebTouchEvent&>(event).cancelable;
482 } // namespace content