Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / layout / style / test / file_animations_omta_scroll.html
blob625b4b6c19a9cde25cc36b07842fcbe3fe132aaf
1 <!DOCTYPE HTML>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width,initial-scale=1">
6 <title>Test for css-animations running on the compositor thread with scroll-timeline</title>
7 <script src="/tests/SimpleTest/SimpleTest.js"></script>
8 <script src="/tests/SimpleTest/paint_listener.js"></script>
9 <script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
10 <script type="application/javascript" src="animation_utils.js"></script>
11 <style type="text/css">
12 @keyframes transform_anim {
13 from { transform: translate(50px); }
14 to { transform: translate(150px); }
17 @keyframes always_fifty {
18 from, to { transform: translate(50px); }
21 @keyframes geometry {
22 from { width: 50px; }
23 to { width: 100px; }
26 .target {
27 /* The animation target needs geometry in order to qualify for OMTA */
28 width: 100px;
29 height: 100px;
30 background-color: green;
33 .scroller {
34 width: 100px;
35 height: 100px;
36 overflow: scroll;
37 scroll-timeline-name: scroll_timeline;
40 .content {
41 block-size: 100%;
42 padding-block-end: 100px;
44 </style>
45 </head>
46 <body>
47 <div id="display"></div>
48 <pre id="test"></pre>
49 </body>
50 <script type="application/javascript">
51 "use strict";
53 // Global state
54 var gScroller = null;
55 var gDiv = null;
57 // Shortcut omta_is and friends by filling in the initial 'elem' argument
58 // with gDiv.
59 [ 'omta_is', 'omta_todo_is', 'omta_is_approx' ].forEach(function(fn) {
60 var origFn = window[fn];
61 window[fn] = function() {
62 var args = Array.from(arguments);
63 if (!(args[0] instanceof Element)) {
64 args.unshift(gDiv);
66 return origFn.apply(window, args);
68 });
70 // Shortcut new_div and done_div to update gDiv
71 var originalNewDiv = window.new_div;
72 window.new_div = function(style) {
73 [ gDiv ] = originalNewDiv(style);
75 var originalDoneDiv = window.done_div;
76 window.done_div = function() {
77 originalDoneDiv();
78 gDiv = null;
81 // Bind the ok and todo to the opener, and close this window when we finish.
82 var ok = opener.ok.bind(opener);
83 var todo = opener.todo.bind(opener);
84 function finish() {
85 var o = opener;
86 self.close();
87 o.SimpleTest.finish();
90 function new_scroller() {
91 gScroller = document.createElement('div');
92 gScroller.className = `scroller`;
94 let content = document.createElement('div');
95 content.className = 'content';
97 gScroller.appendChild(content);
98 document.getElementById("display").appendChild(gScroller);
99 return gScroller;
102 function done_scroller() {
103 gScroller.firstChild.remove();
104 gScroller.remove();
105 gScroller = null;
108 waitUntilApzStable().then(() => {
109 runOMTATest(function() {
110 var onAbort = function() {
111 if (gDiv) {
112 done_div();
114 if (gScroller) {
115 done_scroller();
118 runAllAsyncAnimTests(onAbort).then(finish);
119 }, finish);
122 //----------------------------------------------------------------------
124 // Test cases
126 //----------------------------------------------------------------------
128 // The non-omta property with scroll-timeline together with an omta property
129 // with document-timeline.
130 addAsyncAnimTest(async function() {
131 new_scroller();
132 new_div("animation: geometry 10s scroll_timeline, always_fifty 1s infinite;");
133 await waitForPaintsFlushed();
135 // Note: width is not a OMTA property, so it must be running on the main
136 // thread.
137 omta_is("transform", { tx: 50 }, RunningOn.Compositor,
138 "transform animations should runs on compositor thread");
140 done_div();
141 done_scroller();
144 // transform property with scroll-driven animations.
145 addAsyncAnimTest(async function() {
146 let scroller = new_scroller();
147 new_div("animation: transform_anim 1s linear scroll_timeline;");
148 await waitForPaintsFlushed();
150 scroller.scrollTop = 50;
151 await waitForPaintsFlushed();
153 omta_is_approx("transform", { tx: 100 }, 0.1, RunningOn.Compositor,
154 "scroll transform animations should runs on compositor " +
155 "thread");
157 done_div();
158 done_scroller();
162 // The scroll-driven animation with an underlying value and make it go from the
163 // active phase to the before phase.
164 addAsyncAnimTest(async function() {
165 let scroller = new_scroller();
166 new_div("animation: always_fifty 5s linear 5s scroll_timeline; " +
167 "transform: translate(25px);");
168 await waitForPaintsFlushed();
170 // NOTE: getOMTAStyle() can't detect the animation is running on the
171 // compositor during the delay phase.
172 omta_is_approx("transform", { tx: 25 }, 0.1, RunningOn.Either,
173 "The scroll animation is in delay");
175 scroller.scrollTop = 75;
176 await waitForPaintsFlushed();
178 omta_is_approx("transform", { tx: 50 }, 0.1, RunningOn.Compositor,
179 "scroll transform animations should runs on compositor " +
180 "thread");
182 // Use setAsyncScrollOffset() to update apz (compositor thread only) to make
183 // sure Bug 1776077 is reproducible.
184 let utils = SpecialPowers.wrap(window).windowUtils;
185 utils.setAsyncScrollOffset(scroller, 0, -50);
186 utils.advanceTimeAndRefresh(16);
187 utils.restoreNormalRefresh();
188 await waitForPaints();
190 // NOTE: setAsyncScrollOffset() doesn't update main thread, so we check the
191 // OMTA style directly.
192 let compositorStr =
193 SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform");
194 ok(compositorStr === "matrix(1, 0, 0, 1, 25, 0)",
195 "scroll animations is in delay phase before calling main thread style " +
196 "udpate");
198 scroller.scrollTop = 25;
199 await waitForPaintsFlushed();
201 compositorStr = SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform");
202 ok(compositorStr === "",
203 "scroll animation in delay phase clears its OMTA style");
204 omta_is_approx("transform", { tx: 25 }, 0.1, RunningOn.Either,
205 "The scroll animation is in delay");
207 done_div();
208 done_scroller();
211 // The scroll-driven animation without an underlying value and make it go from
212 // the active phase to the before phase.
213 addAsyncAnimTest(async function() {
214 let scroller = new_scroller();
215 new_div("animation: always_fifty 5s linear 5s scroll_timeline; ");
216 await waitForPaintsFlushed();
218 // NOTE: getOMTAStyle() can't detect the animation is running on the
219 // compositor during the delay phase.
220 omta_is_approx("transform", { tx: 0 }, 0.1, RunningOn.Either,
221 "The scroll animation is in delay");
223 scroller.scrollTop = 75;
224 await waitForPaintsFlushed();
226 omta_is_approx("transform", { tx: 50 }, 0.1, RunningOn.Compositor,
227 "scroll transform animations should runs on compositor " +
228 "thread");
230 // Use setAsyncScrollOffset() to update apz (compositor thread only) to make
231 // sure Bug 1776077 is reproducible.
232 let utils = SpecialPowers.wrap(window).windowUtils;
233 utils.setAsyncScrollOffset(scroller, 0, -50);
234 utils.advanceTimeAndRefresh(16);
235 utils.restoreNormalRefresh();
236 await waitForPaints();
238 // NOTE: setAsyncScrollOffset() doesn't update main thread, so we check the
239 // OMTA style directly.
240 let compositorStr =
241 SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform");
242 ok(compositorStr === "matrix(1, 0, 0, 1, 0, 0)",
243 "scroll animations is in delay phase before calling main thread style " +
244 "udpate");
246 done_div();
247 done_scroller();
250 // The scroll-driven animation is in delay, together with other runing
251 // animations.
252 addAsyncAnimTest(async function() {
253 let scroller = new_scroller();
254 new_div("animation: transform_anim 10s linear -5s paused, " +
255 " always_fifty 5s linear 5s scroll_timeline;");
256 await waitForPaintsFlushed();
258 omta_is_approx("transform", { tx: 100 }, 0.1, RunningOn.Compositor,
259 "The scroll animation is in delay");
261 scroller.scrollTop = 75;
262 await waitForPaintsFlushed();
264 omta_is_approx("transform", { tx: 50 }, 0.1, RunningOn.Compositor,
265 "scroll transform animations should runs on compositor " +
266 "thread");
268 let utils = SpecialPowers.wrap(window).windowUtils;
269 utils.setAsyncScrollOffset(scroller, 0, -50);
270 utils.advanceTimeAndRefresh(16);
271 utils.restoreNormalRefresh();
272 await waitForPaints();
274 // NOTE: setAsyncScrollOffset() doesn't update main thread, so we check the
275 // OMTA style directly.
276 let compositorStr =
277 SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform");
278 ok(compositorStr === "matrix(1, 0, 0, 1, 100, 0)",
279 "scroll animations is in delay phase before calling main thread style " +
280 "udpate");
282 done_div();
283 done_scroller();
286 addAsyncAnimTest(async function() {
287 let iframe = document.createElement("iframe");
288 iframe.setAttribute("style", "width: 200px; height: 200px");
289 iframe.setAttribute("scrolling", "no");
290 iframe.srcdoc =
291 "<!DOCTYPE HTML>" +
292 "<html style='min-height: 100%; padding-bottom: 100px;'>" +
293 "<style>" +
294 "@keyframes anim { from, to { transform: translate(50px) } }" +
295 "</style>" +
296 "<div id='target_in_iframe' " +
297 " style='width:50px; height:50px; background:green;" +
298 " animation: anim 10s linear scroll(root);'>" +
299 "</div>" +
300 "</html>";
302 await new Promise(resolve => {
303 iframe.onload = resolve;
304 document.body.appendChild(iframe);
307 gDiv = iframe.contentDocument.getElementById("target_in_iframe");
309 const root = iframe.contentDocument.scrollingElement;
310 const maxScroll = root.scrollHeight - root.clientHeight;
311 root.scrollTop = 0.5 * maxScroll;
312 await waitForPaintsFlushed();
314 omta_is_approx("transform", { tx: 50 }, 0, RunningOn.MainThread,
315 "scroll transform animations inside an iframe with " +
316 "scrolling:no should run on the main thread");
318 gDiv = null;
319 iframe.remove();
322 // FIXME: Bug 1818346. Support OMTA for view-timeline.
323 addAsyncAnimTest(async function() {
324 let scroller = document.createElement("div");
325 scroller.style.width = "100px";
326 scroller.style.height = "100px";
327 // Use hidden so we don't have scrollbar, to make sure the scrollport size
328 // is 100px x 100px, so view progress visibility range is 100px on block axis.
329 scroller.style.overflow = "hidden";
331 let content1 = document.createElement("div");
332 content1.style.height = "150px";
333 scroller.appendChild(content1);
335 let subject = document.createElement("div");
336 subject.style.width = "50px";
337 subject.style.height = "50px";
338 subject.style.viewTimelineName = "view_timeline";
339 scroller.appendChild(subject);
341 // Let |target| be the child of |subject|, so view-timeline-name property of
342 // |subject| is referenceable.
343 let target = document.createElement("div");
344 target.style.width = "10px";
345 target.style.height = "10px";
346 subject.appendChild(target);
347 gDiv = target;
349 let content2 = document.createElement("div");
350 content2.style.height = "150px";
351 scroller.appendChild(content2);
353 // So the DOM tree looks like this:
354 // <div class=scroller> <!-- "scroller", height: 100px; -->
355 // <div></div> <!-- "", height: 150px -->
356 // <div></div> <!-- "subject", height: 50px; -->
357 // <div></div> <!-- "", height: 150px; -->
358 // </div>
359 // The subject is in view when scroller.scrollTop is [50px, 200px].
360 document.getElementById("display").appendChild(scroller);
361 await waitForPaintsFlushed();
363 scroller.scrollTop = 0;
364 target.style.animation = "transform_anim 10s linear";
365 target.style.animationTimeline = "view_timeline";
366 await waitForPaintsFlushed();
368 omta_is_approx("transform", { tx: 0 }, 0.1, RunningOn.OnMainThread,
369 "The scroll animation is out of view");
371 scroller.scrollTop = 50;
372 await waitForPaintsFlushed();
374 omta_is_approx("transform", { tx: 50 }, 0.1, RunningOn.OnMainThread,
375 "The scroll animation is 0%");
377 scroller.scrollTop = 125;
378 await waitForPaintsFlushed();
380 omta_is_approx("transform", { tx: 100 }, 0.1, RunningOn.OnMainThread,
381 "The scroll animation is 50%");
383 target.remove();
384 content2.remove();
385 subject.remove();
386 content1.remove();
387 scroller.remove();
388 gDiv = null;
391 </script>
392 </html>