Skip to content

Commit 1be7a65

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 b542331 commit 1be7a65

14 files changed

+80
-125
lines changed

layout/base/PresShell.cpp

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12059,6 +12059,7 @@ nsIFrame* PresShell::GetAbsoluteContainingBlock(nsIFrame* aFrame) {
1205912059
nsIFrame* PresShell::GetAnchorPosAnchor(
1206012060
const nsAtom* aName, const nsIFrame* aPositionedFrame) const {
1206112061
MOZ_ASSERT(aName);
12062+
MOZ_ASSERT(mLazyAnchorPosAnchorChanges.IsEmpty());
1206212063
if (const auto& entry = mAnchorPosAnchors.Lookup(aName)) {
1206312064
return AnchorPositioningUtils::FindFirstAcceptableAnchor(aPositionedFrame,
1206412065
entry.Data());
@@ -12067,7 +12068,8 @@ nsIFrame* PresShell::GetAnchorPosAnchor(
1206712068
return nullptr;
1206812069
}
1206912070

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

1207312075
auto& entry = mAnchorPosAnchors.LookupOrInsertWith(
@@ -12082,7 +12084,7 @@ void PresShell::AddAnchorPosAnchor(const nsAtom* aName, nsIFrame* aFrame) {
1208212084
nsIFrame* mFrame;
1208312085

1208412086
int32_t operator()(nsIFrame* aOther) const {
12085-
return nsLayoutUtils::CompareTreePosition(aOther, mFrame, nullptr);
12087+
return nsLayoutUtils::CompareTreePosition(mFrame, aOther, nullptr);
1208612088
}
1208712089
};
1208812090

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

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

1210712119
return;
1210812120
}
1210912121

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

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

12157+
void PresShell::MergeAnchorPosAnchorChanges() {
12158+
for (const auto& [name, frame] : mLazyAnchorPosAnchorChanges) {
12159+
AddAnchorPosAnchorImpl</* AreWeMerging */ true>(name, frame);
12160+
}
12161+
12162+
mLazyAnchorPosAnchorChanges.Clear();
12163+
}
12164+
1213212165
void PresShell::ActivenessMaybeChanged() {
1213312166
if (!mDocument) {
1213412167
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;
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
[anchor-name-001.html]
22
[.target 1]
3-
expected: FAIL
3+
expected: [PASS, FAIL]
44

55
[.target 2]
6-
expected: FAIL
6+
expected: [PASS, FAIL]
77

88
[.target 3]
9-
expected: FAIL
9+
expected: [PASS, FAIL]
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
[anchor-name-002.html]
22
[.target 1]
3-
expected:
4-
if (os != "android"): FAIL
3+
expected: [PASS, FAIL]
Lines changed: 3 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,15 @@
11
[anchor-name-003.html]
2-
[.target 2]
3-
expected: FAIL
4-
5-
[.target 4]
6-
expected: FAIL
7-
8-
[.target 5]
9-
expected: FAIL
10-
11-
[.target 6]
12-
expected: FAIL
13-
14-
[.target 7]
15-
expected: FAIL
16-
17-
[.target 9]
18-
expected: FAIL
19-
20-
[.target 12]
21-
expected: FAIL
22-
23-
[.target 14]
24-
expected: FAIL
25-
26-
[.target 16]
27-
expected: FAIL
28-
29-
[.target 19]
30-
expected: FAIL
31-
32-
[.target 20]
33-
expected: FAIL
34-
35-
[.target 21]
36-
expected: FAIL
37-
38-
[.target 22]
39-
expected: FAIL
40-
41-
[.target 8]
42-
expected: FAIL
43-
44-
[.target 13]
45-
expected: FAIL
46-
47-
[.target 23]
48-
expected: FAIL
49-
50-
[.target 26]
51-
expected: FAIL
52-
53-
[.target 27]
54-
expected: FAIL
55-
56-
[.target 28]
57-
expected: FAIL
58-
59-
[.target 29]
60-
expected: FAIL
61-
62-
[.target 30]
63-
expected: FAIL
64-
65-
[.target 31]
66-
expected: FAIL
67-
68-
[.target 33]
69-
expected: FAIL
70-
71-
[.target 35]
72-
expected: FAIL
73-
74-
[.target 36]
75-
expected: FAIL
76-
77-
[.target 37]
78-
expected: FAIL
79-
80-
[.target 38]
81-
expected: FAIL
82-
83-
[.target 39]
84-
expected: FAIL
85-
86-
[.target 1]
87-
expected: FAIL
88-
89-
[.target 15]
90-
expected: FAIL
91-
922
[.target 3]
93-
expected:
94-
if os != "android": FAIL
3+
expected: [PASS, FAIL]
954

965
[.target 10]
97-
expected:
98-
if os != "android": FAIL
6+
expected: [PASS, FAIL]
997

1008
[.target 17]
101-
expected:
102-
if os != "android": FAIL
9+
expected: [PASS, FAIL]
10310

10411
[.target 32]
10512
expected:
10613
if asan or tsan: [PASS, FAIL]
10714
if (os == "linux") and debug and fission: [FAIL, PASS]
10815
if os == "android": PASS
109-
FAIL
110-
111-
[.target 24]
112-
expected: FAIL
113-
114-
[.target 25]
115-
expected: FAIL

testing/web-platform/meta/css/css-anchor-position/anchor-name-in-shadow.html.ini

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,3 @@
33
if (os == "android") and fission: [TIMEOUT, OK]
44
[anchor() in shadow tree should not match host anchor-name]
55
expected: FAIL
6-
7-
[anchor-name should not leak out of a shadow tree]
8-
expected: FAIL

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
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
[anchor-position-003.html]
22
expected:
33
if (os == "android") and fission: [OK, TIMEOUT]
4-
[.target 1]
5-
expected: FAIL

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

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)