Home | History | Annotate | Download | only in input
      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 
     25 bool CanCoalesce(const WebKeyboardEvent& event_to_coalesce,
     26                  const WebKeyboardEvent& event) {
     27   return false;
     28 }
     29 
     30 void Coalesce(const WebKeyboardEvent& event_to_coalesce,
     31               WebKeyboardEvent* event) {
     32   DCHECK(CanCoalesce(event_to_coalesce, *event));
     33 }
     34 
     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 
     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 
     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 
     61 float GetUnacceleratedDelta(float accelerated_delta, float acceleration_ratio) {
     62   return accelerated_delta * acceleration_ratio;
     63 }
     64 
     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 
     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|.
     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 
    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 
    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 
    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 
    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>
    189   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>
    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)));
    202     return true;
    203   }
    204 };
    205 
    206 struct WebInputEventDelete {
    207   template <class EventType>
    208   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>
    219   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>
    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));
    236     return true;
    237   }
    238 };
    239 
    240 template <typename Operator, typename ArgIn, typename ArgOut>
    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 
    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 
    309 size_t WebInputEventTraits::GetSize(WebInputEvent::Type type) {
    310   size_t size = 0;
    311   Apply(WebInputEventSize(), type, type, &size);
    312   return size;
    313 }
    314 
    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 
    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 
    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 
    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 
    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