tor-browser

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

finished.html (15169B)


      1 <!DOCTYPE html>
      2 <meta charset=utf-8>
      3 <title>Animation.finished</title>
      4 <link rel="help" href="https://drafts.csswg.org/web-animations/#dom-animation-finished">
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 <script src="../../testcommon.js"></script>
      8 <body>
      9 <div id="log"></div>
     10 <script>
     11 'use strict';
     12 
     13 promise_test(t => {
     14  const div = createDiv(t);
     15  const animation = div.animate({}, 100 * MS_PER_SEC);
     16  const previousFinishedPromise = animation.finished;
     17  return animation.ready.then(() => {
     18    assert_equals(animation.finished, previousFinishedPromise,
     19                  'Finished promise is the same object when playing starts');
     20    animation.pause();
     21    assert_equals(animation.finished, previousFinishedPromise,
     22                  'Finished promise does not change when pausing');
     23    animation.play();
     24    assert_equals(animation.finished, previousFinishedPromise,
     25                  'Finished promise does not change when play() unpauses');
     26 
     27    animation.currentTime = 100 * MS_PER_SEC;
     28 
     29    return animation.finished;
     30  }).then(() => {
     31    assert_equals(animation.finished, previousFinishedPromise,
     32                  'Finished promise is the same object when playing completes');
     33  });
     34 }, 'Test pausing then playing does not change the finished promise');
     35 
     36 promise_test(t => {
     37  const div = createDiv(t);
     38  const animation = div.animate({}, 100 * MS_PER_SEC);
     39  let previousFinishedPromise = animation.finished;
     40  animation.finish();
     41  return animation.finished.then(() => {
     42    assert_equals(animation.finished, previousFinishedPromise,
     43                  'Finished promise is the same object when playing completes');
     44    animation.play();
     45    assert_not_equals(animation.finished, previousFinishedPromise,
     46                  'Finished promise changes when replaying animation');
     47 
     48    previousFinishedPromise = animation.finished;
     49    animation.play();
     50    assert_equals(animation.finished, previousFinishedPromise,
     51                  'Finished promise is the same after redundant play() call');
     52 
     53  });
     54 }, 'Test restarting a finished animation');
     55 
     56 promise_test(t => {
     57  const div = createDiv(t);
     58  const animation = div.animate({}, 100 * MS_PER_SEC);
     59  let previousFinishedPromise;
     60  animation.finish();
     61  return animation.finished.then(() => {
     62    previousFinishedPromise = animation.finished;
     63    animation.playbackRate = -1;
     64    assert_not_equals(animation.finished, previousFinishedPromise,
     65                      'Finished promise should be replaced when reversing a ' +
     66                      'finished promise');
     67    animation.currentTime = 0;
     68    return animation.finished;
     69  }).then(() => {
     70    previousFinishedPromise = animation.finished;
     71    animation.play();
     72    assert_not_equals(animation.finished, previousFinishedPromise,
     73                      'Finished promise is replaced after play() call on ' +
     74                      'finished, reversed animation');
     75  });
     76 }, 'Test restarting a reversed finished animation');
     77 
     78 promise_test(t => {
     79  const div = createDiv(t);
     80  const animation = div.animate({}, 100 * MS_PER_SEC);
     81  const previousFinishedPromise = animation.finished;
     82  animation.finish();
     83  return animation.finished.then(() => {
     84    animation.currentTime = 100 * MS_PER_SEC + 1000;
     85    assert_equals(animation.finished, previousFinishedPromise,
     86                  'Finished promise is unchanged jumping past end of ' +
     87                  'finished animation');
     88  });
     89 }, 'Test redundant finishing of animation');
     90 
     91 promise_test(t => {
     92  const div = createDiv(t);
     93  const animation = div.animate({}, 100 * MS_PER_SEC);
     94  // Setup callback to run if finished promise is resolved
     95  let finishPromiseResolved = false;
     96  animation.finished.then(() => {
     97    finishPromiseResolved = true;
     98  });
     99  return animation.ready.then(() => {
    100    // Jump to mid-way in interval and pause
    101    animation.currentTime = 100 * MS_PER_SEC / 2;
    102    animation.pause();
    103    return animation.ready;
    104  }).then(() => {
    105    // Jump to the end
    106    // (But don't use finish() since that should unpause as well)
    107    animation.currentTime = 100 * MS_PER_SEC;
    108    return waitForAnimationFrames(2);
    109  }).then(() => {
    110    assert_false(finishPromiseResolved,
    111                 'Finished promise should not resolve when paused');
    112  });
    113 }, 'Finished promise does not resolve when paused');
    114 
    115 promise_test(t => {
    116  const div = createDiv(t);
    117  const animation = div.animate({}, 100 * MS_PER_SEC);
    118  // Setup callback to run if finished promise is resolved
    119  let finishPromiseResolved = false;
    120  animation.finished.then(() => {
    121    finishPromiseResolved = true;
    122  });
    123  return animation.ready.then(() => {
    124    // Jump to mid-way in interval and pause
    125    animation.currentTime = 100 * MS_PER_SEC / 2;
    126    animation.pause();
    127    // Jump to the end
    128    animation.currentTime = 100 * MS_PER_SEC;
    129    return waitForAnimationFrames(2);
    130  }).then(() => {
    131    assert_false(finishPromiseResolved,
    132                 'Finished promise should not resolve when pause-pending');
    133  });
    134 }, 'Finished promise does not resolve when pause-pending');
    135 
    136 promise_test(t => {
    137  const div = createDiv(t);
    138  const animation = div.animate({}, 100 * MS_PER_SEC);
    139  animation.finish();
    140  return animation.finished.then(resolvedAnimation => {
    141    assert_equals(resolvedAnimation, animation,
    142                  'Object identity of animation passed to Promise callback'
    143                  + ' matches the animation object owning the Promise');
    144  });
    145 }, 'The finished promise is fulfilled with its Animation');
    146 
    147 promise_test(t => {
    148  const div = createDiv(t);
    149  const animation = div.animate({}, 100 * MS_PER_SEC);
    150  const previousFinishedPromise = animation.finished;
    151 
    152  // Set up listeners on finished promise
    153  const retPromise = animation.finished.then(() => {
    154    assert_unreached('finished promise was fulfilled');
    155  }).catch(err => {
    156    assert_equals(err.name, 'AbortError',
    157                  'finished promise is rejected with AbortError');
    158    assert_not_equals(animation.finished, previousFinishedPromise,
    159                      'Finished promise should change after the original is ' +
    160                      'rejected');
    161  });
    162 
    163  animation.cancel();
    164 
    165  return retPromise;
    166 }, 'finished promise is rejected when an animation is canceled by calling ' +
    167   'cancel()');
    168 
    169 promise_test(t => {
    170  const div = createDiv(t);
    171  const animation = div.animate({}, 100 * MS_PER_SEC);
    172  const previousFinishedPromise = animation.finished;
    173  animation.finish();
    174  return animation.finished.then(() => {
    175    animation.cancel();
    176    assert_not_equals(animation.finished, previousFinishedPromise,
    177                      'A new finished promise should be created when'
    178                      + ' canceling a finished animation');
    179  });
    180 }, 'canceling an already-finished animation replaces the finished promise');
    181 
    182 promise_test(t => {
    183  const div = createDiv(t);
    184  const animation = div.animate({}, 100 * MS_PER_SEC);
    185  const HALF_DUR = 100 * MS_PER_SEC / 2;
    186  const QUARTER_DUR = 100 * MS_PER_SEC / 4;
    187  let gotNextFrame = false;
    188  let currentTimeBeforeShortening;
    189  animation.currentTime = HALF_DUR;
    190  return animation.ready.then(() => {
    191    currentTimeBeforeShortening = animation.currentTime;
    192    animation.effect.updateTiming({ duration: QUARTER_DUR });
    193    // Below we use gotNextFrame to check that shortening of the animation
    194    // duration causes the finished promise to resolve, rather than it just
    195    // getting resolved on the next animation frame. This relies on the fact
    196    // that the promises are resolved as a micro-task before the next frame
    197    // happens.
    198    waitForAnimationFrames(1).then(() => {
    199      gotNextFrame = true;
    200    });
    201 
    202    return animation.finished;
    203  }).then(() => {
    204    assert_false(gotNextFrame, 'shortening of the animation duration should ' +
    205                               'resolve the finished promise');
    206    assert_equals(animation.currentTime, currentTimeBeforeShortening,
    207                  'currentTime should be unchanged when duration shortened');
    208    const previousFinishedPromise = animation.finished;
    209    animation.effect.updateTiming({ duration: 100 * MS_PER_SEC });
    210    assert_not_equals(animation.finished, previousFinishedPromise,
    211                      'Finished promise should change after lengthening the ' +
    212                      'duration causes the animation to become active');
    213  });
    214 }, 'Test finished promise changes for animation duration changes');
    215 
    216 promise_test(t => {
    217  const div = createDiv(t);
    218  const animation = div.animate({}, 100 * MS_PER_SEC);
    219  const retPromise = animation.ready.then(() => {
    220    animation.playbackRate = 0;
    221    animation.currentTime = 100 * MS_PER_SEC + 1000;
    222    return waitForAnimationFrames(2);
    223  });
    224 
    225  animation.finished.then(t.step_func(() => {
    226    assert_unreached('finished promise should not resolve when playbackRate ' +
    227                     'is zero');
    228  }));
    229 
    230  return retPromise;
    231 }, 'Test finished promise changes when playbackRate == 0');
    232 
    233 promise_test(t => {
    234  const div = createDiv(t);
    235  const animation = div.animate({}, 100 * MS_PER_SEC);
    236  return animation.ready.then(() => {
    237    animation.playbackRate = -1;
    238    return animation.finished;
    239  });
    240 }, 'Test finished promise resolves when reaching to the natural boundary.');
    241 
    242 promise_test(t => {
    243  const div = createDiv(t);
    244  const animation = div.animate({}, 100 * MS_PER_SEC);
    245  const previousFinishedPromise = animation.finished;
    246  animation.finish();
    247  return animation.finished.then(() => {
    248    animation.currentTime = 0;
    249    assert_not_equals(animation.finished, previousFinishedPromise,
    250                      'Finished promise should change once a prior ' +
    251                      'finished promise resolved and the animation ' +
    252                      'falls out finished state');
    253  });
    254 }, 'Test finished promise changes when a prior finished promise resolved ' +
    255   'and the animation falls out finished state');
    256 
    257 test(t => {
    258  const div = createDiv(t);
    259  const animation = div.animate({}, 100 * MS_PER_SEC);
    260  const previousFinishedPromise = animation.finished;
    261  animation.currentTime = 100 * MS_PER_SEC;
    262  animation.currentTime = 100 * MS_PER_SEC / 2;
    263  assert_equals(animation.finished, previousFinishedPromise,
    264                'No new finished promise generated when finished state ' +
    265                'is checked asynchronously');
    266 }, 'Test no new finished promise generated when finished state ' +
    267   'is checked asynchronously');
    268 
    269 test(t => {
    270  const div = createDiv(t);
    271  const animation = div.animate({}, 100 * MS_PER_SEC);
    272  const previousFinishedPromise = animation.finished;
    273  animation.finish();
    274  animation.currentTime = 100 * MS_PER_SEC / 2;
    275  assert_not_equals(animation.finished, previousFinishedPromise,
    276                    'New finished promise generated when finished state ' +
    277                    'is checked synchronously');
    278 }, 'Test new finished promise generated when finished state ' +
    279   'is checked synchronously');
    280 
    281 promise_test(t => {
    282  const div = createDiv(t);
    283  const animation = div.animate({}, 100 * MS_PER_SEC);
    284  let resolvedFinished = false;
    285  animation.finished.then(() => {
    286    resolvedFinished = true;
    287  });
    288  return animation.ready.then(() => {
    289    animation.finish();
    290    animation.currentTime = 100 * MS_PER_SEC / 2;
    291  }).then(() => {
    292    assert_true(resolvedFinished,
    293      'Animation.finished should be resolved even if ' +
    294      'the finished state is changed soon');
    295  });
    296 
    297 }, 'Test synchronous finished promise resolved even if finished state ' +
    298   'is changed soon');
    299 
    300 promise_test(t => {
    301  const div = createDiv(t);
    302  const animation = div.animate({}, 100 * MS_PER_SEC);
    303  let resolvedFinished = false;
    304  animation.finished.then(() => {
    305    resolvedFinished = true;
    306  });
    307 
    308  return animation.ready.then(() => {
    309    animation.currentTime = 100 * MS_PER_SEC;
    310    animation.finish();
    311  }).then(() => {
    312    assert_true(resolvedFinished,
    313      'Animation.finished should be resolved soon after finish() is ' +
    314      'called even if there are other asynchronous promises just before it');
    315  });
    316 }, 'Test synchronous finished promise resolved even if asynchronous ' +
    317   'finished promise happens just before synchronous promise');
    318 
    319 promise_test(t => {
    320  const div = createDiv(t);
    321  const animation = div.animate({}, 100 * MS_PER_SEC);
    322  animation.finished.then(t.step_func(() => {
    323    assert_unreached('Animation.finished should not be resolved');
    324  }));
    325 
    326  return animation.ready.then(() => {
    327    animation.currentTime = 100 * MS_PER_SEC;
    328    animation.currentTime = 100 * MS_PER_SEC / 2;
    329  });
    330 }, 'Test finished promise is not resolved when the animation ' +
    331   'falls out finished state immediately');
    332 
    333 promise_test(t => {
    334  const div = createDiv(t);
    335  const animation = div.animate({}, 100 * MS_PER_SEC);
    336  return animation.ready.then(() => {
    337    animation.currentTime = 100 * MS_PER_SEC;
    338    animation.finished.then(t.step_func(() => {
    339      assert_unreached('Animation.finished should not be resolved');
    340    }));
    341    animation.currentTime = 0;
    342  });
    343 
    344 }, 'Test finished promise is not resolved once the animation ' +
    345   'falls out finished state even though the current finished ' +
    346   'promise is generated soon after animation state became finished');
    347 
    348 promise_test(t => {
    349  const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
    350  let ready = false;
    351  animation.ready.then(
    352    t.step_func(() => {
    353      ready = true;
    354    }),
    355    t.unreached_func('Ready promise must not be rejected')
    356  );
    357 
    358  const testSuccess = animation.finished.then(
    359    t.step_func(() => {
    360      assert_true(ready, 'Ready promise has resolved');
    361    }),
    362    t.unreached_func('Finished promise must not be rejected')
    363  );
    364 
    365  const timeout = waitForAnimationFrames(3).then(() => {
    366    return Promise.reject('Finished promise did not arrive in time');
    367  });
    368 
    369  animation.finish();
    370  return Promise.race([timeout, testSuccess]);
    371 }, 'Finished promise should be resolved after the ready promise is resolved');
    372 
    373 promise_test(t => {
    374  const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
    375  let caught = false;
    376  animation.ready.then(
    377    t.unreached_func('Ready promise must not be resolved'),
    378    t.step_func(() => {
    379      caught = true;
    380    })
    381  );
    382 
    383  const testSuccess = animation.finished.then(
    384    t.unreached_func('Finished promise must not be resolved'),
    385    t.step_func(() => {
    386      assert_true(caught, 'Ready promise has been rejected');
    387    })
    388  );
    389 
    390  const timeout = waitForAnimationFrames(3).then(() => {
    391    return Promise.reject('Finished promise was not rejected in time');
    392  });
    393 
    394  animation.cancel();
    395  return Promise.race([timeout, testSuccess]);
    396 }, 'Finished promise should be rejected after the ready promise is rejected');
    397 
    398 promise_test(async t => {
    399  const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
    400 
    401  // Ensure the finished promise is created
    402  const finished = animation.finished;
    403 
    404  window.addEventListener(
    405    'unhandledrejection',
    406    t.unreached_func('Should not get an unhandled rejection')
    407  );
    408 
    409  animation.cancel();
    410 
    411  // Wait a moment to allow a chance for the event to be dispatched.
    412  await waitForAnimationFrames(2);
    413 }, 'Finished promise does not report an unhandledrejection when rejected');
    414 
    415 </script>
    416 </body>