tor-browser

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

commit 5ba984e00abbd0ad7aa40abb624865c586907a9a
parent dd74b4671e2e98ac808e2cccbeb3edb09f816bf4
Author: David Shin <dshin@mozilla.com>
Date:   Fri, 31 Oct 2025 02:19:03 +0000

Bug 1987963: Compute and cache compensating for scroll state of anchor positioned elements. r=jwatt

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

Diffstat:
Maccessible/base/nsCoreUtils.cpp | 2+-
Mlayout/base/AnchorPositioningUtils.cpp | 166++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Mlayout/base/AnchorPositioningUtils.h | 94++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mlayout/base/PresShell.cpp | 8++++----
Mlayout/generic/AbsoluteContainingBlock.cpp | 52++++++++++++++++++++++++++++++++--------------------
Mlayout/generic/nsIFrame.cpp | 13+++++++++++++
Mlayout/generic/nsIFrame.h | 2++
Mlayout/style/GeckoBindings.cpp | 128++++++++++++++++++-------------------------------------------------------------
8 files changed, 246 insertions(+), 219 deletions(-)

diff --git a/accessible/base/nsCoreUtils.cpp b/accessible/base/nsCoreUtils.cpp @@ -730,7 +730,7 @@ nsIFrame* nsCoreUtils::GetPositionedFrameForAnchor( continue; } const auto* data = referencedAnchors->Lookup(name.AsAtom()); - if (data && *data && data->ref().mOrigin) { + if (data && *data && data->ref().mOffsetData) { if (aAnchorFrame == aPresShell->GetAnchorPosAnchor(name.AsAtom(), frame)) { if (positionedFrame) { diff --git a/layout/base/AnchorPositioningUtils.cpp b/layout/base/AnchorPositioningUtils.cpp @@ -377,7 +377,7 @@ AnchorPosReferenceData::Result AnchorPosReferenceData::InsertOrModify( // Previous resolution may have been for size only, in which case another // anchor resolution is still required. - return {result->ref().mOrigin.isSome(), result}; + return {result->ref().mOffsetData.isSome(), result}; } const AnchorPosReferenceData::Value* AnchorPosReferenceData::Lookup( @@ -433,10 +433,16 @@ static const nsIFrame* TraverseUpToContainerChild(const nsIFrame* aContainer, } } -Maybe<AnchorPosInfo> AnchorPositioningUtils::GetAnchorPosRect( +static const nsIFrame* GetAnchorOf(const nsIFrame* aPositioned, + const nsAtom* aAnchorName) { + const auto* presShell = aPositioned->PresShell(); + MOZ_ASSERT(presShell, "No PresShell for frame?"); + return presShell->GetAnchorPosAnchor(aAnchorName, aPositioned); +} + +Maybe<nsRect> AnchorPositioningUtils::GetAnchorPosRect( const nsIFrame* aAbsoluteContainingBlock, const nsIFrame* aAnchor, - bool aCBRectIsvalid, - Maybe<AnchorPosResolutionData>* aReferencedAnchorsEntry) { + bool aCBRectIsvalid) { auto rect = [&]() -> Maybe<nsRect> { if (aCBRectIsvalid) { const nsRect result = @@ -477,23 +483,116 @@ Maybe<AnchorPosInfo> AnchorPositioningUtils::GetAnchorPosRect( const auto border = aAbsoluteContainingBlock->GetUsedBorder(); const nsPoint borderTopLeft{border.left, border.top}; const auto rect = aRect - borderTopLeft; - if (aReferencedAnchorsEntry) { + return rect; + }); +} + +Maybe<AnchorPosInfo> AnchorPositioningUtils::ResolveAnchorPosRect( + const nsIFrame* aPositioned, const nsIFrame* aAbsoluteContainingBlock, + const nsAtom* aAnchorName, bool aCBRectIsvalid, + AnchorPosResolutionCache* aResolutionCache) { + MOZ_ASSERT(aPositioned->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)); + MOZ_ASSERT(aPositioned->GetParent() == aAbsoluteContainingBlock); + + if (!aPositioned) { + return Nothing{}; + } + + const auto* anchorName = GetUsedAnchorName(aPositioned, aAnchorName); + if (!anchorName) { + return Nothing{}; + } + + Maybe<AnchorPosResolutionData>* entry = nullptr; + if (aResolutionCache) { + const auto result = + aResolutionCache->mReferenceData->InsertOrModify(anchorName, true); + if (result.mAlreadyResolved) { + MOZ_ASSERT(result.mEntry, "Entry exists but null?"); + return result.mEntry->map([&](const AnchorPosResolutionData& aData) { + MOZ_ASSERT(aData.mOffsetData, "Missing anchor offset resolution."); + const auto& offsetData = aData.mOffsetData.ref(); + return AnchorPosInfo{nsRect{offsetData.mOrigin, aData.mSize}, + offsetData.mCompensatesForScroll}; + }); + } + entry = result.mEntry; + } + + const auto* anchor = GetAnchorOf(aPositioned, anchorName); + if (!anchor) { + // If we have a cached entry, just check that it resolved to nothing last + // time as well. + MOZ_ASSERT_IF(entry, entry->isNothing()); + return Nothing{}; + } + + const auto result = + GetAnchorPosRect(aAbsoluteContainingBlock, anchor, aCBRectIsvalid); + return result.map([&](const nsRect& aRect) { + bool compensatesForScroll = false; + if (aResolutionCache) { + MOZ_ASSERT(entry); + // Update the cache. + compensatesForScroll = [&]() { + auto& defaultAnchorCache = aResolutionCache->mDefaultAnchorCache; + if (!aAnchorName) { + // Explicitly resolved default anchor for the first time - populate + // the cache. + defaultAnchorCache = AnchorPosDefaultAnchorCache{anchor}; + } + if (defaultAnchorCache.mAnchor == anchor) { + // This is referring to the default anchor, so scroll compensated by + // definition. + return true; + } + const auto* scrollContainer = + AnchorPositioningUtils::GetNearestScrollFrame(anchor); + return scrollContainer == + aResolutionCache->mDefaultAnchorCache.mScrollContainer; + }(); // If a partially resolved entry exists, make sure that it matches what we // have now. - MOZ_ASSERT_IF(*aReferencedAnchorsEntry, - aReferencedAnchorsEntry->ref().mSize == rect.Size()); - *aReferencedAnchorsEntry = Some(AnchorPosResolutionData{ - rect.Size(), - Some(rect.TopLeft()), + MOZ_ASSERT_IF(*entry, entry->ref().mSize == aRect.Size()); + *entry = Some(AnchorPosResolutionData{ + aRect.Size(), + Some(AnchorPosOffsetData{aRect.TopLeft(), compensatesForScroll}), }); } - return AnchorPosInfo{ - .mRect = rect, - .mContainingBlock = aAbsoluteContainingBlock, - }; + return AnchorPosInfo{aRect, compensatesForScroll}; }); } +Maybe<nsSize> AnchorPositioningUtils::ResolveAnchorPosSize( + const nsIFrame* aPositioned, const nsAtom* aAnchorName, + AnchorPosResolutionCache* aResolutionCache) { + const auto* anchorName = GetUsedAnchorName(aPositioned, aAnchorName); + if (!anchorName) { + return Nothing{}; + } + Maybe<AnchorPosResolutionData>* entry = nullptr; + auto* referencedAnchors = + aResolutionCache ? aResolutionCache->mReferenceData : nullptr; + if (referencedAnchors) { + const auto result = referencedAnchors->InsertOrModify(anchorName, false); + if (result.mAlreadyResolved) { + MOZ_ASSERT(result.mEntry, "Entry exists but null?"); + return result.mEntry->map( + [](const AnchorPosResolutionData& aData) { return aData.mSize; }); + } + entry = result.mEntry; + } + const auto* anchor = GetAnchorOf(aPositioned, anchorName); + if (!anchor) { + return Nothing{}; + } + const auto size = nsLayoutUtils::GetCombinedFragmentRects(anchor).Size(); + if (entry) { + *entry = Some(AnchorPosResolutionData{size, Nothing{}}); + } + return Some(size); +} + /** * Returns an equivalent StylePositionArea that contains: * [ @@ -667,43 +766,4 @@ const nsIFrame* AnchorPositioningUtils::GetAnchorPosImplicitAnchor( : pseudoRootFrame->GetParent(); } -AnchorPositioningUtils::DefaultAnchorInfo -AnchorPositioningUtils::GetDefaultAnchor( - const nsIFrame* aPositioned, bool aCBRectIsValid, - AnchorPosReferenceData* aAnchorPosReferenceData) { - const auto* presShell = aPositioned->PresShell(); - const auto* name = GetUsedAnchorName(aPositioned, nullptr); - if (!name) { - return {}; - } - - Maybe<AnchorPosResolutionData>* entry = nullptr; - if (aAnchorPosReferenceData) { - auto result = aAnchorPosReferenceData->InsertOrModify(name, true); - entry = result.mEntry; - if (result.mAlreadyResolved) { - return DefaultAnchorInfo{ - name, entry->map([](const AnchorPosResolutionData& aValue) { - MOZ_ASSERT(aValue.mOrigin, "Already resolved but no origin?"); - return nsRect{*aValue.mOrigin, aValue.mSize}; - })}; - } - } - - const auto* anchor = presShell->GetAnchorPosAnchor(name, aPositioned); - if (!anchor) { - return {name, Nothing{}}; - } - - const auto info = - GetAnchorPosRect(aPositioned->GetParent(), anchor, aCBRectIsValid, entry); - if (!info) { - NS_WARNING("Can find anchor frame but not rect (In multicol?)"); - // Behave as if anchor is invalid. - return {name, Nothing{}}; - } - - return {name, Some(info->mRect)}; -} - } // namespace mozilla diff --git a/layout/base/AnchorPositioningUtils.h b/layout/base/AnchorPositioningUtils.h @@ -7,6 +7,8 @@ #ifndef AnchorPositioningUtils_h__ #define AnchorPositioningUtils_h__ +#include "WritingModes.h" +#include "mozilla/EnumSet.h" #include "mozilla/Maybe.h" #include "nsRect.h" #include "nsTHashMap.h" @@ -23,41 +25,48 @@ class CopyableTArray; namespace mozilla { struct AnchorPosInfo { - // Border-box of the anchor frame, offset against `mContainingBlock`'s padding - // box. + // Border-box of the anchor frame, offset against the positioned frame's + // absolute containing block's padding box. nsRect mRect; - const nsIFrame* mContainingBlock; + // See `AnchorPosOffsetData::mCompensatesForScroll`. + bool mCompensatesForScroll; +}; + +struct AnchorPosOffsetData { + // Origin of the referenced anchor, w.r.t. containing block at the time of + // resolution. + nsPoint mOrigin; + // Does this anchor's offset compensate for scroll? + // https://drafts.csswg.org/css-anchor-position-1/#compensate-for-scroll + bool mCompensatesForScroll = false; }; // Resolved anchor positioning data. struct AnchorPosResolutionData { // Size of the referenced anchor. nsSize mSize; - // Origin of the referenced anchor, w.r.t. containing block at the time of - // resolution. Includes scroll offsets, for now. - // Nothing if the anchor did not resolve, or if the anchor was only referred - // to by its size. - mozilla::Maybe<nsPoint> mOrigin; + // Offset resolution data. Nothing if the anchor did not resolve, or if the + // anchor was only referred to by its size. + Maybe<AnchorPosOffsetData> mOffsetData; }; // Data required for an anchor positioned frame, including: // * If valid anchors are found, // * Cached offset/size resolution, if resolution was valid, -// * TODO(dshin, bug 1968745): Compensating for scroll [1] +// * Compensating for scroll [1] // * TODO(dshin, bug 1987962): Default scroll shift [2] // // [1]: https://drafts.csswg.org/css-anchor-position-1/#compensate-for-scroll // [2]: https://drafts.csswg.org/css-anchor-position-1/#default-scroll-shift class AnchorPosReferenceData { private: - using Map = + using ResolutionMap = nsTHashMap<RefPtr<const nsAtom>, mozilla::Maybe<AnchorPosResolutionData>>; public: - struct Empty {}; // Backup data for attempting a different `@position-try` style, when - // the default anchor remains the same. Empty, for now. - using PositionTryBackup = Empty; + // the default anchor remains the same. + using PositionTryBackup = mozilla::PhysicalAxes; using Value = mozilla::Maybe<AnchorPosResolutionData>; AnchorPosReferenceData() = default; @@ -77,17 +86,32 @@ class AnchorPosReferenceData { bool IsEmpty() const { return mMap.IsEmpty(); } - Map::const_iterator begin() const { return mMap.cbegin(); } - Map::const_iterator end() const { return mMap.cend(); } + ResolutionMap::const_iterator begin() const { return mMap.cbegin(); } + ResolutionMap::const_iterator end() const { return mMap.cend(); } + + void AdjustCompensatingForScroll(const mozilla::PhysicalAxes& aAxes) { + mCompensatingForScroll += aAxes; + } + + mozilla::PhysicalAxes CompensatingForScrollAxes() const { + return mCompensatingForScroll; + } PositionTryBackup TryPositionWithSameDefaultAnchor() { - return PositionTryBackup{}; + const auto compensatingForScroll = + std::exchange(mCompensatingForScroll, {}); + return compensatingForScroll; } - void UndoTryPositionWithSameDefaultAnchor(PositionTryBackup&&) {} + void UndoTryPositionWithSameDefaultAnchor(PositionTryBackup&& aBackup) { + mCompensatingForScroll = aBackup; + } private: - Map mMap; + ResolutionMap mMap; + // Axes we need to compensate for scroll [1] in. + // [1]: https://drafts.csswg.org/css-anchor-position-1/#compensate-for-scroll + mozilla::PhysicalAxes mCompensatingForScroll; }; struct StylePositionArea; @@ -160,10 +184,18 @@ struct AnchorPositioningUtils { const nsAtom* aName, const nsIFrame* aPositionedFrame, const nsTArray<nsIFrame*>& aPossibleAnchorFrames); - static Maybe<AnchorPosInfo> GetAnchorPosRect( + static Maybe<nsRect> GetAnchorPosRect( const nsIFrame* aAbsoluteContainingBlock, const nsIFrame* aAnchor, - bool aCBRectIsvalid, - Maybe<AnchorPosResolutionData>* aReferencedAnchorsEntry); + bool aCBRectIsvalid); + + static Maybe<AnchorPosInfo> ResolveAnchorPosRect( + const nsIFrame* aPositioned, const nsIFrame* aAbsoluteContainingBlock, + const nsAtom* aAnchorName, bool aCBRectIsvalid, + AnchorPosResolutionCache* aResolutionCache); + + static Maybe<nsSize> ResolveAnchorPosSize( + const nsIFrame* aPositioned, const nsAtom* aAnchorName, + AnchorPosResolutionCache* aResolutionCache); /** * Adjust the containing block rect for the 'position-area' property. @@ -200,26 +232,6 @@ struct AnchorPositioningUtils { */ static const nsIFrame* GetAnchorPosImplicitAnchor(const nsIFrame* aFrame); - struct DefaultAnchorInfo { - const nsAtom* mName = nullptr; - Maybe<nsRect> mRect; - }; - - /** - * Resolve the default anchor's rect, if the default anchor exists and is - * valid. - * - * @param aFrame The anchor positioned frame. - * @param aCBRectIsValid Whether or not the containing block's `GetRect` - * returns a valid result. This will be not be the case during the abspos - * frame's reflow. - * @param aAnchorPosReferenceData Anchor pos references to attempt to reuse & - * cache lookups to. - */ - static DefaultAnchorInfo GetDefaultAnchor( - const nsIFrame* aPositioned, bool aCBRectIsValid, - AnchorPosReferenceData* aAnchorPosReferenceData); - static const nsIFrame* GetNearestScrollFrame(const nsIFrame* aFrame); }; diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp @@ -11683,15 +11683,15 @@ static bool NeedReflowForAnchorPos( // Size changed, needs reflow. return true; } - if (!anchorReference.mOrigin) { + if (!anchorReference.mOffsetData) { // Didn't resolve offsets, no need to reflow based on it. return false; } const auto posInfo = AnchorPositioningUtils::GetAnchorPosRect( - aPositioned->GetParent(), aAnchor, true, nullptr); + aPositioned->GetParent(), aAnchor, true); MOZ_ASSERT(posInfo, "Can't resolve anchor rect?"); - const auto newOrigin = posInfo.ref().mRect.TopLeft(); - const auto& prevOrigin = anchorReference.mOrigin.ref(); + const auto newOrigin = posInfo.ref().TopLeft(); + const auto& prevOrigin = anchorReference.mOffsetData.ref().mOrigin; // Did the offset change? return newOrigin != prevOrigin; } diff --git a/layout/generic/AbsoluteContainingBlock.cpp b/layout/generic/AbsoluteContainingBlock.cpp @@ -155,6 +155,20 @@ static bool IsSnapshotContainingBlock(const nsIFrame* aFrame) { PseudoStyleType::mozSnapshotContainingBlock; } +static PhysicalAxes CheckEarlyCompensatingForScroll(const nsIFrame* aKidFrame) { + // Three conditions to compensate for scroll, once a default anchor + // exists: + // * Used alignment property is `anchor-center`, + // * `position-area` is not `none`, or + // * `anchor()` function refers to default anchor, or an anchor that + // shares the same scroller with it. + // Second condition is checkable right now, so do that. + if (!aKidFrame->StylePosition()->mPositionArea.IsNone()) { + return PhysicalAxes{PhysicalAxis::Horizontal, PhysicalAxis::Vertical}; + } + return PhysicalAxes{}; +} + static AnchorPosResolutionCache PopulateAnchorResolutionCache( const nsIFrame* aKidFrame, AnchorPosReferenceData* aData) { MOZ_ASSERT(aKidFrame->HasAnchorPosReference()); @@ -164,23 +178,15 @@ static AnchorPosResolutionCache PopulateAnchorResolutionCache( // counter-productive). This is a prerequisite for scroll compensation. We // also need to check for `anchor()` resolutions, so cache information for // default anchor and its scrollers right now. - AnchorPosDefaultAnchorCache defaultAnchorCache; - const auto* defaultAnchorName = - AnchorPositioningUtils::GetUsedAnchorName(aKidFrame, nullptr); - if (defaultAnchorName) { - const auto* anchor = aKidFrame->PresShell()->GetAnchorPosAnchor( - defaultAnchorName, aKidFrame); - defaultAnchorCache = AnchorPosDefaultAnchorCache{anchor}; - if (anchor) { - const auto entryData = aData->InsertOrModify(defaultAnchorName, false); - MOZ_ASSERT(!entryData.mAlreadyResolved); - // Put it in the cache with size resolved. - // TODO(dshin): May as well resolve offsets here? - *entryData.mEntry = - Some(AnchorPosResolutionData{anchor->GetSize(), Nothing{}}); - } + AnchorPosResolutionCache result{aData, {}}; + // Let this call populate the cache. + const auto defaultAnchorInfo = AnchorPositioningUtils::ResolveAnchorPosRect( + aKidFrame, aKidFrame->GetParent(), nullptr, false, &result); + if (defaultAnchorInfo) { + aData->AdjustCompensatingForScroll( + CheckEarlyCompensatingForScroll(aKidFrame)); } - return {aData, defaultAnchorCache}; + return result; } void AbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame, @@ -895,6 +901,10 @@ struct MOZ_STACK_CLASS MOZ_RAII AutoFallbackStyleSetter { } else { mOldCacheState = OldCacheState{aCache->TryPositionWithSameDefaultAnchor()}; + if (aCache->mDefaultAnchorCache.mAnchor) { + aCache->mReferenceData->AdjustCompensatingForScroll( + CheckEarlyCompensatingForScroll(aFrame)); + } } } } @@ -1039,12 +1049,14 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( } if (!positionArea.IsNone() && aAnchorPosResolutionCache) { - const auto defaultAnchorInfo = AnchorPositioningUtils::GetDefaultAnchor( - aKidFrame, false, aAnchorPosResolutionCache->mReferenceData); - if (defaultAnchorInfo.mRect) { + const auto defaultAnchorInfo = + AnchorPositioningUtils::ResolveAnchorPosRect( + aKidFrame, aDelegatingFrame, nullptr, false, + aAnchorPosResolutionCache); + if (defaultAnchorInfo) { return AnchorPositioningUtils:: AdjustAbsoluteContainingBlockRectForPositionArea( - *defaultAnchorInfo.mRect, aOriginalContainingBlockRect, + defaultAnchorInfo->mRect, aOriginalContainingBlockRect, aKidFrame->GetWritingMode(), aDelegatingFrame->GetWritingMode(), positionArea, &resolvedPositionArea); diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp @@ -12,6 +12,7 @@ #include <algorithm> +#include "AnchorPositioningUtils.h" #include "LayoutLogging.h" #include "RubyUtils.h" #include "TextOverflow.h" @@ -12267,6 +12268,18 @@ bool nsIFrame::IsSuppressedScrollableBlockForPrint() const { return true; } +PhysicalAxes nsIFrame::GetAnchorPosCompensatingForScroll() const { + if (!HasAnchorPosReference()) { + return {}; + } + const auto* prop = GetProperty(AnchorPosReferences()); + if (!prop) { + return {}; + } + + return prop->CompensatingForScrollAxes(); +} + bool nsIFrame::HasUnreflowedContainerQueryAncestor() const { // If this frame has done the first reflow, its ancestors are guaranteed to // have as well. diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h @@ -1451,6 +1451,8 @@ class nsIFrame : public nsQueryFrame { NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(LastSuccessfulPositionFallback, uint32_t); + mozilla::PhysicalAxes GetAnchorPosCompensatingForScroll() const; + // This tracks the start and end page value for a frame. // // https://www.w3.org/TR/css-page-3/#using-named-pages diff --git a/layout/style/GeckoBindings.cpp b/layout/style/GeckoBindings.cpp @@ -1845,66 +1845,6 @@ static bool AnchorSideUsesCBWM( return false; } -static const nsIFrame* GetAnchorOf(const nsIFrame* aPositioned, - const nsAtom* aAnchorName) { - MOZ_ASSERT(aPositioned, "Must have a positioned frame"); - const auto* presShell = aPositioned->PresShell(); - MOZ_ASSERT(presShell, "No PresShell for frame?"); - return presShell->GetAnchorPosAnchor(aAnchorName, aPositioned); -} - -static Maybe<AnchorPosInfo> GetAnchorPosRect( - const nsIFrame* aPositioned, const nsAtom* aAnchorName, bool aCBRectIsvalid, - AnchorPosReferenceData* aReferenceData) { - if (!aPositioned) { - return Nothing{}; - } - - const auto* anchorName = - AnchorPositioningUtils::GetUsedAnchorName(aPositioned, aAnchorName); - if (!anchorName) { - return Nothing{}; - } - - MOZ_ASSERT(aPositioned->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW), - "Calling GetAnchorPoseRect on non-abspos frame?"); - const auto* containingBlock = aPositioned->GetParent(); - - Maybe<AnchorPosResolutionData>* entry = nullptr; - - if (aReferenceData) { - const auto result = aReferenceData->InsertOrModify(anchorName, true); - if (result.mAlreadyResolved) { - MOZ_ASSERT(result.mEntry, "Entry exists but null?"); - return result.mEntry->map([&](const AnchorPosResolutionData& aData) { - MOZ_ASSERT(aData.mOrigin, "Missing anchor offset resolution."); - return AnchorPosInfo{nsRect{aData.mOrigin.ref(), aData.mSize}, - containingBlock}; - }); - } - entry = result.mEntry; - } - - const auto* anchor = GetAnchorOf(aPositioned, anchorName); - if (!anchor) { - // If we have a cached entry, just check that it resolved to nothing last - // time as well. - MOZ_ASSERT_IF(entry, entry->isNothing()); - return Nothing{}; - } - - return AnchorPositioningUtils::GetAnchorPosRect(containingBlock, anchor, - 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, @@ -1913,22 +1853,36 @@ bool Gecko_GetAnchorPosOffset(const AnchorPosOffsetResolutionParams* aParams, if (!aParams || !aParams->mBaseParams.mFrame) { return false; } - - const auto* anchorName = AnchorPositioningUtils::GetUsedAnchorName( - aParams->mBaseParams.mFrame, aAnchorName); - - // Note: No exit on null anchorName: Instead, GetAnchorPosRect may return the - // containing block. - const auto info = GetAnchorPosRect(aParams->mBaseParams.mFrame, anchorName, - !aParams->mCBSize, - GetReferenceData(aParams->mBaseParams)); - if (info.isNothing()) { + const auto* positioned = aParams->mBaseParams.mFrame; + const auto* containingBlock = positioned->GetParent(); + const auto info = AnchorPositioningUtils::ResolveAnchorPosRect( + positioned, containingBlock, aAnchorName, !aParams->mCBSize, + aParams->mBaseParams.mCache); + if (!info) { return false; } + if (info->mCompensatesForScroll && aParams->mBaseParams.mCache) { + // Without cache (Containing information on default anchor) being available, + // we woudln't be able to determine scroll compensation status. + const auto axis = [aPropSide]() { + switch (aPropSide) { + case StylePhysicalSide::Left: + case StylePhysicalSide::Right: + return PhysicalAxis::Horizontal; + case StylePhysicalSide::Top: + case StylePhysicalSide::Bottom: + break; + default: + MOZ_ASSERT_UNREACHABLE("Unhandled side?"); + } + return PhysicalAxis::Vertical; + }(); + aParams->mBaseParams.mCache->mReferenceData->AdjustCompensatingForScroll( + axis); + } // Compute the offset here in C++, where translating between physical/logical // coordinates is easier. - const auto& rect = info.ref().mRect; - const auto* containingBlock = info.ref().mContainingBlock; + const auto& rect = info->mRect; const auto usesCBWM = AnchorSideUsesCBWM(aAnchorSideKeyword); const auto cbwm = containingBlock->GetWritingMode(); const auto wm = @@ -2004,34 +1958,8 @@ bool Gecko_GetAnchorPosSize(const AnchorPosResolutionParams* aParams, return false; } const auto* positioned = aParams->mFrame; - - const auto* anchorName = - AnchorPositioningUtils::GetUsedAnchorName(positioned, aAnchorName); - if (!anchorName) { - return false; - } - const auto size = [&]() -> Maybe<nsSize> { - Maybe<AnchorPosResolutionData>* entry = nullptr; - 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( - [](const AnchorPosResolutionData& aData) { return aData.mSize; }); - } - entry = result.mEntry; - } - const auto* anchor = GetAnchorOf(positioned, anchorName); - if (!anchor) { - return Nothing{}; - } - const auto size = nsLayoutUtils::GetCombinedFragmentRects(anchor).Size(); - if (entry) { - *entry = Some(AnchorPosResolutionData{size, Nothing{}}); - } - return Some(size); - }(); + const auto size = AnchorPositioningUtils::ResolveAnchorPosSize( + positioned, aAnchorName, aParams->mCache); if (!size) { return false; }