tor-browser

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

commit 89b86fad2fafb90dcd329d06999393dab29af58a
parent 0aaf10f74f5189678fbfdd64e37888d7fe86c5e1
Author: Jonathan Kew <jkew@mozilla.com>
Date:   Sat, 27 Dec 2025 15:37:19 +0000

Bug 2006373 - Rework handling of last-successful-fallback. r=layout-anchor-positioning-reviewers,layout-reviewers,emilio

Author: Jonathan Kew <jkew@mozilla.com>
Date:   Tue Dec 23 13:47:40 2025 +0000

This brings our last-successful-fallback handling into line with the spec.
We get a couple of new test passes. There would be additional passing tests,
I believe (e.g. last-successful-basic.html) except that it's also affected
by bug 1924792.

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

Diffstat:
Mlayout/base/AnchorPositioningUtils.cpp | 3---
Mlayout/generic/AbsoluteContainingBlock.cpp | 60+++++++++++++++++++++++++++++++++++++++++++++---------------
Dtesting/web-platform/meta/css/css-anchor-position/last-successful-pseudo-element-basic.html.ini | 3---
Dtesting/web-platform/meta/css/css-anchor-position/last-successful-pseudo-element-fallbacks.html.ini | 3---
4 files changed, 45 insertions(+), 24 deletions(-)

diff --git a/layout/base/AnchorPositioningUtils.cpp b/layout/base/AnchorPositioningUtils.cpp @@ -986,9 +986,6 @@ static bool TriggerFallbackReflow(PresShell* aPresShell, nsIFrame* aPositioned, if (!needsRetry) { return false; } - // We want to retry from the first position; remove the last position - // property so all potential positions are re-evaluated. - aPositioned->RemoveProperty(nsIFrame::LastSuccessfulPositionFallback()); aPresShell->MarkPositionedFrameForReflow(aPositioned); return true; } diff --git a/layout/generic/AbsoluteContainingBlock.cpp b/layout/generic/AbsoluteContainingBlock.cpp @@ -1183,15 +1183,27 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( Maybe<uint32_t> currentFallbackIndex; const StylePositionTryFallbacksItem* currentFallback = nullptr; RefPtr<ComputedStyle> currentFallbackStyle; + RefPtr<ComputedStyle> firstTryStyle; + Maybe<uint32_t> firstTryIndex; - auto SeekFallbackTo = [&](uint32_t aIndex) -> bool { - if (aIndex >= fallbacks.Length()) { + // Set the current fallback to the given index, or reset to the base position + // if Nothing() is passed. + auto SeekFallbackTo = [&](Maybe<uint32_t> aIndex) -> bool { + if (!aIndex) { + currentFallbackIndex = Nothing(); + currentFallback = nullptr; + currentFallbackStyle = nullptr; + return true; + } + uint32_t index = *aIndex; + if (index >= fallbacks.Length()) { return false; } + const StylePositionTryFallbacksItem* nextFallback; RefPtr<ComputedStyle> nextFallbackStyle; while (true) { - nextFallback = &fallbacks[aIndex]; + nextFallback = &fallbacks[index]; nextFallbackStyle = aPresContext->StyleSet()->ResolvePositionTry( *aKidFrame->GetContent()->AsElement(), *aKidFrame->Style(), *nextFallback); @@ -1200,35 +1212,47 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( } // No @position-try rule for this name was found, per spec we should // skip it. - aIndex++; - if (aIndex >= fallbacks.Length()) { + index++; + if (index >= fallbacks.Length()) { return false; } } - currentFallbackIndex = Some(aIndex); + currentFallbackIndex = Some(index); currentFallback = nextFallback; currentFallbackStyle = std::move(nextFallbackStyle); return true; }; + // Advance to the next fallback to be tried. Normally this is simply the next + // index in the position-try-fallbacks list, but we have some special cases: + // - if we're currently at the last-successful fallback (recorded as + // firstTryIndex), we "advance" to the base position + // - we skip the last-successful fallback when we reach its position again auto TryAdvanceFallback = [&]() -> bool { if (fallbacks.IsEmpty()) { return false; } + if (firstTryIndex && currentFallbackIndex == firstTryIndex) { + return SeekFallbackTo(Nothing()); + } uint32_t nextFallbackIndex = currentFallbackIndex ? *currentFallbackIndex + 1 : 0; - return SeekFallbackTo(nextFallbackIndex); + if (firstTryIndex && nextFallbackIndex == *firstTryIndex) { + ++nextFallbackIndex; + } + return SeekFallbackTo(Some(nextFallbackIndex)); }; - Maybe<uint32_t> firstTryIndex; Maybe<nsPoint> firstTryNormalPosition; - const auto* lastSuccessfulPosition = - aKidFrame->GetProperty(nsIFrame::LastSuccessfulPositionFallback()); - if (lastSuccessfulPosition) { - if (!SeekFallbackTo(lastSuccessfulPosition->mIndex)) { - aKidFrame->RemoveProperty(nsIFrame::LastSuccessfulPositionFallback()); - } else { + if (auto* lastSuccessfulPosition = + aKidFrame->GetProperty(nsIFrame::LastSuccessfulPositionFallback())) { + if (SeekFallbackTo(Some(lastSuccessfulPosition->mIndex))) { + // Remember which fallback we're trying first; also record its style, + // in case we need to restore it later. firstTryIndex = Some(lastSuccessfulPosition->mIndex); + firstTryStyle = currentFallbackStyle; + } else { + aKidFrame->RemoveProperty(nsIFrame::LastSuccessfulPositionFallback()); } } @@ -1639,6 +1663,9 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( // didn't have any to try in the first place. isOverflowingCB = !fits; fallback.CommitCurrentFallback(); + if (currentFallbackIndex == Nothing()) { + aKidFrame->RemoveProperty(nsIFrame::LastSuccessfulPositionFallback()); + } break; } @@ -1657,8 +1684,11 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( !firstTryNormalPosition) { return; } - // We gave up applying fallbacks. Recover previous values, if changed. + // We gave up applying fallbacks. Recover previous values, if changed, and + // reset currentFallbackIndex/Style to match. // 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 oldNormalPosition = aKidFrame->GetNormalPosition(); if (normalPosition != oldNormalPosition) { diff --git a/testing/web-platform/meta/css/css-anchor-position/last-successful-pseudo-element-basic.html.ini b/testing/web-platform/meta/css/css-anchor-position/last-successful-pseudo-element-basic.html.ini @@ -1,3 +0,0 @@ -[last-successful-pseudo-element-basic.html] - [No successful position, keep flip-block] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-anchor-position/last-successful-pseudo-element-fallbacks.html.ini b/testing/web-platform/meta/css/css-anchor-position/last-successful-pseudo-element-fallbacks.html.ini @@ -1,3 +0,0 @@ -[last-successful-pseudo-element-fallbacks.html] - [No successful position, keep flip-block] - expected: FAIL