tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit e83327ce2280e6c0f44f1ec4a752c91aaedc99ff
parent 8a183125a82f2c1c1eb4cf2dce05c52f124d2719
Author: Botond Ballo <botond@mozilla.com>
Date:   Thu, 13 Nov 2025 04:59:03 +0000

Bug 1730749 - Create ASRs for sticky frames in BuildDisplayListForStackingContext. r=mstange

ASRs are only created for sticky frames inside an *active* scroll frame,
and only when painting to the window. This ensures a sticky ASR always
has a scroll ASR ancestor.

Differential Revision: https://phabricator.services.mozilla.com/D254147

Diffstat:
Mlayout/generic/nsIFrame.cpp | 46+++++++++++++++++++++++++++++-----------------
Mlayout/painting/nsDisplayList.cpp | 10+++++++++-
Mlayout/painting/nsDisplayList.h | 2++
3 files changed, 40 insertions(+), 18 deletions(-)

diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp @@ -3326,6 +3326,7 @@ void nsIFrame::BuildDisplayListForStackingContext( const bool useStickyPosition = disp->mPosition == StylePositionProperty::Sticky; + bool shouldFlattenStickyItem = true; const bool useFixedPosition = disp->mPosition == StylePositionProperty::Fixed && @@ -3364,7 +3365,7 @@ void nsIFrame::BuildDisplayListForStackingContext( // NOTE(emilio): The order of these RAII objects is quite subtle. nsDisplayListBuilder::AutoEnterViewTransitionCapture inViewTransitionCaptureSetter(aBuilder, capturedByViewTransition); - nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder); + RefPtr<const ActiveScrolledRoot> stickyASR = nullptr; nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder); if (aBuilder->IsInViewTransitionCapture()) { // View transition contents shouldn't scroll along our ASR. They get @@ -3372,6 +3373,28 @@ void nsIFrame::BuildDisplayListForStackingContext( // anyways). asrSetter.SetCurrentActiveScrolledRoot(nullptr); } + if (useStickyPosition) { + StickyScrollContainer* stickyScrollContainer = + StickyScrollContainer::GetOrCreateForFrame(this); + if (stickyScrollContainer) { + if (aBuilder->IsPaintingToWindow() && + stickyScrollContainer->ScrollContainer() + ->IsMaybeAsynchronouslyScrolled()) { + shouldFlattenStickyItem = false; + } + stickyScrollContainer->SetShouldFlatten(shouldFlattenStickyItem); + } + + if (shouldFlattenStickyItem) { + stickyASR = aBuilder->CurrentActiveScrolledRoot(); + } else { + stickyASR = aBuilder->AllocateActiveScrolledRootForSticky( + aBuilder->CurrentActiveScrolledRoot(), this); + asrSetter.SetCurrentActiveScrolledRoot(stickyASR); + } + } + + nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder); auto cssClip = GetClipPropClipRect(disp, effects, GetSize()); auto ApplyClipProp = [&](DisplayListClipState::AutoSaveRestore& aClipState) { @@ -3450,6 +3473,7 @@ void nsIFrame::BuildDisplayListForStackingContext( nsDisplayListCollection set(aBuilder); Maybe<nsRect> clipForMask; + { DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder); nsDisplayListBuilder::AutoInTransformSetter inTransformSetter(aBuilder, @@ -3895,28 +3919,16 @@ void nsIFrame::BuildDisplayListForStackingContext( // that on the display item as the "container ASR" (i.e. the normal ASR of // the container item, excluding the special behaviour induced by fixed // descendants). - const ActiveScrolledRoot* stickyASR = ActiveScrolledRoot::PickAncestor( + const ActiveScrolledRoot* stickyItemASR = ActiveScrolledRoot::PickAncestor( containerItemASR, aBuilder->CurrentActiveScrolledRoot()); auto* stickyItem = MakeDisplayItem<nsDisplayStickyPosition>( - aBuilder, this, &resultList, stickyASR, + aBuilder, this, &resultList, stickyItemASR, nsDisplayItem::ContainerASRType::AncestorOfContained, aBuilder->CurrentActiveScrolledRoot(), clipState.IsClippedToDisplayPort()); - bool shouldFlatten = true; - - StickyScrollContainer* stickyScrollContainer = - StickyScrollContainer::GetOrCreateForFrame(this); - if (stickyScrollContainer) { - if (stickyScrollContainer->ScrollContainer() - ->IsMaybeAsynchronouslyScrolled()) { - shouldFlatten = false; - } - stickyScrollContainer->SetShouldFlatten(shouldFlatten); - } - - stickyItem->SetShouldFlatten(shouldFlatten); + stickyItem->SetShouldFlatten(shouldFlattenStickyItem); resultList.AppendToTop(stickyItem); createdContainer = true; @@ -3924,7 +3936,7 @@ void nsIFrame::BuildDisplayListForStackingContext( // If the sticky element is inside a filter, annotate the scroll frame that // scrolls the filter as having out-of-flow content inside a filter (this // inhibits paint skipping). - if (aBuilder->GetFilterASR() && aBuilder->GetFilterASR() == stickyASR) { + if (aBuilder->GetFilterASR() && aBuilder->GetFilterASR() == stickyItemASR) { aBuilder->GetFilterASR() ->GetNearestScrollASR() ->ScrollFrame() diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp @@ -1503,6 +1503,14 @@ ActiveScrolledRoot* nsDisplayListBuilder::AllocateActiveScrolledRoot( return asr; } +ActiveScrolledRoot* nsDisplayListBuilder::AllocateActiveScrolledRootForSticky( + const ActiveScrolledRoot* aParent, nsIFrame* aStickyFrame) { + RefPtr<ActiveScrolledRoot> asr = ActiveScrolledRoot::CreateASRForStickyFrame( + aParent, aStickyFrame, IsRetainingDisplayList()); + mActiveScrolledRoots.AppendElement(asr); + return asr; +} + const DisplayItemClipChain* nsDisplayListBuilder::AllocateDisplayItemClipChain( const DisplayItemClip& aClip, const ActiveScrolledRoot* aASR, const DisplayItemClipChain* aParent) { @@ -5880,7 +5888,7 @@ bool nsDisplayStickyPosition::CreateWebRenderCommands( Maybe<wr::SpaceAndClipChainHelper> saccHelper; - if (stickyScrollContainer) { + if (stickyScrollContainer && !stickyScrollContainer->ShouldFlattenAway()) { float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); bool snap; diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h @@ -970,6 +970,8 @@ class nsDisplayListBuilder { ActiveScrolledRoot* AllocateActiveScrolledRoot( const ActiveScrolledRoot* aParent, ScrollContainerFrame* aScrollContainerFrame); + ActiveScrolledRoot* AllocateActiveScrolledRootForSticky( + const ActiveScrolledRoot* aParent, nsIFrame* aStickyFrame); /** * Allocate a new DisplayItemClipChain object in the arena. Will be cleaned