| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/renderer_host/ui_events_helper.h" |
| |
| #include "third_party/WebKit/public/web/WebInputEvent.h" |
| #include "ui/events/event.h" |
| #include "ui/events/event_constants.h" |
| |
| namespace { |
| |
| int WebModifiersToUIFlags(int modifiers) { |
| int flags = ui::EF_NONE; |
| |
| if (modifiers & WebKit::WebInputEvent::ShiftKey) |
| flags |= ui::EF_SHIFT_DOWN; |
| if (modifiers & WebKit::WebInputEvent::ControlKey) |
| flags |= ui::EF_CONTROL_DOWN; |
| if (modifiers & WebKit::WebInputEvent::AltKey) |
| flags |= ui::EF_ALT_DOWN; |
| |
| if (modifiers & WebKit::WebInputEvent::LeftButtonDown) |
| flags |= ui::EF_LEFT_MOUSE_BUTTON; |
| if (modifiers & WebKit::WebInputEvent::RightButtonDown) |
| flags |= ui::EF_RIGHT_MOUSE_BUTTON; |
| if (modifiers & WebKit::WebInputEvent::MiddleButtonDown) |
| flags |= ui::EF_MIDDLE_MOUSE_BUTTON; |
| |
| if (modifiers & WebKit::WebInputEvent::CapsLockOn) |
| flags |= ui::EF_CAPS_LOCK_DOWN; |
| |
| return flags; |
| } |
| |
| ui::EventType WebTouchPointStateToEventType( |
| WebKit::WebTouchPoint::State state) { |
| switch (state) { |
| case WebKit::WebTouchPoint::StateReleased: |
| return ui::ET_TOUCH_RELEASED; |
| |
| case WebKit::WebTouchPoint::StatePressed: |
| return ui::ET_TOUCH_PRESSED; |
| |
| case WebKit::WebTouchPoint::StateMoved: |
| return ui::ET_TOUCH_MOVED; |
| |
| case WebKit::WebTouchPoint::StateCancelled: |
| return ui::ET_TOUCH_CANCELLED; |
| |
| default: |
| return ui::ET_UNKNOWN; |
| } |
| } |
| |
| WebKit::WebTouchPoint::State TouchPointStateFromEvent( |
| const ui::TouchEvent& event) { |
| switch (event.type()) { |
| case ui::ET_TOUCH_PRESSED: |
| return WebKit::WebTouchPoint::StatePressed; |
| case ui::ET_TOUCH_RELEASED: |
| return WebKit::WebTouchPoint::StateReleased; |
| case ui::ET_TOUCH_MOVED: |
| return WebKit::WebTouchPoint::StateMoved; |
| case ui::ET_TOUCH_CANCELLED: |
| return WebKit::WebTouchPoint::StateCancelled; |
| default: |
| return WebKit::WebTouchPoint::StateUndefined; |
| } |
| } |
| |
| WebKit::WebInputEvent::Type TouchEventTypeFromEvent( |
| const ui::TouchEvent& event) { |
| switch (event.type()) { |
| case ui::ET_TOUCH_PRESSED: |
| return WebKit::WebInputEvent::TouchStart; |
| case ui::ET_TOUCH_RELEASED: |
| return WebKit::WebInputEvent::TouchEnd; |
| case ui::ET_TOUCH_MOVED: |
| return WebKit::WebInputEvent::TouchMove; |
| case ui::ET_TOUCH_CANCELLED: |
| return WebKit::WebInputEvent::TouchCancel; |
| default: |
| return WebKit::WebInputEvent::Undefined; |
| } |
| } |
| |
| } // namespace |
| |
| namespace content { |
| |
| bool MakeUITouchEventsFromWebTouchEvents( |
| const TouchEventWithLatencyInfo& touch_with_latency, |
| ScopedVector<ui::TouchEvent>* list, |
| TouchEventCoordinateSystem coordinate_system) { |
| const WebKit::WebTouchEvent& touch = touch_with_latency.event; |
| ui::EventType type = ui::ET_UNKNOWN; |
| switch (touch.type) { |
| case WebKit::WebInputEvent::TouchStart: |
| type = ui::ET_TOUCH_PRESSED; |
| break; |
| case WebKit::WebInputEvent::TouchEnd: |
| type = ui::ET_TOUCH_RELEASED; |
| break; |
| case WebKit::WebInputEvent::TouchMove: |
| type = ui::ET_TOUCH_MOVED; |
| break; |
| case WebKit::WebInputEvent::TouchCancel: |
| type = ui::ET_TOUCH_CANCELLED; |
| break; |
| default: |
| NOTREACHED(); |
| return false; |
| } |
| |
| int flags = WebModifiersToUIFlags(touch.modifiers); |
| base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds( |
| static_cast<int64>(touch.timeStampSeconds * 1000000)); |
| for (unsigned i = 0; i < touch.touchesLength; ++i) { |
| const WebKit::WebTouchPoint& point = touch.touches[i]; |
| if (WebTouchPointStateToEventType(point.state) != type) |
| continue; |
| // In aura, the touch-event needs to be in the screen coordinate, since the |
| // touch-event is routed to RootWindow first. In Windows, on the other hand, |
| // the touch-event is dispatched directly to the gesture-recognizer, so the |
| // location needs to be in the local coordinate space. |
| #if defined(USE_AURA) |
| gfx::Point location; |
| if (coordinate_system == LOCAL_COORDINATES) |
| location = gfx::Point(point.position.x, point.position.y); |
| else |
| location = gfx::Point(point.screenPosition.x, point.screenPosition.y); |
| #else |
| gfx::Point location(point.position.x, point.position.y); |
| #endif |
| ui::TouchEvent* uievent = new ui::TouchEvent(type, |
| location, |
| flags, |
| point.id, |
| timestamp, |
| point.radiusX, |
| point.radiusY, |
| point.rotationAngle, |
| point.force); |
| uievent->set_latency(touch_with_latency.latency); |
| list->push_back(uievent); |
| } |
| return true; |
| } |
| |
| WebKit::WebGestureEvent MakeWebGestureEventFromUIEvent( |
| const ui::GestureEvent& event) { |
| WebKit::WebGestureEvent gesture_event; |
| |
| switch (event.type()) { |
| case ui::ET_GESTURE_TAP: |
| gesture_event.type = WebKit::WebInputEvent::GestureTap; |
| gesture_event.data.tap.tapCount = event.details().tap_count(); |
| gesture_event.data.tap.width = event.details().bounding_box().width(); |
| gesture_event.data.tap.height = event.details().bounding_box().height(); |
| break; |
| case ui::ET_GESTURE_TAP_DOWN: |
| gesture_event.type = WebKit::WebInputEvent::GestureTapDown; |
| gesture_event.data.tapDown.width = |
| event.details().bounding_box().width(); |
| gesture_event.data.tapDown.height = |
| event.details().bounding_box().height(); |
| break; |
| case ui::ET_GESTURE_SHOW_PRESS: |
| gesture_event.type = WebKit::WebInputEvent::GestureShowPress; |
| gesture_event.data.showPress.width = |
| event.details().bounding_box().width(); |
| gesture_event.data.showPress.height = |
| event.details().bounding_box().height(); |
| break; |
| case ui::ET_GESTURE_TAP_CANCEL: |
| gesture_event.type = WebKit::WebInputEvent::GestureTapCancel; |
| break; |
| case ui::ET_GESTURE_SCROLL_BEGIN: |
| gesture_event.type = WebKit::WebInputEvent::GestureScrollBegin; |
| break; |
| case ui::ET_GESTURE_SCROLL_UPDATE: |
| gesture_event.type = WebKit::WebInputEvent::GestureScrollUpdate; |
| gesture_event.data.scrollUpdate.deltaX = event.details().scroll_x(); |
| gesture_event.data.scrollUpdate.deltaY = event.details().scroll_y(); |
| break; |
| case ui::ET_GESTURE_SCROLL_END: |
| gesture_event.type = WebKit::WebInputEvent::GestureScrollEnd; |
| break; |
| case ui::ET_GESTURE_PINCH_BEGIN: |
| gesture_event.type = WebKit::WebInputEvent::GesturePinchBegin; |
| break; |
| case ui::ET_GESTURE_PINCH_UPDATE: |
| gesture_event.type = WebKit::WebInputEvent::GesturePinchUpdate; |
| gesture_event.data.pinchUpdate.scale = event.details().scale(); |
| break; |
| case ui::ET_GESTURE_PINCH_END: |
| gesture_event.type = WebKit::WebInputEvent::GesturePinchEnd; |
| break; |
| case ui::ET_SCROLL_FLING_START: |
| gesture_event.type = WebKit::WebInputEvent::GestureFlingStart; |
| gesture_event.data.flingStart.velocityX = event.details().velocity_x(); |
| gesture_event.data.flingStart.velocityY = event.details().velocity_y(); |
| break; |
| case ui::ET_SCROLL_FLING_CANCEL: |
| gesture_event.type = WebKit::WebInputEvent::GestureFlingCancel; |
| break; |
| case ui::ET_GESTURE_LONG_PRESS: |
| gesture_event.type = WebKit::WebInputEvent::GestureLongPress; |
| gesture_event.data.longPress.width = |
| event.details().bounding_box().width(); |
| gesture_event.data.longPress.height = |
| event.details().bounding_box().height(); |
| break; |
| case ui::ET_GESTURE_LONG_TAP: |
| gesture_event.type = WebKit::WebInputEvent::GestureLongTap; |
| gesture_event.data.longPress.width = |
| event.details().bounding_box().width(); |
| gesture_event.data.longPress.height = |
| event.details().bounding_box().height(); |
| break; |
| case ui::ET_GESTURE_TWO_FINGER_TAP: |
| gesture_event.type = WebKit::WebInputEvent::GestureTwoFingerTap; |
| gesture_event.data.twoFingerTap.firstFingerWidth = |
| event.details().first_finger_width(); |
| gesture_event.data.twoFingerTap.firstFingerHeight = |
| event.details().first_finger_height(); |
| break; |
| case ui::ET_GESTURE_BEGIN: |
| case ui::ET_GESTURE_END: |
| case ui::ET_GESTURE_MULTIFINGER_SWIPE: |
| gesture_event.type = WebKit::WebInputEvent::Undefined; |
| break; |
| default: |
| NOTREACHED() << "Unknown gesture type: " << event.type(); |
| } |
| |
| gesture_event.sourceDevice = WebKit::WebGestureEvent::Touchscreen; |
| gesture_event.modifiers = EventFlagsToWebEventModifiers(event.flags()); |
| gesture_event.timeStampSeconds = event.time_stamp().InSecondsF(); |
| |
| return gesture_event; |
| } |
| |
| int EventFlagsToWebEventModifiers(int flags) { |
| int modifiers = 0; |
| |
| if (flags & ui::EF_SHIFT_DOWN) |
| modifiers |= WebKit::WebInputEvent::ShiftKey; |
| if (flags & ui::EF_CONTROL_DOWN) |
| modifiers |= WebKit::WebInputEvent::ControlKey; |
| if (flags & ui::EF_ALT_DOWN) |
| modifiers |= WebKit::WebInputEvent::AltKey; |
| // TODO(beng): MetaKey/META_MASK |
| if (flags & ui::EF_LEFT_MOUSE_BUTTON) |
| modifiers |= WebKit::WebInputEvent::LeftButtonDown; |
| if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) |
| modifiers |= WebKit::WebInputEvent::MiddleButtonDown; |
| if (flags & ui::EF_RIGHT_MOUSE_BUTTON) |
| modifiers |= WebKit::WebInputEvent::RightButtonDown; |
| if (flags & ui::EF_CAPS_LOCK_DOWN) |
| modifiers |= WebKit::WebInputEvent::CapsLockOn; |
| return modifiers; |
| } |
| |
| WebKit::WebTouchPoint* UpdateWebTouchEventFromUIEvent( |
| const ui::TouchEvent& event, |
| WebKit::WebTouchEvent* web_event) { |
| WebKit::WebTouchPoint* point = NULL; |
| switch (event.type()) { |
| case ui::ET_TOUCH_PRESSED: |
| // Add a new touch point. |
| if (web_event->touchesLength < WebKit::WebTouchEvent::touchesLengthCap) { |
| point = &web_event->touches[web_event->touchesLength++]; |
| point->id = event.touch_id(); |
| } |
| break; |
| case ui::ET_TOUCH_RELEASED: |
| case ui::ET_TOUCH_CANCELLED: |
| case ui::ET_TOUCH_MOVED: { |
| // The touch point should have been added to the event from an earlier |
| // _PRESSED event. So find that. |
| // At the moment, only a maximum of 4 touch-points are allowed. So a |
| // simple loop should be sufficient. |
| for (unsigned i = 0; i < web_event->touchesLength; ++i) { |
| point = web_event->touches + i; |
| if (point->id == event.touch_id()) |
| break; |
| point = NULL; |
| } |
| break; |
| } |
| default: |
| DLOG(WARNING) << "Unknown touch event " << event.type(); |
| break; |
| } |
| |
| if (!point) |
| return NULL; |
| |
| // The spec requires the radii values to be positive (and 1 when unknown). |
| point->radiusX = std::max(1.f, event.radius_x()); |
| point->radiusY = std::max(1.f, event.radius_y()); |
| point->rotationAngle = event.rotation_angle(); |
| point->force = event.force(); |
| |
| // Update the location and state of the point. |
| point->state = TouchPointStateFromEvent(event); |
| if (point->state == WebKit::WebTouchPoint::StateMoved) { |
| // It is possible for badly written touch drivers to emit Move events even |
| // when the touch location hasn't changed. In such cases, consume the event |
| // and pretend nothing happened. |
| if (point->position.x == event.x() && point->position.y == event.y()) |
| return NULL; |
| } |
| point->position.x = event.x(); |
| point->position.y = event.y(); |
| |
| const gfx::Point root_point = event.root_location(); |
| point->screenPosition.x = root_point.x(); |
| point->screenPosition.y = root_point.y(); |
| |
| // Mark the rest of the points as stationary. |
| for (unsigned i = 0; i < web_event->touchesLength; ++i) { |
| WebKit::WebTouchPoint* iter = web_event->touches + i; |
| if (iter != point) |
| iter->state = WebKit::WebTouchPoint::StateStationary; |
| } |
| |
| // Update the type of the touch event. |
| web_event->type = TouchEventTypeFromEvent(event); |
| web_event->timeStampSeconds = event.time_stamp().InSecondsF(); |
| web_event->modifiers = EventFlagsToWebEventModifiers(event.flags()); |
| |
| return point; |
| } |
| |
| } // namespace content |