Update testharness.js from upstream
[WebKit-https.git] / LayoutTests / fast / shadow-dom / offsetParent-across-shadow-boundaries.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
5 <meta name="assert" content="offsetParent should only return nodes that are shadow including ancestor">
6 <link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetparent">
7 <link rel="help" href="https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor">
8 <script src="../../resources/testharness.js"></script>
9 <script src="../../resources/testharnessreport.js"></script>
10 <script src="resources/event-path-test-helpers.js"></script>
11 </head>
12 <body>
13 <div id="log"></div>
14 <div id="container" style="position: relative"></div>
15 <script>
16
17 const container = document.getElementById('container');
18
19 function testOffsetParentInShadowTree(mode) {
20     test(function () {
21         const host = document.createElement('div');
22         container.appendChild(host);
23         this.add_cleanup(() => host.remove());
24         const shadowRoot = host.attachShadow({mode});
25         shadowRoot.innerHTML = '<div id="relativeParent" style="position: relative; padding-left: 100px; padding-top: 70px;"><div id="target"></div></div>';
26         const relativeParent = shadowRoot.getElementById('relativeParent');
27
28         assert_true(relativeParent instanceof HTMLDivElement);
29         const target = shadowRoot.getElementById('target');
30         assert_equals(target.offsetParent, relativeParent);
31         assert_equals(target.offsetLeft, 100);
32         assert_equals(target.offsetTop, 70);
33     }, `offsetParent must return the offset parent in the same shadow tree of ${mode} mode`);
34 }
35
36 testOffsetParentInShadowTree('open');
37 testOffsetParentInShadowTree('closed');
38
39 function testOffsetParentInNestedShadowTrees(mode) {
40     test(function () {
41         const outerHost = document.createElement('section');
42         container.appendChild(outerHost);
43         this.add_cleanup(() => outerHost.remove());
44         const outerShadow = outerHost.attachShadow({mode});
45         outerShadow.innerHTML = '<section id="outerParent" style="position: absolute; top: 50px; left: 50px;"></section>';
46
47         const innerHost = document.createElement('div');
48         outerShadow.firstChild.appendChild(innerHost);
49         const innerShadow = innerHost.attachShadow({mode});
50         innerShadow.innerHTML = '<div id="innerParent" style="position: relative; padding-left: 60px; padding-top: 40px;"><div id="target"></div></div>';
51         const innerParent = innerShadow.getElementById('innerParent');
52
53         const target = innerShadow.getElementById('target');
54         assert_true(innerParent instanceof HTMLDivElement);
55         assert_equals(target.offsetParent, innerParent);
56         assert_equals(target.offsetLeft, 60);
57         assert_equals(target.offsetTop, 40);
58
59         outerHost.remove();
60     }, `offsetParent must return the offset parent in the same shadow tree of ${mode} mode even when nested`);
61 }
62
63 testOffsetParentInNestedShadowTrees('open');
64 testOffsetParentInNestedShadowTrees('closed');
65
66 function testOffsetParentOnElementAssignedToSlotInsideOffsetParent(mode) {
67     test(function () {
68         const host = document.createElement('div');
69         host.innerHTML = '<div id="target"></div>'
70         container.appendChild(host);
71         this.add_cleanup(() => host.remove());
72         const shadowRoot = host.attachShadow({mode});
73         shadowRoot.innerHTML = '<div style="position: relative; padding-left: 85px; padding-top: 45px;"><slot></slot></div>';
74         const target = host.querySelector('#target');
75         assert_equals(target.offsetParent, container);
76         assert_equals(target.offsetLeft, 85);
77         assert_equals(target.offsetTop, 45);
78     }, `offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of ${mode} mode`);
79 }
80
81 testOffsetParentOnElementAssignedToSlotInsideOffsetParent('open');
82 testOffsetParentOnElementAssignedToSlotInsideOffsetParent('closed');
83
84 function testOffsetParentOnElementAssignedToSlotInsideNestedOffsetParents(mode) {
85     test(function () {
86         const host = document.createElement('div');
87         host.innerHTML = '<div id="target" style="border:solid 1px blue;">hi</div>';
88         const previousBlock = document.createElement('div');
89         previousBlock.style.height = '12px';
90         container.append(previousBlock, host);
91         this.add_cleanup(() => { container.innerHTML = ''; });
92         const shadowRoot = host.attachShadow({mode});
93         shadowRoot.innerHTML = '<section style="position: relative; margin-left: 20px; margin-top: 100px; background: #ccc"><div style="position: absolute; top: 10px; left: 10px;"><slot></slot></div></section>';
94         const target = host.querySelector('#target');
95         assert_equals(target.offsetParent, container);
96         assert_equals(target.offsetLeft, 30);
97         assert_equals(target.offsetTop, 122);
98     }, `offsetParent must skip multiple offset parents of an element when the context object is assigned to a slot in a shadow tree of ${mode} mode`);
99 }
100
101 testOffsetParentOnElementAssignedToSlotInsideNestedOffsetParents('open');
102 testOffsetParentOnElementAssignedToSlotInsideNestedOffsetParents('closed');
103
104 function testOffsetParentOnElementAssignedToSlotInsideNestedShadowTrees(mode) {
105     test(function () {
106         const outerHost = document.createElement('section');
107         outerHost.innerHTML = '<div id="target"></div>';
108         container.appendChild(outerHost);
109         this.add_cleanup(() => outerHost.remove());
110         const outerShadow = outerHost.attachShadow({mode});
111         outerShadow.innerHTML = '<section style="position: absolute; top: 40px; left: 50px;"><div id="innerHost"><slot></slot></div></section>';
112
113         const innerShadow = outerShadow.getElementById('innerHost').attachShadow({mode});
114         innerShadow.innerHTML = '<div style="position: absolute; top: 200px; margin-left: 100px;"><slot></slot></div>';
115
116         const target = outerHost.querySelector('#target');
117         assert_equals(target.offsetParent, container);
118         assert_equals(target.offsetLeft, 150);
119         assert_equals(target.offsetTop, 240);
120         outerHost.remove();
121     }, `offsetParent must skip offset parents of an element when the context object is assigned to a slot in nested shadow trees of ${mode} mode`);
122 }
123
124 testOffsetParentOnElementAssignedToSlotInsideNestedShadowTrees('open');
125 testOffsetParentOnElementAssignedToSlotInsideNestedShadowTrees('closed');
126
127 function testOffsetParentOnElementInsideShadowTreeWithoutOffsetParent(mode) {
128     test(function () {
129         const outerHost = document.createElement('section');
130         container.appendChild(outerHost);
131         this.add_cleanup(() => outerHost.remove());
132         const outerShadow = outerHost.attachShadow({mode});
133         outerShadow.innerHTML = '<div id="innerHost"><div id="target"></div></div>';
134
135         const innerShadow = outerShadow.getElementById('innerHost').attachShadow({mode});
136         innerShadow.innerHTML = '<div style="position: absolute; top: 23px; left: 24px;"><slot></slot></div>';
137
138         const target = outerShadow.querySelector('#target');
139         assert_equals(target.offsetParent, container);
140         assert_equals(target.offsetLeft, 24);
141         assert_equals(target.offsetTop, 23);
142     }, `offsetParent must find the first offset parent which is a shadow-including ancestor of the context object even some shadow tree of ${mode} mode did not have any offset parent`);
143 }
144
145 testOffsetParentOnElementInsideShadowTreeWithoutOffsetParent('open');
146 testOffsetParentOnElementInsideShadowTreeWithoutOffsetParent('closed');
147
148 function testOffsetParentOnUnassignedChild(mode) {
149     test(function () {
150         const host = document.createElement('section');
151         host.innerHTML = '<div id="target"></div>';
152         this.add_cleanup(() => host.remove());
153         container.appendChild(host);
154         const shadowRoot = host.attachShadow({mode});
155         shadowRoot.innerHTML = '<section style="position: absolute; top: 50px; left: 50px;">content</section>';
156         const target = host.querySelector('#target');
157         assert_equals(target.offsetParent, null);
158         assert_equals(target.offsetLeft, 0);
159         assert_equals(target.offsetTop, 0);
160     }, `offsetParent must return null on a child element of a shadow host for the shadow tree in ${mode} mode which is not assigned to any slot`);
161 }
162
163 testOffsetParentOnUnassignedChild('open');
164 testOffsetParentOnUnassignedChild('closed');
165
166 function testOffsetParentOnAssignedChildNotInFlatTree(mode) {
167     test(function () {
168         const outerHost = document.createElement('section');
169         outerHost.innerHTML = '<div id="target"></div>';
170         container.appendChild(outerHost);
171         this.add_cleanup(() => outerHost.remove());
172         const outerShadow = outerHost.attachShadow({mode});
173         outerShadow.innerHTML = '<div id="innerHost"><div style="position: absolute; top: 50px; left: 50px;"><slot></slot></div></div>';
174
175         const innerShadow = outerShadow.getElementById('innerHost').attachShadow({mode});
176         innerShadow.innerHTML = '<div>content</div>';
177
178         const target = outerHost.querySelector('#target');
179         assert_equals(target.offsetParent, null);
180         assert_equals(target.offsetLeft, 0);
181         assert_equals(target.offsetTop, 0);
182     }, `offsetParent must return null on a child element of a shadow host for the shadow tree in ${mode} mode which is not in the flat tree`);
183 }
184
185 testOffsetParentOnAssignedChildNotInFlatTree('open');
186 testOffsetParentOnAssignedChildNotInFlatTree('closed');
187
188 </script>
189 </body>
190 </html>