Skip to content

Commit 21c7700

Browse files
zp1996moz-wptsync-bot
authored andcommitted
Bug 1980051 [wpt PR 54043] - Fix offsetParent computation for elements with fixed position, a=testonly
Automatic update from web-platform-tests Fix offsetParent computation for elements with fixed position (#54043) Consider this test case: <div style="transform: translate(50px, 50px);" id="transform"> <div style="position: fixed;" id="fixed"></div> </div> Observed behavior: `div#fixed` has `offsetParent = null`, but its `offsetLeft` and `offsetTop` are relative to `div#transform` (both values = 0). Specification conflict, current CSSOM spec states: > The element’s computed value of the position property is fixed. This contradicts the observed behavior since transform establishes a containing block for fixed-position descendants. Other properties like will-change and contain can trigger this too. Proposed Specification Update of `offsetParent`: > If any of the following holds true return null and terminate this algorithm: - The element’s computed value of the position property is fixed and no ancestor establishes a fixed position containing block. > Let ancestor be the parent of the element in the flat tree and repeat these substeps: - If ancestor is closed-shadow-hidden from the element, the element has a computed `position` value of `fixed`, and no ancestor establishes a fixed position containing block, terminate this algorithm and return null. Implement it in Chromium first, then update the spec. See w3c/csswg-drafts#12352 for more details. Bug: 40694036 Change-Id: I4bf32a9a5ab73c44051d13fd9c79eac7515c973e Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6774502 Reviewed-by: Ian Kilpatrick <ikilpatrick@chromium.org> Commit-Queue: Peng Zhou <zhoupeng.1996@bytedance.com> Cr-Commit-Position: refs/heads/main@{#1492432} Co-authored-by: zhoupeng <zhoupeng.1996@bytedance.com> -- wpt-commits: ae114742821d016d3dc6cf2e816d801847b78193 wpt-pr: 54043
1 parent fe9a008 commit 21c7700

File tree

3 files changed

+133
-6
lines changed

3 files changed

+133
-6
lines changed

testing/web-platform/tests/css/css-anchor-position/anchor-position-multicol-002.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
data-offset-x=26 data-offset-y=140
9191
data-expected-width=50 data-expected-height=90></div>
9292
<div class="target fixed target1-rb"
93-
data-offset-x=66 data-offset-y=220></div>
93+
data-offset-x=58 data-offset-y=215></div>
9494
</div>
9595

9696
<!-- The containing block of querying elements is a multi-column. -->
@@ -100,10 +100,10 @@
100100
<div class="target target1-rb"
101101
data-offset-x=294 data-offset-y=95></div>
102102
<div class="target fixed target1"
103-
data-offset-x=152 data-offset-y=10
103+
data-offset-x=144 data-offset-y=5
104104
data-expected-width=160 data-expected-height=100></div>
105105
<div class="target fixed target1-rb"
106-
data-offset-x=302 data-offset-y=100></div>
106+
data-offset-x=294 data-offset-y=95></div>
107107
</div>
108108

109109
<!-- The containing block of querying elements is not fragmented. -->
@@ -113,9 +113,9 @@
113113
<div class="target target1-rb"
114114
data-offset-x=294 data-offset-y=95></div>
115115
<div class="target fixed target1"
116-
data-offset-x=152 data-offset-y=10
116+
data-offset-x=144 data-offset-y=5
117117
data-expected-width=160 data-expected-height=100></div>
118118
<div class="target fixed target1-rb"
119-
data-offset-x=302 data-offset-y=100></div>
120-
</div>
119+
data-offset-x=294 data-offset-y=95></div>
120+
</div>
121121
</body>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<!DOCTYPE html>
2+
<meta charset="utf-8">
3+
<link rel="author" href="mailto:zhoupeng.1996@bytedance.com">
4+
<link rel="help" href="https://www.w3.org/TR/css-position-3/#fixed-cb">
5+
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/12352">
6+
<script src="/resources/testharness.js"></script>
7+
<script src="/resources/testharnessreport.js"></script>
8+
<style>
9+
#a, #b, #c, #d, #e, #f {
10+
position: fixed;
11+
}
12+
#margin {
13+
margin: 10px;
14+
}
15+
#transform {
16+
transform: translateX(10px);
17+
}
18+
#perspective {
19+
perspective: 10px;
20+
}
21+
#filter {
22+
filter: opacity(25%);
23+
}
24+
#contain {
25+
contain: paint;
26+
}
27+
</style>
28+
29+
<body>
30+
<div id="a"></div>
31+
<div id="margin">
32+
<div><div id="b"></div></div>
33+
</div>
34+
35+
<div id="transform">
36+
<div><div id="c"></div></div>
37+
</div>
38+
<div id="perspective">
39+
<div><div id="d"></div></div>
40+
</div>
41+
<div id="filter">
42+
<div><div id="e"></div></div>
43+
</div>
44+
<div id="contain">
45+
<div><div id="f"></div></div>
46+
</div>
47+
</body>
48+
49+
<script>
50+
test(() => {
51+
assert_equals(a.offsetParent, null);
52+
assert_equals(b.offsetParent, null);
53+
}, 'If the containing block for a fixed positioned element is viewport, the offsetParent should be null.');
54+
55+
test(() => {
56+
assert_equals(c.offsetParent, transform);
57+
assert_equals(d.offsetParent, perspective);
58+
assert_equals(e.offsetParent, filter);
59+
assert_equals(f.offsetParent, contain);
60+
}, 'If the containing block for a fixed positioned element is the nearest ancestor box, the offsetParent should be the nearest ancestor box.');
61+
</script>

