tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

animation-display-lock.html (6859B)


      1 <!DOCTYPE html>
      2 <meta charset=utf8>
      3 <title>Test getComputedStyle on a CSS animation in a display locked subtree</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: visible;
     11    contain: style layout paint;
     12    contain-intrinsic-size: 0 100px;
     13  }
     14  @keyframes fade {
     15    from { opacity: 1; }
     16    to { opacity: 0;  }
     17  }
     18  #target {
     19    background: 'green';
     20    height: 100px;
     21    width: 100px;
     22  }
     23  .animate {
     24    animation: fade 10s linear 2 alternate;
     25  }
     26  .transition {
     27    transition: opacity 10s linear;
     28  }
     29 </style>
     30 <body>
     31  <div id="container"></div>
     32 </body>
     33 <script>
     34 "use strict";
     35 
     36 function reset() {
     37  const container = document.getElementById('container');
     38  const target = document.getElementById('target');
     39  container.style = '';
     40  container.removeChild(target);
     41 }
     42 
     43 function createAnimatingElement(test, name) {
     44  const container = document.getElementById('container');
     45  const target = document.createElement('div');
     46  container.appendChild(target);
     47  target.id = 'target';
     48  target.className = name;
     49  test.add_cleanup(() => {
     50    reset();
     51  });
     52  return target;
     53 }
     54 
     55 function waitForEvent(element, eventName) {
     56  return new Promise(resolve => element.addEventListener(eventName, resolve, { once: true }));
     57 }
     58 
     59 promise_test(async t => {
     60  const container = document.getElementById('container');
     61  const target = createAnimatingElement(t, 'animate');
     62  let animationIterationEvent = false;
     63  target.addEventListener('animationiteration', () => {
     64    animationIterationEvent = true;
     65  });
     66  const animation = target.getAnimations()[0];
     67  await animation.ready;
     68  await waitForAnimationFrames(1);
     69  container.style.contentVisibility = 'hidden';
     70  animation.currentTime = 15000;
     71  assert_approx_equals(
     72      parseFloat(getComputedStyle(target).opacity), 0.5, 1e-6,
     73      'Computed style is updated even when the animation is running in a ' +
     74      'display locked subtree');
     75  await waitForAnimationFrames(2);
     76  assert_false(animationIterationEvent,
     77               'Animation events do not fire while the animation is ' +
     78               'running in a display locked subtree');
     79  container.style.contentVisibility = 'visible';
     80  await waitForEvent(target, 'animationiteration');
     81  assert_true(animationIterationEvent,
     82              'The animationiteration event fires once the animation is ' +
     83              'no longer display locked');
     84 }, 'Animation events do not fire for a CSS animation running in a display ' +
     85   'locked subtree');
     86 
     87 promise_test(async t => {
     88  const container = document.getElementById('container');
     89  const target = createAnimatingElement(t, 'animate');
     90  const animation = target.getAnimations()[0];
     91  await animation.ready;
     92  let finishedWhileDisplayLocked = false;
     93  animation.finished.then(() => {
     94    finishedWhileDisplayLocked =
     95        getComputedStyle(container).contentVisibility == 'hidden';
     96  });
     97  await waitForAnimationFrames(1);
     98  container.style.contentVisibility = 'hidden';
     99  // Advance to just shy of the effect end.
    100  animation.currentTime = 19999;
    101  assert_approx_equals(
    102      parseFloat(getComputedStyle(target).opacity), 0.9999, 1e-6,
    103                'Computed style is updated even when the animation is ' +
    104                'running in a display locked subtree');
    105  // Advancing frames should not resolve the finished promise.
    106  await waitForAnimationFrames(3);
    107  container.style.contentVisibility = 'visible';
    108  // Now we can resolve the finished promise.
    109  await animation.finished;
    110  assert_equals(finishedWhileDisplayLocked, false);
    111 }, 'The finished promise does not resolve due to the normal passage of time  ' +
    112   'for a CSS animation in a display locked subtree');
    113 
    114 promise_test(async t => {
    115  const container = document.getElementById('container');
    116  await waitForAnimationFrames(1);
    117  const target = createAnimatingElement(t, 'transition');
    118  await waitForAnimationFrames(1);
    119  target.style.opacity = 0;
    120  const animation = target.getAnimations()[0];
    121  await animation.ready;
    122  let finishedWhileDisplayLocked = false;
    123  animation.finished.then(() => {
    124    finishedWhileDisplayLocked =
    125        getComputedStyle(container).contentVisibility == 'hidden';
    126  });
    127  await waitForAnimationFrames(1);
    128  container.style.contentVisibility = 'hidden';
    129  // Advance to just shy of the effect end.
    130  animation.currentTime = 9999;
    131  assert_approx_equals(
    132      parseFloat(getComputedStyle(target).opacity), 0.0001, 1e-6,
    133                'Computed style is updated even when the animation is ' +
    134                'running in a display locked subtree');
    135  // Advancing frames should not resolve the finished promise.
    136  await waitForAnimationFrames(3);
    137  container.style.contentVisibility = 'visible';
    138  // Now we can resolve the finished promise.
    139  await animation.finished;
    140  assert_equals(finishedWhileDisplayLocked, false);
    141 }, 'The finished promise does not resolve due to the normal passage of time  ' +
    142   'for a CSS transition in a display locked subtree');
    143 
    144 promise_test(async t => {
    145  const container = document.getElementById('container');
    146  const target = createAnimatingElement(t, 'animate');
    147  const animation = target.getAnimations()[0];
    148  target.className = '';
    149  container.style.contentVisibility = 'hidden';
    150  assert_equals(target.getAnimations().length, 0);
    151 
    152  // Though originally a CSS animation, it is no longer associated with
    153  // CSS rules and no longer has an owning element. It now behaves like a
    154  // programmatic web animation. Animation playback events (but not CSS
    155  // animation events) should be dispatched and promises resolved despite
    156  // being in a display locked subtree.
    157 
    158  let cssAnimationEndEvent = false;
    159  target.addEventListener('animationend', () => {
    160    cssAnimationEndEvent = true;
    161  });
    162 
    163  let animationFinishEvent = false;
    164  animation.addEventListener('finish', () => {
    165    animationFinishEvent = true;
    166  });
    167 
    168  let animationFinished = false;
    169  animation.finished.then(() => {
    170    animationFinished = true;
    171  });
    172 
    173  animation.play();
    174  assert_equals(target.getAnimations().length, 1);
    175 
    176  animation.currentTime = 19999;
    177  await animation.ready;
    178  await waitForAnimationFrames(2);
    179 
    180  assert_true(animationFinishEvent,
    181              'Animation event not blocked on display locked subtree if ' +
    182              'no owning element');
    183  assert_true(animationFinished,
    184              'Finished promise not blocked on display locked subtrtee if ' +
    185              'no owning element');
    186  assert_false(cssAnimationEndEvent,
    187              'CSS animation events should not be dispatched if there is no ' +
    188              'owning element');
    189 }, 'Events and promises are handled normally for animations without an ' +
    190   'owning element');
    191 
    192 </script>