Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / content / common / input / web_input_event_traits.cc
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}",
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 }
75
76 void ApppendEventDetails(const WebGestureEvent& event, std::string* result) {
77   StringAppendF(result,
78                 "{\n Pos: (%d, %d)\n GlobalPos: (%d, %d)\n SourceDevice: %d\n"
79                 " RawData: (%f, %f, %f, %f)\n}",
80                 event.x,
81                 event.y,
82                 event.globalX,
83                 event.globalY,
84                 event.sourceDevice,
85                 event.data.scrollUpdate.deltaX,
86                 event.data.scrollUpdate.deltaY,
87                 event.data.scrollUpdate.velocityX,
88                 event.data.scrollUpdate.velocityY);
89 }
90
91 void ApppendTouchPointDetails(const WebTouchPoint& point, std::string* result) {
92   StringAppendF(result,
93                  "  (ID: %d, State: %d, ScreenPos: (%f, %f), Pos: (%f, %f),"
94                      " Radius: (%f, %f), Rot: %f, Force: %f),\n",
95   point.id,
96   point.state,
97   point.screenPosition.x,
98   point.screenPosition.y,
99   point.position.x,
100   point.position.y,
101   point.radiusX,
102   point.radiusY,
103   point.rotationAngle,
104   point.force);
105 }
106
107 void ApppendEventDetails(const WebTouchEvent& event, std::string* result) {
108   StringAppendF(result,
109                 "{\n Touches: %u, Cancelable: %d,\n[\n",
110                 event.touchesLength,
111                 event.cancelable);
112   for (unsigned i = 0; i < event.touchesLength; ++i)
113     ApppendTouchPointDetails(event.touches[i], result);
114   result->append(" ]\n}");
115 }
116
117 bool CanCoalesce(const WebKeyboardEvent& event_to_coalesce,
118                  const WebKeyboardEvent& event) {
119   return false;
120 }
121
122 void Coalesce(const WebKeyboardEvent& event_to_coalesce,
123               WebKeyboardEvent* event) {
124   DCHECK(CanCoalesce(event_to_coalesce, *event));
125 }
126
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;
131 }
132
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;
141 }
142
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;
151 }
152
153 float GetUnacceleratedDelta(float accelerated_delta, float acceleration_ratio) {
154   return accelerated_delta * acceleration_ratio;
155 }
156
157 float GetAccelerationRatio(float accelerated_delta, float unaccelerated_delta) {
158   if (unaccelerated_delta == 0.f || accelerated_delta == 0.f)
159     return 1.f;
160   return unaccelerated_delta / accelerated_delta;
161 }
162
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;
186 }
187
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)
192       return i;
193   }
194   return kInvalidTouchIndex;
195 }
196
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)
204     return false;
205
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)
215       return false;
216     if (!unmatched_event_touches[event_touch_index])
217       return false;
218     unmatched_event_touches[event_touch_index] = false;
219   }
220   return unmatched_event_touches.none();
221 }
222
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;
237   }
238 }
239
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)
245     return false;
246
247   if (event.type == WebInputEvent::GestureScrollUpdate)
248     return true;
249
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)
255     return true;
256
257   return false;
258 }
259
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();
276   }
277 }
278
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,
285                   event.modifiers);
286     const EventType& typed_event = static_cast<const EventType&>(event);
287     ApppendEventDetails(typed_event, result);
288     return true;
289   }
290 };
291
292 struct WebInputEventSize {
293   template <class EventType>
294   bool Execute(WebInputEvent::Type /* type */, size_t* type_size) const {
295     *type_size = sizeof(EventType);
296     return true;
297   }
298 };
299
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)));
307     return true;
308   }
309 };
310
311 struct WebInputEventDelete {
312   template <class EventType>
313   bool Execute(WebInputEvent* event, bool* /* dummy_var */) const {
314     if (!event)
315       return false;
316     DCHECK_EQ(sizeof(EventType), event->size);
317     delete static_cast<EventType*>(event);
318     return true;
319   }
320 };
321
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)
327       return false;
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));
332   }
333 };
334
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));
341     return true;
342   }
343 };
344
345 template <typename Operator, typename ArgIn, typename ArgOut>
346 bool Apply(Operator op,
347            WebInputEvent::Type type,
348            const ArgIn& arg_in,
349            ArgOut* arg_out) {
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);
360
361   NOTREACHED() << "Unknown webkit event type " << type;
362   return false;
363 }
364
365 }  // namespace
366
367 const char* WebInputEventTraits::GetName(WebInputEvent::Type type) {
368 #define CASE_TYPE(t) case WebInputEvent::t:  return #t
369   switch(type) {
370     CASE_TYPE(Undefined);
371     CASE_TYPE(MouseDown);
372     CASE_TYPE(MouseUp);
373     CASE_TYPE(MouseMove);
374     CASE_TYPE(MouseEnter);
375     CASE_TYPE(MouseLeave);
376     CASE_TYPE(ContextMenu);
377     CASE_TYPE(MouseWheel);
378     CASE_TYPE(RawKeyDown);
379     CASE_TYPE(KeyDown);
380     CASE_TYPE(KeyUp);
381     CASE_TYPE(Char);
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);
401     CASE_TYPE(TouchEnd);
402     CASE_TYPE(TouchCancel);
403     default:
404       // Must include default to let blink::WebInputEvent add new event types
405       // before they're added here.
406       DLOG(WARNING) <<
407           "Unhandled WebInputEvent type in WebInputEventTraits::GetName.\n";
408       break;
409   }
410 #undef CASE_TYPE
411   return "";
412 }
413
414 std::string WebInputEventTraits::ToString(const WebInputEvent& event) {
415   std::string result;
416   Apply(WebInputEventToString(), event.type, event, &result);
417   return result;
418 }
419
420 size_t WebInputEventTraits::GetSize(WebInputEvent::Type type) {
421   size_t size = 0;
422   Apply(WebInputEventSize(), type, type, &size);
423   return size;
424 }
425
426 ScopedWebInputEvent WebInputEventTraits::Clone(const WebInputEvent& event) {
427   ScopedWebInputEvent scoped_event;
428   Apply(WebInputEventClone(), event.type, event, &scoped_event);
429   return scoped_event.Pass();
430 }
431
432 void WebInputEventTraits::Delete(WebInputEvent* event) {
433   if (!event)
434     return;
435   bool dummy_var = false;
436   Apply(WebInputEventDelete(), event->type, event, &dummy_var);
437 }
438
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)
443     return false;
444   return Apply(WebInputEventCanCoalesce(),
445                event.type,
446                event_to_coalesce,
447                &event);
448 }
449
450 void WebInputEventTraits::Coalesce(const WebInputEvent& event_to_coalesce,
451                                    WebInputEvent* event) {
452   DCHECK(event);
453   Apply(WebInputEventCoalesce(), event->type, event_to_coalesce, event);
454 }
455
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:
472       return true;
473     case WebInputEvent::TouchStart:
474     case WebInputEvent::TouchMove:
475     case WebInputEvent::TouchEnd:
476       return !static_cast<const WebTouchEvent&>(event).cancelable;
477     default:
478       return false;
479   }
480 }
481
482 }  // namespace content