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>