Files
sousa-gecko/layout/generic/ScrollPositionUpdate.cpp
Hiroyuki Ikezoe c3d332f653 Bug 2035957 - Ignore zero delta smooth script scroll operation if there's no user triggered animation. r=botond
When scrollTo() or similar script APIs are called with the layout viewport
already at the destination, ApzSmoothScrollTo previously sent a NewSmoothScroll
update regardless. In APZ, receiving any smooth scroll update cancels any
ongoing animation — including user-triggered ones — which is undesirable when
the script scroll is effectively a no-op.

To fix this, send a zero-delta scroll updates targeting the layout viewport
with ScrollTriggeredByScript::Yes. And in
AsyncPanZoomController::NotifyMainThreadTransaction, the zero-delta smooth
scroll updates that are script-triggered call CancelCanimation() if a
script-triggered animation is running, then skip triggering a new smooth
scroll animation.

Differential Revision: https://phabricator.services.mozilla.com/D297520
2026-07-13 23:59:58 +00:00

162 lines
5.5 KiB
C++

/* 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 "ScrollPositionUpdate.h"
#include <ostream>
#include "mozilla/Assertions.h"
namespace mozilla {
static ScrollGenerationCounter sGenerationCounter;
ScrollPositionUpdate::ScrollPositionUpdate()
: mType(ScrollUpdateType::Absolute),
mScrollMode(ScrollMode::Normal),
mScrollOrigin(ScrollOrigin::None),
mTriggeredByScript(ScrollTriggeredByScript::No) {}
/*static*/
ScrollPositionUpdate ScrollPositionUpdate::NewScrollframe(
nsPoint aInitialPosition) {
ScrollPositionUpdate ret;
ret.mScrollGeneration = sGenerationCounter.NewMainThreadGeneration();
ret.mScrollMode = ScrollMode::Instant;
ret.mDestination = CSSPoint::FromAppUnits(aInitialPosition);
return ret;
}
/*static*/
ScrollPositionUpdate ScrollPositionUpdate::NewScroll(ScrollOrigin aOrigin,
nsPoint aDestination) {
MOZ_ASSERT(aOrigin != ScrollOrigin::NotSpecified);
MOZ_ASSERT(aOrigin != ScrollOrigin::None);
ScrollPositionUpdate ret;
ret.mScrollGeneration = sGenerationCounter.NewMainThreadGeneration();
ret.mType = ScrollUpdateType::Absolute;
ret.mScrollMode = ScrollMode::Instant;
ret.mScrollOrigin = aOrigin;
ret.mDestination = CSSPoint::FromAppUnits(aDestination);
return ret;
}
/*static*/
ScrollPositionUpdate ScrollPositionUpdate::NewRelativeScroll(
nsPoint aSource, nsPoint aDestination) {
ScrollPositionUpdate ret;
ret.mScrollGeneration = sGenerationCounter.NewMainThreadGeneration();
ret.mType = ScrollUpdateType::Relative;
ret.mScrollMode = ScrollMode::Instant;
ret.mScrollOrigin = ScrollOrigin::Relative;
ret.mDestination = CSSPoint::FromAppUnits(aDestination);
ret.mSource = CSSPoint::FromAppUnits(aSource);
return ret;
}
/*static*/
ScrollPositionUpdate ScrollPositionUpdate::NewSmoothScroll(
ScrollMode aMode, ScrollOrigin aOrigin, nsPoint aDestination,
ScrollTriggeredByScript aTriggeredByScript,
UniquePtr<ScrollSnapTargetIds> aSnapTargetIds,
ViewportType aViewportToScroll) {
MOZ_ASSERT(aOrigin != ScrollOrigin::NotSpecified);
MOZ_ASSERT(aOrigin != ScrollOrigin::None);
MOZ_ASSERT(aMode == ScrollMode::Smooth || aMode == ScrollMode::SmoothMsd);
ScrollPositionUpdate ret;
ret.mScrollGeneration = sGenerationCounter.NewMainThreadGeneration();
ret.mType = ScrollUpdateType::Absolute;
ret.mScrollMode = aMode;
ret.mScrollOrigin = aOrigin;
ret.mDestination = CSSPoint::FromAppUnits(aDestination);
ret.mViewportType = aViewportToScroll;
ret.mTriggeredByScript = aTriggeredByScript;
if (aSnapTargetIds) {
ret.mSnapTargetIds = *aSnapTargetIds;
}
return ret;
}
/*static*/
ScrollPositionUpdate ScrollPositionUpdate::NewPureRelativeScroll(
ScrollOrigin aOrigin, ScrollMode aMode, const nsPoint& aDelta) {
MOZ_ASSERT(aOrigin != ScrollOrigin::NotSpecified);
MOZ_ASSERT(aOrigin != ScrollOrigin::None);
ScrollPositionUpdate ret;
ret.mScrollGeneration = sGenerationCounter.NewMainThreadGeneration();
ret.mType = ScrollUpdateType::PureRelative;
ret.mScrollMode = aMode;
// Pure relative scrolls always apply to the visual viewport.
// FIXME: Should they? (Bug 1984224)
ret.mViewportType = ViewportType::Visual;
ret.mScrollOrigin = aOrigin;
ret.mDelta = CSSPoint::FromAppUnits(aDelta);
return ret;
}
/* static */
ScrollPositionUpdate ScrollPositionUpdate::NewZeroDeltaLayoutScroll(
ScrollOrigin aOrigin, ScrollMode aMode,
UniquePtr<ScrollSnapTargetIds> aSnapTargetIds) {
ScrollPositionUpdate ret;
ret.mScrollGeneration = sGenerationCounter.NewMainThreadGeneration();
ret.mType = ScrollUpdateType::ZeroDeltaLayoutScroll;
ret.mScrollMode = aMode;
ret.mScrollOrigin = aOrigin;
ret.mTriggeredByScript = ScrollTriggeredByScript::Yes;
if (aSnapTargetIds) {
ret.mSnapTargetIds = *aSnapTargetIds;
}
return ret;
}
bool ScrollPositionUpdate::operator==(
const ScrollPositionUpdate& aOther) const {
// instances are immutable, and all the fields are set when the generation
// is set. So if the generation matches, these instances are identical.
return mScrollGeneration == aOther.mScrollGeneration;
}
MainThreadScrollGeneration ScrollPositionUpdate::GetGeneration() const {
return mScrollGeneration;
}
ScrollUpdateType ScrollPositionUpdate::GetType() const { return mType; }
ScrollMode ScrollPositionUpdate::GetMode() const { return mScrollMode; }
ScrollOrigin ScrollPositionUpdate::GetOrigin() const { return mScrollOrigin; }
CSSPoint ScrollPositionUpdate::GetDestination() const {
MOZ_ASSERT(mType == ScrollUpdateType::Absolute ||
mType == ScrollUpdateType::Relative);
return mDestination;
}
CSSPoint ScrollPositionUpdate::GetSource() const {
MOZ_ASSERT(mType == ScrollUpdateType::Relative);
return mSource;
}
CSSPoint ScrollPositionUpdate::GetDelta() const {
MOZ_ASSERT(mType == ScrollUpdateType::PureRelative);
return mDelta;
}
std::ostream& operator<<(std::ostream& aStream,
const ScrollPositionUpdate& aUpdate) {
aStream << "{ gen=" << aUpdate.mScrollGeneration << ", type=" << aUpdate.mType
<< ", mode=" << aUpdate.mScrollMode
<< ", origin=" << aUpdate.mScrollOrigin
<< ", dst=" << aUpdate.mDestination << ", src=" << aUpdate.mSource
<< ", delta=" << aUpdate.mDelta
<< ", triggered by script=" << aUpdate.WasTriggeredByScript() << " }";
return aStream;
}
} // namespace mozilla