Home | History | Annotate | Download | only in renderer_host
      1 // Copyright (c) 2012 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/browser/renderer_host/ui_events_helper.h"
      6 
      7 #include "third_party/WebKit/public/web/WebInputEvent.h"
      8 #include "ui/base/events/event.h"
      9 #include "ui/base/events/event_constants.h"
     10 
     11 namespace {
     12 
     13 int WebModifiersToUIFlags(int modifiers) {
     14   int flags = ui::EF_NONE;
     15 
     16   if (modifiers & WebKit::WebInputEvent::ShiftKey)
     17     flags |= ui::EF_SHIFT_DOWN;
     18   if (modifiers & WebKit::WebInputEvent::ControlKey)
     19     flags |= ui::EF_CONTROL_DOWN;
     20   if (modifiers & WebKit::WebInputEvent::AltKey)
     21     flags |= ui::EF_ALT_DOWN;
     22 
     23   if (modifiers & WebKit::WebInputEvent::LeftButtonDown)
     24     flags |= ui::EF_LEFT_MOUSE_BUTTON;
     25   if (modifiers & WebKit::WebInputEvent::RightButtonDown)
     26     flags |= ui::EF_RIGHT_MOUSE_BUTTON;
     27   if (modifiers & WebKit::WebInputEvent::MiddleButtonDown)
     28     flags |= ui::EF_MIDDLE_MOUSE_BUTTON;
     29 
     30   if (modifiers & WebKit::WebInputEvent::CapsLockOn)
     31     flags |= ui::EF_CAPS_LOCK_DOWN;
     32 
     33   return flags;
     34 }
     35 
     36 ui::EventType WebTouchPointStateToEventType(
     37     WebKit::WebTouchPoint::State state) {
     38   switch (state) {
     39     case WebKit::WebTouchPoint::StateReleased:
     40       return ui::ET_TOUCH_RELEASED;
     41 
     42     case WebKit::WebTouchPoint::StatePressed:
     43       return ui::ET_TOUCH_PRESSED;
     44 
     45     case WebKit::WebTouchPoint::StateMoved:
     46       return ui::ET_TOUCH_MOVED;
     47 
     48     case WebKit::WebTouchPoint::StateCancelled:
     49       return ui::ET_TOUCH_CANCELLED;
     50 
     51     default:
     52       return ui::ET_UNKNOWN;
     53   }
     54 }
     55 
     56 WebKit::WebTouchPoint::State TouchPointStateFromEvent(
     57     const ui::TouchEvent& event) {
     58   switch (event.type()) {
     59     case ui::ET_TOUCH_PRESSED:
     60       return WebKit::WebTouchPoint::StatePressed;
     61     case ui::ET_TOUCH_RELEASED:
     62       return WebKit::WebTouchPoint::StateReleased;
     63     case ui::ET_TOUCH_MOVED:
     64       return WebKit::WebTouchPoint::StateMoved;
     65     case ui::ET_TOUCH_CANCELLED:
     66       return WebKit::WebTouchPoint::StateCancelled;
     67     default:
     68       return WebKit::WebTouchPoint::StateUndefined;
     69   }
     70 }
     71 
     72 WebKit::WebInputEvent::Type TouchEventTypeFromEvent(
     73     const ui::TouchEvent& event) {
     74   switch (event.type()) {
     75     case ui::ET_TOUCH_PRESSED:
     76       return WebKit::WebInputEvent::TouchStart;
     77     case ui::ET_TOUCH_RELEASED:
     78       return WebKit::WebInputEvent::TouchEnd;
     79     case ui::ET_TOUCH_MOVED:
     80       return WebKit::WebInputEvent::TouchMove;
     81     case ui::ET_TOUCH_CANCELLED:
     82       return WebKit::WebInputEvent::TouchCancel;
     83     default:
     84       return WebKit::WebInputEvent::Undefined;
     85   }
     86 }
     87 
     88 }  // namespace
     89 
     90 namespace content {
     91 
     92 bool MakeUITouchEventsFromWebTouchEvents(
     93     const TouchEventWithLatencyInfo& touch_with_latency,
     94     ScopedVector<ui::TouchEvent>* list,
     95     TouchEventCoordinateSystem coordinate_system) {
     96   const WebKit::WebTouchEvent& touch = touch_with_latency.event;
     97   ui::EventType type = ui::ET_UNKNOWN;
     98   switch (touch.type) {
     99     case WebKit::WebInputEvent::TouchStart:
    100       type = ui::ET_TOUCH_PRESSED;
    101       break;
    102     case WebKit::WebInputEvent::TouchEnd:
    103       type = ui::ET_TOUCH_RELEASED;
    104       break;
    105     case WebKit::WebInputEvent::TouchMove:
    106       type = ui::ET_TOUCH_MOVED;
    107       break;
    108     case WebKit::WebInputEvent::TouchCancel:
    109       type = ui::ET_TOUCH_CANCELLED;
    110       break;
    111     default:
    112       NOTREACHED();
    113       return false;
    114   }
    115 
    116   int flags = WebModifiersToUIFlags(touch.modifiers);
    117   base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(
    118       static_cast<int64>(touch.timeStampSeconds * 1000000));
    119   for (unsigned i = 0; i < touch.touchesLength; ++i) {
    120     const WebKit::WebTouchPoint& point = touch.touches[i];
    121     if (WebTouchPointStateToEventType(point.state) != type)
    122       continue;
    123     // In aura, the touch-event needs to be in the screen coordinate, since the
    124     // touch-event is routed to RootWindow first. In Windows, on the other hand,
    125     // the touch-event is dispatched directly to the gesture-recognizer, so the
    126     // location needs to be in the local coordinate space.
    127 #if defined(USE_AURA)
    128     gfx::Point location;
    129     if (coordinate_system == LOCAL_COORDINATES)
    130       location = gfx::Point(point.position.x, point.position.y);
    131     else
    132       location = gfx::Point(point.screenPosition.x, point.screenPosition.y);
    133 #else
    134     gfx::Point location(point.position.x, point.position.y);
    135 #endif
    136     ui::TouchEvent* uievent = new ui::TouchEvent(type,
    137           location,
    138           flags,
    139           point.id,
    140           timestamp,
    141           point.radiusX,
    142           point.radiusY,
    143           point.rotationAngle,
    144           point.force);
    145     uievent->set_latency(touch_with_latency.latency);
    146     list->push_back(uievent);
    147   }
    148   return true;
    149 }
    150 
    151 WebKit::WebGestureEvent MakeWebGestureEventFromUIEvent(
    152     const ui::GestureEvent& event) {
    153   WebKit::WebGestureEvent gesture_event;
    154 
    155   switch (event.type()) {
    156     case ui::ET_GESTURE_TAP:
    157       gesture_event.type = WebKit::WebInputEvent::GestureTap;
    158       gesture_event.data.tap.tapCount = event.details().tap_count();
    159       gesture_event.data.tap.width = event.details().bounding_box().width();
    160       gesture_event.data.tap.height = event.details().bounding_box().height();
    161       break;
    162     case ui::ET_GESTURE_TAP_DOWN:
    163       gesture_event.type = WebKit::WebInputEvent::GestureTapDown;
    164       gesture_event.data.tapDown.width =
    165           event.details().bounding_box().width();
    166       gesture_event.data.tapDown.height =
    167           event.details().bounding_box().height();
    168       break;
    169     case ui::ET_GESTURE_TAP_CANCEL:
    170       gesture_event.type = WebKit::WebInputEvent::GestureTapCancel;
    171       break;
    172     case ui::ET_GESTURE_SCROLL_BEGIN:
    173       gesture_event.type = WebKit::WebInputEvent::GestureScrollBegin;
    174       break;
    175     case ui::ET_GESTURE_SCROLL_UPDATE:
    176       gesture_event.type = WebKit::WebInputEvent::GestureScrollUpdate;
    177       gesture_event.data.scrollUpdate.deltaX = event.details().scroll_x();
    178       gesture_event.data.scrollUpdate.deltaY = event.details().scroll_y();
    179       break;
    180     case ui::ET_GESTURE_SCROLL_END:
    181       gesture_event.type = WebKit::WebInputEvent::GestureScrollEnd;
    182       break;
    183     case ui::ET_GESTURE_PINCH_BEGIN:
    184       gesture_event.type = WebKit::WebInputEvent::GesturePinchBegin;
    185       break;
    186     case ui::ET_GESTURE_PINCH_UPDATE:
    187       gesture_event.type = WebKit::WebInputEvent::GesturePinchUpdate;
    188       gesture_event.data.pinchUpdate.scale = event.details().scale();
    189       break;
    190     case ui::ET_GESTURE_PINCH_END:
    191       gesture_event.type = WebKit::WebInputEvent::GesturePinchEnd;
    192       break;
    193     case ui::ET_SCROLL_FLING_START:
    194       gesture_event.type = WebKit::WebInputEvent::GestureFlingStart;
    195       gesture_event.data.flingStart.velocityX = event.details().velocity_x();
    196       gesture_event.data.flingStart.velocityY = event.details().velocity_y();
    197       break;
    198     case ui::ET_SCROLL_FLING_CANCEL:
    199       gesture_event.type = WebKit::WebInputEvent::GestureFlingCancel;
    200       break;
    201     case ui::ET_GESTURE_LONG_PRESS:
    202       gesture_event.type = WebKit::WebInputEvent::GestureLongPress;
    203       gesture_event.data.longPress.width =
    204           event.details().bounding_box().width();
    205       gesture_event.data.longPress.height =
    206           event.details().bounding_box().height();
    207       break;
    208     case ui::ET_GESTURE_LONG_TAP:
    209       gesture_event.type = WebKit::WebInputEvent::GestureLongTap;
    210       gesture_event.data.longPress.width =
    211           event.details().bounding_box().width();
    212       gesture_event.data.longPress.height =
    213           event.details().bounding_box().height();
    214       break;
    215     case ui::ET_GESTURE_TWO_FINGER_TAP:
    216       gesture_event.type = WebKit::WebInputEvent::GestureTwoFingerTap;
    217       gesture_event.data.twoFingerTap.firstFingerWidth =
    218           event.details().first_finger_width();
    219       gesture_event.data.twoFingerTap.firstFingerHeight =
    220           event.details().first_finger_height();
    221       break;
    222     case ui::ET_GESTURE_BEGIN:
    223     case ui::ET_GESTURE_END:
    224     case ui::ET_GESTURE_MULTIFINGER_SWIPE:
    225       gesture_event.type = WebKit::WebInputEvent::Undefined;
    226       break;
    227     default:
    228       NOTREACHED() << "Unknown gesture type: " << event.type();
    229   }
    230 
    231   gesture_event.sourceDevice = WebKit::WebGestureEvent::Touchscreen;
    232   gesture_event.modifiers = EventFlagsToWebEventModifiers(event.flags());
    233   gesture_event.timeStampSeconds = event.time_stamp().InSecondsF();
    234 
    235   return gesture_event;
    236 }
    237 
    238 int EventFlagsToWebEventModifiers(int flags) {
    239   int modifiers = 0;
    240 
    241   if (flags & ui::EF_SHIFT_DOWN)
    242     modifiers |= WebKit::WebInputEvent::ShiftKey;
    243   if (flags & ui::EF_CONTROL_DOWN)
    244     modifiers |= WebKit::WebInputEvent::ControlKey;
    245   if (flags & ui::EF_ALT_DOWN)
    246     modifiers |= WebKit::WebInputEvent::AltKey;
    247   // TODO(beng): MetaKey/META_MASK
    248   if (flags & ui::EF_LEFT_MOUSE_BUTTON)
    249     modifiers |= WebKit::WebInputEvent::LeftButtonDown;
    250   if (flags & ui::EF_MIDDLE_MOUSE_BUTTON)
    251     modifiers |= WebKit::WebInputEvent::MiddleButtonDown;
    252   if (flags & ui::EF_RIGHT_MOUSE_BUTTON)
    253     modifiers |= WebKit::WebInputEvent::RightButtonDown;
    254   if (flags & ui::EF_CAPS_LOCK_DOWN)
    255     modifiers |= WebKit::WebInputEvent::CapsLockOn;
    256   return modifiers;
    257 }
    258 
    259 WebKit::WebTouchPoint* UpdateWebTouchEventFromUIEvent(
    260     const ui::TouchEvent& event,
    261     WebKit::WebTouchEvent* web_event) {
    262   WebKit::WebTouchPoint* point = NULL;
    263   switch (event.type()) {
    264     case ui::ET_TOUCH_PRESSED:
    265       // Add a new touch point.
    266       if (web_event->touchesLength < WebKit::WebTouchEvent::touchesLengthCap) {
    267         point = &web_event->touches[web_event->touchesLength++];
    268         point->id = event.touch_id();
    269       }
    270       break;
    271     case ui::ET_TOUCH_RELEASED:
    272     case ui::ET_TOUCH_CANCELLED:
    273     case ui::ET_TOUCH_MOVED: {
    274       // The touch point should have been added to the event from an earlier
    275       // _PRESSED event. So find that.
    276       // At the moment, only a maximum of 4 touch-points are allowed. So a
    277       // simple loop should be sufficient.
    278       for (unsigned i = 0; i < web_event->touchesLength; ++i) {
    279         point = web_event->touches + i;
    280         if (point->id == event.touch_id())
    281           break;
    282         point = NULL;
    283       }
    284       break;
    285     }
    286     default:
    287       DLOG(WARNING) << "Unknown touch event " << event.type();
    288       break;
    289   }
    290 
    291   if (!point)
    292     return NULL;
    293 
    294   // The spec requires the radii values to be positive (and 1 when unknown).
    295   point->radiusX = std::max(1.f, event.radius_x());
    296   point->radiusY = std::max(1.f, event.radius_y());
    297   point->rotationAngle = event.rotation_angle();
    298   point->force = event.force();
    299 
    300   // Update the location and state of the point.
    301   point->state = TouchPointStateFromEvent(event);
    302   if (point->state == WebKit::WebTouchPoint::StateMoved) {
    303     // It is possible for badly written touch drivers to emit Move events even
    304     // when the touch location hasn't changed. In such cases, consume the event
    305     // and pretend nothing happened.
    306     if (point->position.x == event.x() && point->position.y == event.y())
    307       return NULL;
    308   }
    309   point->position.x = event.x();
    310   point->position.y = event.y();
    311 
    312   const gfx::Point root_point = event.root_location();
    313   point->screenPosition.x = root_point.x();
    314   point->screenPosition.y = root_point.y();
    315 
    316   // Mark the rest of the points as stationary.
    317   for (unsigned i = 0; i < web_event->touchesLength; ++i) {
    318     WebKit::WebTouchPoint* iter = web_event->touches + i;
    319     if (iter != point)
    320       iter->state = WebKit::WebTouchPoint::StateStationary;
    321   }
    322 
    323   // Update the type of the touch event.
    324   web_event->type = TouchEventTypeFromEvent(event);
    325   web_event->timeStampSeconds = event.time_stamp().InSecondsF();
    326   web_event->modifiers = EventFlagsToWebEventModifiers(event.flags());
    327 
    328   return point;
    329 }
    330 
    331 }  // namespace content
    332