content-visibility-animation-in-auto-subtree.html (8685B)
1 <!DOCTYPE html> 2 <meta charset=utf8> 3 <title>Test getComputedStyle on a CSS animation in a content visibility subtree using content-visibility: auto</title> 4 <link rel="help" href="https://drafts.csswg.org/css-contain-2/"> 5 <script src="/web-animations/testcommon.js"></script> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 <style> 9 #container { 10 content-visibility: auto; 11 } 12 @keyframes fade { 13 from { opacity: 1; } 14 to { opacity: 0; } 15 } 16 #target { 17 background: green; 18 height: 100px; 19 width: 100px; 20 } 21 .animate { 22 animation: fade 1s linear 2 alternate; 23 } 24 .transition { 25 transition: opacity 1s linear; 26 } 27 </style> 28 <body> 29 <div id="spacer"></div> 30 <div id="container"></div> 31 </body> 32 <script> 33 "use strict"; 34 35 function reset() { 36 const container = document.getElementById('container'); 37 const target = document.getElementById('target'); 38 container.style = ''; 39 container.removeChild(target); 40 } 41 42 function createAnimatingElement(test, name) { 43 const container = document.getElementById('container'); 44 const target = document.createElement('div'); 45 container.appendChild(target); 46 target.id = 'target'; 47 target.className = name; 48 test.add_cleanup(() => { 49 reset(); 50 }); 51 return target; 52 } 53 54 promise_test(async t => { 55 const container = document.getElementById('container'); 56 const target = createAnimatingElement(t, 'animate'); 57 let animationIterationEvent = false; 58 const animation = target.getAnimations()[0]; 59 await animation.ready; 60 await waitForAnimationFrames(1); 61 document.getElementById("spacer").style.height = "300vh"; 62 await waitForAnimationFrames(1); 63 target.addEventListener('animationiteration', () => { 64 animationIterationEvent = true; 65 }); 66 animation.currentTime = 1500; 67 assert_approx_equals( 68 parseFloat(getComputedStyle(target).opacity), 0.5, 1e-6, 69 'Computed style is updated even when the animation is running in a ' + 70 'content visibility subtree'); 71 await waitForAnimationFrames(2); 72 assert_false(animationIterationEvent, 73 'Animation events do not fire while the animation is ' + 74 'running in a content visibility subtree'); 75 document.getElementById("spacer").style.height = "0vh"; 76 await waitForAnimationFrames(2); 77 assert_true(animationIterationEvent, 78 'The animationiteration event fires once the animation is ' + 79 'no longer content visibility'); 80 }, 'Animation events do not fire for a CSS animation running in a content ' + 81 'visibility subtree'); 82 83 promise_test(async t => { 84 const container = document.getElementById('container'); 85 const target = createAnimatingElement(t, 'animate'); 86 const animation = target.getAnimations()[0]; 87 await animation.ready; 88 let finishedWhileDisplayLocked = false; 89 animation.finished.then(() => { 90 finishedWhileDisplayLocked = 91 getComputedStyle(target).height == '0px'; 92 }); 93 await waitForAnimationFrames(1); 94 document.getElementById("spacer").style.height = "300vh"; 95 // Advance to just shy of the effect end. 96 animation.currentTime = 1999; 97 assert_approx_equals( 98 parseFloat(getComputedStyle(target).opacity), 0.999, 1e-6, 99 'Computed style is updated even when the animation is ' + 100 'running in a content visibility subtree'); 101 // Advancing frames should not resolve the finished promise. 102 await waitForAnimationFrames(3); 103 document.getElementById("spacer").style.height = "0vh"; 104 // Now we can resolve the finished promise. 105 await animation.finished; 106 assert_equals(finishedWhileDisplayLocked, false); 107 }, 'The finished promise does not resolve due to the normal passage of time ' + 108 'for a CSS animation in a content visibility subtree'); 109 110 promise_test(async t => { 111 const container = document.getElementById('container'); 112 await waitForAnimationFrames(1); 113 const target = createAnimatingElement(t, 'transition'); 114 await waitForAnimationFrames(1); 115 target.style.opacity = 0; 116 const animation = target.getAnimations()[0]; 117 await animation.ready; 118 let finishedWhileDisplayLocked = false; 119 animation.finished.then(() => { 120 finishedWhileDisplayLocked = 121 getComputedStyle(target).height == '0px'; 122 }); 123 await waitForAnimationFrames(1); 124 document.getElementById("spacer").style.height = "300vh"; 125 // Advance to just shy of the effect end. 126 animation.currentTime = 999; 127 assert_approx_equals( 128 parseFloat(getComputedStyle(target).opacity), 0.001, 1e-6, 129 'Computed style is updated even when the animation is ' + 130 'running in a content visibility subtree'); 131 // Advancing frames should not resolve the finished promise. 132 await waitForAnimationFrames(3); 133 document.getElementById("spacer").style.height = "0vh"; 134 // Now we can resolve the finished promise. 135 await animation.finished; 136 assert_equals(finishedWhileDisplayLocked, false); 137 }, 'The finished promise does not resolve due to the normal passage of time ' + 138 'for a CSS transition in a content visibility subtree'); 139 140 promise_test(async t => { 141 const container = document.getElementById('container'); 142 const target = createAnimatingElement(t, 'animate'); 143 const animation = target.getAnimations()[0]; 144 target.className = ''; 145 document.getElementById("spacer").style.height = "300vh"; 146 assert_equals(target.getAnimations().length, 0); 147 148 // Though originally a CSS animation, it is no longer associated with 149 // CSS rules and no longer has an owning element. It now behaves like a 150 // programmatic web animation. Animation playback events (but not CSS 151 // animation events) should be dispatched and promises resolved despite 152 // being in a content visibility subtree. 153 154 let cssAnimationEndEvent = false; 155 target.addEventListener('animationend', () => { 156 cssAnimationEndEvent = true; 157 }); 158 159 let animationFinishEvent = false; 160 animation.addEventListener('finish', () => { 161 animationFinishEvent = true; 162 }); 163 164 let animationFinished = false; 165 animation.finished.then(() => { 166 animationFinished = true; 167 }); 168 169 animation.play(); 170 assert_equals(target.getAnimations().length, 1); 171 172 animation.currentTime = 1999; 173 await animation.ready; 174 await waitForAnimationFrames(2); 175 176 assert_true(animationFinishEvent, 177 'Animation event not blocked on content visibility subtree if ' + 178 'no owning element'); 179 assert_true(animationFinished, 180 'Finished promise not blocked on content visibility subtree if ' + 181 'no owning element'); 182 assert_false(cssAnimationEndEvent, 183 'CSS animation events should not be dispatched if there is no ' + 184 'owning element'); 185 }, 'Events and promises are handled normally for animations without an ' + 186 'owning element'); 187 188 promise_test(async t => { 189 // The animation is hidden when it is created. 190 document.getElementById("spacer").style.height = "300vh"; 191 const container = document.getElementById('container'); 192 const target = createAnimatingElement(t, 'animate'); 193 const animation = target.getAnimations()[0]; 194 await waitForAnimationFrames(2); 195 // Make this animation no longer associated with its owning element. 196 target.className = ''; 197 assert_equals(target.getAnimations().length, 0); 198 199 // Though originally a CSS animation, it is no longer associated with 200 // CSS rules and no longer has an owning element. It now behaves like a 201 // programmatic web animation. Animation playback events (but not CSS 202 // animation events) should be dispatched and promises resolved despite 203 // being in a content visibility subtree. 204 205 let cssAnimationEndEvent = false; 206 target.addEventListener('animationend', () => { 207 cssAnimationEndEvent = true; 208 }); 209 210 let animationFinishEvent = false; 211 animation.addEventListener('finish', () => { 212 animationFinishEvent = true; 213 }); 214 215 let animationFinished = false; 216 animation.finished.then(() => { 217 animationFinished = true; 218 }); 219 220 animation.play(); 221 assert_equals(target.getAnimations().length, 1); 222 223 animation.currentTime = 1999; 224 await animation.ready; 225 await waitForAnimationFrames(2); 226 227 assert_true(animationFinishEvent, 228 'Animation event not blocked on content visibility subtree if ' + 229 'no owning element'); 230 assert_true(animationFinished, 231 'Finished promise not blocked on content visibility subtree if ' + 232 'no owning element'); 233 assert_false(cssAnimationEndEvent, 234 'CSS animation events should not be dispatched if there is no ' + 235 'owning element'); 236 }, 'CSS animations without an owning element should handle events and promises ' + 237 'normally, even c-v value does change'); 238 239 </script>