summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/src/GestureEventListener.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/apz/src/GestureEventListener.cpp')
-rw-r--r--gfx/layers/apz/src/GestureEventListener.cpp663
1 files changed, 663 insertions, 0 deletions
diff --git a/gfx/layers/apz/src/GestureEventListener.cpp b/gfx/layers/apz/src/GestureEventListener.cpp
new file mode 100644
index 0000000000..b54674b593
--- /dev/null
+++ b/gfx/layers/apz/src/GestureEventListener.cpp
@@ -0,0 +1,663 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "GestureEventListener.h"
+#include <algorithm> // for max
+#include <math.h> // for fabsf
+#include <stddef.h> // for size_t
+#include "AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "InputBlockState.h" // for TouchBlockState
+#include "base/task.h" // for CancelableTask, etc
+#include "InputBlockState.h" // for TouchBlockState
+#include "mozilla/StaticPrefs_apz.h"
+#include "mozilla/StaticPrefs_ui.h"
+#include "nsDebug.h" // for NS_WARNING
+#include "nsMathUtils.h" // for NS_hypot
+
+static mozilla::LazyLogModule sApzGelLog("apz.gesture");
+#define GEL_LOG(...) MOZ_LOG(sApzGelLog, LogLevel::Debug, (__VA_ARGS__))
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Amount of span or focus change needed to take us from the
+ * GESTURE_WAITING_PINCH state to the GESTURE_PINCH state. This is measured as
+ * either a change in distance between the fingers used to compute the span
+ * ratio, or the a change in position of the focus point between the two
+ * fingers.
+ */
+static const float PINCH_START_THRESHOLD = 35.0f;
+
+/**
+ * Determines how fast a one touch pinch zooms in and out. The greater the
+ * value, the faster it zooms.
+ */
+static const float ONE_TOUCH_PINCH_SPEED = 0.005f;
+
+static bool sLongTapEnabled = true;
+
+static ScreenPoint GetCurrentFocus(const MultiTouchInput& aEvent) {
+ const ScreenPoint& firstTouch = aEvent.mTouches[0].mScreenPoint;
+ const ScreenPoint& secondTouch = aEvent.mTouches[1].mScreenPoint;
+ return (firstTouch + secondTouch) / 2;
+}
+
+static ScreenCoord GetCurrentSpan(const MultiTouchInput& aEvent) {
+ const ScreenPoint& firstTouch = aEvent.mTouches[0].mScreenPoint;
+ const ScreenPoint& secondTouch = aEvent.mTouches[1].mScreenPoint;
+ ScreenPoint delta = secondTouch - firstTouch;
+ return delta.Length();
+}
+
+ScreenCoord GestureEventListener::GetYSpanFromGestureStartPoint() {
+ // use the position that began the one-touch-pinch gesture rather
+ // mTouchStartPosition
+ const ScreenPoint start = mOneTouchPinchStartPosition;
+ const ScreenPoint& current = mTouches[0].mScreenPoint;
+ return current.y - start.y;
+}
+
+static TapGestureInput CreateTapEvent(const MultiTouchInput& aTouch,
+ TapGestureInput::TapGestureType aType) {
+ return TapGestureInput(aType, aTouch.mTimeStamp,
+ aTouch.mTouches[0].mScreenPoint, aTouch.modifiers);
+}
+
+GestureEventListener::GestureEventListener(
+ AsyncPanZoomController* aAsyncPanZoomController)
+ : mAsyncPanZoomController(aAsyncPanZoomController),
+ mState(GESTURE_NONE),
+ mSpanChange(0.0f),
+ mPreviousSpan(0.0f),
+ mFocusChange(0.0f),
+ mLastTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0),
+ mLastTapInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0),
+ mLongTapTimeoutTask(nullptr),
+ mMaxTapTimeoutTask(nullptr) {}
+
+GestureEventListener::~GestureEventListener() = default;
+
+nsEventStatus GestureEventListener::HandleInputEvent(
+ const MultiTouchInput& aEvent) {
+ GEL_LOG("Receiving event type %d with %zu touches in state %d\n",
+ aEvent.mType, aEvent.mTouches.Length(), mState);
+
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ // 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:
+ mTouches.Clear();
+ // Cache every touch.
+ for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
+ mTouches.AppendElement(aEvent.mTouches[i]);
+ }
+
+ if (aEvent.mTouches.Length() == 1) {
+ rv = HandleInputTouchSingleStart();
+ } else {
+ rv = HandleInputTouchMultiStart();
+ }
+ break;
+ case MultiTouchInput::MULTITOUCH_MOVE:
+ // Update the screen points of the cached touches.
+ for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
+ for (size_t j = 0; j < mTouches.Length(); j++) {
+ if (aEvent.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
+ mTouches[j].mScreenPoint = aEvent.mTouches[i].mScreenPoint;
+ mTouches[j].mLocalScreenPoint =
+ aEvent.mTouches[i].mLocalScreenPoint;
+ }
+ }
+ }
+ rv = HandleInputTouchMove();
+ break;
+ case MultiTouchInput::MULTITOUCH_END:
+ // Remove the cache of the touch that ended.
+ for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
+ for (size_t j = 0; j < mTouches.Length(); j++) {
+ if (aEvent.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
+ mTouches.RemoveElementAt(j);
+ break;
+ }
+ }
+ }
+
+ rv = HandleInputTouchEnd();
+ break;
+ case MultiTouchInput::MULTITOUCH_CANCEL:
+ mTouches.Clear();
+ rv = HandleInputTouchCancel();
+ break;
+ }
+
+ return rv;
+}
+
+int32_t GestureEventListener::GetLastTouchIdentifier() const {
+ if (mTouches.Length() != 1) {
+ NS_WARNING(
+ "GetLastTouchIdentifier() called when last touch event "
+ "did not have one touch");
+ }
+ return mTouches.IsEmpty() ? -1 : mTouches[0].mIdentifier;
+}
+
+/* static */
+void GestureEventListener::SetLongTapEnabled(bool aLongTapEnabled) {
+ sLongTapEnabled = aLongTapEnabled;
+}
+
+/* static */
+bool GestureEventListener::IsLongTapEnabled() { return sLongTapEnabled; }
+
+void GestureEventListener::EnterFirstSingleTouchDown() {
+ SetState(GESTURE_FIRST_SINGLE_TOUCH_DOWN);
+ mTouchStartPosition = mLastTouchInput.mTouches[0].mScreenPoint;
+ mTouchStartOffset = mLastTouchInput.mScreenOffset;
+
+ if (sLongTapEnabled) {
+ CreateLongTapTimeoutTask();
+ }
+ CreateMaxTapTimeoutTask();
+}
+
+nsEventStatus GestureEventListener::HandleInputTouchSingleStart() {
+ switch (mState) {
+ case GESTURE_NONE:
+ EnterFirstSingleTouchDown();
+ break;
+ case GESTURE_FIRST_SINGLE_TOUCH_UP:
+ if (SecondTapIsFar()) {
+ // If the second tap goes down far away from the first, then bail out
+ // of any gesture that includes the first tap.
+ CancelLongTapTimeoutTask();
+ CancelMaxTapTimeoutTask();
+ mSingleTapSent = Nothing();
+
+ // But still allow the second tap to participate in a gesture
+ // (e.g. lead to a single tap, or a double tap if an additional
+ // tap occurs near the same location).
+ EnterFirstSingleTouchDown();
+ } else {
+ // Otherwise, reset the touch start position so that, if this turns into
+ // a one-touch-pinch gesture, it uses the second tap's down position as
+ // the focus, rather than the first tap's.
+ mTouchStartPosition = mLastTouchInput.mTouches[0].mScreenPoint;
+ mTouchStartOffset = mLastTouchInput.mScreenOffset;
+ SetState(GESTURE_SECOND_SINGLE_TOUCH_DOWN);
+ }
+ break;
+ default:
+ NS_WARNING("Unhandled state upon single touch start");
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ return nsEventStatus_eIgnore;
+}
+
+nsEventStatus GestureEventListener::HandleInputTouchMultiStart() {
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ switch (mState) {
+ case GESTURE_NONE:
+ SetState(GESTURE_MULTI_TOUCH_DOWN);
+ break;
+ case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
+ CancelLongTapTimeoutTask();
+ CancelMaxTapTimeoutTask();
+ SetState(GESTURE_MULTI_TOUCH_DOWN);
+ // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
+ rv = nsEventStatus_eConsumeNoDefault;
+ break;
+ case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
+ CancelLongTapTimeoutTask();
+ SetState(GESTURE_MULTI_TOUCH_DOWN);
+ // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
+ rv = nsEventStatus_eConsumeNoDefault;
+ break;
+ case GESTURE_FIRST_SINGLE_TOUCH_UP:
+ case GESTURE_SECOND_SINGLE_TOUCH_DOWN:
+ // Cancel wait for double tap
+ CancelMaxTapTimeoutTask();
+ MOZ_ASSERT(mSingleTapSent.isSome());
+ if (!mSingleTapSent.value()) {
+ TriggerSingleTapConfirmedEvent();
+ }
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_MULTI_TOUCH_DOWN);
+ // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
+ rv = nsEventStatus_eConsumeNoDefault;
+ break;
+ case GESTURE_LONG_TOUCH_DOWN:
+ SetState(GESTURE_MULTI_TOUCH_DOWN);
+ break;
+ case GESTURE_MULTI_TOUCH_DOWN:
+ case GESTURE_PINCH:
+ // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
+ rv = nsEventStatus_eConsumeNoDefault;
+ break;
+ default:
+ NS_WARNING("Unhandled state upon multitouch start");
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ return rv;
+}
+
+bool GestureEventListener::MoveDistanceExceeds(ScreenCoord aThreshold) const {
+ ExternalPoint start = AsyncPanZoomController::ToExternalPoint(
+ mTouchStartOffset, mTouchStartPosition);
+ ExternalPoint end = AsyncPanZoomController::ToExternalPoint(
+ mLastTouchInput.mScreenOffset, mLastTouchInput.mTouches[0].mScreenPoint);
+ return (start - end).Length() > aThreshold;
+}
+
+bool GestureEventListener::MoveDistanceIsLarge() const {
+ return MoveDistanceExceeds(mAsyncPanZoomController->GetTouchStartTolerance());
+}
+
+bool GestureEventListener::SecondTapIsFar() const {
+ // Allow a little more room here, because the is actually lifting their finger
+ // off the screen before replacing it, and that tends to have more error than
+ // wiggling the finger while on the screen.
+ return MoveDistanceExceeds(mAsyncPanZoomController->GetSecondTapTolerance());
+}
+
+nsEventStatus GestureEventListener::HandleInputTouchMove() {
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ switch (mState) {
+ case GESTURE_NONE:
+ // Ignore this input signal as the corresponding events get handled by
+ // APZC
+ break;
+
+ case GESTURE_LONG_TOUCH_DOWN:
+ if (MoveDistanceIsLarge()) {
+ // So that we don't fire a long-tap-up if the user moves around after a
+ // long-tap
+ SetState(GESTURE_NONE);
+ }
+ break;
+
+ case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
+ case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN: {
+ // If we move too much, bail out of the tap.
+ if (MoveDistanceIsLarge()) {
+ CancelLongTapTimeoutTask();
+ CancelMaxTapTimeoutTask();
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_NONE);
+ }
+ break;
+ }
+
+ // The user has performed a double tap, but not lifted her finger.
+ case GESTURE_SECOND_SINGLE_TOUCH_DOWN: {
+ // If touch has moved noticeably (within StaticPrefs::apz_max_tap_time()),
+ // change state.
+ if (MoveDistanceIsLarge()) {
+ CancelLongTapTimeoutTask();
+ CancelMaxTapTimeoutTask();
+ mSingleTapSent = Nothing();
+ if (!StaticPrefs::apz_one_touch_pinch_enabled()) {
+ // If the one-touch-pinch feature is disabled, bail out of the double-
+ // tap gesture instead.
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ SetState(GESTURE_ONE_TOUCH_PINCH);
+
+ ScreenCoord currentSpan = 1.0f;
+ ScreenPoint currentFocus = mTouchStartPosition;
+
+ // save the position that the one-touch-pinch gesture actually begins
+ mOneTouchPinchStartPosition = mLastTouchInput.mTouches[0].mScreenPoint;
+
+ PinchGestureInput pinchEvent(
+ PinchGestureInput::PINCHGESTURE_START, PinchGestureInput::ONE_TOUCH,
+ mLastTouchInput.mTimeStamp, mLastTouchInput.mScreenOffset,
+ currentFocus, currentSpan, currentSpan, mLastTouchInput.modifiers);
+
+ rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
+
+ mPreviousSpan = currentSpan;
+ mPreviousFocus = currentFocus;
+ }
+ 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;
+ }
+
+ ScreenCoord currentSpan = GetCurrentSpan(mLastTouchInput);
+ ScreenPoint currentFocus = GetCurrentFocus(mLastTouchInput);
+
+ mSpanChange += fabsf(currentSpan - mPreviousSpan);
+ mFocusChange += (currentFocus - mPreviousFocus).Length();
+ if (mSpanChange > PINCH_START_THRESHOLD ||
+ mFocusChange > PINCH_START_THRESHOLD) {
+ SetState(GESTURE_PINCH);
+ PinchGestureInput pinchEvent(
+ PinchGestureInput::PINCHGESTURE_START, PinchGestureInput::TOUCH,
+ mLastTouchInput.mTimeStamp, mLastTouchInput.mScreenOffset,
+ currentFocus, currentSpan, currentSpan, mLastTouchInput.modifiers);
+
+ rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
+ } else {
+ // Prevent APZC::OnTouchMove from processing a move event when two
+ // touches are active
+ rv = nsEventStatus_eConsumeNoDefault;
+ }
+
+ mPreviousSpan = currentSpan;
+ mPreviousFocus = currentFocus;
+ 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;
+ }
+
+ ScreenCoord currentSpan = GetCurrentSpan(mLastTouchInput);
+
+ PinchGestureInput pinchEvent(
+ PinchGestureInput::PINCHGESTURE_SCALE, PinchGestureInput::TOUCH,
+ mLastTouchInput.mTimeStamp, mLastTouchInput.mScreenOffset,
+ GetCurrentFocus(mLastTouchInput), currentSpan, mPreviousSpan,
+ mLastTouchInput.modifiers);
+
+ rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
+ mPreviousSpan = currentSpan;
+
+ break;
+ }
+
+ case GESTURE_ONE_TOUCH_PINCH: {
+ ScreenCoord currentSpan = GetYSpanFromGestureStartPoint();
+ float effectiveSpan =
+ 1.0f + (fabsf(currentSpan.value) * ONE_TOUCH_PINCH_SPEED);
+ ScreenPoint currentFocus = mTouchStartPosition;
+
+ // Invert zoom.
+ if (currentSpan.value < 0) {
+ effectiveSpan = 1.0f / effectiveSpan;
+ }
+
+ PinchGestureInput pinchEvent(
+ PinchGestureInput::PINCHGESTURE_SCALE, PinchGestureInput::ONE_TOUCH,
+ mLastTouchInput.mTimeStamp, mLastTouchInput.mScreenOffset,
+ currentFocus, effectiveSpan, mPreviousSpan,
+ mLastTouchInput.modifiers);
+
+ rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
+ mPreviousSpan = effectiveSpan;
+
+ break;
+ }
+
+ default:
+ NS_WARNING("Unhandled state upon touch move");
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ return rv;
+}
+
+nsEventStatus GestureEventListener::HandleInputTouchEnd() {
+ // We intentionally do not pass apzc return statuses up since
+ // it may cause apzc stay in the touching state even after
+ // gestures are completed (please see Bug 1013378 for reference).
+
+ nsEventStatus rv = nsEventStatus_eIgnore;
+
+ switch (mState) {
+ case GESTURE_NONE:
+ // GEL doesn't have a dedicated state for PANNING handled in APZC thus
+ // ignore.
+ break;
+
+ case GESTURE_FIRST_SINGLE_TOUCH_DOWN: {
+ CancelLongTapTimeoutTask();
+ CancelMaxTapTimeoutTask();
+ nsEventStatus tapupStatus = mAsyncPanZoomController->HandleGestureEvent(
+ CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_UP));
+ mSingleTapSent = Some(tapupStatus != nsEventStatus_eIgnore);
+ SetState(GESTURE_FIRST_SINGLE_TOUCH_UP);
+ CreateMaxTapTimeoutTask();
+ break;
+ }
+
+ case GESTURE_SECOND_SINGLE_TOUCH_DOWN: {
+ CancelMaxTapTimeoutTask();
+ MOZ_ASSERT(mSingleTapSent.isSome());
+ mAsyncPanZoomController->HandleGestureEvent(CreateTapEvent(
+ mLastTouchInput, mSingleTapSent.value()
+ ? TapGestureInput::TAPGESTURE_SECOND
+ : TapGestureInput::TAPGESTURE_DOUBLE));
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
+ CancelLongTapTimeoutTask();
+ SetState(GESTURE_NONE);
+ TriggerSingleTapConfirmedEvent();
+ break;
+
+ case GESTURE_LONG_TOUCH_DOWN: {
+ SetState(GESTURE_NONE);
+ mAsyncPanZoomController->HandleGestureEvent(
+ CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_LONG_UP));
+ break;
+ }
+
+ case GESTURE_MULTI_TOUCH_DOWN:
+ if (mTouches.Length() < 2) {
+ SetState(GESTURE_NONE);
+ }
+ break;
+
+ case GESTURE_PINCH:
+ if (mTouches.Length() < 2) {
+ SetState(GESTURE_NONE);
+ PinchGestureInput::PinchGestureType type =
+ PinchGestureInput::PINCHGESTURE_END;
+ ScreenPoint point;
+ if (mTouches.Length() == 1) {
+ // As user still keeps one finger down the event's focus point should
+ // contain meaningful data.
+ type = PinchGestureInput::PINCHGESTURE_FINGERLIFTED;
+ point = mTouches[0].mScreenPoint;
+ }
+ PinchGestureInput pinchEvent(type, PinchGestureInput::TOUCH,
+ mLastTouchInput.mTimeStamp,
+ mLastTouchInput.mScreenOffset, point, 1.0f,
+ 1.0f, mLastTouchInput.modifiers);
+ mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
+ }
+
+ rv = nsEventStatus_eConsumeNoDefault;
+
+ break;
+
+ case GESTURE_ONE_TOUCH_PINCH: {
+ SetState(GESTURE_NONE);
+ PinchGestureInput pinchEvent(
+ PinchGestureInput::PINCHGESTURE_END, PinchGestureInput::ONE_TOUCH,
+ mLastTouchInput.mTimeStamp, mLastTouchInput.mScreenOffset,
+ ScreenPoint(), 1.0f, 1.0f, mLastTouchInput.modifiers);
+ mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
+
+ rv = nsEventStatus_eConsumeNoDefault;
+
+ break;
+ }
+
+ default:
+ NS_WARNING("Unhandled state upon touch end");
+ SetState(GESTURE_NONE);
+ break;
+ }
+
+ return rv;
+}
+
+nsEventStatus GestureEventListener::HandleInputTouchCancel() {
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_NONE);
+ CancelMaxTapTimeoutTask();
+ CancelLongTapTimeoutTask();
+ return nsEventStatus_eIgnore;
+}
+
+void GestureEventListener::HandleInputTimeoutLongTap() {
+ GEL_LOG("Running long-tap timeout task in state %d\n", mState);
+
+ mLongTapTimeoutTask = nullptr;
+
+ switch (mState) {
+ case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
+ // just in case MaxTapTime > ContextMenuDelay cancel MaxTap timer
+ // and fall through
+ CancelMaxTapTimeoutTask();
+ [[fallthrough]];
+ case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN: {
+ SetState(GESTURE_LONG_TOUCH_DOWN);
+ mAsyncPanZoomController->HandleGestureEvent(
+ CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_LONG));
+ break;
+ }
+ default:
+ NS_WARNING("Unhandled state upon long tap timeout");
+ SetState(GESTURE_NONE);
+ break;
+ }
+}
+
+void GestureEventListener::HandleInputTimeoutMaxTap(bool aDuringFastFling) {
+ GEL_LOG("Running max-tap timeout task in state %d\n", mState);
+
+ mMaxTapTimeoutTask = nullptr;
+
+ if (mState == GESTURE_FIRST_SINGLE_TOUCH_DOWN) {
+ SetState(GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN);
+ } else if (mState == GESTURE_FIRST_SINGLE_TOUCH_UP ||
+ mState == GESTURE_SECOND_SINGLE_TOUCH_DOWN) {
+ MOZ_ASSERT(mSingleTapSent.isSome());
+ if (!aDuringFastFling && !mSingleTapSent.value()) {
+ TriggerSingleTapConfirmedEvent();
+ }
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_NONE);
+ } else {
+ NS_WARNING("Unhandled state upon MAX_TAP timeout");
+ SetState(GESTURE_NONE);
+ }
+}
+
+void GestureEventListener::TriggerSingleTapConfirmedEvent() {
+ mAsyncPanZoomController->HandleGestureEvent(
+ CreateTapEvent(mLastTapInput, TapGestureInput::TAPGESTURE_CONFIRMED));
+}
+
+void GestureEventListener::SetState(GestureState aState) {
+ mState = aState;
+
+ if (mState == GESTURE_NONE) {
+ mSpanChange = 0.0f;
+ mPreviousSpan = 0.0f;
+ mFocusChange = 0.0f;
+ } else if (mState == GESTURE_MULTI_TOUCH_DOWN) {
+ mPreviousSpan = GetCurrentSpan(mLastTouchInput);
+ mPreviousFocus = GetCurrentFocus(mLastTouchInput);
+ }
+}
+
+void GestureEventListener::CancelLongTapTimeoutTask() {
+ if (mState == GESTURE_SECOND_SINGLE_TOUCH_DOWN) {
+ // being in this state means the task has been canceled already
+ return;
+ }
+
+ if (mLongTapTimeoutTask) {
+ mLongTapTimeoutTask->Cancel();
+ mLongTapTimeoutTask = nullptr;
+ }
+}
+
+void GestureEventListener::CreateLongTapTimeoutTask() {
+ RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod(
+ "layers::GestureEventListener::HandleInputTimeoutLongTap", this,
+ &GestureEventListener::HandleInputTimeoutLongTap);
+
+ mLongTapTimeoutTask = task;
+
+ TouchBlockState* block =
+ mAsyncPanZoomController->GetInputQueue()->GetCurrentTouchBlock();
+ MOZ_ASSERT(block);
+ long alreadyElapsed =
+ static_cast<long>(block->GetTimeSinceBlockStart().ToMilliseconds());
+ long remainingDelay =
+ StaticPrefs::ui_click_hold_context_menus_delay() - alreadyElapsed;
+ mAsyncPanZoomController->PostDelayedTask(task.forget(),
+ std::max(0L, remainingDelay));
+}
+
+void GestureEventListener::CancelMaxTapTimeoutTask() {
+ if (mState == GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN) {
+ // being in this state means the timer has just been triggered
+ return;
+ }
+
+ if (mMaxTapTimeoutTask) {
+ mMaxTapTimeoutTask->Cancel();
+ mMaxTapTimeoutTask = nullptr;
+ }
+}
+
+void GestureEventListener::CreateMaxTapTimeoutTask() {
+ mLastTapInput = mLastTouchInput;
+
+ TouchBlockState* block =
+ mAsyncPanZoomController->GetInputQueue()->GetCurrentTouchBlock();
+ MOZ_ASSERT(block);
+ RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod<bool>(
+ "layers::GestureEventListener::HandleInputTimeoutMaxTap", this,
+ &GestureEventListener::HandleInputTimeoutMaxTap,
+ block->IsDuringFastFling());
+
+ mMaxTapTimeoutTask = task;
+
+ long alreadyElapsed =
+ static_cast<long>(block->GetTimeSinceBlockStart().ToMilliseconds());
+ long remainingDelay = StaticPrefs::apz_max_tap_time() - alreadyElapsed;
+ mAsyncPanZoomController->PostDelayedTask(task.forget(),
+ std::max(0L, remainingDelay));
+}
+
+} // namespace layers
+} // namespace mozilla