tor-browser

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

updating-the-finished-state.html (18703B)


      1 <!DOCTYPE html>
      2 <meta charset=utf-8>
      3 <title>Updating the finished state</title>
      4 <link rel="help" href="https://drafts.csswg.org/web-animations/#updating-the-finished-state">
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 <script src="/web-animations/testcommon.js"></script>
      8 <script src="testcommon.js"></script>
      9 <style>
     10 .scroller {
     11  overflow: auto;
     12  height: 100px;
     13  width: 100px;
     14  will-change: transform;
     15 }
     16 
     17 .contents {
     18  height: 1000px;
     19  width: 100%;
     20 }
     21 </style>
     22 <body>
     23 <script>
     24 'use strict';
     25 
     26 // --------------------------------------------------------------------
     27 //
     28 // TESTS FOR UPDATING THE HOLD TIME
     29 //
     30 // --------------------------------------------------------------------
     31 
     32 // CASE 1: playback rate > 0 and current time >= target effect end
     33 // (Also the start time is resolved and there is pending task)
     34 // Did seek = true
     35 promise_test(async t => {
     36  const anim = createScrollLinkedAnimation(t);
     37  const scroller = anim.timeline.source;
     38  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
     39  // Wait for new animation frame which allows the timeline to compute new
     40  // current time.
     41  await waitForNextFrame();
     42  anim.play();
     43 
     44  await anim.ready;
     45 
     46  anim.currentTime = CSS.percent(200);
     47  scroller.scrollTop = 0.7 * maxScroll;
     48  await waitForNextFrame();
     49 
     50  assert_percents_equal(anim.currentTime, 200,
     51      'Hold time is set so current time should NOT change');
     52 }, 'Updating the finished state when seeking past end');
     53 
     54 // Did seek = false
     55 promise_test(async t => {
     56  const anim = createScrollLinkedAnimation(t);
     57  const scroller = anim.timeline.source;
     58  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
     59 
     60  // Wait for new animation frame which allows the timeline to compute new
     61  // current time.
     62  await waitForNextFrame();
     63  anim.play();
     64  await anim.ready;
     65 
     66  scroller.scrollTop = maxScroll;
     67  await waitForNextFrame();
     68 
     69  assert_percents_equal(anim.currentTime, 100,
     70      'Hold time is set to target end clamping current time');
     71 }, 'Updating the finished state when playing exactly to end');
     72 
     73 // Did seek = true
     74 promise_test(async t => {
     75  const anim = createScrollLinkedAnimation(t);
     76  const scroller = anim.timeline.source;
     77  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
     78 
     79  // Wait for new animation frame which allows the timeline to compute new
     80  // current time.
     81  await waitForNextFrame();
     82  await anim.ready;
     83 
     84  anim.currentTime = CSS.percent(100);
     85  scroller.scrollTop = 0.7 * maxScroll;
     86  await waitForNextFrame();
     87 
     88  assert_percents_equal(anim.currentTime, 100,
     89      'Hold time is set so current time should NOT change');
     90 }, 'Updating the finished state when seeking exactly to end');
     91 
     92 
     93 // CASE 2: playback rate < 0 and current time <= 0
     94 // (Also the start time is resolved and there is pending task)
     95 
     96 // Did seek = false
     97 promise_test(async t => {
     98  const anim = createScrollLinkedAnimation(t);
     99  const scroller = anim.timeline.source;
    100  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    101 
    102  // Wait for new animation frame which allows the timeline to compute new
    103  // current time.
    104  await waitForNextFrame();
    105  anim.playbackRate = -1;
    106  anim.play(); // Make sure animation is not initially finished
    107 
    108  await anim.ready;
    109 
    110  // Seek to 1ms before 0 and then wait 1ms
    111  anim.currentTime = CSS.percent(1);
    112  scroller.scrollTop = 0.2 * maxScroll;
    113  await waitForNextFrame();
    114 
    115  assert_percents_equal(anim.currentTime, 0,
    116      'Hold time is set to zero clamping current time');
    117 }, 'Updating the finished state when playing in reverse past zero');
    118 
    119 // Did seek = true
    120 promise_test(async t => {
    121  const anim = createScrollLinkedAnimation(t);
    122  const scroller = anim.timeline.source;
    123  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    124 
    125  // Wait for new animation frame which allows the timeline to compute new
    126  // current time.
    127  await waitForNextFrame();
    128  anim.playbackRate = -1;
    129  anim.play();
    130 
    131  await anim.ready;
    132 
    133  anim.currentTime = CSS.percent(-100);
    134  scroller.scrollTop = 0.2 * maxScroll;
    135  await waitForNextFrame();
    136 
    137  assert_percents_equal(anim.currentTime, -100,
    138      'Hold time is set so current time should NOT change');
    139 }, 'Updating the finished state when seeking a reversed animation past zero');
    140 
    141 // Did seek = false
    142 promise_test(async t => {
    143  const anim = createScrollLinkedAnimation(t);
    144  const scroller = anim.timeline.source;
    145  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    146 
    147  // Wait for new animation frame which allows the timeline to compute new
    148  // current time.
    149  await waitForNextFrame();
    150  anim.playbackRate = -1;
    151  anim.play();
    152  await anim.ready;
    153 
    154  scroller.scrollTop = maxScroll;
    155  await waitForNextFrame();
    156 
    157  assert_percents_equal(anim.currentTime, 0,
    158      'Hold time is set to target end clamping current time');
    159 }, 'Updating the finished state when playing  a reversed animation exactly ' +
    160   'to zero');
    161 
    162 // Did seek = true
    163 promise_test(async t => {
    164  const anim = createScrollLinkedAnimation(t);
    165  const scroller = anim.timeline.source;
    166  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    167 
    168  // Wait for new animation frame which allows the timeline to compute new
    169  // current time.
    170  await waitForNextFrame();
    171  anim.playbackRate = -1;
    172  anim.play();
    173  await anim.ready;
    174 
    175  anim.currentTime = CSS.percent(0);
    176 
    177  scroller.scrollTop = 0.2 * maxScroll;
    178  await waitForNextFrame();
    179 
    180  assert_percents_equal(anim.currentTime, 0,
    181      'Hold time is set so current time should NOT change');
    182 }, 'Updating the finished state when seeking a reversed animation exactly ' +
    183   'to zero');
    184 
    185 // CASE 3: playback rate > 0 and current time < target end OR
    186 //         playback rate < 0 and current time > 0
    187 // (Also the start time is resolved and there is pending task)
    188 
    189 // Did seek = true; playback rate > 0
    190 promise_test(async t => {
    191  const anim = createScrollLinkedAnimation(t);
    192  const scroller = anim.timeline.source;
    193  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    194 
    195  // Wait for new animation frame which allows the timeline to compute new
    196  // current time.
    197  await waitForNextFrame();
    198  anim.play();
    199  anim.finish();
    200  await anim.ready;
    201  assert_percents_equal(anim.startTime, -100);
    202 
    203  anim.currentTime = CSS.percent(50);
    204  // When did seek = true, updating the finished state: (i) updates
    205  // the animation's start time and (ii) clears the hold time.
    206  // We can test both by checking that the currentTime is initially
    207  // updated and then increases.
    208  assert_percents_equal(anim.currentTime, 50, 'Start time is updated');
    209  assert_percents_equal(anim.startTime, -50);
    210 
    211  scroller.scrollTop = 0.2 * maxScroll;
    212  await waitForNextFrame();
    213 
    214  assert_percents_equal(anim.currentTime, 70,
    215      'Hold time is not set so current time should increase');
    216 }, 'Updating the finished state when seeking before end');
    217 
    218 // Did seek = false; playback rate < 0
    219 //
    220 // Unfortunately it is not possible to test this case. We need to have
    221 // a hold time set, a resolved start time, and then perform some
    222 // operation that updates the finished state with did seek set to true.
    223 //
    224 // However, the only situation where this could arrive is when we
    225 // replace the timeline and that procedure is likely to change. For all
    226 // other cases we either have an unresolved start time (e.g. when
    227 // paused), we don't have a set hold time (e.g. regular playback), or
    228 // the current time is zero (and anything that gets us out of that state
    229 // will set did seek = true).
    230 
    231 // Did seek = true; playback rate < 0
    232 promise_test(async t => {
    233  const anim = createScrollLinkedAnimation(t);
    234  const scroller = anim.timeline.source;
    235  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    236 
    237  // Wait for new animation frame which allows the timeline to compute new
    238  // current time.
    239  await waitForNextFrame();
    240  anim.play();
    241  anim.playbackRate = -1;
    242  await anim.ready;
    243 
    244  anim.currentTime = CSS.percent(50);
    245  assert_percents_equal(anim.startTime, 50, 'Start time is updated');
    246  assert_percents_equal(anim.currentTime, 50, 'Current time is updated');
    247 
    248  scroller.scrollTop = 0.2 * maxScroll;
    249  await waitForNextFrame();
    250 
    251  assert_percents_equal(anim.currentTime, 30,
    252      'Hold time is not set so current time should decrease');
    253 }, 'Updating the finished state when seeking a reversed animation before end');
    254 
    255 
    256 // CASE 4: playback rate == 0
    257 
    258 // current time < 0
    259 promise_test(async t => {
    260  const anim = createScrollLinkedAnimation(t);
    261  const scroller = anim.timeline.source;
    262  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    263 
    264  // Wait for new animation frame which allows the timeline to compute new
    265  // current time.
    266  await waitForNextFrame();
    267  anim.play();
    268  anim.playbackRate = 0;
    269  await anim.ready;
    270 
    271  anim.currentTime = CSS.percent(-100);
    272 
    273  scroller.scrollTop = 0.2 * maxScroll;
    274  await waitForNextFrame();
    275 
    276  assert_percents_equal(anim.currentTime, -100,
    277      'Hold time should not be cleared so current time should NOT change');
    278 }, 'Updating the finished state when playback rate is zero and the current ' +
    279   'time is less than zero');
    280 
    281 // current time < target end
    282 promise_test(async t => {
    283  const anim = createScrollLinkedAnimation(t);
    284  const scroller = anim.timeline.source;
    285  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    286 
    287  // Wait for new animation frame which allows the timeline to compute new
    288  // current time.
    289  await waitForNextFrame();
    290  anim.play();
    291 
    292  anim.playbackRate = 0;
    293  await anim.ready;
    294 
    295  anim.currentTime = CSS.percent(50);
    296  scroller.scrollTop = 0.2 * maxScroll;
    297  await waitForNextFrame();
    298 
    299  assert_percents_equal(anim.currentTime, 50,
    300      'Hold time should not be cleared so current time should NOT change');
    301 }, 'Updating the finished state when playback rate is zero and the current ' +
    302   'time is less than end');
    303 
    304 // current time > target end
    305 promise_test(async t => {
    306  const anim = createScrollLinkedAnimation(t);
    307  const scroller = anim.timeline.source;
    308  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    309 
    310  // Wait for new animation frame which allows the timeline to compute new
    311  // current time.
    312  await waitForNextFrame();
    313  anim.play();
    314  anim.playbackRate = 0;
    315  await anim.ready;
    316 
    317  anim.currentTime = CSS.percent(200);
    318  scroller.scrollTop = 0.2 * maxScroll;
    319  await waitForNextFrame();
    320 
    321  assert_percents_equal(anim.currentTime, 200,
    322      'Hold time should not be cleared so current time should NOT change');
    323 }, 'Updating the finished state when playback rate is zero and the current' +
    324   'time is greater than end');
    325 
    326 // CASE 5: current time unresolved
    327 
    328 promise_test(async t => {
    329  const anim = createScrollLinkedAnimation(t);
    330  // Wait for new animation frame which allows the timeline to compute new
    331  // current time.
    332  await waitForNextFrame();
    333  anim.play();
    334  anim.cancel();
    335  // Trigger a change that will cause the "update the finished state"
    336  // procedure to run.
    337  anim.effect.updateTiming({ duration: 2000 });
    338  assert_equals(anim.currentTime, null,
    339      'The animation hold time / start time should not be updated');
    340  // The "update the finished state" procedure is supposed to run after any
    341  // change to timing, but just in case an implementation defers that, let's
    342  // wait a frame and check that the hold time / start time has still not been
    343  // updated.
    344  await waitForAnimationFrames(1);
    345 
    346  assert_equals(anim.currentTime, null,
    347      'The animation hold time / start time should not be updated');
    348 }, 'Updating the finished state when current time is unresolved');
    349 
    350 // CASE 7: start time unresolved
    351 
    352 // Did seek = true
    353 promise_test(async t => {
    354  const anim = createScrollLinkedAnimation(t);
    355  // Wait for new animation frame which allows the timeline to compute new
    356  // current time.
    357  await waitForNextFrame();
    358  anim.cancel();
    359  anim.currentTime = CSS.percent(150);
    360  // Trigger a change that will cause the "update the finished state"
    361  // procedure to run.
    362  anim.currentTime = CSS.percent(50);
    363  assert_percents_equal(anim.currentTime, 50,
    364      'The animation hold time should not be updated');
    365  assert_equals(anim.startTime, null,
    366      'The animation start time should not be updated');
    367 }, 'Updating the finished state when start time is unresolved and did seek = ' +
    368   'true');
    369 
    370 // --------------------------------------------------------------------
    371 //
    372 // TESTS FOR RUNNING FINISH NOTIFICATION STEPS
    373 //
    374 // --------------------------------------------------------------------
    375 
    376 function waitForFinishEventAndPromise(animation) {
    377  const eventPromise = new Promise(resolve => {
    378    animation.onfinish = resolve;
    379  });
    380  return Promise.all([eventPromise, animation.finished]);
    381 }
    382 
    383 promise_test(t => {
    384  const animation = createScrollLinkedAnimation(t);
    385  const scroller = animation.timeline.source;
    386  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    387  animation.play();
    388  animation.onfinish =
    389      t.unreached_func('Seeking to finish should not fire finish event');
    390  animation.finished.then(
    391      t.unreached_func(
    392          'Seeking to finish should not resolve finished promise'));
    393  animation.currentTime = CSS.percent(100);
    394  animation.currentTime = CSS.percent(0);
    395  animation.pause();
    396  scroller.scrollTop = 0.2 * maxScroll;
    397  return waitForAnimationFrames(3);
    398 }, 'Finish notification steps don\'t run when the animation seeks to finish ' +
    399   'and then seeks back again');
    400 
    401 promise_test(async t => {
    402  const animation = createScrollLinkedAnimation(t);
    403  const scroller = animation.timeline.source;
    404  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    405  animation.play();
    406  await animation.ready;
    407  scroller.scrollTop = maxScroll;
    408 
    409  return waitForFinishEventAndPromise(animation);
    410 }, 'Finish notification steps run when the animation completes normally');
    411 
    412 promise_test(async t => {
    413  const animation = createScrollLinkedAnimation(t);
    414  const scroller = animation.timeline.source;
    415  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    416  animation.effect.target = null;
    417 
    418  animation.play();
    419  await animation.ready;
    420  scroller.scrollTop = maxScroll;
    421  return waitForFinishEventAndPromise(animation);
    422 }, 'Finish notification steps run when an animation without a target effect ' +
    423   'completes normally');
    424 
    425 promise_test(async t => {
    426  const animation = createScrollLinkedAnimation(t);
    427  animation.play();
    428  await animation.ready;
    429 
    430  animation.currentTime = CSS.percent(101);
    431  return waitForFinishEventAndPromise(animation);
    432 }, 'Finish notification steps run when the animation seeks past finish');
    433 
    434 promise_test(async t => {
    435  const animation = createScrollLinkedAnimation(t);
    436  animation.play();
    437  await animation.ready;
    438 
    439  // Register for notifications now since once we seek away from being
    440  // finished the 'finished' promise will be replaced.
    441  const finishNotificationSteps = waitForFinishEventAndPromise(animation);
    442  animation.finish();
    443  animation.currentTime = CSS.percent(0);
    444  animation.pause();
    445  return finishNotificationSteps;
    446 }, 'Finish notification steps run when the animation completes with ' +
    447   '.finish(), even if we then seek away');
    448 
    449 promise_test(async t => {
    450  const animation = createScrollLinkedAnimation(t);
    451  const scroller = animation.timeline.source;
    452  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    453 
    454  animation.play();
    455  scroller.scrollTop = maxScroll;
    456  const initialFinishedPromise = animation.finished;
    457  await animation.finished;
    458 
    459  animation.currentTime = CSS.percent(0);
    460  assert_not_equals(initialFinishedPromise, animation.finished);
    461 }, 'Animation finished promise is replaced after seeking back to start');
    462 
    463 promise_test(async t => {
    464  const animation = createScrollLinkedAnimation(t);
    465  const scroller = animation.timeline.source;
    466  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    467 
    468  animation.play();
    469 
    470  const initialFinishedPromise = animation.finished;
    471  scroller.scrollTop = maxScroll;
    472  await animation.finished;
    473 
    474  scroller.scrollTop = 0;
    475  await waitForNextFrame();
    476 
    477  animation.play();
    478  assert_not_equals(initialFinishedPromise, animation.finished);
    479 }, 'Animation finished promise is replaced after replaying from start');
    480 
    481 async_test(t => {
    482  const animation = createScrollLinkedAnimation(t);
    483  const scroller = animation.timeline.source;
    484  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    485 
    486  animation.play();
    487 
    488  animation.onfinish = event => {
    489    scroller.scrollTop = 0;
    490    window.requestAnimationFrame(function() {
    491      window.requestAnimationFrame(function() {
    492        scroller.scrollTop = maxScroll;
    493      });
    494    });
    495    animation.onfinish = event => {
    496      t.done();
    497    };
    498  };
    499  scroller.scrollTop = maxScroll;
    500 }, 'Animation finish event is fired again after seeking back to start');
    501 
    502 async_test(t => {
    503  const animation = createScrollLinkedAnimation(t);
    504  const scroller = animation.timeline.source;
    505  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    506 
    507  animation.play();
    508 
    509  animation.onfinish = event => {
    510    scroller.scrollTop = 0;
    511    window.requestAnimationFrame(function() {
    512      animation.play();
    513      scroller.scrollTop = maxScroll;
    514      animation.onfinish = event => {
    515        t.done();
    516      };
    517    });
    518  };
    519  scroller.scrollTop = maxScroll;
    520 }, 'Animation finish event is fired again after replaying from start');
    521 
    522 async_test(t => {
    523  const anim = createScrollLinkedAnimation(t);
    524  const scroller = anim.timeline.source;
    525  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    526 
    527  anim.effect.updateTiming({ duration: 800,  endDelay: 200});
    528 
    529  anim.onfinish = t.step_func(event => {
    530    assert_unreached('finish event should not be fired');
    531  });
    532  anim.play();
    533  anim.ready.then(() => {
    534    scroller.scrollTop = 0.9 * maxScroll;
    535    return waitForAnimationFrames(3);
    536  }).then(t.step_func(() => {
    537    t.done();
    538  }));
    539 }, 'finish event is not fired at the end of the active interval when the ' +
    540   'endDelay has not expired');
    541 
    542 async_test(t => {
    543  const anim = createScrollLinkedAnimation(t);
    544  const scroller = anim.timeline.source;
    545  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    546 
    547  anim.effect.updateTiming({ duration: 800,  endDelay: 100});
    548  anim.play();
    549  anim.ready.then(() => {
    550    scroller.scrollTop = 0.95 * maxScroll; // during endDelay
    551    anim.onfinish = t.step_func(event => {
    552      assert_unreached('onfinish event should not be fired during endDelay');
    553    });
    554    return waitForAnimationFrames(2);
    555  }).then(t.step_func(() => {
    556    anim.onfinish = t.step_func(event => {
    557      t.done();
    558    });
    559    scroller.scrollTop = maxScroll;
    560    return waitForAnimationFrames(2);
    561  }));
    562 }, 'finish event is fired after the endDelay has expired');
    563 
    564 </script>
    565 </body>