testing/web-platform/tests/shadow-dom/offsetParent-across-shadow-boundaries.html

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,72 @@
8181
testOffsetParentOnElementAssignedToSlotInsideOffsetParent('open');
8282
testOffsetParentOnElementAssignedToSlotInsideOffsetParent('closed');
8383

84+
function testOffsetParentOnElementAssignedToSlotInsideFixedPositionWithContainingBlock(mode) {
85+
test(function () {
86+
const host = document.createElement('div');
87+
host.innerHTML = '<div id="target"></div>';
88+
container.appendChild(host);
89+
this.add_cleanup(() => host.remove());
90+
const shadowRoot = host.attachShadow({mode});
91+
shadowRoot.innerHTML = [
92+
'<div style="transform: translate(10px, 10px);" id="wrapper">',
93+
'<div style="position: fixed; padding-left: 85px; padding-top: 45px;">',
94+
'<slot></slot>',
95+
'</div></div>'].join('');
96+
const target = host.querySelector('#target');
97+
assert_equals(target.offsetParent, container);
98+
assert_equals(target.offsetLeft, 85);
99+
assert_equals(target.offsetTop, 45);
100+
}, `offsetParent must return the fixed position containing block of an element when the context object is assigned to a slot within a fixed containing block in shadow tree of ${mode} mode`);
101+
}
102+
103+
testOffsetParentOnElementAssignedToSlotInsideFixedPositionWithContainingBlock('open');
104+
testOffsetParentOnElementAssignedToSlotInsideFixedPositionWithContainingBlock('closed');
105+
106+
function testOffsetParentOnFixedElementAssignedToSlotInsideFixedPositionWithContainingBlock(mode) {
107+
test(function () {
108+
const host = document.createElement('div');
109+
host.innerHTML = '<div id="target" style="position: fixed;"></div>';
110+
container.appendChild(host);
111+
this.add_cleanup(() => host.remove());
112+
const shadowRoot = host.attachShadow({mode});
113+
shadowRoot.innerHTML = [
114+
'<div style="transform: translate(10px, 10px);" id="wrapper">',
115+
'<div style="position: fixed; padding-left: 85px; padding-top: 45px;">',
116+
'<slot></slot>',
117+
'</div></div>'].join('');
118+
const target = host.querySelector('#target');
119+
assert_equals(target.offsetParent, container);
120+
assert_equals(target.offsetLeft, 85);
121+
assert_equals(target.offsetTop, 45);
122+
}, `offsetParent must return the fixed position containing block of a fixed element when the context object is assigned to a slot within a fixed containing block in shadow tree of ${mode} mode`);
123+
}
124+
125+
testOffsetParentOnFixedElementAssignedToSlotInsideFixedPositionWithContainingBlock('open');
126+
testOffsetParentOnFixedElementAssignedToSlotInsideFixedPositionWithContainingBlock('closed');
127+
128+
function testOffsetParentOnElementAssignedToSlotInsideFixedPosition(mode) {
129+
test(function () {
130+
const host = document.createElement('div');
131+
host.innerHTML = '<div id="target"></div>';
132+
container.appendChild(host);
133+
this.add_cleanup(() => host.remove());
134+
const shadowRoot = host.attachShadow({mode});
135+
shadowRoot.innerHTML = [
136+
'<div id="fixed" style="position: fixed; padding-left: 85px; padding-top: 45px;">',
137+
'<slot></slot>',
138+
'</div>'].join('');
139+
const target = host.querySelector('#target');
140+
const fixed = shadowRoot.querySelector('#fixed');
141+
assert_equals(target.offsetParent, null);
142+
assert_equals(target.offsetLeft, 85 + fixed.offsetLeft);
143+
assert_equals(target.offsetTop, 45 + fixed.offsetTop);
144+
}, `offsetParent must return null when the context object is assigned to a slot without a fixed containing block in shadow tree of ${mode} mode`);
145+
}
146+
147+
testOffsetParentOnElementAssignedToSlotInsideFixedPosition('open');
148+
testOffsetParentOnElementAssignedToSlotInsideFixedPosition('closed');
149+
84150
function testOffsetParentOnElementAssignedToSlotInsideNestedOffsetParents(mode) {
85151
test(function () {
86152
const host = document.createElement('div');

0 commit comments

Comments
 (0)