Skip to content

Commit f9b77c9

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 46c9062 commit f9b77c9

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
@@ -12058,6 +12058,7 @@ nsIFrame* PresShell::GetAbsoluteContainingBlock(nsIFrame* aFrame) {
1205812058
nsIFrame* PresShell::GetAnchorPosAnchor(
1205912059
const nsAtom* aName, const nsIFrame* aPositionedFrame) const {
1206012060
MOZ_ASSERT(aName);
12061+
MOZ_ASSERT(mLazyAnchorPosAnchorChanges.IsEmpty());
1206112062
if (const auto& entry = mAnchorPosAnchors.Lookup(aName)) {
1206212063
return AnchorPositioningUtils::FindFirstAcceptableAnchor(aPositionedFrame,
1206312064
entry.Data());
@@ -12066,7 +12067,8 @@ nsIFrame* PresShell::GetAnchorPosAnchor(
1206612067
return nullptr;
1206712068
}
1206812069

12069-
void PresShell::AddAnchorPosAnchor(const nsAtom* aName, nsIFrame* aFrame) {
12070+
template <bool AreWeMerging>
12071+
void PresShell::AddAnchorPosAnchorImpl(const nsAtom* aName, nsIFrame* aFrame) {
1207012072
MOZ_ASSERT(aName);
1207112073

1207212074
auto& entry = mAnchorPosAnchors.LookupOrInsertWith(
@@ -12081,7 +12083,7 @@ void PresShell::AddAnchorPosAnchor(const nsAtom* aName, nsIFrame* aFrame) {
1208112083
nsIFrame* mFrame;
1208212084

1208312085
int32_t operator()(nsIFrame* aOther) const {
12084-
return nsLayoutUtils::CompareTreePosition(aOther, mFrame, nullptr);
12086+
return nsLayoutUtils::CompareTreePosition(mFrame, aOther, nullptr);
1208512087
}
1208612088
};
1208712089

@@ -12101,16 +12103,39 @@ void PresShell::AddAnchorPosAnchor(const nsAtom* aName, nsIFrame* aFrame) {
1210112103
}
1210212104
MOZ_ASSERT(!entry.Contains(aFrame));
1210312105

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

1210612118
return;
1210712119
}
1210812120

12121+
MOZ_ASSERT(!entry.Contains(aFrame));
1210912122
*entry.InsertElementAt(matchOrInsertionIdx) = aFrame;
1211012123
}
1211112124

12125+
void PresShell::AddAnchorPosAnchor(const nsAtom* aName, nsIFrame* aFrame) {
12126+
AddAnchorPosAnchorImpl</* AreWeMerging */ false>(aName, aFrame);
12127+
}
12128+
1211212129
void PresShell::RemoveAnchorPosAnchor(const nsAtom* aName, nsIFrame* aFrame) {
1211312130
MOZ_ASSERT(aName);
12131+
12132+
if (!mLazyAnchorPosAnchorChanges.IsEmpty()) {
12133+
mLazyAnchorPosAnchorChanges.RemoveElementsBy(
12134+
[&](const AnchorPosAnchorChange& change) {
12135+
return change.mFrame == aFrame;
12136+
});
12137+
}
12138+
1211412139
auto entry = mAnchorPosAnchors.Lookup(aName);
1211512140
if (!entry) {
1211612141
return; // Nothing to remove.
@@ -12128,6 +12153,14 @@ void PresShell::RemoveAnchorPosAnchor(const nsAtom* aName, nsIFrame* aFrame) {
1212812153
}
1212912154
}
1213012155

12156+
void PresShell::MergeAnchorPosAnchorChanges() {
12157+
for (const auto& [name, frame] : mLazyAnchorPosAnchorChanges) {
12158+
AddAnchorPosAnchorImpl</* AreWeMerging */ true>(name, frame);
12159+
}
12160+
12161+
mLazyAnchorPosAnchorChanges.Clear();
12162+
}
12163+
1213112164
void PresShell::ActivenessMaybeChanged() {
1213212165
if (!mDocument) {
1213312166
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)