commit ca2157db60a3ead8bf8a6c492009765463e06d9a
parent ca8b610fcb97a0dfec685afd9999a9b9979f0199
Author: David Shin <dshin@mozilla.com>
Date: Mon, 20 Oct 2025 20:03:23 +0000
Bug 1995280: Refactor position-area adjustment to take the anchor rect. r=layout-anchor-positioning-reviewers,layout-reviewers,emilio
... And separate out default anchor lookup to support it. This avoids having to
carry around a lot of frames and reference data into
`AdjustAbsoluteContainingBlockRectForPositionArea`.
Differential Revision: https://phabricator.services.mozilla.com/D269257
Diffstat:
3 files changed, 80 insertions(+), 58 deletions(-)
diff --git a/layout/base/AnchorPositioningUtils.cpp b/layout/base/AnchorPositioningUtils.cpp
@@ -781,49 +781,9 @@ static StylePositionArea ToPhysicalPositionArea(StylePositionArea aPosArea,
}
nsRect AnchorPositioningUtils::AdjustAbsoluteContainingBlockRectForPositionArea(
- nsIFrame* aPositionedFrame, nsIFrame* aContainingBlock,
- const nsRect& aCBRect, AnchorPosReferenceData* aAnchorPosReferenceData,
- const StylePositionArea& aPosArea,
+ const nsRect& aAnchorRect, const nsRect& aCBRect, WritingMode aPositionedWM,
+ WritingMode aCBWM, const StylePositionArea& aPosArea,
const StylePositionTryFallbacksTryTactic* aFallbackTactic) {
- // TODO: We need a single, unified way of getting the anchor, unifying
- // GetUsedAnchorName etc.
- const nsAtom* anchorName =
- AnchorPositioningUtils::GetUsedAnchorName(aPositionedFrame, nullptr);
- if (!anchorName) {
- return aCBRect;
- }
-
- nsRect anchorRect;
- MOZ_ASSERT_IF(aPositionedFrame->HasAnchorPosReference(),
- aAnchorPosReferenceData);
- const auto result = aAnchorPosReferenceData->InsertOrModify(anchorName, true);
- if (result.mAlreadyResolved) {
- MOZ_ASSERT(result.mEntry, "Entry exists but null?");
- if (result.mEntry->isNothing()) {
- return aCBRect;
- }
- const auto& data = result.mEntry->value();
- MOZ_ASSERT(data.mOrigin, "Missing anchor offset resolution.");
- anchorRect = nsRect{data.mOrigin.ref(), data.mSize};
- } else {
- Maybe<AnchorPosResolutionData>* entry = result.mEntry;
- PresShell* presShell = aPositionedFrame->PresShell();
- const auto* anchor =
- presShell->GetAnchorPosAnchor(anchorName, aPositionedFrame);
- 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 aCBRect;
- }
- const auto info = AnchorPositioningUtils::GetAnchorPosRect(
- aContainingBlock, anchor, false, entry);
- if (info.isNothing()) {
- return aCBRect;
- }
- anchorRect = info.ref().mRect;
- }
-
// Get the boundaries of 3x3 grid in CB's frame space. The edges of the
// default anchor box are clamped to the bounds of the CB, even if that
// results in zero width/height cells.
@@ -838,28 +798,26 @@ nsRect AnchorPositioningUtils::AdjustAbsoluteContainingBlockRectForPositionArea(
// | | | |
// ttbEdges[3] +------------+------------+------------+
- nscoord ltrEdges[4] = {aCBRect.x, anchorRect.x,
- anchorRect.x + anchorRect.width,
+ nscoord ltrEdges[4] = {aCBRect.x, aAnchorRect.x,
+ aAnchorRect.x + aAnchorRect.width,
aCBRect.x + aCBRect.width};
- nscoord ttbEdges[4] = {aCBRect.y, anchorRect.y,
- anchorRect.y + anchorRect.height,
+ nscoord ttbEdges[4] = {aCBRect.y, aAnchorRect.y,
+ aAnchorRect.y + aAnchorRect.height,
aCBRect.y + aCBRect.height};
ltrEdges[1] = std::clamp(ltrEdges[1], ltrEdges[0], ltrEdges[3]);
ltrEdges[2] = std::clamp(ltrEdges[2], ltrEdges[0], ltrEdges[3]);
ttbEdges[1] = std::clamp(ttbEdges[1], ttbEdges[0], ttbEdges[3]);
ttbEdges[2] = std::clamp(ttbEdges[2], ttbEdges[0], ttbEdges[3]);
- WritingMode cbWM = aContainingBlock->GetWritingMode();
- WritingMode posWM = aPositionedFrame->GetWritingMode();
-
nsRect res = aCBRect;
// PositionArea, resolved to only contain Left/Right/Top/Bottom values.
- StylePositionArea posArea = ToPhysicalPositionArea(aPosArea, cbWM, posWM);
+ StylePositionArea posArea =
+ ToPhysicalPositionArea(aPosArea, aCBWM, aPositionedWM);
if (aFallbackTactic) {
// See https://github.com/w3c/csswg-drafts/issues/12869 for which WM to use
// here.
- ApplyFallbackTactic(posArea, *aFallbackTactic, posWM);
+ ApplyFallbackTactic(posArea, *aFallbackTactic, aPositionedWM);
}
nscoord right = ltrEdges[3];
@@ -968,4 +926,43 @@ 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
@@ -106,9 +106,9 @@ struct AnchorPositioningUtils {
* https://drafts.csswg.org/css-anchor-position-1/#position-area
*/
static nsRect AdjustAbsoluteContainingBlockRectForPositionArea(
- nsIFrame* aPositionedFrame, nsIFrame* aContainingBlock,
- const nsRect& aCBRect, AnchorPosReferenceData* aAnchorPosReferenceData,
- const StylePositionArea& aPositionArea,
+ const nsRect& aAnchorRect, const nsRect& aCBRect,
+ WritingMode aPositionedWM, WritingMode aCBWM,
+ const StylePositionArea& aPosArea,
const StylePositionTryFallbacksTryTactic* aFallbackTactic);
/**
@@ -136,6 +136,26 @@ struct AnchorPositioningUtils {
* all other cases, returns null.
*/
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);
};
} // namespace mozilla
diff --git a/layout/generic/AbsoluteContainingBlock.cpp b/layout/generic/AbsoluteContainingBlock.cpp
@@ -960,10 +960,15 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame(
}
if (!positionArea.IsNone()) {
- return AnchorPositioningUtils::
- AdjustAbsoluteContainingBlockRectForPositionArea(
- aKidFrame, aDelegatingFrame, aOriginalContainingBlockRect,
- aAnchorPosReferenceData, positionArea, tactic);
+ const auto defaultAnchorInfo = AnchorPositioningUtils::GetDefaultAnchor(
+ aKidFrame, false, aAnchorPosReferenceData);
+ if (defaultAnchorInfo.mRect) {
+ return AnchorPositioningUtils::
+ AdjustAbsoluteContainingBlockRectForPositionArea(
+ *defaultAnchorInfo.mRect, aOriginalContainingBlockRect,
+ aKidFrame->GetWritingMode(),
+ aDelegatingFrame->GetWritingMode(), positionArea, tactic);
+ }
}
if (ViewportFrame* viewport = do_QueryFrame(aDelegatingFrame)) {