Skip to content

Commit 7ef20f5

Browse files
committed
Bug 1974382: Avoid mixing anchors in different child lists. r=layout-anchor-positioning-reviewers,dshin,emilio
Differential Revision: https://phabricator.services.mozilla.com/D255502
1 parent 1e53216 commit 7ef20f5

File tree

5 files changed

+69
-7
lines changed

5 files changed

+69
-7
lines changed

layout/base/PresShell.cpp

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12062,6 +12062,7 @@ nsIFrame* PresShell::GetAbsoluteContainingBlock(nsIFrame* aFrame) {
1206212062
nsIFrame* PresShell::GetAnchorPosAnchor(
1206312063
const nsAtom* aName, const nsIFrame* aPositionedFrame) const {
1206412064
MOZ_ASSERT(aName);
12065+
MOZ_ASSERT(mLazyAnchorPosAnchorChanges.IsEmpty());
1206512066
if (const auto& entry = mAnchorPosAnchors.Lookup(aName)) {
1206612067
return AnchorPositioningUtils::FindFirstAcceptableAnchor(aPositionedFrame,
1206712068
entry.Data());
@@ -12070,7 +12071,8 @@ nsIFrame* PresShell::GetAnchorPosAnchor(
1207012071
return nullptr;
1207112072
}
1207212073

12073-
void PresShell::AddAnchorPosAnchor(const nsAtom* aName, nsIFrame* aFrame) {
12074+
template <bool AreWeMerging>
12075+
void PresShell::AddAnchorPosAnchorImpl(const nsAtom* aName, nsIFrame* aFrame) {
1207412076
MOZ_ASSERT(aName);
1207512077

1207612078
auto& entry = mAnchorPosAnchors.LookupOrInsertWith(
@@ -12085,7 +12087,7 @@ void PresShell::AddAnchorPosAnchor(const nsAtom* aName, nsIFrame* aFrame) {
1208512087
nsIFrame* mFrame;
1208612088

1208712089
int32_t operator()(nsIFrame* aOther) const {
12088-
return nsLayoutUtils::CompareTreePosition(aOther, mFrame, nullptr);
12090+
return nsLayoutUtils::CompareTreePosition(mFrame, aOther, nullptr);
1208912091
}
1209012092
};
1209112093

@@ -12105,16 +12107,39 @@ void PresShell::AddAnchorPosAnchor(const nsAtom* aName, nsIFrame* aFrame) {
1210512107
}
1210612108
MOZ_ASSERT(!entry.Contains(aFrame));
1210712109

12108-
MOZ_ASSERT_UNREACHABLE("Frames were in different documents or child lists");
12110+
if constexpr (AreWeMerging) {
12111+
MOZ_ASSERT_UNREACHABLE(
12112+
"A frame may not be in a different child list at merge time");
12113+
} else {
12114+
// nsLayoutUtils::CompareTreePosition() returns 0 when the frames are
12115+
// in different documents or child lists. This indicates that
12116+
// the tree is being restructured and we can defer anchor insertion
12117+
// to a MergeAnchorPosAnchors call after the restructuring is complete.
12118+
mLazyAnchorPosAnchorChanges.AppendElement(
12119+
AnchorPosAnchorChange{RefPtr<const nsAtom>(aName), aFrame});
12120+
}
1210912121

1211012122
return;
1211112123
}
1211212124

12125+
MOZ_ASSERT(!entry.Contains(aFrame));
1211312126
*entry.InsertElementAt(matchOrInsertionIdx) = aFrame;
1211412127
}
1211512128

12129+
void PresShell::AddAnchorPosAnchor(const nsAtom* aName, nsIFrame* aFrame) {
12130+
AddAnchorPosAnchorImpl</* AreWeMerging */ false>(aName, aFrame);
12131+
}
12132+
1211612133
void PresShell::RemoveAnchorPosAnchor(const nsAtom* aName, nsIFrame* aFrame) {
1211712134
MOZ_ASSERT(aName);
12135+
12136+
if (!mLazyAnchorPosAnchorChanges.IsEmpty()) {
12137+
mLazyAnchorPosAnchorChanges.RemoveElementsBy(
12138+
[&](const AnchorPosAnchorChange& change) {
12139+
return change.mFrame == aFrame;
12140+
});
12141+
}
12142+
1211812143
auto entry = mAnchorPosAnchors.Lookup(aName);
1211912144
if (!entry) {
1212012145
return; // Nothing to remove.
@@ -12132,6 +12157,14 @@ void PresShell::RemoveAnchorPosAnchor(const nsAtom* aName, nsIFrame* aFrame) {
1213212157
}
1213312158
}
1213412159

12160+
void PresShell::MergeAnchorPosAnchorChanges() {
12161+
for (const auto& [name, frame] : mLazyAnchorPosAnchorChanges) {
12162+
AddAnchorPosAnchorImpl</* AreWeMerging */ true>(name, frame);
12163+
}
12164+
12165+
mLazyAnchorPosAnchorChanges.Clear();
12166+
}
12167+
1213512168
void PresShell::ActivenessMaybeChanged() {
1213612169
if (!mDocument) {
1213712170
return;

layout/base/PresShell.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1818,9 +1818,26 @@ class PresShell final : public nsStubDocumentObserver,
18181818
*/
18191819
static Modifiers GetCurrentModifiers() { return sCurrentModifiers; }
18201820

1821+
/**
1822+
* Inserts mLazyAnchorPosAnchorChanges into mAnchorPosAnchors. Because the
1823+
* anchor lookup uses tree-ordered sorting, we're implicitly assuming
1824+
* that when AddAnchorPosAnchor and RemoveAnchorPosAnchor are called,
1825+
* the frame tree structure is valid globally.
1826+
*
1827+
* This assumption does not always hold - e.g. when the initial construction
1828+
* frame tree is deferred: the frame tree can may be in an indeterminate state
1829+
* where a frame has a parent but the parent does not have that frame as its
1830+
* child. Therefore, the defer tree position comparison may be deferred to a
1831+
* point where we know the frame tree is stable.
1832+
*/
1833+
void MergeAnchorPosAnchorChanges();
1834+
18211835
private:
18221836
~PresShell();
18231837

1838+
template <bool AreWeMerging>
1839+
void AddAnchorPosAnchorImpl(const nsAtom* aName, nsIFrame* aFrame);
1840+
18241841
void SetIsActive(bool aIsActive);
18251842
bool ComputeActiveness() const;
18261843

@@ -3205,6 +3222,15 @@ class PresShell final : public nsStubDocumentObserver,
32053222
// A hash table of heap allocated weak frames.
32063223
nsTHashSet<WeakFrame*> mWeakFrames;
32073224

3225+
struct AnchorPosAnchorChange {
3226+
RefPtr<const nsAtom> mName;
3227+
nsIFrame* mFrame;
3228+
};
3229+
// Holds deferred anchor changes. These changes should be deferred when
3230+
// the frame tree is under construction and the tree position of an anchor
3231+
// cannot be determined.
3232+
nsTArray<AnchorPosAnchorChange> mLazyAnchorPosAnchorChanges;
3233+
32083234
nsTHashMap<RefPtr<const nsAtom>, nsTArray<nsIFrame*>> mAnchorPosAnchors;
32093235
nsTArray<nsIFrame*> mAnchorPosPositioned;
32103236

layout/style/RestyleManager.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3341,6 +3341,9 @@ void RestyleManager::DoProcessPendingRestyles(ServoTraversalFlags aFlags) {
33413341
IncrementRestyleGeneration();
33423342
}
33433343

3344+
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1980206
3345+
presContext->PresShell()->MergeAnchorPosAnchorChanges();
3346+
33443347
mInStyleRefresh = false;
33453348
presContext->UpdateContainerQueryStyles();
33463349
mInStyleRefresh = true;

testing/web-platform/meta/css/css-anchor-position/anchor-name-inline-001.html.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
[.target 4]
55
expected: FAIL
66

7-
[.target 5]
7+
[.target 2]
88
expected: FAIL
99

10-
[.target 2]
10+
[.target 1]
1111
expected: FAIL

testing/web-platform/meta/css/css-anchor-position/anchor-scope-basic.html.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
[Sibling can not anchor into anchor-scope, even when anchor-name present]
2121
expected: FAIL
2222

23-
[anchor-scope:all on common ancestor]
23+
[anchor-scope:all scopes multiple names]
2424
expected: FAIL
2525

26-
[anchor-scope:--a on common ancestor]
26+
[anchor-scope:--a,--b scopes --a and --b]
2727
expected: FAIL

0 commit comments

Comments
 (0)