tor-browser

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

animate.html (13735B)


      1 <!DOCTYPE html>
      2 <meta charset=utf-8>
      3 <title>Animatable.animate</title>
      4 <link rel="help" href="https://drafts.csswg.org/web-animations/#dom-animatable-animate">
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 <script src="../../testcommon.js"></script>
      8 <script src="../../resources/easing-tests.js"></script>
      9 <script src="../../resources/keyframe-utils.js"></script>
     10 <script src="../../resources/keyframe-tests.js"></script>
     11 <script src="../../resources/timing-utils.js"></script>
     12 <script src="../../resources/timing-tests.js"></script>
     13 <style>
     14 .pseudo::before {content: '';}
     15 .pseudo::after {content: '';}
     16 .pseudo::marker {content: '';}
     17 </style>
     18 <body>
     19 <div id="log"></div>
     20 <iframe width="10" height="10" id="iframe"></iframe>
     21 <script>
     22 'use strict';
     23 
     24 // Tests on Element
     25 
     26 test(t => {
     27  const anim = createDiv(t).animate(null);
     28  assert_class_string(anim, 'Animation', 'Returned object is an Animation');
     29 }, 'Element.animate() creates an Animation object');
     30 
     31 test(t => {
     32  const iframe = window.frames[0];
     33  const div = createDiv(t, iframe.document);
     34  const anim = Element.prototype.animate.call(div, null);
     35  assert_equals(Object.getPrototypeOf(anim), iframe.Animation.prototype,
     36                'The prototype of the created Animation is that defined on'
     37                + ' the relevant global for the target element');
     38  assert_not_equals(Object.getPrototypeOf(anim), Animation.prototype,
     39                    'The prototype of the created Animation is NOT that of'
     40                    + ' the current global');
     41 }, 'Element.animate() creates an Animation object in the relevant realm of'
     42   + ' the target element');
     43 
     44 test(t => {
     45  const div = createDiv(t);
     46  const anim = Element.prototype.animate.call(div, null);
     47  assert_class_string(anim.effect, 'KeyframeEffect',
     48                      'Returned Animation has a KeyframeEffect');
     49 }, 'Element.animate() creates an Animation object with a KeyframeEffect');
     50 
     51 test(t => {
     52  const iframe = window.frames[0];
     53  const div = createDiv(t, iframe.document);
     54  const anim = Element.prototype.animate.call(div, null);
     55  assert_equals(Object.getPrototypeOf(anim.effect),
     56                iframe.KeyframeEffect.prototype,
     57                'The prototype of the created KeyframeEffect is that defined on'
     58                + ' the relevant global for the target element');
     59  assert_not_equals(Object.getPrototypeOf(anim.effect),
     60                    KeyframeEffect.prototype,
     61                    'The prototype of the created KeyframeEffect is NOT that of'
     62                    + ' the current global');
     63 }, 'Element.animate() creates an Animation object with a KeyframeEffect'
     64   + ' that is created in the relevant realm of the target element');
     65 
     66 for (const subtest of gEmptyKeyframeListTests) {
     67  test(t => {
     68    const anim = createDiv(t).animate(subtest, 2000);
     69    assert_not_equals(anim, null);
     70  }, 'Element.animate() accepts empty keyframe lists ' +
     71     `(input: ${JSON.stringify(subtest)})`);
     72 }
     73 
     74 for (const subtest of gKeyframesTests) {
     75  test(t => {
     76    const anim = createDiv(t).animate(subtest.input, 2000);
     77    assert_frame_lists_equal(anim.effect.getKeyframes(), subtest.output);
     78  }, `Element.animate() accepts ${subtest.desc}`);
     79 }
     80 
     81 for (const subtest of gInvalidKeyframesTests) {
     82  test(t => {
     83    const div = createDiv(t);
     84    assert_throws_js(TypeError, () => {
     85      div.animate(subtest.input, 2000);
     86    });
     87  }, `Element.animate() does not accept ${subtest.desc}`);
     88 }
     89 
     90 test(t => {
     91  const anim = createDiv(t).animate(null, 2000);
     92  assert_equals(anim.effect.getTiming().duration, 2000);
     93  assert_default_timing_except(anim.effect, ['duration']);
     94 }, 'Element.animate() accepts a double as an options argument');
     95 
     96 test(t => {
     97  const anim = createDiv(t).animate(null,
     98                                    { duration: Infinity, fill: 'forwards' });
     99  assert_equals(anim.effect.getTiming().duration, Infinity);
    100  assert_equals(anim.effect.getTiming().fill, 'forwards');
    101  assert_default_timing_except(anim.effect, ['duration', 'fill']);
    102 }, 'Element.animate() accepts a KeyframeAnimationOptions argument');
    103 
    104 test(t => {
    105  const anim = createDiv(t).animate(null);
    106  assert_default_timing_except(anim.effect, []);
    107 }, 'Element.animate() accepts an absent options argument');
    108 
    109 for (const invalid of gBadDelayValues) {
    110  test(t => {
    111    assert_throws_js(TypeError, () => {
    112      createDiv(t).animate(null, { delay: invalid });
    113    });
    114  }, `Element.animate() does not accept invalid delay value: ${invalid}`);
    115 }
    116 
    117 test(t => {
    118  const anim = createDiv(t).animate(null, { duration: 'auto' });
    119  assert_equals(anim.effect.getTiming().duration, 'auto', 'set duration \'auto\'');
    120  assert_equals(anim.effect.getComputedTiming().duration, 0,
    121                'getComputedTiming() after set duration \'auto\'');
    122 }, 'Element.animate() accepts a duration of \'auto\' using a dictionary'
    123   + ' object');
    124 
    125 for (const invalid of gBadDurationValues) {
    126  if (typeof invalid === 'string' && !isNaN(parseFloat(invalid))) {
    127    continue;
    128  }
    129  test(t => {
    130    assert_throws_js(TypeError, () => {
    131      createDiv(t).animate(null, invalid);
    132    });
    133  }, 'Element.animate() does not accept invalid duration value: '
    134     + (typeof invalid === 'string' ? `"${invalid}"` : invalid));
    135 }
    136 
    137 for (const invalid of gBadDurationValues) {
    138  test(t => {
    139    assert_throws_js(TypeError, () => {
    140      createDiv(t).animate(null, { duration: invalid });
    141    });
    142  }, 'Element.animate() does not accept invalid duration value: '
    143     + (typeof invalid === 'string' ? `"${invalid}"` : invalid)
    144     + ' using a dictionary object');
    145 }
    146 
    147 for (const invalidEasing of gInvalidEasings) {
    148  test(t => {
    149    assert_throws_js(TypeError, () => {
    150      createDiv(t).animate({ easing: invalidEasing }, 2000);
    151    });
    152  }, `Element.animate() does not accept invalid easing: '${invalidEasing}'`);
    153 }
    154 
    155 for (const invalid of gBadIterationStartValues) {
    156  test(t => {
    157    assert_throws_js(TypeError, () => {
    158      createDiv(t).animate(null, { iterationStart: invalid });
    159    });
    160  }, 'Element.animate() does not accept invalid iterationStart value: ' +
    161     invalid);
    162 }
    163 
    164 for (const invalid of gBadIterationsValues) {
    165  test(t => {
    166    assert_throws_js(TypeError, () => {
    167      createDiv(t).animate(null, { iterations: invalid });
    168    });
    169  }, 'Element.animate() does not accept invalid iterations value: ' +
    170     invalid);
    171 }
    172 
    173 test(t => {
    174  const anim = createDiv(t).animate(null, 2000);
    175  assert_equals(anim.id, '');
    176 }, 'Element.animate() correctly sets the id attribute when no id is specified');
    177 
    178 test(t => {
    179  const anim = createDiv(t).animate(null, { id: 'test' });
    180  assert_equals(anim.id, 'test');
    181 }, 'Element.animate() correctly sets the id attribute');
    182 
    183 test(t => {
    184  const anim = createDiv(t).animate(null, 2000);
    185  assert_equals(anim.timeline, document.timeline);
    186 }, 'Element.animate() correctly sets the Animation\'s timeline');
    187 
    188 async_test(t => {
    189  const iframe = document.createElement('iframe');
    190  iframe.width = 10;
    191  iframe.height = 10;
    192 
    193  iframe.addEventListener('load', t.step_func(() => {
    194    const div = createDiv(t, iframe.contentDocument);
    195    const anim = div.animate(null, 2000);
    196    assert_equals(anim.timeline, iframe.contentDocument.timeline);
    197    iframe.remove();
    198    t.done();
    199  }));
    200 
    201  document.body.appendChild(iframe);
    202 }, 'Element.animate() correctly sets the Animation\'s timeline when ' +
    203   'triggered on an element in a different document');
    204 
    205 for (const subtest of gAnimationTimelineTests) {
    206  test(t => {
    207    const anim = createDiv(t).animate(null, { timeline: subtest.timeline });
    208    assert_not_equals(anim, null,
    209                      'An animation sohuld be created');
    210    assert_equals(anim.timeline, subtest.expectedTimeline,
    211                  'Animation timeline should be '+
    212                  subtest.expectedTimelineDescription);
    213  }, 'Element.animate() correctly sets the Animation\'s timeline '
    214     + subtest.description + ' in KeyframeAnimationOptions.');
    215 }
    216 
    217 test(t => {
    218  const anim = createDiv(t).animate(null, 2000);
    219  assert_equals(anim.playState, 'running');
    220 }, 'Element.animate() calls play on the Animation');
    221 
    222 promise_test(async t => {
    223  const div = createDiv(t);
    224 
    225  let gotTransition = false;
    226  div.addEventListener('transitionrun', () => {
    227    gotTransition = true;
    228  });
    229 
    230  // Setup transition start point.
    231  div.style.transition = 'opacity 100s';
    232  getComputedStyle(div).opacity;
    233 
    234  // Update specified style but don't flush style.
    235  div.style.opacity = '0.5';
    236 
    237  // Trigger a new animation at the same time.
    238  const anim = div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
    239 
    240  // If Element.animate() produces a style change event it will have triggered
    241  // a transition.
    242  //
    243  // If it does NOT produce a style change event, the animation will override
    244  // the before-change style and after-change style such that a transition is
    245  // never triggered.
    246 
    247  // Wait for the animation to start and then for one more animation
    248  // frame to give the transitionrun event a chance to be dispatched.
    249  await anim.ready;
    250  await waitForAnimationFrames(1);
    251 
    252  assert_false(gotTransition, 'A transition should NOT have been triggered');
    253 }, 'Element.animate() does NOT trigger a style change event');
    254 
    255 // Tests on pseudo-elements
    256 // Some tests occur twice (on pseudo-elements with and without content)
    257 // in order to test both code paths for tree-abiding pseudo-elements in blink.
    258 
    259 test(t => {
    260  const div = createDiv(t);
    261  div.classList.add('pseudo');
    262  const anim = div.animate(null, {pseudoElement: '::before'});
    263  assert_class_string(anim, 'Animation', 'The returned object is an Animation');
    264 }, 'animate() with pseudoElement parameter creates an Animation object');
    265 
    266 test(t => {
    267  const div = createDiv(t);
    268  const anim = div.animate(null, {pseudoElement: '::before'});
    269  assert_class_string(anim, 'Animation', 'The returned object is an Animation');
    270 }, 'animate() with pseudoElement parameter without content creates an Animation object');
    271 
    272 test(t => {
    273  const div = createDiv(t);
    274  div.classList.add('pseudo');
    275  div.style.display = 'list-item';
    276  const anim = div.animate(null, {pseudoElement: '::marker'});
    277  assert_class_string(anim, 'Animation', 'The returned object is an Animation for ::marker');
    278 }, 'animate() with pseudoElement parameter creates an Animation object for ::marker');
    279 
    280 test(t => {
    281  const div = createDiv(t);
    282  div.classList.add('pseudo');
    283  div.textContent = 'foo';
    284  const anim = div.animate(null, {pseudoElement: '::first-line'});
    285  assert_class_string(anim, 'Animation', 'The returned object is an Animation for ::first-line');
    286 }, 'animate() with pseudoElement parameter creates an Animation object for ::first-line');
    287 
    288 test(t => {
    289  const div = createDiv(t);
    290  div.classList.add('pseudo');
    291  const anim = div.animate(null, {pseudoElement: '::before'});
    292  assert_equals(anim.effect.target, div, 'The returned element has the correct target element');
    293  assert_equals(anim.effect.pseudoElement, '::before',
    294                'The returned Animation targets the correct selector');
    295 }, 'animate() with pseudoElement an Animation object targeting ' +
    296   'the correct pseudo-element');
    297 
    298 test(t => {
    299  const div = createDiv(t);
    300  const anim = div.animate(null, {pseudoElement: '::before'});
    301  assert_equals(anim.effect.target, div, 'The returned element has the correct target element');
    302  assert_equals(anim.effect.pseudoElement, '::before',
    303                'The returned Animation targets the correct selector');
    304 }, 'animate() with pseudoElement without content creates an Animation object targeting ' +
    305   'the correct pseudo-element');
    306 
    307 test(t => {
    308  const div = createDiv(t);
    309  div.classList.add('pseudo');
    310  div.style.display = 'list-item';
    311  const anim = div.animate(null, {pseudoElement: '::marker'});
    312  assert_equals(anim.effect.target, div, 'The returned element has the correct target element');
    313  assert_equals(anim.effect.pseudoElement, '::marker',
    314                'The returned Animation targets the correct selector');
    315 }, 'animate() with pseudoElement an Animation object targeting ' +
    316   'the correct pseudo-element for ::marker');
    317 
    318 test(t => {
    319  const div = createDiv(t);
    320  div.classList.add('pseudo');
    321  div.textContent = 'foo';
    322  const anim = div.animate(null, {pseudoElement: '::first-line'});
    323  assert_equals(anim.effect.target, div, 'The returned element has the correct target element');
    324  assert_equals(anim.effect.pseudoElement, '::first-line',
    325                'The returned Animation targets the correct selector');
    326 }, 'animate() with pseudoElement an Animation object targeting ' +
    327   'the correct pseudo-element for ::first-line');
    328 
    329 for (const pseudo of [
    330  '',
    331  'before',
    332  ':abc',
    333  '::abc',
    334 ]) {
    335  test(t => {
    336    const div = createDiv(t);
    337    assert_throws_dom("SyntaxError", () => {
    338      div.animate(null, {pseudoElement: pseudo});
    339    });
    340  }, `animate() with a non-null invalid pseudoElement '${pseudo}' throws a ` +
    341     `SyntaxError`);
    342 }
    343 
    344 test(t => {
    345  const div = createDiv(t);
    346  div.animate(null, { pseudoElement: '::placeHOLDER' });
    347 }, `animate() with pseudoElement ::placeholder does not throw`);
    348 
    349 promise_test(async t => {
    350  const div = createDiv(t);
    351  div.classList.add('pseudo');
    352  let animBefore = div.animate({opacity: [1, 0]}, {duration: 1, pseudoElement: '::before', fill: 'both'});
    353  let animAfter = div.animate({opacity: [1, 0]}, {duration: 1, pseudoElement: '::after', fill: 'both'});
    354  await animBefore.finished;
    355  await animAfter.finished;
    356  // The animation on ::before should not be replaced as it targets a different
    357  // pseudo-element.
    358  assert_equals(animBefore.replaceState, 'active');
    359  assert_equals(animAfter.replaceState, 'active');
    360 }, 'Finished fill animation doesn\'t replace animation on a different pseudoElement');
    361 
    362 </script>
    363 </body>