Bug 1526728 [wpt PR 15279] - [animation-worklet] Basic pause implementation (reland), a=testonly
authorMajid Valipour <majidvp@chromium.org>
Tue, 19 Feb 2019 11:46:49 +0000
changeset 461344 d57f0aa7c8a7004d4a638c863255ee3ea2f45fcd
parent 461343 cd859f2c535d48b4fc1208970b6f7025f99d66d0
child 461345 9e5d55b9cba119908c4790871d2c3b6cc5aa6522
push id35622
push userncsoregi@mozilla.com
push dateWed, 27 Feb 2019 04:32:15 +0000
treeherdermozilla-central@5b8896aa3f69 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1526728, 15279, 1434815, 785940, 821910, 1456640, 630548
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1526728 [wpt PR 15279] - [animation-worklet] Basic pause implementation (reland), a=testonly Automatic update from web-platform-tests [animation-worklet] Basic pause implementation (reland) Pausing worklet animation now holds the time. This works as expected for main thread animations. Implementing this for composited worklet animations will be done in a follow up patch. Major changes: - Add and expose pause() method pausing the animation. - Introduce hold_time that is used when animation is paused. - Rework how current time is computed, it is now closer to regular animations i.e., we either compute it based on "start time and timeline.currentTime" or use "hold time". - Instead of setting start time we now set the current time which then works backward to compute either the start time or the hold time based on the animation state. - When transitioning animation play state, we now always set the current time. Previously this was adhoc and inconsistent. - Introduce has_started_ to differentiate when playing an animation for the first time vs playing it from pause. - Update playback_rate related calculation to use new logic. Relanding: Original CL was reverted here [1]. Took the following steps to address this: - Address flakiness in unit test by using more accurate error value matching other tests. - Fix flakiness in layout test (worklet-animation-pause.https.html) by using startTime and waiting for time to actually advance working around pre-existing animation clock issue [2]. Also got rid of an invalid state transition from pause->idle. [1] https://chromium-review.googlesource.com/c/chromium/src/+/1434815 [2] http://crbug.com/785940 TEST: - wpt/animation-worklet/worklet-animation-pause.https.html: js test for basic current time calculations - wpt/animation-worklet/worklet-animation-pause-immediately.https.html: reftest for basic pause - wpt/animation-worklet/worklet-animation-pause-result.https.html: reftest for pause/resume. - WorkletAnimationTest.PausePlay: unit test for basic state transition and time calc Bug: 821910, Change-Id: I11fd2960443081be81055904d6d56a2abc3282f5 Reviewed-on: https://chromium-review.googlesource.com/c/1456640 Reviewed-by: Majid Valipour <majidvp@chromium.org> Reviewed-by: Yi Gu <yigu@chromium.org> Commit-Queue: Majid Valipour <majidvp@chromium.org> Cr-Commit-Position: refs/heads/master@{#630548} -- wpt-commits: 345300fad3945a5f1441fb2b2001109ca48f36e8 wpt-pr: 15279
testing/web-platform/tests/animation-worklet/common.js
testing/web-platform/tests/animation-worklet/references/translated-box-ref.html
testing/web-platform/tests/animation-worklet/worklet-animation-pause-immediately.https.html
testing/web-platform/tests/animation-worklet/worklet-animation-pause-resume.https.html
testing/web-platform/tests/animation-worklet/worklet-animation-pause.https.html
--- a/testing/web-platform/tests/animation-worklet/common.js
+++ b/testing/web-platform/tests/animation-worklet/common.js
@@ -25,8 +25,21 @@ function runInAnimationWorklet(code) {
 
 function waitForAsyncAnimationFrames(count) {
   // In Chrome, waiting for N+1 main thread frames guarantees that compositor has produced
   // at least N frames.
   // TODO(majidvp): re-evaluate this choice once other browsers have implemented
   // AnimationWorklet.
   return waitForAnimationFrames(count + 1);
 }
+
+async function waitForAnimationFrameWithCondition(condition) {
+  do {
+    await new Promise(window.requestAnimationFrame);
+  } while (!condition())
+};
+
+async function waitForDocumentTimelineAdvance() {
+  const timeAtStart = document.timeline.currentTime;
+  do {
+    await new Promise(window.requestAnimationFrame);
+  } while (timeAtStart === document.timeline.currentTime)
+}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/animation-worklet/references/translated-box-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<style>
+#box {
+  width: 100px;
+  height: 100px;
+  transform: translateY(100px);
+  background-color: green;
+}
+</style>
+
+<div id="box"></div>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/animation-worklet/worklet-animation-pause-immediately.https.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Verify that calling pause immediately after playing works as expected</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
+<link rel="match" href="references/translated-box-ref.html">
+
+<script src="/common/reftest-wait.js"></script>
+<script src="/web-animations/testcommon.js"></script>
+<script src="common.js"></script>
+<style>
+#box {
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+</style>
+
+<div id="box"></div>
+
+<script>
+registerPassthroughAnimator().then(async _ => {
+  const box = document.getElementById('box');
+  const effect = new KeyframeEffect(box,
+    { transform: ['translateY(100px)', 'translateY(200px)'] },
+    { duration: 100, iterations: 1 }
+  );
+
+  const animation = new WorkletAnimation('passthrough', effect);
+  animation.play();
+  // Immediately pausing animation should freeze the current time at 0.
+  animation.pause();
+  // Wait at least one frame to ensure a paused animation actually freezes.
+  await waitForAsyncAnimationFrames(1);
+  takeScreenshot();
+});
+</script>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/animation-worklet/worklet-animation-pause-resume.https.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Verify that calling pause immediately after playing works as expected</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
+<link rel="match" href="references/translated-box-ref.html">
+
+<script src="/common/reftest-wait.js"></script>
+<script src="common.js"></script>
+<style>
+#box {
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+</style>
+
+<div id="box"></div>
+
+<script>
+registerPassthroughAnimator().then(async _  => {
+  const duration = 18; // a bit longer than a frame
+  const box = document.getElementById('box');
+  const effect = new KeyframeEffect(box,
+    { transform: ['translateY(0px)', 'translateY(100px)'] },
+    { duration: duration, iterations: 1, fill: 'forwards'}
+  );
+
+  const animation = new WorkletAnimation('passthrough', effect);
+  // Immediately pausing animation should freeze the current time at 0.
+  animation.pause();
+  // Playing should cause animation to resume.
+  animation.play();
+  // Wait until we ensure animation has reached completion.
+  await waitForAnimationFrameWithCondition( _ => {
+    return animation.currentTime >= duration;
+  });
+  takeScreenshot();
+});
+</script>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/animation-worklet/worklet-animation-pause.https.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<title>Verify that currentTime and playState are correct when animation is paused</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/web-animations/testcommon.js"></script>
+<script src="common.js"></script>
+
+<div id="box"></div>
+
+<script>
+
+setup(setupAndRegisterTests, {explicit_done: true});
+
+function createAnimation() {
+  const box = document.getElementById('box');
+  const effect = new KeyframeEffect(box,
+    { transform: ['translateY(100px)', 'translateY(200px)'] },
+    { duration: 100, iterations: 1 }
+  );
+
+  return new WorkletAnimation('passthrough', effect);
+}
+
+async function setupAndRegisterTests() {
+  await registerPassthroughAnimator();
+
+  promise_test(async t => {
+    const animation = createAnimation();
+    animation.play();
+    // Immediately pausing animation should freeze the current time at 0.
+    animation.pause();
+    assert_equals(animation.currentTime, 0);
+    assert_equals(animation.playState, "paused");
+    // Wait some time to ensure a paused animation actually freezes.
+   await waitForNextFrame();
+    assert_equals(animation.currentTime, 0);
+    assert_equals(animation.playState, "paused");
+  }, 'pausing an animation freezes its current time');
+
+  promise_test(async t => {
+    const animation = createAnimation();
+    animation.pause();
+    animation.play();
+    // Allow one async animation frame to pass so that animation is running.
+    await waitForAsyncAnimationFrames(1);
+    assert_equals(animation.playState, "running");
+    // Allow time to advance so that we have a non-zero current time.
+    await waitForDocumentTimelineAdvance();
+    const timelineTime = document.timeline.currentTime;
+    assert_greater_than(animation.currentTime, 0);
+    assert_times_equal(animation.currentTime, (timelineTime - animation.startTime));
+  }, 'playing a paused animation should resume it');
+
+  done();
+}
+
+</script>
+