tor-browser

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

Document-getAnimations.tentative.html (15325B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <title>Document.getAnimations() for CSS animations</title>
      4 <link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-composite-order">
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 <script src="support/testcommon.js"></script>
      8 <style>
      9 @keyframes animLeft {
     10  to { left: 100px }
     11 }
     12 @keyframes animTop {
     13  to { top: 100px }
     14 }
     15 @keyframes animBottom {
     16  to { bottom: 100px }
     17 }
     18 @keyframes animRight {
     19  to { right: 100px }
     20 }
     21 
     22 @keyframes anim1 {
     23  to { left: 100px }
     24 }
     25 @keyframes anim2 {
     26  to { top: 100px }
     27 }
     28 @keyframes anim3 {
     29  to { bottom: 100px }
     30 }
     31 @keyframes anim4 {
     32  to { right: 100px }
     33 }
     34 
     35 </style>
     36 <div id="log"></div>
     37 <script>
     38 'use strict';
     39 
     40 test(t => {
     41  assert_equals(document.getAnimations().length, 0,
     42    'getAnimations returns an empty sequence for a document'
     43    + ' with no animations');
     44 }, 'getAnimations for non-animated content');
     45 
     46 test(t => {
     47  const div = addDiv(t);
     48 
     49  // Add an animation
     50  div.style.animation = 'animLeft 100s';
     51  assert_equals(document.getAnimations().length, 1,
     52                'getAnimations returns a running CSS Animation');
     53 
     54  // Add another animation
     55  div.style.animation = 'animLeft 100s, animTop 100s';
     56  assert_equals(document.getAnimations().length, 2,
     57                'getAnimations returns two running CSS Animations');
     58 
     59  // Remove both
     60  div.style.animation = '';
     61  assert_equals(document.getAnimations().length, 0,
     62                'getAnimations returns no running CSS Animations');
     63 }, 'getAnimations for CSS Animations');
     64 
     65 test(t => {
     66  const div = addDiv(t);
     67  const animation1 = 'animLeft 100s'
     68  const animation2 = 'animBottom 100s'
     69  div.style.animation = animation1;
     70  const animations1 = document.getAnimations();
     71  assert_equals(animations1.length, 1,
     72                'getAnimations returns all running CSS Animations');
     73  div.style.animation = animation2 + ', ' + animation1;
     74  const animations = document.getAnimations();
     75  assert_equals(animations.length, 2,
     76                'getAnimations returns all running CSS Animations');
     77  assert_equals(animations[0].animationName, 'animBottom',
     78                'Order of first animation returned');
     79  assert_equals(animations[1].animationName, 'animLeft',
     80                'Order of second animation returned');
     81 }, 'Order of CSS Animations - within an element unaffected by start time');
     82 
     83 test(t => {
     84  const div = addDiv(t);
     85  div.style.animation = 'animLeft 100s, animTop 100s, animRight 100s, ' +
     86                        'animBottom 100s';
     87 
     88  const animations = document.getAnimations();
     89  assert_equals(animations.length, 4,
     90                'getAnimations returns all running CSS Animations');
     91  assert_equals(animations[0].animationName, 'animLeft',
     92                'Order of first animation returned');
     93  assert_equals(animations[1].animationName, 'animTop',
     94                'Order of second animation returned');
     95  assert_equals(animations[2].animationName, 'animRight',
     96                'Order of third animation returned');
     97  assert_equals(animations[3].animationName, 'animBottom',
     98                'Order of fourth animation returned');
     99 }, 'Order of CSS Animations - within an element');
    100 
    101 test(t => {
    102  const div1 = addDiv(t, { style: 'animation: animLeft 100s' });
    103  const div2 = addDiv(t, { style: 'animation: animLeft 100s' });
    104  const div3 = addDiv(t, { style: 'animation: animLeft 100s' });
    105  const div4 = addDiv(t, { style: 'animation: animLeft 100s' });
    106 
    107  let animations = document.getAnimations();
    108  assert_equals(animations.length, 4,
    109                'getAnimations returns all running CSS Animations');
    110  assert_equals(animations[0].effect.target, div1,
    111                'Order of first animation returned');
    112  assert_equals(animations[1].effect.target, div2,
    113                'Order of second animation returned');
    114  assert_equals(animations[2].effect.target, div3,
    115                'Order of third animation returned');
    116  assert_equals(animations[3].effect.target, div4,
    117                'Order of fourth animation returned');
    118 
    119  // Order should be depth-first pre-order so add some depth as follows:
    120  //
    121  //      <parent>
    122  //       /  |
    123  //      2   3
    124  //    /  \
    125  //   1   4
    126  //
    127  // Which should give: 2, 1, 4, 3
    128  div2.appendChild(div1);
    129  div2.appendChild(div4);
    130  animations = document.getAnimations();
    131  assert_equals(animations[0].effect.target, div2,
    132                'Order of first animation returned after tree surgery');
    133  assert_equals(animations[1].effect.target, div1,
    134                'Order of second animation returned after tree surgery');
    135  assert_equals(animations[2].effect.target, div4,
    136                'Order of third animation returned after tree surgery');
    137  assert_equals(animations[3].effect.target, div3,
    138                'Order of fourth animation returned after tree surgery');
    139 
    140 }, 'Order of CSS Animations - across elements');
    141 
    142 test(t => {
    143  const div1 = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
    144  const div2 = addDiv(t, { style: 'animation: animBottom 100s' });
    145 
    146  let expectedResults = [ [ div1, 'animLeft' ],
    147                            [ div1, 'animTop' ],
    148                            [ div2, 'animBottom' ] ];
    149  let animations = document.getAnimations();
    150  assert_equals(animations.length, expectedResults.length,
    151                'getAnimations returns all running CSS Animations');
    152  animations.forEach((anim, i) => {
    153    assert_equals(anim.effect.target, expectedResults[i][0],
    154                  'Target of animation in position ' + i);
    155    assert_equals(anim.animationName, expectedResults[i][1],
    156                  'Name of animation in position ' + i);
    157  });
    158 
    159  // Modify tree structure and animation list
    160  div2.appendChild(div1);
    161  div1.style.animation = 'animLeft 100s, animRight 100s, animTop 100s';
    162 
    163  expectedResults = [ [ div2, 'animBottom' ],
    164                      [ div1, 'animLeft' ],
    165                      [ div1, 'animRight' ],
    166                      [ div1, 'animTop' ] ];
    167  animations = document.getAnimations();
    168  assert_equals(animations.length, expectedResults.length,
    169                'getAnimations returns all running CSS Animations after ' +
    170                'making changes');
    171  animations.forEach((anim, i) => {
    172    assert_equals(anim.effect.target, expectedResults[i][0],
    173                  'Target of animation in position ' + i + ' after changes');
    174    assert_equals(anim.animationName, expectedResults[i][1],
    175                  'Name of animation in position ' + i + ' after changes');
    176  });
    177 }, 'Order of CSS Animations - across and within elements');
    178 
    179 test(t => {
    180  const div = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
    181  const animLeft = document.getAnimations()[0];
    182  assert_equals(animLeft.animationName, 'animLeft',
    183                'Originally, animLeft animation comes first');
    184 
    185  // Disassociate animLeft from markup and restart
    186  div.style.animation = 'animTop 100s';
    187  animLeft.play();
    188 
    189  const animations = document.getAnimations();
    190  assert_equals(animations.length, 2,
    191                'getAnimations returns markup-bound and free animations');
    192  assert_equals(animations[0].animationName, 'animTop',
    193                'Markup-bound animations come first');
    194  assert_equals(animations[1], animLeft, 'Free animations come last');
    195 }, 'Order of CSS Animations - markup-bound vs free animations');
    196 
    197 test(t => {
    198  // Add an animation first
    199  const div = addDiv(t, { style: 'animation: animLeft 100s' });
    200  const animLeft = document.getAnimations()[0];
    201  // Disassociate animLeft from markup and restart
    202  div.style.animation = '';
    203  animLeft.play();
    204 
    205  div.style.top = '0px';
    206  div.style.transition = 'all 100s';
    207  flushComputedStyle(div);
    208  // *Then* add a transition
    209  div.style.top = '100px';
    210  flushComputedStyle(div);
    211 
    212  // Although the transition was added later, it should come first in the list
    213  const animations = document.getAnimations();
    214  assert_equals(animations.length, 2,
    215                'Both CSS animations and transitions are returned');
    216  assert_class_string(animations[0], 'CSSTransition', 'Transition comes first');
    217  assert_equals(animations[1], animLeft, 'Free animations come last');
    218 }, 'Order of CSS Animations - free animation vs CSS Transitions');
    219 
    220 test(t => {
    221  const div = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
    222  const animLeft = document.getAnimations()[0];
    223  const animTop  = document.getAnimations()[1];
    224 
    225  // Disassociate both animations from markup and restart in opposite order
    226  div.style.animation = '';
    227  animTop.play();
    228  animLeft.play();
    229 
    230  const animations = document.getAnimations();
    231  assert_equals(animations.length, 2,
    232                'getAnimations returns free animations');
    233  assert_equals(animations[0], animTop,
    234                'Free animations are returned in the order they are started');
    235  assert_equals(animations[1], animLeft,
    236                'Animations started later are returned later');
    237 
    238  // Restarting an animation should have no effect
    239  animTop.cancel();
    240  animTop.play();
    241  assert_equals(document.getAnimations()[0], animTop,
    242                'After restarting, the ordering of free animations' +
    243                ' does not change');
    244 }, 'Order of CSS Animations - free animations');
    245 
    246 test(t => {
    247  // Add an animation first
    248  const div = addDiv(t, { style: 'animation: animLeft 100s' });
    249  div.style.top = '0px';
    250  div.style.transition = 'all 100s';
    251  flushComputedStyle(div);
    252 
    253  // *Then* add a transition
    254  div.style.top = '100px';
    255  flushComputedStyle(div);
    256 
    257  // Although the transition was added later, it should come first in the list
    258  const animations = document.getAnimations();
    259  assert_equals(animations.length, 2,
    260                'Both CSS animations and transitions are returned');
    261  assert_class_string(animations[0], 'CSSTransition', 'Transition comes first');
    262  assert_class_string(animations[1], 'CSSAnimation', 'Animation comes second');
    263 }, 'Order of CSS Animations and CSS Transitions');
    264 
    265 test(t => {
    266  const div = addDiv(t, { style: 'animation: animLeft 100s forwards' });
    267  div.getAnimations()[0].finish();
    268  assert_equals(document.getAnimations().length, 1,
    269                'Forwards-filling CSS animations are returned');
    270 }, 'Finished but filling CSS Animations are returned');
    271 
    272 test(t => {
    273  const div = addDiv(t, { style: 'animation: animLeft 100s' });
    274  div.getAnimations()[0].finish();
    275  assert_equals(document.getAnimations().length, 0,
    276                'Non-filling finished CSS animations are not returned');
    277 }, 'Finished but not filling CSS Animations are not returned');
    278 
    279 test(t => {
    280  const div = addDiv(t, { style: 'animation: animLeft 100s 100s' });
    281  assert_equals(document.getAnimations().length, 1,
    282                'Yet-to-start CSS animations are returned');
    283 }, 'Yet-to-start CSS Animations are returned');
    284 
    285 test(t => {
    286  const div = addDiv(t, { style: 'animation: animLeft 100s' });
    287  div.getAnimations()[0].cancel();
    288  assert_equals(document.getAnimations().length, 0,
    289                'CSS animations canceled by the API are not returned');
    290 }, 'CSS Animations canceled via the API are not returned');
    291 
    292 test(t => {
    293  const div = addDiv(t, { style: 'animation: animLeft 100s' });
    294  const anim = div.getAnimations()[0];
    295  anim.cancel();
    296  anim.play();
    297  assert_equals(document.getAnimations().length, 1,
    298                'CSS animations canceled and restarted by the API are ' +
    299                'returned');
    300 }, 'CSS Animations canceled and restarted via the API are returned');
    301 
    302 test(t => {
    303  addStyle(t, {
    304      '#parent::after': "content: ''; animation: anim1 100s ; ",
    305      '#parent::before': "content: ''; animation: anim2 100s;",
    306      '#child::after': "content: ''; animation: anim3 100s;",
    307      '#child::before': "content: ''; animation: anim4 100s;",
    308    });
    309  const parent = addDiv(t, { id: 'parent' });
    310  const child = addDiv(t, { id: 'child'});
    311  parent.appendChild(child);
    312  var animations = document.getAnimations();
    313  var expectedAnimations = [
    314      [parent, '::before', 'anim2'],
    315      [parent, '::after', 'anim1'],
    316      [child, '::before', 'anim4'],
    317      [child, '::after', 'anim3'],
    318    ];
    319  pseudoAnimCompare(animations, expectedAnimations)
    320 
    321  // Swap animation1 and aimation3's effect
    322  var anim1 = animations[0];
    323  var anim3 = animations[2];
    324  const temp = anim1.effect;
    325  anim1.effect = anim3.effect;
    326  anim3.effect = temp;
    327 
    328  animations = document.getAnimations();
    329  expectedAnimations = [
    330      [child, '::before', 'anim2'],
    331      [parent, '::after', 'anim1'],
    332      [parent, '::before', 'anim4'],
    333      [child, '::after', 'anim3'],
    334    ];
    335  pseudoAnimCompare(animations, expectedAnimations)
    336 }, 'pseudo element with replaced target does not affect animation ordering');
    337 
    338 function pseudoAnimCompare(animations, expectedAnimations) {
    339  assert_equals(
    340      animations.length,
    341      expectedAnimations.length,
    342      'CSS animations on both pseudo-elements and elements are returned'
    343    );
    344  for (const [index, expected] of expectedAnimations.entries()) {
    345      const [element, pseudo, name] = expected;
    346      const actual = animations[index];
    347      if (pseudo) {
    348        assert_equals(
    349          actual.effect.target,
    350          element,
    351          `Animation #${index + 1} has the expected target`
    352        );
    353        assert_equals(
    354          actual.effect.pseudoElement,
    355          pseudo,
    356          `Animation #${index + 1} has the expected pseudo type`
    357        );
    358        if (name) {
    359          assert_equals(
    360            actual.animationName,
    361            name,
    362            `Animation #${index + 1} has the expected name`
    363          );
    364        }
    365      } else {
    366        assert_equals(
    367          actual.effect.target,
    368          element,
    369          `Animation #${index + 1} has the expected target`
    370        );
    371        assert_equals(
    372          actual.effect.pseudoElement,
    373          null,
    374          `Animation #${index + 1} has a null pseudo type`
    375        );
    376      }
    377    }
    378 }
    379 
    380 function pseudoTest(description, testMarkerPseudos) {
    381  test(t => {
    382    // Create two divs with the following arrangement:
    383    //
    384    //       parent
    385    //    (::marker,)  // Optionally
    386    //     ::before,
    387    //     ::after
    388    //        |
    389    //       child
    390 
    391    addStyle(t, {
    392      '#parent::after': "content: ''; animation: animLeft 100s;",
    393      '#parent::before': "content: ''; animation: animRight 100s;",
    394    });
    395 
    396    if (testMarkerPseudos) {
    397      addStyle(t, {
    398        '#parent': 'display: list-item;',
    399        '#parent::marker': "content: ''; animation: animLeft 100s;",
    400      });
    401    }
    402 
    403    const parent = addDiv(t, { id: 'parent' });
    404    const child = addDiv(t);
    405    parent.appendChild(child);
    406    for (const div of [parent, child]) {
    407      div.setAttribute('style', 'animation: animBottom 100s');
    408    }
    409 
    410    const expectedAnimations = [
    411      [parent, undefined],
    412      [parent, '::marker'],
    413      [parent, '::before'],
    414      [parent, '::after'],
    415      [child, undefined],
    416    ];
    417    if (!testMarkerPseudos) {
    418      expectedAnimations.splice(1, 1);
    419    }
    420 
    421    const animations = document.getAnimations();
    422    pseudoAnimCompare(animations, expectedAnimations)
    423  }, description);
    424 }
    425 
    426 pseudoTest('CSS Animations targetting (pseudo-)elements should have correct '
    427     + 'order after sorting', false);
    428 pseudoTest('CSS Animations targetting (pseudo-)elements should have correct '
    429     + 'order after sorting (::marker)', true);
    430 </script>