static float
ComputeDistanceFromRect(const nsPoint& aPoint, const nsRect& aRect)
{
  nscoord dx = std::max(0, std::max(aRect.x - aPoint.x, aPoint.x - aRect.XMost()));
  nscoord dy = std::max(0, std::max(aRect.y - aPoint.y, aPoint.y - aRect.YMost()));
  return float(NS_hypot(dx, dy));
}
float GetCurrentSpan(const MultiTouchInput& aEvent)
{
  const ScreenIntPoint& firstTouch = aEvent.mTouches[0].mScreenPoint,
                       secondTouch = aEvent.mTouches[1].mScreenPoint;
  ScreenIntPoint delta = secondTouch - firstTouch;
  return float(NS_hypot(delta.x, delta.y));
}
bool
AccessibleCaretEventHub::MoveDistanceIsLarge(const nsPoint& aPoint) const
{
  nsPoint delta = aPoint - mPressPoint;
  return NS_hypot(delta.x, delta.y) >
         nsPresContext::AppUnitsPerCSSPixel() * kMoveStartToleranceInPixel;
}
nsEventStatus GestureEventListener::HandlePinchGestureEvent(const MultiTouchInput& aEvent, bool aClearTouches)
{
  nsEventStatus rv = nsEventStatus_eIgnore;

  if (mTouches.Length() > 1 && !aClearTouches) {
    const nsIntPoint& firstTouch = mTouches[0].mScreenPoint,
                      secondTouch = mTouches[mTouches.Length() - 1].mScreenPoint;
    nsIntPoint focusPoint =
      nsIntPoint((firstTouch.x + secondTouch.x)/2,
                 (firstTouch.y + secondTouch.y)/2);
    float currentSpan =
      float(NS_hypot(firstTouch.x - secondTouch.x,
                     firstTouch.y - secondTouch.y));

    if (mState == NoGesture) {
      PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START,
                                   aEvent.mTime,
                                   focusPoint,
                                   currentSpan,
                                   currentSpan);

      mAsyncPanZoomController->HandleInputEvent(pinchEvent);

      mState = InPinchGesture;
    } else {
      PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE,
                                   aEvent.mTime,
                                   focusPoint,
                                   currentSpan,
                                   mPreviousSpan);

      mAsyncPanZoomController->HandleInputEvent(pinchEvent);
    }

    mPreviousSpan = currentSpan;

    rv = nsEventStatus_eConsumeNoDefault;
  } else if (mState == InPinchGesture) {
    PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_END,
                                 aEvent.mTime,
                                 mTouches[0].mScreenPoint,
                                 1.0f,
                                 1.0f);
 
    mAsyncPanZoomController->HandleInputEvent(pinchEvent);

    mState = NoGesture;

    rv = nsEventStatus_eConsumeNoDefault;
  }

  if (aClearTouches) {
    mTouches.Clear();
  }

  return rv;
}
nsresult SVGNumberPairSMILType::ComputeDistance(const SMILValue& aFrom,
                                                const SMILValue& aTo,
                                                double& aDistance) const {
  MOZ_ASSERT(aFrom.mType == aTo.mType, "Trying to compare different types");
  MOZ_ASSERT(aFrom.mType == this, "Unexpected source type");

  double delta[2];
  delta[0] = aTo.mU.mNumberPair[0] - aFrom.mU.mNumberPair[0];
  delta[1] = aTo.mU.mNumberPair[1] - aFrom.mU.mNumberPair[1];

  aDistance = NS_hypot(delta[0], delta[1]);
  return NS_OK;
}
// For 'by'.
bool
SVGMotionSMILPathUtils::PathGenerator::
  LineToRelative(const nsAString& aCoordPairStr, double& aSegmentDistance)
{
  mHaveReceivedCommands = true;

  float xVal, yVal;
  if (!ParseCoordinatePair(aCoordPairStr, xVal, yVal)) {
    return false;
  }
  mPathBuilder->LineTo(mPathBuilder->CurrentPoint() + Point(xVal, yVal));
  aSegmentDistance = NS_hypot(xVal, yVal);
  return true;
}
nsresult
SVGIntegerPairSMILType::ComputeDistance(const nsSMILValue& aFrom,
                                        const nsSMILValue& aTo,
                                        double& aDistance) const
{
  NS_PRECONDITION(aFrom.mType == aTo.mType,"Trying to compare different types");
  NS_PRECONDITION(aFrom.mType == this, "Unexpected source type");

  double delta[2];
  delta[0] = aTo.mU.mIntPair[0] - aFrom.mU.mIntPair[0];
  delta[1] = aTo.mU.mIntPair[1] - aFrom.mU.mIntPair[1];

  aDistance = NS_hypot(delta[0], delta[1]);
  return NS_OK;
}
// For 'to' and every entry in 'values' except the first.
bool
SVGMotionSMILPathUtils::PathGenerator::
  LineToAbsolute(const nsAString& aCoordPairStr, double& aSegmentDistance)
{
  mHaveReceivedCommands = true;

  float xVal, yVal;
  if (!ParseCoordinatePair(aCoordPairStr, xVal, yVal)) {
    return false;
  }
  gfxPoint initialPoint = mGfxContext.CurrentPoint();

  mGfxContext.LineTo(gfxPoint(xVal, yVal));
  aSegmentDistance = NS_hypot(initialPoint.x - xVal, initialPoint.y -yVal);
  return true;
}
nsresult
SVGMotionSMILType::ComputeDistance(const nsSMILValue& aFrom,
                                   const nsSMILValue& aTo,
                                   double& aDistance) const
{
  NS_ABORT_IF_FALSE(aFrom.mType == aTo.mType, "Incompatible SMIL types");
  NS_ABORT_IF_FALSE(aFrom.mType == this, "Unexpected SMIL type");
  const MotionSegmentArray& fromArr = ExtractMotionSegmentArray(aFrom);
  const MotionSegmentArray& toArr = ExtractMotionSegmentArray(aTo);

  // ComputeDistance is only used for calculating distances between single
  // values in a values array. So we should only have one entry in each array.
  NS_ABORT_IF_FALSE(fromArr.Length() == 1,
                    "Wrong number of elements in from value");
  NS_ABORT_IF_FALSE(toArr.Length() == 1,
                    "Wrong number of elements in to value");

  const MotionSegment& from = fromArr[0];
  const MotionSegment& to = toArr[0];

  NS_ABORT_IF_FALSE(from.mSegmentType == to.mSegmentType,
                    "Mismatched MotionSegment types");
  if (from.mSegmentType == eSegmentType_PathPoint) {
    const PathPointParams& fromParams = from.mU.mPathPointParams;
    const PathPointParams& toParams   = to.mU.mPathPointParams;
    NS_ABORT_IF_FALSE(fromParams.mPath == toParams.mPath,
                      "Interpolation endpoints should be from same path");
    NS_ABORT_IF_FALSE(fromParams.mDistToPoint <= toParams.mDistToPoint,
                      "To value shouldn't be before from value on path");
    aDistance = fabs(toParams.mDistToPoint - fromParams.mDistToPoint);
  } else {
    const TranslationParams& fromParams = from.mU.mTranslationParams;
    const TranslationParams& toParams   = to.mU.mTranslationParams;
    float dX = toParams.mX - fromParams.mX;
    float dY = toParams.mY - fromParams.mY;
    aDistance = NS_hypot(dX, dY);
  }

  return NS_OK;
}
Beispiel #10
0
bool
AnalyserNode::FFTAnalysis()
{
  float* inputBuffer;
  bool allocated = false;
  if (mWriteIndex == 0) {
    inputBuffer = mBuffer.Elements();
  } else {
    inputBuffer = static_cast<float*>(moz_malloc(FftSize() * sizeof(float)));
    if (!inputBuffer) {
      return false;
    }
    memcpy(inputBuffer, mBuffer.Elements() + mWriteIndex, sizeof(float) * (FftSize() - mWriteIndex));
    memcpy(inputBuffer + FftSize() - mWriteIndex, mBuffer.Elements(), sizeof(float) * mWriteIndex);
    allocated = true;
  }

  ApplyBlackmanWindow(inputBuffer, FftSize());

  mAnalysisBlock.PerformFFT(inputBuffer);

  // Normalize so than an input sine wave at 0dBfs registers as 0dBfs (undo FFT scaling factor).
  const double magnitudeScale = 1.0 / FftSize();

  for (uint32_t i = 0; i < mOutputBuffer.Length(); ++i) {
    double scalarMagnitude = NS_hypot(mAnalysisBlock.RealData(i),
                                      mAnalysisBlock.ImagData(i)) *
                             magnitudeScale;
    mOutputBuffer[i] = mSmoothingTimeConstant * mOutputBuffer[i] +
                       (1.0 - mSmoothingTimeConstant) * scalarMagnitude;
  }

  if (allocated) {
    moz_free(inputBuffer);
  }
  return true;
}
nsEventStatus GestureEventListener::HandleInputTouchMove()
{
  nsEventStatus rv = nsEventStatus_eIgnore;

  switch (mState) {
  case GESTURE_NONE:
  case GESTURE_LONG_TOUCH_DOWN:
    // Ignore this input signal as the corresponding events get handled by APZC
    break;

  case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
  case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
  case GESTURE_SECOND_SINGLE_TOUCH_DOWN: {
    // If we move too much, bail out of the tap.
    ScreenIntPoint delta = mLastTouchInput.mTouches[0].mScreenPoint - mTouchStartPosition;
    if (NS_hypot(delta.x, delta.y) > AsyncPanZoomController::GetTouchStartTolerance()) {
      CancelLongTapTimeoutTask();
      CancelMaxTapTimeoutTask();
      SetState(GESTURE_NONE);
    }
    break;
  }

  case GESTURE_MULTI_TOUCH_DOWN: {
    if (mLastTouchInput.mTouches.Length() < 2) {
      NS_WARNING("Wrong input: less than 2 moving points in GESTURE_MULTI_TOUCH_DOWN state");
      break;
    }

    float currentSpan = GetCurrentSpan(mLastTouchInput);

    mSpanChange += fabsf(currentSpan - mPreviousSpan);
    if (mSpanChange > PINCH_START_THRESHOLD) {
      SetState(GESTURE_PINCH);
      PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START,
                                   mLastTouchInput.mTime,
                                   GetCurrentFocus(mLastTouchInput),
                                   currentSpan,
                                   currentSpan,
                                   mLastTouchInput.modifiers);

      mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
    }
    rv = nsEventStatus_eConsumeNoDefault;
    mPreviousSpan = currentSpan;
    break;
  }

  case GESTURE_PINCH: {
    if (mLastTouchInput.mTouches.Length() < 2) {
      NS_WARNING("Wrong input: less than 2 moving points in GESTURE_PINCH state");
      // Prevent APZC::OnTouchMove() from handling this wrong input
      rv = nsEventStatus_eConsumeNoDefault;
      break;
    }

    float currentSpan = GetCurrentSpan(mLastTouchInput);

    PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE,
                                 mLastTouchInput.mTime,
                                 GetCurrentFocus(mLastTouchInput),
                                 currentSpan,
                                 mPreviousSpan,
                                 mLastTouchInput.modifiers);

    mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
    rv = nsEventStatus_eConsumeNoDefault;
    mPreviousSpan = currentSpan;

    break;
  }

  default:
    NS_WARNING("Unhandled state upon touch move");
    SetState(GESTURE_NONE);
    break;
  }

  return rv;
}
nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
{
  if (aEvent.mInputType != MULTITOUCH_INPUT) {
    return nsEventStatus_eIgnore;
  }

  const MultiTouchInput& event = static_cast<const MultiTouchInput&>(aEvent);

  // Cache the current event since it may become the single or long tap that we
  // send.
  mLastTouchInput = event;

  switch (event.mType)
  {
  case MultiTouchInput::MULTITOUCH_START:
  case MultiTouchInput::MULTITOUCH_ENTER: {
    for (size_t i = 0; i < event.mTouches.Length(); i++) {
      bool foundAlreadyExistingTouch = false;
      for (size_t j = 0; j < mTouches.Length(); j++) {
        if (mTouches[j].mIdentifier == event.mTouches[i].mIdentifier) {
          foundAlreadyExistingTouch = true;
        }
      }

      NS_WARN_IF_FALSE(!foundAlreadyExistingTouch, "Tried to add a touch that already exists");

      // If we didn't find a touch in our list that matches this, then add it.
      // If it already existed, we don't want to add it twice because that
      // messes with our touch move/end code.
      if (!foundAlreadyExistingTouch) {
        mTouches.AppendElement(event.mTouches[i]);
      }
    }

    size_t length = mTouches.Length();
    if (length == 1) {
      mTapStartTime = event.mTime;
      mTouchStartPosition = event.mTouches[0].mScreenPoint;
      if (mState == GESTURE_NONE) {
        mState = GESTURE_WAITING_SINGLE_TAP;

        mLongTapTimeoutTask =
          NewRunnableMethod(this, &GestureEventListener::TimeoutLongTap);

        MessageLoop::current()->PostDelayedTask(
          FROM_HERE,
          mLongTapTimeoutTask,
          Preferences::GetInt("ui.click_hold_context_menus.delay", 500));
      }
    } else if (length == 2) {
      // Another finger has been added; it can't be a tap anymore.
      HandleTapCancel(event);
    }

    break;
  }
  case MultiTouchInput::MULTITOUCH_MOVE: {
    // If we move too much, bail out of the tap.
    nsIntPoint touch = (nsIntPoint&)event.mTouches[0].mScreenPoint;
    if (mTouches.Length() == 1 &&
        NS_hypot(mTouchStartPosition.x - touch.x, mTouchStartPosition.y - touch.y) >
          mAsyncPanZoomController->GetDPI() * AsyncPanZoomController::TOUCH_START_TOLERANCE)
    {
      HandleTapCancel(event);
    }

    bool foundAlreadyExistingTouch = false;
    for (size_t i = 0; i < mTouches.Length(); i++) {
      for (size_t j = 0; j < event.mTouches.Length(); j++) {
        if (mTouches[i].mIdentifier == event.mTouches[j].mIdentifier) {
          foundAlreadyExistingTouch = true;
          mTouches[i] = event.mTouches[j];
        }
      }
    }

    NS_WARN_IF_FALSE(foundAlreadyExistingTouch, "Touch moved, but not in list");

    break;
  }
  case MultiTouchInput::MULTITOUCH_END:
  case MultiTouchInput::MULTITOUCH_LEAVE: {
    bool foundAlreadyExistingTouch = false;
    for (size_t i = 0; i < event.mTouches.Length() && !foundAlreadyExistingTouch; i++) {
      for (size_t j = 0; j < mTouches.Length() && !foundAlreadyExistingTouch; j++) {
        if (event.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
          foundAlreadyExistingTouch = true;
          mTouches.RemoveElementAt(j);
        }
      }
    }

    NS_WARN_IF_FALSE(foundAlreadyExistingTouch, "Touch ended, but not in list");

    if (event.mTime - mTapStartTime <= MAX_TAP_TIME) {
      if (mState == GESTURE_WAITING_DOUBLE_TAP &&
          event.mTime - mLastTapEndTime > MAX_TAP_TIME) {
        // mDoubleTapTimeoutTask wasn't scheduled in time. We need to run the
        // task synchronously to confirm the last tap.
        mDoubleTapTimeoutTask->Cancel();
        TimeoutDoubleTap();

        // Change the state so we can proceed to process the current tap.
        mState = GESTURE_WAITING_SINGLE_TAP;
      }

      if (mState == GESTURE_WAITING_DOUBLE_TAP) {
        mDoubleTapTimeoutTask->Cancel();

        // We were waiting for a double tap and it has arrived.
        HandleDoubleTap(event);
        mState = GESTURE_NONE;
      } else if (mState == GESTURE_WAITING_SINGLE_TAP) {
        mLongTapTimeoutTask->Cancel();

        HandleSingleTapUpEvent(event);

        // We were not waiting for anything but a single tap has happened that
        // may turn into a double tap. Wait a while and if it doesn't turn into
        // a double tap, send a single tap instead.
        mState = GESTURE_WAITING_DOUBLE_TAP;

        mDoubleTapTimeoutTask =
          NewRunnableMethod(this, &GestureEventListener::TimeoutDoubleTap);

        MessageLoop::current()->PostDelayedTask(
          FROM_HERE,
          mDoubleTapTimeoutTask,
          MAX_TAP_TIME);
      }

      mLastTapEndTime = event.mTime;
    }

    if (mState == GESTURE_WAITING_SINGLE_TAP) {
      mState = GESTURE_NONE;
    }

    if (!mTouches.Length()) {
      mSpanChange = 0.0f;
    }

    break;
  }
  case MultiTouchInput::MULTITOUCH_CANCEL:
    // This gets called if there's a touch that has to bail for weird reasons
    // like pinching and then moving away from the window that the pinch was
    // started in without letting go of the screen.
    HandlePinchGestureEvent(event, true);
    break;
  }

  return HandlePinchGestureEvent(event, false);
}
float AsyncPanZoomController::PanDistance() {
  return NS_hypot(mX.PanDistance(), mY.PanDistance()) * mFrameMetrics.mResolution.width;
}
nsEventStatus GestureEventListener::HandlePinchGestureEvent(const MultiTouchInput& aEvent)
{
  nsEventStatus rv = nsEventStatus_eIgnore;

  if (aEvent.mType == MultiTouchInput::MULTITOUCH_CANCEL) {
    mTouches.Clear();
    mState = GESTURE_NONE;
    return rv;
  }

  if (mTouches.Length() > 1) {
    const ScreenIntPoint& firstTouch = mTouches[0].mScreenPoint,
                         secondTouch = mTouches[1].mScreenPoint;
    ScreenPoint focusPoint = ScreenPoint(firstTouch + secondTouch) / 2;
    ScreenIntPoint delta = secondTouch - firstTouch;
    float currentSpan = float(NS_hypot(delta.x, delta.y));

    switch (mState) {
    case GESTURE_NONE:
      mPreviousSpan = currentSpan;
      mState = GESTURE_WAITING_PINCH;
      // Deliberately fall through. If the user pinched and took their fingers
      // off the screen such that they still had 1 left on it, we want there to
      // be no resistance. We should only reset |mSpanChange| once all fingers
      // are off the screen.
    case GESTURE_WAITING_PINCH: {
      mSpanChange += fabsf(currentSpan - mPreviousSpan);
      if (mSpanChange > PINCH_START_THRESHOLD) {
        PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START,
                                     aEvent.mTime,
                                     focusPoint,
                                     currentSpan,
                                     currentSpan,
                                     aEvent.modifiers);

        mAsyncPanZoomController->HandleInputEvent(pinchEvent);

        mState = GESTURE_PINCH;
      }

      break;
    }
    case GESTURE_PINCH: {
      PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE,
                                   aEvent.mTime,
                                   focusPoint,
                                   currentSpan,
                                   mPreviousSpan,
                                   aEvent.modifiers);

      mAsyncPanZoomController->HandleInputEvent(pinchEvent);
      break;
    }
    default:
      // What?
      break;
    }

    mPreviousSpan = currentSpan;

    rv = nsEventStatus_eConsumeNoDefault;
  } else if (mState == GESTURE_PINCH) {
    PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_END,
                                 aEvent.mTime,
                                 ScreenPoint(),
                                 1.0f,
                                 1.0f,
                                 aEvent.modifiers);
    mAsyncPanZoomController->HandleInputEvent(pinchEvent);

    mState = GESTURE_NONE;

    // If the user left a finger on the screen, spoof a touch start event and
    // send it to APZC so that they can continue panning from that point.
    if (mTouches.Length() == 1) {
      MultiTouchInput touchEvent(MultiTouchInput::MULTITOUCH_START,
                                 aEvent.mTime,
                                 aEvent.modifiers);
      touchEvent.mTouches.AppendElement(mTouches[0]);
      mAsyncPanZoomController->HandleInputEvent(touchEvent);

      // The spoofed touch start will get back to GEL and make us enter the
      // GESTURE_WAITING_SINGLE_TAP state, but this isn't a new touch, so there
      // is no condition under which this touch should turn into any tap.
      mState = GESTURE_NONE;
    }

    rv = nsEventStatus_eConsumeNoDefault;
  } else if (mState == GESTURE_WAITING_PINCH) {
    mState = GESTURE_NONE;
  }

  return rv;
}
nsEventStatus GestureEventListener::HandlePinchGestureEvent(const MultiTouchInput& aEvent, bool aClearTouches)
{
  nsEventStatus rv = nsEventStatus_eIgnore;

  if (mTouches.Length() > 1 && !aClearTouches) {
    const nsIntPoint& firstTouch = mTouches[0].mScreenPoint,
                      secondTouch = mTouches[mTouches.Length() - 1].mScreenPoint;
    nsIntPoint focusPoint =
      nsIntPoint((firstTouch.x + secondTouch.x)/2,
                 (firstTouch.y + secondTouch.y)/2);
    float currentSpan =
      float(NS_hypot(firstTouch.x - secondTouch.x,
                     firstTouch.y - secondTouch.y));

    switch (mState) {
    case GESTURE_NONE:
      mPreviousSpan = currentSpan;
      mState = GESTURE_WAITING_PINCH;
      // Deliberately fall through. If the user pinched and took their fingers
      // off the screen such that they still had 1 left on it, we want there to
      // be no resistance. We should only reset |mSpanChange| once all fingers
      // are off the screen.
    case GESTURE_WAITING_PINCH: {
      mSpanChange += fabsf(currentSpan - mPreviousSpan);
      if (mSpanChange > PINCH_START_THRESHOLD) {
        PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START,
                                     aEvent.mTime,
                                     focusPoint,
                                     currentSpan,
                                     currentSpan);

        mAsyncPanZoomController->ReceiveInputEvent(pinchEvent);

        mState = GESTURE_PINCH;
      }

      break;
    }
    case GESTURE_PINCH: {
      PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE,
                                   aEvent.mTime,
                                   focusPoint,
                                   currentSpan,
                                   mPreviousSpan);

      mAsyncPanZoomController->ReceiveInputEvent(pinchEvent);
      break;
    }
    default:
      // What?
      break;
    }

    mPreviousSpan = currentSpan;

    rv = nsEventStatus_eConsumeNoDefault;
  } else if (mState == GESTURE_PINCH) {
    PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_END,
                                 aEvent.mTime,
                                 mTouches[0].mScreenPoint,
                                 1.0f,
                                 1.0f);

    mAsyncPanZoomController->ReceiveInputEvent(pinchEvent);

    mState = GESTURE_NONE;

    rv = nsEventStatus_eConsumeNoDefault;
  }

  if (aClearTouches) {
    mTouches.Clear();
  }

  return rv;
}
nsEventStatus GestureEventListener::HandleInputEvent(const MultiTouchInput& aEvent)
{
  // Cache the current event since it may become the single or long tap that we
  // send.
  mLastTouchInput = aEvent;

  switch (aEvent.mType)
  {
  case MultiTouchInput::MULTITOUCH_START:
  case MultiTouchInput::MULTITOUCH_ENTER: {
    for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
      bool foundAlreadyExistingTouch = false;
      for (size_t j = 0; j < mTouches.Length(); j++) {
        if (mTouches[j].mIdentifier == aEvent.mTouches[i].mIdentifier) {
          foundAlreadyExistingTouch = true;
          break;
        }
      }

      // If we didn't find a touch in our list that matches this, then add it.
      if (!foundAlreadyExistingTouch) {
        mTouches.AppendElement(aEvent.mTouches[i]);
      }
    }

    size_t length = mTouches.Length();
    if (length == 1) {
      mTapStartTime = aEvent.mTime;
      mTouchStartPosition = aEvent.mTouches[0].mScreenPoint;
      if (mState == GESTURE_NONE) {
        mState = GESTURE_WAITING_SINGLE_TAP;

        mLongTapTimeoutTask =
          NewRunnableMethod(this, &GestureEventListener::TimeoutLongTap);

        mAsyncPanZoomController->PostDelayedTask(
          mLongTapTimeoutTask,
          Preferences::GetInt("ui.click_hold_context_menus.delay", 500));
      }
    } else if (length == 2) {
      // Another finger has been added; it can't be a tap anymore.
      HandleTapCancel(aEvent);
    }

    break;
  }
  case MultiTouchInput::MULTITOUCH_MOVE: {
    // If we move too much, bail out of the tap.
    ScreenIntPoint delta = aEvent.mTouches[0].mScreenPoint - mTouchStartPosition;
    if (mTouches.Length() == 1 &&
        NS_hypot(delta.x, delta.y) > AsyncPanZoomController::GetTouchStartTolerance())
    {
      HandleTapCancel(aEvent);
    }

    size_t eventTouchesMatched = 0;
    for (size_t i = 0; i < mTouches.Length(); i++) {
      bool isTouchRemoved = true;
      for (size_t j = 0; j < aEvent.mTouches.Length(); j++) {
        if (mTouches[i].mIdentifier == aEvent.mTouches[j].mIdentifier) {
          eventTouchesMatched++;
          isTouchRemoved = false;
          mTouches[i] = aEvent.mTouches[j];
        }
      }
      if (isTouchRemoved) {
        // this touch point was lifted, so remove it from our list
        mTouches.RemoveElementAt(i);
        i--;
      }
    }

    NS_WARN_IF_FALSE(eventTouchesMatched == aEvent.mTouches.Length(), "Touch moved, but not in list");

    break;
  }
  case MultiTouchInput::MULTITOUCH_END:
  case MultiTouchInput::MULTITOUCH_LEAVE: {
    for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
      bool foundAlreadyExistingTouch = false;
      for (size_t j = 0; j < mTouches.Length() && !foundAlreadyExistingTouch; j++) {
        if (aEvent.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
          foundAlreadyExistingTouch = true;
          mTouches.RemoveElementAt(j);
        }
      }
      NS_WARN_IF_FALSE(foundAlreadyExistingTouch, "Touch ended, but not in list");
    }

    if (mState == GESTURE_WAITING_DOUBLE_TAP) {
      CancelDoubleTapTimeoutTask();
      if (mTapStartTime - mLastTapEndTime > MAX_TAP_TIME ||
          aEvent.mTime - mTapStartTime > MAX_TAP_TIME) {
        // Either the time between taps or the last tap took too long
        // confirm previous tap and handle current tap seperately
        TimeoutDoubleTap();
        mState = GESTURE_WAITING_SINGLE_TAP;
      } else {
        // We were waiting for a double tap and it has arrived.
        HandleDoubleTap(aEvent);
        mState = GESTURE_NONE;
      }
    }

    if (mState == GESTURE_LONG_TAP_UP) {
      HandleLongTapUpEvent(aEvent);
      mState = GESTURE_NONE;
    } else if (mState == GESTURE_WAITING_SINGLE_TAP &&
        aEvent.mTime - mTapStartTime > MAX_TAP_TIME) {
      // Extended taps are immediately dispatched as single taps
      CancelLongTapTimeoutTask();
      HandleSingleTapConfirmedEvent(aEvent);
      mState = GESTURE_NONE;
    } else if (mState == GESTURE_WAITING_SINGLE_TAP) {
      CancelLongTapTimeoutTask();
      nsEventStatus tapupEvent = HandleSingleTapUpEvent(aEvent);

      if (tapupEvent == nsEventStatus_eIgnore) {
        // We were not waiting for anything but a single tap has happened that
        // may turn into a double tap. Wait a while and if it doesn't turn into
        // a double tap, send a single tap instead.
        mState = GESTURE_WAITING_DOUBLE_TAP;

        mDoubleTapTimeoutTask =
          NewRunnableMethod(this, &GestureEventListener::TimeoutDoubleTap);

        mAsyncPanZoomController->PostDelayedTask(
          mDoubleTapTimeoutTask,
          MAX_TAP_TIME);

      } else if (tapupEvent == nsEventStatus_eConsumeNoDefault) {
        // We sent the tapup into content without waiting for a double tap
        mState = GESTURE_NONE;
      }
    }

    mLastTapEndTime = aEvent.mTime;

    if (!mTouches.Length()) {
      mSpanChange = 0.0f;
    }

    break;
  }
  case MultiTouchInput::MULTITOUCH_CANCEL:
    // FIXME: we should probably clear a bunch of gesture state here
    break;
  }

  return HandlePinchGestureEvent(aEvent);
}
bool GestureEventListener::MoveDistanceIsLarge()
{
  ScreenIntPoint delta = mLastTouchInput.mTouches[0].mScreenPoint - mTouchStartPosition;
  return (NS_hypot(delta.x, delta.y) > AsyncPanZoomController::GetTouchStartTolerance());
}