KeyframeEffect-getKeyframes.tentative.html (31047B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <title>KeyframeEffect.getKeyframes() for CSS animations</title> 4 <!-- TODO: Add a more specific link for this once it is specified. --> 5 <link rel="help" href="https://drafts.csswg.org/css-animations-2/"> 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-empty { } 11 12 @keyframes anim-empty-frames { 13 from { } 14 to { } 15 } 16 17 @keyframes anim-only-timing { 18 from { animation-timing-function: linear; } 19 to { } 20 } 21 22 @keyframes anim-only-non-animatable { 23 from { animation-duration: 3s; } 24 to { animation-duration: 5s; } 25 } 26 27 @keyframes anim-simple { 28 from { color: rgb(0, 0, 0); } 29 to { color: rgb(255, 255, 255); } 30 } 31 32 @keyframes anim-simple-three { 33 from { color: rgb(0, 0, 0); } 34 50% { color: rgb(0, 0, 255); } 35 to { color: rgb(255, 255, 255); } 36 } 37 38 @keyframes anim-simple-timing { 39 from { color: rgb(0, 0, 0); animation-timing-function: linear; } 40 50% { color: rgb(0, 0, 255); animation-timing-function: ease-in-out; } 41 to { color: rgb(255, 255, 255); animation-timing-function: step-end; } 42 } 43 44 @keyframes anim-simple-timing-some { 45 from { color: rgb(0, 0, 0); animation-timing-function: linear; } 46 50% { color: rgb(0, 0, 255); } 47 to { color: rgb(255, 255, 255); } 48 } 49 50 @keyframes anim-simple-composite { 51 from { color: rgb(0, 0, 0); animation-composition: replace; } 52 50% { color: rgb(0, 0, 255); animation-composition: add; } 53 to { color: rgb(255, 255, 255); animation-composition: accumulate; } 54 } 55 56 @keyframes anim-simple-composite-some { 57 from { color: rgb(0, 0, 0); animation-composition: add; } 58 50% { color: rgb(0, 0, 255); } 59 to { color: rgb(255, 255, 255); } 60 } 61 62 @keyframes anim-simple-shorthand { 63 from { margin: 8px; } 64 to { margin: 16px; } 65 } 66 67 @keyframes anim-omit-to { 68 from { color: rgb(0, 0, 255); } 69 } 70 71 @keyframes anim-omit-from { 72 to { color: rgb(0, 0, 255); } 73 } 74 75 @keyframes anim-omit-from-to { 76 50% { color: rgb(0, 0, 255); } 77 } 78 79 @keyframes anim-partially-omit-to { 80 from { margin-top: 50px; 81 margin-bottom: 100px; } 82 to { margin-top: 150px !important; /* ignored */ 83 margin-bottom: 200px; } 84 } 85 86 @keyframes anim-different-props { 87 from { color: rgb(0, 0, 0); margin-top: 8px; } 88 25% { color: rgb(0, 0, 255); } 89 75% { margin-top: 12px; } 90 to { color: rgb(255, 255, 255); margin-top: 16px } 91 } 92 93 @keyframes anim-different-props-and-easing { 94 from { color: rgb(0, 0, 0); margin-top: 8px; animation-timing-function: linear; } 95 25% { color: rgb(0, 0, 255); animation-timing-function: step-end; } 96 75% { margin-top: 12px; animation-timing-function: ease-in; } 97 to { color: rgb(255, 255, 255); margin-top: 16px } 98 } 99 100 @keyframes anim-merge-offset { 101 from { color: rgb(0, 0, 0); } 102 to { color: rgb(255, 255, 255); } 103 from { margin-top: 8px; } 104 to { margin-top: 16px; } 105 } 106 107 @keyframes anim-merge-offset-and-easing { 108 from { color: rgb(0, 0, 0); animation-timing-function: step-end; } 109 to { color: rgb(255, 255, 255); } 110 from { margin-top: 8px; animation-timing-function: linear; } 111 to { margin-top: 16px; } 112 from { font-size: 16px; animation-timing-function: step-end; } 113 to { font-size: 32px; } 114 from { padding-left: 2px; animation-timing-function: linear; } 115 to { padding-left: 4px; } 116 } 117 118 @keyframes anim-no-merge-equiv-easing { 119 from { margin-top: 0px; animation-timing-function: steps(1, end); } 120 from { margin-right: 0px; animation-timing-function: step-end; } 121 from { margin-bottom: 0px; animation-timing-function: steps(1); } 122 50% { margin-top: 10px; animation-timing-function: step-end; } 123 50% { margin-right: 10px; animation-timing-function: step-end; } 124 50% { margin-bottom: 10px; animation-timing-function: step-end; } 125 to { margin-top: 20px; margin-right: 20px; margin-bottom: 20px; } 126 } 127 128 @keyframes anim-merge-offset-and-composite { 129 from { color: rgb(0, 0, 0); animation-composition: add; } 130 to { color: rgb(255, 255, 255); } 131 from { margin-top: 8px; animation-composition: accumulate; } 132 to { margin-top: 16px; } 133 from { font-size: 16px; animation-composition: add; } 134 to { font-size: 32px; } 135 from { padding-left: 2px; animation-composition: accumulate; } 136 to { padding-left: 4px; } 137 } 138 139 @keyframes anim-merge-offset-easing-and-composite { 140 from { color: rgb(0, 0, 0); animation-composition: add; } 141 to { color: rgb(255, 255, 255); } 142 from { margin-top: 8px; animation-composition: accumulate; } 143 to { margin-top: 16px; } 144 from { font-size: 16px; animation-composition: add; animation-timing-function: linear; } 145 to { font-size: 32px; } 146 from { padding-left: 2px; animation-composition: accumulate; } 147 to { padding-left: 4px; } 148 } 149 150 @keyframes anim-overriding { 151 from { padding-top: 50px } 152 50%, from { padding-top: 30px } /* wins: 0% */ 153 75%, 85%, 50% { padding-top: 20px } /* wins: 75%, 50% */ 154 100%, 85% { padding-top: 70px } /* wins: 100% */ 155 85.1% { padding-top: 60px } /* wins: 85.1% */ 156 85% { padding-top: 30px } /* wins: 85% */ 157 } 158 159 @keyframes anim-filter { 160 to { filter: blur(5px) sepia(60%) saturate(30%); } 161 } 162 163 @keyframes anim-filter-drop-shadow { 164 from { filter: drop-shadow(10px 10px 10px rgb(0, 255, 0)); } 165 to { filter: drop-shadow(50px 30px 10px rgb(255, 0, 0)); } 166 } 167 168 @keyframes anim-text-shadow { 169 to { text-shadow: none; } 170 } 171 172 @keyframes anim-background-size { 173 to { background-size: 50%, 6px, contain } 174 } 175 176 :root { 177 --var-100px: 100px; 178 --end-color: rgb(255, 0, 0); 179 } 180 @keyframes anim-variables { 181 to { transform: translate(var(--var-100px), 0) } 182 } 183 @keyframes anim-variables-shorthand { 184 to { margin: var(--var-100px) } 185 } 186 @keyframes anim-custom-property-in-keyframe { 187 to { --end-color: rgb(0, 255, 0); color: var(--end-color) } 188 } 189 @keyframes anim-only-custom-property-in-keyframe { 190 from { transform: translate(100px, 0) } 191 to { --not-used: 200px } 192 } 193 194 @keyframes anim-only-timing-function-for-from-and-to { 195 from, to { animation-timing-function: linear } 196 50% { left: 10px } 197 } 198 199 </style> 200 <body> 201 <div id="log"></div> 202 <script> 203 "use strict"; 204 205 const getKeyframes = elem => elem.getAnimations()[0].effect.getKeyframes(); 206 207 // animation-timing-function values to test with, where the value 208 // is exactly the same as its serialization, sorted by the order 209 // getKeyframes() will group frames with the same easing function 210 // together (by nsTimingFunction::Compare). 211 const kTimingFunctionValues = [ 212 "ease", 213 "linear", 214 "ease-in", 215 "ease-out", 216 "ease-in-out", 217 "steps(1, start)", 218 "steps(2, start)", 219 "steps(1)", 220 "steps(2)", 221 "cubic-bezier(0, 0, 1, 1)", 222 "cubic-bezier(0, 0.25, 0.75, 1)" 223 ]; 224 225 const kCompositeValues = [ 226 "replace", 227 "add", 228 "accumulate" 229 ]; 230 231 test(t => { 232 const div = addDiv(t); 233 234 div.style.animation = 'anim-empty 100s'; 235 assert_equals(getKeyframes(div).length, 0, 236 "number of frames with empty @keyframes"); 237 238 div.style.animation = 'anim-empty-frames 100s'; 239 assert_equals(getKeyframes(div).length, 0, 240 "number of frames when @keyframes has empty keyframes"); 241 242 div.style.animation = 'anim-only-timing 100s'; 243 assert_equals(getKeyframes(div).length, 0, 244 "number of frames when @keyframes only has keyframes with " + 245 "animation-timing-function"); 246 247 div.style.animation = 'anim-only-non-animatable 100s'; 248 assert_equals(getKeyframes(div).length, 0, 249 "number of frames when @keyframes only has frames with " + 250 "non-animatable properties"); 251 }, 'KeyframeEffect.getKeyframes() returns no frames for various kinds' 252 + ' of empty animations'); 253 254 test(t => { 255 const div = addDiv(t); 256 div.style.animation = 'anim-simple 100s'; 257 258 const frames = getKeyframes(div); 259 260 const expected = [ 261 { offset: 0, computedOffset: 0, easing: "ease", 262 color: "rgb(0, 0, 0)", composite: "auto" }, 263 { offset: 1, computedOffset: 1, easing: "ease", 264 color: "rgb(255, 255, 255)", composite: "auto" }, 265 ]; 266 assert_frame_lists_equal(frames, expected); 267 }, 'KeyframeEffect.getKeyframes() returns expected frames for a simple' 268 + ' animation'); 269 270 test(t => { 271 for (const easing of kTimingFunctionValues) { 272 const div = addDiv(t); 273 274 div.style.animation = 'anim-simple-three 100s ' + easing; 275 const frames = getKeyframes(div); 276 277 assert_equals(frames.length, 3, "number of frames"); 278 279 for (let i = 0; i < frames.length; i++) { 280 assert_equals(frames[i].easing, easing, 281 "value for 'easing' on ComputedKeyframe #" + i); 282 } 283 } 284 }, 'KeyframeEffect.getKeyframes() returns frames with expected easing' 285 + ' values, when the easing comes from animation-timing-function on the' 286 + ' element'); 287 288 test(t => { 289 const div = addDiv(t); 290 291 div.style.animation = 'anim-simple-timing 100s'; 292 const frames = getKeyframes(div); 293 294 assert_equals(frames.length, 3, "number of frames"); 295 assert_equals(frames[0].easing, "linear", 296 "value of 'easing' on ComputedKeyframe #0"); 297 assert_equals(frames[1].easing, "ease-in-out", 298 "value of 'easing' on ComputedKeyframe #1"); 299 assert_equals(frames[2].easing, "steps(1)", 300 "value of 'easing' on ComputedKeyframe #2"); 301 }, 'KeyframeEffect.getKeyframes() returns frames with expected easing' 302 + ' values, when the easing is specified on each keyframe'); 303 304 test(t => { 305 const div = addDiv(t); 306 307 div.style.animation = 'anim-simple-timing-some 100s step-start'; 308 const frames = getKeyframes(div); 309 310 assert_equals(frames.length, 3, "number of frames"); 311 assert_equals(frames[0].easing, "linear", 312 "value of 'easing' on ComputedKeyframe #0"); 313 assert_equals(frames[1].easing, "steps(1, start)", 314 "value of 'easing' on ComputedKeyframe #1"); 315 assert_equals(frames[2].easing, "steps(1, start)", 316 "value of 'easing' on ComputedKeyframe #2"); 317 }, 'KeyframeEffect.getKeyframes() returns frames with expected easing' 318 + ' values, when the easing is specified on some keyframes'); 319 320 test(t => { 321 for (const composite of kCompositeValues) { 322 const div = addDiv(t); 323 324 div.style.animation = 'anim-simple-three 100s'; 325 div.style.animationComposition = composite; 326 const frames = getKeyframes(div); 327 328 assert_equals(frames.length, 3, "number of frames"); 329 330 for (let i = 0; i < frames.length; i++) { 331 assert_equals(frames[i].composite, "auto", 332 "value for 'composite' on ComputedKeyframe #" + i); 333 } 334 } 335 }, 'KeyframeEffect.getKeyframes() returns frames with expected composite' 336 + ' values, when the composite is set on the effect using animation-composition on the' 337 + ' element'); 338 339 test(t => { 340 const div = addDiv(t); 341 342 div.style.animation = 'anim-simple-composite 100s'; 343 const frames = getKeyframes(div); 344 345 assert_equals(frames.length, 3, "number of frames"); 346 assert_equals(frames[0].composite, "replace", 347 "value of 'composite' on ComputedKeyframe #0"); 348 assert_equals(frames[1].composite, "add", 349 "value of 'composite' on ComputedKeyframe #1"); 350 assert_equals(frames[2].composite, "accumulate", 351 "value of 'composite' on ComputedKeyframe #2"); 352 }, 'KeyframeEffect.getKeyframes() returns frames with expected composite' 353 + ' values, when the composite is specified on each keyframe'); 354 355 test(t => { 356 const div = addDiv(t); 357 358 div.style.animation = 'anim-simple-composite-some 100s'; 359 div.style.animationComposition = 'accumulate'; 360 const frames = getKeyframes(div); 361 362 assert_equals(frames.length, 3, "number of frames"); 363 assert_equals(frames[0].composite, "add", 364 "value of 'composite' on ComputedKeyframe #0"); 365 assert_equals(frames[1].composite, "auto", 366 "value of 'composite' on ComputedKeyframe #1"); 367 assert_equals(frames[2].composite, "auto", 368 "value of 'composite' on ComputedKeyframe #2"); 369 }, 'KeyframeEffect.getKeyframes() returns frames with expected composite' 370 + ' values, when the composite is specified on some keyframes'); 371 372 test(t => { 373 const div = addDiv(t); 374 div.style.animation = 'anim-simple-shorthand 100s'; 375 376 const frames = getKeyframes(div); 377 378 const expected = [ 379 { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", 380 marginBottom: "8px", marginLeft: "8px", 381 marginRight: "8px", marginTop: "8px" }, 382 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 383 marginBottom: "16px", marginLeft: "16px", 384 marginRight: "16px", marginTop: "16px" }, 385 ]; 386 assert_frame_lists_equal(frames, expected); 387 }, 'KeyframeEffect.getKeyframes() returns expected frames for a simple' 388 + ' animation that specifies a single shorthand property'); 389 390 test(t => { 391 const div = addDiv(t); 392 div.style.animation = 'anim-omit-to 100s'; 393 div.style.color = 'rgb(255, 255, 255)'; 394 395 const frames = getKeyframes(div); 396 397 // Final keyframe should be replace as per sections 7 and 8 of 398 // https://drafts.csswg.org/css-animations-2/#keyframes 399 const expected = [ 400 { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", 401 color: "rgb(0, 0, 255)" }, 402 { offset: 1, computedOffset: 1, easing: "ease", composite: "replace", 403 color: "rgb(255, 255, 255)" }, 404 ]; 405 assert_frame_lists_equal(frames, expected); 406 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + 407 'animation with a 0% keyframe and no 100% keyframe'); 408 409 test(t => { 410 const div = addDiv(t); 411 div.style.animation = 'anim-omit-from 100s'; 412 div.style.color = 'rgb(255, 255, 255)'; 413 414 const frames = getKeyframes(div); 415 416 // Initial keyframe should be replace as per sections 7 and 8 of 417 // https://drafts.csswg.org/css-animations-2/#keyframes 418 const expected = [ 419 { offset: 0, computedOffset: 0, easing: "ease", composite: "replace", 420 color: "rgb(255, 255, 255)" }, 421 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 422 color: "rgb(0, 0, 255)" }, 423 ]; 424 assert_frame_lists_equal(frames, expected); 425 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + 426 'animation with a 100% keyframe and no 0% keyframe'); 427 428 test(t => { 429 const div = addDiv(t); 430 div.style.animation = 'anim-omit-from-to 100s'; 431 div.style.color = 'rgb(255, 255, 255)'; 432 433 const frames = getKeyframes(div); 434 435 // Initial and final keyframes should be replace as per sections 7 and 8 of 436 // https://drafts.csswg.org/css-animations-2/#keyframes 437 const expected = [ 438 { offset: 0, computedOffset: 0, easing: "ease", composite: "replace", 439 color: "rgb(255, 255, 255)" }, 440 { offset: 0.5, computedOffset: 0.5, easing: "ease", composite: "auto", 441 color: "rgb(0, 0, 255)" }, 442 { offset: 1, computedOffset: 1, easing: "ease", composite: "replace", 443 color: "rgb(255, 255, 255)" }, 444 ]; 445 assert_frame_lists_equal(frames, expected); 446 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + 447 'animation with no 0% or 100% keyframe but with a 50% keyframe'); 448 449 test(t => { 450 const div = addDiv(t); 451 div.style.animation = 'anim-partially-omit-to 100s'; 452 div.style.marginTop = '250px'; 453 454 const frames = getKeyframes(div); 455 456 const expected = [ 457 { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", 458 marginTop: '50px', marginBottom: '100px' }, 459 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 460 marginTop: '250px', marginBottom: '200px' }, 461 ]; 462 assert_frame_lists_equal(frames, expected); 463 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + 464 'animation with a partially complete 100% keyframe (because the ' + 465 '!important rule is ignored)'); 466 467 test(t => { 468 const div = addDiv(t); 469 div.style.animation = 'anim-different-props 100s'; 470 471 const frames = getKeyframes(div); 472 473 const expected = [ 474 { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", 475 color: "rgb(0, 0, 0)", marginTop: "8px" }, 476 { offset: 0.25, computedOffset: 0.25, easing: "ease", composite: "auto", 477 color: "rgb(0, 0, 255)" }, 478 { offset: 0.75, computedOffset: 0.75, easing: "ease", composite: "auto", 479 marginTop: "12px" }, 480 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 481 color: "rgb(255, 255, 255)", marginTop: "16px" }, 482 ]; 483 assert_frame_lists_equal(frames, expected); 484 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + 485 'animation with different properties on different keyframes, all ' + 486 'with the same easing function'); 487 488 test(t => { 489 const div = addDiv(t); 490 div.style.animation = 'anim-different-props-and-easing 100s'; 491 492 const frames = getKeyframes(div); 493 494 const expected = [ 495 { offset: 0, computedOffset: 0, easing: "linear", composite: "auto", 496 color: "rgb(0, 0, 0)", marginTop: "8px" }, 497 { offset: 0.25, computedOffset: 0.25, easing: "steps(1)", composite: "auto", 498 color: "rgb(0, 0, 255)" }, 499 { offset: 0.75, computedOffset: 0.75, easing: "ease-in", composite: "auto", 500 marginTop: "12px" }, 501 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 502 color: "rgb(255, 255, 255)", marginTop: "16px" }, 503 ]; 504 assert_frame_lists_equal(frames, expected); 505 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + 506 'animation with different properties on different keyframes, with ' + 507 'a different easing function on each'); 508 509 test(t => { 510 const div = addDiv(t); 511 div.style.animation = 'anim-merge-offset 100s'; 512 513 const frames = getKeyframes(div); 514 515 const expected = [ 516 { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", 517 color: "rgb(0, 0, 0)", marginTop: "8px" }, 518 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 519 color: "rgb(255, 255, 255)", marginTop: "16px" }, 520 ]; 521 assert_frame_lists_equal(frames, expected); 522 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + 523 'animation with multiple keyframes for the same time, and all with ' + 524 'the same easing function'); 525 526 test(t => { 527 const div = addDiv(t); 528 div.style.animation = 'anim-merge-offset-and-easing 100s'; 529 530 const frames = getKeyframes(div); 531 532 const expected = [ 533 { offset: 0, computedOffset: 0, easing: "steps(1)", composite: "auto", 534 color: "rgb(0, 0, 0)", fontSize: "16px" }, 535 { offset: 0, computedOffset: 0, easing: "linear", composite: "auto", 536 marginTop: "8px", paddingLeft: "2px" }, 537 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 538 color: "rgb(255, 255, 255)", fontSize: "32px", marginTop: "16px", 539 paddingLeft: "4px" }, 540 ]; 541 assert_frame_lists_equal(frames, expected); 542 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + 543 'animation with multiple keyframes for the same time and with ' + 544 'different easing functions'); 545 546 test(t => { 547 const div = addDiv(t); 548 div.style.animation = 'anim-no-merge-equiv-easing 100s'; 549 550 const frames = getKeyframes(div); 551 552 const expected = [ 553 { offset: 0, computedOffset: 0, easing: "steps(1)", composite: "auto", 554 marginTop: "0px", marginRight: "0px", marginBottom: "0px" }, 555 { offset: 0.5, computedOffset: 0.5, easing: "steps(1)", composite: "auto", 556 marginTop: "10px", marginRight: "10px", marginBottom: "10px" }, 557 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 558 marginTop: "20px", marginRight: "20px", marginBottom: "20px" }, 559 ]; 560 assert_frame_lists_equal(frames, expected); 561 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + 562 'animation with multiple keyframes for the same time and with ' + 563 'different but equivalent easing functions'); 564 565 test(t => { 566 const div = addDiv(t); 567 div.style.animation = 'anim-merge-offset-and-composite 100s'; 568 569 const frames = getKeyframes(div); 570 571 const expected = [ 572 { offset: 0, computedOffset: 0, easing: "ease", composite: "add", 573 color: "rgb(0, 0, 0)", fontSize: "16px" }, 574 { offset: 0, computedOffset: 0, easing: "ease", composite: "accumulate", 575 marginTop: "8px", paddingLeft: "2px" }, 576 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 577 color: "rgb(255, 255, 255)", fontSize: "32px", marginTop: "16px", 578 paddingLeft: "4px" }, 579 ]; 580 assert_frame_lists_equal(frames, expected); 581 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + 582 'animation with multiple keyframes for the same time and with ' + 583 'different composite operations'); 584 585 test(t => { 586 const div = addDiv(t); 587 div.style.animation = 'anim-merge-offset-easing-and-composite 100s'; 588 589 const frames = getKeyframes(div); 590 591 const expected = [ 592 { offset: 0, computedOffset: 0, easing: "ease", composite: "add", 593 color: "rgb(0, 0, 0)" }, 594 { offset: 0, computedOffset: 0, easing: "ease", composite: "accumulate", 595 marginTop: "8px", paddingLeft: "2px" }, 596 { offset: 0, computedOffset: 0, easing: "linear", composite: "add", 597 fontSize: "16px" }, 598 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 599 color: "rgb(255, 255, 255)", fontSize: "32px", marginTop: "16px", 600 paddingLeft: "4px" }, 601 ]; 602 assert_frame_lists_equal(frames, expected); 603 }, 'KeyframeEffect.getKeyframes() returns expected frames for an ' + 604 'animation with multiple keyframes for the same time and with ' + 605 'different easing functions and composite operations'); 606 607 test(t => { 608 const div = addDiv(t); 609 div.style.animation = 'anim-overriding 100s'; 610 611 const frames = getKeyframes(div); 612 613 const expected = [ 614 { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", 615 paddingTop: "30px" }, 616 { offset: 0.5, computedOffset: 0.5, easing: "ease", composite: "auto", 617 paddingTop: "20px" }, 618 { offset: 0.75, computedOffset: 0.75, easing: "ease", composite: "auto", 619 paddingTop: "20px" }, 620 { offset: 0.85, computedOffset: 0.85, easing: "ease", composite: "auto", 621 paddingTop: "30px" }, 622 { offset: 0.851, computedOffset: 0.851, easing: "ease", composite: "auto", 623 paddingTop: "60px" }, 624 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 625 paddingTop: "70px" }, 626 ]; 627 assert_frame_lists_equal(frames, expected); 628 }, 'KeyframeEffect.getKeyframes() returns expected frames for ' + 629 'overlapping keyframes'); 630 631 // Gecko-specific test case: We are specifically concerned here that the 632 // computed value for filter, "none", is correctly represented. 633 634 test(t => { 635 const div = addDiv(t); 636 div.style.animation = 'anim-filter 100s'; 637 638 const frames = getKeyframes(div); 639 640 // Initial keyframe should be replace as per sections 7 and 8 of 641 // https://drafts.csswg.org/css-animations-2/#keyframes 642 const expected = [ 643 { offset: 0, computedOffset: 0, easing: "ease", composite: "replace", 644 filter: "none" }, 645 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 646 filter: "blur(5px) sepia(60%) saturate(30%)" }, 647 ]; 648 assert_frame_lists_equal(frames, expected); 649 }, 'KeyframeEffect.getKeyframes() returns expected values for ' + 650 'animations with filter properties and missing keyframes'); 651 652 test(t => { 653 const div = addDiv(t); 654 div.style.animation = 'anim-filter-drop-shadow 100s'; 655 656 const frames = getKeyframes(div); 657 658 const expected = [ 659 { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", 660 filter: "drop-shadow(rgb(0, 255, 0) 10px 10px 10px)" }, 661 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 662 filter: "drop-shadow(rgb(255, 0, 0) 50px 30px 10px)" }, 663 ]; 664 assert_frame_lists_equal(frames, expected); 665 }, 'KeyframeEffect.getKeyframes() returns expected values for ' + 666 'animation with drop-shadow of filter property'); 667 668 // Gecko-specific test case: We are specifically concerned here that the 669 // computed value for text-shadow and a "none" specified on a keyframe 670 // are correctly represented. 671 672 test(t => { 673 const div = addDiv(t); 674 div.style.textShadow = '1px 1px 2px rgb(0, 0, 0), ' + 675 '0 0 16px rgb(0, 0, 255), ' + 676 '0 0 3.2px rgb(0, 0, 255)'; 677 div.style.animation = 'anim-text-shadow 100s'; 678 679 const frames = getKeyframes(div); 680 681 // Initial keyframe should be replace as per sections 7 and 8 of 682 // https://drafts.csswg.org/css-animations-2/#keyframes 683 const expected = [ 684 { offset: 0, computedOffset: 0, easing: "ease", composite: "replace", 685 textShadow: "rgb(0, 0, 0) 1px 1px 2px," 686 + " rgb(0, 0, 255) 0px 0px 16px," 687 + " rgb(0, 0, 255) 0px 0px 3.2px" }, 688 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 689 textShadow: "none" }, 690 ]; 691 assert_frame_lists_equal(frames, expected); 692 }, 'KeyframeEffect.getKeyframes() returns expected values for ' + 693 'animations with text-shadow properties and missing keyframes'); 694 695 // Gecko-specific test case: We are specifically concerned here that the 696 // initial value for background-size and the specified list are correctly 697 // represented. 698 699 test(t => { 700 const div = addDiv(t); 701 702 div.style.animation = 'anim-background-size 100s'; 703 let frames = getKeyframes(div); 704 705 assert_equals(frames.length, 2, "number of frames"); 706 707 // Initial keyframe should be replace as per sections 7 and 8 of 708 // https://drafts.csswg.org/css-animations-2/#keyframes 709 const expected = [ 710 { offset: 0, computedOffset: 0, easing: "ease", composite: "replace", 711 backgroundSize: "auto" }, 712 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 713 backgroundSize: "50% auto, 6px auto, contain" }, 714 ]; 715 716 for (let i = 0; i < frames.length; i++) { 717 assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i); 718 } 719 720 // Test inheriting a background-size value 721 722 expected[0].backgroundSize = div.style.backgroundSize = 723 "30px auto, 40% auto, auto"; 724 frames = getKeyframes(div); 725 726 for (let i = 0; i < frames.length; i++) { 727 assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i 728 + " after updating current style"); 729 } 730 }, 'KeyframeEffect.getKeyframes() returns expected values for ' + 731 'animations with background-size properties and missing keyframes'); 732 733 test(t => { 734 const div = addDiv(t); 735 div.style.animation = 'anim-variables 100s'; 736 737 const frames = getKeyframes(div); 738 739 // Initial keyframe should be replace as per sections 7 and 8 of 740 // https://drafts.csswg.org/css-animations-2/#keyframes 741 const expected = [ 742 { offset: 0, computedOffset: 0, easing: "ease", composite: "replace", 743 transform: "none" }, 744 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 745 transform: "translate(100px)" }, 746 ]; 747 assert_frame_lists_equal(frames, expected); 748 }, 'KeyframeEffect.getKeyframes() returns expected values for ' + 749 'animations with CSS variables as keyframe values'); 750 751 test(t => { 752 const div = addDiv(t); 753 div.style.animation = 'anim-variables-shorthand 100s'; 754 755 const frames = getKeyframes(div); 756 757 // Initial keyframe should be replace as per sections 7 and 8 of 758 // https://drafts.csswg.org/css-animations-2/#keyframes 759 const expected = [ 760 { offset: 0, computedOffset: 0, easing: "ease", composite: "replace", 761 marginBottom: "0px", 762 marginLeft: "0px", 763 marginRight: "0px", 764 marginTop: "0px" }, 765 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 766 marginBottom: "100px", 767 marginLeft: "100px", 768 marginRight: "100px", 769 marginTop: "100px" }, 770 ]; 771 assert_frame_lists_equal(frames, expected); 772 }, 'KeyframeEffect.getKeyframes() returns expected values for ' + 773 'animations with CSS variables as keyframe values in a shorthand property'); 774 775 test(t => { 776 const div = addDiv(t); 777 div.style.animation = 'anim-custom-property-in-keyframe 100s steps(2, start)'; 778 779 const frames = getKeyframes(div); 780 781 // Initial keyframe should be replace as per sections 7 and 8 of 782 // https://drafts.csswg.org/css-animations-2/#keyframes 783 const expected = [ 784 { offset: 0, computedOffset: 0, easing: "steps(2, start)", composite: "replace", 785 color: "rgb(0, 0, 0)" }, 786 { offset: 1, computedOffset: 1, easing: "steps(2, start)", composite: "auto", 787 color: "rgb(0, 255, 0)" }, 788 ]; 789 assert_frame_lists_equal(frames, expected); 790 }, 'KeyframeEffect.getKeyframes() returns expected values for ' + 791 'animations with a CSS variable which is overriden by the value in keyframe'); 792 793 test(t => { 794 const div = addDiv(t); 795 div.style.animation = 'anim-only-custom-property-in-keyframe 100s'; 796 797 const frames = getKeyframes(div); 798 799 const expected = [ 800 { offset: 0, computedOffset: 0, easing: "ease", composite: "auto", 801 transform: "translate(100px)" }, 802 { offset: 1, computedOffset: 1, easing: "ease", composite: "auto", 803 transform: "none" }, 804 ]; 805 assert_frame_lists_equal(frames, expected); 806 }, 'KeyframeEffect.getKeyframes() returns expected values for ' + 807 'animations with only custom property in a keyframe'); 808 809 test(t => { 810 const div = addDiv(t); 811 812 // Add custom @keyframes rule 813 const stylesheet = document.styleSheets[0]; 814 const keyframes = '@keyframes anim-custom { to { left: 100px } }'; 815 const ruleIndex = stylesheet.insertRule(keyframes, 0); 816 const keyframesRule = stylesheet.cssRules[ruleIndex]; 817 818 t.add_cleanup(function() { 819 stylesheet.deleteRule(ruleIndex); 820 }); 821 822 div.style.animation = 'anim-custom 100s'; 823 824 // Sanity check the initial result 825 let frames = getKeyframes(div); 826 assert_frames_equal( 827 frames[frames.length - 1], 828 { 829 offset: 1, 830 computedOffset: 1, 831 easing: 'ease', 832 composite: 'auto', 833 left: '100px', 834 }, 835 'Keyframes reflect the initial @keyframes rule' 836 ); 837 838 // Update the @keyframes rule 839 keyframesRule.deleteRule(0); 840 keyframesRule.appendRule('to { left: 200px }'); 841 842 // Check the result from getKeyframes() is updated 843 frames = getKeyframes(div); 844 assert_frames_equal( 845 frames[frames.length - 1], 846 { 847 offset: 1, 848 computedOffset: 1, 849 easing: 'ease', 850 composite: 'auto', 851 left: '200px', 852 }, 853 'Keyframes reflects the updated @keyframes rule' 854 ); 855 }, 'KeyframeEffect.getKeyframes() reflects changes to @keyframes rules'); 856 857 test(t => { 858 const div = addDiv(t); 859 div.style.animation = 'anim-only-timing-function-for-from-and-to 100s'; 860 861 const frames = getKeyframes(div); 862 863 // Implicit initial and final keyframes should be replace as per sections 864 // 7 and 8 of https://drafts.csswg.org/css-animations-2/#keyframes 865 const expected = [ 866 { offset: 0, computedOffset: 0, easing: "linear", composite: "auto" }, 867 { offset: 0, computedOffset: 0, easing: "ease", composite: "replace", left: "auto" }, 868 { offset: 0.5, computedOffset: 0.5, easing: "ease", composite: "auto", left: "10px" }, 869 { offset: 1, computedOffset: 1, easing: "linear", composite: "auto" }, 870 { offset: 1, computedOffset: 1, easing: "ease", composite: "replace", left: "auto" } 871 ]; 872 assert_frame_lists_equal(frames, expected); 873 }, 'KeyframeEffect.getKeyframes() returns expected values for ' + 874 'animations with implicit values and a non-default timing' + 875 'function specified for 0% and 100%'); 876 877 </script> 878 </body>