Global Metrics

path: .metrics.mi.mi_original
old: 10.002557278489874
new: -20.03810864341135

path: .metrics.mi.mi_visual_studio
old: 5.849448700871271
new: 0.0

path: .metrics.mi.mi_sei
old: -22.24583314623475
new: -73.65679725138594

path: .metrics.cyclomatic.average
old: 3.8
new: 2.3846153846153846

path: .metrics.cyclomatic.sum
old: 57.0
new: 124.0

path: .metrics.nargs.sum
old: 35.0
new: 91.0

path: .metrics.nargs.average
old: 2.6923076923076925
new: 1.82

path: .metrics.loc.ploc
old: 294.0
new: 719.0

path: .metrics.loc.sloc
old: 459.0
new: 852.0

path: .metrics.loc.blank
old: 64.0
new: 83.0

path: .metrics.loc.lloc
old: 115.0
new: 311.0

path: .metrics.loc.cloc
old: 101.0
new: 50.0

path: .metrics.halstead.level
old: 0.01432678260049955
new: 0.013303153045826384

path: .metrics.halstead.difficulty
old: 69.79934210526316
new: 75.17014925373134

path: .metrics.halstead.time
old: 44391.21755852657
new: 116022.29125346296

path: .metrics.halstead.estimated_program_length
old: 1268.1499879822538
new: 2996.103090642544

path: .metrics.halstead.n1
old: 33.0
new: 36.0

path: .metrics.halstead.vocabulary
old: 185.0
new: 371.0

path: .metrics.halstead.bugs
old: 2.870285651696175
new: 5.446130461031956

path: .metrics.halstead.n2
old: 152.0
new: 335.0

path: .metrics.halstead.volume
old: 11447.699819984797
new: 27782.32135090071

path: .metrics.halstead.effort
old: 799041.9160534783
new: 2088401.2425623336

path: .metrics.halstead.N1
old: 877.0
new: 1856.0

path: .metrics.halstead.purity_ratio
old: 0.8343092026199038
new: 0.9204617789992454

path: .metrics.halstead.N2
old: 643.0
new: 1399.0

path: .metrics.halstead.length
old: 1520.0
new: 3255.0

path: .metrics.nexits.sum
old: 14.0
new: 47.0

path: .metrics.nexits.average
old: 1.0769230769230769
new: 0.94

path: .metrics.cognitive.sum
old: 51.0
new: 33.0

path: .metrics.cognitive.average
old: 3.923076923076923
new: 0.66

path: .metrics.nom.functions
old: 13.0
new: 50.0

path: .metrics.nom.total
old: 13.0
new: 50.0

Spaces Data

Minimal test - lines (19, 852)

path: .spaces[0].metrics.cognitive.average
old: 2.0
new: 0.66

path: .spaces[0].metrics.cognitive.sum
old: 2.0
new: 33.0

path: .spaces[0].metrics.loc.lloc
old: 9.0
new: 311.0

path: .spaces[0].metrics.loc.blank
old: 5.0
new: 81.0

path: .spaces[0].metrics.loc.cloc
old: 11.0
new: 45.0

path: .spaces[0].metrics.loc.ploc
old: 15.0
new: 708.0

path: .spaces[0].metrics.loc.sloc
old: 31.0
new: 834.0

path: .spaces[0].metrics.nom.total
old: 1.0
new: 50.0

path: .spaces[0].metrics.nom.functions
old: 1.0
new: 50.0

path: .spaces[0].metrics.cyclomatic.sum
old: 3.0
new: 123.0

path: .spaces[0].metrics.cyclomatic.average
old: 3.0
new: 2.411764705882353

path: .spaces[0].metrics.nexits.average
old: 0.0
new: 0.94

path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 47.0

path: .spaces[0].metrics.halstead.n2
old: 17.0
new: 324.0

path: .spaces[0].metrics.halstead.difficulty
old: 14.529411764705882
new: 77.11111111111111

path: .spaces[0].metrics.halstead.volume
old: 456.34082539159226
new: 27547.571444493464

path: .spaces[0].metrics.halstead.level
old: 0.06882591093117409
new: 0.012968299711815562

path: .spaces[0].metrics.halstead.purity_ratio
old: 1.264436393947204
new: 0.8903294392683544

path: .spaces[0].metrics.halstead.bugs
old: 0.11764414441078624
new: 5.508232516887917

