| <!DOCTYPE html> |
| <meta charset=utf8> |
| <title>Test getComputedStyle on a CSS animation in a display locked subtree</title> |
| <link rel="help" href="https://drafts.csswg.org/css-contain-2/"> |
| <script src="/web-animations/testcommon.js"></script> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <style> |
| #container { |
| content-visibility: visible; |
| contain: style layout paint; |
| contain-intrinsic-size: 0 100px; |
| } |
| @keyframes fade { |
| from { opacity: 1; } |
| to { opacity: 0; } |
| } |
| #target { |
| background: 'green'; |
| height: 100px; |
| width: 100px; |
| } |
| .animate { |
| animation: fade 1s linear 2 alternate; |
| } |
| .transition { |
| transition: opacity 1s linear; |
| } |
| </style> |
| <body> |
| <div id="container"></div> |
| </body> |
| <script> |
| "use strict"; |
| |
| function reset() { |
| const container = document.getElementById('container'); |
| const target = document.getElementById('target'); |
| container.style = ''; |
| container.removeChild(target); |
| } |
| |
| function createAnimatingElement(test, name) { |
| const container = document.getElementById('container'); |
| const target = document.createElement('div'); |
| container.appendChild(target); |
| target.id = 'target'; |
| target.className = name; |
| test.add_cleanup(() => { |
| reset(); |
| }); |
| return target; |
| } |
| |
| promise_test(async t => { |
| const container = document.getElementById('container'); |
| const target = createAnimatingElement(t, 'animate'); |
| let animationIterationEvent = false; |
| target.addEventListener('animationiteration', () => { |
| animationIterationEvent = true; |
| }); |
| const animation = target.getAnimations()[0]; |
| await animation.ready; |
| await waitForAnimationFrames(1); |
| container.style.contentVisibility = 'hidden'; |
| animation.currentTime = 1500; |
| assert_approx_equals( |
| parseFloat(getComputedStyle(target).opacity), 0.5, 1e-6, |
| 'Computed style is updated even when the animation is running in a ' + |
| 'display locked subtree'); |
| await waitForAnimationFrames(2); |
| assert_false(animationIterationEvent, |
| 'Animation events do no fire while the animation is ' + |
| 'running in a display locked subtree'); |
| container.style.contentVisibility = 'visible'; |
| await waitForAnimationFrames(2); |
| assert_true(animationIterationEvent, |
| 'The animationiteration event fires once the animation is ' + |
| 'no longer display locked'); |
| }, 'Animation events do not fire for a CSS animation running in a display ' + |
| 'locked subtree'); |
| |
| promise_test(async t => { |
| const container = document.getElementById('container'); |
| const target = createAnimatingElement(t, 'animate'); |
| const animation = target.getAnimations()[0]; |
| await animation.ready; |
| let finishedWhileDisplayLocked = false; |
| animation.finished.then(() => { |
| finishedWhileDisplayLocked = |
| getComputedStyle(container).contentVisibility == 'hidden'; |
| }); |
| await waitForAnimationFrames(1); |
| container.style.contentVisibility = 'hidden'; |
| // Advance to just shy of the effect end. |
| animation.currentTime = 1999; |
| assert_approx_equals( |
| parseFloat(getComputedStyle(target).opacity), 0.999, 1e-6, |
| 'Computed style is updated even when the animation is ' + |
| 'running in a display locked subtree'); |
| // Advancing frames should not resolve the finished promise. |
| await waitForAnimationFrames(3); |
| container.style.contentVisibility = 'visible'; |
| // Now we can resolve the finished promise. |
| await animation.finished; |
| assert_equals(finishedWhileDisplayLocked, false); |
| }, 'The finished promise does not resolve due to the normal passage of time ' + |
| 'for a CSS animation in a display locked subtree'); |
| |
| promise_test(async t => { |
| const container = document.getElementById('container'); |
| await waitForAnimationFrames(1); |
| const target = createAnimatingElement(t, 'transition'); |
| await waitForAnimationFrames(1); |
| target.style.opacity = 0; |
| const animation = target.getAnimations()[0]; |
| await animation.ready; |
| let finishedWhileDisplayLocked = false; |
| animation.finished.then(() => { |
| finishedWhileDisplayLocked = |
| getComputedStyle(container).contentVisibility == 'hidden'; |
| }); |
| await waitForAnimationFrames(1); |
| container.style.contentVisibility = 'hidden'; |
| // Advance to just shy of the effect end. |
| animation.currentTime = 999; |
| assert_approx_equals( |
| parseFloat(getComputedStyle(target).opacity), 0.001, 1e-6, |
| 'Computed style is updated even when the animation is ' + |
| 'running in a display locked subtree'); |
| // Advancing frames should not resolve the finished promise. |
| await waitForAnimationFrames(3); |
| container.style.contentVisibility = 'visible'; |
| // Now we can resolve the finished promise. |
| await animation.finished; |
| assert_equals(finishedWhileDisplayLocked, false); |
| }, 'The finished promise does not resolve due to the normal passage of time ' + |
| 'for a CSS transition in a display locked subtree'); |
| |
| promise_test(async t => { |
| const container = document.getElementById('container'); |
| const target = createAnimatingElement(t, 'animate'); |
| const animation = target.getAnimations()[0]; |
| target.className = ''; |
| container.style.contentVisibility = 'hidden'; |
| assert_equals(target.getAnimations().length, 0); |
| let animationStartEvent = false; |
| let animationFinished = false; |
| target.addEventListener('animationstart', () => { |
| animationStartEvent = true; |
| }); |
| // Though originally a CSS animation, it is no longer associated with |
| // CSS rules and no longer has an owning element. It now behaves like a |
| // programmatic web animation. Events should be dispatched and promises |
| // resolved despite being in a display locked subtree. |
| animation.play(); |
| animation.finished.then(() => { |
| animationFinished = true; |
| }); |
| assert_equals(target.getAnimations().length, 1); |
| await animation.ready; |
| await waitForAnimationFrames(2); |
| assert_true(animationStartEvent, |
| 'Animation event not blocked on display locked subtree if ' + |
| 'no owning element'); |
| animation.currentTime = 1999; |
| await waitForAnimationFrames(2); |
| assert_true(animationFinished, |
| 'Finished promise not blocked on display locked subtrtee if ' + |
| 'no owning element'); |
| }, 'Events and promises are handled normally for animations without an ' + |
| 'owning element'); |
| |
| </script> |