| /* |
| * Copyright (C) 2009 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "web/WebInputEventConversion.h" |
| |
| #include "core/dom/Touch.h" |
| #include "core/dom/TouchList.h" |
| #include "core/events/GestureEvent.h" |
| #include "core/events/KeyboardEvent.h" |
| #include "core/events/MouseEvent.h" |
| #include "core/events/TouchEvent.h" |
| #include "core/events/WheelEvent.h" |
| #include "core/frame/FrameHost.h" |
| #include "core/frame/FrameView.h" |
| #include "core/frame/VisualViewport.h" |
| #include "core/layout/api/LayoutItem.h" |
| #include "core/page/ChromeClient.h" |
| #include "core/page/Page.h" |
| #include "platform/KeyboardCodes.h" |
| #include "platform/Widget.h" |
| #include "public/platform/Platform.h" |
| |
| namespace blink { |
| |
| namespace { |
| float frameScale(const Widget* widget) { |
| float scale = 1; |
| if (widget) { |
| FrameView* rootView = toFrameView(widget->root()); |
| if (rootView) |
| scale = rootView->inputEventsScaleFactor(); |
| } |
| return scale; |
| } |
| |
| FloatPoint frameTranslation(const Widget* widget) { |
| float scale = 1; |
| FloatSize offset; |
| IntPoint visualViewport; |
| FloatSize overscrollOffset; |
| if (widget) { |
| FrameView* rootView = toFrameView(widget->root()); |
| if (rootView) { |
| scale = rootView->inputEventsScaleFactor(); |
| offset = FloatSize(rootView->inputEventsOffsetForEmulation()); |
| visualViewport = flooredIntPoint(rootView->page() |
| ->frameHost() |
| .visualViewport() |
| .visibleRect() |
| .location()); |
| overscrollOffset = |
| rootView->page()->frameHost().chromeClient().elasticOverscroll(); |
| } |
| } |
| return FloatPoint( |
| -offset.width() / scale + visualViewport.x() + overscrollOffset.width(), |
| -offset.height() / scale + visualViewport.y() + |
| overscrollOffset.height()); |
| } |
| |
| FloatPoint convertAbsoluteLocationForLayoutObjectFloat( |
| const DoublePoint& location, |
| const LayoutItem layoutItem) { |
| return layoutItem.absoluteToLocal(FloatPoint(location), UseTransforms); |
| } |
| |
| IntPoint convertAbsoluteLocationForLayoutObjectInt( |
| const DoublePoint& location, |
| const LayoutItem layoutItem) { |
| return roundedIntPoint( |
| convertAbsoluteLocationForLayoutObjectFloat(location, layoutItem)); |
| } |
| |
| // FIXME: Change |widget| to const Widget& after RemoteFrames get |
| // RemoteFrameViews. |
| void updateWebMouseEventFromCoreMouseEvent(const MouseEvent& event, |
| const Widget* widget, |
| const LayoutItem layoutItem, |
| WebMouseEvent& webEvent) { |
| webEvent.setTimeStampSeconds(event.platformTimeStamp().InSeconds()); |
| webEvent.setModifiers(event.modifiers()); |
| |
| FrameView* view = widget ? toFrameView(widget->parent()) : 0; |
| // TODO(bokan): If view == nullptr, pointInRootFrame will really be |
| // pointInRootContent. |
| IntPoint pointInRootFrame(event.absoluteLocation().x(), |
| event.absoluteLocation().y()); |
| if (view) |
| pointInRootFrame = view->contentsToRootFrame(pointInRootFrame); |
| webEvent.globalX = event.screenX(); |
| webEvent.globalY = event.screenY(); |
| webEvent.windowX = pointInRootFrame.x(); |
| webEvent.windowY = pointInRootFrame.y(); |
| IntPoint localPoint = convertAbsoluteLocationForLayoutObjectInt( |
| event.absoluteLocation(), layoutItem); |
| webEvent.x = localPoint.x(); |
| webEvent.y = localPoint.y(); |
| } |
| |
| unsigned toWebInputEventModifierFrom(WebMouseEvent::Button button) { |
| if (button == WebMouseEvent::Button::NoButton) |
| return 0; |
| |
| unsigned webMouseButtonToPlatformModifier[] = { |
| WebInputEvent::LeftButtonDown, WebInputEvent::MiddleButtonDown, |
| WebInputEvent::RightButtonDown}; |
| |
| return webMouseButtonToPlatformModifier[static_cast<int>(button)]; |
| } |
| |
| } // namespace |
| |
| WebMouseEvent TransformWebMouseEvent(Widget* widget, |
| const WebMouseEvent& event) { |
| WebMouseEvent result = event; |
| |
| // TODO(dtapuska): Remove this translation. In the past blink has |
| // converted leaves into moves and not known about leaves. It should |
| // be educated about them. crbug.com/686196 |
| if (event.type() == WebInputEvent::MouseEnter || |
| event.type() == WebInputEvent::MouseLeave) { |
| result.setType(WebInputEvent::MouseMove); |
| } |
| |
| // TODO(dtapuska): Perhaps the event should be constructed correctly? |
| // crbug.com/686200 |
| if (event.type() == WebInputEvent::MouseUp) { |
| result.setModifiers(event.modifiers() & |
| ~toWebInputEventModifierFrom(event.button)); |
| } |
| result.setFrameScale(frameScale(widget)); |
| result.setFrameTranslate(frameTranslation(widget)); |
| return result; |
| } |
| |
| WebMouseWheelEvent TransformWebMouseWheelEvent( |
| Widget* widget, |
| const WebMouseWheelEvent& event) { |
| WebMouseWheelEvent result = event; |
| result.setFrameScale(frameScale(widget)); |
| result.setFrameTranslate(frameTranslation(widget)); |
| return result; |
| } |
| |
| WebGestureEvent TransformWebGestureEvent(Widget* widget, |
| const WebGestureEvent& event) { |
| WebGestureEvent result = event; |
| result.setFrameScale(frameScale(widget)); |
| result.setFrameTranslate(frameTranslation(widget)); |
| return result; |
| } |
| |
| WebTouchEvent TransformWebTouchEvent(float frameScale, |
| FloatPoint frameTranslate, |
| const WebTouchEvent& event) { |
| // frameScale is default initialized in debug builds to be 0. |
| DCHECK_EQ(0, event.frameScale()); |
| DCHECK_EQ(0, event.frameTranslate().x); |
| DCHECK_EQ(0, event.frameTranslate().y); |
| WebTouchEvent result = event; |
| result.setFrameScale(frameScale); |
| result.setFrameTranslate(frameTranslate); |
| return result; |
| } |
| |
| WebTouchEvent TransformWebTouchEvent(Widget* widget, |
| const WebTouchEvent& event) { |
| return TransformWebTouchEvent(frameScale(widget), frameTranslation(widget), |
| event); |
| } |
| |
| WebMouseEventBuilder::WebMouseEventBuilder(const Widget* widget, |
| const LayoutItem layoutItem, |
| const MouseEvent& event) { |
| if (event.nativeEvent()) { |
| *static_cast<WebMouseEvent*>(this) = |
| event.nativeEvent()->flattenTransform(); |
| WebFloatPoint absoluteRootFrameLocation = positionInRootFrame(); |
| IntPoint localPoint = roundedIntPoint( |
| layoutItem.absoluteToLocal(absoluteRootFrameLocation, UseTransforms)); |
| x = localPoint.x(); |
| y = localPoint.y(); |
| return; |
| } |
| |
| // Code below here can be removed once OOPIF ships. |
| // OOPIF will prevent synthetic events being dispatched into |
| // other frames; but for now we allow the fallback to generate |
| // WebMouseEvents from synthetic events. |
| if (event.type() == EventTypeNames::mousemove) |
| m_type = WebInputEvent::MouseMove; |
| else if (event.type() == EventTypeNames::mouseout) |
| m_type = WebInputEvent::MouseLeave; |
| else if (event.type() == EventTypeNames::mouseover) |
| m_type = WebInputEvent::MouseEnter; |
| else if (event.type() == EventTypeNames::mousedown) |
| m_type = WebInputEvent::MouseDown; |
| else if (event.type() == EventTypeNames::mouseup) |
| m_type = WebInputEvent::MouseUp; |
| else if (event.type() == EventTypeNames::contextmenu) |
| m_type = WebInputEvent::ContextMenu; |
| else |
| return; // Skip all other mouse events. |
| |
| m_timeStampSeconds = event.platformTimeStamp().InSeconds(); |
| m_modifiers = event.modifiers(); |
| updateWebMouseEventFromCoreMouseEvent(event, widget, layoutItem, *this); |
| |
| switch (event.button()) { |
| case short(WebPointerProperties::Button::Left): |
| button = WebMouseEvent::Button::Left; |
| break; |
| case short(WebPointerProperties::Button::Middle): |
| button = WebMouseEvent::Button::Middle; |
| break; |
| case short(WebPointerProperties::Button::Right): |
| button = WebMouseEvent::Button::Right; |
| break; |
| } |
| if (event.buttonDown()) { |
| switch (event.button()) { |
| case short(WebPointerProperties::Button::Left): |
| m_modifiers |= WebInputEvent::LeftButtonDown; |
| break; |
| case short(WebPointerProperties::Button::Middle): |
| m_modifiers |= WebInputEvent::MiddleButtonDown; |
| break; |
| case short(WebPointerProperties::Button::Right): |
| m_modifiers |= WebInputEvent::RightButtonDown; |
| break; |
| } |
| } else { |
| button = WebMouseEvent::Button::NoButton; |
| } |
| movementX = event.movementX(); |
| movementY = event.movementY(); |
| clickCount = event.detail(); |
| |
| pointerType = WebPointerProperties::PointerType::Mouse; |
| } |
| |
| // Generate a synthetic WebMouseEvent given a TouchEvent (eg. for emulating a |
| // mouse with touch input for plugins that don't support touch input). |
| WebMouseEventBuilder::WebMouseEventBuilder(const Widget* widget, |
| const LayoutItem layoutItem, |
| const TouchEvent& event) { |
| if (!event.touches()) |
| return; |
| if (event.touches()->length() != 1) { |
| if (event.touches()->length() || event.type() != EventTypeNames::touchend || |
| !event.changedTouches() || event.changedTouches()->length() != 1) |
| return; |
| } |
| |
| const Touch* touch = event.touches()->length() == 1 |
| ? event.touches()->item(0) |
| : event.changedTouches()->item(0); |
| if (touch->identifier()) |
| return; |
| |
| if (event.type() == EventTypeNames::touchstart) |
| m_type = MouseDown; |
| else if (event.type() == EventTypeNames::touchmove) |
| m_type = MouseMove; |
| else if (event.type() == EventTypeNames::touchend) |
| m_type = MouseUp; |
| else |
| return; |
| |
| m_timeStampSeconds = event.platformTimeStamp().InSeconds(); |
| m_modifiers = event.modifiers(); |
| m_frameScale = 1; |
| m_frameTranslate = WebFloatPoint(); |
| |
| // The mouse event co-ordinates should be generated from the co-ordinates of |
| // the touch point. |
| FrameView* view = toFrameView(widget->parent()); |
| // FIXME: if view == nullptr, pointInRootFrame will really be |
| // pointInRootContent. |
| IntPoint pointInRootFrame = roundedIntPoint(touch->absoluteLocation()); |
| if (view) |
| pointInRootFrame = view->contentsToRootFrame(pointInRootFrame); |
| IntPoint screenPoint = roundedIntPoint(touch->screenLocation()); |
| globalX = screenPoint.x(); |
| globalY = screenPoint.y(); |
| windowX = pointInRootFrame.x(); |
| windowY = pointInRootFrame.y(); |
| |
| button = WebMouseEvent::Button::Left; |
| m_modifiers |= WebInputEvent::LeftButtonDown; |
| clickCount = (m_type == MouseDown || m_type == MouseUp); |
| |
| IntPoint localPoint = convertAbsoluteLocationForLayoutObjectInt( |
| DoublePoint(touch->absoluteLocation()), layoutItem); |
| x = localPoint.x(); |
| y = localPoint.y(); |
| |
| pointerType = WebPointerProperties::PointerType::Touch; |
| } |
| |
| WebKeyboardEventBuilder::WebKeyboardEventBuilder(const KeyboardEvent& event) { |
| if (const WebKeyboardEvent* webEvent = event.keyEvent()) { |
| *static_cast<WebKeyboardEvent*>(this) = *webEvent; |
| |
| // TODO(dtapuska): DOM KeyboardEvents converted back to WebInputEvents |
| // drop the Raw behaviour. Figure out if this is actually really needed. |
| if (m_type == RawKeyDown) |
| m_type = KeyDown; |
| return; |
| } |
| |
| if (event.type() == EventTypeNames::keydown) |
| m_type = KeyDown; |
| else if (event.type() == EventTypeNames::keyup) |
| m_type = WebInputEvent::KeyUp; |
| else if (event.type() == EventTypeNames::keypress) |
| m_type = WebInputEvent::Char; |
| else |
| return; // Skip all other keyboard events. |
| |
| m_modifiers = event.modifiers(); |
| m_timeStampSeconds = event.platformTimeStamp().InSeconds(); |
| windowsKeyCode = event.keyCode(); |
| } |
| |
| Vector<WebMouseEvent> TransformWebMouseEventVector( |
| Widget* widget, |
| const std::vector<const WebInputEvent*>& coalescedEvents) { |
| Vector<WebMouseEvent> result; |
| for (const auto& event : coalescedEvents) { |
| DCHECK(WebInputEvent::isMouseEventType(event->type())); |
| result.push_back(TransformWebMouseEvent( |
| widget, static_cast<const WebMouseEvent&>(*event))); |
| } |
| return result; |
| } |
| |
| Vector<WebTouchEvent> TransformWebTouchEventVector( |
| Widget* widget, |
| const std::vector<const WebInputEvent*>& coalescedEvents) { |
| float scale = frameScale(widget); |
| FloatPoint translation = frameTranslation(widget); |
| Vector<WebTouchEvent> result; |
| for (const auto& event : coalescedEvents) { |
| DCHECK(WebInputEvent::isTouchEventType(event->type())); |
| result.push_back(TransformWebTouchEvent( |
| scale, translation, static_cast<const WebTouchEvent&>(*event))); |
| } |
| return result; |
| } |
| |
| } // namespace blink |