<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[260577] trunk/LayoutTests</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/260577">260577</a></dd>
<dt>Author</dt> <dd>graouts@webkit.org</dd>
<dt>Date</dt> <dd>2020-04-23 10:23:18 -0700 (Thu, 23 Apr 2020)</dd>
</dl>

<h3>Log Message</h3>
<pre>[ Mac iOS ] animations/animation-direction-normal.html is a flaky failure
https://bugs.webkit.org/show_bug.cgi?id=210156
<rdar://problem/61411725>

Reviewed by Simon Fraser.

The tests animations/animation-direction-normal.html and animations/animation-direction-reverse.html were both written
similarly to test that an element targeted by a CSS Animation would have styles animated while the animation is running
and that those styles would no longer be animated once the CSS Animation was paused using the "animation-play-state"
CSS property.

The way those assertions were made were to use setTimeout() to check the computed style at a given time and compared it
to an expected value give or take an error margin. This design was flaky, as a system under load could easily not run the
timeout until a much larger delta than the one expected would elapse.

We use a new JS helper to write these tests in a non-flaky manner. The technique used now is to record the computed style
while the animation is running without providing expected times and values, but rather specifying delays between which we
want to record the computed style. Once all values have been recorded, a method can be used to check those recorded values
by using the Web Animations API to pause and seek the animation at recorded times and query the computed style, which allows
us to test values without an error margin.

Finally, the new JS helper also allows to check the computed style using a timeout when the animation play state is not relevant,
allowing those tests to pause the animation using the "animation-play-state" property and check after incremental timeouts
that the computed style did not change.

We also made the tests use the WPT harness for assertions and reporting.

