summaryrefslogtreecommitdiffstats
path: root/widget/InputData.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/InputData.cpp')
-rw-r--r--widget/InputData.cpp924
1 files changed, 924 insertions, 0 deletions
diff --git a/widget/InputData.cpp b/widget/InputData.cpp
new file mode 100644
index 0000000000..3d1695dd8e
--- /dev/null
+++ b/widget/InputData.cpp
@@ -0,0 +1,924 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InputData.h"
+
+#include "mozilla/dom/MouseEventBinding.h"
+#include "mozilla/dom/Touch.h"
+#include "mozilla/dom/WheelEventBinding.h"
+#include "mozilla/TextEvents.h"
+#include "nsContentUtils.h"
+#include "nsDebug.h"
+#include "nsThreadUtils.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/TouchEvents.h"
+#include "mozilla/SwipeTracker.h"
+#include "UnitTransforms.h"
+
+namespace mozilla {
+
+using namespace dom;
+
+InputData::~InputData() = default;
+
+InputData::InputData(InputType aInputType)
+ : mInputType(aInputType),
+ mFocusSequenceNumber(0),
+ mLayersId{0},
+ modifiers(0) {}
+
+InputData::InputData(InputType aInputType, TimeStamp aTimeStamp,
+ Modifiers aModifiers)
+ : mInputType(aInputType),
+ mTimeStamp(aTimeStamp),
+ mFocusSequenceNumber(0),
+ mLayersId{0},
+ modifiers(aModifiers) {}
+
+SingleTouchData::SingleTouchData(int32_t aIdentifier,
+ ScreenIntPoint aScreenPoint,
+ ScreenSize aRadius, float aRotationAngle,
+ float aForce)
+ : mIdentifier(aIdentifier),
+ mScreenPoint(aScreenPoint),
+ mRadius(aRadius),
+ mRotationAngle(aRotationAngle),
+ mForce(aForce) {}
+
+SingleTouchData::SingleTouchData(int32_t aIdentifier,
+ ParentLayerPoint aLocalScreenPoint,
+ ScreenSize aRadius, float aRotationAngle,
+ float aForce)
+ : mIdentifier(aIdentifier),
+ mLocalScreenPoint(aLocalScreenPoint),
+ mRadius(aRadius),
+ mRotationAngle(aRotationAngle),
+ mForce(aForce) {}
+
+SingleTouchData::SingleTouchData()
+ : mIdentifier(0), mRotationAngle(0.0), mForce(0.0) {}
+
+already_AddRefed<Touch> SingleTouchData::ToNewDOMTouch() const {
+ MOZ_ASSERT(NS_IsMainThread(),
+ "Can only create dom::Touch instances on main thread");
+ RefPtr<Touch> touch =
+ new Touch(mIdentifier,
+ LayoutDeviceIntPoint::Truncate(mScreenPoint.x, mScreenPoint.y),
+ LayoutDeviceIntPoint::Truncate(mRadius.width, mRadius.height),
+ mRotationAngle, mForce);
+ touch->tiltX = mTiltX;
+ touch->tiltY = mTiltY;
+ touch->twist = mTwist;
+ return touch.forget();
+}
+
+MultiTouchInput::MultiTouchInput(MultiTouchType aType, uint32_t aTime,
+ TimeStamp aTimeStamp, Modifiers aModifiers)
+ : InputData(MULTITOUCH_INPUT, aTimeStamp, aModifiers),
+ mType(aType),
+ mHandledByAPZ(false) {}
+
+MultiTouchInput::MultiTouchInput()
+ : InputData(MULTITOUCH_INPUT),
+ mType(MULTITOUCH_START),
+ mHandledByAPZ(false) {}
+
+MultiTouchInput::MultiTouchInput(const WidgetTouchEvent& aTouchEvent)
+ : InputData(MULTITOUCH_INPUT, aTouchEvent.mTimeStamp,
+ aTouchEvent.mModifiers),
+ mHandledByAPZ(aTouchEvent.mFlags.mHandledByAPZ),
+ mButton(aTouchEvent.mButton),
+ mButtons(aTouchEvent.mButtons) {
+ MOZ_ASSERT(NS_IsMainThread(),
+ "Can only copy from WidgetTouchEvent on main thread");
+
+ switch (aTouchEvent.mMessage) {
+ case eTouchStart:
+ mType = MULTITOUCH_START;
+ break;
+ case eTouchMove:
+ mType = MULTITOUCH_MOVE;
+ break;
+ case eTouchEnd:
+ mType = MULTITOUCH_END;
+ break;
+ case eTouchCancel:
+ mType = MULTITOUCH_CANCEL;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Did not assign a type to a MultiTouchInput");
+ break;
+ }
+
+ mScreenOffset = ViewAs<ExternalPixel>(
+ aTouchEvent.mWidget->WidgetToScreenOffset(),
+ PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
+
+ for (size_t i = 0; i < aTouchEvent.mTouches.Length(); i++) {
+ const Touch* domTouch = aTouchEvent.mTouches[i];
+
+ // Extract data from weird interfaces.
+ int32_t identifier = domTouch->Identifier();
+ int32_t radiusX = domTouch->RadiusX(CallerType::System);
+ int32_t radiusY = domTouch->RadiusY(CallerType::System);
+ float rotationAngle = domTouch->RotationAngle(CallerType::System);
+ float force = domTouch->Force(CallerType::System);
+
+ SingleTouchData data(
+ identifier,
+ ViewAs<ScreenPixel>(
+ domTouch->mRefPoint,
+ PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent),
+ ScreenSize((float)radiusX, (float)radiusY), rotationAngle, force);
+
+ mTouches.AppendElement(data);
+ }
+}
+
+void MultiTouchInput::Translate(const ScreenPoint& aTranslation) {
+ ScreenIntPoint translation = RoundedToInt(aTranslation);
+
+ for (auto& touchData : mTouches) {
+ for (auto& historicalData : touchData.mHistoricalData) {
+ historicalData.mScreenPoint.MoveBy(translation.x, translation.y);
+ }
+ touchData.mScreenPoint.MoveBy(translation.x, translation.y);
+ }
+}
+
+WidgetTouchEvent MultiTouchInput::ToWidgetEvent(nsIWidget* aWidget,
+ uint16_t aInputSource) const {
+ MOZ_ASSERT(NS_IsMainThread(),
+ "Can only convert To WidgetTouchEvent on main thread");
+ MOZ_ASSERT(aInputSource ==
+ mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH ||
+ aInputSource == mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_PEN);
+
+ EventMessage touchEventMessage = eVoidEvent;
+ switch (mType) {
+ case MULTITOUCH_START:
+ touchEventMessage = eTouchStart;
+ break;
+ case MULTITOUCH_MOVE:
+ touchEventMessage = eTouchMove;
+ break;
+ case MULTITOUCH_END:
+ touchEventMessage = eTouchEnd;
+ break;
+ case MULTITOUCH_CANCEL:
+ touchEventMessage = eTouchCancel;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE(
+ "Did not assign a type to WidgetTouchEvent in MultiTouchInput");
+ break;
+ }
+
+ WidgetTouchEvent event(true, touchEventMessage, aWidget);
+ if (touchEventMessage == eVoidEvent) {
+ return event;
+ }
+
+ event.mModifiers = this->modifiers;
+ event.mTimeStamp = this->mTimeStamp;
+ event.mFlags.mHandledByAPZ = mHandledByAPZ;
+ event.mFocusSequenceNumber = mFocusSequenceNumber;
+ event.mLayersId = mLayersId;
+ event.mInputSource = aInputSource;
+ event.mButton = mButton;
+ event.mButtons = mButtons;
+
+ for (size_t i = 0; i < mTouches.Length(); i++) {
+ *event.mTouches.AppendElement() = mTouches[i].ToNewDOMTouch();
+ }
+
+ return event;
+}
+
+int32_t MultiTouchInput::IndexOfTouch(int32_t aTouchIdentifier) {
+ for (size_t i = 0; i < mTouches.Length(); i++) {
+ if (mTouches[i].mIdentifier == aTouchIdentifier) {
+ return (int32_t)i;
+ }
+ }
+ return -1;
+}
+
+bool MultiTouchInput::TransformToLocal(
+ const ScreenToParentLayerMatrix4x4& aTransform) {
+ for (auto& touchData : mTouches) {
+ for (auto& historicalData : touchData.mHistoricalData) {
+ Maybe<ParentLayerIntPoint> historicalPoint =
+ UntransformBy(aTransform, historicalData.mScreenPoint);
+ if (!historicalPoint) {
+ return false;
+ }
+ historicalData.mLocalScreenPoint = *historicalPoint;
+ }
+ Maybe<ParentLayerIntPoint> point =
+ UntransformBy(aTransform, touchData.mScreenPoint);
+ if (!point) {
+ return false;
+ }
+ touchData.mLocalScreenPoint = *point;
+ }
+ return true;
+}
+
+MouseInput::MouseInput()
+ : InputData(MOUSE_INPUT),
+ mType(MOUSE_NONE),
+ mButtonType(NONE),
+ mInputSource(0),
+ mButtons(0),
+ mHandledByAPZ(false),
+ mPreventClickEvent(false) {}
+
+MouseInput::MouseInput(MouseType aType, ButtonType aButtonType,
+ uint16_t aInputSource, int16_t aButtons,
+ const ScreenPoint& aPoint, TimeStamp aTimeStamp,
+ Modifiers aModifiers)
+ : InputData(MOUSE_INPUT, aTimeStamp, aModifiers),
+ mType(aType),
+ mButtonType(aButtonType),
+ mInputSource(aInputSource),
+ mButtons(aButtons),
+ mOrigin(aPoint),
+ mHandledByAPZ(false),
+ mPreventClickEvent(false) {}
+
+MouseInput::MouseInput(const WidgetMouseEventBase& aMouseEvent)
+ : InputData(MOUSE_INPUT, aMouseEvent.mTimeStamp, aMouseEvent.mModifiers),
+ mType(MOUSE_NONE),
+ mButtonType(NONE),
+ mInputSource(aMouseEvent.mInputSource),
+ mButtons(aMouseEvent.mButtons),
+ mHandledByAPZ(aMouseEvent.mFlags.mHandledByAPZ),
+ mPreventClickEvent(aMouseEvent.mClass == eMouseEventClass &&
+ aMouseEvent.AsMouseEvent()->mClickEventPrevented) {
+ MOZ_ASSERT(NS_IsMainThread(),
+ "Can only copy from WidgetTouchEvent on main thread");
+
+ mButtonType = NONE;
+
+ switch (aMouseEvent.mButton) {
+ case MouseButton::ePrimary:
+ mButtonType = MouseInput::PRIMARY_BUTTON;
+ break;
+ case MouseButton::eMiddle:
+ mButtonType = MouseInput::MIDDLE_BUTTON;
+ break;
+ case MouseButton::eSecondary:
+ mButtonType = MouseInput::SECONDARY_BUTTON;
+ break;
+ }
+
+ switch (aMouseEvent.mMessage) {
+ case eMouseMove:
+ mType = MOUSE_MOVE;
+ break;
+ case eMouseUp:
+ mType = MOUSE_UP;
+ break;
+ case eMouseDown:
+ mType = MOUSE_DOWN;
+ break;
+ case eDragStart:
+ mType = MOUSE_DRAG_START;
+ break;
+ case eDragEnd:
+ mType = MOUSE_DRAG_END;
+ break;
+ case eMouseEnterIntoWidget:
+ mType = MOUSE_WIDGET_ENTER;
+ break;
+ case eMouseExitFromWidget:
+ mType = MOUSE_WIDGET_EXIT;
+ break;
+ case eMouseExploreByTouch:
+ mType = MOUSE_EXPLORE_BY_TOUCH;
+ break;
+ case eMouseHitTest:
+ mType = MOUSE_HITTEST;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Mouse event type not supported");
+ break;
+ }
+
+ mOrigin = ScreenPoint(ViewAs<ScreenPixel>(
+ aMouseEvent.mRefPoint,
+ PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
+}
+
+bool MouseInput::IsLeftButton() const { return mButtonType == PRIMARY_BUTTON; }
+
+bool MouseInput::TransformToLocal(
+ const ScreenToParentLayerMatrix4x4& aTransform) {
+ Maybe<ParentLayerPoint> point = UntransformBy(aTransform, mOrigin);
+ if (!point) {
+ return false;
+ }
+ mLocalOrigin = *point;
+
+ return true;
+}
+
+WidgetMouseEvent MouseInput::ToWidgetEvent(nsIWidget* aWidget) const {
+ MOZ_ASSERT(NS_IsMainThread(),
+ "Can only convert To WidgetTouchEvent on main thread");
+
+ EventMessage msg = eVoidEvent;
+ uint32_t clickCount = 0;
+ Maybe<WidgetMouseEvent::ExitFrom> exitFrom;
+ switch (mType) {
+ case MOUSE_MOVE:
+ msg = eMouseMove;
+ break;
+ case MOUSE_UP:
+ msg = eMouseUp;
+ clickCount = 1;
+ break;
+ case MOUSE_DOWN:
+ msg = eMouseDown;
+ clickCount = 1;
+ break;
+ case MOUSE_DRAG_START:
+ msg = eDragStart;
+ break;
+ case MOUSE_DRAG_END:
+ msg = eDragEnd;
+ break;
+ case MOUSE_WIDGET_ENTER:
+ msg = eMouseEnterIntoWidget;
+ break;
+ case MOUSE_WIDGET_EXIT:
+ msg = eMouseExitFromWidget;
+ exitFrom = Some(WidgetMouseEvent::ePlatformChild);
+ break;
+ case MOUSE_EXPLORE_BY_TOUCH:
+ msg = eMouseExploreByTouch;
+ break;
+ case MOUSE_HITTEST:
+ msg = eMouseHitTest;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE(
+ "Did not assign a type to WidgetMouseEvent in MouseInput");
+ break;
+ }
+
+ WidgetMouseEvent event(true, msg, aWidget, WidgetMouseEvent::eReal,
+ WidgetMouseEvent::eNormal);
+
+ if (msg == eVoidEvent) {
+ return event;
+ }
+
+ switch (mButtonType) {
+ case MouseInput::PRIMARY_BUTTON:
+ event.mButton = MouseButton::ePrimary;
+ break;
+ case MouseInput::MIDDLE_BUTTON:
+ event.mButton = MouseButton::eMiddle;
+ break;
+ case MouseInput::SECONDARY_BUTTON:
+ event.mButton = MouseButton::eSecondary;
+ break;
+ case MouseInput::NONE:
+ default:
+ break;
+ }
+
+ event.mButtons = mButtons;
+ event.mModifiers = modifiers;
+ event.mTimeStamp = mTimeStamp;
+ event.mLayersId = mLayersId;
+ event.mFlags.mHandledByAPZ = mHandledByAPZ;
+ event.mRefPoint = RoundedToInt(ViewAs<LayoutDevicePixel>(
+ mOrigin,
+ PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
+ event.mClickCount = clickCount;
+ event.mInputSource = mInputSource;
+ event.mFocusSequenceNumber = mFocusSequenceNumber;
+ event.mExitFrom = exitFrom;
+ event.mClickEventPrevented = mPreventClickEvent;
+
+ return event;
+}
+
+PanGestureInput::PanGestureInput()
+ : InputData(PANGESTURE_INPUT),
+ mType(PANGESTURE_MAYSTART),
+ mLineOrPageDeltaX(0),
+ mLineOrPageDeltaY(0),
+ mUserDeltaMultiplierX(1.0),
+ mUserDeltaMultiplierY(1.0),
+ mHandledByAPZ(false),
+ mOverscrollBehaviorAllowsSwipe(false),
+ mSimulateMomentum(false),
+ mIsNoLineOrPageDelta(true),
+ mMayTriggerSwipe(false) {}
+
+PanGestureInput::PanGestureInput(PanGestureType aType, TimeStamp aTimeStamp,
+ const ScreenPoint& aPanStartPoint,
+ const ScreenPoint& aPanDisplacement,
+ Modifiers aModifiers)
+ : InputData(PANGESTURE_INPUT, aTimeStamp, aModifiers),
+ mType(aType),
+ mPanStartPoint(aPanStartPoint),
+ mPanDisplacement(aPanDisplacement),
+ mLineOrPageDeltaX(0),
+ mLineOrPageDeltaY(0),
+ mUserDeltaMultiplierX(1.0),
+ mUserDeltaMultiplierY(1.0),
+ mHandledByAPZ(false),
+ mOverscrollBehaviorAllowsSwipe(false),
+ mSimulateMomentum(false),
+ mIsNoLineOrPageDelta(true) {
+ mMayTriggerSwipe = SwipeTracker::CanTriggerSwipe(*this);
+}
+
+PanGestureInput::PanGestureInput(PanGestureType aType, TimeStamp aTimeStamp,
+ const ScreenPoint& aPanStartPoint,
+ const ScreenPoint& aPanDisplacement,
+ Modifiers aModifiers,
+ IsEligibleForSwipe aIsEligibleForSwipe)
+ : PanGestureInput(aType, aTimeStamp, aPanStartPoint, aPanDisplacement,
+ aModifiers) {
+ mMayTriggerSwipe &= bool(aIsEligibleForSwipe);
+}
+
+void PanGestureInput::SetLineOrPageDeltas(int32_t aLineOrPageDeltaX,
+ int32_t aLineOrPageDeltaY) {
+ mLineOrPageDeltaX = aLineOrPageDeltaX;
+ mLineOrPageDeltaY = aLineOrPageDeltaY;
+ mIsNoLineOrPageDelta = false;
+}
+
+bool PanGestureInput::IsMomentum() const {
+ switch (mType) {
+ case PanGestureInput::PANGESTURE_MOMENTUMSTART:
+ case PanGestureInput::PANGESTURE_MOMENTUMPAN:
+ case PanGestureInput::PANGESTURE_MOMENTUMEND:
+ return true;
+ default:
+ return false;
+ }
+}
+
+WidgetWheelEvent PanGestureInput::ToWidgetEvent(nsIWidget* aWidget) const {
+ WidgetWheelEvent wheelEvent(true, eWheel, aWidget);
+ wheelEvent.mModifiers = this->modifiers;
+ wheelEvent.mTimeStamp = mTimeStamp;
+ wheelEvent.mLayersId = mLayersId;
+ wheelEvent.mRefPoint = RoundedToInt(ViewAs<LayoutDevicePixel>(
+ mPanStartPoint,
+ PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
+ wheelEvent.mButtons = 0;
+ wheelEvent.mMayHaveMomentum = true; // pan inputs may have momentum
+ wheelEvent.mIsMomentum = IsMomentum();
+ wheelEvent.mLineOrPageDeltaX = mLineOrPageDeltaX;
+ wheelEvent.mLineOrPageDeltaY = mLineOrPageDeltaY;
+ wheelEvent.mDeltaX = mPanDisplacement.x;
+ wheelEvent.mDeltaY = mPanDisplacement.y;
+ wheelEvent.mFlags.mHandledByAPZ = mHandledByAPZ;
+ wheelEvent.mFocusSequenceNumber = mFocusSequenceNumber;
+ wheelEvent.mIsNoLineOrPageDelta = mIsNoLineOrPageDelta;
+ if (mDeltaType == PanGestureInput::PANDELTA_PAGE) {
+ // widget/gtk is currently the only consumer that uses delta type
+ // PANDELTA_PAGE
+ // Emulate legacy widget/gtk behavior
+ wheelEvent.mDeltaMode = WheelEvent_Binding::DOM_DELTA_LINE;
+ wheelEvent.mScrollType = WidgetWheelEvent::SCROLL_ASYNCHRONOUSLY;
+ wheelEvent.mDeltaX *= 3;
+ wheelEvent.mDeltaY *= 3;
+ } else {
+ wheelEvent.mDeltaMode = WheelEvent_Binding::DOM_DELTA_PIXEL;
+ }
+ return wheelEvent;
+}
+
+bool PanGestureInput::TransformToLocal(
+ const ScreenToParentLayerMatrix4x4& aTransform) {
+ Maybe<ParentLayerPoint> panStartPoint =
+ UntransformBy(aTransform, mPanStartPoint);
+ if (!panStartPoint) {
+ return false;
+ }
+ mLocalPanStartPoint = *panStartPoint;
+
+ if (mDeltaType == PanGestureInput::PANDELTA_PAGE) {
+ // Skip transforming the pan displacement because we want
+ // raw page proportion counts.
+ mLocalPanDisplacement = ViewAs<ParentLayerPixel>(
+ mPanDisplacement, PixelCastJustification::DeltaIsPageProportion);
+ return true;
+ }
+
+ Maybe<ParentLayerPoint> panDisplacement =
+ UntransformVector(aTransform, mPanDisplacement, mPanStartPoint);
+ if (!panDisplacement) {
+ return false;
+ }
+ mLocalPanDisplacement = *panDisplacement;
+ return true;
+}
+
+ScreenPoint PanGestureInput::UserMultipliedPanDisplacement() const {
+ return ScreenPoint(mPanDisplacement.x * mUserDeltaMultiplierX,
+ mPanDisplacement.y * mUserDeltaMultiplierY);
+}
+
+ParentLayerPoint PanGestureInput::UserMultipliedLocalPanDisplacement() const {
+ return ParentLayerPoint(mLocalPanDisplacement.x * mUserDeltaMultiplierX,
+ mLocalPanDisplacement.y * mUserDeltaMultiplierY);
+}
+
+static int32_t TakeLargestInt(gfx::Coord* aCoord) {
+ int32_t result(aCoord->value); // truncate towards zero
+ aCoord->value -= result;
+ return result;
+}
+
+/* static */ gfx::IntPoint PanGestureInput::GetIntegerDeltaForEvent(
+ bool aIsStart, float x, float y) {
+ static gfx::Point sAccumulator(0.0f, 0.0f);
+ if (aIsStart) {
+ sAccumulator = gfx::Point(0.0f, 0.0f);
+ }
+ sAccumulator.x += x;
+ sAccumulator.y += y;
+ return gfx::IntPoint(TakeLargestInt(&sAccumulator.x),
+ TakeLargestInt(&sAccumulator.y));
+}
+
+PinchGestureInput::PinchGestureInput()
+ : InputData(PINCHGESTURE_INPUT),
+ mType(PINCHGESTURE_START),
+ mSource(UNKNOWN),
+ mHandledByAPZ(false) {}
+
+PinchGestureInput::PinchGestureInput(
+ PinchGestureType aType, PinchGestureSource aSource, TimeStamp aTimeStamp,
+ const ExternalPoint& aScreenOffset, const ScreenPoint& aFocusPoint,
+ ScreenCoord aCurrentSpan, ScreenCoord aPreviousSpan, Modifiers aModifiers)
+ : InputData(PINCHGESTURE_INPUT, aTimeStamp, aModifiers),
+ mType(aType),
+ mSource(aSource),
+ mFocusPoint(aFocusPoint),
+ mScreenOffset(aScreenOffset),
+ mCurrentSpan(aCurrentSpan),
+ mPreviousSpan(aPreviousSpan),
+ mLineOrPageDeltaY(0),
+ mHandledByAPZ(false) {}
+
+bool PinchGestureInput::TransformToLocal(
+ const ScreenToParentLayerMatrix4x4& aTransform) {
+ Maybe<ParentLayerPoint> point = UntransformBy(aTransform, mFocusPoint);
+ if (!point) {
+ return false;
+ }
+ mLocalFocusPoint = *point;
+ return true;
+}
+
+WidgetWheelEvent PinchGestureInput::ToWidgetEvent(nsIWidget* aWidget) const {
+ WidgetWheelEvent wheelEvent(true, eWheel, aWidget);
+ wheelEvent.mModifiers = this->modifiers | MODIFIER_CONTROL;
+ wheelEvent.mTimeStamp = mTimeStamp;
+ wheelEvent.mLayersId = mLayersId;
+ wheelEvent.mRefPoint = RoundedToInt(ViewAs<LayoutDevicePixel>(
+ mFocusPoint,
+ PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
+ wheelEvent.mButtons = 0;
+ wheelEvent.mFlags.mHandledByAPZ = mHandledByAPZ;
+ wheelEvent.mDeltaMode = WheelEvent_Binding::DOM_DELTA_PIXEL;
+
+ wheelEvent.mDeltaY = ComputeDeltaY(aWidget);
+
+ wheelEvent.mLineOrPageDeltaY = mLineOrPageDeltaY;
+
+ MOZ_ASSERT(mType == PINCHGESTURE_END || wheelEvent.mDeltaY != 0.0);
+
+ return wheelEvent;
+}
+
+double PinchGestureInput::ComputeDeltaY(nsIWidget* aWidget) const {
+#if defined(XP_DARWIN)
+ // This converts the pinch gesture value to a fake wheel event that has the
+ // control key pressed so that pages can implement custom pinch gesture
+ // handling. It may seem strange that this doesn't use a wheel event with
+ // the deltaZ property set, but this matches Chrome's behavior as described
+ // at https://code.google.com/p/chromium/issues/detail?id=289887
+ //
+ // The intent of the formula below is to produce numbers similar to Chrome's
+ // implementation of this feature. Chrome implements deltaY using the formula
+ // "-100 * log(1 + [event magnification])" which is unfortunately incorrect.
+ // All deltas for a single pinch gesture should sum to 0 if the start and end
+ // of a pinch gesture end up in the same place. This doesn't happen in Chrome
+ // because they followed Apple's misleading documentation, which implies that
+ // "1 + [event magnification]" is the scale factor. The scale factor is
+ // instead "pow(ratio, [event magnification])" so "[event magnification]" is
+ // already in log space.
+ //
+ // The multiplication by the backing scale factor below counteracts the
+ // division by the backing scale factor in WheelEvent.
+
+ // We want to set deltaY to |-100.0 * M * GetDefaultScaleInternal()| where M
+ // is [event magnification] but [event magnification] is only available in the
+ // macOS widget code so we have to reverse engineer from mCurrentSpan and
+ // mPreviousSpan (which are derived from [event magnification]) to get it.
+ // Specifically, we know |mCurrentSpan == 100.0| and |mPreviousSpan == 100.0 *
+ // (1.0 - M)|. We can calculate deltaY by solving the mPreviousSpan equation
+ // for M in terms of mPreviousSpan and plugging that into to the formula for
+ // deltaY.
+ return (mPreviousSpan - 100.0) *
+ (aWidget ? aWidget->GetDefaultScaleInternal() : 1.f);
+#else
+ // This calculation is based on what the Windows and Linux widget code does.
+ // Specifically, it creates a PinchGestureInput with |mCurrentSpan == 100.0 *
+ // currentScale| and |mPreviousSpan == 100.0 * lastScale| where currentScale
+ // is the scale from the current OS event and lastScale is the scale when the
+ // previous OS event happened. On macOS [event magnification] is a relative
+ // change in scale factor, ie if the scale factor changed from 1 to 1.1 it
+ // will be 0.1, similarly if it changed from 1 to 0.9 it will be -0.1. To
+ // calculate the relative scale change on Windows we would calculate |M =
+ // currentScale - lastScale = (mCurrentSpan-mPreviousSpan)/100| and use the
+ // same formula as the macOS code
+ // (|-100.0 * M * GetDefaultScaleInternal()|).
+
+ return (mPreviousSpan - mCurrentSpan) *
+ (aWidget ? aWidget->GetDefaultScaleInternal() : 1.f);
+#endif
+}
+
+bool PinchGestureInput::SetLineOrPageDeltaY(nsIWidget* aWidget) {
+ double deltaY = ComputeDeltaY(aWidget);
+ if (deltaY == 0 && mType != PINCHGESTURE_END) {
+ return false;
+ }
+ gfx::IntPoint lineOrPageDelta = PinchGestureInput::GetIntegerDeltaForEvent(
+ (mType == PINCHGESTURE_START), 0, deltaY);
+ mLineOrPageDeltaY = lineOrPageDelta.y;
+ if (mLineOrPageDeltaY == 0) {
+ // For PINCHGESTURE_SCALE events, don't dispatch them. Note that the delta
+ // isn't lost; it remains in the accumulator in GetIntegerDeltaForEvent().
+ if (mType == PINCHGESTURE_SCALE) {
+ return false;
+ }
+ // On Windows, drop PINCHGESTURE_START as well (the Windows widget code will
+ // defer the START event until we accumulate enough delta).
+ // The Linux widget code doesn't support this, so instead set the event's
+ // mLineOrPageDeltaY to the smallest nonzero amount in the relevant
+ // direction.
+ if (mType == PINCHGESTURE_START) {
+#ifdef XP_WIN
+ return false;
+#else
+ mLineOrPageDeltaY = (deltaY >= 0) ? 1 : -1;
+#endif
+ }
+ // For PINCHGESTURE_END events, not dispatching a DOMMouseScroll for them is
+ // fine.
+ }
+ return true;
+}
+
+/* static */ gfx::IntPoint PinchGestureInput::GetIntegerDeltaForEvent(
+ bool aIsStart, float x, float y) {
+ static gfx::Point sAccumulator(0.0f, 0.0f);
+ if (aIsStart) {
+ sAccumulator = gfx::Point(0.0f, 0.0f);
+ }
+ sAccumulator.x += x;
+ sAccumulator.y += y;
+ return gfx::IntPoint(TakeLargestInt(&sAccumulator.x),
+ TakeLargestInt(&sAccumulator.y));
+}
+
+TapGestureInput::TapGestureInput()
+ : InputData(TAPGESTURE_INPUT), mType(TAPGESTURE_LONG) {}
+
+TapGestureInput::TapGestureInput(TapGestureType aType, TimeStamp aTimeStamp,
+ const ScreenIntPoint& aPoint,
+ Modifiers aModifiers)
+ : InputData(TAPGESTURE_INPUT, aTimeStamp, aModifiers),
+ mType(aType),
+ mPoint(aPoint) {}
+
+TapGestureInput::TapGestureInput(TapGestureType aType, TimeStamp aTimeStamp,
+ const ParentLayerPoint& aLocalPoint,
+ Modifiers aModifiers)
+ : InputData(TAPGESTURE_INPUT, aTimeStamp, aModifiers),
+ mType(aType),
+ mLocalPoint(aLocalPoint) {}
+
+bool TapGestureInput::TransformToLocal(
+ const ScreenToParentLayerMatrix4x4& aTransform) {
+ Maybe<ParentLayerIntPoint> point = UntransformBy(aTransform, mPoint);
+ if (!point) {
+ return false;
+ }
+ mLocalPoint = *point;
+ return true;
+}
+
+WidgetSimpleGestureEvent TapGestureInput::ToWidgetEvent(
+ nsIWidget* aWidget) const {
+ WidgetSimpleGestureEvent event(true, eTapGesture, aWidget);
+
+ event.mTimeStamp = mTimeStamp;
+ event.mLayersId = mLayersId;
+ event.mRefPoint = ViewAs<LayoutDevicePixel>(
+ mPoint,
+ PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
+ event.mButtons = 0;
+ event.mClickCount = 1;
+ event.mModifiers = modifiers;
+
+ return event;
+}
+
+ScrollWheelInput::ScrollWheelInput()
+ : InputData(SCROLLWHEEL_INPUT),
+ mDeltaType(SCROLLDELTA_LINE),
+ mScrollMode(SCROLLMODE_INSTANT),
+ mHandledByAPZ(false),
+ mDeltaX(0.0),
+ mDeltaY(0.0),
+ mLineOrPageDeltaX(0),
+ mLineOrPageDeltaY(0),
+ mScrollSeriesNumber(0),
+ mUserDeltaMultiplierX(1.0),
+ mUserDeltaMultiplierY(1.0),
+ mMayHaveMomentum(false),
+ mIsMomentum(false),
+ mAPZAction(APZWheelAction::Scroll) {}
+
+ScrollWheelInput::ScrollWheelInput(
+ TimeStamp aTimeStamp, Modifiers aModifiers, ScrollMode aScrollMode,
+ ScrollDeltaType aDeltaType, const ScreenPoint& aOrigin, double aDeltaX,
+ double aDeltaY, bool aAllowToOverrideSystemScrollSpeed,
+ WheelDeltaAdjustmentStrategy aWheelDeltaAdjustmentStrategy)
+ : InputData(SCROLLWHEEL_INPUT, aTimeStamp, aModifiers),
+ mDeltaType(aDeltaType),
+ mScrollMode(aScrollMode),
+ mOrigin(aOrigin),
+ mHandledByAPZ(false),
+ mDeltaX(aDeltaX),
+ mDeltaY(aDeltaY),
+ mLineOrPageDeltaX(0),
+ mLineOrPageDeltaY(0),
+ mScrollSeriesNumber(0),
+ mUserDeltaMultiplierX(1.0),
+ mUserDeltaMultiplierY(1.0),
+ mMayHaveMomentum(false),
+ mIsMomentum(false),
+ mAllowToOverrideSystemScrollSpeed(aAllowToOverrideSystemScrollSpeed),
+ mWheelDeltaAdjustmentStrategy(aWheelDeltaAdjustmentStrategy),
+ mAPZAction(APZWheelAction::Scroll) {}
+
+ScrollWheelInput::ScrollWheelInput(const WidgetWheelEvent& aWheelEvent)
+ : InputData(SCROLLWHEEL_INPUT, aWheelEvent.mTimeStamp,
+ aWheelEvent.mModifiers),
+ mDeltaType(DeltaTypeForDeltaMode(aWheelEvent.mDeltaMode)),
+ mScrollMode(SCROLLMODE_INSTANT),
+ mHandledByAPZ(aWheelEvent.mFlags.mHandledByAPZ),
+ mDeltaX(aWheelEvent.mDeltaX),
+ mDeltaY(aWheelEvent.mDeltaY),
+ mWheelTicksX(aWheelEvent.mWheelTicksX),
+ mWheelTicksY(aWheelEvent.mWheelTicksX),
+ mLineOrPageDeltaX(aWheelEvent.mLineOrPageDeltaX),
+ mLineOrPageDeltaY(aWheelEvent.mLineOrPageDeltaY),
+ mScrollSeriesNumber(0),
+ mUserDeltaMultiplierX(1.0),
+ mUserDeltaMultiplierY(1.0),
+ mMayHaveMomentum(aWheelEvent.mMayHaveMomentum),
+ mIsMomentum(aWheelEvent.mIsMomentum),
+ mAllowToOverrideSystemScrollSpeed(
+ aWheelEvent.mAllowToOverrideSystemScrollSpeed),
+ mWheelDeltaAdjustmentStrategy(WheelDeltaAdjustmentStrategy::eNone),
+ mAPZAction(APZWheelAction::Scroll) {
+ mOrigin = ScreenPoint(ViewAs<ScreenPixel>(
+ aWheelEvent.mRefPoint,
+ PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
+}
+
+ScrollWheelInput::ScrollDeltaType ScrollWheelInput::DeltaTypeForDeltaMode(
+ uint32_t aDeltaMode) {
+ switch (aDeltaMode) {
+ case WheelEvent_Binding::DOM_DELTA_LINE:
+ return SCROLLDELTA_LINE;
+ case WheelEvent_Binding::DOM_DELTA_PAGE:
+ return SCROLLDELTA_PAGE;
+ case WheelEvent_Binding::DOM_DELTA_PIXEL:
+ return SCROLLDELTA_PIXEL;
+ default:
+ MOZ_CRASH();
+ }
+ return SCROLLDELTA_LINE;
+}
+
+uint32_t ScrollWheelInput::DeltaModeForDeltaType(ScrollDeltaType aDeltaType) {
+ switch (aDeltaType) {
+ case ScrollWheelInput::SCROLLDELTA_LINE:
+ return WheelEvent_Binding::DOM_DELTA_LINE;
+ case ScrollWheelInput::SCROLLDELTA_PAGE:
+ return WheelEvent_Binding::DOM_DELTA_PAGE;
+ case ScrollWheelInput::SCROLLDELTA_PIXEL:
+ default:
+ return WheelEvent_Binding::DOM_DELTA_PIXEL;
+ }
+}
+
+ScrollUnit ScrollWheelInput::ScrollUnitForDeltaType(
+ ScrollDeltaType aDeltaType) {
+ switch (aDeltaType) {
+ case SCROLLDELTA_LINE:
+ return ScrollUnit::LINES;
+ case SCROLLDELTA_PAGE:
+ return ScrollUnit::PAGES;
+ case SCROLLDELTA_PIXEL:
+ return ScrollUnit::DEVICE_PIXELS;
+ default:
+ MOZ_CRASH();
+ }
+ return ScrollUnit::LINES;
+}
+
+WidgetWheelEvent ScrollWheelInput::ToWidgetEvent(nsIWidget* aWidget) const {
+ WidgetWheelEvent wheelEvent(true, eWheel, aWidget);
+ wheelEvent.mModifiers = this->modifiers;
+ wheelEvent.mTimeStamp = mTimeStamp;
+ wheelEvent.mLayersId = mLayersId;
+ wheelEvent.mRefPoint = RoundedToInt(ViewAs<LayoutDevicePixel>(
+ mOrigin,
+ PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
+ wheelEvent.mButtons = 0;
+ wheelEvent.mDeltaMode = DeltaModeForDeltaType(mDeltaType);
+ wheelEvent.mMayHaveMomentum = mMayHaveMomentum;
+ wheelEvent.mIsMomentum = mIsMomentum;
+ wheelEvent.mDeltaX = mDeltaX;
+ wheelEvent.mDeltaY = mDeltaY;
+ wheelEvent.mWheelTicksX = mWheelTicksX;
+ wheelEvent.mWheelTicksY = mWheelTicksY;
+ wheelEvent.mLineOrPageDeltaX = mLineOrPageDeltaX;
+ wheelEvent.mLineOrPageDeltaY = mLineOrPageDeltaY;
+ wheelEvent.mAllowToOverrideSystemScrollSpeed =
+ mAllowToOverrideSystemScrollSpeed;
+ wheelEvent.mFlags.mHandledByAPZ = mHandledByAPZ;
+ wheelEvent.mFocusSequenceNumber = mFocusSequenceNumber;
+ return wheelEvent;
+}
+
+bool ScrollWheelInput::TransformToLocal(
+ const ScreenToParentLayerMatrix4x4& aTransform) {
+ Maybe<ParentLayerPoint> point = UntransformBy(aTransform, mOrigin);
+ if (!point) {
+ return false;
+ }
+ mLocalOrigin = *point;
+ return true;
+}
+
+bool ScrollWheelInput::IsCustomizedByUserPrefs() const {
+ return mUserDeltaMultiplierX != 1.0 || mUserDeltaMultiplierY != 1.0;
+}
+
+KeyboardInput::KeyboardInput(const WidgetKeyboardEvent& aEvent)
+ : InputData(KEYBOARD_INPUT, aEvent.mTimeStamp, aEvent.mModifiers),
+ mKeyCode(aEvent.mKeyCode),
+ mCharCode(aEvent.mCharCode),
+ mHandledByAPZ(false) {
+ switch (aEvent.mMessage) {
+ case eKeyPress: {
+ mType = KeyboardInput::KEY_PRESS;
+ break;
+ }
+ case eKeyUp: {
+ mType = KeyboardInput::KEY_UP;
+ break;
+ }
+ case eKeyDown: {
+ mType = KeyboardInput::KEY_DOWN;
+ break;
+ }
+ default:
+ mType = KeyboardInput::KEY_OTHER;
+ break;
+ }
+
+ aEvent.GetShortcutKeyCandidates(mShortcutCandidates);
+}
+
+KeyboardInput::KeyboardInput()
+ : InputData(KEYBOARD_INPUT),
+ mType(KEY_DOWN),
+ mKeyCode(0),
+ mCharCode(0),
+ mHandledByAPZ(false) {}
+
+} // namespace mozilla