path: .spaces[0].metrics.halstead.vocabulary
old: 30.0
new: 360.0

path: .spaces[0].metrics.halstead.N1
old: 55.0
new: 1856.0

path: .spaces[0].metrics.halstead.n1
old: 13.0
new: 36.0

path: .spaces[0].metrics.halstead.length
old: 93.0
new: 3244.0

path: .spaces[0].metrics.halstead.estimated_program_length
old: 117.59258463708996
new: 2888.2287009865418

path: .spaces[0].metrics.halstead.time
old: 368.3535420644552
new: 118012.43569431153

path: .spaces[0].metrics.halstead.effort
old: 6630.363757160193
new: 2124223.842497607

path: .spaces[0].metrics.halstead.N2
old: 38.0
new: 1388.0

path: .spaces[0].metrics.nargs.sum
old: 1.0
new: 91.0

path: .spaces[0].metrics.nargs.average
old: 1.0
new: 1.82

path: .spaces[0].metrics.mi.mi_sei
old: 83.98099404853237
new: -73.58437591860728

path: .spaces[0].metrics.mi.mi_visual_studio
old: 48.44360206097722
new: 0.0

path: .spaces[0].metrics.mi.mi_original
old: 82.83855952427103
new: -19.41806334948612

Code

namespace mozilla {

using namespace dom;

InputData::~InputData() = default;

InputData::InputData(InputType aInputType)
    : mInputType(aInputType),
      mTime(0),
      mFocusSequenceNumber(0),
      mLayersId{0},
      modifiers(0) {}

InputData::InputData(InputType aInputType, uint32_t aTime, TimeStamp aTimeStamp,
                     Modifiers aModifiers)
    : mInputType(aInputType),
      mTime(aTime),
      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 SingleTouchData::ToNewDOMTouch() const {
  MOZ_ASSERT(NS_IsMainThread(),
             "Can only create dom::Touch instances on main thread");
  RefPtr touch =
      new Touch(mIdentifier,
                LayoutDeviceIntPoint::Truncate(mScreenPoint.x, mScreenPoint.y),
                LayoutDeviceIntPoint::Truncate(mRadius.width, mRadius.height),
                mRotationAngle, mForce);
  return touch.forget();
}

MultiTouchInput::MultiTouchInput(MultiTouchType aType, uint32_t aTime,
                                 TimeStamp aTimeStamp, Modifiers aModifiers)
    : InputData(MULTITOUCH_INPUT, aTime, aTimeStamp, aModifiers),
      mType(aType),
      mHandledByAPZ(false) {}

MultiTouchInput::MultiTouchInput()
    : InputData(MULTITOUCH_INPUT),
      mType(MULTITOUCH_START),
      mHandledByAPZ(false) {}

MultiTouchInput::MultiTouchInput(const MultiTouchInput& aOther)
    : InputData(MULTITOUCH_INPUT, aOther.mTime, aOther.mTimeStamp,
                aOther.modifiers),
      mType(aOther.mType),
      mScreenOffset(aOther.mScreenOffset),
      mHandledByAPZ(aOther.mHandledByAPZ) {
  mTouches.AppendElements(aOther.mTouches);
}

MultiTouchInput::MultiTouchInput(const WidgetTouchEvent& aTouchEvent)
    : InputData(MULTITOUCH_INPUT, aTouchEvent.mTime, aTouchEvent.mTimeStamp,
                aTouchEvent.mModifiers),
      mHandledByAPZ(aTouchEvent.mFlags.mHandledByAPZ) {
  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(
      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(
            domTouch->mRefPoint,
            PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent),
        ScreenSize(radiusX, radiusY), rotationAngle, force);

    mTouches.AppendElement(data);
  }
}

void MultiTouchInput::Translate(const ScreenPoint& aTranslation) {
  const int32_t xTranslation = (int32_t)(aTranslation.x + 0.5f);
  const int32_t yTranslation = (int32_t)(aTranslation.y + 0.5f);

  for (auto& touchData : mTouches) {
    for (auto& historicalData : touchData.mHistoricalData) {
      historicalData.mScreenPoint.MoveBy(xTranslation, yTranslation);
    }
    touchData.mScreenPoint.MoveBy(xTranslation, yTranslation);
  }
}