* animations/animation-direction-normal-expected.txt:
* animations/animation-direction-normal.html:
* animations/animation-direction-reverse-expected.txt:
* animations/animation-direction-reverse.html:
* animations/resources/animation-test.js: Added.
(AnimationTest):
(AnimationTest.prototype.get animation):
(AnimationTest.prototype.get value):
(AnimationTest.prototype.async valueAfterWaitingFor):
(AnimationTest.prototype.async recordValueAfterRunningFor):
(AnimationTest.prototype.checkRecordedValues):
(AnimationTest.prototype._tickUntil):
* platform/ios-wk1/TestExpectations:
* platform/ios-wk2/TestExpectations:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsanimationsanimationdirectionnormalexpectedtxt">trunk/LayoutTests/animations/animation-direction-normal-expected.txt</a></li>
<li><a href="#trunkLayoutTestsanimationsanimationdirectionnormalhtml">trunk/LayoutTests/animations/animation-direction-normal.html</a></li>
<li><a href="#trunkLayoutTestsanimationsanimationdirectionreverseexpectedtxt">trunk/LayoutTests/animations/animation-direction-reverse-expected.txt</a></li>
<li><a href="#trunkLayoutTestsanimationsanimationdirectionreversehtml">trunk/LayoutTests/animations/animation-direction-reverse.html</a></li>
<li><a href="#trunkLayoutTestsplatformioswk1TestExpectations">trunk/LayoutTests/platform/ios-wk1/TestExpectations</a></li>
<li><a href="#trunkLayoutTestsplatformioswk2TestExpectations">trunk/LayoutTests/platform/ios-wk2/TestExpectations</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsanimationsresourcesanimationtestjs">trunk/LayoutTests/animations/resources/animation-test.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (260576 => 260577)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog      2020-04-23 17:15:45 UTC (rev 260576)
+++ trunk/LayoutTests/ChangeLog 2020-04-23 17:23:18 UTC (rev 260577)
</span><span class="lines">@@ -1,3 +1,47 @@
</span><ins>+2020-04-23  Antoine Quint  <graouts@apple.com>
+
+        [ Mac iOS ] animations/animation-direction-normal.html is a flaky failure
+        https://bugs.webkit.org/show_bug.cgi?id=210156
+        <rdar://problem/61411725>
+
+        Reviewed by Simon Fraser.
+
+        The tests animations/animation-direction-normal.html and animations/animation-direction-reverse.html were both written
+        similarly to test that an element targeted by a CSS Animation would have styles animated while the animation is running
+        and that those styles would no longer be animated once the CSS Animation was paused using the "animation-play-state"
+        CSS property.
+
+        The way those assertions were made were to use setTimeout() to check the computed style at a given time and compared it
+        to an expected value give or take an error margin. This design was flaky, as a system under load could easily not run the
+        timeout until a much larger delta than the one expected would elapse.
+
+        We use a new JS helper to write these tests in a non-flaky manner. The technique used now is to record the computed style
+        while the animation is running without providing expected times and values, but rather specifying delays between which we
+        want to record the computed style. Once all values have been recorded, a method can be used to check those recorded values
+        by using the Web Animations API to pause and seek the animation at recorded times and query the computed style, which allows
+        us to test values without an error margin.
+
+        Finally, the new JS helper also allows to check the computed style using a timeout when the animation play state is not relevant,
+        allowing those tests to pause the animation using the "animation-play-state" property and check after incremental timeouts
+        that the computed style did not change.
+
+        We also made the tests use the WPT harness for assertions and reporting.
+
+        * animations/animation-direction-normal-expected.txt:
+        * animations/animation-direction-normal.html:
+        * animations/animation-direction-reverse-expected.txt:
+        * animations/animation-direction-reverse.html:
+        * animations/resources/animation-test.js: Added.
+        (AnimationTest):
+        (AnimationTest.prototype.get animation):
+        (AnimationTest.prototype.get value):
+        (AnimationTest.prototype.async valueAfterWaitingFor):
+        (AnimationTest.prototype.async recordValueAfterRunningFor):
+        (AnimationTest.prototype.checkRecordedValues):
+        (AnimationTest.prototype._tickUntil):
+        * platform/ios-wk1/TestExpectations:
+        * platform/ios-wk2/TestExpectations:
+
</ins><span class="cx"> 2020-04-23  Chris Dumez  <cdumez@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [ Mac wk2 ] imported/w3c/web-platform-tests/notifications/event-onclose.html is flaky failing.
</span></span></pre></div>
<a id="trunkLayoutTestsanimationsanimationdirectionnormalexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/animations/animation-direction-normal-expected.txt (260576 => 260577)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/animations/animation-direction-normal-expected.txt     2020-04-23 17:15:45 UTC (rev 260576)
+++ trunk/LayoutTests/animations/animation-direction-normal-expected.txt        2020-04-23 17:23:18 UTC (rev 260577)
</span><span class="lines">@@ -1,4 +1,3 @@
</span><del>-PASS - "margin-left" property for "box" element at 0.5s saw something close to: 50
-PASS - "margin-left" property for "box" element at 1s saw something close to: 100
-PASS - "margin-left" property for "box" element at 2.5s saw something close to: 50
</del><span class="cx"> 
</span><ins>+PASS Pausing an animation using the animation-play-state property stops animating styles. 
+
</ins></span></pre></div>
<a id="trunkLayoutTestsanimationsanimationdirectionnormalhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/animations/animation-direction-normal.html (260576 => 260577)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/animations/animation-direction-normal.html     2020-04-23 17:15:45 UTC (rev 260576)
+++ trunk/LayoutTests/animations/animation-direction-normal.html        2020-04-23 17:23:18 UTC (rev 260577)
</span><span class="lines">@@ -1,7 +1,7 @@
</span><span class="cx"> <html lang="en">
</span><span class="cx"> <head>
</span><span class="cx">   <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</span><del>-  <title>Test of -webkit-animation-play-state</title>
</del><ins>+  <title>Test of animation-play-state</title>
</ins><span class="cx">   <style type="text/css" media="screen">
</span><span class="cx">     body {
</span><span class="cx">       margin: 0;
</span><span class="lines">@@ -15,58 +15,50 @@
</span><span class="cx">       width: 100px;
</span><span class="cx">       background-color: red;
</span><span class="cx">       margin: 0;
</span><del>-      -webkit-animation-duration: 2s;
-      -webkit-animation-direction: normal;
-      -webkit-animation-iteration-count: 2;
-      -webkit-animation-timing-function: linear;
-      -webkit-animation-name: "move1";
</del><ins>+      animation-duration: 500ms;
+      animation-direction: normal;
+      animation-iteration-count: infinite;
+      animation-timing-function: linear;
+      animation-name: "move1";
</ins><span class="cx">     }
</span><del>-    #safezone {
-      position: absolute;
-      top: 100px;
-      height: 100px;
-      width: 120px;
-      left: 30px;
-      background-color: green;
-    }
-    @-webkit-keyframes "move1" {
</del><ins>+
+    @keyframes move1 {
</ins><span class="cx">       from { margin-left: 0px; }
</span><span class="cx">       to   { margin-left: 200px; }
</span><span class="cx">     }
</span><del>-    #result {
-      color: white; /* hide from pixel results */
-    }
</del><span class="cx">   </style>
</span><del>-  <script src="resources/animation-test-helpers.js" type="text/javascript" charset="utf-8"></script>
-  <script type="text/javascript" charset="utf-8">
-    const expectedValues = [
-      // [animation-name, time, element-id, property, expected-value, tolerance]
-      ["move1", 0.5, "box", "margin-left", 50, 20],
-      ["move1", 1.0, "box", "margin-left", 100, 20],
-      ["move1", 2.5, "box", "margin-left", 50, 20],
-    ];
</del><ins>+  <script src="../resources/testharness.js"></script>
+  <script src="../resources/testharnessreport.js"></script>
+  <script src="resources/animation-test.js"></script>
+</head>
+<body>
+<div id="box"></div>
+<script>
+    async_test(async t => {
+        const delay = 100;
</ins><span class="cx"> 
</span><del>-    function pauseAnimation()
-    {
-        const box = document.getElementById("box");
-        box.style.webkitAnimationPlayState = "paused";
-        box.getAnimations()[0].currentTime = 2500;
-    }
</del><ins>+        const test = new AnimationTest({
+            target: document.getElementById("box"),
+            styleExtractor: style => parseFloat(style.marginLeft)
+        });
</ins><span class="cx"> 
</span><del>-    function setTimers()
-    {
-        setTimeout(pauseAnimation, 2500);
-    }
</del><ins>+        // Record two computed values after the specified delay each.
+        await test.recordValueAfterRunningFor(delay);
+        await test.recordValueAfterRunningFor(delay);
</ins><span class="cx"> 
</span><del>-    runAnimationTest(expectedValues, setTimers, null, true);
</del><ins>+        // We'll now pause the animation using the CSS property "animation-play-state".
+        box.style.animationPlayState = "paused";
</ins><span class="cx"> 
</span><del>-  </script>
-</head>
-<body>
-<!-- This tests the operation of -webkit-animation-play-state. After 1 second the red boxes should be hidden by the green boxes. You should see no red boxes. -->
-<div id="box"></div>
-<div id="safezone"></div>
-<div id="result"></div>
-</div>
</del><ins>+        // And now we'll record values after the specified delay each and check that those are the same.
+        const initialPausedValue = await test.valueAfterWaitingFor(delay);
+        const currentPausedValue = await test.valueAfterWaitingFor(delay);
+        assert_equals(initialPausedValue, currentPausedValue, "Values recorded while paused are the same.");
+
+        // Finally, check the values recorded earlier in the test.
+        test.checkRecordedValues();
+
+        t.done();
+    }, `Pausing an animation using the animation-play-state property stops animating styles.`);
+</script>
</ins><span class="cx"> </body>
</span><span class="cx"> </html>
</span></span></pre></div>
<a id="trunkLayoutTestsanimationsanimationdirectionreverseexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/animations/animation-direction-reverse-expected.txt (260576 => 260577)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/animations/animation-direction-reverse-expected.txt    2020-04-23 17:15:45 UTC (rev 260576)
+++ trunk/LayoutTests/animations/animation-direction-reverse-expected.txt       2020-04-23 17:23:18 UTC (rev 260577)
</span><span class="lines">@@ -1,4 +1,3 @@
</span><del>-PASS - "margin-left" property for "box" element at 0.5s saw something close to: 150
-PASS - "margin-left" property for "box" element at 1s saw something close to: 100
-PASS - "margin-left" property for "box" element at 2.5s saw something close to: 0
</del><span class="cx"> 
</span><ins>+PASS Pausing an animation using the animation-play-state property stops animating styles with animation-direction: reverse. 
+
</ins></span></pre></div>
<a id="trunkLayoutTestsanimationsanimationdirectionreversehtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/animations/animation-direction-reverse.html (260576 => 260577)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/animations/animation-direction-reverse.html    2020-04-23 17:15:45 UTC (rev 260576)
+++ trunk/LayoutTests/animations/animation-direction-reverse.html       2020-04-23 17:23:18 UTC (rev 260577)
</span><span class="lines">@@ -1,7 +1,7 @@
</span><span class="cx"> <html lang="en">
</span><span class="cx"> <head>
</span><span class="cx">   <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</span><del>-  <title>Test of -webkit-animation-play-state</title>
</del><ins>+  <title>Test of animation-play-state</title>
</ins><span class="cx">   <style type="text/css" media="screen">
</span><span class="cx">     body {
</span><span class="cx">       margin: 0;
</span><span class="lines">@@ -15,45 +15,50 @@
</span><span class="cx">       width: 100px;
</span><span class="cx">       background-color: red;
</span><span class="cx">       margin: 0;
</span><del>-      -webkit-animation-duration: 2s;
-      -webkit-animation-direction: reverse;
-      -webkit-animation-timing-function: linear;
-      -webkit-animation-name: "move1";
</del><ins>+      animation-duration: 500ms;
+      animation-direction: reverse;
+      animation-iteration-count: infinite;
+      animation-timing-function: linear;
+      animation-name: "move1";
</ins><span class="cx">     }
</span><del>-    @-webkit-keyframes "move1" {
</del><ins>+
+    @keyframes move1 {
</ins><span class="cx">       from { margin-left: 0px; }
</span><span class="cx">       to   { margin-left: 200px; }
</span><span class="cx">     }
</span><del>-    #result {
-      color: white; /* hide from pixel results */
-    }
</del><span class="cx">   </style>
</span><del>-  <script src="resources/animation-test-helpers.js" type="text/javascript" charset="utf-8"></script>
-  <script type="text/javascript" charset="utf-8">
-    const expectedValues = [
-      // [animation-name, time, element-id, property, expected-value, tolerance]
-      ["move1", 0.5, "box", "margin-left", 150, 20],
-      ["move1", 1.0, "box", "margin-left", 100, 20],
-      ["move1", 2.5, "box", "margin-left", 0, 20],
-    ];
</del><ins>+  <script src="../resources/testharness.js"></script>
+  <script src="../resources/testharnessreport.js"></script>
+  <script src="resources/animation-test.js"></script>
+</head>
+<body>
+<div id="box"></div>
+<script>
+    async_test(async t => {
+        const delay = 100;
</ins><span class="cx"> 
</span><del>-    function pauseAnimation()
-    {
-        document.getElementById("box").style.webkitAnimationPlayState = "paused";
-    }
</del><ins>+        const test = new AnimationTest({
+            target: document.getElementById("box"),
+            styleExtractor: style => parseFloat(style.marginLeft)
+        });
</ins><span class="cx"> 
</span><del>-    function setTimers()
-    {
-        setTimeout(pauseAnimation, 2500);
-    }
</del><ins>+        // Record two computed values after the specified delay each.
+        await test.recordValueAfterRunningFor(delay);
+        await test.recordValueAfterRunningFor(delay);
</ins><span class="cx"> 
</span><del>-    runAnimationTest(expectedValues, setTimers, null, true);
</del><ins>+        // We'll now pause the animation using the CSS property "animation-play-state".
+        box.style.animationPlayState = "paused";
</ins><span class="cx"> 
</span><del>-  </script>
-</head>
-<body>
-<div id="box"></div>
-<div id="result"></div>
-</div>
</del><ins>+        // And now we'll record values after 250ms each and check that those are the same.
+        const initialPausedValue = await test.valueAfterWaitingFor(delay);
+        const currentPausedValue = await test.valueAfterWaitingFor(delay);
+        assert_equals(initialPausedValue, currentPausedValue, "Values recorded while paused are the same.");
+
+        // Finally, check the values recorded earlier in the test.
+        test.checkRecordedValues();
+
+        t.done();
+    }, `Pausing an animation using the animation-play-state property stops animating styles with animation-direction: reverse.`);
+</script>
</ins><span class="cx"> </body>
</span><span class="cx"> </html>
</span></span></pre></div>
<a id="trunkLayoutTestsanimationsresourcesanimationtestjs"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/animations/resources/animation-test.js (0 => 260577)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/animations/resources/animation-test.js                         (rev 0)
+++ trunk/LayoutTests/animations/resources/animation-test.js    2020-04-23 17:23:18 UTC (rev 260577)
</span><span class="lines">@@ -0,0 +1,118 @@
</span><ins>+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+class AnimationTest
+{
+
+    // Expects a dictionary with an Element (target) and a Function (styleExtractor) used to extract
+    // the recorded value from a computed style.
+    constructor(options)
+    {
+        this._records = [];
+        this._target = options.target;
+        this._styleExtractor = options.styleExtractor;
+    }
+
+    // Public
+
+    // Returns the Animation object. Currently, this expects a single animation is running on the
+    // provided element.
+    get animation()
+    {
+        return this._target.getAnimations()[0];
+    }
+
+    // Returns the requested value from the computed style using the provided style extractor.
+    get value()
+    {
+        return this._styleExtractor(getComputedStyle(this._target));
+    }
+
+    // Returns the requested value from the computed style after waiting a certain delay.
+    // This method runs independently of the animation play state and waits at a minimum
+    // for an animation frame to have elapsed.
+    async valueAfterWaitingFor(delay)
+    {
+        await Promise.all([
+            new Promise(requestAnimationFrame),
+            new Promise(resolve => setTimeout(resolve, delay))
+        ]);
+        return this.value;
+    }
+
+    // Records the requested value from the computed style after a given delay has elapsed while the
+    // animation is running. The record stored will be checked when checkRecordedValues() is called.
+    async recordValueAfterRunningFor(delay)
+    {
+        const animation = this.animation;
+        await animation.ready;
+
+        const targetTime = animation.currentTime + delay;
+        await this._tickUntil(elapsedTime => animation.currentTime >= targetTime);
+
+        this._records.push({
+            currentTime: animation.currentTime,
+            value: this.value
+        });
+    }
+
+    // Check that all requested values recorded using recordValueAfterRunningFor() match the same values
+    // when the animation is paused and seeked at the same animation currentTime as when the record was made.
+    checkRecordedValues()
+    {
+        const animation = this.animation;
+
+        // Reset the animation.
+        animation.cancel();
+
+        // Now seek to each of the recorded times and assert the value.
+        this._records.forEach((record, index) => {
+            animation.currentTime = record.currentTime;
+            assert_equals(record.value, this.value, `Recorded value #${index} is correct.`);
+        });
+    }
+
+    // Private
+
+    // Wait until the provided condition is true using animation frames.
+    _tickUntil(condition)
+    {
+        return new Promise(resolve => {
+            if (!this._startTime)
+                this._startTime = performance.now();
+
+            const tick = timestamp => {
+                const elapsedTime = timestamp - this._startTime;
+                if (condition(elapsedTime)) {
+                    delete this._startTime;
+                    resolve();
+                }
+                requestAnimationFrame(tick);
+            }
+            requestAnimationFrame(tick);
+        });
+    }
+
+}
</ins></span></pre></div>
<a id="trunkLayoutTestsplatformioswk1TestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/ios-wk1/TestExpectations (260576 => 260577)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/ios-wk1/TestExpectations      2020-04-23 17:15:45 UTC (rev 260576)
+++ trunk/LayoutTests/platform/ios-wk1/TestExpectations 2020-04-23 17:23:18 UTC (rev 260577)
</span><span class="lines">@@ -1137,7 +1137,6 @@
</span><span class="cx"> animations/animation-direction-reverse-hardware-opacity.html [ Failure Pass ]
</span><span class="cx"> animations/animation-direction-reverse-hardware.html [ Failure Pass ]
</span><span class="cx"> animations/animation-direction-reverse-timing-functions-hardware.html [ Failure Pass ]
</span><del>-animations/animation-direction-reverse.html [ Failure Pass ]
</del><span class="cx"> animations/animation-hit-test-transform.html [ Failure Pass ]
</span><span class="cx"> compositing/layer-creation/mismatched-rotated-transform-transition-overlap.html [ Failure Pass ]
</span><span class="cx"> compositing/layer-creation/mismatched-transform-transition-overlap.html [ Failure Pass ]
</span></span></pre></div>
<a id="trunkLayoutTestsplatformioswk2TestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/ios-wk2/TestExpectations (260576 => 260577)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/ios-wk2/TestExpectations      2020-04-23 17:15:45 UTC (rev 260576)
+++ trunk/LayoutTests/platform/ios-wk2/TestExpectations 2020-04-23 17:23:18 UTC (rev 260577)
</span><span class="lines">@@ -1327,8 +1327,6 @@
</span><span class="cx"> 
</span><span class="cx"> webkit.org/b/207153 animations/animation-callback-timestamp.html [ Pass Failure ]
</span><span class="cx"> 
</span><del>-webkit.org/b/209362 animations/animation-direction-reverse.html [ Pass Failure ]
-
</del><span class="cx"> webkit.org/b/207156 [ Release ] http/tests/websocket/tests/hybi/workers/close.html [ Pass Failure ]
</span><span class="cx"> 
</span><span class="cx"> webkit.org/b/207161 imported/w3c/web-platform-tests/2dcontext/imagebitmap/createImageBitmap-transfer.html [ Pass Failure ]
</span></span></pre>
</div>
</div>

</body>
</html>