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:
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