WidgetTouchEvent MultiTouchInput::ToWidgetTouchEvent(nsIWidget* aWidget) const {
  MOZ_ASSERT(NS_IsMainThread(),
             "Can only convert To WidgetTouchEvent on main thread");

  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.mTime = this->mTime;
  event.mTimeStamp = this->mTimeStamp;
  event.mFlags.mHandledByAPZ = mHandledByAPZ;
  event.mFocusSequenceNumber = mFocusSequenceNumber;
  event.mLayersId = mLayersId;

  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 historicalPoint =
          UntransformBy(aTransform, historicalData.mScreenPoint);
      if (!historicalPoint) {
        return false;
      }
      historicalData.mLocalScreenPoint = *historicalPoint;
    }
    Maybe 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) {}

MouseInput::MouseInput(MouseType aType, ButtonType aButtonType,
                       uint16_t aInputSource, int16_t aButtons,
                       const ScreenPoint& aPoint, uint32_t aTime,
                       TimeStamp aTimeStamp, Modifiers aModifiers)
    : InputData(MOUSE_INPUT, aTime, aTimeStamp, aModifiers),
      mType(aType),
      mButtonType(aButtonType),
      mInputSource(aInputSource),
      mButtons(aButtons),
      mOrigin(aPoint),
      mHandledByAPZ(false) {}

