Bug 1774537 - Propagate last snap target id(s) from APZ to the content. r=botond

Depends on D149495

Differential Revision: https://phabricator.services.mozilla.com/D149496
This commit is contained in:
Hiroyuki Ikezoe 2022-07-14 02:19:11 +00:00
parent 52a1e4ddb2
commit 802ddc1ef6
13 changed files with 208 additions and 71 deletions

View File

@ -10,14 +10,15 @@
#include <iosfwd>
#include <stdint.h> // for uint8_t, uint32_t, uint64_t
#include "FrameMetrics.h" // for FrameMetrics
#include "mozilla/DefineEnum.h" // for MOZ_DEFINE_ENUM
#include "mozilla/gfx/BasePoint.h" // for BasePoint
#include "mozilla/gfx/Rect.h" // for RoundedIn
#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
#include "mozilla/TimeStamp.h" // for TimeStamp
#include "Units.h" // for CSSRect, CSSPixel, etc
#include "UnitTransforms.h" // for ViewAs
#include "FrameMetrics.h" // for FrameMetrics
#include "mozilla/DefineEnum.h" // for MOZ_DEFINE_ENUM
#include "mozilla/gfx/BasePoint.h" // for BasePoint
#include "mozilla/gfx/Rect.h" // for RoundedIn
#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
#include "mozilla/ScrollSnapTargetId.h" // for ScrollSnapTargetIds
#include "mozilla/TimeStamp.h" // for TimeStamp
#include "Units.h" // for CSSRect, CSSPixel, etc
#include "UnitTransforms.h" // for ViewAs
namespace IPC {
template <typename T>
@ -65,7 +66,8 @@ struct RepaintRequest {
const ScreenMargin& aDisplayportMargins,
const ScrollOffsetUpdateType aScrollUpdateType,
APZScrollAnimationType aScrollAnimationType,
const APZScrollGeneration& aScrollGenerationOnApz)
const APZScrollGeneration& aScrollGenerationOnApz,
const ScrollSnapTargetIds& aLastSnapTargetIds)
: mScrollId(aOther.GetScrollId()),
mPresShellResolution(aOther.GetPresShellResolution()),
mCompositionBounds(aOther.GetCompositionBounds()),
@ -82,6 +84,7 @@ struct RepaintRequest {
mPaintRequestTime(aOther.GetPaintRequestTime()),
mScrollUpdateType(aScrollUpdateType),
mScrollAnimationType(aScrollAnimationType),
mLastSnapTargetIds(aLastSnapTargetIds),
mIsRootContent(aOther.IsRootContent()),
mIsScrollInfoLayer(aOther.IsScrollInfoLayer()) {}
@ -104,6 +107,7 @@ struct RepaintRequest {
mPaintRequestTime == aOther.mPaintRequestTime &&
mScrollUpdateType == aOther.mScrollUpdateType &&
mScrollAnimationType == aOther.mScrollAnimationType &&
mLastSnapTargetIds == aOther.mLastSnapTargetIds &&
mIsRootContent == aOther.mIsRootContent &&
mIsScrollInfoLayer == aOther.mIsScrollInfoLayer;
}
@ -198,6 +202,10 @@ struct RepaintRequest {
return mScrollAnimationType;
}
const ScrollSnapTargetIds& GetLastSnapTargetIds() const {
return mLastSnapTargetIds;
}
protected:
void SetIsRootContent(bool aIsRootContent) {
mIsRootContent = aIsRootContent;
@ -298,6 +306,8 @@ struct RepaintRequest {
APZScrollAnimationType mScrollAnimationType;
ScrollSnapTargetIds mLastSnapTargetIds;
// Whether or not this is the root scroll frame for the root content document.
bool mIsRootContent : 1;

View File

@ -1976,7 +1976,7 @@ nsEventStatus AsyncPanZoomController::OnKeyboard(const KeyboardInput& aEvent) {
CSSPoint destination = GetKeyboardDestination(aEvent.mAction);
ScrollOrigin scrollOrigin =
SmoothScrollAnimation::GetScrollOriginForAction(aEvent.mAction.mType);
bool scrollSnapped = MaybeAdjustDestinationForScrollSnapping(
Maybe<CSSSnapTarget> snapTarget = MaybeAdjustDestinationForScrollSnapping(
aEvent, destination, GetScrollSnapFlagsForKeyboardAction(aEvent.mAction));
ScrollMode scrollMode = apz::GetScrollModeForOrigin(scrollOrigin);
@ -2013,6 +2013,9 @@ nsEventStatus AsyncPanZoomController::OnKeyboard(const KeyboardInput& aEvent) {
SetState(KEYBOARD_SCROLL);
}
if (snapTarget) {
mLastSnapTargetIds = std::move(snapTarget->mTargetIds);
}
SetState(NOTHING);
return nsEventStatus_eConsumeDoDefault;
@ -2023,13 +2026,13 @@ nsEventStatus AsyncPanZoomController::OnKeyboard(const KeyboardInput& aEvent) {
// update it.
RecursiveMutexAutoLock lock(mRecursiveMutex);
if (scrollSnapped) {
if (snapTarget) {
// If we're scroll snapping, use a smooth scroll animation to get
// the desired physics. Note that SmoothMsdScrollTo() will re-use an
// existing smooth scroll animation if there is one.
APZC_LOG("%p keyboard scrolling to snap point %s\n", this,
ToString(destination).c_str());
SmoothMsdScrollTo(destination, ScrollTriggeredByScript::No);
SmoothMsdScrollTo(std::move(*snapTarget), ScrollTriggeredByScript::No);
return nsEventStatus_eConsumeDoDefault;
}
@ -2418,8 +2421,9 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(
RecursiveMutexAutoLock lock(mRecursiveMutex);
startPosition = Metrics().GetVisualScrollOffset();
}
MaybeAdjustDeltaForScrollSnappingOnWheelInput(aEvent, delta,
startPosition);
Maybe<CSSSnapTarget> snapTarget =
MaybeAdjustDeltaForScrollSnappingOnWheelInput(aEvent, delta,
startPosition);
ScreenPoint distance = ToScreenCoordinates(
ParentLayerPoint(fabs(delta.x), fabs(delta.y)), aEvent.mLocalOrigin);
@ -2436,11 +2440,14 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(
CallDispatchScroll(startPoint, endPoint, handoffState);
ParentLayerPoint remainingDelta = endPoint - startPoint;
if (remainingDelta != delta) {
// If any scrolling happened, set KEYBOARD_SCROLL explicitly so that it
// If any scrolling happened, set WHEEL_SCROLL explicitly so that it
// will trigger a TransformEnd notification.
SetState(WHEEL_SCROLL);
}
if (snapTarget) {
mLastSnapTargetIds = std::move(snapTarget->mTargetIds);
}
SetState(NOTHING);
// The calls above handle their own locking; moreover,
@ -2465,14 +2472,15 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(
CSSPoint startPosition = GetCurrentAnimationDestination(lock).valueOr(
Metrics().GetVisualScrollOffset());
if (MaybeAdjustDeltaForScrollSnappingOnWheelInput(aEvent, delta,
startPosition)) {
if (Maybe<CSSSnapTarget> snapTarget =
MaybeAdjustDeltaForScrollSnappingOnWheelInput(aEvent, delta,
startPosition)) {
// If we're scroll snapping, use a smooth scroll animation to get
// the desired physics. Note that SmoothMsdScrollTo() will re-use an
// existing smooth scroll animation if there is one.
APZC_LOG("%p wheel scrolling to snap point %s\n", this,
ToString(startPosition).c_str());
SmoothMsdScrollTo(startPosition, ScrollTriggeredByScript::No);
SmoothMsdScrollTo(std::move(*snapTarget), ScrollTriggeredByScript::No);
break;
}
@ -3811,12 +3819,14 @@ void AsyncPanZoomController::SmoothScrollTo(const CSSPoint& aDestination,
}
void AsyncPanZoomController::SmoothMsdScrollTo(
const CSSPoint& aDestination, ScrollTriggeredByScript aTriggeredByScript) {
CSSSnapTarget&& aDestination, ScrollTriggeredByScript aTriggeredByScript) {
if (mState == SMOOTHMSD_SCROLL && mAnimation) {
APZC_LOG("%p updating destination on existing animation\n", this);
RefPtr<SmoothMsdScrollAnimation> animation(
static_cast<SmoothMsdScrollAnimation*>(mAnimation.get()));
animation->SetDestination(aDestination, aTriggeredByScript);
animation->SetDestination(aDestination.mPosition,
std::move(aDestination.mTargetIds),
aTriggeredByScript);
} else {
CancelAnimation();
SetState(SMOOTHMSD_SCROLL);
@ -3829,10 +3839,11 @@ void AsyncPanZoomController::SmoothMsdScrollTo(
}
StartAnimation(new SmoothMsdScrollAnimation(
*this, Metrics().GetVisualScrollOffset(), initialVelocity, aDestination,
*this, Metrics().GetVisualScrollOffset(), initialVelocity,
aDestination.mPosition,
StaticPrefs::layout_css_scroll_behavior_spring_constant(),
StaticPrefs::layout_css_scroll_behavior_damping_ratio(),
aTriggeredByScript));
std::move(aDestination.mTargetIds), aTriggeredByScript));
}
}
@ -3983,6 +3994,7 @@ void AsyncPanZoomController::CancelAnimation(CancelAnimationFlags aFlags) {
}
SetState(NOTHING);
mLastSnapTargetIds = ScrollSnapTargetIds{};
mAnimation = nullptr;
// Since there is no animation in progress now the axes should
// have no velocity either. If we are dropping the velocity from a non-zero
@ -4409,7 +4421,7 @@ void AsyncPanZoomController::RequestContentRepaint(
: APZScrollAnimationType::TriggeredByUserInput;
}
RepaintRequest request(aFrameMetrics, aDisplayportMargins, aUpdateType,
animationType, mScrollGeneration);
animationType, mScrollGeneration, mLastSnapTargetIds);
if (request.IsRootContent() && request.GetZoom() != mLastNotifiedZoom &&
mState != PINCHING && mState != ANIMATING_ZOOM) {
@ -4435,7 +4447,9 @@ void AsyncPanZoomController::RequestContentRepaint(
request.GetScrollUpdateType() ==
mLastPaintRequestMetrics.GetScrollUpdateType() &&
request.GetScrollAnimationType() ==
mLastPaintRequestMetrics.GetScrollAnimationType()) {
mLastPaintRequestMetrics.GetScrollAnimationType() &&
request.GetLastSnapTargetIds() ==
mLastPaintRequestMetrics.GetLastSnapTargetIds()) {
return;
}
@ -4511,6 +4525,10 @@ bool AsyncPanZoomController::UpdateAnimation(
*aOutDeferredTasks = mAnimation->TakeDeferredTasks();
if (!continueAnimation) {
SetState(NOTHING);
if (mAnimation->AsSmoothMsdScrollAnimation()) {
mLastSnapTargetIds =
mAnimation->AsSmoothMsdScrollAnimation()->TakeSnapTargetIds();
}
mAnimation = nullptr;
}
// Request a repaint at the end of the animation in case something such as a
@ -5318,7 +5336,8 @@ void AsyncPanZoomController::NotifyLayersUpdated(
}
if (scrollUpdate.GetMode() == ScrollMode::SmoothMsd) {
SmoothMsdScrollTo(destination,
// FIXME: Need to use ScrollSnapTargetIds coming from the main-thread.
SmoothMsdScrollTo(CSSSnapTarget{destination},
scrollUpdate.GetScrollTriggeredByScript());
} else {
MOZ_ASSERT(scrollUpdate.GetMode() == ScrollMode::Smooth);
@ -6087,7 +6106,7 @@ void AsyncPanZoomController::SetTestAsyncZoom(
ScheduleComposite();
}
Maybe<CSSPoint> AsyncPanZoomController::FindSnapPointNear(
Maybe<CSSSnapTarget> AsyncPanZoomController::FindSnapPointNear(
const CSSPoint& aDestination, ScrollUnit aUnit,
ScrollSnapFlags aSnapFlags) {
mRecursiveMutex.AssertCurrentThreadIn();
@ -6104,19 +6123,20 @@ Maybe<CSSPoint> AsyncPanZoomController::FindSnapPointNear(
// of the scroll frame's scroll range. Clamp it here (this matches the
// behaviour of the main-thread code path, which clamps it in
// nsGfxScrollFrame::ScrollTo()).
return Some(scrollRange.ClampPoint(cssSnapPoint));
return Some(CSSSnapTarget{scrollRange.ClampPoint(cssSnapPoint),
snapTarget->mTargetIds});
}
return Nothing();
}
void AsyncPanZoomController::ScrollSnapNear(const CSSPoint& aDestination,
ScrollSnapFlags aSnapFlags) {
if (Maybe<CSSPoint> snapPoint = FindSnapPointNear(
if (Maybe<CSSSnapTarget> snapTarget = FindSnapPointNear(
aDestination, ScrollUnit::DEVICE_PIXELS, aSnapFlags)) {
if (*snapPoint != Metrics().GetVisualScrollOffset()) {
if (snapTarget->mPosition != Metrics().GetVisualScrollOffset()) {
APZC_LOG("%p smooth scrolling to snap point %s\n", this,
ToString(*snapPoint).c_str());
SmoothMsdScrollTo(*snapPoint, ScrollTriggeredByScript::No);
ToString(snapTarget->mPosition).c_str());
SmoothMsdScrollTo(std::move(*snapTarget), ScrollTriggeredByScript::No);
}
}
}
@ -6155,8 +6175,9 @@ void AsyncPanZoomController::ScrollSnapToDestination() {
if (predictedDelta != ParentLayerPoint()) {
snapFlags |= ScrollSnapFlags::IntendedDirection;
}
if (MaybeAdjustDeltaForScrollSnapping(ScrollUnit::DEVICE_PIXELS, snapFlags,
predictedDelta, startPosition)) {
if (Maybe<CSSSnapTarget> snapTarget = MaybeAdjustDeltaForScrollSnapping(
ScrollUnit::DEVICE_PIXELS, snapFlags, predictedDelta,
startPosition)) {
APZC_LOG(
"%p fling snapping. friction: %f velocity: %f, %f "
"predictedDelta: %f, %f position: %f, %f "
@ -6166,37 +6187,38 @@ void AsyncPanZoomController::ScrollSnapToDestination() {
(float)Metrics().GetVisualScrollOffset().y, (float)startPosition.x,
(float)startPosition.y);
SmoothMsdScrollTo(startPosition, ScrollTriggeredByScript::No);
SmoothMsdScrollTo(std::move(*snapTarget), ScrollTriggeredByScript::No);
}
}
bool AsyncPanZoomController::MaybeAdjustDeltaForScrollSnapping(
Maybe<CSSSnapTarget> AsyncPanZoomController::MaybeAdjustDeltaForScrollSnapping(
ScrollUnit aUnit, ScrollSnapFlags aSnapFlags, ParentLayerPoint& aDelta,
CSSPoint& aStartPosition) {
RecursiveMutexAutoLock lock(mRecursiveMutex);
CSSToParentLayerScale zoom = Metrics().GetZoom();
if (zoom == CSSToParentLayerScale(0)) {
return false;
return Nothing();
}
CSSPoint destination = Metrics().CalculateScrollRange().ClampPoint(
aStartPosition + (aDelta / zoom));
if (Maybe<CSSPoint> snapPoint =
if (Maybe<CSSSnapTarget> snapTarget =
FindSnapPointNear(destination, aUnit, aSnapFlags)) {
aDelta = (*snapPoint - aStartPosition) * zoom;
aStartPosition = *snapPoint;
return true;
aDelta = (snapTarget->mPosition - aStartPosition) * zoom;
aStartPosition = snapTarget->mPosition;
return snapTarget;
}
return false;
return Nothing();
}
bool AsyncPanZoomController::MaybeAdjustDeltaForScrollSnappingOnWheelInput(
Maybe<CSSSnapTarget>
AsyncPanZoomController::MaybeAdjustDeltaForScrollSnappingOnWheelInput(
const ScrollWheelInput& aEvent, ParentLayerPoint& aDelta,
CSSPoint& aStartPosition) {
// Don't scroll snap for pixel scrolls. This matches the main thread
// behaviour in EventStateManager::DoScrollText().
if (aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_PIXEL) {
return false;
return Nothing();
}
// Note that this MaybeAdjustDeltaForScrollSnappingOnWheelInput also gets
@ -6217,18 +6239,19 @@ bool AsyncPanZoomController::MaybeAdjustDeltaForScrollSnappingOnWheelInput(
ScrollSnapFlags::IntendedDirection, aDelta, aStartPosition);
}
bool AsyncPanZoomController::MaybeAdjustDestinationForScrollSnapping(
Maybe<CSSSnapTarget>
AsyncPanZoomController::MaybeAdjustDestinationForScrollSnapping(
const KeyboardInput& aEvent, CSSPoint& aDestination,
ScrollSnapFlags aSnapFlags) {
RecursiveMutexAutoLock lock(mRecursiveMutex);
ScrollUnit unit = KeyboardScrollAction::GetScrollUnit(aEvent.mAction.mType);
if (Maybe<CSSPoint> snapPoint =
if (Maybe<CSSSnapTarget> snapPoint =
FindSnapPointNear(aDestination, unit, aSnapFlags)) {
aDestination = *snapPoint;
return true;
aDestination = snapPoint->mPosition;
return snapPoint;
}
return false;
return Nothing();
}
void AsyncPanZoomController::SetZoomAnimationId(

View File

@ -1521,7 +1521,7 @@ class AsyncPanZoomController {
// Start a smooth-scrolling animation to the given destination, with MSD
// physics that is suited for scroll-snapping.
void SmoothMsdScrollTo(const CSSPoint& aDestination,
void SmoothMsdScrollTo(CSSSnapTarget&& aDestination,
ScrollTriggeredByScript aTriggeredByScript);
// Returns whether overscroll is allowed during an event.
@ -1754,6 +1754,7 @@ class AsyncPanZoomController {
// is in a panning state.
TimeDuration mTouchStartRestingTimeBeforePan;
Maybe<ParentLayerCoord> mMinimumVelocityDuringPan;
ScrollSnapTargetIds mLastSnapTargetIds;
// Extra offset to add to the async scroll position for testing
CSSPoint mTestAsyncScrollOffset;
// Extra zoom to include in the aync zoom for testing
@ -1794,20 +1795,19 @@ class AsyncPanZoomController {
// |aUnit| affects the snapping behaviour (see ScrollSnapUtils::
// GetSnapPointForDestination).
// Returns true iff. a target snap point was found.
bool MaybeAdjustDeltaForScrollSnapping(ScrollUnit aUnit,
ScrollSnapFlags aFlags,
ParentLayerPoint& aDelta,
CSSPoint& aStartPosition);
Maybe<CSSSnapTarget> MaybeAdjustDeltaForScrollSnapping(
ScrollUnit aUnit, ScrollSnapFlags aSnapFlags, ParentLayerPoint& aDelta,
CSSPoint& aStartPosition);
// A wrapper function of MaybeAdjustDeltaForScrollSnapping for
// ScrollWheelInput.
bool MaybeAdjustDeltaForScrollSnappingOnWheelInput(
Maybe<CSSSnapTarget> MaybeAdjustDeltaForScrollSnappingOnWheelInput(
const ScrollWheelInput& aEvent, ParentLayerPoint& aDelta,
CSSPoint& aStartPosition);
bool MaybeAdjustDestinationForScrollSnapping(const KeyboardInput& aEvent,
CSSPoint& aDestination,
ScrollSnapFlags aSnapFlags);
Maybe<CSSSnapTarget> MaybeAdjustDestinationForScrollSnapping(
const KeyboardInput& aEvent, CSSPoint& aDestination,
ScrollSnapFlags aSnapFlags);
// Snap to a snap position nearby the current scroll position, if appropriate.
void ScrollSnap(ScrollSnapFlags aSnapFlags);
@ -1824,9 +1824,9 @@ class AsyncPanZoomController {
// |aUnit| affects the snapping behaviour (see ScrollSnapUtils::
// GetSnapPointForDestination). It should generally be determined by the
// type of event that's triggering the scroll.
Maybe<CSSPoint> FindSnapPointNear(const CSSPoint& aDestination,
ScrollUnit aUnit,
ScrollSnapFlags aSnapFlags);
Maybe<CSSSnapTarget> FindSnapPointNear(const CSSPoint& aDestination,
ScrollUnit aUnit,
ScrollSnapFlags aSnapFlags);
friend std::ostream& operator<<(
std::ostream& aOut, const AsyncPanZoomController::PanZoomState& aState);

View File

@ -13,12 +13,14 @@ SmoothMsdScrollAnimation::SmoothMsdScrollAnimation(
AsyncPanZoomController& aApzc, const CSSPoint& aInitialPosition,
const CSSPoint& aInitialVelocity, const CSSPoint& aDestination,
double aSpringConstant, double aDampingRatio,
ScrollSnapTargetIds&& aSnapTargetIds,
ScrollTriggeredByScript aTriggeredByScript)
: mApzc(aApzc),
mXAxisModel(aInitialPosition.x, aDestination.x, aInitialVelocity.x,
aSpringConstant, aDampingRatio),
mYAxisModel(aInitialPosition.y, aDestination.y, aInitialVelocity.y,
aSpringConstant, aDampingRatio),
mSnapTargetIds(std::move(aSnapTargetIds)),
mTriggeredByScript(aTriggeredByScript) {}
bool SmoothMsdScrollAnimation::DoSample(FrameMetrics& aFrameMetrics,
@ -116,10 +118,11 @@ bool SmoothMsdScrollAnimation::DoSample(FrameMetrics& aFrameMetrics,
}
void SmoothMsdScrollAnimation::SetDestination(
const CSSPoint& aNewDestination,
const CSSPoint& aNewDestination, ScrollSnapTargetIds&& aSnapTargetIds,
ScrollTriggeredByScript aTriggeredByScript) {
mXAxisModel.SetDestination(aNewDestination.x);
mYAxisModel.SetDestination(aNewDestination.y);
mSnapTargetIds = std::move(aSnapTargetIds);
mTriggeredByScript = aTriggeredByScript;
}

View File

@ -23,6 +23,7 @@ class SmoothMsdScrollAnimation final : public AsyncPanZoomAnimation {
const CSSPoint& aInitialVelocity,
const CSSPoint& aDestination, double aSpringConstant,
double aDampingRatio,
ScrollSnapTargetIds&& aSnapTargetIds,
ScrollTriggeredByScript aTriggeredByScript);
/**
@ -35,6 +36,7 @@ class SmoothMsdScrollAnimation final : public AsyncPanZoomAnimation {
const TimeDuration& aDelta) override;
void SetDestination(const CSSPoint& aNewDestination,
ScrollSnapTargetIds&& aSnapTargetIds,
ScrollTriggeredByScript aTriggeredByScript);
CSSPoint GetDestination() const;
SmoothMsdScrollAnimation* AsSmoothMsdScrollAnimation() override;
@ -43,10 +45,13 @@ class SmoothMsdScrollAnimation final : public AsyncPanZoomAnimation {
return mTriggeredByScript == ScrollTriggeredByScript::Yes;
}
ScrollSnapTargetIds TakeSnapTargetIds() { return std::move(mSnapTargetIds); }
private:
AsyncPanZoomController& mApzc;
AxisPhysicsMSDModel mXAxisModel;
AxisPhysicsMSDModel mYAxisModel;
ScrollSnapTargetIds mSnapTargetIds;
ScrollTriggeredByScript mTriggeredByScript;
};

View File

@ -0,0 +1,53 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Re-snapping to the last snapped element on APZ</title>
<script src="apz_test_utils.js"></script>
<script src="apz_test_native_event_utils.js"></script>
<script src="/tests/SimpleTest/paint_listener.js"></script>
<style>
div {
position: absolute;
}
#scroller {
width: 100%;
height: 500px;
overflow-y: scroll;
scroll-snap-type: y mandatory;
}
.child {
width: 100%;
height: 100px;
background-color: blue;
scroll-snap-align: start;
}
</style>
</head>
<body>
<div id="scroller">
<div class="child" style="top: 0px;"></div>
<div class="child" style="top: 200px;"></div>
<div style="width: 100%; height: 2000px;"></div>
</div>
<script type="application/javascript">
async function test() {
let transformEndPromise = promiseTransformEnd();
await promiseMoveMouseAndScrollWheelOver(scroller, 100, 100);
await transformEndPromise;
await promiseApzFlushedRepaints();
is(scroller.scrollTop, 200, "snap to 200px");
document.querySelectorAll(".child")[1].style.top = "400px";
is(scroller.scrollTop, 400, "re-snap to 400px");
}
waitUntilApzStable()
.then(test)
.then(subtestDone, subtestFailed);
</script>
</body>
</html>

View File

@ -19,6 +19,9 @@ const prefs = [
const subtests = [
{"file": "helper_scroll_snap_no_valid_snap_position.html", "prefs": prefs},
{"file": "helper_scroll_snap_resnap_after_async_scroll.html"},
{"file": "helper_scroll_snap_resnap_after_async_scroll.html",
"prefs": [["general.smoothScroll", false]]},
];
if (isApzEnabled()) {

View File

@ -129,7 +129,9 @@ static CSSPoint ScrollFrameTo(nsIScrollableFrame* aFrame,
// request because we'll clobber that one, which is bad.
bool scrollInProgress = APZCCallbackHelper::IsScrollInProgress(aFrame);
if (!scrollInProgress) {
aFrame->ScrollToCSSPixelsForApz(targetScrollPosition);
ScrollSnapTargetIds snapTargetIds = aRequest.GetLastSnapTargetIds();
aFrame->ScrollToCSSPixelsForApz(targetScrollPosition,
std::move(snapTargetIds));
geckoScrollPosition = CSSPoint::FromAppUnits(aFrame->GetScrollPosition());
aSuccessOut = true;
}
@ -387,7 +389,9 @@ void APZCCallbackHelper::UpdateRootFrame(const RepaintRequest& aRequest) {
nsLayoutUtils::FindScrollableFrameFor(aRequest.GetScrollId());
CSSPoint currentScrollPosition =
CSSPoint::FromAppUnits(sf->GetScrollPosition());
sf->ScrollToCSSPixelsForApz(currentScrollPosition);
ScrollSnapTargetIds snapTargetIds = aRequest.GetLastSnapTargetIds();
sf->ScrollToCSSPixelsForApz(currentScrollPosition,
std::move(snapTargetIds));
}
// Do this as late as possible since scrolling can flush layout. It also

View File

@ -305,6 +305,21 @@ struct ParamTraits<mozilla::APZScrollAnimationType>
mozilla::APZScrollAnimationType, mozilla::APZScrollAnimationType::No,
mozilla::APZScrollAnimationType::TriggeredByUserInput> {};
template <>
struct ParamTraits<mozilla::ScrollSnapTargetIds> {
typedef mozilla::ScrollSnapTargetIds paramType;
static void Write(MessageWriter* aWriter, const paramType& aParam) {
WriteParam(aWriter, aParam.mIdsOnX);
WriteParam(aWriter, aParam.mIdsOnY);
}
static bool Read(MessageReader* aReader, paramType* aResult) {
return ReadParam(aReader, &aResult->mIdsOnX) &&
ReadParam(aReader, &aResult->mIdsOnY);
}
};
template <>
struct ParamTraits<mozilla::layers::RepaintRequest>
: BitfieldHelper<mozilla::layers::RepaintRequest> {
@ -327,6 +342,7 @@ struct ParamTraits<mozilla::layers::RepaintRequest>
WriteParam(aWriter, aParam.mPaintRequestTime);
WriteParam(aWriter, aParam.mScrollUpdateType);
WriteParam(aWriter, aParam.mScrollAnimationType);
WriteParam(aWriter, aParam.mLastSnapTargetIds);
WriteParam(aWriter, aParam.mIsRootContent);
WriteParam(aWriter, aParam.mIsScrollInfoLayer);
}
@ -349,6 +365,7 @@ struct ParamTraits<mozilla::layers::RepaintRequest>
ReadParam(aReader, &aResult->mPaintRequestTime) &&
ReadParam(aReader, &aResult->mScrollUpdateType) &&
ReadParam(aReader, &aResult->mScrollAnimationType) &&
ReadParam(aReader, &aResult->mLastSnapTargetIds) &&
ReadBoolForBitfield(aReader, aResult, &paramType::SetIsRootContent) &&
ReadBoolForBitfield(aReader, aResult,
&paramType::SetIsScrollInfoLayer));

View File

@ -8,6 +8,7 @@
#include <cstdint>
#include "nsPoint.h"
#include "nsTArray.h"
#include "Units.h"
namespace mozilla {
@ -20,6 +21,9 @@ enum class ScrollSnapTargetId : uintptr_t {
struct ScrollSnapTargetIds {
CopyableTArray<ScrollSnapTargetId> mIdsOnX;
CopyableTArray<ScrollSnapTargetId> mIdsOnY;
bool operator==(const ScrollSnapTargetIds& aOther) const {
return mIdsOnX == aOther.mIdsOnX && mIdsOnY == aOther.mIdsOnY;
}
};
struct SnapTarget {
@ -27,6 +31,11 @@ struct SnapTarget {
ScrollSnapTargetIds mTargetIds;
};
struct CSSSnapTarget {
CSSPoint mPosition;
ScrollSnapTargetIds mTargetIds;
};
} // namespace mozilla
#endif // mozilla_ScrollSnapTargetId_h_

View File

@ -2530,14 +2530,15 @@ void ScrollFrameHelper::ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
}
void ScrollFrameHelper::ScrollToCSSPixelsForApz(
const CSSPoint& aScrollPosition) {
const CSSPoint& aScrollPosition, ScrollSnapTargetIds&& aLastSnapTargetIds) {
nsPoint pt = CSSPoint::ToAppUnits(aScrollPosition);
nscoord halfRange = nsPresContext::CSSPixelsToAppUnits(1000);
nsRect range(pt.x - halfRange, pt.y - halfRange, 2 * halfRange - 1,
2 * halfRange - 1);
ScrollToWithOrigin(
pt, &range,
ScrollOperationParams{ScrollMode::Instant, ScrollOrigin::Apz});
ScrollOperationParams{ScrollMode::Instant, ScrollOrigin::Apz,
std::move(aLastSnapTargetIds)});
// 'this' might be destroyed here
}

View File

@ -274,7 +274,9 @@ class ScrollFrameHelper : public nsIReflowCallback {
/**
* @note This method might destroy the frame, pres shell and other objects.
*/
void ScrollToCSSPixelsForApz(const mozilla::CSSPoint& aScrollPosition);
void ScrollToCSSPixelsForApz(
const mozilla::CSSPoint& aScrollPosition,
mozilla::ScrollSnapTargetIds&& aLastSnapTargetIds);
CSSIntPoint GetScrollPositionCSSPixels();
/**
@ -1102,8 +1104,11 @@ class nsHTMLScrollFrame : public nsContainerFrame,
ScrollMode aMode = ScrollMode::Instant) final {
mHelper.ScrollToCSSPixels(aScrollPosition, aMode);
}
void ScrollToCSSPixelsForApz(const mozilla::CSSPoint& aScrollPosition) final {
mHelper.ScrollToCSSPixelsForApz(aScrollPosition);
void ScrollToCSSPixelsForApz(
const mozilla::CSSPoint& aScrollPosition,
mozilla::ScrollSnapTargetIds&& aLastSnapTargetIds) final {
mHelper.ScrollToCSSPixelsForApz(aScrollPosition,
std::move(aLastSnapTargetIds));
}
/**
* @note This method might destroy the frame, pres shell and other objects.
@ -1581,8 +1586,11 @@ class nsXULScrollFrame final : public nsBoxFrame,
ScrollMode aMode = ScrollMode::Instant) final {
mHelper.ScrollToCSSPixels(aScrollPosition, aMode);
}
void ScrollToCSSPixelsForApz(const mozilla::CSSPoint& aScrollPosition) final {
mHelper.ScrollToCSSPixelsForApz(aScrollPosition);
void ScrollToCSSPixelsForApz(
const mozilla::CSSPoint& aScrollPosition,
mozilla::ScrollSnapTargetIds&& aLastSnapTargetIds) final {
mHelper.ScrollToCSSPixelsForApz(aScrollPosition,
std::move(aLastSnapTargetIds));
}
CSSIntPoint GetScrollPositionCSSPixels() final {
return mHelper.GetScrollPositionCSSPixels();

View File

@ -294,7 +294,8 @@ class nsIScrollableFrame : public nsIScrollbarMediator {
* number of layer pixels (so the operation is fast and looks clean).
*/
virtual void ScrollToCSSPixelsForApz(
const mozilla::CSSPoint& aScrollPosition) = 0;
const mozilla::CSSPoint& aScrollPosition,
mozilla::ScrollSnapTargetIds&& aLastSnapTargetIds) = 0;
/**
* Returns the scroll position in integer CSS pixels, rounded to the nearest