tor-browser

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

event-dispatch.tentative.html (18280B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <title>Tests for CSS animation event dispatch</title>
      4 <meta name="timeout" content="long">
      5 <link rel="help" href="https://drafts.csswg.org/css-animations-2/#event-dispatch"/>
      6 <script src="/resources/testharness.js"></script>
      7 <script src="/resources/testharnessreport.js"></script>
      8 <script src="support/testcommon.js"></script>
      9 <style>
     10 @keyframes anim {
     11  from { margin-left: 0px; }
     12  to { margin-left: 100px; }
     13 }
     14 @keyframes anim2 {
     15  from { margin-top: 100px; }
     16  to { margin-top: 200px; }
     17 }
     18 @keyframes anim3 {
     19  from { padding-left: 100px; }
     20  to { padding-left: 200px; }
     21 }
     22 </style>
     23 <div id="log"></div>
     24 <script>
     25 'use strict';
     26 
     27 const setupAnimation = (t, animationStyle) => {
     28  const div = addDiv(t, { style: 'animation: ' + animationStyle });
     29  const animation = div.getAnimations()[0];
     30  const timeoutPromise = armTimeoutWhenReady(animation, fastEventsTimeout);
     31 
     32  const watcher = new EventWatcher(t, div, [ 'animationstart',
     33                                             'animationiteration',
     34                                             'animationend',
     35                                             'animationcancel' ],
     36                                   timeoutPromise);
     37 
     38  return { animation, watcher, div };
     39 };
     40 
     41 promise_test(async t => {
     42  // Add 1ms delay to ensure that the delay is not included in the elapsedTime.
     43  const { animation, watcher } = setupAnimation(t, 'anim 100s 1ms');
     44 
     45  const evt = await watcher.wait_for('animationstart');
     46  assert_equals(evt.elapsedTime, 0.0);
     47 }, 'Idle -> Active');
     48 
     49 promise_test(async t => {
     50  const { animation, watcher } = setupAnimation(t, 'anim 100s');
     51 
     52  // Seek to After phase.
     53  animation.finish();
     54 
     55  const events = await watcher.wait_for(['animationstart', 'animationend'], {
     56    record: 'all',
     57  });
     58  assert_equals(events[0].elapsedTime, 0.0);
     59  assert_equals(events[1].elapsedTime, 100);
     60 }, 'Idle -> After');
     61 
     62 promise_test(async t => {
     63  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused');
     64 
     65  await animation.ready;
     66 
     67  // Seek to Active phase.
     68  animation.currentTime = 100 * MS_PER_SEC;
     69 
     70  const evt = await watcher.wait_for('animationstart');
     71  assert_equals(evt.elapsedTime, 0.0);
     72 }, 'Before -> Active');
     73 
     74 promise_test(async t => {
     75  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused');
     76  const allEvents = watcher.wait_for(['animationstart', 'animationend'], {
     77    record: 'all',
     78  });
     79 
     80  await animation.ready;
     81 
     82  // Seek to After phase.
     83  animation.finish();
     84 
     85  const events = await allEvents;
     86  assert_equals(events[0].elapsedTime, 0.0);
     87  assert_equals(events[1].elapsedTime, 100.0);
     88 }, 'Before -> After');
     89 
     90 promise_test(async t => {
     91  const { animation, watcher, div } = setupAnimation(t, 'anim 100s paused');
     92 
     93  await watcher.wait_for('animationstart');
     94 
     95  // Make idle
     96  div.style.display = 'none';
     97 
     98  const evt = await watcher.wait_for('animationcancel');
     99  assert_equals(evt.elapsedTime, 0.0);
    100 }, 'Active -> Idle, display: none');
    101 
    102 promise_test(async t => {
    103  const { animation, watcher, div } = setupAnimation(t, 'anim 100s paused');
    104 
    105  // Seek to After phase.
    106  animation.finish();
    107  await watcher.wait_for(['animationstart', 'animationend']);
    108 
    109  div.style.display = 'none';
    110 
    111  // Wait a couple of frames and check that no event was dispatched.
    112  await waitForAnimationFrames(2);
    113 }, 'After -> Idle, display: none');
    114 
    115 promise_test(async t => {
    116  const { animation, watcher } = setupAnimation(t, 'anim 100s');
    117 
    118  await watcher.wait_for('animationstart');
    119 
    120  animation.currentTime = 100.0;
    121 
    122  // Make idle
    123  animation.timeline = null;
    124 
    125  const evt = await watcher.wait_for('animationcancel');
    126  assert_time_equals_literal(evt.elapsedTime, 0.1);
    127 }, 'Active -> Idle, setting Animation.timeline = null');
    128 
    129 promise_test(async t => {
    130  // We should NOT pause animation since calling cancel synchronously.
    131  const { animation, watcher } = setupAnimation(t, 'anim 100s');
    132 
    133  await watcher.wait_for('animationstart');
    134 
    135  animation.currentTime = 50.0;
    136  animation.cancel();
    137 
    138  const evt = await watcher.wait_for('animationcancel');
    139  assert_time_equals_literal(evt.elapsedTime, 0.05);
    140 }, 'Active -> Idle, calling Animation.cancel()');
    141 
    142 promise_test(async t => {
    143  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused');
    144 
    145  // Seek to Active phase.
    146  animation.currentTime = 100 * MS_PER_SEC;
    147  await watcher.wait_for('animationstart');
    148 
    149  // Seek to Before phase.
    150  animation.currentTime = 0;
    151 
    152  const evt = await watcher.wait_for('animationend');
    153  assert_equals(evt.elapsedTime, 0.0);
    154 }, 'Active -> Before');
    155 
    156 promise_test(async t => {
    157  const { animation, watcher } = setupAnimation(t, 'anim 100s paused');
    158 
    159  await watcher.wait_for('animationstart');
    160 
    161  // Seek to After phase.
    162  animation.finish();
    163 
    164  const evt = await watcher.wait_for('animationend');
    165  assert_equals(evt.elapsedTime, 100.0);
    166 }, 'Active -> After');
    167 
    168 promise_test(async t => {
    169  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused');
    170 
    171  // Seek to After phase.
    172  animation.finish();
    173  await watcher.wait_for([ 'animationstart', 'animationend' ]);
    174 
    175  // Seek to Before phase.
    176  animation.currentTime = 0;
    177 
    178  const events = await watcher.wait_for(['animationstart', 'animationend'], {
    179    record: 'all',
    180  });
    181  assert_equals(events[0].elapsedTime, 100.0);
    182  assert_equals(events[1].elapsedTime, 0.0);
    183 }, 'After -> Before');
    184 
    185 promise_test(async t => {
    186  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused');
    187 
    188  // Seek to After phase.
    189  animation.finish();
    190  await watcher.wait_for([ 'animationstart', 'animationend' ]);
    191 
    192  // Seek to Active phase.
    193  animation.currentTime = 100 * MS_PER_SEC;
    194 
    195  const evt = await watcher.wait_for('animationstart');
    196  assert_equals(evt.elapsedTime, 100.0);
    197 }, 'After -> Active');
    198 
    199 promise_test(async t => {
    200  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s 3 paused');
    201 
    202  await animation.ready;
    203 
    204  // Seek to iteration 0 (no animationiteration event should be dispatched)
    205  animation.currentTime = 100 * MS_PER_SEC;
    206  await watcher.wait_for('animationstart');
    207 
    208  // Seek to iteration 2
    209  animation.currentTime = 300 * MS_PER_SEC;
    210  let evt = await watcher.wait_for('animationiteration');
    211  assert_equals(evt.elapsedTime, 200);
    212 
    213  // Seek to After phase (no animationiteration event should be dispatched)
    214  animation.currentTime = 400 * MS_PER_SEC;
    215  evt = await watcher.wait_for('animationend');
    216  assert_equals(evt.elapsedTime, 300);
    217 }, 'Active -> Active (forwards)');
    218 
    219 promise_test(async t => {
    220  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s 3');
    221 
    222  // Seek to After phase.
    223  animation.finish();
    224  await watcher.wait_for([ 'animationstart', 'animationend' ]);
    225 
    226  // Seek to iteration 2 (no animationiteration event should be dispatched)
    227  animation.pause();
    228  animation.currentTime = 300 * MS_PER_SEC;
    229  await watcher.wait_for('animationstart');
    230 
    231  // Seek to mid of iteration 0 phase.
    232  animation.currentTime = 200 * MS_PER_SEC;
    233 
    234  const evt = await watcher.wait_for('animationiteration');
    235  assert_equals(evt.elapsedTime, 200.0);
    236 
    237  // Seek to before phase (no animationiteration event should be dispatched)
    238  animation.currentTime = 0;
    239  await watcher.wait_for('animationend');
    240 }, 'Active -> Active (backwards)');
    241 
    242 promise_test(async t => {
    243  const { animation, watcher, div } = setupAnimation(t, 'anim 100s paused');
    244 
    245  await watcher.wait_for('animationstart');
    246 
    247  // Seek to Idle phase.
    248  div.style.display = 'none';
    249  flushComputedStyle(div);
    250 
    251  await watcher.wait_for('animationcancel');
    252 
    253  // Restart this animation.
    254  div.style.display = '';
    255  await watcher.wait_for('animationstart');
    256 }, 'Active -> Idle -> Active: animationstart is fired by restarting animation');
    257 
    258 promise_test(async t => {
    259  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s 2 paused');
    260 
    261  // Make After.
    262  animation.finish();
    263 
    264  await watcher.wait_for([ 'animationstart', 'animationend' ]);
    265  animation.playbackRate = -1;
    266 
    267  let evt = await watcher.wait_for('animationstart');
    268  assert_equals(evt.elapsedTime, 200);
    269 
    270  // Seek to 1st iteration
    271  animation.currentTime = 200 * MS_PER_SEC - 1;
    272 
    273  evt = await watcher.wait_for('animationiteration');
    274  assert_equals(evt.elapsedTime, 100);
    275 
    276  // Seek to before
    277  animation.currentTime = 100 * MS_PER_SEC - 1;
    278 
    279  evt = await watcher.wait_for('animationend');
    280  assert_equals(evt.elapsedTime, 0);
    281  assert_equals(animation.playState, 'running'); // delay
    282 }, 'Negative playbackRate sanity test(Before -> Active -> Before)');
    283 
    284 promise_test(async t => {
    285  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
    286 
    287  animation.currentTime = 150 * MS_PER_SEC;
    288  animation.currentTime = 50 * MS_PER_SEC;
    289 
    290  // Then wait a couple of frames and check that no event was dispatched.
    291  await waitForAnimationFrames(2);
    292 }, 'Redundant change, before -> active, then back');
    293 
    294 promise_test(async t => {
    295  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
    296 
    297  animation.currentTime = 250 * MS_PER_SEC;
    298  animation.currentTime = 50 * MS_PER_SEC;
    299 
    300  // Then wait a couple of frames and check that no event was dispatched.
    301  await waitForAnimationFrames(2);
    302 }, 'Redundant change, before -> after, then back');
    303 
    304 promise_test(async t => {
    305  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
    306 
    307  // Get us into the initial state:
    308  animation.currentTime = 150 * MS_PER_SEC;
    309 
    310  await watcher.wait_for('animationstart');
    311 
    312  animation.currentTime = 50 * MS_PER_SEC;
    313  animation.currentTime = 150 * MS_PER_SEC;
    314 
    315  // Then wait a couple of frames and check that no event was dispatched.
    316  await waitForAnimationFrames(2);
    317 }, 'Redundant change, active -> before, then back');
    318 
    319 promise_test(async t => {
    320  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
    321 
    322  // Get us into the initial state:
    323  animation.currentTime = 150 * MS_PER_SEC;
    324 
    325  await watcher.wait_for('animationstart');
    326 
    327  animation.currentTime = 250 * MS_PER_SEC;
    328  animation.currentTime = 150 * MS_PER_SEC;
    329 
    330  // Then wait a couple of frames and check that no event was dispatched.
    331  await waitForAnimationFrames(2);
    332 }, 'Redundant change, active -> after, then back');
    333 
    334 promise_test(async t => {
    335  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
    336 
    337  // Get us into the initial state:
    338  animation.currentTime = 250 * MS_PER_SEC;
    339 
    340  await watcher.wait_for(['animationstart', 'animationend']);
    341 
    342  animation.currentTime = 50 * MS_PER_SEC;
    343  animation.currentTime = 250 * MS_PER_SEC;
    344 
    345  // Then wait a couple of frames and check that no event was dispatched.
    346  await waitForAnimationFrames(2);
    347 }, 'Redundant change, after -> before, then back');
    348 
    349 promise_test(async t => {
    350  const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
    351 
    352  // Get us into the initial state:
    353  animation.currentTime = 250 * MS_PER_SEC;
    354 
    355  await watcher.wait_for(['animationstart', 'animationend']);
    356 
    357  animation.currentTime = 150 * MS_PER_SEC;
    358  animation.currentTime = 250 * MS_PER_SEC;
    359 
    360  // Then wait a couple of frames and check that no event was dispatched.
    361  await waitForAnimationFrames(2);
    362 }, 'Redundant change, after -> active, then back');
    363 
    364 promise_test(async t => {
    365  const { animation, watcher } = setupAnimation(t, 'anim 100s');
    366 
    367  await watcher.wait_for('animationstart');
    368 
    369  // Make idle
    370  animation.cancel();
    371  await watcher.wait_for('animationcancel');
    372 
    373  animation.cancel();
    374  // Then wait a couple of frames and check that no event was dispatched.
    375  await waitForAnimationFrames(2);
    376 }, 'Call Animation.cancel after canceling animation.');
    377 
    378 promise_test(async t => {
    379  const { animation, watcher } = setupAnimation(t, 'anim 100s');
    380 
    381  await watcher.wait_for('animationstart');
    382 
    383  // Make idle
    384  animation.cancel();
    385  animation.play();
    386  await watcher.wait_for([ 'animationcancel', 'animationstart' ]);
    387 }, 'Restart animation after canceling animation immediately.');
    388 
    389 promise_test(async t => {
    390  const { animation, watcher } = setupAnimation(t, 'anim 100s');
    391 
    392  await watcher.wait_for('animationstart');
    393 
    394  // Make idle
    395  animation.cancel();
    396  animation.play();
    397  animation.cancel();
    398  await watcher.wait_for('animationcancel');
    399 
    400  // Then wait a couple of frames and check that no event was dispatched.
    401  await waitForAnimationFrames(2);
    402 }, 'Call Animation.cancel after restarting animation immediately.');
    403 
    404 promise_test(async t => {
    405  const { animation, watcher } = setupAnimation(t, 'anim 100s');
    406 
    407  await watcher.wait_for('animationstart');
    408 
    409  // Make idle
    410  animation.timeline = null;
    411  await watcher.wait_for('animationcancel');
    412 
    413  animation.timeline = document.timeline;
    414  animation.play();
    415  await watcher.wait_for('animationstart');
    416 }, 'Set timeline and play transition after clearing the timeline.');
    417 
    418 promise_test(async t => {
    419  const { animation, watcher } = setupAnimation(t, 'anim 100s');
    420 
    421  await watcher.wait_for('animationstart');
    422 
    423  // Make idle
    424  animation.cancel();
    425  await watcher.wait_for('animationcancel');
    426 
    427  animation.effect = null;
    428  // Then wait a couple of frames and check that no event was dispatched.
    429  await waitForAnimationFrames(2);
    430 }, 'Set null target effect after canceling the animation.');
    431 
    432 promise_test(async t => {
    433  const { animation, watcher } = setupAnimation(t, 'anim 100s');
    434 
    435  await watcher.wait_for('animationstart');
    436 
    437  animation.effect = null;
    438  await watcher.wait_for('animationend');
    439 
    440  animation.cancel();
    441  // Then wait a couple of frames and check that no event was dispatched.
    442  await waitForAnimationFrames(2);
    443 }, 'Cancel the animation after clearing the target effect.');
    444 
    445 promise_test(async t => {
    446  const { animation, watcher, div } = setupAnimation(t, 'anim 100s');
    447 
    448  await watcher.wait_for('animationstart');
    449 
    450  // Replace the running animation.
    451  div.style.animation = 'anim2 100s';
    452 
    453  // animationcancel event should be fired before animationstart because we
    454  // expect to cancel the running animation first.
    455  const events = await watcher.wait_for(
    456    ['animationcancel', 'animationstart'],
    457    { record: 'all' }
    458  );
    459  assert_equals(events[0].animationName, 'anim');
    460  assert_equals(events[1].animationName, 'anim2');
    461 
    462  // Then wait a couple of frames and check that no event was dispatched.
    463  await waitForAnimationFrames(2);
    464 }, 'Replacing a running animation should get animationcancel earlier than ' +
    465   'animationstart');
    466 
    467 promise_test(async t => {
    468  const div = addDiv(t, { style: 'animation: anim 100s, anim2 100s' });
    469  const animations = div.getAnimations();
    470  const watcher = new EventWatcher(t, div, [ 'animationstart',
    471                                             'animationcancel' ],
    472    () => {
    473      return Promise.all([animations[0].ready, animations[1].ready]).then(() => {
    474        return fastEventsTimeout();
    475      });
    476    }
    477  );
    478 
    479  await watcher.wait_for(['animationstart', 'animationstart']);
    480 
    481  // Replace the first animation
    482  div.style.animation = 'anim3 100s, anim2 100s';
    483 
    484  // animationcancel event should be fired before animationstart because we
    485  // expect to cancel the running animation first.
    486  const events = await watcher.wait_for(
    487    ['animationcancel', 'animationstart'],
    488    { record: 'all' }
    489  );
    490  assert_equals(events[0].animationName, 'anim');
    491  assert_equals(events[1].animationName, 'anim3');
    492 
    493  // Then wait a couple of frames and check that no event was dispatched.
    494  await waitForAnimationFrames(2);
    495 }, 'The cancel event should be fired before the new start event if both have ' +
    496   'the same position in the animation list');
    497 
    498 promise_test(async t => {
    499  const div =
    500    addDiv(t, { style: 'animation: anim 100s, anim2 100s, anim3 100s' });
    501  const animations = div.getAnimations();
    502  const watcher = new EventWatcher(t, div, [ 'animationstart',
    503                                             'animationcancel' ],
    504    () => {
    505      return Promise.all([
    506        animations[0].ready,
    507        animations[1].ready,
    508        animations[2].ready
    509      ]).then(() => {
    510        return fastEventsTimeout();
    511      });
    512    }
    513  );
    514 
    515  await watcher.wait_for(
    516    ['animationstart', 'animationstart', 'animationstart']
    517  );
    518 
    519  // Cancel the first and the second animations.
    520  div.style.animation = 'anim3 100s';
    521 
    522  const events = await watcher.wait_for(
    523    ['animationcancel', 'animationcancel'],
    524    { record: 'all' }
    525  );
    526  // Per https://drafts.csswg.org/css-animations-2/#animation-composite-order,
    527  // the cancel event with the earlier position should be fired earlier.
    528  assert_equals(events[0].animationName, 'anim');
    529  assert_equals(events[1].animationName, 'anim2');
    530 
    531  // Then wait a couple of frames and check that no event was dispatched.
    532  await waitForAnimationFrames(2);
    533 }, 'The cancel event with an earlier position in animation list should be ' +
    534   'fired earlier');
    535 
    536 promise_test(async t => {
    537  const div =
    538    addDiv(t, { style: 'animation: anim 100s, anim2 100s, anim3 100s' });
    539  const animations = div.getAnimations();
    540  const watcher = new EventWatcher(t, div, [ 'animationstart',
    541                                             'animationcancel' ],
    542    () => {
    543      return Promise.all([
    544        animations[0].ready,
    545        animations[1].ready,
    546        animations[2].ready
    547      ]).then(() => {
    548        return fastEventsTimeout();
    549      });
    550    }
    551  );
    552 
    553  await watcher.wait_for(
    554    ['animationstart', 'animationstart', 'animationstart']
    555  );
    556 
    557  // Change the order, i.e. swap the order of |anim| and |anim2|.
    558  div.style.animation = 'anim2 100s, anim 100s, anim3 100s';
    559  getComputedStyle(div).animation;
    560 
    561  // Then we cancel the first and the second animations.
    562  div.style.animation = 'anim3 100s';
    563 
    564  const events = await watcher.wait_for(
    565    ['animationcancel', 'animationcancel'],
    566    { record: 'all' }
    567  );
    568  // Per https://drafts.csswg.org/css-animations-2/#animation-composite-order,
    569  // the cancel event to |anim2| should be dispatched before the cancel event to
    570  // |anim| since |anim2| appeared before |anim| in the animation-name at the
    571  // point when they were cancelled.
    572  assert_equals(events[0].animationName, 'anim2');
    573  assert_equals(events[1].animationName, 'anim');
    574 
    575  // Then wait a couple of frames and check that no event was dispatched.
    576  await waitForAnimationFrames(2);
    577 }, 'The order of the cancel events follows the relative positions in the ' +
    578   'animation list at the point when they were cancelled');
    579 
    580 </script>