file_deferred_start.html (6160B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <script src="../testcommon.js"></script> 4 <script src="/tests/SimpleTest/paint_listener.js"></script> 5 <style> 6 @keyframes empty { } 7 .target { 8 /* Element needs geometry to be eligible for layerization */ 9 width: 100px; 10 height: 100px; 11 background-color: white; 12 } 13 </style> 14 <body> 15 <script> 16 'use strict'; 17 18 function waitForDocLoad() { 19 return new Promise((resolve, reject) => { 20 if (document.readyState === 'complete') { 21 resolve(); 22 } else { 23 window.addEventListener('load', resolve); 24 } 25 }); 26 } 27 28 function waitForPaints() { 29 return new Promise((resolve, reject) => { 30 waitForAllPaintsFlushed(resolve); 31 }); 32 } 33 34 promise_test(async t => { 35 // Test that empty animations actually start. 36 // 37 // Normally we tie the start of animations to when their first frame of 38 // the animation is rendered. However, for animations that don't actually 39 // trigger a paint (e.g. because they are empty, or are animating something 40 // that doesn't render or is offscreen) we want to make sure they still 41 // start. 42 // 43 // Before we start, wait for the document to finish loading, then create 44 // div element, and wait for painting. This is because during loading we will 45 // have other paint events taking place which might, by luck, happen to 46 // trigger animations that otherwise would not have been triggered, leading to 47 // false positives. 48 // 49 // As a result, it's better to wait until we have a more stable state before 50 // continuing. 51 await waitForDocLoad(); 52 53 const div = addDiv(t); 54 55 await waitForPaints(); 56 57 div.style.animation = 'empty 1000s'; 58 const animation = div.getAnimations()[0]; 59 60 let promiseCallbackDone = false; 61 animation.ready.then(() => { 62 promiseCallbackDone = true; 63 }).catch(() => { 64 assert_unreached('ready promise was rejected'); 65 }); 66 67 // We need to wait for up to three frames. This is because in some 68 // cases it can take up to two frames for the initial layout 69 // to take place. Even after that happens we don't actually resolve the 70 // ready promise until the following tick. 71 await waitForAnimationFrames(3); 72 73 assert_true(promiseCallbackDone, 74 'ready promise for an empty animation was resolved' 75 + ' within three animation frames'); 76 }, 'Animation.ready is resolved for an empty animation'); 77 78 // Test that compositor animations with delays get synced correctly 79 // 80 // NOTE: It is important that we DON'T use 81 // SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh here since that takes 82 // us through a different code path. 83 promise_test(async t => { 84 assert_false(SpecialPowers.DOMWindowUtils.isTestControllingRefreshes, 85 'Test should run without the refresh driver being under' 86 + ' test control'); 87 88 // This test only applies to compositor animations 89 if (!isOMTAEnabled()) { 90 return; 91 } 92 93 const div = addDiv(t, { class: 'target' }); 94 95 // As with the above test, any stray paints can cause this test to produce 96 // a false negative (that is, pass when it should fail). To avoid this we 97 // wait for paints and only then do we commence the test. 98 await waitForPaints(); 99 100 const animation = 101 div.animate({ transform: [ 'translate(0px)', 'translate(100px)' ] }, 102 { duration: 400 * MS_PER_SEC, 103 delay: -200 * MS_PER_SEC }); 104 105 await waitForAnimationReadyToRestyle(animation); 106 107 await waitForPaints(); 108 109 const transformStr = 110 SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform'); 111 const translateX = getTranslateXFromTransform(transformStr); 112 113 // If the delay has been applied we should be about half-way through 114 // the animation. However, if we applied it twice we will be at the 115 // end of the animation already so check that we are roughly half way 116 // through. 117 assert_between_inclusive(translateX, 40, 75, 118 'Animation is about half-way through on the compositor'); 119 }, 'Starting an animation with a delay starts from the correct point'); 120 121 // Test that compositor animations with a playback rate start at the 122 // appropriate point. 123 // 124 // NOTE: As with the previous test, it is important that we DON'T use 125 // SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh here since that takes 126 // us through a different code path. 127 promise_test(async t => { 128 assert_false(SpecialPowers.DOMWindowUtils.isTestControllingRefreshes, 129 'Test should run without the refresh driver being under' 130 + ' test control'); 131 132 // This test only applies to compositor animations 133 if (!isOMTAEnabled()) { 134 return; 135 } 136 137 const div = addDiv(t, { class: 'target' }); 138 139 // Wait for the document to load and painting (see notes in previous test). 140 await waitForPaints(); 141 142 const animation = 143 div.animate({ transform: [ 'translate(0px)', 'translate(100px)' ] }, 144 200 * MS_PER_SEC); 145 animation.currentTime = 100 * MS_PER_SEC; 146 animation.playbackRate = 0.1; 147 148 await waitForPaints(); 149 150 const transformStr = 151 SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform'); 152 const translateX = getTranslateXFromTransform(transformStr); 153 154 // We pass the playback rate to the compositor independently and we have 155 // tests to ensure that it is correctly applied there. However, if, when 156 // we resolve the start time of the pending animation, we fail to 157 // incorporate the playback rate, we will end up starting from the wrong 158 // point and the current time calculated on the compositor will be wrong. 159 assert_between_inclusive(translateX, 25, 75, 160 'Animation is about half-way through on the compositor'); 161 }, 'Starting an animation with a playbackRate starts from the correct point'); 162 163 function getTranslateXFromTransform(transformStr) { 164 const matrixComponents = 165 transformStr.startsWith('matrix(') 166 ? transformStr.substring('matrix('.length, transformStr.length-1) 167 .split(',') 168 .map(component => Number(component)) 169 : []; 170 assert_equals(matrixComponents.length, 6, 171 'Got a valid transform matrix on the compositor' 172 + ' (got: "' + transformStr + '")'); 173 174 return matrixComponents[4]; 175 } 176 177 done(); 178 </script> 179 </body>