timelines.html (4979B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <title>Timelines</title> 4 <link rel="help" href="https://drafts.csswg.org/web-animations/#timelines"> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="../../testcommon.js"></script> 8 <style> 9 @keyframes opacity-animation { 10 from { opacity: 1; } 11 to { opacity: 0; } 12 } 13 </style> 14 <div id="log"></div> 15 <script> 16 'use strict'; 17 18 promise_test(t => { 19 const valueAtStart = document.timeline.currentTime; 20 const timeAtStart = window.performance.now(); 21 while (window.performance.now() - timeAtStart < 50) { 22 // Wait 50ms 23 } 24 assert_equals(document.timeline.currentTime, valueAtStart, 25 'Timeline time does not change within an animation frame'); 26 return waitForAnimationFrames(1).then(() => { 27 assert_greater_than(document.timeline.currentTime, valueAtStart, 28 'Timeline time increases between animation frames'); 29 }); 30 }, 'Timeline time increases once per animation frame'); 31 32 async_test(t => { 33 const iframe = document.createElement('iframe'); 34 iframe.width = 10; 35 iframe.height = 10; 36 37 iframe.addEventListener('load', t.step_func(() => { 38 const iframeTimeline = iframe.contentDocument.timeline; 39 const valueAtStart = iframeTimeline.currentTime; 40 const timeAtStart = window.performance.now(); 41 while (iframe.contentWindow.performance.now() - timeAtStart < 50) { 42 // Wait 50ms 43 } 44 assert_equals(iframeTimeline.currentTime, valueAtStart, 45 'Timeline time within an iframe does not change within an ' 46 + ' animation frame'); 47 48 iframe.contentWindow.requestAnimationFrame(t.step_func_done(() => { 49 assert_greater_than(iframeTimeline.currentTime, valueAtStart, 50 'Timeline time within an iframe increases between animation frames'); 51 iframe.remove(); 52 })); 53 })); 54 55 document.body.appendChild(iframe); 56 }, 'Timeline time increases once per animation frame in an iframe'); 57 58 async_test(t => { 59 const startTime = document.timeline.currentTime; 60 let firstRafTime; 61 62 requestAnimationFrame(() => { 63 t.step(() => { 64 assert_greater_than_equal(document.timeline.currentTime, startTime, 65 'Timeline time should have progressed'); 66 firstRafTime = document.timeline.currentTime; 67 }); 68 }); 69 70 requestAnimationFrame(() => { 71 t.step(() => { 72 assert_equals(document.timeline.currentTime, firstRafTime, 73 'Timeline time should be the same'); 74 }); 75 t.done(); 76 }); 77 }, 'Timeline time should be the same for all RAF callbacks in an animation' 78 + ' frame'); 79 80 promise_test(async t => { 81 // A microtask checkpoint is run as part of the process of updating 82 // timelines to ensure that any microtasks queued during promise 83 // resolution are handled before dispatching animation events. 84 const div = createDiv(t); 85 const events = []; 86 let microtaskFrameTime = undefined; 87 let finishFrameTime = undefined; 88 const waitForMicrotask = (animation) => { 89 return new Promise(resolve => { 90 queueMicrotask(() => { 91 events.push('microtask'); 92 microtaskFrameTime = document.timeline.currentTime; 93 resolve(); 94 }); 95 }); 96 } 97 const waitForFinishEvent = (animation) => { 98 return new Promise(resolve => { 99 animation.onfinish = (event) => { 100 events.push('finish'); 101 finishFrameTime = event.timelineTime; 102 resolve(); 103 }; 104 }); 105 } 106 107 await waitForNextFrame(); 108 109 const animation = div.animate({ opacity: [0, 1] }, 1000 * MS_PER_SEC); 110 const finishPromise = waitForFinishEvent(animation); 111 await animation.ready; 112 113 // Advance the timing to effect end, to asynchronously queue up a finish task. 114 // Queue up microtask, which must be processed ahead of the finish event. 115 // See "Perform a microtask checkpoint" step in 116 // https://www.w3.org/TR/web-animations-1/#timelines. 117 animation.currentTime = animation.effect.getComputedTiming().duration; 118 const microtaskPromise = waitForMicrotask(animation); 119 await Promise.all([finishPromise, microtaskPromise]); 120 assert_array_equals(events, ['microtask', 'finish']); 121 assert_times_equal(microtaskFrameTime, finishFrameTime); 122 123 }, 'Performs a microtask checkpoint after updating timelines'); 124 125 async_test(t => { 126 const div = createDiv(t); 127 let readyPromiseRan = false; 128 let finishedPromiseRan = false; 129 div.style.animation = 'opacity-animation 1ms'; 130 let anim = div.getAnimations()[0]; 131 anim.ready.then(t.step_func(() => { 132 readyPromiseRan = true; 133 })); 134 div.addEventListener('animationstart', t.step_func(() => { 135 assert_true(readyPromiseRan, 'It should run ready promise before animationstart event'); 136 })); 137 anim.finished.then(t.step_func(() => { 138 finishedPromiseRan = true; 139 })); 140 div.addEventListener('animationend', t.step_func_done(() => { 141 assert_true(finishedPromiseRan, 'It should run finished promise before animationend event'); 142 })); 143 }, 'Runs finished promise before animation events'); 144 </script>