// Copyright 2013 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/input/web_input_event_util.h" #include "base/strings/string_util.h" #include "content/common/input/web_touch_event_traits.h" #include "ui/events/gesture_detection/gesture_event_data.h" #include "ui/events/gesture_detection/motion_event.h" using blink::WebGestureEvent; using blink::WebInputEvent; using blink::WebTouchEvent; using blink::WebTouchPoint; using ui::MotionEvent; namespace { const char* GetKeyIdentifier(ui::KeyboardCode key_code) { switch (key_code) { case ui::VKEY_MENU: return "Alt"; case ui::VKEY_CONTROL: return "Control"; case ui::VKEY_SHIFT: return "Shift"; case ui::VKEY_CAPITAL: return "CapsLock"; case ui::VKEY_LWIN: case ui::VKEY_RWIN: return "Win"; case ui::VKEY_CLEAR: return "Clear"; case ui::VKEY_DOWN: return "Down"; case ui::VKEY_END: return "End"; case ui::VKEY_RETURN: return "Enter"; case ui::VKEY_EXECUTE: return "Execute"; case ui::VKEY_F1: return "F1"; case ui::VKEY_F2: return "F2"; case ui::VKEY_F3: return "F3"; case ui::VKEY_F4: return "F4"; case ui::VKEY_F5: return "F5"; case ui::VKEY_F6: return "F6"; case ui::VKEY_F7: return "F7"; case ui::VKEY_F8: return "F8"; case ui::VKEY_F9: return "F9"; case ui::VKEY_F10: return "F10"; case ui::VKEY_F11: return "F11"; case ui::VKEY_F12: return "F12"; case ui::VKEY_F13: return "F13"; case ui::VKEY_F14: return "F14"; case ui::VKEY_F15: return "F15"; case ui::VKEY_F16: return "F16"; case ui::VKEY_F17: return "F17"; case ui::VKEY_F18: return "F18"; case ui::VKEY_F19: return "F19"; case ui::VKEY_F20: return "F20"; case ui::VKEY_F21: return "F21"; case ui::VKEY_F22: return "F22"; case ui::VKEY_F23: return "F23"; case ui::VKEY_F24: return "F24"; case ui::VKEY_HELP: return "Help"; case ui::VKEY_HOME: return "Home"; case ui::VKEY_INSERT: return "Insert"; case ui::VKEY_LEFT: return "Left"; case ui::VKEY_NEXT: return "PageDown"; case ui::VKEY_PRIOR: return "PageUp"; case ui::VKEY_PAUSE: return "Pause"; case ui::VKEY_SNAPSHOT: return "PrintScreen"; case ui::VKEY_RIGHT: return "Right"; case ui::VKEY_SCROLL: return "Scroll"; case ui::VKEY_SELECT: return "Select"; case ui::VKEY_UP: return "Up"; case ui::VKEY_DELETE: return "U+007F"; // Standard says that DEL becomes U+007F. case ui::VKEY_MEDIA_NEXT_TRACK: return "MediaNextTrack"; case ui::VKEY_MEDIA_PREV_TRACK: return "MediaPreviousTrack"; case ui::VKEY_MEDIA_STOP: return "MediaStop"; case ui::VKEY_MEDIA_PLAY_PAUSE: return "MediaPlayPause"; case ui::VKEY_VOLUME_MUTE: return "VolumeMute"; case ui::VKEY_VOLUME_DOWN: return "VolumeDown"; case ui::VKEY_VOLUME_UP: return "VolumeUp"; default: return NULL; }; } WebInputEvent::Type ToWebInputEventType(MotionEvent::Action action) { switch (action) { case MotionEvent::ACTION_DOWN: return WebInputEvent::TouchStart; case MotionEvent::ACTION_MOVE: return WebInputEvent::TouchMove; case MotionEvent::ACTION_UP: return WebInputEvent::TouchEnd; case MotionEvent::ACTION_CANCEL: return WebInputEvent::TouchCancel; case MotionEvent::ACTION_POINTER_DOWN: return WebInputEvent::TouchStart; case MotionEvent::ACTION_POINTER_UP: return WebInputEvent::TouchEnd; } NOTREACHED() << "Invalid MotionEvent::Action."; return WebInputEvent::Undefined; } // Note that |is_action_pointer| is meaningful only in the context of // |ACTION_POINTER_UP| and |ACTION_POINTER_DOWN|; other actions map directly to // WebTouchPoint::State. WebTouchPoint::State ToWebTouchPointState(MotionEvent::Action action, bool is_action_pointer) { switch (action) { case MotionEvent::ACTION_DOWN: return WebTouchPoint::StatePressed; case MotionEvent::ACTION_MOVE: return WebTouchPoint::StateMoved; case MotionEvent::ACTION_UP: return WebTouchPoint::StateReleased; case MotionEvent::ACTION_CANCEL: return WebTouchPoint::StateCancelled; case MotionEvent::ACTION_POINTER_DOWN: return is_action_pointer ? WebTouchPoint::StatePressed : WebTouchPoint::StateStationary; case MotionEvent::ACTION_POINTER_UP: return is_action_pointer ? WebTouchPoint::StateReleased : WebTouchPoint::StateStationary; } NOTREACHED() << "Invalid MotionEvent::Action."; return WebTouchPoint::StateUndefined; } WebTouchPoint CreateWebTouchPoint(const MotionEvent& event, size_t pointer_index) { WebTouchPoint touch; touch.id = event.GetPointerId(pointer_index); touch.state = ToWebTouchPointState( event.GetAction(), static_cast(pointer_index) == event.GetActionIndex()); touch.position.x = event.GetX(pointer_index); touch.position.y = event.GetY(pointer_index); // TODO(joth): Raw event co-ordinates. touch.screenPosition = touch.position; touch.radiusX = touch.radiusY = event.GetTouchMajor(pointer_index) * 0.5f; touch.force = event.GetPressure(pointer_index); return touch; } } // namespace namespace content { void UpdateWindowsKeyCodeAndKeyIdentifier(blink::WebKeyboardEvent* event, ui::KeyboardCode windows_key_code) { event->windowsKeyCode = windows_key_code; const char* id = GetKeyIdentifier(windows_key_code); if (id) { base::strlcpy(event->keyIdentifier, id, sizeof(event->keyIdentifier) - 1); } else { base::snprintf(event->keyIdentifier, sizeof(event->keyIdentifier), "U+%04X", base::ToUpperASCII(static_cast(windows_key_code))); } } blink::WebTouchEvent CreateWebTouchEventFromMotionEvent( const ui::MotionEvent& event) { blink::WebTouchEvent result; WebTouchEventTraits::ResetType( ToWebInputEventType(event.GetAction()), (event.GetEventTime() - base::TimeTicks()).InSecondsF(), &result); result.touchesLength = std::min(event.GetPointerCount(), static_cast(WebTouchEvent::touchesLengthCap)); DCHECK_GT(result.touchesLength, 0U); for (size_t i = 0; i < result.touchesLength; ++i) result.touches[i] = CreateWebTouchPoint(event, i); return result; } WebGestureEvent CreateWebGestureEventFromGestureEventData( const ui::GestureEventData& data) { WebGestureEvent gesture; gesture.x = data.x; gesture.y = data.y; gesture.timeStampSeconds = (data.time - base::TimeTicks()).InSecondsF(); gesture.sourceDevice = blink::WebGestureDeviceTouchscreen; switch (data.type()) { case ui::ET_GESTURE_SHOW_PRESS: gesture.type = WebInputEvent::GestureShowPress; gesture.data.showPress.width = data.details.bounding_box_f().width(); gesture.data.showPress.height = data.details.bounding_box_f().height(); break; case ui::ET_GESTURE_DOUBLE_TAP: gesture.type = WebInputEvent::GestureDoubleTap; DCHECK_EQ(1, data.details.tap_count()); gesture.data.tap.tapCount = data.details.tap_count(); gesture.data.tap.width = data.details.bounding_box_f().width(); gesture.data.tap.height = data.details.bounding_box_f().height(); break; case ui::ET_GESTURE_TAP: gesture.type = WebInputEvent::GestureTap; DCHECK_EQ(1, data.details.tap_count()); gesture.data.tap.tapCount = data.details.tap_count(); gesture.data.tap.width = data.details.bounding_box_f().width(); gesture.data.tap.height = data.details.bounding_box_f().height(); break; case ui::ET_GESTURE_TAP_UNCONFIRMED: gesture.type = WebInputEvent::GestureTapUnconfirmed; DCHECK_EQ(1, data.details.tap_count()); gesture.data.tap.tapCount = data.details.tap_count(); gesture.data.tap.width = data.details.bounding_box_f().width(); gesture.data.tap.height = data.details.bounding_box_f().height(); break; case ui::ET_GESTURE_LONG_PRESS: gesture.type = WebInputEvent::GestureLongPress; gesture.data.longPress.width = data.details.bounding_box_f().width(); gesture.data.longPress.height = data.details.bounding_box_f().height(); break; case ui::ET_GESTURE_LONG_TAP: gesture.type = WebInputEvent::GestureLongTap; gesture.data.longPress.width = data.details.bounding_box_f().width(); gesture.data.longPress.height = data.details.bounding_box_f().height(); break; case ui::ET_GESTURE_SCROLL_BEGIN: gesture.type = WebInputEvent::GestureScrollBegin; gesture.data.scrollBegin.deltaXHint = data.details.scroll_x_hint(); gesture.data.scrollBegin.deltaYHint = data.details.scroll_y_hint(); break; case ui::ET_GESTURE_SCROLL_UPDATE: gesture.type = WebInputEvent::GestureScrollUpdate; gesture.data.scrollUpdate.deltaX = data.details.scroll_x(); gesture.data.scrollUpdate.deltaY = data.details.scroll_y(); break; case ui::ET_GESTURE_SCROLL_END: gesture.type = WebInputEvent::GestureScrollEnd; break; case ui::ET_SCROLL_FLING_START: gesture.type = WebInputEvent::GestureFlingStart; gesture.data.flingStart.velocityX = data.details.velocity_x(); gesture.data.flingStart.velocityY = data.details.velocity_y(); break; case ui::ET_SCROLL_FLING_CANCEL: gesture.type = WebInputEvent::GestureFlingCancel; break; case ui::ET_GESTURE_PINCH_BEGIN: gesture.type = WebInputEvent::GesturePinchBegin; break; case ui::ET_GESTURE_PINCH_UPDATE: gesture.type = WebInputEvent::GesturePinchUpdate; gesture.data.pinchUpdate.scale = data.details.scale(); break; case ui::ET_GESTURE_PINCH_END: gesture.type = WebInputEvent::GesturePinchEnd; break; case ui::ET_GESTURE_TAP_CANCEL: gesture.type = WebInputEvent::GestureTapCancel; break; case ui::ET_GESTURE_TAP_DOWN: gesture.type = WebInputEvent::GestureTapDown; gesture.data.tapDown.width = data.details.bounding_box_f().width(); gesture.data.tapDown.height = data.details.bounding_box_f().height(); break; case ui::ET_GESTURE_BEGIN: case ui::ET_GESTURE_END: NOTREACHED() << "ET_GESTURE_BEGIN and ET_GESTURE_END are only produced " << "in Aura, and should never end up here."; break; default: NOTREACHED() << "ui::EventType provided wasn't a valid gesture event."; break; } return gesture; } } // namespace content