gecko-dev/widget/InputData.cpp

966 строки
32 KiB
C++

/* -*- 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/MouseEvents.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/SwipeTracker.h"
#include "mozilla/TextEvents.h"
#include "mozilla/TouchEvents.h"
#include "nsContentUtils.h"
#include "nsDebug.h"
#include "nsThreadUtils.h"
#include "UnitTransforms.h"
#include <type_traits>
namespace mozilla {
using namespace dom;
template WidgetMouseEvent MouseInput::ToWidgetEvent(nsIWidget* aWidget) const;
template WidgetPointerEvent MouseInput::ToWidgetEvent(nsIWidget* aWidget) const;
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),
mIgnoreCapturingContent(false),
mSynthesizeMoveAfterDispatch(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),
mIgnoreCapturingContent(false),
mSynthesizeMoveAfterDispatch(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 &&
static_cast<const WidgetMouseEvent&>(aMouseEvent)
.mClickEventPrevented),
mIgnoreCapturingContent((aMouseEvent.mClass == eMouseEventClass ||
aMouseEvent.mClass == ePointerEventClass) &&
static_cast<const WidgetMouseEvent&>(aMouseEvent)
.mIgnoreCapturingContent),
mSynthesizeMoveAfterDispatch(
(aMouseEvent.mClass == eMouseEventClass ||
aMouseEvent.mClass == ePointerEventClass) &&
static_cast<const WidgetMouseEvent&>(aMouseEvent)
.mSynthesizeMoveAfterDispatch) {
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;
case eContextMenu:
mType = MOUSE_CONTEXTMENU;
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;
}
bool MouseInput::IsPointerEventType() const {
return mType == MOUSE_CONTEXTMENU;
}
template <class WidgetMouseOrPointerEvent>
WidgetMouseOrPointerEvent MouseInput::ToWidgetEvent(nsIWidget* aWidget) const {
MOZ_ASSERT(NS_IsMainThread(),
"Can only convert To WidgetTouchEvent on main thread");
const DebugOnly<bool> isPointerEvent =
std::is_same<WidgetMouseOrPointerEvent, WidgetPointerEvent>::value;
const DebugOnly<bool> isMouseEvent =
std::is_same<WidgetMouseOrPointerEvent, WidgetMouseEvent>::value;
MOZ_ASSERT(!IsPointerEventType() || isPointerEvent,
"Please use ToWidgetEvent<WidgetPointerEvent>() for the instance");
MOZ_ASSERT(IsPointerEventType() || isMouseEvent,
"Please use ToWidgetEvent<WidgetMouseEvent>() for the instance");
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;
case MOUSE_CONTEXTMENU:
msg = eContextMenu;
MOZ_ASSERT(mButtonType == MouseInput::SECONDARY_BUTTON);
break;
default:
MOZ_ASSERT_UNREACHABLE(
"Did not assign a type to WidgetMouseEvent in MouseInput");
break;
}
WidgetMouseOrPointerEvent event(true, msg, aWidget);
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;
event.mIgnoreCapturingContent = mIgnoreCapturingContent;
event.mSynthesizeMoveAfterDispatch = mSynthesizeMoveAfterDispatch;
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