• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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;
19 
20 namespace content {
21 namespace {
22 
23 const int kInvalidTouchIndex = -1;
24 
CanCoalesce(const WebKeyboardEvent & event_to_coalesce,const WebKeyboardEvent & event)25 bool CanCoalesce(const WebKeyboardEvent& event_to_coalesce,
26                  const WebKeyboardEvent& event) {
27   return false;
28 }
29 
Coalesce(const WebKeyboardEvent & event_to_coalesce,WebKeyboardEvent * event)30 void Coalesce(const WebKeyboardEvent& event_to_coalesce,
31               WebKeyboardEvent* event) {
32   DCHECK(CanCoalesce(event_to_coalesce, *event));
33 }
34 
CanCoalesce(const WebMouseEvent & event_to_coalesce,const WebMouseEvent & 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;
39 }
40 
Coalesce(const WebMouseEvent & event_to_coalesce,WebMouseEvent * event)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;
49 }
50 
CanCoalesce(const WebMouseWheelEvent & event_to_coalesce,const WebMouseWheelEvent & event)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;
59 }
60 
GetUnacceleratedDelta(float accelerated_delta,float acceleration_ratio)61 float GetUnacceleratedDelta(float accelerated_delta, float acceleration_ratio) {
62   return accelerated_delta * acceleration_ratio;
63 }
64 
GetAccelerationRatio(float accelerated_delta,float unaccelerated_delta)65 float GetAccelerationRatio(float accelerated_delta, float unaccelerated_delta) {
66   if (unaccelerated_delta == 0.f || accelerated_delta == 0.f)
67     return 1.f;
68   return unaccelerated_delta / accelerated_delta;
69 }
70 
Coalesce(const WebMouseWheelEvent & event_to_coalesce,WebMouseWheelEvent * event)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;
94 }
95 
96 // Returns |kInvalidTouchIndex| iff |event| lacks a touch with an ID of |id|.
GetIndexOfTouchID(const WebTouchEvent & event,int 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)
100       return i;
101   }
102   return kInvalidTouchIndex;
103 }
104 
CanCoalesce(const WebTouchEvent & event_to_coalesce,const WebTouchEvent & event)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)
112     return false;
113 
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)
123       return false;
124     if (!unmatched_event_touches[event_touch_index])
125       return false;
126     unmatched_event_touches[event_touch_index] = false;
127   }
128   return unmatched_event_touches.none();
129 }
130 
Coalesce(const WebTouchEvent & event_to_coalesce,WebTouchEvent * event)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;
145   }
146 }
147 
CanCoalesce(const WebGestureEvent & event_to_coalesce,const WebGestureEvent & event)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)
153     return false;
154 
155   if (event.type == WebInputEvent::GestureScrollUpdate)
156     return true;
157 
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)
163     return true;
164 
165   return false;
166 }
167 
Coalesce(const WebGestureEvent & event_to_coalesce,WebGestureEvent * event)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();
184   }
185 }
186 
187 struct WebInputEventSize {
188   template <class EventType>
Executecontent::__anonc2f76c9b0111::WebInputEventSize189   bool Execute(WebInputEvent::Type /* type */, size_t* type_size) const {
190     *type_size = sizeof(EventType);
191     return true;
192   }
193 };
194 
195 struct WebInputEventClone {
196   template <class EventType>
Executecontent::__anonc2f76c9b0111::WebInputEventClone197   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)));
202     return true;
203   }
204 };
205 
206 struct WebInputEventDelete {
207   template <class EventType>
Executecontent::__anonc2f76c9b0111::WebInputEventDelete208   bool Execute(WebInputEvent* event, bool* /* dummy_var */) const {
209     if (!event)
210       return false;
211     DCHECK_EQ(sizeof(EventType), event->size);
212     delete static_cast<EventType*>(event);
213     return true;
214   }
215 };
216 
217 struct WebInputEventCanCoalesce {
218   template <class EventType>
Executecontent::__anonc2f76c9b0111::WebInputEventCanCoalesce219   bool Execute(const WebInputEvent& event_to_coalesce,
220                const WebInputEvent* event) const {
221     if (event_to_coalesce.type != event->type)
222       return false;
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));
227   }
228 };
229 
230 struct WebInputEventCoalesce {
231   template <class EventType>
Executecontent::__anonc2f76c9b0111::WebInputEventCoalesce232   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));
236     return true;
237   }
238 };
239 
240 template <typename Operator, typename ArgIn, typename ArgOut>
Apply(Operator op,WebInputEvent::Type type,const ArgIn & arg_in,ArgOut * arg_out)241 bool Apply(Operator op,
242            WebInputEvent::Type type,
243            const ArgIn& arg_in,
244            ArgOut* arg_out) {
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);
255 
256   NOTREACHED() << "Unknown webkit event type " << type;
257   return false;
258 }
259 
260 }  // namespace
261 
GetName(WebInputEvent::Type type)262 const char* WebInputEventTraits::GetName(WebInputEvent::Type type) {
263 #define CASE_TYPE(t) case WebInputEvent::t:  return #t
264   switch(type) {
265     CASE_TYPE(Undefined);
266     CASE_TYPE(MouseDown);
267     CASE_TYPE(MouseUp);
268     CASE_TYPE(MouseMove);
269     CASE_TYPE(MouseEnter);
270     CASE_TYPE(MouseLeave);
271     CASE_TYPE(ContextMenu);
272     CASE_TYPE(MouseWheel);
273     CASE_TYPE(RawKeyDown);
274     CASE_TYPE(KeyDown);
275     CASE_TYPE(KeyUp);
276     CASE_TYPE(Char);
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);
296     CASE_TYPE(TouchEnd);
297     CASE_TYPE(TouchCancel);
298     default:
299       // Must include default to let blink::WebInputEvent add new event types
300       // before they're added here.
301       DLOG(WARNING) <<
302           "Unhandled WebInputEvent type in WebInputEventTraits::GetName.\n";
303       break;
304   }
305 #undef CASE_TYPE
306   return "";
307 }
308 
GetSize(WebInputEvent::Type type)309 size_t WebInputEventTraits::GetSize(WebInputEvent::Type type) {
310   size_t size = 0;
311   Apply(WebInputEventSize(), type, type, &size);
312   return size;
313 }
314 
Clone(const WebInputEvent & event)315 ScopedWebInputEvent WebInputEventTraits::Clone(const WebInputEvent& event) {
316   ScopedWebInputEvent scoped_event;
317   Apply(WebInputEventClone(), event.type, event, &scoped_event);
318   return scoped_event.Pass();
319 }
320 
Delete(WebInputEvent * event)321 void WebInputEventTraits::Delete(WebInputEvent* event) {
322   if (!event)
323     return;
324   bool dummy_var = false;
325   Apply(WebInputEventDelete(), event->type, event, &dummy_var);
326 }
327 
CanCoalesce(const WebInputEvent & event_to_coalesce,const WebInputEvent & event)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)
332     return false;
333   return Apply(WebInputEventCanCoalesce(),
334                event.type,
335                event_to_coalesce,
336                &event);
337 }
338 
Coalesce(const WebInputEvent & event_to_coalesce,WebInputEvent * event)339 void WebInputEventTraits::Coalesce(const WebInputEvent& event_to_coalesce,
340                                    WebInputEvent* event) {
341   DCHECK(event);
342   Apply(WebInputEventCoalesce(), event->type, event_to_coalesce, event);
343 }
344 
IgnoresAckDisposition(const WebInputEvent & 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:
361       return true;
362     case WebInputEvent::TouchStart:
363     case WebInputEvent::TouchMove:
364     case WebInputEvent::TouchEnd:
365       return !static_cast<const WebTouchEvent&>(event).cancelable;
366     default:
367       return false;
368   }
369 }
370 
371 }  // namespace content
372