summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/util
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:50 +0000
commitdef92d1b8e9d373e2f6f27c366d578d97d8960c6 (patch)
tree2ef34b9ad8bb9a9220e05d60352558b15f513894 /gfx/layers/apz/util
parentAdding debian version 125.0.3-1. (diff)
downloadfirefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.tar.xz
firefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.zip
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/layers/apz/util')
-rw-r--r--gfx/layers/apz/util/APZEventState.cpp23
-rw-r--r--gfx/layers/apz/util/APZEventState.h6
-rw-r--r--gfx/layers/apz/util/ActiveElementManager.cpp119
-rw-r--r--gfx/layers/apz/util/ActiveElementManager.h47
4 files changed, 147 insertions, 48 deletions
diff --git a/gfx/layers/apz/util/APZEventState.cpp b/gfx/layers/apz/util/APZEventState.cpp
index 5db6a08429..c205b09ca2 100644
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -27,6 +27,7 @@
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/layers/APZCCallbackHelper.h"
+#include "mozilla/layers/APZUtils.h"
#include "mozilla/layers/IAPZCTreeManager.h"
#include "mozilla/widget/nsAutoRollup.h"
#include "nsCOMPtr.h"
@@ -101,7 +102,7 @@ APZEventState::APZEventState(nsIWidget* aWidget,
mContentReceivedInputBlockCallback(std::move(aCallback)),
mPendingTouchPreventedResponse(false),
mPendingTouchPreventedBlockId(0),
- mEndTouchIsClick(false),
+ mEndTouchState(apz::SingleTapState::NotClick),
mFirstTouchCancelled(false),
mTouchEndCancelled(false),
mReceivedNonTouchStart(false),
@@ -349,11 +350,13 @@ void APZEventState::ProcessTouchEvent(
case eTouchEnd:
if (isTouchPrevented) {
mTouchEndCancelled = true;
- mEndTouchIsClick = false;
+ mEndTouchState = apz::SingleTapState::NotClick;
}
[[fallthrough]];
case eTouchCancel:
- mActiveElementManager->HandleTouchEndEvent(mEndTouchIsClick);
+ if (mActiveElementManager->HandleTouchEndEvent(mEndTouchState)) {
+ mEndTouchState = apz::SingleTapState::NotClick;
+ }
[[fallthrough]];
case eTouchMove: {
if (!mReceivedNonTouchStart) {
@@ -515,13 +518,13 @@ void APZEventState::ProcessAPZStateChange(ViewID aViewId,
break;
}
case APZStateChange::eStartTouch: {
- bool canBePan = aArg;
- mActiveElementManager->HandleTouchStart(canBePan);
+ bool canBePanOrZoom = aArg;
+ mActiveElementManager->HandleTouchStart(canBePanOrZoom);
// If this is a non-scrollable content, set a timer for the amount of
// time specified by ui.touch_activation.duration_ms to clear the
// active element state.
- APZES_LOG("%s: can-be-pan=%d", __FUNCTION__, aArg);
- if (!canBePan) {
+ APZES_LOG("%s: can-be-pan-or-zoom=%d", __FUNCTION__, aArg);
+ if (!canBePanOrZoom) {
MOZ_ASSERT(aInputBlockId.isSome());
}
break;
@@ -532,8 +535,10 @@ void APZEventState::ProcessAPZStateChange(ViewID aViewId,
break;
}
case APZStateChange::eEndTouch: {
- mEndTouchIsClick = aArg;
- mActiveElementManager->HandleTouchEnd();
+ mEndTouchState = static_cast<apz::SingleTapState>(aArg);
+ if (mActiveElementManager->HandleTouchEnd(mEndTouchState)) {
+ mEndTouchState = apz::SingleTapState::NotClick;
+ }
break;
}
}
diff --git a/gfx/layers/apz/util/APZEventState.h b/gfx/layers/apz/util/APZEventState.h
index 52febc0424..3a57e9e6c6 100644
--- a/gfx/layers/apz/util/APZEventState.h
+++ b/gfx/layers/apz/util/APZEventState.h
@@ -39,6 +39,10 @@ namespace layers {
class ActiveElementManager;
+namespace apz {
+enum class SingleTapState : uint8_t;
+} // namespace apz
+
typedef std::function<void(uint64_t /* input block id */,
bool /* prevent default */)>
ContentReceivedInputBlockCallback;
@@ -106,7 +110,7 @@ class APZEventState final {
bool mPendingTouchPreventedResponse;
ScrollableLayerGuid mPendingTouchPreventedGuid;
uint64_t mPendingTouchPreventedBlockId;
- bool mEndTouchIsClick;
+ apz::SingleTapState mEndTouchState;
bool mFirstTouchCancelled;
bool mTouchEndCancelled;
// Set to true when we have received any one of
diff --git a/gfx/layers/apz/util/ActiveElementManager.cpp b/gfx/layers/apz/util/ActiveElementManager.cpp
index f2d981e9f0..c92fec783a 100644
--- a/gfx/layers/apz/util/ActiveElementManager.cpp
+++ b/gfx/layers/apz/util/ActiveElementManager.cpp
@@ -10,6 +10,8 @@
#include "mozilla/StaticPrefs_ui.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Document.h"
+#include "mozilla/layers/APZEventState.h"
+#include "mozilla/layers/APZUtils.h"
#include "nsITimer.h"
static mozilla::LazyLogModule sApzAemLog("apz.activeelement");
@@ -21,7 +23,7 @@ namespace layers {
class DelayedClearElementActivation final : public nsITimerCallback,
public nsINamed {
private:
- explicit DelayedClearElementActivation(nsCOMPtr<dom::Element>& aTarget,
+ explicit DelayedClearElementActivation(RefPtr<dom::Element>& aTarget,
const nsCOMPtr<nsITimer>& aTimer)
: mTarget(aTarget)
// Hold the reference count until we are called back.
@@ -33,7 +35,7 @@ class DelayedClearElementActivation final : public nsITimerCallback,
NS_DECL_ISUPPORTS
static RefPtr<DelayedClearElementActivation> Create(
- nsCOMPtr<dom::Element>& aTarget);
+ RefPtr<dom::Element>& aTarget);
NS_IMETHOD Notify(nsITimer*) override;
@@ -56,11 +58,12 @@ class DelayedClearElementActivation final : public nsITimerCallback,
mTimer = nullptr;
}
}
+ dom::Element* GetTarget() const { return mTarget; }
private:
~DelayedClearElementActivation() = default;
- nsCOMPtr<dom::Element> mTarget;
+ RefPtr<dom::Element> mTarget;
nsCOMPtr<nsITimer> mTimer;
bool mProcessedSingleTap;
};
@@ -77,7 +80,7 @@ static nsPresContext* GetPresContextFor(nsIContent* aContent) {
}
RefPtr<DelayedClearElementActivation> DelayedClearElementActivation::Create(
- nsCOMPtr<dom::Element>& aTarget) {
+ RefPtr<dom::Element>& aTarget) {
nsCOMPtr<nsITimer> timer = NS_NewTimer();
if (!timer) {
return nullptr;
@@ -137,7 +140,11 @@ void DelayedClearElementActivation::ClearGlobalActiveContent() {
NS_IMPL_ISUPPORTS(DelayedClearElementActivation, nsITimerCallback, nsINamed)
ActiveElementManager::ActiveElementManager()
- : mCanBePan(false), mCanBePanSet(false), mSetActiveTask(nullptr) {}
+ : mCanBePanOrZoom(false),
+ mCanBePanOrZoomSet(false),
+ mSingleTapBeforeActivation(false),
+ mSingleTapState(apz::SingleTapState::NotClick),
+ mSetActiveTask(nullptr) {}
ActiveElementManager::~ActiveElementManager() = default;
@@ -156,9 +163,9 @@ void ActiveElementManager::SetTargetElement(dom::EventTarget* aTarget) {
TriggerElementActivation();
}
-void ActiveElementManager::HandleTouchStart(bool aCanBePan) {
- AEM_LOG("Touch start, aCanBePan: %d\n", aCanBePan);
- if (mCanBePanSet) {
+void ActiveElementManager::HandleTouchStart(bool aCanBePanOrZoom) {
+ AEM_LOG("Touch start, aCanBePanOrZoom: %d\n", aCanBePanOrZoom);
+ if (mCanBePanOrZoomSet) {
// Multiple fingers on screen (since HandleTouchEnd clears mCanBePanSet).
AEM_LOG("Multiple fingers on-screen, clearing touch block state\n");
CancelTask();
@@ -167,16 +174,29 @@ void ActiveElementManager::HandleTouchStart(bool aCanBePan) {
return;
}
- mCanBePan = aCanBePan;
- mCanBePanSet = true;
+ mCanBePanOrZoom = aCanBePanOrZoom;
+ mCanBePanOrZoomSet = true;
TriggerElementActivation();
}
void ActiveElementManager::TriggerElementActivation() {
+ // Reset mSingleTapState here either when HandleTouchStart() or
+ // SetTargetElement() gets called.
+ // NOTE: It's possible that ProcessSingleTap() gets called in between
+ // HandleTouchStart() and SetTargetElement() calls. I.e.,
+ // mSingleTapBeforeActivation is true, in such cases it doesn't matter that
+ // mSingleTapState was reset once and referred it in ProcessSingleTap() and
+ // then reset here again because in ProcessSingleTap() `NotYetDetermined` is
+ // the only one state we need to care, and it should NOT happen in the
+ // scenario. In other words the case where we need to care `NotYetDetermined`
+ // is when ProcessSingleTap() gets called later than any other events and
+ // notifications.
+ mSingleTapState = apz::SingleTapState::NotClick;
+
// Both HandleTouchStart() and SetTargetElement() call this. They can be
- // called in either order. One will set mCanBePanSet, and the other, mTarget.
- // We want to actually trigger the activation once both are set.
- if (!(mTarget && mCanBePanSet)) {
+ // called in either order. One will set mCanBePanOrZoomSet, and the other,
+ // mTarget. We want to actually trigger the activation once both are set.
+ if (!(mTarget && mCanBePanOrZoomSet)) {
return;
}
@@ -190,10 +210,13 @@ void ActiveElementManager::TriggerElementActivation() {
// If the touch cannot be a pan, make mTarget :active right away.
// Otherwise, wait a bit to see if the user will pan or not.
- if (!mCanBePan) {
+ if (!mCanBePanOrZoom) {
SetActive(mTarget);
if (mDelayedClearElementActivation) {
+ if (mSingleTapBeforeActivation) {
+ mDelayedClearElementActivation->MarkSingleTapProcessed();
+ }
mDelayedClearElementActivation->StartTimer();
}
} else {
@@ -210,6 +233,10 @@ void ActiveElementManager::TriggerElementActivation() {
task.forget(), StaticPrefs::ui_touch_activation_delay_ms());
AEM_LOG("Scheduling mSetActiveTask %p\n", mSetActiveTask.get());
}
+ AEM_LOG(
+ "Got both touch-end event and end touch notiication, clearing pan "
+ "state\n");
+ mCanBePanOrZoomSet = false;
}
void ActiveElementManager::ClearActivation() {
@@ -218,43 +245,70 @@ void ActiveElementManager::ClearActivation() {
ResetActive();
}
-void ActiveElementManager::HandleTouchEndEvent(bool aWasClick) {
- AEM_LOG("Touch end event, aWasClick: %d\n", aWasClick);
+bool ActiveElementManager::HandleTouchEndEvent(apz::SingleTapState aState) {
+ AEM_LOG("Touch end event, state: %hhu\n", static_cast<uint8_t>(aState));
+
+ mTouchEndState += TouchEndState::GotTouchEndEvent;
+ return MaybeChangeActiveState(aState);
+}
+
+bool ActiveElementManager::HandleTouchEnd(apz::SingleTapState aState) {
+ AEM_LOG("Touch end\n");
+
+ mTouchEndState += TouchEndState::GotTouchEndNotification;
+ return MaybeChangeActiveState(aState);
+}
+
+bool ActiveElementManager::MaybeChangeActiveState(apz::SingleTapState aState) {
+ if (mTouchEndState !=
+ TouchEndStates(TouchEndState::GotTouchEndEvent,
+ TouchEndState::GotTouchEndNotification)) {
+ return false;
+ }
- // If the touch was a click, make mTarget :active right away.
- // nsEventStateManager will reset the active element when processing
- // the mouse-down event generated by the click.
CancelTask();
- if (aWasClick) {
+
+ mSingleTapState = aState;
+
+ if (aState == apz::SingleTapState::WasClick) {
// Scrollbar thumbs use a different mechanism for their active
// highlight (the "active" attribute), so don't set the active state
// on them because nothing will clear it.
- if (!(mTarget && mTarget->IsXULElement(nsGkAtoms::thumb))) {
+ if (mCanBePanOrZoom &&
+ !(mTarget && mTarget->IsXULElement(nsGkAtoms::thumb))) {
SetActive(mTarget);
}
} else {
- // We might reach here if mCanBePan was false on touch-start and
+ // We might reach here if mCanBePanOrZoom was false on touch-start and
// so we set the element active right away. Now it turns out the
// action was not a click so we need to reset the active element.
ResetActive();
}
ResetTouchBlockState();
-}
-
-void ActiveElementManager::HandleTouchEnd() {
- AEM_LOG("Touch end, clearing pan state\n");
- mCanBePanSet = false;
+ return true;
}
void ActiveElementManager::ProcessSingleTap() {
if (!mDelayedClearElementActivation) {
+ // We have not received touch-start notification yet. We will have to run
+ // MarkSingleTapProcessed() when we receive the touch-start notification.
+ mSingleTapBeforeActivation = true;
return;
}
+ if (mSingleTapState == apz::SingleTapState::NotYetDetermined) {
+ // If we got `NotYetDetermined`, which means at the moment we don't know for
+ // sure whether double-tapping will be incoming or not, but now we are sure
+ // that no double-tapping will happen, thus it's time to activate the target
+ // element.
+ if (auto* target = mDelayedClearElementActivation->GetTarget()) {
+ SetActive(target);
+ }
+ }
mDelayedClearElementActivation->MarkSingleTapProcessed();
- if (mCanBePan) {
+ if (mCanBePanOrZoom) {
// In the case that we have not started the delayed reset of the element
// activation state, start the timer now.
mDelayedClearElementActivation->StartTimer();
@@ -297,7 +351,14 @@ void ActiveElementManager::ResetActive() {
void ActiveElementManager::ResetTouchBlockState() {
mTarget = nullptr;
- mCanBePanSet = false;
+ mCanBePanOrZoomSet = false;
+ mTouchEndState.clear();
+ mSingleTapBeforeActivation = false;
+ // NOTE: Do not reset mSingleTapState here since it will be necessary in
+ // ProcessSingleTap() to tell whether we need to activate the target element
+ // because on environments where double-tap is enabled ProcessSingleTap()
+ // gets called after both of touch-end event and end touch notiication
+ // arrived.
}
void ActiveElementManager::SetActiveTask(
diff --git a/gfx/layers/apz/util/ActiveElementManager.h b/gfx/layers/apz/util/ActiveElementManager.h
index f8a6f07261..1f2e1e4aad 100644
--- a/gfx/layers/apz/util/ActiveElementManager.h
+++ b/gfx/layers/apz/util/ActiveElementManager.h
@@ -9,6 +9,7 @@
#include "nsCOMPtr.h"
#include "nsISupportsImpl.h"
+#include "mozilla/EnumSet.h"
namespace mozilla {
@@ -23,6 +24,10 @@ namespace layers {
class DelayedClearElementActivation;
+namespace apz {
+enum class SingleTapState : uint8_t;
+} // namespace apz
+
/**
* Manages setting and clearing the ':active' CSS pseudostate in the presence
* of touch input.
@@ -46,9 +51,9 @@ class ActiveElementManager final {
/**
* Handle a touch-start state notification from APZ. This notification
* may be delayed until after touch listeners have responded to the APZ.
- * @param aCanBePan whether the touch can be a pan
+ * @param aCanBePanOrZoom whether the touch can be a pan or double-tap-to-zoom
*/
- void HandleTouchStart(bool aCanBePan);
+ void HandleTouchStart(bool aCanBePanOrZoom);
/**
* Clear the active element.
*/
@@ -57,12 +62,12 @@ class ActiveElementManager final {
* Handle a touch-end or touch-cancel event.
* @param aWasClick whether the touch was a click
*/
- void HandleTouchEndEvent(bool aWasClick);
+ bool HandleTouchEndEvent(apz::SingleTapState aState);
/**
* Handle a touch-end state notification from APZ. This notification may be
* delayed until after touch listeners have responded to the APZ.
*/
- void HandleTouchEnd();
+ bool HandleTouchEnd(apz::SingleTapState aState);
/**
* Possibly clear active element sate in response to a single tap.
*/
@@ -76,17 +81,39 @@ class ActiveElementManager final {
/**
* The target of the first touch point in the current touch block.
*/
- nsCOMPtr<dom::Element> mTarget;
+ RefPtr<dom::Element> mTarget;
/**
- * Whether the current touch block can be a pan. Set in HandleTouchStart().
+ * Whether the current touch block can be a pan or double-tap-to-zoom. Set in
+ * HandleTouchStart().
*/
- bool mCanBePan;
+ bool mCanBePanOrZoom;
/**
- * Whether mCanBePan has been set for the current touch block.
+ * Whether mCanBePanOrZoom has been set for the current touch block.
* We need to keep track of this to allow HandleTouchStart() and
* SetTargetElement() to be called in either order.
*/
- bool mCanBePanSet;
+ bool mCanBePanOrZoomSet;
+
+ bool mSingleTapBeforeActivation;
+
+ enum class TouchEndState : uint8_t {
+ GotTouchEndNotification,
+ GotTouchEndEvent,
+ };
+ using TouchEndStates = EnumSet<TouchEndState>;
+
+ /**
+ * A flag tracks whether `APZStateChange::eEndTouch` notification has arrived
+ * and whether `eTouchEnd` event has arrived.
+ */
+ TouchEndStates mTouchEndState;
+
+ /**
+ * A tri-state variable to represent the single tap state when both of
+ * `APZStateChange::eEndTouch` notification and `eTouchEnd` event arrived.
+ */
+ apz::SingleTapState mSingleTapState;
+
/**
* A task for calling SetActive() after a timeout.
*/
@@ -103,6 +130,8 @@ class ActiveElementManager final {
void ResetTouchBlockState();
void SetActiveTask(const nsCOMPtr<dom::Element>& aTarget);
void CancelTask();
+ // Returns true if the function changed the active element state.
+ bool MaybeChangeActiveState(apz::SingleTapState aState);
};
} // namespace layers