summaryrefslogtreecommitdiffstats
path: root/layout/style/test/file_animations_omta_scroll.html
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/test/file_animations_omta_scroll.html')
-rw-r--r--layout/style/test/file_animations_omta_scroll.html392
1 files changed, 392 insertions, 0 deletions
diff --git a/layout/style/test/file_animations_omta_scroll.html b/layout/style/test/file_animations_omta_scroll.html
new file mode 100644
index 0000000000..625b4b6c19
--- /dev/null
+++ b/layout/style/test/file_animations_omta_scroll.html
@@ -0,0 +1,392 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <title>Test for css-animations running on the compositor thread with scroll-timeline</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+ <script type="application/javascript" src="animation_utils.js"></script>
+ <style type="text/css">
+ @keyframes transform_anim {
+ from { transform: translate(50px); }
+ to { transform: translate(150px); }
+ }
+
+ @keyframes always_fifty {
+ from, to { transform: translate(50px); }
+ }
+
+ @keyframes geometry {
+ from { width: 50px; }
+ to { width: 100px; }
+ }
+
+ .target {
+ /* The animation target needs geometry in order to qualify for OMTA */
+ width: 100px;
+ height: 100px;
+ background-color: green;
+ }
+
+ .scroller {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ scroll-timeline-name: scroll_timeline;
+ }
+
+ .content {
+ block-size: 100%;
+ padding-block-end: 100px;
+ }
+ </style>
+</head>
+<body>
+ <div id="display"></div>
+ <pre id="test"></pre>
+</body>
+<script type="application/javascript">
+"use strict";
+
+// Global state
+var gScroller = null;
+var gDiv = null;
+
+// Shortcut omta_is and friends by filling in the initial 'elem' argument
+// with gDiv.
+[ 'omta_is', 'omta_todo_is', 'omta_is_approx' ].forEach(function(fn) {
+ var origFn = window[fn];
+ window[fn] = function() {
+ var args = Array.from(arguments);
+ if (!(args[0] instanceof Element)) {
+ args.unshift(gDiv);
+ }
+ return origFn.apply(window, args);
+ };
+});
+
+// Shortcut new_div and done_div to update gDiv
+var originalNewDiv = window.new_div;
+window.new_div = function(style) {
+ [ gDiv ] = originalNewDiv(style);
+};
+var originalDoneDiv = window.done_div;
+window.done_div = function() {
+ originalDoneDiv();
+ gDiv = null;
+};
+
+// Bind the ok and todo to the opener, and close this window when we finish.
+var ok = opener.ok.bind(opener);
+var todo = opener.todo.bind(opener);
+function finish() {
+ var o = opener;
+ self.close();
+ o.SimpleTest.finish();
+}
+
+function new_scroller() {
+ gScroller = document.createElement('div');
+ gScroller.className = `scroller`;
+
+ let content = document.createElement('div');
+ content.className = 'content';
+
+ gScroller.appendChild(content);
+ document.getElementById("display").appendChild(gScroller);
+ return gScroller;
+}
+
+function done_scroller() {
+ gScroller.firstChild.remove();
+ gScroller.remove();
+ gScroller = null;
+}
+
+waitUntilApzStable().then(() => {
+ runOMTATest(function() {
+ var onAbort = function() {
+ if (gDiv) {
+ done_div();
+ }
+ if (gScroller) {
+ done_scroller();
+ }
+ };
+ runAllAsyncAnimTests(onAbort).then(finish);
+ }, finish);
+});
+
+//----------------------------------------------------------------------
+//
+// Test cases
+//
+//----------------------------------------------------------------------
+
+// The non-omta property with scroll-timeline together with an omta property
+// with document-timeline.
+addAsyncAnimTest(async function() {
+ new_scroller();
+ new_div("animation: geometry 10s scroll_timeline, always_fifty 1s infinite;");
+ await waitForPaintsFlushed();
+
+ // Note: width is not a OMTA property, so it must be running on the main
+ // thread.
+ omta_is("transform", { tx: 50 }, RunningOn.Compositor,
+ "transform animations should runs on compositor thread");
+
+ done_div();
+ done_scroller();
+});
+
+// transform property with scroll-driven animations.
+addAsyncAnimTest(async function() {
+ let scroller = new_scroller();
+ new_div("animation: transform_anim 1s linear scroll_timeline;");
+ await waitForPaintsFlushed();
+
+ scroller.scrollTop = 50;
+ await waitForPaintsFlushed();
+
+ omta_is_approx("transform", { tx: 100 }, 0.1, RunningOn.Compositor,
+ "scroll transform animations should runs on compositor " +
+ "thread");
+
+ done_div();
+ done_scroller();
+});
+
+
+// The scroll-driven animation with an underlying value and make it go from the
+// active phase to the before phase.
+addAsyncAnimTest(async function() {
+ let scroller = new_scroller();
+ new_div("animation: always_fifty 5s linear 5s scroll_timeline; " +
+ "transform: translate(25px);");
+ await waitForPaintsFlushed();
+
+ // NOTE: getOMTAStyle() can't detect the animation is running on the
+ // compositor during the delay phase.
+ omta_is_approx("transform", { tx: 25 }, 0.1, RunningOn.Either,
+ "The scroll animation is in delay");
+
+ scroller.scrollTop = 75;
+ await waitForPaintsFlushed();
+
+ omta_is_approx("transform", { tx: 50 }, 0.1, RunningOn.Compositor,
+ "scroll transform animations should runs on compositor " +
+ "thread");
+
+ // Use setAsyncScrollOffset() to update apz (compositor thread only) to make
+ // sure Bug 1776077 is reproducible.
+ let utils = SpecialPowers.wrap(window).windowUtils;
+ utils.setAsyncScrollOffset(scroller, 0, -50);
+ utils.advanceTimeAndRefresh(16);
+ utils.restoreNormalRefresh();
+ await waitForPaints();
+
+ // NOTE: setAsyncScrollOffset() doesn't update main thread, so we check the
+ // OMTA style directly.
+ let compositorStr =
+ SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform");
+ ok(compositorStr === "matrix(1, 0, 0, 1, 25, 0)",
+ "scroll animations is in delay phase before calling main thread style " +
+ "udpate");
+
+ scroller.scrollTop = 25;
+ await waitForPaintsFlushed();
+
+ compositorStr = SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform");
+ ok(compositorStr === "",
+ "scroll animation in delay phase clears its OMTA style");
+ omta_is_approx("transform", { tx: 25 }, 0.1, RunningOn.Either,
+ "The scroll animation is in delay");
+
+ done_div();
+ done_scroller();
+});
+
+// The scroll-driven animation without an underlying value and make it go from
+// the active phase to the before phase.
+addAsyncAnimTest(async function() {
+ let scroller = new_scroller();
+ new_div("animation: always_fifty 5s linear 5s scroll_timeline; ");
+ await waitForPaintsFlushed();
+
+ // NOTE: getOMTAStyle() can't detect the animation is running on the
+ // compositor during the delay phase.
+ omta_is_approx("transform", { tx: 0 }, 0.1, RunningOn.Either,
+ "The scroll animation is in delay");
+
+ scroller.scrollTop = 75;
+ await waitForPaintsFlushed();
+
+ omta_is_approx("transform", { tx: 50 }, 0.1, RunningOn.Compositor,
+ "scroll transform animations should runs on compositor " +
+ "thread");
+
+ // Use setAsyncScrollOffset() to update apz (compositor thread only) to make
+ // sure Bug 1776077 is reproducible.
+ let utils = SpecialPowers.wrap(window).windowUtils;
+ utils.setAsyncScrollOffset(scroller, 0, -50);
+ utils.advanceTimeAndRefresh(16);
+ utils.restoreNormalRefresh();
+ await waitForPaints();
+
+ // NOTE: setAsyncScrollOffset() doesn't update main thread, so we check the
+ // OMTA style directly.
+ let compositorStr =
+ SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform");
+ ok(compositorStr === "matrix(1, 0, 0, 1, 0, 0)",
+ "scroll animations is in delay phase before calling main thread style " +
+ "udpate");
+
+ done_div();
+ done_scroller();
+});
+
+// The scroll-driven animation is in delay, together with other runing
+// animations.
+addAsyncAnimTest(async function() {
+ let scroller = new_scroller();
+ new_div("animation: transform_anim 10s linear -5s paused, " +
+ " always_fifty 5s linear 5s scroll_timeline;");
+ await waitForPaintsFlushed();
+
+ omta_is_approx("transform", { tx: 100 }, 0.1, RunningOn.Compositor,
+ "The scroll animation is in delay");
+
+ scroller.scrollTop = 75;
+ await waitForPaintsFlushed();
+
+ omta_is_approx("transform", { tx: 50 }, 0.1, RunningOn.Compositor,
+ "scroll transform animations should runs on compositor " +
+ "thread");
+
+ let utils = SpecialPowers.wrap(window).windowUtils;
+ utils.setAsyncScrollOffset(scroller, 0, -50);
+ utils.advanceTimeAndRefresh(16);
+ utils.restoreNormalRefresh();
+ await waitForPaints();
+
+ // NOTE: setAsyncScrollOffset() doesn't update main thread, so we check the
+ // OMTA style directly.
+ let compositorStr =
+ SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform");
+ ok(compositorStr === "matrix(1, 0, 0, 1, 100, 0)",
+ "scroll animations is in delay phase before calling main thread style " +
+ "udpate");
+
+ done_div();
+ done_scroller();
+});
+
+addAsyncAnimTest(async function() {
+ let iframe = document.createElement("iframe");
+ iframe.setAttribute("style", "width: 200px; height: 200px");
+ iframe.setAttribute("scrolling", "no");
+ iframe.srcdoc =
+ "<!DOCTYPE HTML>" +
+ "<html style='min-height: 100%; padding-bottom: 100px;'>" +
+ "<style>" +
+ "@keyframes anim { from, to { transform: translate(50px) } }" +
+ "</style>" +
+ "<div id='target_in_iframe' " +
+ " style='width:50px; height:50px; background:green;" +
+ " animation: anim 10s linear scroll(root);'>" +
+ "</div>" +
+ "</html>";
+
+ await new Promise(resolve => {
+ iframe.onload = resolve;
+ document.body.appendChild(iframe);
+ });
+
+ gDiv = iframe.contentDocument.getElementById("target_in_iframe");
+
+ const root = iframe.contentDocument.scrollingElement;
+ const maxScroll = root.scrollHeight - root.clientHeight;
+ root.scrollTop = 0.5 * maxScroll;
+ await waitForPaintsFlushed();
+
+ omta_is_approx("transform", { tx: 50 }, 0, RunningOn.MainThread,
+ "scroll transform animations inside an iframe with " +
+ "scrolling:no should run on the main thread");
+
+ gDiv = null;
+ iframe.remove();
+});
+
+// FIXME: Bug 1818346. Support OMTA for view-timeline.
+addAsyncAnimTest(async function() {
+ let scroller = document.createElement("div");
+ scroller.style.width = "100px";
+ scroller.style.height = "100px";
+ // Use hidden so we don't have scrollbar, to make sure the scrollport size
+ // is 100px x 100px, so view progress visibility range is 100px on block axis.
+ scroller.style.overflow = "hidden";
+
+ let content1 = document.createElement("div");
+ content1.style.height = "150px";
+ scroller.appendChild(content1);
+
+ let subject = document.createElement("div");
+ subject.style.width = "50px";
+ subject.style.height = "50px";
+ subject.style.viewTimelineName = "view_timeline";
+ scroller.appendChild(subject);
+
+ // Let |target| be the child of |subject|, so view-timeline-name property of
+ // |subject| is referenceable.
+ let target = document.createElement("div");
+ target.style.width = "10px";
+ target.style.height = "10px";
+ subject.appendChild(target);
+ gDiv = target;
+
+ let content2 = document.createElement("div");
+ content2.style.height = "150px";
+ scroller.appendChild(content2);
+
+ // So the DOM tree looks like this:
+ // <div class=scroller> <!-- "scroller", height: 100px; -->
+ // <div></div> <!-- "", height: 150px -->
+ // <div></div> <!-- "subject", height: 50px; -->
+ // <div></div> <!-- "", height: 150px; -->
+ // </div>
+ // The subject is in view when scroller.scrollTop is [50px, 200px].
+ document.getElementById("display").appendChild(scroller);
+ await waitForPaintsFlushed();
+
+ scroller.scrollTop = 0;
+ target.style.animation = "transform_anim 10s linear";
+ target.style.animationTimeline = "view_timeline";
+ await waitForPaintsFlushed();
+
+ omta_is_approx("transform", { tx: 0 }, 0.1, RunningOn.OnMainThread,
+ "The scroll animation is out of view");
+
+ scroller.scrollTop = 50;
+ await waitForPaintsFlushed();
+
+ omta_is_approx("transform", { tx: 50 }, 0.1, RunningOn.OnMainThread,
+ "The scroll animation is 0%");
+
+ scroller.scrollTop = 125;
+ await waitForPaintsFlushed();
+
+ omta_is_approx("transform", { tx: 100 }, 0.1, RunningOn.OnMainThread,
+ "The scroll animation is 50%");
+
+ target.remove();
+ content2.remove();
+ subject.remove();
+ content1.remove();
+ scroller.remove();
+ gDiv = null;
+});
+
+</script>
+</html>