commit 045b5b1cb3b67f4fc035b2958a18ffbf77966d31
parent 11ea3d17d5db71036cfcdb3e25ae7cb75372a7b5
Author: David Shin <dshin@mozilla.com>
Date: Thu, 30 Oct 2025 15:44:25 +0000
Bug 1968745: Pass short-lived cache into anchor resolution. r=jwatt
... So that we can store default anchor related data for a reflow to use for
determining if we need to compensate for scroll.
Differential Revision: https://phabricator.services.mozilla.com/D266466
Diffstat:
9 files changed, 85 insertions(+), 58 deletions(-)
diff --git a/layout/base/AnchorPositioningUtils.h b/layout/base/AnchorPositioningUtils.h
@@ -83,6 +83,24 @@ class AnchorPosReferenceData {
struct StylePositionArea;
class WritingMode;
+struct AnchorPosDefaultAnchorCache {
+ // Default anchor element's corresponding frame.
+ const nsIFrame* mAnchor = nullptr;
+ // Scroll container for the default anchor.
+ const nsIFrame* mScrollContainer = nullptr;
+};
+
+// Cache data used by anchor resolution. To be populated on abspos reflow,
+// whenever the frame makes any anchor reference.
+struct AnchorPosResolutionCache {
+ // Storage for referenced anchors. Designed to be long-lived (i.e. beyond
+ // a reflow cycle).
+ AnchorPosReferenceData* mReferenceData = nullptr;
+ // Cached data for default anchor resolution. Designed to be short-lived,
+ // so it can contain e.g. frame pointers.
+ AnchorPosDefaultAnchorCache mDefaultAnchorCache;
+};
+
enum class StylePositionTryFallbacksTryTacticKeyword : uint8_t;
using StylePositionTryFallbacksTryTactic =
CopyableTArray<StylePositionTryFallbacksTryTacticKeyword>;
diff --git a/layout/generic/AbsoluteContainingBlock.cpp b/layout/generic/AbsoluteContainingBlock.cpp
@@ -177,10 +177,12 @@ void AbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame,
aFlags.contains(AbsPosReflowFlag::CBHeightChanged);
nsOverflowContinuationTracker tracker(aDelegatingFrame, true);
for (nsIFrame* kidFrame : mAbsoluteFrames) {
- AnchorPosReferenceData* anchorPosReferenceData = nullptr;
+ Maybe<AnchorPosResolutionCache> anchorPosResolutionCache;
if (kidFrame->HasAnchorPosReference()) {
- anchorPosReferenceData = kidFrame->SetOrUpdateDeletableProperty(
- nsIFrame::AnchorPosReferences());
+ anchorPosResolutionCache = Some(AnchorPosResolutionCache{});
+ anchorPosResolutionCache->mReferenceData =
+ kidFrame->SetOrUpdateDeletableProperty(
+ nsIFrame::AnchorPosReferences());
} else {
kidFrame->RemoveProperty(nsIFrame::AnchorPosReferences());
}
@@ -188,7 +190,7 @@ void AbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame,
bool kidNeedsReflow =
reflowAll || kidFrame->IsSubtreeDirty() ||
FrameDependsOnContainer(kidFrame, cbWidthChanged, cbHeightChanged,
- anchorPosReferenceData);
+ anchorPosResolutionCache.ptrOr(nullptr));
if (kidFrame->IsSubtreeDirty()) {
MaybeMarkAncestorsAsHavingDescendantDependentOnItsStaticPos(
kidFrame, aDelegatingFrame);
@@ -229,7 +231,8 @@ void AbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame,
nsReflowStatus kidStatus;
ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowInput,
aContainingBlock, aFlags, kidFrame, kidStatus,
- aOverflowAreas, anchorPosReferenceData);
+ aOverflowAreas,
+ anchorPosResolutionCache.ptrOr(nullptr));
MOZ_ASSERT(!kidStatus.IsInlineBreakBefore(),
"ShouldAvoidBreakInside should prevent this from happening");
nsIFrame* nextFrame = kidFrame->GetNextInFlow();
@@ -305,7 +308,7 @@ static inline bool IsFixedOffset(const AnchorResolvedInset& aInset) {
bool AbsoluteContainingBlock::FrameDependsOnContainer(
nsIFrame* f, bool aCBWidthChanged, bool aCBHeightChanged,
- AnchorPosReferenceData* anchorPosReferenceData) {
+ AnchorPosResolutionCache* aAnchorPosResolutionCache) {
const nsStylePosition* pos = f->StylePosition();
// See if f's position might have changed because it depends on a
// placeholder's position.
@@ -320,7 +323,7 @@ bool AbsoluteContainingBlock::FrameDependsOnContainer(
const nsStyleMargin* margin = f->StyleMargin();
WritingMode wm = f->GetWritingMode();
const auto anchorResolutionParams =
- AnchorPosResolutionParams::From(f, anchorPosReferenceData);
+ AnchorPosResolutionParams::From(f, aAnchorPosResolutionCache);
if (wm.IsVertical() ? aCBHeightChanged : aCBWidthChanged) {
// See if f's inline-size might have changed.
// If margin-inline-start/end, padding-inline-start/end,
@@ -868,7 +871,7 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame(
const ReflowInput& aReflowInput, const nsRect& aOriginalContainingBlockRect,
AbsPosReflowFlags aFlags, nsIFrame* aKidFrame, nsReflowStatus& aStatus,
OverflowAreas* aOverflowAreas,
- AnchorPosReferenceData* aAnchorPosReferenceData) {
+ AnchorPosResolutionCache* aAnchorPosResolutionCache) {
MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
#ifdef DEBUG
@@ -939,7 +942,7 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame(
// TODO(emilio): Right now fallback only applies to position-area, which only
// makes a difference with a default anchor... Generalize it?
- if (aAnchorPosReferenceData) {
+ if (aAnchorPosResolutionCache) {
bool found = false;
uint32_t index = aKidFrame->GetProperty(
nsIFrame::LastSuccessfulPositionFallback(), &found);
@@ -967,9 +970,9 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame(
positionArea = currentFallback->AsPositionArea();
}
- if (!positionArea.IsNone()) {
+ if (!positionArea.IsNone() && aAnchorPosResolutionCache) {
const auto defaultAnchorInfo = AnchorPositioningUtils::GetDefaultAnchor(
- aKidFrame, false, aAnchorPosReferenceData);
+ aKidFrame, false, aAnchorPosResolutionCache->mReferenceData);
if (defaultAnchorInfo.mRect) {
return AnchorPositioningUtils::
AdjustAbsoluteContainingBlockRectForPositionArea(
@@ -1056,7 +1059,7 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame(
ReflowInput kidReflowInput(aPresContext, aReflowInput, aKidFrame,
availSize.ConvertTo(wm, outerWM),
Some(cbSize.ConvertTo(wm, outerWM)), initFlags,
- {}, {}, aAnchorPosReferenceData);
+ {}, {}, aAnchorPosResolutionCache);
if (nscoord kidAvailBSize = kidReflowInput.AvailableBSize();
kidAvailBSize != NS_UNCONSTRAINEDSIZE) {
@@ -1111,7 +1114,7 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame(
const auto anchorResolutionParams =
AnchorPosOffsetResolutionParams::ExplicitCBFrameSize(
AnchorPosResolutionParams::From(aKidFrame,
- aAnchorPosReferenceData),
+ aAnchorPosResolutionCache),
&cbSize);
const bool iInsetAuto =
stylePos
diff --git a/layout/generic/AbsoluteContainingBlock.h b/layout/generic/AbsoluteContainingBlock.h
@@ -110,7 +110,7 @@ class AbsoluteContainingBlock {
*/
bool FrameDependsOnContainer(
nsIFrame* aFrame, bool aCBWidthChanged, bool aCBHeightChanged,
- AnchorPosReferenceData* aAnchorPosReferenceData = nullptr);
+ mozilla::AnchorPosResolutionCache* aAnchorPosResolutionCache = nullptr);
/**
* After an abspos child's size is known, this method can be used to
@@ -145,14 +145,13 @@ class AbsoluteContainingBlock {
LogicalMargin& aMargin,
LogicalMargin& aOffsets);
- void ReflowAbsoluteFrame(nsIFrame* aDelegatingFrame,
- nsPresContext* aPresContext,
- const ReflowInput& aReflowInput,
- const nsRect& aOriginalContainingBlockRect,
- AbsPosReflowFlags aFlags, nsIFrame* aKidFrame,
- nsReflowStatus& aStatus,
- OverflowAreas* aOverflowAreas,
- AnchorPosReferenceData* aAnchorPosReferenceData);
+ void ReflowAbsoluteFrame(
+ nsIFrame* aDelegatingFrame, nsPresContext* aPresContext,
+ const ReflowInput& aReflowInput,
+ const nsRect& aOriginalContainingBlockRect, AbsPosReflowFlags aFlags,
+ nsIFrame* aKidFrame, nsReflowStatus& aStatus,
+ OverflowAreas* aOverflowAreas,
+ mozilla::AnchorPosResolutionCache* aAnchorPosResolutionCache = nullptr);
/**
* Mark our absolute frames dirty.
diff --git a/layout/generic/ReflowInput.cpp b/layout/generic/ReflowInput.cpp
@@ -109,10 +109,10 @@ static nscoord FontSizeInflationListMarginAdjustment(const nsIFrame* aFrame) {
SizeComputationInput::SizeComputationInput(
nsIFrame* aFrame, gfxContext* aRenderingContext,
- AnchorPosReferenceData* aAnchorPosReferenceData)
+ AnchorPosResolutionCache* aAnchorPosResolutionCache)
: mFrame(aFrame),
mRenderingContext(aRenderingContext),
- mAnchorPosReferenceData(aAnchorPosReferenceData),
+ mAnchorPosResolutionCache(aAnchorPosResolutionCache),
mWritingMode(aFrame->GetWritingMode()),
mIsThemed(aFrame->IsThemed()),
mComputedMargin(mWritingMode),
@@ -180,9 +180,9 @@ ReflowInput::ReflowInput(nsPresContext* aPresContext,
InitFlags aFlags,
const StyleSizeOverrides& aSizeOverrides,
ComputeSizeFlags aComputeSizeFlags,
- AnchorPosReferenceData* aAnchorPosReferenceData)
+ AnchorPosResolutionCache* aAnchorPosResolutionCache)
: SizeComputationInput(aFrame, aParentReflowInput.mRenderingContext,
- aAnchorPosReferenceData),
+ aAnchorPosResolutionCache),
mParentReflowInput(&aParentReflowInput),
mFloatManager(aParentReflowInput.mFloatManager),
mLineLayout(mFrame->IsLineParticipant() ? aParentReflowInput.mLineLayout
@@ -212,7 +212,7 @@ ReflowInput::ReflowInput(nsPresContext* aPresContext,
bool* aFixed = nullptr) -> nscoord {
nscoord limit = NS_UNCONSTRAINEDSIZE;
const auto* pos = aFrame->StylePosition();
- // Don't add to referenced anchors, since this function is called for
+ // Don't add to anchor resolution cache, since this function is called for
// other frames.
const auto anchorResolutionParams =
AnchorPosResolutionParams::From(aFrame);
@@ -365,13 +365,13 @@ nscoord SizeComputationInput::ComputeISizeValue(
contentEdgeToBoxSizing.ISize(wm);
return mFrame
- ->ComputeISizeValue(
- mRenderingContext, wm, aContainingBlockSize, contentEdgeToBoxSizing,
- boxSizingToMarginEdgeISize, aSize,
- *mFrame->StylePosition()->BSize(
- wm,
- AnchorPosResolutionParams::From(mFrame, mAnchorPosReferenceData)),
- mFrame->GetAspectRatio())
+ ->ComputeISizeValue(mRenderingContext, wm, aContainingBlockSize,
+ contentEdgeToBoxSizing, boxSizingToMarginEdgeISize,
+ aSize,
+ *mFrame->StylePosition()->BSize(
+ wm, AnchorPosResolutionParams::From(
+ mFrame, mAnchorPosResolutionCache)),
+ mFrame->GetAspectRatio())
.mISize;
}
@@ -527,7 +527,7 @@ void ReflowInput::Init(nsPresContext* aPresContext,
nsIFrame* containingBlk = mFrame;
while (containingBlk) {
const nsStylePosition* stylePos = containingBlk->StylePosition();
- // It's for containing block, so don't add to referenced anchors
+ // It's for containing block, so don't add to anchor resolution cache
const auto containingBlkAnchorResolutionParams =
AnchorPosResolutionParams::From(containingBlk);
const auto bSizeCoord =
@@ -2945,7 +2945,7 @@ bool SizeComputationInput::ComputeMargin(WritingMode aCBWM,
}
LogicalMargin m(aCBWM);
const auto anchorResolutionParams =
- AnchorPosResolutionParams::From(mFrame, mAnchorPosReferenceData);
+ AnchorPosResolutionParams::From(mFrame, mAnchorPosResolutionCache);
for (const LogicalSide side : LogicalSides::All) {
m.Side(side, aCBWM) = nsLayoutUtils::ComputeCBDependentValue(
aPercentBasis,
diff --git a/layout/generic/ReflowInput.h b/layout/generic/ReflowInput.h
@@ -68,8 +68,8 @@ struct SizeComputationInput {
// Rendering context to use for measurement.
gfxContext* mRenderingContext;
- // Cache of referenced anchors for this computation.
- AnchorPosReferenceData* mAnchorPosReferenceData = nullptr;
+ // Cache for anchor resolution in this computation.
+ AnchorPosResolutionCache* mAnchorPosResolutionCache = nullptr;
nsMargin ComputedPhysicalMargin() const {
return mComputedMargin.GetPhysicalMargin(mWritingMode);
@@ -132,7 +132,7 @@ struct SizeComputationInput {
// Callers using this constructor must call InitOffsets on their own.
SizeComputationInput(
nsIFrame* aFrame, gfxContext* aRenderingContext,
- AnchorPosReferenceData* aAnchorPosReferenceData = nullptr);
+ AnchorPosResolutionCache* aAnchorPosResolutionCache = nullptr);
SizeComputationInput(nsIFrame* aFrame, gfxContext* aRenderingContext,
WritingMode aContainingBlockWritingMode,
@@ -626,9 +626,9 @@ struct ReflowInput : public SizeComputationInput {
* call nsIFrame::ComputeSize() internally.
* @param aComputeSizeFlags A set of flags used when we call
* nsIFrame::ComputeSize() internally.
- * @param aAnchorPosReferenceData A cache of referenced anchors to be
- * populated (If specified) for this reflowed frame. Should live for the
- * lifetime of this ReflowInput.
+ * @param aAnchorResolutionCache A cache of referenced anchors to be populated
+ * (If specified) for this reflowed frame. Should live for the lifetime
+ * of this ReflowInput.
*/
ReflowInput(nsPresContext* aPresContext,
const ReflowInput& aParentReflowInput, nsIFrame* aFrame,
@@ -637,7 +637,7 @@ struct ReflowInput : public SizeComputationInput {
InitFlags aFlags = {},
const StyleSizeOverrides& aSizeOverrides = {},
ComputeSizeFlags aComputeSizeFlags = {},
- AnchorPosReferenceData* aAnchorPosReferenceData = nullptr);
+ AnchorPosResolutionCache* aAnchorPosResolutionCache = nullptr);
/**
* This method initializes various data members. It is automatically called by
@@ -985,7 +985,7 @@ inline AnchorPosResolutionParams AnchorPosResolutionParams::From(
aIgnorePositionArea ? mozilla::StylePositionArea{}
: aRI->mStylePosition->mPositionArea;
return {aRI->mFrame, aRI->mStyleDisplay->mPosition, posArea,
- aRI->mAnchorPosReferenceData};
+ aRI->mAnchorPosResolutionCache};
}
#endif // mozilla_ReflowInput_h
diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h
@@ -5921,9 +5921,9 @@ inline nsIFrame* nsFrameList::BackwardFrameTraversal::Prev(nsIFrame* aFrame) {
inline AnchorPosResolutionParams AnchorPosResolutionParams::From(
const nsIFrame* aFrame,
- mozilla::AnchorPosReferenceData* aAnchorPosReferenceData) {
+ mozilla::AnchorPosResolutionCache* aAnchorPosResolutionCache) {
return {aFrame, aFrame->StyleDisplay()->mPosition,
- aFrame->StylePosition()->mPositionArea, aAnchorPosReferenceData};
+ aFrame->StylePosition()->mPositionArea, aAnchorPosResolutionCache};
}
#endif /* nsIFrame_h___ */
diff --git a/layout/style/GeckoBindings.cpp b/layout/style/GeckoBindings.cpp
@@ -1897,6 +1897,14 @@ static Maybe<AnchorPosInfo> GetAnchorPosRect(
aCBRectIsvalid, entry);
}
+static AnchorPosReferenceData* GetReferenceData(
+ const AnchorPosResolutionParams& aParams) {
+ if (!aParams.mCache) {
+ return nullptr;
+ }
+ return aParams.mCache->mReferenceData;
+}
+
bool Gecko_GetAnchorPosOffset(const AnchorPosOffsetResolutionParams* aParams,
const nsAtom* aAnchorName,
StylePhysicalSide aPropSide,
@@ -1911,9 +1919,9 @@ bool Gecko_GetAnchorPosOffset(const AnchorPosOffsetResolutionParams* aParams,
// Note: No exit on null anchorName: Instead, GetAnchorPosRect may return the
// containing block.
- const auto info = GetAnchorPosRect(
- aParams->mBaseParams.mFrame, anchorName, !aParams->mCBSize,
- aParams->mBaseParams.mAnchorPosReferenceData);
+ const auto info = GetAnchorPosRect(aParams->mBaseParams.mFrame, anchorName,
+ !aParams->mCBSize,
+ GetReferenceData(aParams->mBaseParams));
if (info.isNothing()) {
return false;
}
@@ -2004,9 +2012,9 @@ bool Gecko_GetAnchorPosSize(const AnchorPosResolutionParams* aParams,
}
const auto size = [&]() -> Maybe<nsSize> {
Maybe<AnchorPosResolutionData>* entry = nullptr;
- if (aParams->mAnchorPosReferenceData) {
- const auto result =
- aParams->mAnchorPosReferenceData->InsertOrModify(anchorName, false);
+ auto* referencedAnchors = GetReferenceData(*aParams);
+ if (referencedAnchors) {
+ const auto result = referencedAnchors->InsertOrModify(anchorName, false);
if (result.mAlreadyResolved) {
MOZ_ASSERT(result.mEntry, "Entry exists but null?");
return result.mEntry->map(
diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h
@@ -43,7 +43,7 @@ namespace mozilla {
class ComputedStyle;
struct IntrinsicSize;
struct ReflowInput;
-class AnchorPosReferenceData;
+struct AnchorPosResolutionCache;
} // namespace mozilla
@@ -390,15 +390,14 @@ struct AnchorPosResolutionParams {
mozilla::StylePositionProperty mPosition;
// position-area property of the element in question.
mozilla::StylePositionArea mPositionArea;
- // Storage for anchor reference data. To be populated on abspos reflow,
- // whenever the frame makes any anchor reference.
- mozilla::AnchorPosReferenceData* const mAnchorPosReferenceData = nullptr;
+ // Cache data used for anchor resolution.
+ mozilla::AnchorPosResolutionCache* const mCache;
// Helper functions for creating anchor resolution parameters.
// Defined in corresponding header files.
static inline AnchorPosResolutionParams From(
const nsIFrame* aFrame,
- mozilla::AnchorPosReferenceData* aAnchorPosReferenceData = nullptr);
+ mozilla::AnchorPosResolutionCache* aAnchorPosResolutionCache = nullptr);
static inline AnchorPosResolutionParams From(
const mozilla::ReflowInput* aRI, bool aIgnorePositionArea = false);
static inline AnchorPosResolutionParams From(
diff --git a/servo/ports/geckolib/glue.rs b/servo/ports/geckolib/glue.rs
@@ -10710,7 +10710,7 @@ fn offset_params_from_base_params(
mFrame: params.mFrame,
mPosition: params.mPosition,
mPositionArea: params.mPositionArea,
- mAnchorPosReferenceData: params.mAnchorPosReferenceData,
+ mCache: params.mCache,
},
}
}