commit e6859b8c29f22121ebbe796970825109dc0bb282
parent c5e03584c8aa591ce00d8e94543bba2b7c301b35
Author: Vladimir Levin <vmpstr@chromium.org>
Date: Tue, 21 Oct 2025 10:30:22 +0000
Bug 1993636 [wpt PR 55336] - VT: Implement waitUntil, a=testonly
Automatic update from web-platform-tests
VT: Implement waitUntil
This implements waitUntil by keeping a count of pending promises on
VT and only allowing it to go beyond the animating phase when this
count is 0.
Based on resolution
https://github.com/w3c/csswg-drafts/issues/9901#issuecomment-2165674531
Spec & chromestatus pending
R=nrosenthal@google.com
Bug: 346976175
Change-Id: I8f2270d532f8bb441c6a6746095eb7a535cc6833
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7028593
Commit-Queue: Vladimir Levin <vmpstr@chromium.org>
Reviewed-by: Noam Rosenthal <nrosenthal@google.com>
Cr-Commit-Position: refs/heads/main@{#1530874}
--
wpt-commits: 524f6f4e2e9ed4aee125a6cf8807029c7bd759c0
wpt-pr: 55336
Diffstat:
3 files changed, 178 insertions(+), 0 deletions(-)
diff --git a/testing/web-platform/tests/css/css-view-transitions/view-transition-waituntil-animation-manipulation-ref.html b/testing/web-platform/tests/css/css-view-transitions/view-transition-waituntil-animation-manipulation-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>View transitions: waitUntil allows animation manipulation (reference)</title>
+<link rel="help" href="https://www.w3.org/TR/css-view-transitions-1/">
+<link rel="author" href="mailto:vmpstr@chromium.org">
+<style>
+body {
+ background: lightpink;
+}
+.target {
+ width: 100px;
+ height: 100px;
+ background: green;
+}
+</style>
+
+<div class=target></div>
diff --git a/testing/web-platform/tests/css/css-view-transitions/view-transition-waituntil-animation-manipulation.html b/testing/web-platform/tests/css/css-view-transitions/view-transition-waituntil-animation-manipulation.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<title>View transitions: waitUntil allows animation manipulation</title>
+<link rel="help" href="https://www.w3.org/TR/css-view-transitions-1/">
+<link rel="author" href="mailto:vmpstr@chromium.org">
+<link rel="match" href="view-transition-waituntil-animation-manipulation-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<style>
+:root { view-transition-name: none }
+
+#target {
+ width: 100px;
+ height: 100px;
+ background: green;
+ view-transition-name: target;
+}
+
+#target.after {
+ background: red;
+}
+
+::view-transition { background: lightpink; }
+::view-transition-group(*) {
+ animation-duration: 1ms;
+}
+</style>
+
+<div id=target></div>
+
+<script>
+failIfNot(document.startViewTransition, "Missing document.startViewTransition");
+failIfNot(ViewTransition.prototype.waitUntil, "ViewTransition.waitUntil is not available");
+
+async function runTest() {
+ const transition = document.startViewTransition(() => target.classList.add("after"));
+ transition.waitUntil(new Promise(() => {}));
+
+ transition.ready.then(async () => {
+ // Let the animation run to the end.
+ const animations = document.getAnimations();
+ animations.forEach(a => {
+ a.currentTime = 1;
+ });
+
+ // Wait a few frames.
+ await new Promise(requestAnimationFrame);
+ await new Promise(requestAnimationFrame);
+ await new Promise(requestAnimationFrame);
+
+ // Now, rewind the animation and take a screenshot.
+ requestAnimationFrame(() => {
+ animations.forEach(a => {
+ a.play();
+ a.pause();
+ a.currentTime = 0;
+ });
+ takeScreenshot();
+ });
+ });
+}
+onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest));
+</script>
diff --git a/testing/web-platform/tests/css/css-view-transitions/view-transition-waituntil-finished-promise.html b/testing/web-platform/tests/css/css-view-transitions/view-transition-waituntil-finished-promise.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>View Transition: waitUntil delays the finished promise</title>
+<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/">
+<link rel="author" href="mailto:vmpstr@chromium.org">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+#target {
+ width: 100px;
+ height: 100px;
+ view-transition-name: target;
+}
+::view-transition-group(*) {
+ animation-duration: 1ms;
+}
+</style>
+
+<div id=target></div>
+
+<script>
+promise_test(async t => {
+ assert_implements(document.startViewTransition, "View Transitions are not supported");
+ assert_implements(ViewTransition.prototype.waitUntil, "ViewTransition.waitUntil is not available");
+
+ let waitUntilPromiseResolve;
+ const waitUntilPromise = new Promise(resolve => {
+ waitUntilPromiseResolve = resolve;
+ });
+
+ const transition = document.startViewTransition(() => {});
+ transition.waitUntil(waitUntilPromise);
+
+ let finished = false;
+ transition.finished.then(() => {
+ finished = true;
+ });
+
+ // Wait for longer than the animation duration.
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+
+ assert_false(finished, "transition.finished should not resolve before waitUntil promise");
+
+ waitUntilPromiseResolve();
+ await transition.finished;
+
+ assert_true(finished, "transition.finished should resolve after waitUntil promise");
+}, "View transition finished promise is delayed by waitUntil");
+
+promise_test(async t => {
+ assert_implements(document.startViewTransition, "View Transitions are not supported");
+
+ let resolve1, resolve2;
+ const promise1 = new Promise(r => resolve1 = r);
+ const promise2 = new Promise(r => resolve2 = r);
+
+ const transition = document.startViewTransition(() => {});
+ transition.waitUntil(promise1);
+ transition.waitUntil(promise2);
+
+ let finished = false;
+ transition.finished.then(() => finished = true);
+
+ // Wait for longer than the animation duration.
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_false(finished, "transition.finished should not resolve before first promise");
+
+ resolve1();
+ // Wait a bit to ensure the promise resolution propagates.
+ await new Promise(resolve => t.step_timeout(resolve, 10));
+ assert_false(finished, "transition.finished should not resolve after first promise but before second");
+
+ resolve2();
+ await transition.finished;
+ assert_true(finished, "transition.finished should resolve after both promises");
+}, "View transition finished promise is delayed by multiple waitUntil calls");
+
+promise_test(async t => {
+ assert_implements(document.startViewTransition, "View Transitions are not supported");
+
+ let reject1;
+ const promise1 = new Promise((_, r) => reject1 = r);
+
+ const transition = document.startViewTransition(() => {});
+ transition.waitUntil(promise1);
+
+ let finished = false;
+ transition.finished.then(() => finished = true);
+
+ // Wait for longer than the animation duration.
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_false(finished, "transition.finished should not resolve before rejected promise");
+
+ reject1(new DOMException("test", "AbortError"));
+ await transition.finished;
+ assert_true(finished, "transition.finished should resolve after rejected promise");
+}, "View transition finished promise is delayed by a rejecting waitUntil promise");
+</script>