diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index e7dffa51e0dc..c6d90f5c5cab 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -1311,7 +1311,8 @@ void PresShell::Destroy() { } mFramesToDirty.Clear(); - mDirtyScrollAnchorContainers.Clear(); + mPendingScrollAnchorSelection.Clear(); + mPendingScrollAnchorAdjustment.Clear(); if (mViewManager) { // Clear the view manager's weak pointer back to |this| in case it @@ -2151,7 +2152,8 @@ void PresShell::NotifyDestroyingFrame(nsIFrame* aFrame) { nsIScrollableFrame* scrollableFrame = do_QueryFrame(aFrame); if (scrollableFrame) { - mDirtyScrollAnchorContainers.RemoveEntry(scrollableFrame); + mPendingScrollAnchorSelection.RemoveEntry(scrollableFrame); + mPendingScrollAnchorAdjustment.RemoveEntry(scrollableFrame); } } } @@ -2574,17 +2576,32 @@ void PresShell::VerifyHasDirtyRootAncestor(nsIFrame* aFrame) { } #endif -void PresShell::PostDirtyScrollAnchorContainer(nsIScrollableFrame* aFrame) { - mDirtyScrollAnchorContainers.PutEntry(aFrame); +void PresShell::PostPendingScrollAnchorSelection( + mozilla::layout::ScrollAnchorContainer* aContainer) { + mPendingScrollAnchorSelection.PutEntry(aContainer->ScrollableFrame()); } -void PresShell::FlushDirtyScrollAnchorContainers() { - for (auto iter = mDirtyScrollAnchorContainers.Iter(); !iter.Done(); +void PresShell::FlushPendingScrollAnchorSelections() { + for (auto iter = mPendingScrollAnchorSelection.Iter(); !iter.Done(); iter.Next()) { nsIScrollableFrame* scroll = iter.Get()->GetKey(); scroll->GetAnchor()->SelectAnchor(); } - mDirtyScrollAnchorContainers.Clear(); + mPendingScrollAnchorSelection.Clear(); +} + +void PresShell::PostPendingScrollAnchorAdjustment( + ScrollAnchorContainer* aContainer) { + mPendingScrollAnchorAdjustment.PutEntry(aContainer->ScrollableFrame()); +} + +void PresShell::FlushPendingScrollAnchorAdjustments() { + for (auto iter = mPendingScrollAnchorAdjustment.Iter(); !iter.Done(); + iter.Next()) { + nsIScrollableFrame* scroll = iter.Get()->GetKey(); + scroll->GetAnchor()->ApplyAdjustments(); + } + mPendingScrollAnchorAdjustment.Clear(); } void PresShell::FrameNeedsReflow(nsIFrame* aFrame, @@ -4174,13 +4191,17 @@ void PresShell::DoFlushPendingNotifications(mozilla::ChangesToFlush aFlush) { didLayoutFlush = true; mFrameConstructor->RecalcQuotesAndCounters(); viewManager->FlushDelayedResize(true); - if (ProcessReflowCommands(flushType < FlushType::Layout) && - mContentToScrollTo) { - // We didn't get interrupted. Go ahead and scroll to our content - DoScrollContentIntoView(); + if (ProcessReflowCommands(flushType < FlushType::Layout)) { + // We didn't get interrupted. Go ahead and perform scroll anchor + // adjustments and scroll content into view + FlushPendingScrollAnchorAdjustments(); + if (mContentToScrollTo) { - mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling); - mContentToScrollTo = nullptr; + DoScrollContentIntoView(); + if (mContentToScrollTo) { + mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling); + mContentToScrollTo = nullptr; + } } } } @@ -8498,7 +8519,7 @@ bool PresShell::DoReflow(nsIFrame* target, bool aInterruptible, mReflowCause = nullptr; #endif - FlushDirtyScrollAnchorContainers(); + FlushPendingScrollAnchorSelections(); if (mReflowContinueTimer) { mReflowContinueTimer->Cancel(); @@ -8621,7 +8642,7 @@ bool PresShell::DoReflow(nsIFrame* target, bool aInterruptible, target->DidReflow(mPresContext, nullptr); if (target->IsInScrollAnchorChain()) { ScrollAnchorContainer* container = ScrollAnchorContainer::FindFor(target); - container->ApplyAdjustments(); + PostPendingScrollAnchorAdjustment(container); } if (isRoot && size.BSize(wm) == NS_UNCONSTRAINEDSIZE) { mPresContext->SetVisibleArea(boundsRelativeToTarget); @@ -10034,7 +10055,8 @@ void PresShell::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const { aSizes.mLayoutPresShellSize += mApproximatelyVisibleFrames.ShallowSizeOfExcludingThis(mallocSizeOf) + mFramesToDirty.ShallowSizeOfExcludingThis(mallocSizeOf) + - mDirtyScrollAnchorContainers.ShallowSizeOfExcludingThis(mallocSizeOf); + mPendingScrollAnchorSelection.ShallowSizeOfExcludingThis(mallocSizeOf) + + mPendingScrollAnchorAdjustment.ShallowSizeOfExcludingThis(mallocSizeOf); StyleSet()->AddSizeOfIncludingThis(aSizes); diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h index 955d076f1873..70e5044e0d0b 100644 --- a/layout/base/PresShell.h +++ b/layout/base/PresShell.h @@ -113,8 +113,12 @@ class PresShell final : public nsIPresShell, nsIPageSequenceFrame* GetPageSequenceFrame() const override; nsCanvasFrame* GetCanvasFrame() const override; - void PostDirtyScrollAnchorContainer(nsIScrollableFrame* aFrame) override; - void FlushDirtyScrollAnchorContainers() override; + void PostPendingScrollAnchorSelection( + mozilla::layout::ScrollAnchorContainer* aContainer) override; + void FlushPendingScrollAnchorSelections() override; + void PostPendingScrollAnchorAdjustment( + mozilla::layout::ScrollAnchorContainer* aContainer) override; + void FlushPendingScrollAnchorAdjustments(); void FrameNeedsReflow( nsIFrame* aFrame, IntrinsicDirty aIntrinsicDirty, nsFrameState aBitToAdd, @@ -747,7 +751,8 @@ class PresShell final : public nsIPresShell, // Set of frames that we should mark with NS_FRAME_HAS_DIRTY_CHILDREN after // we finish reflowing mCurrentReflowRoot. nsTHashtable> mFramesToDirty; - nsTHashtable> mDirtyScrollAnchorContainers; + nsTHashtable> mPendingScrollAnchorSelection; + nsTHashtable> mPendingScrollAnchorAdjustment; nsTArray> mDelayedEvents; diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 6b47b5ef20c8..e2320025646a 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -776,7 +776,7 @@ static bool RecomputePosition(nsIFrame* aFrame) { if (aFrame->IsInScrollAnchorChain()) { ScrollAnchorContainer* container = ScrollAnchorContainer::FindFor(aFrame); - container->ApplyAdjustments(); + aFrame->PresShell()->PostPendingScrollAnchorAdjustment(container); } return true; } @@ -885,7 +885,7 @@ static bool RecomputePosition(nsIFrame* aFrame) { if (aFrame->IsInScrollAnchorChain()) { ScrollAnchorContainer* container = ScrollAnchorContainer::FindFor(aFrame); - container->ApplyAdjustments(); + aFrame->PresShell()->PostPendingScrollAnchorAdjustment(container); } return true; } @@ -2972,7 +2972,7 @@ void RestyleManager::DoProcessPendingRestyles(ServoTraversalFlags aFlags) { // Select scroll anchors for frames that have been scrolled. Do this // before restyling so that anchor nodes are correctly marked for // scroll anchor update suppressions. - presContext->PresShell()->FlushDirtyScrollAnchorContainers(); + presContext->PresShell()->FlushPendingScrollAnchorSelections(); // Create a AnimationsWithDestroyedFrame during restyling process to // stop animations and transitions on elements that have no frame at the end diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index 1c1023f22f08..4282dd411a63 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -103,6 +103,10 @@ class Selection; class ShadowRoot; } // namespace dom +namespace layout { +class ScrollAnchorContainer; +} // namespace layout + namespace layers { class LayerManager; } // namespace layers @@ -456,8 +460,11 @@ class nsIPresShell : public nsStubDocumentObserver { */ virtual nsCanvasFrame* GetCanvasFrame() const = 0; - virtual void PostDirtyScrollAnchorContainer(nsIScrollableFrame* aFrame) = 0; - virtual void FlushDirtyScrollAnchorContainers() = 0; + virtual void PostPendingScrollAnchorSelection( + mozilla::layout::ScrollAnchorContainer* aContainer) = 0; + virtual void FlushPendingScrollAnchorSelections() = 0; + virtual void PostPendingScrollAnchorAdjustment( + mozilla::layout::ScrollAnchorContainer* aContainer) = 0; /** * Tell the pres shell that a frame needs to be marked dirty and needs diff --git a/layout/generic/ScrollAnchorContainer.cpp b/layout/generic/ScrollAnchorContainer.cpp index 069b22f02806..ee63572f0232 100644 --- a/layout/generic/ScrollAnchorContainer.cpp +++ b/layout/generic/ScrollAnchorContainer.cpp @@ -227,7 +227,7 @@ void ScrollAnchorContainer::InvalidateAnchor() { mAnchorNode = nullptr; mAnchorNodeIsDirty = true; mLastAnchorPos = nsPoint(); - Frame()->PresShell()->PostDirtyScrollAnchorContainer(ScrollableFrame()); + Frame()->PresShell()->PostPendingScrollAnchorSelection(this); } void ScrollAnchorContainer::Destroy() { diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 6277cddc5276..696b5f60802c 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -1136,7 +1136,7 @@ void nsHTMLScrollFrame::Reflow(nsPresContext* aPresContext, void nsHTMLScrollFrame::DidReflow(nsPresContext* aPresContext, const ReflowInput* aReflowInput) { nsContainerFrame::DidReflow(aPresContext, aReflowInput); - mHelper.mAnchor.ApplyAdjustments(); + PresShell()->PostPendingScrollAnchorAdjustment(GetAnchor()); } //////////////////////////////////////////////////////////////////////////////// diff --git a/testing/web-platform/meta/css/css-scroll-anchoring/anchoring-with-bounds-clamping-div.html.ini b/testing/web-platform/meta/css/css-scroll-anchoring/anchoring-with-bounds-clamping-div.html.ini new file mode 100644 index 000000000000..5fb47f102c5c --- /dev/null +++ b/testing/web-platform/meta/css/css-scroll-anchoring/anchoring-with-bounds-clamping-div.html.ini @@ -0,0 +1,3 @@ +[anchoring-with-bounds-clamping-div.html] + [Anchoring combined with scroll bounds clamping in a
.] + expected: FAIL diff --git a/testing/web-platform/tests/css/css-scroll-anchoring/heuristic-with-offset-update.html b/testing/web-platform/tests/css/css-scroll-anchoring/heuristic-with-offset-update.html new file mode 100644 index 000000000000..7fcbd983ed56 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-anchoring/heuristic-with-offset-update.html @@ -0,0 +1,58 @@ + + + + + + + + + + +
+
+
+
+
+
+ + + +