summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMoonchild <moonchild@palemoon.org>2022-09-30 12:24:19 +0000
committerMoonchild <moonchild@palemoon.org>2022-09-30 12:24:19 +0000
commitbdc924d25ee6d0bbe6c780c1d88b60db573562e7 (patch)
tree426c983ca8bb4dd32d4f8db93625a9a07e11367d
parentb83dd4e5e8b95f61fa2c25b1635793f919e4f101 (diff)
downloaduxp-scroll-anchoring-wip.tar.gz
WIP: basic scroll-anchoring attempt.scroll-anchoring-wip
This is incomplete and won't build due to prerequisites.
-rw-r--r--layout/base/nsIPresShell.h6
-rw-r--r--layout/base/nsPresShell.cpp32
-rw-r--r--layout/base/nsPresShell.h7
-rw-r--r--layout/generic/nsFrame.cpp57
-rw-r--r--layout/generic/nsFrameStateBits.h6
-rw-r--r--layout/generic/nsGfxScrollFrame.cpp184
-rw-r--r--layout/generic/nsGfxScrollFrame.h30
-rw-r--r--layout/generic/nsIFrame.h4
-rw-r--r--layout/generic/nsIScrollableFrame.h3
9 files changed, 323 insertions, 6 deletions
diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h
index cf19867ef0..d368402e4f 100644
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -486,6 +486,12 @@ public:
virtual nsCanvasFrame* GetCanvasFrame() const = 0;
/**
+ * Returns whether the scollable frame need to have a position adjustment
+ * based on scroll-anchoring.
+ */
+ virtual void ScrollableFrameNeedsAnchorAdjustment(nsIScrollableFrame* aFrame) = 0;
+
+ /**
* Tell the pres shell that a frame needs to be marked dirty and needs
* Reflow. It's OK if this is an ancestor of the frame needing reflow as
* long as the ancestor chain between them doesn't cross a reflow root.
diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp
index 23876cc112..0fc84d736c 100644
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -1225,6 +1225,7 @@ PresShell::Destroy()
}
mFramesToDirty.Clear();
+ mScrollAnchorAdjustments.Clear();
if (mViewManager) {
// Clear the view manager's weak pointer back to |this| in case it
@@ -2049,6 +2050,11 @@ PresShell::NotifyDestroyingFrame(nsIFrame* aFrame)
}
mFramesToDirty.RemoveEntry(aFrame);
+
+ nsIScrollableFrame* scrollableFrame = do_QueryFrame(aFrame);
+ if (scrollableFrame) {
+ mScrollAnchorAdjustments.RemoveEntry(scrollableFrame);
+ }
} else {
// We must delete this property in situ so that its destructor removes the
// frame from FrameLayerBuilder::DisplayItemData::mFrameList -- otherwise
@@ -2584,6 +2590,12 @@ PresShell::VerifyHasDirtyRootAncestor(nsIFrame* aFrame)
#endif
void
+PresShell::ScrollableFrameNeedsAnchorAdjustment(nsIScrollableFrame* aFrame)
+{
+ mScrollAnchorAdjustments.PutEntry(aFrame);
+}
+
+void
PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
nsFrameState aBitToAdd,
ReflowRootHandling aRootHandling)
@@ -4106,12 +4118,21 @@ PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aFlush)
didLayoutFlush = true;
mFrameConstructor->RecalcQuotesAndCounters();
viewManager->FlushDelayedResize(true);
- if (ProcessReflowCommands(flushType < Flush_Layout) && mContentToScrollTo) {
- // We didn't get interrupted. Go ahead and scroll to our content
- DoScrollContentIntoView();
+ if (ProcessReflowCommands(flushType < Flush_Layout)) {
+ // Apply scroll offset updates for scroll anchors.
+ for (auto iter = mScrollAnchorAdjustments.Iter(); !iter.Done(); iter.Next()) {
+ nsIScrollableFrame* frame = iter.Get()->GetKey();
+ frame->ApplyScrollAnchorOffsetAdjustment();
+ }
+ mScrollAnchorAdjustments.Clear();
+
if (mContentToScrollTo) {
- mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
- mContentToScrollTo = nullptr;
+ // We didn't get interrupted; go ahead and scroll to our content.
+ DoScrollContentIntoView();
+ if (mContentToScrollTo) {
+ mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
+ mContentToScrollTo = nullptr;
+ }
}
}
}
@@ -10833,6 +10854,7 @@ PresShell::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
}
*aPresShellSize += mApproximatelyVisibleFrames.ShallowSizeOfExcludingThis(aMallocSizeOf);
*aPresShellSize += mFramesToDirty.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ *aPresShellSize += mScrollAnchorAdjustments.ShallowSizeOfExcludingThis(aMallocSizeOf);
*aPresShellSize += aArenaObjectsSize->mOther;
if (nsStyleSet* styleSet = StyleSet()->GetAsGecko()) {
diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h
index fbbcfc7ecd..de638f4bf6 100644
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -129,6 +129,8 @@ public:
virtual nsIPageSequenceFrame* GetPageSequenceFrame() const override;
virtual nsCanvasFrame* GetCanvasFrame() const override;
+ virtual void ScrollableFrameNeedsAnchorAdjustment(nsIScrollableFrame* aFrame) override;
+
virtual void FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
nsFrameState aBitToAdd,
ReflowRootHandling aRootHandling =
@@ -427,9 +429,10 @@ public:
void SetNextPaintCompressed() { mNextPaintCompressed = true; }
-protected:
virtual ~PresShell();
+protected:
+
void HandlePostedReflowCallbacks(bool aInterruptible);
void CancelPostedReflowCallbacks();
@@ -852,6 +855,8 @@ protected:
// Set of frames that we should mark with NS_FRAME_HAS_DIRTY_CHILDREN after
// we finish reflowing mCurrentReflowRoot.
nsTHashtable<nsPtrHashKey<nsIFrame> > mFramesToDirty;
+
+ nsTHashtable<nsPtrHashKey<nsIScrollableFrame> > mScrollAnchorAdjustments;
// Reflow roots that need to be reflowed.
nsTArray<nsIFrame*> mDirtyRoots;
diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp
index 43ad970890..5524b726de 100644
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -688,6 +688,20 @@ nsFrame::DestroyFrom(nsIFrame* aDestructRoot)
}
}
+ if (HasAnyStateBits(NS_FRAME_IS_SCROLL_ANCHOR)) {
+ // Find the nearest scroll frame, that's the one that marked us as a
+ // scroll anchor
+ nsIFrame* currentFrame = this;
+ while (currentFrame) {
+ nsIScrollableFrame* scrollTarget = currentFrame->GetScrollTargetFrame();
+ if (scrollTarget) {
+ scrollTarget->ScrollAnchorWillDestroy();
+ break;
+ }
+ currentFrame = currentFrame->GetParent();
+ }
+ }
+
if (HasCSSAnimations() || HasCSSTransitions() ||
EffectSet::GetEffectSet(this)) {
// If no new frame for this element is created by the end of the
@@ -814,6 +828,35 @@ AddAndRemoveImageAssociations(nsFrame* aFrame,
}
}
+void
+nsIFrame::MaybeNotifyScrollAnchor()
+{
+ bool isScrollAnchor = HasAnyStateBits(NS_FRAME_IS_SCROLL_ANCHOR);
+ bool containsScrollAnchor = HasAnyStateBits(NS_FRAME_CONTAINS_SCROLL_ANCHOR);
+
+ if (isScrollAnchor) {
+ printf_stderr("nsIFrame(%p)::MaybeNotifyScrollAnchor scroll anchor offset changed, queueing adjustment.\n", this);
+ }
+ if (containsScrollAnchor) {
+ printf_stderr("nsIFrame(%p)::MaybeNotifyScrollAnchor offset for scroll anchor container changed, queueing adjustment.\n", this);
+ }
+ MOZ_ASSERT(!(isScrollAnchor && containsScrollAnchor));
+
+ if (isScrollAnchor || containsScrollAnchor) {
+ // Find the nearest scroll frame, that's the one that marked us as a
+ // scroll anchor
+ nsIFrame* currentFrame = this;
+ while (currentFrame) {
+ nsIScrollableFrame* scrollTarget = currentFrame->GetScrollTargetFrame();
+ if (scrollTarget) {
+ PresShell().ScrollableFrameNeedsAnchorAdjustment(scrollTarget);
+ break;
+ }
+ currentFrame = currentFrame->GetParent();
+ }
+ }
+}
+
// Subclass hook for style post processing
/* virtual */ void
nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
@@ -2902,6 +2945,13 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
aBuilder->AdjustWindowDraggingRegion(child);
child->BuildDisplayList(aBuilder, aLists);
+/* // Visualise scroll anchor
+ if (child->HasAnyStateBits(NS_FRAME_IS_SCROLL_ANCHOR)) {
+ nsRect bounds = child->GetContentRectRelativeToSelf() +
+ aBuilder->ToReferenceFrame(child);
+ list.AppendToTop(MakeDisplayItem<nsDisplaySolidColor>(aBuilder, child, bounds, NS_RGBA(255, 0, 0, 55)));
+ }
+ // */
aBuilder->DisplayCaret(child, aLists.Content());
#ifdef DEBUG
DisplayDebugBorders(aBuilder, child, aLists);
@@ -2916,6 +2966,13 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
nsDisplayListCollection pseudoStack(aBuilder);
aBuilder->AdjustWindowDraggingRegion(child);
child->BuildDisplayList(aBuilder, pseudoStack);
+/* // Visualise scroll anchor
+ if (child->HasAnyStateBits(NS_FRAME_IS_SCROLL_ANCHOR)) {
+ nsRect bounds = child->GetContentRectRelativeToSelf() +
+ aBuilder->ToReferenceFrame(child);
+ list.AppendToTop(MakeDisplayItem<nsDisplaySolidColor>(aBuilder, child, bounds, NS_RGBA(255, 0, 0, 55)));
+ }
+ // */
aBuilder->DisplayCaret(child, pseudoStack.Content());
list.AppendToTop(pseudoStack.BorderBackground());
diff --git a/layout/generic/nsFrameStateBits.h b/layout/generic/nsFrameStateBits.h
index ba43e37d45..03abab0b9a 100644
--- a/layout/generic/nsFrameStateBits.h
+++ b/layout/generic/nsFrameStateBits.h
@@ -266,6 +266,12 @@ FRAME_STATE_BIT(Generic, 53, NS_FRAME_IS_NONDISPLAY)
// Frame has a LayerActivityProperty property
FRAME_STATE_BIT(Generic, 54, NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY)
+// For scroll-anchoring
+FRAME_STATE_BIT(Generic, 55, NS_FRAME_CONTAINS_SCROLL_ANCHOR)
+FRAME_STATE_BIT(Generic, 56, NS_FRAME_IS_SCROLL_ANCHOR)
+
+// Bit 57 is currently unused
+
// Set for all descendants of MathML sub/supscript elements (other than the
// base frame) to indicate that the SSTY font feature should be used.
FRAME_STATE_BIT(Generic, 58, NS_FRAME_MATHML_SCRIPT_DESCENDANT)
diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp
index ff8500b92f..d3b85a18e5 100644
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -55,6 +55,7 @@
#include "nsSVGIntegrationUtils.h"
#include "nsIScrollPositionListener.h"
#include "StickyScrollContainer.h"
+#include "nsIFrame.h"
#include "nsIFrameInlines.h"
#include "gfxPlatform.h"
#include "gfxPrefs.h"
@@ -2022,6 +2023,7 @@ ScrollFrameHelper::ScrollFrameHelper(nsContainerFrame* aOuter,
, mScrolledFrame(nullptr)
, mScrollCornerBox(nullptr)
, mResizerBox(nullptr)
+ , mAnchorNode(nullptr)
, mOuter(aOuter)
, mAsyncScroll(nullptr)
, mAsyncSmoothMSDScroll(nullptr)
@@ -2033,6 +2035,7 @@ ScrollFrameHelper::ScrollFrameHelper(nsContainerFrame* aOuter,
, mScrollPosAtLastPaint(0, 0)
, mRestorePos(-1, -1)
, mLastPos(-1, -1)
+ , mLastAnchorPos(0, 0)
, mScrollPosForLayerPixelAlignment(-1, -1)
, mLastUpdateFramesPos(-1, -1)
, mHadDisplayPortAtLastFrameUpdate(false)
@@ -2250,6 +2253,146 @@ ScrollFrameHelper::GetScrollPositionCSSPixels()
return CSSIntPoint::FromAppUnitsRounded(GetScrollPosition());
}
+static void
+MarkAsScrollAnchorNode(nsIFrame* aScrolledFrame, nsIFrame* aAnchorNode)
+{
+ aAnchorNode->AddStateBits(NS_FRAME_IS_SCROLL_ANCHOR);
+ nsIFrame* containsAnchor = aAnchorNode->GetParent();
+ while (containsAnchor && containsAnchor != aScrolledFrame) {
+ containsAnchor->AddStateBits(NS_FRAME_CONTAINS_SCROLL_ANCHOR);
+ containsAnchor = containsAnchor->GetParent();
+ }
+}
+
+static void
+UnmarkAsScrollAnchorNode(nsIFrame* aScrolledFrame, nsIFrame* aAnchorNode)
+{
+ aAnchorNode->RemoveStateBits(NS_FRAME_IS_SCROLL_ANCHOR);
+ nsIFrame* containsAnchor = aAnchorNode->GetParent();
+ while (containsAnchor && containsAnchor != aScrolledFrame) {
+ containsAnchor->RemoveStateBits(NS_FRAME_CONTAINS_SCROLL_ANCHOR);
+ containsAnchor = containsAnchor->GetParent();
+ }
+}
+
+static nsRect
+FindScrollAnchorRect(nsIFrame* aScrollableFrame, nsIFrame* aAnchorNode)
+{
+ nsRect rect = aAnchorNode->GetContentRectRelativeToSelf();
+ rect.MoveBy(aAnchorNode->GetOffsetTo(aScrollableFrame->GetParent()));
+ return rect;
+}
+
+void
+ScrollFrameHelper::UpdateScrollAnchor()
+{
+ printf_stderr("\nnsGfxScrollFrame(%p)::UpdateScrollAnchor scrollport=(%d, %d)x(%d, %d)\n",
+ this,
+ mScrollPort.x,
+ mScrollPort.y,
+ mScrollPort.width,
+ mScrollPort.height);
+ MOZ_ASSERT(mScrolledFrame);
+
+ nsIFrame* oldAnchorNode = mAnchorNode;
+ for (nsIFrame* kid : mScrolledFrame->PrincipalChildList()) {
+ nsIFrame* candidate = SelectScrollAnchorImpl(kid);
+ if (candidate) {
+ mAnchorNode = candidate;
+ break;
+ }
+ }
+
+ if (oldAnchorNode != mAnchorNode) {
+ if (oldAnchorNode) {
+ UnmarkAsScrollAnchorNode(mScrolledFrame, oldAnchorNode);
+ // Make sure we stop highlighting the old anchor
+ oldAnchorNode->InvalidateFrame();
+ }
+ if (mAnchorNode) {
+ MarkAsScrollAnchorNode(mScrolledFrame, mAnchorNode);
+ // Make sure we start highlighting the new anchor
+ mAnchorNode->InvalidateFrame();
+ }
+ }
+
+ if (mAnchorNode) {
+ mLastAnchorPos = FindScrollAnchorRect(mOuter, mAnchorNode).TopLeft();
+ } else {
+ mLastAnchorPos = nsPoint();
+ }
+}
+
+nsIFrame*
+ScrollFrameHelper::SelectScrollAnchorImpl(nsIFrame* aNode)
+{
+ nsRect rect = FindScrollAnchorRect(mOuter, aNode);
+
+ bool excludedSubtree =
+ aNode->IsTransformed() ||
+ aNode->GetType() == mozilla::LayoutFrameType::Placeholder ||
+ aNode->GetType() == mozilla::LayoutFrameType::Inline ||
+ aNode->GetType() == mozilla::LayoutFrameType::Line ||
+ aNode->IsRelativelyPositioned(); // should exclude only sticky, this was easier
+ bool opaqueSubtree = aNode->GetType() == mozilla::LayoutFrameType::Scroll;
+
+ nsCString tag;
+ nsIFrame::ListTag(tag, aNode);
+
+ if (excludedSubtree) {
+ printf_stderr("Found excluded subtree [frame=%s]\n",
+ tag.get());
+ return nullptr;
+ }
+
+ bool fullyVisible = mScrollPort.Contains(rect) && !rect.IsEmpty();
+
+ // If this frame is fully visible then select it as the scroll anchor
+ if (fullyVisible) {
+ printf_stderr("Found contained frame [frame=%s rect=(%d, %d)x(%d, %d)]\n",
+ tag.get(),
+ rect.x,
+ rect.y,
+ rect.width,
+ rect.height);
+
+ return aNode;
+ }
+
+ // If this frame is partially visible then examine its children for a better
+ // scroll anchor
+ if (mScrollPort.Intersects(rect)) {
+ printf_stderr("Found intersected frame [frame=%s rect=(%d, %d)x(%d, %d)]\n",
+ tag.get(),
+ rect.x,
+ rect.y,
+ rect.width,
+ rect.height);
+
+ // This frame is opaque and can be a scroll anchor, but its contents should
+ // not be scroll anchors
+ if (opaqueSubtree) {
+ return aNode;
+ }
+
+ // Try to find a deeper frame that is fully visible in the scroll port
+ for (nsIFrame* kid : aNode->PrincipalChildList()) {
+ nsIFrame* candidate = SelectScrollAnchorImpl(kid);
+ if (candidate) {
+ printf_stderr("Using a closer child frame [frame=%p]\n", candidate);
+ return candidate;
+ }
+ }
+
+ printf_stderr("Fallback to intersected frame [frame=%s]\n",
+ tag.get());
+
+ return aNode;
+ }
+
+ return nullptr;
+}
+
/*
* this method wraps calls to ScrollToImpl(), either in one shot or incrementally,
* based on the setting of the smoothness scroll pref
@@ -2833,6 +2976,7 @@ ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsIAtom* aOri
mScrollGeneration = ++sScrollGenerationCounter;
ScrollVisual();
+ UpdateScrollAnchor();
bool schedulePaint = true;
if (nsLayoutUtils::AsyncPanZoomEnabled(mOuter) && gfxPrefs::APZPaintSkipping()) {
@@ -4342,6 +4486,7 @@ ScrollFrameHelper::ReloadChildFrames()
mVScrollbarBox = nullptr;
mScrollCornerBox = nullptr;
mResizerBox = nullptr;
+ mAnchorNode = nullptr;
for (nsIFrame* frame : mOuter->PrincipalChildList()) {
nsIContent* content = frame->GetContent();
@@ -6250,3 +6395,42 @@ ScrollFrameHelper::UsesContainerScrolling() const
return false;
}
+void
+ScrollFrameHelper::ScrollAnchorWillDestroy()
+{
+ if (mAnchorNode) {
+ UnmarkAsScrollAnchorNode(mScrolledFrame, mAnchorNode);
+ mAnchorNode = nullptr;
+ mLastAnchorPos = nsPoint();
+ // XXX Should we calculate a new one anchor here, or push a callback
+ // to do that at a safer time?
+ }
+}
+
+void
+ScrollFrameHelper::ApplyScrollAnchorOffsetAdjustment()
+{
+ MOZ_ASSERT(mAnchorNode);
+
+ nsPoint currentAnchorPos = FindScrollAnchorRect(mOuter, mAnchorNode).TopLeft();
+ nsPoint adjustment = currentAnchorPos - mLastAnchorPos;
+ nsIntPoint adjustmentDevicePixels = adjustment
+ .ToNearestPixels(mOuter->PresContext()->AppUnitsPerDevPixel());
+
+ printf_stderr("nsGfxScrollFrame(%p)::ApplyScrollAnchorOffsetAdjustment adjustment=(%d, %d)\n",
+ this,
+ adjustmentDevicePixels.x,
+ adjustmentDevicePixels.y);
+
+ nsIFrame* scrollAnchor = mAnchorNode;
+
+ ScrollBy(adjustmentDevicePixels,
+ nsIScrollableFrame::DEVICE_PIXELS,
+ nsIScrollableFrame::INSTANT,
+ nullptr,
+ nsGkAtoms::relative);
+
+ // The last scroll anchor offset should be updated within ScrollToImpl and
+ // the scroll anchor should not have changed
+ MOZ_ASSERT(scrollAnchor == mAnchorNode);
+} \ No newline at end of file
diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h
index 41957d5e55..49e22b9224 100644
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -458,6 +458,11 @@ public:
return mSuppressScrollbarRepaints;
}
+
+ void ScrollAnchorWillDestroy();
+
+ void ApplyScrollAnchorOffsetAdjustment();
+
// owning references to the nsIAnonymousContentCreator-built content
nsCOMPtr<nsIContent> mHScrollbarContent;
nsCOMPtr<nsIContent> mVScrollbarContent;
@@ -472,6 +477,7 @@ public:
nsIFrame* mScrolledFrame;
nsIFrame* mScrollCornerBox;
nsIFrame* mResizerBox;
+ nsIFrame* mAnchorNode;
nsContainerFrame* mOuter;
RefPtr<AsyncScroll> mAsyncScroll;
RefPtr<AsyncSmoothMSDScroll> mAsyncSmoothMSDScroll;
@@ -500,6 +506,11 @@ public:
// other than trying to restore mRestorePos.
nsPoint mLastPos;
+ // The last scroll position of the scroll anchor node. This is in the same
+ // coordinate space as mScrollPort, that is, relative to the parent of this
+ // scrollable frame.
+ nsPoint mLastAnchorPos;
+
nsExpirationState mActivityExpirationState;
nsCOMPtr<nsITimer> mScrollActivityTimer;
@@ -619,6 +630,9 @@ protected:
bool mOldSuppressValue;
};
+ void UpdateScrollAnchor();
+ nsIFrame* SelectScrollAnchorImpl(nsIFrame* aNode);
+
/**
* @note This method might destroy the frame, pres shell and other objects.
*/
@@ -1047,6 +1061,14 @@ public:
virtual mozilla::a11y::AccType AccessibleType() override;
#endif
+ virtual void ScrollAnchorWillDestroy() override {
+ mHelper.ScrollAnchorWillDestroy();
+ }
+
+ virtual void ApplyScrollAnchorOffsetAdjustment() override {
+ mHelper.ApplyScrollAnchorOffsetAdjustment();
+ }
+
protected:
nsHTMLScrollFrame(nsStyleContext* aContext, bool aIsRoot);
void SetSuppressScrollbarUpdate(bool aSuppress) {
@@ -1471,6 +1493,14 @@ public:
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif
+ virtual void ScrollAnchorWillDestroy() override {
+ mHelper.ScrollAnchorWillDestroy();
+ }
+
+ virtual void ApplyScrollAnchorOffsetAdjustment() override {
+ mHelper.ApplyScrollAnchorOffsetAdjustment();
+ }
+
protected:
nsXULScrollFrame(nsStyleContext* aContext, bool aIsRoot,
bool aClipAllDescendants);
diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h
index 1c5dbbe9ac..db95478bd0 100644
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -861,6 +861,7 @@ public:
} else {
mRect = aRect;
}
+ MaybeNotifyScrollAnchor();
}
/**
* Set this frame's rect from a logical rect in its own writing direction
@@ -922,6 +923,7 @@ public:
// the top right of the frame instead of the top left.
mRect.MoveTo(aPt.GetPhysicalPoint(aWritingMode,
aContainerSize - mRect.Size()));
+ MaybeNotifyScrollAnchor();
}
/**
@@ -2742,6 +2744,8 @@ public:
const nsRect* aFrameDamageRect = nullptr,
uint32_t aFlags = 0);
+ void MaybeNotifyScrollAnchor();
+
/**
* Returns a rect that encompasses everything that might be painted by
* this frame. This includes this frame, all its descendant frames, this
diff --git a/layout/generic/nsIScrollableFrame.h b/layout/generic/nsIScrollableFrame.h
index 4ad45a528d..96b19a1c78 100644
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -475,6 +475,9 @@ public:
virtual ScrollSnapInfo GetScrollSnapInfo() const = 0;
virtual void SetScrollsClipOnUnscrolledOutOfFlow() = 0;
+
+ virtual void ScrollAnchorWillDestroy() = 0;
+ virtual void ApplyScrollAnchorOffsetAdjustment() = 0;
};
#endif