commit d82c74ec452de160807e56da6c1fde677edd6232
parent 8684c3bcd0ad2ff392214d702d8bb48fe556a820
Author: Jonathan Kew <jkew@mozilla.com>
Date: Fri, 2 Jan 2026 17:22:04 +0000
Bug 2006513 - Restore size as well as position of aKidFrame if no position-try fallback fits. r=layout-anchor-positioning-reviewers,layout-reviewers,emilio
Differential Revision: https://phabricator.services.mozilla.com/D277753
Diffstat:
3 files changed, 39 insertions(+), 34 deletions(-)
diff --git a/layout/generic/AbsoluteContainingBlock.cpp b/layout/generic/AbsoluteContainingBlock.cpp
@@ -1296,7 +1296,7 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame(
return SeekFallbackTo(Some(nextFallbackIndex));
};
- Maybe<nsPoint> firstTryNormalPosition;
+ Maybe<nsRect> firstTryNormalRect;
if (auto* lastSuccessfulPosition =
aKidFrame->GetProperty(nsIFrame::LastSuccessfulPositionFallback())) {
if (SeekFallbackTo(Some(lastSuccessfulPosition->mIndex))) {
@@ -1660,9 +1660,9 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame(
aKidFrame->DidReflow(aPresContext, &kidReflowInput);
[&]() {
- const auto position = aKidFrame->GetPosition();
- if (!firstTryNormalPosition) {
- firstTryNormalPosition = Some(position);
+ const auto rect = aKidFrame->GetRect();
+ if (!firstTryNormalRect) {
+ firstTryNormalRect = Some(rect);
}
if (!aAnchorPosResolutionCache) {
return;
@@ -1685,9 +1685,10 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame(
// Apply the hypothetical scroll offset.
// Set initial scroll position. TODO(dshin, bug 1987962): Need
// additional work for remembered scroll offset here.
- aKidFrame->SetProperty(nsIFrame::NormalPositionProperty(), position);
+ aKidFrame->SetProperty(nsIFrame::NormalPositionProperty(),
+ rect.TopLeft());
if (offset != nsPoint{}) {
- aKidFrame->SetPosition(position - offset);
+ aKidFrame->SetPosition(rect.TopLeft() - offset);
// Ensure that the positioned frame's overflow is updated. Absolutely
// containing block's overflow will be updated shortly below.
aKidFrame->UpdateOverflow();
@@ -1747,11 +1748,22 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame(
if (!TryAdvanceFallback()) {
// If there are no further fallbacks, we're done.
if (bestSize >= 0) {
- finalizing = true;
SeekFallbackTo(bestIndex);
} else {
- break;
+ // If we're going to roll back to the first try position, and the
+ // target's size was different, we need to do a "finalizing" reflow
+ // to ensure the inner layout is correct. If the size is unchanged,
+ // we can just break the fallback loop now.
+ if (isOverflowingCB && firstTryNormalRect &&
+ firstTryNormalRect->Size() != aKidFrame->GetSize()) {
+ SeekFallbackTo(firstTryIndex);
+ } else {
+ break;
+ }
}
+ // The fallback we've just selected is the final choice, regardless of
+ // whether it overflows.
+ finalizing = true;
}
// Try with the next fallback.
@@ -1760,7 +1772,7 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame(
} while (true);
[&]() {
- if (!isOverflowingCB || !firstTryNormalPosition) {
+ if (!isOverflowingCB || !firstTryNormalRect) {
return;
}
// We gave up applying fallbacks. Recover previous values, if changed, and
@@ -1768,45 +1780,42 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame(
// Because we rolled back to first try data, our cache should be up-to-date.
currentFallbackIndex = firstTryIndex;
currentFallbackStyle = firstTryStyle;
- const auto normalPosition = *firstTryNormalPosition;
+ const auto normalRect = *firstTryNormalRect;
const auto oldNormalPosition = aKidFrame->GetNormalPosition();
- if (normalPosition != oldNormalPosition) {
+ if (normalRect.TopLeft() != oldNormalPosition) {
aKidFrame->SetProperty(nsIFrame::NormalPositionProperty(),
- normalPosition);
+ normalRect.TopLeft());
}
- auto position = normalPosition;
+ auto rect = normalRect;
if (aAnchorPosResolutionCache) {
- position -=
- aAnchorPosResolutionCache->mReferenceData->mDefaultScrollShift;
+ rect.MoveBy(
+ -aAnchorPosResolutionCache->mReferenceData->mDefaultScrollShift);
}
if (isOverflowingCB &&
!aKidFrame->StylePosition()->mPositionArea.IsNone()) {
// The anchored element overflows the IMCB of its position-area. Would it
// have fit within the original CB? If so, shift it to stay within that.
- nsSize size = aKidFrame->GetSize();
- if (size.width <= aOriginalContainingBlockRect.width &&
- size.height <= aOriginalContainingBlockRect.height) {
- if (position.x < aOriginalContainingBlockRect.x) {
- position.x = aOriginalContainingBlockRect.x;
- } else if (position.x + size.width >
- aOriginalContainingBlockRect.XMost()) {
- position.x = aOriginalContainingBlockRect.XMost() - size.width;
+ if (rect.width <= aOriginalContainingBlockRect.width &&
+ rect.height <= aOriginalContainingBlockRect.height) {
+ if (rect.x < aOriginalContainingBlockRect.x) {
+ rect.x = aOriginalContainingBlockRect.x;
+ } else if (rect.XMost() > aOriginalContainingBlockRect.XMost()) {
+ rect.x = aOriginalContainingBlockRect.XMost() - rect.width;
}
- if (position.y < aOriginalContainingBlockRect.y) {
- position.y = aOriginalContainingBlockRect.y;
- } else if (position.y + size.height >
- aOriginalContainingBlockRect.YMost()) {
- position.y = aOriginalContainingBlockRect.YMost() - size.height;
+ if (rect.y < aOriginalContainingBlockRect.y) {
+ rect.y = aOriginalContainingBlockRect.y;
+ } else if (rect.YMost() > aOriginalContainingBlockRect.YMost()) {
+ rect.y = aOriginalContainingBlockRect.YMost() - rect.height;
}
}
}
const auto oldPosition = aKidFrame->GetPosition();
- if (position == oldPosition) {
+ if (rect.TopLeft() == oldPosition) {
return;
}
- aKidFrame->SetPosition(position);
+ aKidFrame->SetPosition(rect.TopLeft());
aKidFrame->UpdateOverflow();
}();
diff --git a/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-007.html.ini b/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-007.html.ini
@@ -3,4 +3,3 @@
expected:
if os == "android": PASS
FAIL
-
diff --git a/testing/web-platform/meta/css/css-anchor-position/position-try-001.html.ini b/testing/web-platform/meta/css/css-anchor-position/position-try-001.html.ini
@@ -1,3 +0,0 @@
-[position-try-001.html]
- [.target 6]
- expected: FAIL