commit e83f5939e36abcb07415d85c3b04f5c3db05589a
parent f47abb0c30dff9ad971771ccfc5368f373111d80
Author: David Shin <dshin@mozilla.com>
Date: Thu, 30 Oct 2025 15:44:25 +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:
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;
}