tor-browser

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

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>