MouseInput::MouseInput(const WidgetMouseEventBase& aMouseEvent)
    : InputData(MOUSE_INPUT, aMouseEvent.mTime, aMouseEvent.mTimeStamp,
                aMouseEvent.mModifiers),
      mType(MOUSE_NONE),
      mButtonType(NONE),
      mInputSource(aMouseEvent.mInputSource),
      mButtons(aMouseEvent.mButtons),
      mHandledByAPZ(aMouseEvent.mFlags.mHandledByAPZ) {
  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 eMouseHitTest:
      mType = MOUSE_HITTEST;
      break;
    default:
      MOZ_ASSERT_UNREACHABLE("Mouse event type not supported");
      break;
  }

  mOrigin = ScreenPoint(ViewAs(
      aMouseEvent.mRefPoint,
      PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
}

bool MouseInput::IsLeftButton() const { return mButtonType == PRIMARY_BUTTON; }

bool MouseInput::TransformToLocal(
    const ScreenToParentLayerMatrix4x4& aTransform) {
  Maybe 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 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_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.mTime = mTime;
  event.mTimeStamp = mTimeStamp;
  event.mLayersId = mLayersId;
  event.mFlags.mHandledByAPZ = mHandledByAPZ;
  event.mRefPoint = RoundedToInt(ViewAs(
      mOrigin,
      PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
  event.mClickCount = clickCount;
  event.mInputSource = mInputSource;
  event.mFocusSequenceNumber = mFocusSequenceNumber;
  event.mExitFrom = exitFrom;

  return event;
}

PanGestureInput::PanGestureInput()
    : InputData(PANGESTURE_INPUT),
      mType(PANGESTURE_MAYSTART),
      mLineOrPageDeltaX(0),
      mLineOrPageDeltaY(0),
      mUserDeltaMultiplierX(1.0),
      mUserDeltaMultiplierY(1.0),
      mHandledByAPZ(false),
      mFollowedByMomentum(false),
      mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection(false),
      mOverscrollBehaviorAllowsSwipe(false),
      mSimulateMomentum(false) {}

PanGestureInput::PanGestureInput(PanGestureType aType, uint32_t aTime,
                                 TimeStamp aTimeStamp,
                                 const ScreenPoint& aPanStartPoint,
                                 const ScreenPoint& aPanDisplacement,
                                 Modifiers aModifiers)
    : InputData(PANGESTURE_INPUT, aTime, aTimeStamp, aModifiers),
      mType(aType),
      mPanStartPoint(aPanStartPoint),
      mPanDisplacement(aPanDisplacement),
      mLineOrPageDeltaX(0),
      mLineOrPageDeltaY(0),
      mUserDeltaMultiplierX(1.0),
      mUserDeltaMultiplierY(1.0),
      mHandledByAPZ(false),
      mFollowedByMomentum(false),
      mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection(false),
      mOverscrollBehaviorAllowsSwipe(false),
      mSimulateMomentum(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.mTime = mTime;
  wheelEvent.mTimeStamp = mTimeStamp;
  wheelEvent.mLayersId = mLayersId;
  wheelEvent.mRefPoint = RoundedToInt(ViewAs(
      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;
  if (mDeltaType == PanGestureInput::PANDELTA_PAGE) {
    // Emulate legacy widget/gtk behavior
    wheelEvent.mDeltaMode = WheelEvent_Binding::DOM_DELTA_LINE;
    wheelEvent.mIsNoLineOrPageDelta = true;
    wheelEvent.mScrollType = WidgetWheelEvent::SCROLL_ASYNCHRONOUSELY;
    wheelEvent.mDeltaX *= 3;
    wheelEvent.mDeltaY *= 3;
  } else {
    wheelEvent.mDeltaMode = WheelEvent_Binding::DOM_DELTA_PIXEL;
  }
  return wheelEvent;
}

bool PanGestureInput::TransformToLocal(
    const ScreenToParentLayerMatrix4x4& aTransform) {
  Maybe 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.x = mPanDisplacement.x;
    mLocalPanDisplacement.y = mPanDisplacement.y;
    return true;
  }

  Maybe 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::Float* aFloat) {
  int32_t result(*aFloat);  // truncate towards zero
  *aFloat -= 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, uint32_t aTime,
    TimeStamp aTimeStamp, const ExternalPoint& aScreenOffset,
    const ScreenPoint& aFocusPoint, ScreenCoord aCurrentSpan,
    ScreenCoord aPreviousSpan, Modifiers aModifiers)
    : InputData(PINCHGESTURE_INPUT, aTime, 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 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.mTime = mTime;
  wheelEvent.mTimeStamp = mTimeStamp;
  wheelEvent.mLayersId = mLayersId;
  wheelEvent.mRefPoint = RoundedToInt(ViewAs(
      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(OS_MACOSX)
  // 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
}

/* 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, uint32_t aTime,
                                 TimeStamp aTimeStamp,
                                 const ScreenIntPoint& aPoint,
                                 Modifiers aModifiers)
    : InputData(TAPGESTURE_INPUT, aTime, aTimeStamp, aModifiers),
      mType(aType),
      mPoint(aPoint) {}

TapGestureInput::TapGestureInput(TapGestureType aType, uint32_t aTime,
                                 TimeStamp aTimeStamp,
                                 const ParentLayerPoint& aLocalPoint,
                                 Modifiers aModifiers)
    : InputData(TAPGESTURE_INPUT, aTime, aTimeStamp, aModifiers),
      mType(aType),
      mLocalPoint(aLocalPoint) {}

bool TapGestureInput::TransformToLocal(
    const ScreenToParentLayerMatrix4x4& aTransform) {
  Maybe point = UntransformBy(aTransform, mPoint);
  if (!point) {
    return false;
  }
  mLocalPoint = *point;
  return true;
}

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(
    uint32_t aTime, TimeStamp aTimeStamp, Modifiers aModifiers,
    ScrollMode aScrollMode, ScrollDeltaType aDeltaType,
    const ScreenPoint& aOrigin, double aDeltaX, double aDeltaY,
    bool aAllowToOverrideSystemScrollSpeed,
    WheelDeltaAdjustmentStrategy aWheelDeltaAdjustmentStrategy)
    : InputData(SCROLLWHEEL_INPUT, aTime, 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.mTime, aWheelEvent.mTimeStamp,
                aWheelEvent.mModifiers),
      mDeltaType(DeltaTypeForDeltaMode(aWheelEvent.mDeltaMode)),
      mScrollMode(SCROLLMODE_INSTANT),
      mHandledByAPZ(aWheelEvent.mFlags.mHandledByAPZ),
      mDeltaX(aWheelEvent.mDeltaX),
      mDeltaY(aWheelEvent.mDeltaY),
      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(
      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.mTime = mTime;
  wheelEvent.mTimeStamp = mTimeStamp;
  wheelEvent.mLayersId = mLayersId;
  wheelEvent.mRefPoint = RoundedToInt(ViewAs(
      mOrigin,
      PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
  wheelEvent.mButtons = 0;
  wheelEvent.mDeltaMode = DeltaModeForDeltaType(mDeltaType);
  wheelEvent.mMayHaveMomentum = mMayHaveMomentum;
  wheelEvent.mIsMomentum = mIsMomentum;
  wheelEvent.mDeltaX = mDeltaX;
  wheelEvent.mDeltaY = mDeltaY;
  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 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.mTime, 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