blob: d1fbcfa0279e46ad345c774f363ff0cd7d1d6f1a [file] [log] [blame]
// 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