tor-browser

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

finishing-an-animation.html (11552B)


      1 <!DOCTYPE html>
      2 <meta charset=utf-8>
      3 <title>Finishing an animation</title>
      4 <link rel="help"
      5  href="https://drafts.csswg.org/web-animations/#finishing-an-animation-section">
      6 <script src="/resources/testharness.js"></script>
      7 <script src="/resources/testharnessreport.js"></script>
      8 <script src="../../testcommon.js"></script>
      9 <script src="../../resources/timing-override.js"></script>
     10 <body>
     11 <div id="log"></div>
     12 <script>
     13 'use strict';
     14 
     15 test(t => {
     16  const div = createDiv(t);
     17  const animation = div.animate(null, 100 * MS_PER_SEC);
     18  animation.playbackRate = 0;
     19 
     20  assert_throws_dom('InvalidStateError', () => {
     21    animation.finish();
     22  });
     23 }, 'Finishing an animation with a zero playback rate throws');
     24 
     25 test(t => {
     26  const div = createDiv(t);
     27  const animation = div.animate(null,
     28                                { duration : 100 * MS_PER_SEC,
     29                                  iterations : Infinity });
     30 
     31  assert_throws_dom('InvalidStateError', () => {
     32    animation.finish();
     33  });
     34 }, 'Finishing an infinite animation throws');
     35 
     36 test(t => {
     37  const div = createDiv(t);
     38  const animation = div.animate(null, 100 * MS_PER_SEC);
     39  animation.finish();
     40 
     41  assert_time_equals_literal(animation.currentTime, 100 * MS_PER_SEC,
     42    'After finishing, the currentTime should be set to the end of the'
     43    + ' active duration');
     44 }, 'Finishing an animation seeks to the end time');
     45 
     46 test(t => {
     47  const div = createDiv(t);
     48  const animation = div.animate(null, 100 * MS_PER_SEC);
     49   // 1s past effect end
     50  animation.currentTime =
     51    animation.effect.getComputedTiming().endTime + 1 * MS_PER_SEC;
     52  animation.finish();
     53 
     54  assert_time_equals_literal(animation.currentTime, 100 * MS_PER_SEC,
     55    'After finishing, the currentTime should be set back to the end of the'
     56    + ' active duration');
     57 }, 'Finishing an animation with a current time past the effect end jumps'
     58   + ' back to the end');
     59 
     60 promise_test(async t => {
     61  const div = createDiv(t);
     62  const animation = div.animate(null, 100 * MS_PER_SEC);
     63  animation.currentTime = 100 * MS_PER_SEC;
     64  await animation.finished;
     65 
     66  animation.playbackRate = -1;
     67  animation.finish();
     68 
     69  assert_equals(animation.currentTime, 0,
     70                'After finishing a reversed animation the currentTime ' +
     71                'should be set to zero');
     72 }, 'Finishing a reversed animation jumps to zero time');
     73 
     74 promise_test(async t => {
     75  const div = createDiv(t);
     76  const animation = div.animate(null, 100 * MS_PER_SEC);
     77  animation.currentTime = 100 * MS_PER_SEC;
     78  await animation.finished;
     79 
     80  animation.playbackRate = -1;
     81  animation.currentTime = -1000;
     82  animation.finish();
     83 
     84  assert_equals(animation.currentTime, 0,
     85                'After finishing a reversed animation the currentTime ' +
     86                'should be set back to zero');
     87 }, 'Finishing a reversed animation with a current time less than zero'
     88   + ' makes it jump back to zero');
     89 
     90 promise_test(async t => {
     91  const div = createDiv(t);
     92  const animation = div.animate(null, 100 * MS_PER_SEC);
     93  animation.pause();
     94  await animation.ready;
     95 
     96  animation.finish();
     97 
     98  assert_equals(animation.playState, 'finished',
     99                'The play state of a paused animation should become ' +
    100                '"finished"');
    101  assert_times_equal(animation.startTime,
    102                     animation.timeline.currentTime - 100 * MS_PER_SEC,
    103                     'The start time of a paused animation should be set');
    104 }, 'Finishing a paused animation resolves the start time');
    105 
    106 test(t => {
    107  const div = createDiv(t);
    108  const animation = div.animate(null, 100 * MS_PER_SEC);
    109  // Update playbackRate so we can test that the calculated startTime
    110  // respects it
    111  animation.playbackRate = 2;
    112  animation.pause();
    113  // While animation is still pause-pending call finish()
    114  animation.finish();
    115 
    116  assert_false(animation.pending);
    117  assert_equals(animation.playState, 'finished',
    118                'The play state of a pause-pending animation should become ' +
    119                '"finished"');
    120  assert_times_equal(animation.startTime,
    121                     animation.timeline.currentTime - 100 * MS_PER_SEC / 2,
    122                     'The start time of a pause-pending animation should ' +
    123                     'be set');
    124 }, 'Finishing a pause-pending animation resolves the pending task'
    125   + ' immediately and update the start time');
    126 
    127 test(t => {
    128  const div = createDiv(t);
    129  const animation = div.animate(null, 100 * MS_PER_SEC);
    130  animation.playbackRate = -2;
    131  animation.pause();
    132  animation.finish();
    133 
    134  assert_false(animation.pending);
    135  assert_equals(animation.playState, 'finished',
    136                'The play state of a pause-pending animation should become ' +
    137                '"finished"');
    138  assert_times_equal(animation.startTime, animation.timeline.currentTime,
    139                     'The start time of a pause-pending animation should be ' +
    140                     'set');
    141 }, 'Finishing a pause-pending animation with negative playback rate'
    142   + ' resolves the pending task immediately');
    143 
    144 test(t => {
    145  const div = createDiv(t);
    146  const animation = div.animate(null, 100 * MS_PER_SEC);
    147  animation.playbackRate = 0.5;
    148  animation.finish();
    149 
    150  assert_false(animation.pending);
    151  assert_equals(animation.playState, 'finished',
    152                'The play state of a play-pending animation should become ' +
    153                '"finished"');
    154  assert_times_equal(animation.startTime,
    155                     animation.timeline.currentTime - 100 * MS_PER_SEC / 0.5,
    156                     'The start time of a play-pending animation should ' +
    157                     'be set');
    158 }, 'Finishing an animation while play-pending resolves the pending'
    159   + ' task immediately');
    160 
    161 // FIXME: Add a test for when we are play-pending without an active timeline.
    162 // - In that case even after calling finish() we should still be pending but
    163 //   the current time should be updated
    164 
    165 promise_test(async t => {
    166  const div = createDiv(t);
    167  const animation = div.animate(null, 100 * MS_PER_SEC);
    168  await animation.ready;
    169 
    170  animation.pause();
    171  animation.play();
    172  // We are now in the unusual situation of being play-pending whilst having
    173  // a resolved start time. Check that finish() still triggers a transition
    174  // to the finished state immediately.
    175  animation.finish();
    176 
    177  assert_equals(animation.playState, 'finished',
    178                'After aborting a pause then finishing an animation its play ' +
    179                'state should become "finished" immediately');
    180 }, 'Finishing an animation during an aborted pause makes it finished'
    181   + ' immediately');
    182 
    183 promise_test(async t => {
    184  const div = createDiv(t);
    185  const animation = div.animate(null, 100 * MS_PER_SEC);
    186  let resolvedFinished = false;
    187  animation.finished.then(() => {
    188    resolvedFinished = true;
    189  });
    190 
    191  await animation.ready;
    192 
    193  animation.finish();
    194  await Promise.resolve();
    195 
    196  assert_true(resolvedFinished, 'finished promise should be resolved');
    197 }, 'Finishing an animation resolves the finished promise synchronously');
    198 
    199 promise_test(async t => {
    200  const effect = new KeyframeEffect(null, null, 100 * MS_PER_SEC);
    201  const animation = new Animation(effect, document.timeline);
    202  let resolvedFinished = false;
    203  animation.finished.then(() => {
    204    resolvedFinished = true;
    205  });
    206 
    207  await animation.ready;
    208 
    209  animation.finish();
    210  await Promise.resolve();
    211 
    212  assert_true(resolvedFinished, 'finished promise should be resolved');
    213 }, 'Finishing an animation without a target resolves the finished promise'
    214   + ' synchronously');
    215 
    216 promise_test(async t => {
    217  const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
    218  const promise = animation.ready;
    219  let readyResolved = false;
    220 
    221  animation.finish();
    222  animation.ready.then(() => { readyResolved = true; });
    223 
    224  const promiseResult = await animation.finished;
    225 
    226  assert_equals(promiseResult, animation);
    227  assert_equals(animation.ready, promise);
    228  assert_true(readyResolved);
    229 }, 'A pending ready promise is resolved and not replaced when the animation'
    230   + ' is finished');
    231 
    232 promise_test(async t => {
    233  const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
    234  await animation.ready;
    235 
    236  animation.updatePlaybackRate(2);
    237  assert_true(animation.pending);
    238 
    239  animation.finish();
    240  assert_false(animation.pending);
    241  assert_equals(animation.playbackRate, 2);
    242  assert_time_equals_literal(animation.currentTime, 100 * MS_PER_SEC);
    243 }, 'A pending playback rate should be applied immediately when an animation'
    244   + ' is finished');
    245 
    246 promise_test(async t => {
    247  const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
    248  await animation.ready;
    249 
    250  animation.updatePlaybackRate(0);
    251 
    252  assert_throws_dom('InvalidStateError', () => {
    253    animation.finish();
    254  });
    255 }, 'An exception should be thrown if the effective playback rate is zero');
    256 
    257 promise_test(async t => {
    258  const animation = createDiv(t).animate(null, {
    259    duration: 100 * MS_PER_SEC,
    260    iterations: Infinity
    261  });
    262  animation.currentTime = 50 * MS_PER_SEC;
    263  animation.playbackRate = -1;
    264  await animation.ready;
    265 
    266  animation.updatePlaybackRate(1);
    267 
    268  assert_throws_dom('InvalidStateError', () => {
    269    animation.finish();
    270  });
    271 }, 'An exception should be thrown when finishing if the effective playback rate'
    272   + ' is positive and the target effect end is infinity');
    273 
    274 promise_test(async t => {
    275  const animation = createDiv(t).animate(null, {
    276    duration: 100 * MS_PER_SEC,
    277    iterations: Infinity
    278  });
    279  await animation.ready;
    280 
    281  animation.updatePlaybackRate(-1);
    282 
    283  animation.finish();
    284  // Should not have thrown
    285 }, 'An exception is NOT thrown when finishing if the effective playback rate'
    286   + ' is negative and the target effect end is infinity');
    287 
    288 promise_test(async t => {
    289  const div = createDiv(t);
    290  const animation = div.animate({}, 100 * MS_PER_SEC);
    291  div.remove();
    292 
    293  const eventWatcher = new EventWatcher(t, animation, 'finish');
    294 
    295  await animation.ready;
    296  animation.finish();
    297 
    298  await eventWatcher.wait_for('finish');
    299  assert_equals(animation.effect.target.parentNode, null,
    300    'finish event should be fired for the animation on an orphaned element');
    301 }, 'Finishing an animation fires finish event on orphaned element');
    302 
    303 promise_test(async t => {
    304  const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
    305  await animation.ready;
    306 
    307  const originalFinishPromise = animation.finished;
    308 
    309  animation.cancel();
    310  assert_equals(animation.startTime, null);
    311  assert_equals(animation.currentTime, null);
    312 
    313  const resolvedFinishPromise = animation.finished;
    314  assert_not_equals(originalFinishPromise, resolvedFinishPromise,
    315               'Canceling an animation should create a new finished promise');
    316 
    317  animation.finish();
    318  assert_equals(animation.playState, 'finished',
    319                'The play state of a canceled animation should become ' +
    320                '"finished"');
    321  assert_times_equal(animation.startTime,
    322                     animation.timeline.currentTime - 100 * MS_PER_SEC,
    323                     'The start time of a finished animation should be set');
    324  assert_times_equal(animation.currentTime, 100000,
    325                     'Hold time should be set to end boundary of the animation');
    326 
    327 }, 'Finishing a canceled animation sets the current and start times');
    328 
    329 promise_test(async t => {
    330  const animation = createDiv(t).animate(null, 1);
    331  await animation.finished;
    332  const eventWatcher = new EventWatcher(t, animation, 'finish', waitForNextFrame);
    333  await eventWatcher.wait_for('finish');
    334 }, 'Finishing an animation fires finish event when a finish event listener is '
    335  + 'added as the finished promise resolves');
    336 
    337 </script>
    338 </body>