tor-browser

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

commit b135da1f086ed830a58c792e7fda21b0681fc2f7
parent 9383341578da9ef00e5050262269e3c92fee39be
Author: Serban Stanca <sstanca@mozilla.com>
Date:   Fri, 31 Oct 2025 02:07:55 +0200

Revert "Bug 1987963, Bug 1968745, Bug 1987961: Adjust WPT expectations. r=layout-anchor-positioning-reviewers,emilio" for causing wpt failures in position-try-fallbacks-003.html.

This reverts commit 30b907bc2066ae5d0a2e9f618dbddbed525b5582.

This reverts commit 6a497f0487da5f63d3bdc197b2f455a7e531a62b.

This reverts commit 31be694c9976a77e01c00093a893075633d5a189.

This reverts commit f61b13a337c132423da40b281846cda78ddff092.

This reverts commit 46693ba1b8b8bb06b30b5507b15e97ecc4723c7d.

This reverts commit 2ed02be9e7d033dd5f14229bba4fc104f0d2ea82.

This reverts commit e6479485e316c7f203a252f7492de0a0469e9a44.

This reverts commit e7f0af00dbe76b831d710e00e15a7b5631c0c984.

Diffstat:
Maccessible/base/nsCoreUtils.cpp | 2+-
Mlayout/base/AnchorPositioningUtils.cpp | 252+++++++++++++++++--------------------------------------------------------------
Mlayout/base/AnchorPositioningUtils.h | 201++++++++++++++-----------------------------------------------------------------
Mlayout/base/PresShell.cpp | 359+++++++++++++++++++++++++++++++++----------------------------------------------
Mlayout/base/PresShell.h | 2+-
Mlayout/generic/AbsoluteContainingBlock.cpp | 352+++++++++----------------------------------------------------------------------
Mlayout/generic/AbsoluteContainingBlock.h | 17+++++++++--------
Mlayout/generic/ReflowInput.cpp | 28++++++++++++++--------------
Mlayout/generic/ReflowInput.h | 16++++++++--------
Mlayout/generic/ScrollContainerFrame.cpp | 2+-
Mlayout/generic/nsIFrame.cpp | 21++++-----------------
Mlayout/generic/nsIFrame.h | 6++----
Mlayout/style/GeckoBindings.cpp | 120++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mlayout/style/nsStyleStruct.h | 9+++++----
Mservo/ports/geckolib/glue.rs | 2+-
Dtesting/web-platform/meta/css/css-anchor-position/anchor-scroll-chained-002.html.ini | 3---
Mtesting/web-platform/meta/css/css-anchor-position/anchor-scroll-chained-003.html.ini | 2+-
Dtesting/web-platform/meta/css/css-anchor-position/anchor-scroll-chained-004.html.ini | 4----
Atesting/web-platform/meta/css/css-anchor-position/anchor-scroll-fixedpos-004.html.ini | 2++
Atesting/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-003.html.ini | 3+++
Mtesting/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-006.html.ini | 3++-
Mtesting/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-007.html.ini | 5-----
Mtesting/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-008.html.ini | 5-----
Mtesting/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-009.html.ini | 1-
Mtesting/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-010.html.ini | 5+++--
Mtesting/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-015.tentative.html.ini | 2+-
Atesting/web-platform/meta/css/css-anchor-position/last-successful-pseudo-element-fallbacks.html.ini | 3+++
Atesting/web-platform/meta/css/css-anchor-position/position-area-in-position-try.html.ini | 6++++++
Mtesting/web-platform/meta/css/css-anchor-position/position-area-scrolling-002.tentative.html.ini | 3+++
Mtesting/web-platform/meta/css/css-anchor-position/position-area-scrolling-005.html.ini | 3+++
Mtesting/web-platform/meta/css/css-anchor-position/position-try-fallbacks-003.html.ini | 6++----
Mtesting/web-platform/meta/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container.html.ini | 3++-
Atesting/web-platform/meta/css/css-anchor-position/try-tactic-basic-anchor.html.ini | 6++++++
Atesting/web-platform/meta/css/css-highlight-api/target-text-text-decoration-001.html.ini | 3+++
Dtesting/web-platform/tests/css/css-anchor-position/anchor-scroll-update-008.html | 70----------------------------------------------------------------------
Dtesting/web-platform/tests/css/css-anchor-position/anchor-scroll-update-009.html | 70----------------------------------------------------------------------
Dtesting/web-platform/tests/css/css-anchor-position/anchor-scroll-update-010.html | 84-------------------------------------------------------------------------------
Dtesting/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-008-ref.html | 33---------------------------------
Dtesting/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-009-ref.html | 50--------------------------------------------------
Dtesting/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-010-ref.html | 60------------------------------------------------------------
40 files changed, 454 insertions(+), 1370 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().mOffsetData) { + if (data && *data && data->ref().mOrigin) { if (aAnchorFrame == aPresShell->GetAnchorPosAnchor(name.AsAtom(), frame)) { if (positionedFrame) { diff --git a/layout/base/AnchorPositioningUtils.cpp b/layout/base/AnchorPositioningUtils.cpp @@ -6,7 +6,6 @@ #include "AnchorPositioningUtils.h" -#include "ScrollContainerFrame.h" #include "mozilla/Maybe.h" #include "mozilla/PresShell.h" #include "mozilla/dom/Document.h" @@ -377,7 +376,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().mOffsetData.isSome(), result}; + return {result->ref().mOrigin.isSome(), result}; } const AnchorPosReferenceData::Value* AnchorPosReferenceData::Lookup( @@ -385,17 +384,6 @@ const AnchorPosReferenceData::Value* AnchorPosReferenceData::Lookup( return mMap.Lookup(aAnchorName).DataPtrOrNull(); } -AnchorPosDefaultAnchorCache::AnchorPosDefaultAnchorCache( - const nsIFrame* aAnchor, const nsIFrame* aScrollContainer) - : mAnchor{aAnchor}, mScrollContainer{aScrollContainer} { - MOZ_ASSERT_IF( - aAnchor, - nsLayoutUtils::GetNearestScrollContainerFrame( - const_cast<nsContainerFrame*>(aAnchor->GetParent()), - nsLayoutUtils::SCROLLABLE_SAME_DOC | - nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN) == mScrollContainer); -} - nsIFrame* AnchorPositioningUtils::FindFirstAcceptableAnchor( const nsAtom* aName, const nsIFrame* aPositionedFrame, const nsTArray<nsIFrame*>& aPossibleAnchorFrames) { @@ -438,22 +426,15 @@ static const nsIFrame* TraverseUpToContainerChild(const nsIFrame* aContainer, } } -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( +Maybe<AnchorPosInfo> AnchorPositioningUtils::GetAnchorPosRect( const nsIFrame* aAbsoluteContainingBlock, const nsIFrame* aAnchor, - bool aCBRectIsvalid) { + bool aCBRectIsvalid, + Maybe<AnchorPosResolutionData>* aReferencedAnchorsEntry) { auto rect = [&]() -> Maybe<nsRect> { if (aCBRectIsvalid) { const nsRect result = nsLayoutUtils::GetCombinedFragmentRects(aAnchor, true); - const auto offset = - aAnchor->GetOffsetToIgnoringScrolling(aAbsoluteContainingBlock); + const auto offset = aAnchor->GetOffsetTo(aAbsoluteContainingBlock); // Easy, just use the existing function. return Some(result + offset); } @@ -477,7 +458,7 @@ Maybe<nsRect> AnchorPositioningUtils::GetAnchorPosRect( // going to do it again here, which feels a little wasteful. const nsRect rectToContainerChild = nsLayoutUtils::GetCombinedFragmentRects(aAnchor, true); - const auto offset = aAnchor->GetOffsetToIgnoringScrolling(containerChild); + const auto offset = aAnchor->GetOffsetTo(containerChild); return Some(rectToContainerChild + offset + containerChild->GetPosition()); }(); return rect.map([&](const nsRect& aRect) { @@ -488,128 +469,23 @@ Maybe<nsRect> AnchorPositioningUtils::GetAnchorPosRect( const auto border = aAbsoluteContainingBlock->GetUsedBorder(); const nsPoint borderTopLeft{border.left, border.top}; const auto rect = aRect - borderTopLeft; - 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; - DistanceToNearestScrollContainer distanceToNearestScrollContainer; - 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.mAnchor = anchor; - const auto [scrollContainer, distance] = - AnchorPositioningUtils::GetNearestScrollFrame(anchor); - distanceToNearestScrollContainer = distance; - defaultAnchorCache.mScrollContainer = scrollContainer; - aResolutionCache->mReferenceData->mDistanceToDefaultScrollContainer = - distance; - aResolutionCache->mReferenceData->mDefaultAnchorName = anchorName; - // This is the default anchor, so scroll compensated by definition. - return true; - } - if (defaultAnchorCache.mAnchor == anchor) { - // This is referring to the default anchor, so scroll compensated by - // definition. - return true; - } - const auto [scrollContainer, distance] = - AnchorPositioningUtils::GetNearestScrollFrame(anchor); - distanceToNearestScrollContainer = distance; - return scrollContainer == - aResolutionCache->mDefaultAnchorCache.mScrollContainer; - }(); + if (aReferencedAnchorsEntry) { // If a partially resolved entry exists, make sure that it matches what we // have now. - MOZ_ASSERT_IF(*entry, entry->ref().mSize == aRect.Size()); - *entry = Some(AnchorPosResolutionData{ - aRect.Size(), - Some(AnchorPosOffsetData{aRect.TopLeft(), compensatesForScroll, - distanceToNearestScrollContainer}), + MOZ_ASSERT_IF(*aReferencedAnchorsEntry, + aReferencedAnchorsEntry->ref().mSize == rect.Size()); + *aReferencedAnchorsEntry = Some(AnchorPosResolutionData{ + rect.Size(), + Some(rect.TopLeft()), }); } - return AnchorPosInfo{aRect, compensatesForScroll}; + return AnchorPosInfo{ + .mRect = rect, + .mContainingBlock = aAbsoluteContainingBlock, + }; }); } -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: * [ @@ -704,59 +580,6 @@ nsRect AnchorPositioningUtils::AdjustAbsoluteContainingBlockRectForPositionArea( return res; } -AnchorPositioningUtils::NearestScrollFrameInfo -AnchorPositioningUtils::GetNearestScrollFrame(const nsIFrame* aFrame) { - if (!aFrame) { - return {nullptr, {}}; - } - uint32_t distance = 1; - // `GetNearestScrollContainerFrame` will return the incoming frame if it's a - // scroll frame, so nudge to parent. - for (const nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) { - if (f->IsScrollContainerOrSubclass()) { - return {f, DistanceToNearestScrollContainer{distance}}; - } - distance++; - } - return {nullptr, {}}; -} - -nsPoint AnchorPositioningUtils::GetScrollOffsetFor( - PhysicalAxes aAxes, const nsIFrame* aPositioned, - const AnchorPosDefaultAnchorCache& aDefaultAnchorCache) { - MOZ_ASSERT(aPositioned); - if (!aDefaultAnchorCache.mAnchor || aAxes.isEmpty()) { - return nsPoint{}; - } - nsPoint offset; - const bool trackHorizontal = aAxes.contains(PhysicalAxis::Horizontal); - const bool trackVertical = aAxes.contains(PhysicalAxis::Vertical); - // TODO(dshin, bug 1991489): Traverse properly, in case anchor and positioned - // elements are in different continuation frames of the absolute containing - // block. - const auto* absoluteContainingBlock = aPositioned->GetParent(); - if (GetNearestScrollFrame(aPositioned).mScrollContainer == - aDefaultAnchorCache.mScrollContainer) { - // Would scroll together anyway, skip. - return nsPoint{}; - } - // Grab the accumulated offset up to, but not including, the abspos - // container. - for (const auto* f = aDefaultAnchorCache.mScrollContainer; - f && f != absoluteContainingBlock; f = f->GetParent()) { - if (const ScrollContainerFrame* scrollFrame = do_QueryFrame(f)) { - const auto o = scrollFrame->GetScrollPosition(); - if (trackHorizontal) { - offset.x += o.x; - } - if (trackVertical) { - offset.y += o.y; - } - } - } - return offset; -} - // Out of line to avoid having to include AnchorPosReferenceData from nsIFrame.h void DeleteAnchorPosReferenceData(AnchorPosReferenceData* aData) { delete aData; @@ -822,12 +645,43 @@ const nsIFrame* AnchorPositioningUtils::GetAnchorPosImplicitAnchor( : pseudoRootFrame->GetParent(); } -bool AnchorPositioningUtils::FitsInContainingBlock( - const nsRect& aOverflowCheckRect, - const nsRect& aOriginalContainingBlockSize, const nsRect& aRect) { - return aOverflowCheckRect.Intersect(aOriginalContainingBlockSize) - .Union(aOriginalContainingBlockSize) - .Contains(aRect); +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,8 +7,6 @@ #ifndef AnchorPositioningUtils_h__ #define AnchorPositioningUtils_h__ -#include "WritingModes.h" -#include "mozilla/EnumSet.h" #include "mozilla/Maybe.h" #include "nsRect.h" #include "nsTHashMap.h" @@ -25,79 +23,37 @@ class CopyableTArray; namespace mozilla { struct AnchorPosInfo { - // Border-box of the anchor frame, offset against the positioned frame's - // absolute containing block's padding box. + // Border-box of the anchor frame, offset against `mContainingBlock`'s padding + // box. nsRect mRect; - // See `AnchorPosOffsetData::mCompensatesForScroll`. - bool mCompensatesForScroll; -}; - -class DistanceToNearestScrollContainer { - public: - DistanceToNearestScrollContainer() : mDistance{kInvalid} {} - explicit DistanceToNearestScrollContainer(uint32_t aDistance) - : mDistance{aDistance} {} - bool Valid() const { return mDistance != kInvalid; } - bool operator==(const DistanceToNearestScrollContainer& aOther) const { - return mDistance == aOther.mDistance; - } - bool operator!=(const DistanceToNearestScrollContainer& aOther) const { - return !(*this == aOther); - } - - private: - // 0 is invalid - a frame itself cannot be its own nearest scroll container. - static constexpr uint32_t kInvalid = 0; - // Ancestor hops to the nearest scroll container. Note that scroll containers - // between abspos/fixedpos frames and their containing blocks are irrelevant, - // so the distance should be measured from the out-of-flow frame, not the - // placeholder frame. - uint32_t mDistance; -}; - -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; - // Distance to this anchor's nearest scroll container. - DistanceToNearestScrollContainer mDistanceToNearestScrollContainer; + const nsIFrame* mContainingBlock; }; // Resolved anchor positioning data. struct AnchorPosResolutionData { // Size of the referenced anchor. nsSize mSize; - // Offset resolution data. Nothing if the anchor did not resolve, or if the - // anchor was only referred to by its size. - Maybe<AnchorPosOffsetData> mOffsetData; + // 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; }; // Data required for an anchor positioned frame, including: // * If valid anchors are found, // * Cached offset/size resolution, if resolution was valid, -// * Compensating for scroll [1] -// * Default scroll shift [2] +// * TODO(dshin, bug 1968745): 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 ResolutionMap = + using Map = nsTHashMap<RefPtr<const nsAtom>, mozilla::Maybe<AnchorPosResolutionData>>; public: - // Backup data for attempting a different `@position-try` style, when - // the default anchor remains the same. - // These entries correspond 1:1 to that of `AnchorPosReferenceData`. - struct PositionTryBackup { - mozilla::PhysicalAxes mCompensatingForScroll; - nsPoint mDefaultScrollShift; - nsRect mContainingBlockRect; - bool mUseScrollableContainingBlock = false; - }; using Value = mozilla::Maybe<AnchorPosResolutionData>; AnchorPosReferenceData() = default; @@ -117,103 +73,16 @@ class AnchorPosReferenceData { bool IsEmpty() const { return mMap.IsEmpty(); } - 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() { - auto compensatingForScroll = std::exchange(mCompensatingForScroll, {}); - auto defaultScrollShift = std::exchange(mDefaultScrollShift, {}); - auto insetModifiedContainingBlock = std::exchange(mContainingBlockRect, {}); - return {compensatingForScroll, defaultScrollShift, - insetModifiedContainingBlock}; - } - - void UndoTryPositionWithSameDefaultAnchor(PositionTryBackup&& aBackup) { - mCompensatingForScroll = aBackup.mCompensatingForScroll; - mDefaultScrollShift = aBackup.mDefaultScrollShift; - mContainingBlockRect = aBackup.mContainingBlockRect; - } - - // Distance from the default anchor to the nearest scroll container. - DistanceToNearestScrollContainer mDistanceToDefaultScrollContainer; - // https://drafts.csswg.org/css-anchor-position-1/#default-scroll-shift - nsPoint mDefaultScrollShift; - // Rect of containing block before being inset-modified, at the time of - // resolution. - nsRect mContainingBlockRect; - // TODO(dshin, bug 1987962): Remembered scroll offset - // https://drafts.csswg.org/css-anchor-position-1/#remembered-scroll-offset - // Name of the default used anchor. Not necessarily positioned frame's - // style, because of fallbacks. - RefPtr<const nsAtom> mDefaultAnchorName; + Map::const_iterator begin() const { return mMap.cbegin(); } + Map::const_iterator end() const { return mMap.cend(); } private: - 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; + Map mMap; }; struct StylePositionArea; class WritingMode; -struct AnchorPosDefaultAnchorCache { - // Default anchor element's corresponding frame. - const nsIFrame* mAnchor = nullptr; - // Scroll container for the default anchor. - const nsIFrame* mScrollContainer = nullptr; - - AnchorPosDefaultAnchorCache() = default; - AnchorPosDefaultAnchorCache(const nsIFrame* aAnchor, - const nsIFrame* aScrollContainer); -}; - -// Cache data used by anchor resolution. To be populated on abspos reflow, -// whenever the frame makes any anchor reference. -struct AnchorPosResolutionCache { - // Storage for referenced anchors. Designed to be long-lived (i.e. beyond - // a reflow cycle). - AnchorPosReferenceData* mReferenceData = nullptr; - // Cached data for default anchor resolution. Designed to be short-lived, - // so it can contain e.g. frame pointers. - AnchorPosDefaultAnchorCache mDefaultAnchorCache; - - // Backup data for attempting a different `@position-try` style, when - // the default anchor remains the same. - using PositionTryBackup = AnchorPosReferenceData::PositionTryBackup; - PositionTryBackup TryPositionWithSameDefaultAnchor() { - return mReferenceData->TryPositionWithSameDefaultAnchor(); - } - void UndoTryPositionWithSameDefaultAnchor(PositionTryBackup&& aBackup) { - mReferenceData->UndoTryPositionWithSameDefaultAnchor(std::move(aBackup)); - } - - // Backup data for attempting a different `@position-try` style, when - // the default anchor changes. - using PositionTryFullBackup = - std::pair<AnchorPosReferenceData, AnchorPosDefaultAnchorCache>; - PositionTryFullBackup TryPositionWithDifferentDefaultAnchor() { - auto referenceData = std::move(*mReferenceData); - *mReferenceData = {}; - return std::make_pair( - std::move(referenceData), - std::exchange(mDefaultAnchorCache, AnchorPosDefaultAnchorCache{})); - } - void UndoTryPositionWithDifferentDefaultAnchor( - PositionTryFullBackup&& aBackup) { - *mReferenceData = std::move(aBackup.first); - std::exchange(mDefaultAnchorCache, aBackup.second); - } -}; - enum class StylePositionTryFallbacksTryTacticKeyword : uint8_t; using StylePositionTryFallbacksTryTactic = CopyableTArray<StylePositionTryFallbacksTryTacticKeyword>; @@ -233,18 +102,10 @@ struct AnchorPositioningUtils { const nsAtom* aName, const nsIFrame* aPositionedFrame, const nsTArray<nsIFrame*>& aPossibleAnchorFrames); - static Maybe<nsRect> GetAnchorPosRect( + static Maybe<AnchorPosInfo> GetAnchorPosRect( const nsIFrame* aAbsoluteContainingBlock, const nsIFrame* aAnchor, - 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); + bool aCBRectIsvalid, + Maybe<AnchorPosResolutionData>* aReferencedAnchorsEntry); /** * Adjust the containing block rect for the 'position-area' property. @@ -281,19 +142,25 @@ struct AnchorPositioningUtils { */ static const nsIFrame* GetAnchorPosImplicitAnchor(const nsIFrame* aFrame); - struct NearestScrollFrameInfo { - const nsIFrame* mScrollContainer = nullptr; - DistanceToNearestScrollContainer mDistance; + struct DefaultAnchorInfo { + const nsAtom* mName = nullptr; + Maybe<nsRect> mRect; }; - static NearestScrollFrameInfo GetNearestScrollFrame(const nsIFrame* aFrame); - static nsPoint GetScrollOffsetFor( - PhysicalAxes aAxes, const nsIFrame* aPositioned, - const AnchorPosDefaultAnchorCache& aDefaultAnchorCache); - - static bool FitsInContainingBlock(const nsRect& aOverflowCheckRect, - const nsRect& aOriginalContainingBlockSize, - const nsRect& aRect); + /** + * 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/base/PresShell.cpp b/layout/base/PresShell.cpp @@ -11683,34 +11683,19 @@ static bool NeedReflowForAnchorPos( // Size changed, needs reflow. return true; } - if (!anchorReference.mOffsetData) { + if (!anchorReference.mOrigin) { // Didn't resolve offsets, no need to reflow based on it. return false; } - - const auto nearestScrollFrameInfo = - AnchorPositioningUtils::GetNearestScrollFrame(aAnchor); - if (anchorReference.mOffsetData->mDistanceToNearestScrollContainer != - nearestScrollFrameInfo.mDistance) { - // Scroll container relationship changed, need to reflow. - return true; - } - const auto posInfo = AnchorPositioningUtils::GetAnchorPosRect( - aPositioned->GetParent(), aAnchor, true); + aPositioned->GetParent(), aAnchor, true, nullptr); MOZ_ASSERT(posInfo, "Can't resolve anchor rect?"); - const auto newOrigin = posInfo.ref().TopLeft(); - const auto& prevOrigin = anchorReference.mOffsetData.ref().mOrigin; + const auto newOrigin = posInfo.ref().mRect.TopLeft(); + const auto& prevOrigin = anchorReference.mOrigin.ref(); // Did the offset change? return newOrigin != prevOrigin; } -struct DefaultAnchorInfo { - const nsAtom* mName; - const nsIFrame* mAnchor; - DistanceToNearestScrollContainer mDistanceToNearestScrollContainer; -}; - PresShell::AnchorPosUpdateResult PresShell::UpdateAnchorPosLayout() { if (mAnchorPosPositioned.IsEmpty()) { return AnchorPosUpdateResult::NotApplicable; @@ -11737,237 +11722,191 @@ PresShell::AnchorPosUpdateResult PresShell::UpdateAnchorPosLayout() { // Already marked for reflow. continue; } - const auto defaultAnchorInfo = [&]() -> Maybe<DefaultAnchorInfo> { - const auto* anchorName = - AnchorPositioningUtils::GetUsedAnchorName(positioned, nullptr); - if (!anchorName) { - return Nothing{}; - } + for (const auto& kv : *anchorPosReferenceData) { + const auto& data = kv.GetData(); + const auto& anchorName = kv.GetKey(); const auto* anchor = GetAnchorPosAnchor(anchorName, positioned); - if (!anchor) { - return Nothing{}; - } - const auto nearestScrollFrame = - AnchorPositioningUtils::GetNearestScrollFrame(anchor); - return Some( - DefaultAnchorInfo{anchorName, anchor, nearestScrollFrame.mDistance}); - }(); - bool shouldReflow = false; - if (defaultAnchorInfo && - defaultAnchorInfo->mDistanceToNearestScrollContainer != - anchorPosReferenceData->mDistanceToDefaultScrollContainer) { - // Default anchor's nearest scroller changed, reflow. - shouldReflow = true; - } else { - const auto GetAnchor = - [&](const nsAtom* aName, - const nsIFrame* aPositioned) -> const nsIFrame* { - if (!defaultAnchorInfo) { - return GetAnchorPosAnchor(aName, aPositioned); - } - const auto* defaultAnchorName = defaultAnchorInfo->mName; - if (aName != defaultAnchorName) { - return GetAnchorPosAnchor(aName, aPositioned); - } - return defaultAnchorInfo->mAnchor; - }; - for (const auto& kv : *anchorPosReferenceData) { - const auto& data = kv.GetData(); - const auto& anchorName = kv.GetKey(); - const auto* anchor = GetAnchor(anchorName, positioned); - if (NeedReflowForAnchorPos(anchor, positioned, data)) { - shouldReflow = true; - break; - } + if (NeedReflowForAnchorPos(anchor, positioned, data)) { + result = AnchorPosUpdateResult::NeedReflow; + // Abspos frames should not affect ancestor intrinsics. + FrameNeedsReflow(positioned, IntrinsicDirty::None, + NS_FRAME_HAS_DIRTY_CHILDREN); } } - if (shouldReflow) { - result = AnchorPosUpdateResult::NeedReflow; - // Abspos frames should not affect ancestor intrinsics. - FrameNeedsReflow(positioned, IntrinsicDirty::None, - NS_FRAME_HAS_DIRTY_CHILDREN); - } } return result; } -using AffectedAnchor = AnchorPosDefaultAnchorCache; -struct AffectedAnchorGroup { - const nsAtom* mAnchorName; - nsTArray<AffectedAnchor> mFrames; -}; +static ScrollContainerFrame* FindScrollContainerFrameOf( + const nsIFrame* aFrame) { + MOZ_ASSERT(aFrame, "NULL frame for FindScrollContainerFrameOf()"); + auto* parent = aFrame->GetParent(); + return nsLayoutUtils::GetNearestScrollContainerFrame( + parent, nsLayoutUtils::SCROLLABLE_SAME_DOC | + nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN); +} -static nsTArray<AffectedAnchorGroup> FindAnchorsAffectedByScroll( - const nsTHashMap<RefPtr<const nsAtom>, nsTArray<nsIFrame*>>& aAnchors, - const ScrollContainerFrame* aScrollContainer) { - const auto AffectedByScrollContainer = - [](const nsIFrame* aFrame, const ScrollContainerFrame* aScrollContainer) { - MOZ_ASSERT(aFrame); - MOZ_ASSERT(aScrollContainer); - return aFrame == aScrollContainer || - nsLayoutUtils::IsProperAncestorFrame(aScrollContainer, aFrame); - }; +static bool UnderScrollContainer(nsIFrame* aFrame, + const ScrollContainerFrame* aScrollContainer) { + MOZ_ASSERT(aFrame); + MOZ_ASSERT(aScrollContainer); + return aFrame == aScrollContainer || + nsLayoutUtils::IsProperAncestorFrame(aScrollContainer, aFrame); +} + +void PresShell::UpdateAnchorPosLayoutForScroll( + ScrollContainerFrame* aScrollContainer) { + if (mAnchorPosAnchors.IsEmpty()) { + return; + } + AUTO_PROFILER_MARKER_UNTYPED("UpdateAnchorPosLayoutForScroll", LAYOUT, {}); + // TODO(dshin, bug 1923401): What follows is a non-spec compliant + // implementation of scroll handling for anchor positioning. Specifically, it + // will compensate for any offset between anchor and all positioned elements, + // instead of compensating in axes anchoring to elements in the same scroller + // as the default anchor. As a result, positioned elements will resize to + // stay attached to all anchors. This needs to be adressed after we stop + // storing anchor offset that includes scroll offsets. + // TODO(dshin, bug 1987463): After bug 1923401, we still need this code path + // to update the scroll offset, so it's worth investigating further + // optimizations. + struct AffectedAnchor { + const nsIFrame* mFrame; + ScrollContainerFrame* mNearestScrollContainer; + }; + struct AffectedAnchorGroup { + const nsAtom* mAnchorName; + nsTArray<AffectedAnchor> mFrames; + }; + struct Comparator { + bool Equals(const AffectedAnchor& aEntry, const nsIFrame* aFrame) const { + return aEntry.mFrame == aFrame; + } + }; + + // First, find all anchors under this scroll container. Can look at positioned + // frames' anchor references first, but we want to avoid anchor lookups if we + // can. nsTArray<AffectedAnchorGroup> affectedAnchors; - // We keep only referenced anchors' name in positioned frames to avoid dealing - // with lifetime issues associated with it. Now we need to re-establish that - // association. - for (const auto& kv : aAnchors) { + for (const auto& kv : mAnchorPosAnchors) { const auto& anchorFrames = kv.GetData(); Maybe<nsTArray<AffectedAnchor>> affected; for (const auto& frame : anchorFrames) { - const auto* scrollContainer = - AnchorPositioningUtils::GetNearestScrollFrame(frame).mScrollContainer; - if (!scrollContainer) { - // Fixed-pos anchor, likely + auto* nearestScrollFrame = FindScrollContainerFrameOf(frame); + if (!nearestScrollFrame) { + // Fixed-pos anchor. continue; } - // Does this scroll container match a anchor's nearest scroll container, - // or contain it? - if (!AffectedByScrollContainer(scrollContainer, aScrollContainer)) { + if (!UnderScrollContainer(nearestScrollFrame, aScrollContainer)) { continue; } if (affected.isNothing()) { affected = Some(nsTArray<AffectedAnchor>{anchorFrames.Length()}); } - affected.ref().AppendElement(AffectedAnchor{frame, scrollContainer}); + affected.ref().AppendElement(AffectedAnchor{frame, nearestScrollFrame}); } if (affected.isSome()) { affectedAnchors.AppendElement( AffectedAnchorGroup{kv.GetKey(), std::move(*affected)}); } } - return affectedAnchors; -} -struct FindScrollCompensatedAnchorResult { - const AffectedAnchor& mAnchorInfo; - AnchorPosReferenceData& mReferenceData; -}; - -// Given a list of anchors affected by scrolling, find one that the given -// positioned frame need to compensate scroll for. -static Maybe<FindScrollCompensatedAnchorResult> FindScrollCompensatedAnchor( - const PresShell* aPresShell, - const nsTArray<AffectedAnchorGroup>& aAffectedAnchors, - const nsIFrame* aPositioned) { - MOZ_ASSERT(aPositioned->IsAbsolutelyPositioned(), - "Anchor positioned frame is not absolutely positioned?"); - auto* referencedAnchors = - aPositioned->GetProperty(nsIFrame::AnchorPosReferences()); - if (!referencedAnchors || referencedAnchors->IsEmpty()) { - return Nothing{}; - } - - if (!referencedAnchors->mDefaultAnchorName) { - return Nothing{}; - } - - const auto compensatingForScroll = - referencedAnchors->CompensatingForScrollAxes(); - if (compensatingForScroll.isEmpty()) { - return Nothing{}; + if (affectedAnchors.IsEmpty()) { + return; } - struct Comparator { - bool Equals(const AffectedAnchor& aEntry, const nsIFrame* aFrame) const { - return aEntry.mAnchor == aFrame; + // Now, find positioned frames that depend on anchors under the scroll frame. + for (auto* positioned : mAnchorPosPositioned) { + MOZ_ASSERT(positioned->IsAbsolutelyPositioned(), + "Anchor positioned frame is not absolutely positioned?"); + if (positioned->HasAnyStateBits(NS_FRAME_IS_DIRTY)) { + // Already dirty? Skip. + continue; } - }; - // Find the relevant default anchor. - const auto* defaultAnchorName = - AnchorPositioningUtils::GetUsedAnchorName(aPositioned, nullptr); - nsIFrame const* defaultAnchor = nullptr; - for (const auto& group : aAffectedAnchors) { - if (defaultAnchorName && - !group.mAnchorName->Equals(defaultAnchorName->GetUTF16String(), - defaultAnchorName->GetLength())) { - // Default anchor has a name, and it's different from this affected - // anchor group. + const auto* anchorPosReferenceData = + positioned->GetProperty(nsIFrame::AnchorPosReferences()); + if (!anchorPosReferenceData || anchorPosReferenceData->IsEmpty()) { + // If it doesn't reference any anchors then it doesn't compensate for + // scroll. continue; } - if (!defaultAnchor) { - defaultAnchor = - aPresShell->GetAnchorPosAnchor(defaultAnchorName, aPositioned); - if (!defaultAnchor) { - // Default anchor not valid for this positioned frame. - return Nothing{}; - } + + const nsAtom* defaultAnchorName = + AnchorPositioningUtils::GetUsedAnchorName(positioned, nullptr); + if (!defaultAnchorName) { + continue; } - const auto& anchors = group.mFrames; - // Find the affected anchor that not only matches in name, but in actual - // frame. - const auto idx = anchors.IndexOf(defaultAnchor, 0, Comparator{}); - if (idx == anchors.NoIndex) { - // We found the default anchor, but it wasn't correct. - break; + // We might not need to do this GetAnchorPosAnchor call at all if none of + // the affected anchors are referenced by positioned below. We could + // improve this. + const nsIFrame* defaultAnchorFrame = + GetAnchorPosAnchor(defaultAnchorName, positioned); + if (!defaultAnchorFrame) { + continue; } - const auto& info = anchors.ElementAt(idx); - return Some(FindScrollCompensatedAnchorResult{info, *referencedAnchors}); - } + auto* nearestScrollToDefaultAnchor = + FindScrollContainerFrameOf(defaultAnchorFrame); - return Nothing{}; -} - -static bool CheckOverflow(nsIFrame* aPositioned, const nsPoint& aOffset, - const AnchorPosReferenceData& aData) { - const auto* stylePos = aPositioned->StylePosition(); - const auto hasFallbacks = !stylePos->mPositionTryFallbacks._0.IsEmpty(); - const auto visibilityDependsOnOverflow = - stylePos->mPositionVisibility == StylePositionVisibility::NO_OVERFLOW; - if (!hasFallbacks && !visibilityDependsOnOverflow) { - return false; - } - const auto* cb = aPositioned->GetParent(); - MOZ_ASSERT(cb); - const auto overflows = !AnchorPositioningUtils::FitsInContainingBlock( - aData.mContainingBlockRect - aOffset, cb->GetPaddingRect(), - aPositioned->GetRect()); - aPositioned->AddOrRemoveStateBits(NS_FRAME_POSITION_VISIBILITY_HIDDEN, - visibilityDependsOnOverflow && overflows); - return hasFallbacks && overflows; -} - -// https://drafts.csswg.org/css-anchor-position-1/#default-scroll-shift -void PresShell::UpdateAnchorPosForScroll( - const ScrollContainerFrame* aScrollContainer) { - if (mAnchorPosAnchors.IsEmpty()) { - return; - } - - AUTO_PROFILER_MARKER_UNTYPED("UpdateAnchorPosForScroll", LAYOUT, {}); - - // First, find all anchors under this scroll container. Can look at positioned - // frames' anchor references first, but we want to avoid anchor lookups if we - // can. - nsTArray<AffectedAnchorGroup> affectedAnchors = - FindAnchorsAffectedByScroll(mAnchorPosAnchors, aScrollContainer); + auto* absoluteContainingBlock = positioned->GetParent(); - if (affectedAnchors.IsEmpty()) { - return; - } + if (UnderScrollContainer(absoluteContainingBlock, + nearestScrollToDefaultAnchor)) { + // If the positioned element's containing block is under the only possible + // anchor scroll container that it can scroll with, they'll scroll + // together without intervention, so skip the update. + continue; + } - // Now, update all affected positioned elements' scroll offsets. - for (auto* positioned : mAnchorPosPositioned) { - const auto scrollDependency = - FindScrollCompensatedAnchor(this, affectedAnchors, positioned); - if (!scrollDependency) { + const auto* stylePos = positioned->StylePosition(); + if (!stylePos->mPositionAnchor.IsIdent()) { + auto* nearestScrollFrame = FindScrollContainerFrameOf(defaultAnchorFrame); + if (!nearestScrollFrame) { + // Fixed-pos anchor. + continue; + } + if (!UnderScrollContainer(nearestScrollFrame, aScrollContainer)) { + continue; + } + const auto* data = anchorPosReferenceData->Lookup(defaultAnchorName); + if (!data) { + continue; + } + if (NeedReflowForAnchorPos(defaultAnchorFrame, positioned, *data)) { + // Abspos frames should not affect ancestor intrinsics. + FrameNeedsReflow(positioned, IntrinsicDirty::None, + NS_FRAME_HAS_DIRTY_CHILDREN); + } continue; } - const auto& info = scrollDependency->mAnchorInfo; - auto& referenceData = scrollDependency->mReferenceData; - const auto offset = AnchorPositioningUtils::GetScrollOffsetFor( - referenceData.CompensatingForScrollAxes(), positioned, info); - if (referenceData.mDefaultScrollShift != offset) { - positioned->SetPosition(positioned->GetNormalPosition() - offset); - // Update positioned frame's overflow, then the absolute containing - // block's. - positioned->UpdateOverflow(); - nsContainerFrame::PlaceFrameView(positioned); - positioned->GetParent()->UpdateOverflow(); - referenceData.mDefaultScrollShift = offset; - if (CheckOverflow(positioned, offset, referenceData)) { + + for (const auto& entry : affectedAnchors) { + const auto* anchorName = entry.mAnchorName; + const auto& anchors = entry.mFrames; + const auto* data = anchorPosReferenceData->Lookup(anchorName); + if (!data) { + continue; + } + const auto* anchorFrame = + anchorName == defaultAnchorName + ? defaultAnchorFrame + : GetAnchorPosAnchor(anchorName, positioned); + const auto idx = anchors.IndexOf(anchorFrame, 0, Comparator{}); + if (idx == nsTArray<AffectedAnchor>::NoIndex) { + // Referring to an anchor of the same name but unaffected by scrolling - + // skip. + continue; + } + auto* anchorScrollContainer = + anchors.ElementAt(idx).mNearestScrollContainer; + if (anchorScrollContainer != nearestScrollToDefaultAnchor) { + // We do not compensate for scroll for this anchor + continue; + } + + if (NeedReflowForAnchorPos(anchorFrame, positioned, *data)) { + // Abspos frames should not affect ancestor intrinsics. FrameNeedsReflow(positioned, IntrinsicDirty::None, NS_FRAME_HAS_DIRTY_CHILDREN); } diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h @@ -760,7 +760,7 @@ class PresShell final : public nsStubDocumentObserver, NeedReflow, }; AnchorPosUpdateResult UpdateAnchorPosLayout(); - void UpdateAnchorPosForScroll(const ScrollContainerFrame* aScrollContainer); + void UpdateAnchorPosLayoutForScroll(ScrollContainerFrame* aScrollContainer); inline void AddAnchorPosPositioned(nsIFrame* aFrame) { if (!mAnchorPosPositioned.Contains(aFrame)) { diff --git a/layout/generic/AbsoluteContainingBlock.cpp b/layout/generic/AbsoluteContainingBlock.cpp @@ -155,40 +155,6 @@ 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()); - // If the default anchor exists, it will likely be referenced (Except when - // authors then use `anchor()` without referring to anchors whose nearest - // scroller that of the default anchor, but that seems - // 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. - 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 result; -} - void AbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame, nsPresContext* aPresContext, const ReflowInput& aReflowInput, @@ -211,12 +177,10 @@ void AbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame, aFlags.contains(AbsPosReflowFlag::CBHeightChanged); nsOverflowContinuationTracker tracker(aDelegatingFrame, true); for (nsIFrame* kidFrame : mAbsoluteFrames) { - Maybe<AnchorPosResolutionCache> anchorPosResolutionCache; + AnchorPosReferenceData* anchorPosReferenceData = nullptr; if (kidFrame->HasAnchorPosReference()) { - auto* referenceData = kidFrame->SetOrUpdateDeletableProperty( + anchorPosReferenceData = kidFrame->SetOrUpdateDeletableProperty( nsIFrame::AnchorPosReferences()); - anchorPosResolutionCache = - Some(PopulateAnchorResolutionCache(kidFrame, referenceData)); } else { kidFrame->RemoveProperty(nsIFrame::AnchorPosReferences()); } @@ -224,7 +188,7 @@ void AbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame, bool kidNeedsReflow = reflowAll || kidFrame->IsSubtreeDirty() || FrameDependsOnContainer(kidFrame, cbWidthChanged, cbHeightChanged, - anchorPosResolutionCache.ptrOr(nullptr)); + anchorPosReferenceData); if (kidFrame->IsSubtreeDirty()) { MaybeMarkAncestorsAsHavingDescendantDependentOnItsStaticPos( kidFrame, aDelegatingFrame); @@ -265,8 +229,7 @@ void AbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame, nsReflowStatus kidStatus; ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowInput, aContainingBlock, aFlags, kidFrame, kidStatus, - aOverflowAreas, - anchorPosResolutionCache.ptrOr(nullptr)); + aOverflowAreas, anchorPosReferenceData); MOZ_ASSERT(!kidStatus.IsInlineBreakBefore(), "ShouldAvoidBreakInside should prevent this from happening"); nsIFrame* nextFrame = kidFrame->GetNextInFlow(); @@ -342,7 +305,7 @@ static inline bool IsFixedOffset(const AnchorResolvedInset& aInset) { bool AbsoluteContainingBlock::FrameDependsOnContainer( nsIFrame* f, bool aCBWidthChanged, bool aCBHeightChanged, - AnchorPosResolutionCache* aAnchorPosResolutionCache) { + AnchorPosReferenceData* anchorPosReferenceData) { const nsStylePosition* pos = f->StylePosition(); // See if f's position might have changed because it depends on a // placeholder's position. @@ -357,7 +320,7 @@ bool AbsoluteContainingBlock::FrameDependsOnContainer( const nsStyleMargin* margin = f->StyleMargin(); WritingMode wm = f->GetWritingMode(); const auto anchorResolutionParams = - AnchorPosResolutionParams::From(f, aAnchorPosResolutionCache); + AnchorPosResolutionParams::From(f, anchorPosReferenceData); if (wm.IsVertical() ? aCBHeightChanged : aCBWidthChanged) { // See if f's inline-size might have changed. // If margin-inline-start/end, padding-inline-start/end, @@ -878,145 +841,25 @@ void AbsoluteContainingBlock::ResolveAutoMarginsAfterLayout( } } -struct None {}; -using OldCacheState = Variant<None, AnchorPosResolutionCache::PositionTryBackup, - AnchorPosResolutionCache::PositionTryFullBackup>; - struct MOZ_STACK_CLASS MOZ_RAII AutoFallbackStyleSetter { - AutoFallbackStyleSetter(nsIFrame* aFrame, ComputedStyle* aFallbackStyle, - AnchorPosResolutionCache* aCache, bool aIsFirstTry) - : mFrame(aFrame), mCache{aCache}, mOldCacheState{None{}} { + AutoFallbackStyleSetter(nsIFrame* aFrame, ComputedStyle* aFallbackStyle) + : mFrame(aFrame) { if (aFallbackStyle) { mOldStyle = aFrame->SetComputedStyleWithoutNotification(aFallbackStyle); } - // We need to be able to "go back" to the old, first try (Which is not - // necessarily base style) cache. - if (!aIsFirstTry && aCache) { - // New fallback could just be a flip keyword. - if (mOldStyle && mOldStyle->StylePosition()->mPositionAnchor != - aFrame->StylePosition()->mPositionAnchor) { - mOldCacheState = - OldCacheState{aCache->TryPositionWithDifferentDefaultAnchor()}; - *aCache = PopulateAnchorResolutionCache(aFrame, aCache->mReferenceData); - } else { - mOldCacheState = - OldCacheState{aCache->TryPositionWithSameDefaultAnchor()}; - if (aCache->mDefaultAnchorCache.mAnchor) { - aCache->mReferenceData->AdjustCompensatingForScroll( - CheckEarlyCompensatingForScroll(aFrame)); - } - } - } } ~AutoFallbackStyleSetter() { if (mOldStyle) { mFrame->SetComputedStyleWithoutNotification(std::move(mOldStyle)); } - std::move(mOldCacheState) - .match( - [](None&&) {}, - [&](AnchorPosResolutionCache::PositionTryBackup&& aBackup) { - mCache->UndoTryPositionWithSameDefaultAnchor(std::move(aBackup)); - }, - [&](AnchorPosResolutionCache::PositionTryFullBackup&& aBackup) { - mCache->UndoTryPositionWithDifferentDefaultAnchor( - std::move(aBackup)); - }); } - void CommitCurrentFallback() { mOldCacheState = OldCacheState{None{}}; } - private: nsIFrame* const mFrame; RefPtr<ComputedStyle> mOldStyle; - AnchorPosResolutionCache* const mCache; - OldCacheState mOldCacheState; }; -struct AnchorShiftInfo { - nsPoint mOffset; - StylePositionArea mResolvedArea; -}; - -struct ContainingBlockRect { - Maybe<AnchorShiftInfo> mAnchorShiftInfo = Nothing{}; - nsRect mRect; - - explicit ContainingBlockRect(const nsRect& aRect) : mRect{aRect} {} - ContainingBlockRect(const nsPoint& aOffset, - const StylePositionArea& aResolvedArea, - const nsRect& aRect) - : mAnchorShiftInfo{Some(AnchorShiftInfo{aOffset, aResolvedArea})}, - mRect{aRect} {} - - StylePositionArea ResolvedPositionArea() const { - return mAnchorShiftInfo - .map([](const AnchorShiftInfo& aInfo) { return aInfo.mResolvedArea; }) - .valueOr(StylePositionArea{}); - } -}; - -static nsRect GrowOverflowCheckRect(const nsRect& aOverflowCheckRect, - const nsRect& aKidRect, - const StylePositionArea& aPosArea) { - // The overflow check rect may end up being smaller than the positioned rect - - // imagine an absolute containing block & a scroller of the same size, and an - // anchor inside it. If position-area: bottom, and the anchor positioned such - // that the anchor is touching the lower edge of the containing block & the - // scroller, the grid height is 0, which the positioned frame will always - // overflow - until the scrollbar moves. To account for this, we will let this - // containing block grow in directions that aren't constrained by the anchor. - auto result = aOverflowCheckRect; - if (aPosArea.first == StylePositionAreaKeyword::Left || - aPosArea.first == StylePositionAreaKeyword::SpanLeft) { - // Allowed to grow left - if (aKidRect.x < result.x) { - result.SetLeftEdge(aKidRect.x); - } - } else if (aPosArea.first == StylePositionAreaKeyword::Center) { - // Not allowed to grow in this axis - } else if (aPosArea.first == StylePositionAreaKeyword::Right || - aPosArea.first == StylePositionAreaKeyword::SpanRight) { - // Allowed to grow right - if (aKidRect.XMost() > aOverflowCheckRect.XMost()) { - result.SetRightEdge(aKidRect.XMost()); - } - } else if (aPosArea.first == StylePositionAreaKeyword::SpanAll) { - // Allowed to grow in both directions - if (aKidRect.x < aOverflowCheckRect.x) { - result.SetLeftEdge(aKidRect.x); - } - if (aKidRect.XMost() > aOverflowCheckRect.XMost()) { - result.SetRightEdge(aKidRect.XMost()); - } - } - if (aPosArea.first == StylePositionAreaKeyword::Top || - aPosArea.first == StylePositionAreaKeyword::SpanTop) { - // Allowed to grow up - if (aKidRect.y < aOverflowCheckRect.y) { - result.SetTopEdge(aKidRect.y); - } - } else if (aPosArea.first == StylePositionAreaKeyword::Center) { - // Not allowed to grow in this axis - } else if (aPosArea.first == StylePositionAreaKeyword::Bottom || - aPosArea.first == StylePositionAreaKeyword::SpanBottom) { - // Allowed to grow down - if (aKidRect.YMost() > aOverflowCheckRect.YMost()) { - result.SetBottomEdge(aKidRect.YMost()); - } - } else if (aPosArea.first == StylePositionAreaKeyword::SpanAll) { - // Allowed to grow in both directions - if (aKidRect.y < aOverflowCheckRect.y) { - result.SetTopEdge(aKidRect.y); - } - if (aKidRect.YMost() > aOverflowCheckRect.YMost()) { - result.SetBottomEdge(aKidRect.YMost()); - } - } - return result; -} - // XXX Optimize the case where it's a resize reflow and the absolutely // positioned child has the exact same size and position and skip the // reflow... @@ -1025,7 +868,7 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( const ReflowInput& aReflowInput, const nsRect& aOriginalContainingBlockRect, AbsPosReflowFlags aFlags, nsIFrame* aKidFrame, nsReflowStatus& aStatus, OverflowAreas* aOverflowAreas, - AnchorPosResolutionCache* aAnchorPosResolutionCache) { + AnchorPosReferenceData* aAnchorPosReferenceData) { MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); #ifdef DEBUG @@ -1094,20 +937,14 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( return SeekFallbackTo(nextFallbackIndex); }; - Maybe<uint32_t> firstTryIndex; - Maybe<nsPoint> firstTryNormalPosition; // TODO(emilio): Right now fallback only applies to position-area, which only // makes a difference with a default anchor... Generalize it? - if (aAnchorPosResolutionCache) { + if (aAnchorPosReferenceData) { bool found = false; uint32_t index = aKidFrame->GetProperty( nsIFrame::LastSuccessfulPositionFallback(), &found); - if (found) { - if (!SeekFallbackTo(index)) { - aKidFrame->RemoveProperty(nsIFrame::LastSuccessfulPositionFallback()); - } else { - firstTryIndex = Some(index); - } + if (found && !SeekFallbackTo(index)) { + aKidFrame->RemoveProperty(nsIFrame::LastSuccessfulPositionFallback()); } } @@ -1116,70 +953,47 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( bool isOverflowingCB = true; do { - AutoFallbackStyleSetter fallback(aKidFrame, currentFallbackStyle, - aAnchorPosResolutionCache, - firstTryIndex == currentFallbackIndex); - const auto cb = [&]() { + AutoFallbackStyleSetter fallback(aKidFrame, currentFallbackStyle); + auto positionArea = aKidFrame->StylePosition()->mPositionArea; + StylePositionArea resolvedPositionArea; + const nsRect usedCb = [&] { if (isGrid) { // TODO(emilio): how does position-area interact with grid? - return ContainingBlockRect{nsGridContainerFrame::GridItemCB(aKidFrame)}; + return nsGridContainerFrame::GridItemCB(aKidFrame); } - auto positionArea = aKidFrame->StylePosition()->mPositionArea; if (currentFallback && currentFallback->IsPositionArea()) { MOZ_ASSERT(currentFallback->IsPositionArea()); positionArea = currentFallback->AsPositionArea(); } - if (!positionArea.IsNone() && aAnchorPosResolutionCache) { - const auto defaultAnchorInfo = - AnchorPositioningUtils::ResolveAnchorPosRect( - aKidFrame, aDelegatingFrame, nullptr, false, - aAnchorPosResolutionCache); - if (defaultAnchorInfo) { - // Offset should be up to, but not including the containing block's - // scroll offset. - const auto offset = AnchorPositioningUtils::GetScrollOffsetFor( - aAnchorPosResolutionCache->mReferenceData - ->CompensatingForScrollAxes(), - aKidFrame, aAnchorPosResolutionCache->mDefaultAnchorCache); - // Imagine an abspos container with a scroller in it, and then an - // anchor in it, where the anchor is visually in the middle of the - // scrollport. Then, when the scroller moves such that the anchor's - // left edge is on that of the scrollports, w.r.t. containing block, - // the anchor is zero left offset horizontally. The position-area grid - // needs to account for this. - const auto scrolledAnchorRect = defaultAnchorInfo->mRect - offset; - StylePositionArea resolvedPositionArea{}; - const auto scrolledAnchorCb = AnchorPositioningUtils:: + if (!positionArea.IsNone()) { + const auto defaultAnchorInfo = AnchorPositioningUtils::GetDefaultAnchor( + aKidFrame, false, aAnchorPosReferenceData); + if (defaultAnchorInfo.mRect) { + return AnchorPositioningUtils:: AdjustAbsoluteContainingBlockRectForPositionArea( - scrolledAnchorRect, aOriginalContainingBlockRect, + *defaultAnchorInfo.mRect, aOriginalContainingBlockRect, aKidFrame->GetWritingMode(), aDelegatingFrame->GetWritingMode(), positionArea, &resolvedPositionArea); - return ContainingBlockRect{offset, resolvedPositionArea, - scrolledAnchorCb}; } } if (ViewportFrame* viewport = do_QueryFrame(aDelegatingFrame)) { if (!IsSnapshotContainingBlock(aKidFrame)) { - return ContainingBlockRect{ - viewport->GetContainingBlockAdjustedForScrollbars(aReflowInput)}; + return viewport->GetContainingBlockAdjustedForScrollbars( + aReflowInput); } - return ContainingBlockRect{ - dom::ViewTransition::SnapshotContainingBlockRect( - viewport->PresContext())}; + return dom::ViewTransition::SnapshotContainingBlockRect( + viewport->PresContext()); } - return ContainingBlockRect{aOriginalContainingBlockRect}; + return aOriginalContainingBlockRect; }(); - if (aAnchorPosResolutionCache) { - aAnchorPosResolutionCache->mReferenceData->mContainingBlockRect = - cb.mRect; - } + const WritingMode outerWM = aReflowInput.GetWritingMode(); const WritingMode wm = aKidFrame->GetWritingMode(); - const LogicalSize cbSize(outerWM, cb.mRect.Size()); + const LogicalSize cbSize(outerWM, usedCb.Size()); ReflowInput::InitFlags initFlags; const bool staticPosIsCBOrigin = [&] { @@ -1227,7 +1041,7 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( // Don't split things below the fold. (Ideally we shouldn't *have* // anything totally below the fold, but we can't position frames // across next-in-flow breaks yet. - (aKidFrame->GetLogicalRect(cb.mRect.Size()).BStart(wm) <= + (aKidFrame->GetLogicalRect(usedCb.Size()).BStart(wm) <= aReflowInput.AvailableBSize()); // Get the border values @@ -1242,7 +1056,7 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( ReflowInput kidReflowInput(aPresContext, aReflowInput, aKidFrame, availSize.ConvertTo(wm, outerWM), Some(cbSize.ConvertTo(wm, outerWM)), initFlags, - {}, {}, aAnchorPosResolutionCache); + {}, {}, aAnchorPosReferenceData); if (nscoord kidAvailBSize = kidReflowInput.AvailableBSize(); kidAvailBSize != NS_UNCONSTRAINEDSIZE) { @@ -1282,7 +1096,7 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( // If we're solving for start in either inline or block direction, // then compute it now that we know the dimensions. ResolveSizeDependentOffsets(kidReflowInput, cbSize, kidSize, margin, - cb.ResolvedPositionArea(), offsets); + resolvedPositionArea, offsets); if (kidReflowInput.mFlags.mDeferAutoMarginComputation) { ResolveAutoMarginsAfterLayout(kidReflowInput, cbSize, kidSize, margin, @@ -1297,7 +1111,7 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( const auto anchorResolutionParams = AnchorPosOffsetResolutionParams::ExplicitCBFrameSize( AnchorPosResolutionParams::From(aKidFrame, - aAnchorPosResolutionCache), + aAnchorPosReferenceData), &cbSize); const bool iInsetAuto = stylePos @@ -1335,7 +1149,7 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( offsets.IStart(outerWM), offsets.IEnd(outerWM), }), - cb.ResolvedPositionArea()); + resolvedPositionArea); offsets.IStart(outerWM) += alignOffset; offsets.IEnd(outerWM) = @@ -1353,7 +1167,7 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( offsets.BStart(outerWM), offsets.BEnd(outerWM), }), - cb.ResolvedPositionArea()); + resolvedPositionArea); offsets.BStart(outerWM) += alignOffset; offsets.BEnd(outerWM) = cbSize.BSize(outerWM) - @@ -1370,11 +1184,7 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( border.Size(outerWM).GetPhysicalSize(outerWM)); // Offset the frame rect by the given origin of the absolute CB. - r += cb.mRect.TopLeft(); - if (cb.mAnchorShiftInfo) { - // Push the frame out to where the anchor is. - r += cb.mAnchorShiftInfo->mOffset; - } + r += usedCb.TopLeft(); aKidFrame->SetRect(r); @@ -1391,70 +1201,13 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( aKidFrame->DidReflow(aPresContext, &kidReflowInput); - [&]() { - if (!aAnchorPosResolutionCache) { - return; - } - auto* referenceData = aAnchorPosResolutionCache->mReferenceData; - if (referenceData->CompensatingForScrollAxes().isEmpty()) { - return; - } - // Now that all the anchor-related values are resolved, completing the - // scroll compensation flag, compute the scroll offsets. - const auto offset = [&]() { - if (cb.mAnchorShiftInfo) { - // Already resolved. - return cb.mAnchorShiftInfo->mOffset; - } - return AnchorPositioningUtils::GetScrollOffsetFor( - referenceData->CompensatingForScrollAxes(), aKidFrame, - aAnchorPosResolutionCache->mDefaultAnchorCache); - }(); - // Apply the hypothetical scroll offset. - const auto position = aKidFrame->GetPosition(); - // Set initial scroll position. TODO(dshin, bug 1987962): Need - // additional work for remembered scroll offset here. - if (!firstTryNormalPosition) { - firstTryNormalPosition = Some(position); - } - aKidFrame->SetProperty(nsIFrame::NormalPositionProperty(), position); - if (offset != nsPoint{}) { - aKidFrame->SetPosition(position - offset); - // Ensure that the positioned frame's overflow is updated. Absolutely - // containing block's overflow will be updated shortly below. - aKidFrame->UpdateOverflow(); - nsContainerFrame::PlaceFrameView(aKidFrame); - } - aAnchorPosResolutionCache->mReferenceData->mDefaultScrollShift = offset; - }(); - - const auto fits = aStatus.IsComplete() && [&]() { - // TODO(dshin, bug 1996832): This should probably be done at call sites of - // `AbsoluteContainingBlock::Reflow`. - const auto paddingEdgeShift = [&]() { - const auto border = aDelegatingFrame->GetUsedBorder(); - return nsPoint{border.left, border.top}; - }(); - auto overflowCheckRect = cb.mRect + paddingEdgeShift; - if (aAnchorPosResolutionCache && cb.mAnchorShiftInfo) { - overflowCheckRect = - GrowOverflowCheckRect(overflowCheckRect, aKidFrame->GetNormalRect(), - cb.mAnchorShiftInfo->mResolvedArea); - aAnchorPosResolutionCache->mReferenceData->mContainingBlockRect = - overflowCheckRect; - const auto originalContainingBlockRect = - aOriginalContainingBlockRect + paddingEdgeShift; - return AnchorPositioningUtils::FitsInContainingBlock( - overflowCheckRect, originalContainingBlockRect, - aKidFrame->GetRect()); - } - return overflowCheckRect.Contains(aKidFrame->GetRect()); - }(); + const bool fits = + aStatus.IsComplete() && usedCb.Contains(aKidFrame->GetRect()); if (fallbacks.IsEmpty() || fits) { // We completed the reflow - Either we had a fallback that fit, or we // didn't have any to try in the first place. + // TODO(dshin, bug 1987963): Hypothetical scroll will be committed here. isOverflowingCB = !fits; - fallback.CommitCurrentFallback(); break; } @@ -1468,31 +1221,6 @@ void AbsoluteContainingBlock::ReflowAbsoluteFrame( aStatus.Reset(); } while (true); - [&]() { - if (!isOverflowingCB || !aAnchorPosResolutionCache || - !firstTryNormalPosition) { - return; - } - // We gave up applying fallbacks. Recover previous values, if changed. - // Because we rolled back to first try data, our cache should be up-to-date. - const auto normalPosition = *firstTryNormalPosition; - const auto oldNormalPosition = aKidFrame->GetNormalPosition(); - if (normalPosition != oldNormalPosition) { - aKidFrame->SetProperty(nsIFrame::NormalPositionProperty(), - normalPosition); - } - const auto position = - normalPosition - - aAnchorPosResolutionCache->mReferenceData->mDefaultScrollShift; - const auto oldPosition = aKidFrame->GetPosition(); - if (position == oldPosition) { - return; - } - aKidFrame->SetPosition(position); - aKidFrame->UpdateOverflow(); - nsContainerFrame::PlaceFrameView(aKidFrame); - }(); - // If author asked for `position-visibility: no-overflow` and we overflow // `usedCB`, treat as "strongly hidden". aKidFrame->AddOrRemoveStateBits( diff --git a/layout/generic/AbsoluteContainingBlock.h b/layout/generic/AbsoluteContainingBlock.h @@ -110,7 +110,7 @@ class AbsoluteContainingBlock { */ bool FrameDependsOnContainer( nsIFrame* aFrame, bool aCBWidthChanged, bool aCBHeightChanged, - mozilla::AnchorPosResolutionCache* aAnchorPosResolutionCache = nullptr); + AnchorPosReferenceData* aAnchorPosReferenceData = nullptr); /** * After an abspos child's size is known, this method can be used to @@ -145,13 +145,14 @@ class AbsoluteContainingBlock { LogicalMargin& aMargin, LogicalMargin& aOffsets); - void ReflowAbsoluteFrame( - nsIFrame* aDelegatingFrame, nsPresContext* aPresContext, - const ReflowInput& aReflowInput, - const nsRect& aOriginalContainingBlockRect, AbsPosReflowFlags aFlags, - nsIFrame* aKidFrame, nsReflowStatus& aStatus, - OverflowAreas* aOverflowAreas, - mozilla::AnchorPosResolutionCache* aAnchorPosResolutionCache = nullptr); + void ReflowAbsoluteFrame(nsIFrame* aDelegatingFrame, + nsPresContext* aPresContext, + const ReflowInput& aReflowInput, + const nsRect& aOriginalContainingBlockRect, + AbsPosReflowFlags aFlags, nsIFrame* aKidFrame, + nsReflowStatus& aStatus, + OverflowAreas* aOverflowAreas, + AnchorPosReferenceData* aAnchorPosReferenceData); /** * Mark our absolute frames dirty. diff --git a/layout/generic/ReflowInput.cpp b/layout/generic/ReflowInput.cpp @@ -109,10 +109,10 @@ static nscoord FontSizeInflationListMarginAdjustment(const nsIFrame* aFrame) { SizeComputationInput::SizeComputationInput( nsIFrame* aFrame, gfxContext* aRenderingContext, - AnchorPosResolutionCache* aAnchorPosResolutionCache) + AnchorPosReferenceData* aAnchorPosReferenceData) : mFrame(aFrame), mRenderingContext(aRenderingContext), - mAnchorPosResolutionCache(aAnchorPosResolutionCache), + mAnchorPosReferenceData(aAnchorPosReferenceData), mWritingMode(aFrame->GetWritingMode()), mIsThemed(aFrame->IsThemed()), mComputedMargin(mWritingMode), @@ -180,9 +180,9 @@ ReflowInput::ReflowInput(nsPresContext* aPresContext, InitFlags aFlags, const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aComputeSizeFlags, - AnchorPosResolutionCache* aAnchorPosResolutionCache) + AnchorPosReferenceData* aAnchorPosReferenceData) : SizeComputationInput(aFrame, aParentReflowInput.mRenderingContext, - aAnchorPosResolutionCache), + aAnchorPosReferenceData), mParentReflowInput(&aParentReflowInput), mFloatManager(aParentReflowInput.mFloatManager), mLineLayout(mFrame->IsLineParticipant() ? aParentReflowInput.mLineLayout @@ -212,7 +212,7 @@ ReflowInput::ReflowInput(nsPresContext* aPresContext, bool* aFixed = nullptr) -> nscoord { nscoord limit = NS_UNCONSTRAINEDSIZE; const auto* pos = aFrame->StylePosition(); - // Don't add to anchor resolution cache, since this function is called for + // Don't add to referenced anchors, since this function is called for // other frames. const auto anchorResolutionParams = AnchorPosResolutionParams::From(aFrame); @@ -365,13 +365,13 @@ nscoord SizeComputationInput::ComputeISizeValue( contentEdgeToBoxSizing.ISize(wm); return mFrame - ->ComputeISizeValue(mRenderingContext, wm, aContainingBlockSize, - contentEdgeToBoxSizing, boxSizingToMarginEdgeISize, - aSize, - *mFrame->StylePosition()->BSize( - wm, AnchorPosResolutionParams::From( - mFrame, mAnchorPosResolutionCache)), - mFrame->GetAspectRatio()) + ->ComputeISizeValue( + mRenderingContext, wm, aContainingBlockSize, contentEdgeToBoxSizing, + boxSizingToMarginEdgeISize, aSize, + *mFrame->StylePosition()->BSize( + wm, + AnchorPosResolutionParams::From(mFrame, mAnchorPosReferenceData)), + mFrame->GetAspectRatio()) .mISize; } @@ -527,7 +527,7 @@ void ReflowInput::Init(nsPresContext* aPresContext, nsIFrame* containingBlk = mFrame; while (containingBlk) { const nsStylePosition* stylePos = containingBlk->StylePosition(); - // It's for containing block, so don't add to anchor resolution cache + // It's for containing block, so don't add to referenced anchors const auto containingBlkAnchorResolutionParams = AnchorPosResolutionParams::From(containingBlk); const auto bSizeCoord = @@ -2945,7 +2945,7 @@ bool SizeComputationInput::ComputeMargin(WritingMode aCBWM, } LogicalMargin m(aCBWM); const auto anchorResolutionParams = - AnchorPosResolutionParams::From(mFrame, mAnchorPosResolutionCache); + AnchorPosResolutionParams::From(mFrame, mAnchorPosReferenceData); for (const LogicalSide side : LogicalSides::All) { m.Side(side, aCBWM) = nsLayoutUtils::ComputeCBDependentValue( aPercentBasis, diff --git a/layout/generic/ReflowInput.h b/layout/generic/ReflowInput.h @@ -68,8 +68,8 @@ struct SizeComputationInput { // Rendering context to use for measurement. gfxContext* mRenderingContext; - // Cache for anchor resolution in this computation. - AnchorPosResolutionCache* mAnchorPosResolutionCache = nullptr; + // Cache of referenced anchors for this computation. + AnchorPosReferenceData* mAnchorPosReferenceData = nullptr; nsMargin ComputedPhysicalMargin() const { return mComputedMargin.GetPhysicalMargin(mWritingMode); @@ -132,7 +132,7 @@ struct SizeComputationInput { // Callers using this constructor must call InitOffsets on their own. SizeComputationInput( nsIFrame* aFrame, gfxContext* aRenderingContext, - AnchorPosResolutionCache* aAnchorPosResolutionCache = nullptr); + AnchorPosReferenceData* aAnchorPosReferenceData = nullptr); SizeComputationInput(nsIFrame* aFrame, gfxContext* aRenderingContext, WritingMode aContainingBlockWritingMode, @@ -626,9 +626,9 @@ struct ReflowInput : public SizeComputationInput { * call nsIFrame::ComputeSize() internally. * @param aComputeSizeFlags A set of flags used when we call * nsIFrame::ComputeSize() internally. - * @param aAnchorResolutionCache A cache of referenced anchors to be populated - * (If specified) for this reflowed frame. Should live for the lifetime - * of this ReflowInput. + * @param aAnchorPosReferenceData A cache of referenced anchors to be + * populated (If specified) for this reflowed frame. Should live for the + * lifetime of this ReflowInput. */ ReflowInput(nsPresContext* aPresContext, const ReflowInput& aParentReflowInput, nsIFrame* aFrame, @@ -637,7 +637,7 @@ struct ReflowInput : public SizeComputationInput { InitFlags aFlags = {}, const StyleSizeOverrides& aSizeOverrides = {}, ComputeSizeFlags aComputeSizeFlags = {}, - AnchorPosResolutionCache* aAnchorPosResolutionCache = nullptr); + AnchorPosReferenceData* aAnchorPosReferenceData = nullptr); /** * This method initializes various data members. It is automatically called by @@ -985,7 +985,7 @@ inline AnchorPosResolutionParams AnchorPosResolutionParams::From( aIgnorePositionArea ? mozilla::StylePositionArea{} : aRI->mStylePosition->mPositionArea; return {aRI->mFrame, aRI->mStyleDisplay->mPosition, posArea, - aRI->mAnchorPosResolutionCache}; + aRI->mAnchorPosReferenceData}; } #endif // mozilla_ReflowInput_h diff --git a/layout/generic/ScrollContainerFrame.cpp b/layout/generic/ScrollContainerFrame.cpp @@ -3270,7 +3270,7 @@ void ScrollContainerFrame::ScrollToImpl( return; } } - PresShell()->UpdateAnchorPosForScroll(this); + PresShell()->UpdateAnchorPosLayoutForScroll(this); presContext->RecordInteractionTime( nsPresContext::InteractionType::ScrollInteraction, TimeStamp::Now()); diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp @@ -12,7 +12,6 @@ #include <algorithm> -#include "AnchorPositioningUtils.h" #include "LayoutLogging.h" #include "RubyUtils.h" #include "TextOverflow.h" @@ -796,7 +795,6 @@ void nsIFrame::HandlePrimaryFrameStyleChange(ComputedStyle* aOldStyle) { const bool isReferringToAnchor = HasAnchorPosReference(); if (wasReferringToAnchor && !isReferringToAnchor) { PresShell()->RemoveAnchorPosPositioned(this); - RemoveProperty(NormalPositionProperty()); } else if (!wasReferringToAnchor && isReferringToAnchor) { PresShell()->AddAnchorPosPositioned(this); } @@ -8405,6 +8403,8 @@ void nsIFrame::MovePositionBy(const nsPoint& aTranslation) { } nsRect nsIFrame::GetNormalRect() const { + // It might be faster to first check + // StyleDisplay()->IsRelativelyPositionedStyle(). bool hasProperty; nsPoint normalPosition = GetProperty(NormalPositionProperty(), &hasProperty); if (hasProperty) { @@ -8472,14 +8472,13 @@ OverflowAreas nsIFrame::GetOverflowAreasRelativeToParent() const { OverflowAreas nsIFrame::GetActualAndNormalOverflowAreasRelativeToParent() const { - const bool hasAnchorPosReference = HasAnchorPosReference(); - if (MOZ_LIKELY(!IsRelativelyOrStickyPositioned() && !hasAnchorPosReference)) { + if (MOZ_LIKELY(!IsRelativelyOrStickyPositioned())) { return GetOverflowAreasRelativeToParent(); } const OverflowAreas overflows = GetOverflowAreas(); OverflowAreas actualAndNormalOverflows = overflows + GetNormalPosition(); - if (IsRelativelyPositioned() || hasAnchorPosReference) { + if (IsRelativelyPositioned()) { actualAndNormalOverflows.UnionWith(overflows + GetPosition()); } else { // For sticky positioned elements, we only use the normal position for the @@ -12268,18 +12267,6 @@ 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,8 +1451,6 @@ 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 @@ -5923,9 +5921,9 @@ inline nsIFrame* nsFrameList::BackwardFrameTraversal::Prev(nsIFrame* aFrame) { inline AnchorPosResolutionParams AnchorPosResolutionParams::From( const nsIFrame* aFrame, - mozilla::AnchorPosResolutionCache* aAnchorPosResolutionCache) { + mozilla::AnchorPosReferenceData* aAnchorPosReferenceData) { return {aFrame, aFrame->StyleDisplay()->mPosition, - aFrame->StylePosition()->mPositionArea, aAnchorPosResolutionCache}; + aFrame->StylePosition()->mPositionArea, aAnchorPosReferenceData}; } #endif /* nsIFrame_h___ */ diff --git a/layout/style/GeckoBindings.cpp b/layout/style/GeckoBindings.cpp @@ -1845,6 +1845,58 @@ 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); +} + bool Gecko_GetAnchorPosOffset(const AnchorPosOffsetResolutionParams* aParams, const nsAtom* aAnchorName, StylePhysicalSide aPropSide, @@ -1853,36 +1905,22 @@ bool Gecko_GetAnchorPosOffset(const AnchorPosOffsetResolutionParams* aParams, if (!aParams || !aParams->mBaseParams.mFrame) { return false; } - 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) { + + 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, + aParams->mBaseParams.mAnchorPosReferenceData); + if (info.isNothing()) { 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->mRect; + const auto& rect = info.ref().mRect; + const auto* containingBlock = info.ref().mContainingBlock; const auto usesCBWM = AnchorSideUsesCBWM(aAnchorSideKeyword); const auto cbwm = containingBlock->GetWritingMode(); const auto wm = @@ -1958,8 +1996,34 @@ bool Gecko_GetAnchorPosSize(const AnchorPosResolutionParams* aParams, return false; } const auto* positioned = aParams->mFrame; - const auto size = AnchorPositioningUtils::ResolveAnchorPosSize( - positioned, aAnchorName, aParams->mCache); + + const auto* anchorName = + AnchorPositioningUtils::GetUsedAnchorName(positioned, aAnchorName); + if (!anchorName) { + return false; + } + const auto size = [&]() -> Maybe<nsSize> { + Maybe<AnchorPosResolutionData>* entry = nullptr; + if (aParams->mAnchorPosReferenceData) { + const auto result = + aParams->mAnchorPosReferenceData->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); + }(); if (!size) { return false; } diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h @@ -43,7 +43,7 @@ namespace mozilla { class ComputedStyle; struct IntrinsicSize; struct ReflowInput; -struct AnchorPosResolutionCache; +class AnchorPosReferenceData; } // namespace mozilla @@ -390,14 +390,15 @@ struct AnchorPosResolutionParams { mozilla::StylePositionProperty mPosition; // position-area property of the element in question. mozilla::StylePositionArea mPositionArea; - // Cache data used for anchor resolution. - mozilla::AnchorPosResolutionCache* const mCache; + // Storage for anchor reference data. To be populated on abspos reflow, + // whenever the frame makes any anchor reference. + mozilla::AnchorPosReferenceData* const mAnchorPosReferenceData = nullptr; // Helper functions for creating anchor resolution parameters. // Defined in corresponding header files. static inline AnchorPosResolutionParams From( const nsIFrame* aFrame, - mozilla::AnchorPosResolutionCache* aAnchorPosResolutionCache = nullptr); + mozilla::AnchorPosReferenceData* aAnchorPosReferenceData = nullptr); static inline AnchorPosResolutionParams From( const mozilla::ReflowInput* aRI, bool aIgnorePositionArea = false); static inline AnchorPosResolutionParams From( diff --git a/servo/ports/geckolib/glue.rs b/servo/ports/geckolib/glue.rs @@ -10759,7 +10759,7 @@ fn offset_params_from_base_params( mFrame: params.mFrame, mPosition: params.mPosition, mPositionArea: params.mPositionArea, - mCache: params.mCache, + mAnchorPosReferenceData: params.mAnchorPosReferenceData, }, } } diff --git a/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-chained-002.html.ini b/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-chained-002.html.ini @@ -1,3 +0,0 @@ -[anchor-scroll-chained-002.html] - # If scrollbars are always visible, we will fail this test. - expected: [PASS, FAIL] diff --git a/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-chained-003.html.ini b/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-chained-003.html.ini @@ -1,4 +1,4 @@ [anchor-scroll-chained-003.html] expected: - if os == "android": [PASS, FAIL] + if os == "android": PASS FAIL diff --git a/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-chained-004.html.ini b/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-chained-004.html.ini @@ -1,4 +0,0 @@ -[anchor-scroll-chained-004.html] - expected: - if os == "android": [PASS, FAIL] - PASS diff --git a/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-fixedpos-004.html.ini b/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-fixedpos-004.html.ini @@ -0,0 +1,2 @@ +[anchor-scroll-fixedpos-004.html] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-003.html.ini b/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-003.html.ini @@ -0,0 +1,3 @@ +[anchor-scroll-position-try-003.html] + [Should use the first fallback position at the initial scroll offset] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-006.html.ini b/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-006.html.ini @@ -14,7 +14,8 @@ FAIL [Should use the last (fourth) position option initially] - expected: FAIL + expected: + if os == "android": FAIL [Should still use the last position option as long as it fits.] expected: 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 @@ -9,8 +9,3 @@ expected: if (os == "mac") and not debug: [PASS, FAIL] FAIL - - [Should use the last position option initially] - expected: - if (os == "android"): [PASS, FAIL] - FAIL diff --git a/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-008.html.ini b/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-008.html.ini @@ -9,8 +9,3 @@ expected: if (os == "mac") and not debug: [PASS, FAIL] FAIL - - [Should use the last fallback position initially] - expected: - if (os == "android"): [PASS, FAIL] - FAIL diff --git a/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-009.html.ini b/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-009.html.ini @@ -14,4 +14,3 @@ [Should use the last fallback position initially] expected: if os == "android": [PASS, FAIL] - FAIL diff --git a/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-010.html.ini b/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-010.html.ini @@ -1,7 +1,7 @@ [anchor-scroll-position-try-010.html] [Should use the third fallback position with enough space right] expected: - if os == "android": [PASS, FAIL] + if (os == "android"): [PASS, FAIL] FAIL [Should use the second fallback position with enough space above] @@ -13,4 +13,5 @@ FAIL [Should use the last fallback position initially] - expected: FAIL + expected: + if (os == "android"): FAIL diff --git a/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-015.tentative.html.ini b/testing/web-platform/meta/css/css-anchor-position/anchor-scroll-position-try-015.tentative.html.ini @@ -1,2 +1,2 @@ [anchor-scroll-position-try-015.tentative.html] - expected: [PASS, FAIL] + 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 @@ -0,0 +1,3 @@ +[last-successful-pseudo-element-fallbacks.html] + [No successful position, last successful invalidated by position-try-fallbacks change] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-anchor-position/position-area-in-position-try.html.ini b/testing/web-platform/meta/css/css-anchor-position/position-area-in-position-try.html.ini @@ -0,0 +1,6 @@ +[position-area-in-position-try.html] + [Placement: --bottom, --right, --left, --top] + expected: FAIL + + [Placement: --bottom, --left, --top, --right] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-anchor-position/position-area-scrolling-002.tentative.html.ini b/testing/web-platform/meta/css/css-anchor-position/position-area-scrolling-002.tentative.html.ini @@ -1,4 +1,7 @@ [position-area-scrolling-002.tentative.html] + [Scroll to 40,60] + expected: FAIL + [Reattach at 40,60] expected: FAIL diff --git a/testing/web-platform/meta/css/css-anchor-position/position-area-scrolling-005.html.ini b/testing/web-platform/meta/css/css-anchor-position/position-area-scrolling-005.html.ini @@ -1,4 +1,7 @@ [position-area-scrolling-005.html] + [Scroll to 40,60] + expected: FAIL + [Scroll to 100,150] expected: FAIL diff --git a/testing/web-platform/meta/css/css-anchor-position/position-try-fallbacks-003.html.ini b/testing/web-platform/meta/css/css-anchor-position/position-try-fallbacks-003.html.ini @@ -2,14 +2,11 @@ expected: if (os == "linux") and debug and fission: [OK, TIMEOUT] [initial position] - expected: - if (os == "android"): [PASS, FAIL] - FAIL + expected: FAIL [scroll to 100] expected: if (os == "linux") and debug and fission: [FAIL, TIMEOUT] - if (os == "android"): [PASS, FAIL] FAIL [scroll to 101] @@ -25,6 +22,7 @@ [redisplay at 100] expected: if (os == "linux") and debug and fission: [FAIL, NOTRUN] + FAIL [scroll to 299] expected: diff --git a/testing/web-platform/meta/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container.html.ini b/testing/web-platform/meta/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container.html.ini @@ -1,3 +1,4 @@ [position-visibility-anchors-visible-non-intervening-container.html] fuzzy: - maxDifference=0-96;totalPixels=0-1 + if os == "win": maxDifference=92;totalPixels=0-1 + expected: [FAIL, PASS] diff --git a/testing/web-platform/meta/css/css-anchor-position/try-tactic-basic-anchor.html.ini b/testing/web-platform/meta/css/css-anchor-position/try-tactic-basic-anchor.html.ini @@ -0,0 +1,6 @@ +[try-tactic-basic-anchor.html] + [Uses flip-block] + expected: FAIL + + [Uses flip-inline] + expected: FAIL diff --git a/testing/web-platform/meta/css/css-highlight-api/target-text-text-decoration-001.html.ini b/testing/web-platform/meta/css/css-highlight-api/target-text-text-decoration-001.html.ini @@ -0,0 +1,2 @@ +[target-text-text-decoration-001.html] + fuzzy: maxDifference=0-92;totalPixels=0-1 +\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-008.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-008.html @@ -1,70 +0,0 @@ -<!DOCTYPE html> -<html class="reftest-wait"> -<title>Anchored element should update when anchor's div under `contain: layout size` becomes a non scroll container.</title> -<link rel="author" href="mailto:dshin@mozilla.com"> -<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> -<link rel="match" href="reference/anchor-scroll-update-008-ref.html"> -<style> -.abspos-cb { - position: relative; - width: 200px; - height: 200px; - border: 1px solid; -} - -.positioned { - width: 15px; - height: 15px; - background: purple; - - position: absolute; - position-anchor: --a; - left: anchor(right); - top: anchor(top); -} - -.scroller { - overflow-y: scroll; - height: 100%; -} - -.filler { - width: 1px; - height: 500px; -} - -.anchor { - width: 15px; - height: 15px; - background: magenta; - anchor-name: --a; -} - -.contain { - contain: layout size; - width: 200px; - height: 200px; -} -</style> -<div class=abspos-cb> - <div class=positioned></div> - <div class=contain><div id=dut class=scroller> - <div class=anchor></div> - <div class=filler></div> - </div></div> -</div> -<script> -dut.scrollTop = 300; -dut.classList.toggle('scroller'); - -function raf() { - return new Promise(resolve => requestAnimationFrame(resolve)); -} - -async function runTest() { - await raf(); - await raf(); - document.documentElement.classList.remove('reftest-wait'); -} -runTest(); -</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-009.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-009.html @@ -1,70 +0,0 @@ -<!DOCTYPE html> -<html class="reftest-wait"> -<title>Anchored element should update when anchor's div under `contain: layout size` becomes a scroll container.</title> -<link rel="author" href="mailto:dshin@mozilla.com"> -<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> -<link rel="match" href="reference/anchor-scroll-update-009-ref.html"> -<style> -.abspos-cb { - position: relative; - width: 200px; - height: 200px; - border: 1px solid; -} - -.positioned { - width: 15px; - height: 15px; - background: purple; - - position: absolute; - position-anchor: --a; - left: anchor(right); - top: anchor(top); -} - -.scroller { - overflow-y: scroll; - height: 100%; -} - -.filler { - width: 1px; - height: 500px; -} - -.anchor { - width: 15px; - height: 15px; - background: magenta; - anchor-name: --a; -} - -.contain { - contain: layout size; - width: 200px; - height: 200px; -} -</style> -<div class=abspos-cb> - <div class=positioned></div> - <div class=contain><div id=dut> - <div class=filler></div> - <div class=anchor></div> - </div></div> -</div> -<script> -dut.classList.toggle('scroller'); -dut.scrollTop = 315; - -function raf() { - return new Promise(resolve => requestAnimationFrame(resolve)); -} - -async function runTest() { - await raf(); - await raf(); - document.documentElement.classList.remove('reftest-wait'); -} -runTest(); -</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-010.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-010.html @@ -1,84 +0,0 @@ -<!DOCTYPE html> -<html class="reftest-wait"> -<title>Anchored element should update when the nearest scroll container under `contain: layout size` element changes.</title> -<link rel="author" href="mailto:dshin@mozilla.com"> -<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> -<link rel="match" href="reference/anchor-scroll-update-010-ref.html"> -<style> -.anchor { - width: 20px; - height: 20px; - background: magenta; -} - -.positioned { - position-anchor: --a; - position: absolute; - background: purple; - width: 20px; - height: 20px; - /* Initially not part of --a's nearest scroll container */ - left: anchor(--b right); - top: anchor(--b top); -} - -.abs-cb { - position: relative; - width: 200px; - height: 200px; - border: 1px solid; -} - -.scroll { - overflow: scroll; -} - -.outer { - width: 200px; - height: 200px; -} - -.inner { - width: 150px; - height: 150px; -} - -.filler { - width: 1px; - height: 200px; -} - -.contain { - contain: layout size; - width: 200px; - height: 200px; -} -</style> -<div class=abs-cb> - <div class=contain> - <div id=outer class="scroll outer"> - <div class=filler></div> - <div class=anchor style="anchor-name: --b"></div> - <div id=inner class="scroll inner"> - <div class=anchor style="anchor-name: --a"></div> - </div> - </div> - </div> - <div class=positioned></div> -</div> -<script> -inner.classList.toggle('scroll'); -outer.scrollTop = 100; - -function raf() { - return new Promise(resolve => requestAnimationFrame(resolve)); -} - -async function runTest() { - await raf(); - await raf(); - document.documentElement.classList.remove('reftest-wait'); -} -runTest(); -</script> -</html> diff --git a/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-008-ref.html b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-008-ref.html @@ -1,33 +0,0 @@ -<!DOCTYPE html> -<title>Anchored element should update when anchor's div under `contain: layout size` becomes a scroll container.</title> -<link rel="author" href="mailto:dshin@mozilla.com"> -<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> -<style> -.abspos-cb { - position: relative; - width: 200px; - height: 200px; - border: 1px solid; -} - -.flex { - display: flex; -} - -.positioned { - width: 15px; - height: 15px; - background: purple; -} - -.anchor { - width: 15px; - height: 15px; - background: magenta; -} -</style> -<div class=abspos-cb> - <div class=flex> - <div class=anchor></div><div class=positioned></div> - </div> -</div> diff --git a/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-009-ref.html b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-009-ref.html @@ -1,50 +0,0 @@ -<!DOCTYPE html> -<title>Anchored element should update when anchor's div under `contain: layout size` becomes a scroll container.</title> -<link rel="author" href="mailto:dshin@mozilla.com"> -<link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> -<style> -.abspos-cb { - position: relative; - width: 200px; - height: 200px; - border: 1px solid; -} - -.flex { - display: flex; -} - -.positioned { - width: 15px; - height: 15px; - background: purple; -} - -.scroller { - overflow-y: scroll; - height: 100%; -} - -.anchor { - width: 15px; - height: 15px; - background: magenta; -} - -.filler { - width: 1px; - height: 500px; -} -</style> -<div class=abspos-cb> - <div id=dut class=scroller> - <div class=filler></div> - <div class=flex> - <div class=anchor></div> - <div class=positioned></div> - </div> - </div> -</div> -<script> -dut.scrollTop = 315; -</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-010-ref.html b/testing/web-platform/tests/css/css-anchor-position/reference/anchor-scroll-update-010-ref.html @@ -1,60 +0,0 @@ -<!DOCTYPE html> -<title>Anchored element should update when the nearest scroll container under `contain: layout size` element changes.</title> -<style> -.anchor { - width: 20px; - height: 20px; - background: magenta; -} - -.positioned { - background: purple; - width: 20px; - height: 20px; -} - -.flex { - display: flex; -} - -.abs-cb { - position: relative; - width: 200px; - height: 200px; - border: 1px solid; -} - -.scroll { - overflow: scroll; -} - -.outer { - width: 200px; - height: 200px; -} - -.inner { - width: 150px; - height: 150px; -} - -.filler { - width: 1px; - height: 200px; -} -</style> -<div class=abs-cb> - <div id=outer class="scroll outer"> - <div class=filler></div> - <div class=flex> - <div class=anchor></div> - <div class=positioned></div> - </div> - <div class=inner> - <div class=anchor></div> - </div> - </div> -</div> -<script> -outer.scrollTop = 100; -</script>