tor-browser

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

test_transitions_per_property.html (144700B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <!--
      4 https://bugzilla.mozilla.org/show_bug.cgi?id=435441
      5 -->
      6 <head>
      7  <title>Test for Bug 435441</title>
      8  <meta charset=utf-8>
      9  <script src="/tests/SimpleTest/SimpleTest.js"></script>
     10  <script type="text/javascript" src="property_database.js"></script>
     11  <script type="text/javascript" src="animation_utils.js"></script>
     12  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     13  <style type="text/css">
     14 
     15  #display > p { margin-top: 0; margin-bottom: 0; }
     16 
     17  </style>
     18 </head>
     19 <body>
     20 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=435441">Mozilla Bug 435441</a>
     21 
     22 <!--
     23  fixed-height container so percentage heights compute to different
     24  (i.e., nonzero) values
     25  fixed-width container so that percentages for margin-top and
     26  margin-bottom are all relative to the same size container (rather than
     27  one that depends on whether we're tall enough to need a scrollbar)
     28 
     29  Use a 20px font size and line-height so that percentage line-height
     30  and vertical-align doesn't accumulate rounding error.
     31  -->
     32 <div style="height: 50px; width: 300px; font-size: 20px; line-height: 20px">
     33 
     34 <div id="display">
     35 </div>
     36 
     37 </div>
     38 <pre id="test">
     39 <script type="application/javascript">
     40 
     41 /* eslint no-shadow: ["error", {"allow": ["prop", "div"]}] */
     42 /* eslint-disable dot-notation */
     43 
     44 
     45 /** Test for Bug 435441 */
     46 
     47 SimpleTest.requestLongerTimeout(2);
     48 SimpleTest.waitForExplicitFinish();
     49 
     50 function has_num(str)
     51 {
     52    return !!String(str).match(/^([\d.]+)/);
     53 }
     54 
     55 function any_unit_to_num(str)
     56 {
     57    return Number(String(str).match(/^([\d.]+)/)[1]);
     58 }
     59 
     60 var FUNC_NEGATIVE = "cubic-bezier(0.25, -2, 0.75, 1)";
     61 var FUNC_OVERONE = "cubic-bezier(0.25, 0, 0.75, 3)";
     62 
     63 var supported_properties = {
     64    "aspect-ratio" : [ test_aspect_ratio_transition ],
     65    "border-bottom-left-radius": [ test_radius_transition ],
     66    "border-bottom-right-radius": [ test_radius_transition ],
     67    "border-top-left-radius": [ test_radius_transition ],
     68    "border-top-right-radius": [ test_radius_transition ],
     69    "border-start-start-radius": [ test_radius_transition ],
     70    "border-start-end-radius": [ test_radius_transition ],
     71    "border-end-start-radius": [ test_radius_transition ],
     72    "border-end-end-radius": [ test_radius_transition ],
     73    "-moz-box-flex": [ test_float_zeroToOne_transition,
     74                       test_float_aboveOne_transition,
     75                       test_float_zeroToOne_clamped ],
     76    "box-shadow": [ test_shadow_transition ],
     77    "column-count": [ test_pos_integer_or_auto_transition,
     78                      test_integer_at_least_one_clamping ],
     79    "column-rule-color": [ test_color_transition,
     80                           test_currentcolor_transition ],
     81    "column-rule-width": [ test_length_transition,
     82                           test_length_clamped ],
     83    "column-width": [ test_length_transition,
     84                      test_length_clamped ],
     85    "cx": [ test_length_transition, test_percent_transition,
     86            test_length_unclamped, test_percent_unclamped ],
     87    "cy": [ test_length_transition, test_percent_transition,
     88            test_length_unclamped, test_percent_unclamped ],
     89    "background-color": [ test_color_transition,
     90                          test_currentcolor_transition ],
     91    "background-position": [ test_background_position_transition,
     92                             test_length_percent_pair_unclamped ],
     93    "background-position-x": [ test_background_position_coord_transition,
     94                               test_length_transition,
     95                               test_percent_transition,
     96                             // FIXME: We don't currently test clamping,
     97                             // since background-position-x uses calc() as
     98                             // an intermediate form.
     99                             /* test_length_percent_pair_unclamped */ ],
    100    "background-position-y": [ test_background_position_coord_transition,
    101                               test_length_transition,
    102                               test_percent_transition,
    103                             // FIXME: We don't currently test clamping,
    104                             // since background-position-y uses calc() as
    105                             // an intermediate form.
    106                             /* test_length_percent_pair_unclamped */ ],
    107    "background-size": [ test_background_size_transition,
    108                         test_length_percent_pair_clamped ],
    109    "border-bottom-color": [ test_color_transition,
    110                             test_currentcolor_transition ],
    111    "border-bottom-width": [ test_length_transition,
    112                             test_length_clamped ],
    113    "border-left-color": [ test_color_transition,
    114                           test_currentcolor_transition ],
    115    "border-left-width": [ test_length_transition,
    116                           test_length_clamped ],
    117    "border-right-color": [ test_color_transition,
    118                            test_currentcolor_transition ],
    119    "border-right-width": [ test_length_transition,
    120                            test_length_clamped ],
    121    "border-spacing": [ test_length_pair_transition,
    122                        test_length_pair_transition_clamped ],
    123    "border-top-color": [ test_color_transition,
    124                          test_currentcolor_transition ],
    125    "border-top-width": [ test_length_transition,
    126                          test_length_clamped ],
    127    "bottom": [ test_length_transition, test_percent_transition,
    128                test_length_percent_calc_transition,
    129                test_length_unclamped, test_percent_unclamped ],
    130    "accent-color": [ test_color_transition,
    131                      test_currentcolor_transition,
    132                      test_auto_color_transition ],
    133    "caret-color": [ test_color_transition,
    134                     test_currentcolor_transition,
    135                     test_auto_color_transition ],
    136    "clip": [ test_rect_transition ],
    137    "clip-path": [ test_basic_shape_or_url_transition,
    138                   test_path_function ],
    139    "color": [ test_color_transition,
    140               test_currentcolor_transition ],
    141    "d": [ test_path_function ],
    142    "fill": [ test_color_transition,
    143              test_currentcolor_transition ],
    144    "fill-opacity" : [ test_float_zeroToOne_transition,
    145                       // opacity is clamped in computed style
    146                       // (not parsing/interpolation)
    147                       test_float_zeroToOne_clamped ],
    148    "filter" : [ test_filter_transition ],
    149    "flex-basis": [ test_length_transition, test_percent_transition,
    150                    test_length_clamped, test_percent_clamped,
    151                    test_flex_basis_content_transition ],
    152    "flex-grow": [ test_float_zeroToOne_transition,
    153                   test_float_aboveOne_transition ],
    154    "flex-shrink": [ test_float_zeroToOne_transition,
    155                     test_float_aboveOne_transition ],
    156    "flood-color": [ test_color_transition,
    157                     test_currentcolor_transition ],
    158    "flood-opacity" : [ test_float_zeroToOne_transition,
    159                        // opacity is clamped in computed style
    160                        // (not parsing/interpolation)
    161                        test_float_zeroToOne_clamped ],
    162    "font-size": [ test_length_transition, test_percent_transition,
    163                   test_length_percent_calc_transition,
    164                   test_length_clamped, test_percent_clamped ],
    165    "font-size-adjust": [ test_float_zeroToOne_transition,
    166                          test_float_aboveOne_transition,
    167                          /* FIXME: font-size-adjust treats zero specially */
    168                          /* test_float_zeroToOne_clamped */ ],
    169    "font-stretch": [ test_percent_transition, test_percent_clamped ],
    170    "font-weight": [ test_font_weight ],
    171    "column-gap": [ test_grid_gap ],
    172    "row-gap": [ test_grid_gap ],
    173    "height": [ test_length_transition, test_percent_transition,
    174                test_length_percent_calc_transition,
    175                test_length_clamped, test_percent_clamped ],
    176    "left": [ test_length_transition, test_percent_transition,
    177              test_length_percent_calc_transition,
    178              test_length_unclamped, test_percent_unclamped ],
    179    "letter-spacing": [ test_length_transition, test_percent_transition,
    180                        test_length_unclamped, test_percent_unclamped ],
    181    "lighting-color": [ test_color_transition,
    182                        test_currentcolor_transition ],
    183    // NOTE: when calc() is supported on 'line-height', we should add
    184    // test_length_percent_calc_transition.
    185    "line-height": [ test_length_transition, test_percent_transition,
    186                     test_length_clamped, test_percent_clamped ],
    187    "margin-bottom": [ test_length_transition, test_percent_transition,
    188                       test_length_percent_calc_transition,
    189                       test_length_unclamped, test_percent_unclamped ],
    190    "margin-left": [ test_length_transition, test_percent_transition,
    191                     test_length_percent_calc_transition,
    192                     test_length_unclamped, test_percent_unclamped ],
    193    "margin-right": [ test_length_transition, test_percent_transition,
    194                      test_length_percent_calc_transition,
    195                      test_length_unclamped, test_percent_unclamped ],
    196    "margin-top": [ test_length_transition, test_percent_transition,
    197                    test_length_percent_calc_transition,
    198                    test_length_unclamped, test_percent_unclamped ],
    199    "mask-position": [ test_background_position_transition,
    200                       test_length_percent_pair_unclamped ],
    201    "mask-position-x": [ test_background_position_coord_transition,
    202                         test_length_transition,
    203                         test_percent_transition,
    204                         // FIXME: We don't currently test clamping,
    205                         // since background-position-x uses calc() as
    206                         // an intermediate form.
    207                         /* test_length_percent_pair_unclamped */ ],
    208    "mask-position-y": [ test_background_position_coord_transition,
    209                         test_length_transition,
    210                         test_percent_transition,
    211                         // FIXME: We don't currently test clamping,
    212                         // since background-position-y uses calc() as
    213                         // an intermediate form.
    214                         /* test_length_percent_pair_unclamped */ ],
    215    "mask-size": [ test_background_size_transition,
    216                   test_length_percent_pair_clamped ],
    217    "math-depth": [ test_integer_transition ],
    218    "max-height": [ test_length_transition, test_percent_transition,
    219                    test_length_clamped, test_percent_clamped ],
    220    "max-width": [ test_length_transition, test_percent_transition,
    221                   test_length_clamped, test_percent_clamped ],
    222    "min-height": [ test_length_transition, test_percent_transition,
    223                    test_length_clamped, test_percent_clamped ],
    224    "min-width": [ test_length_transition, test_percent_transition,
    225                   test_length_clamped, test_percent_clamped ],
    226    "object-position": [ test_background_position_transition ],
    227    "overflow-clip-margin": [ test_length_transition ],
    228    "opacity" : [ test_float_zeroToOne_transition,
    229                  // opacity is clamped in computed style
    230                  // (not parsing/interpolation)
    231                  test_float_zeroToOne_clamped ],
    232    "order": [ test_integer_transition ],
    233    "outline-color": [ test_color_transition,
    234                       test_currentcolor_transition ],
    235    "outline-offset": [ test_length_transition, test_length_unclamped ],
    236    "outline-width": [ test_length_transition, test_length_clamped ],
    237    "padding-bottom": [ test_length_transition, test_percent_transition,
    238                        test_length_percent_calc_transition,
    239                        test_length_clamped, test_percent_clamped ],
    240    "padding-left": [ test_length_transition, test_percent_transition,
    241                      test_length_percent_calc_transition,
    242                      test_length_clamped, test_percent_clamped ],
    243    "padding-right": [ test_length_transition, test_percent_transition,
    244                       test_length_percent_calc_transition,
    245                       test_length_clamped, test_percent_clamped ],
    246    "padding-top": [ test_length_transition, test_percent_transition,
    247                     test_length_percent_calc_transition,
    248                     test_length_clamped, test_percent_clamped ],
    249    "perspective": [ test_length_transition ],
    250    "perspective-origin": [ test_length_pair_transition,
    251                            test_length_percent_pair_transition,
    252                            test_length_percent_pair_unclamped ],
    253    "right": [ test_length_transition, test_percent_transition,
    254               test_length_percent_calc_transition,
    255               test_length_unclamped, test_percent_unclamped ],
    256    "r": [ test_length_transition, test_percent_transition,
    257           test_length_clamped, test_percent_clamped ],
    258    "rx": [ test_length_transition, test_percent_transition,
    259            test_length_clamped, test_percent_clamped ],
    260    "ry": [ test_length_transition, test_percent_transition,
    261            test_length_clamped, test_percent_clamped ],
    262    "shape-image-threshold": [ test_float_zeroToOne_transition,
    263                               // shape-image-threshold (like opacity) is
    264                               // clamped in computed style
    265                               // (not parsing/interpolation)
    266                               test_float_zeroToOne_clamped ],
    267    "shape-margin": [ test_length_transition, test_percent_transition,
    268                      test_length_clamped, test_percent_clamped ],
    269    "shape-outside": [ test_basic_shape_or_url_transition ],
    270    "stop-color": [ test_color_transition,
    271                    test_currentcolor_transition ],
    272    "stop-opacity" : [ test_float_zeroToOne_transition,
    273                       // opacity is clamped in computed style
    274                       // (not parsing/interpolation)
    275                       test_float_zeroToOne_clamped ],
    276    "stroke": [ test_color_transition,
    277                test_currentcolor_transition ],
    278    "stroke-dasharray": [ test_dasharray_transition ],
    279    "stroke-dashoffset": [ test_length_transition, test_percent_transition,
    280                           test_length_unclamped, test_percent_unclamped, ],
    281    "stroke-miterlimit": [ test_float_zeroToOne_transition,
    282                           test_float_aboveOne_transition,
    283                           test_float_aboveZero_clamped ],
    284    "stroke-opacity" : [ test_float_zeroToOne_transition,
    285                         // opacity is clamped in computed style
    286                         // (not parsing/interpolation)
    287                         test_float_zeroToOne_clamped ],
    288    "stroke-width": [ test_length_transition, test_percent_transition,
    289                      test_length_clamped, test_percent_clamped, ],
    290    "tab-size": [ test_float_zeroToOne_transition,
    291                  test_float_aboveOne_transition, test_length_clamped ],
    292    "text-decoration": [ test_color_shorthand_transition,
    293                         test_currentcolor_shorthand_transition ],
    294    "text-decoration-color": [ test_color_transition,
    295                               test_currentcolor_transition ],
    296    "text-emphasis-color": [ test_color_transition,
    297                             test_currentcolor_transition ],
    298    "text-indent": [ test_length_transition, test_percent_transition,
    299                     test_length_unclamped, test_percent_unclamped ],
    300    "text-shadow": [ test_shadow_transition ],
    301    "top": [ test_length_transition, test_percent_transition,
    302             test_length_percent_calc_transition,
    303             test_length_unclamped, test_percent_unclamped ],
    304    "transform": [ test_transform_transition ],
    305    "transform-origin": [ test_length_pair_transition,
    306                          test_length_percent_pair_transition,
    307                          test_length_percent_pair_unclamped ],
    308    "rotate": [ test_rotate_transition ],
    309    "scale": [ test_scale_transition ],
    310    "translate": [ test_translate_transition ],
    311    "vertical-align": [ test_length_transition, test_percent_transition,
    312                        test_length_unclamped, test_percent_unclamped ],
    313    "visibility": [ test_visibility_transition ],
    314    "width": [ test_length_transition, test_percent_transition,
    315               test_length_percent_calc_transition,
    316               test_length_clamped, test_percent_clamped ],
    317    "word-spacing": [ test_length_transition, test_percent_transition,
    318                      test_length_unclamped, test_percent_unclamped ],
    319    "x": [ test_length_transition, test_percent_transition,
    320           test_length_unclamped, test_percent_unclamped ],
    321    "y": [ test_length_transition, test_percent_transition,
    322           test_length_unclamped, test_percent_unclamped ],
    323    "z-index": [ test_integer_transition, test_pos_integer_or_auto_transition ],
    324    "-webkit-line-clamp": [ test_pos_integer_or_none_transition ],
    325    "-webkit-text-fill-color": [ test_color_transition,
    326                                 test_currentcolor_transition ],
    327    "-webkit-text-stroke-color": [ test_color_transition,
    328                                   test_currentcolor_transition ],
    329    "text-underline-offset": [ test_length_transition ],
    330    "text-decoration-thickness": [ test_length_transition ],
    331    "scroll-margin-top": [
    332      test_length_transition,
    333    ],
    334    "scroll-margin-right": [
    335      test_length_transition,
    336    ],
    337    "scroll-margin-bottom": [
    338      test_length_transition,
    339    ],
    340    "scroll-margin-left": [
    341      test_length_transition,
    342    ],
    343    "scroll-padding-top": [
    344      test_length_transition, test_percent_transition,
    345      test_length_clamped, test_percent_clamped,
    346    ],
    347    "scroll-padding-right": [
    348      test_length_transition, test_percent_transition,
    349      test_length_clamped, test_percent_clamped,
    350    ],
    351    "scroll-padding-bottom": [
    352      test_length_transition, test_percent_transition,
    353      test_length_clamped, test_percent_clamped,
    354    ],
    355    "scroll-padding-left": [
    356      test_length_transition, test_percent_transition,
    357      test_length_clamped, test_percent_clamped,
    358    ],
    359    "scrollbar-color": [ test_scrollbar_color_transition ],
    360 };
    361 
    362 if (IsCSSPropertyPrefEnabled("layout.css.backdrop-filter.enabled")) {
    363  supported_properties["backdrop-filter"] = [ test_filter_transition ];
    364 }
    365 
    366 if (IsCSSPropertyPrefEnabled("layout.css.font-variations.enabled")) {
    367  supported_properties["font-variation-settings"] = [ test_font_variations_transition ];
    368 }
    369 
    370 supported_properties["contain-intrinsic-width"] = [ test_length_transition, test_auto_with_length_transition ];
    371 supported_properties["contain-intrinsic-height"] = supported_properties["contain-intrinsic-width"];
    372 supported_properties["content-visibility"] = [test_content_visibility_transition];
    373 
    374 if (IsCSSPropertyPrefEnabled("layout.css.zoom.enabled")) {
    375  Object.assign(supported_properties, {
    376    "zoom": [ test_number_transition, test_percent_transition ],
    377  });
    378 }
    379 
    380 if (IsCSSPropertyPrefEnabled("layout.css.text-decoration-inset.enabled")) {
    381  Object.assign(supported_properties, {
    382    "text-decoration-inset": [ test_length_transition ],
    383  });
    384 }
    385 
    386 // For properties which are well-tested by web-platform-tests, we don't need to
    387 // test animations/transitions again on them.
    388 var skipped_transitionable_properties = [
    389  "border-image-outset",
    390  "border-image-slice",
    391  "border-image-width",
    392  "font-style",  // Tests being added in https://github.com/web-platform-tests/wpt/pull/37570
    393  "grid-template-columns",
    394  "grid-template-rows",
    395  "hyphenate-limit-chars",  // WPT tests being added in bug 1521723.
    396  "offset-path",
    397  "offset-distance",
    398  "offset-rotate",
    399  "offset-anchor",
    400  "offset-position",
    401 ]
    402 
    403 // Logical properties.
    404 for (const logical_side of ["inline-start", "inline-end", "block-start", "block-end"]) {
    405  supported_properties["border-" + logical_side + "-color"] = supported_properties["border-top-color"];
    406  supported_properties["border-" + logical_side + "-width"] = supported_properties["border-top-width"];
    407  supported_properties["margin-" + logical_side] = supported_properties["margin-top"];
    408  supported_properties["padding-" + logical_side] = supported_properties["padding-top"];
    409  supported_properties["inset-" + logical_side] = supported_properties["top"];
    410  supported_properties["scroll-margin-" + logical_side] = supported_properties["scroll-margin-top"];
    411  supported_properties["scroll-padding-" + logical_side] = supported_properties["scroll-padding-top"];
    412 }
    413 
    414 for (const logical_size of ["inline", "block"]) {
    415  supported_properties[logical_size + "-size"] = supported_properties["width"];
    416  supported_properties["min-" + logical_size + "-size"] = supported_properties["min-width"];
    417  supported_properties["max-" + logical_size + "-size"] = supported_properties["max-width"];
    418  supported_properties["contain-intrinsic-" + logical_size + "-size"] = supported_properties["contain-intrinsic-width"];
    419 }
    420 
    421 var div = document.getElementById("display");
    422 var cs = getComputedStyle(div, "");
    423 var winUtils = SpecialPowers.getDOMWindowUtils(window);
    424 
    425 function computeMatrix(v) {
    426  div.style.setProperty("transform", v, "");
    427  var result = cs.getPropertyValue("transform");
    428  div.style.removeProperty("transform");
    429  return result;
    430 }
    431 var c_rot_15 = computeMatrix("rotate(15deg)");
    432 is(c_rot_15.substring(0,6), "matrix", "should compute to matrix value");
    433 var c_rot_60 = computeMatrix("rotate(60deg)");
    434 is(c_rot_60.substring(0,6), "matrix", "should compute to matrix value");
    435 
    436 var transformTests = [
    437  // rotate
    438  { start: 'none', end: 'rotate(60deg)',
    439    expected_uncomputed: 'rotate(15deg)',
    440    expected: c_rot_15 },
    441  { start: 'rotate(0)', end: 'rotate(60deg)',
    442    expected_uncomputed: 'rotate(15deg)',
    443    expected: c_rot_15 },
    444  { start: 'rotate(0deg)', end: 'rotate(60deg)',
    445    expected_uncomputed: 'rotate(15deg)',
    446    expected: c_rot_15 },
    447  { start: 'none', end: c_rot_60,
    448    expected: c_rot_15 },
    449  { start: 'none', end: 'rotate(360deg)',
    450    expected_uncomputed: 'rotate(90deg)',
    451    expected: computeMatrix('rotate(90deg)') },
    452  { start: 'none', end: 'rotatez(360deg)',
    453    expected_uncomputed: 'rotate(90deg)',
    454    expected: computeMatrix('rotate(90deg)') },
    455  { start: 'none', end: 'rotate(720deg)',
    456    expected_uncomputed: 'rotate(180deg)',
    457    expected: computeMatrix('rotate(180deg)') },
    458  { start: 'none', end: 'rotate(720deg)',
    459    expected_uncomputed: 'rotatez(180deg)',
    460    expected: computeMatrix('rotate(180deg)') },
    461  { start: 'none', end: 'rotate(1080deg)',
    462    expected_uncomputed: 'rotate(270deg)',
    463    expected: computeMatrix('rotate(270deg)') },
    464  { start: 'none', end: 'rotate(1080deg)',
    465    expected_uncomputed: 'rotate(270deg)',
    466    expected: computeMatrix('rotatez(270deg)') },
    467  { start: 'none', end: 'rotate(1440deg)',
    468    expected_uncomputed: 'rotate(360deg)',
    469    expected: computeMatrix('scale(1)'),
    470    round_error_ok: true },
    471  { start: 'none', end: 'rotatey(60deg)',
    472    expected_uncomputed: 'rotatey(15deg)',
    473    expected: computeMatrix('rotatey(15deg)') },
    474  { start: 'none', end: 'rotatey(720deg)',
    475    expected_uncomputed: 'rotatey(180deg)',
    476    expected: computeMatrix('rotatey(180deg)') },
    477  { start: 'none', end: 'rotatex(60deg)',
    478    expected_uncomputed: 'rotatex(15deg)',
    479    expected: computeMatrix('rotatex(15deg)') },
    480  { start: 'none', end: 'rotatex(720deg)',
    481    expected_uncomputed: 'rotatex(180deg)',
    482    expected: computeMatrix('rotatex(180deg)') },
    483 
    484  // translate
    485  { start: 'translate(20px)', end: 'none',
    486    expected_uncomputed: 'translate(15px)',
    487    expected: 'matrix(1, 0, 0, 1, 15, 0)' },
    488  { start: 'translate(20px, 12px)', end: 'none',
    489    expected_uncomputed: 'translate(15px, 9px)',
    490    expected: 'matrix(1, 0, 0, 1, 15, 9)' },
    491  { start: 'translateX(-20px)', end: 'none',
    492    expected_uncomputed: 'translateX(-15px)',
    493    expected: 'matrix(1, 0, 0, 1, -15, 0)' },
    494  { start: 'translateY(-40px)', end: 'none',
    495    expected_uncomputed: 'translateY(-30px)',
    496    expected: 'matrix(1, 0, 0, 1, 0, -30)' },
    497  { start: 'translateZ(40px)', end: 'none',
    498    expected_uncomputed: 'translateZ(30px)',
    499    expected: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 30, 1)' },
    500  { start: 'none', end: 'translate3D(40px, 60px, -40px)',
    501    expected_uncomputed: 'translate3D(10px, 15px, -10px)',
    502    expected: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 15, -10, 1)' },
    503  // percentages are relative to 300px (width) and 50px (height)
    504  // per the prerequisites in property_database.js
    505  { start: 'translate(20%)', end: 'none',
    506    expected_uncomputed: 'translate(15%)',
    507    expected: 'matrix(1, 0, 0, 1, 45, 0)',
    508    round_error_ok: true },
    509  { start: 'translate(20%, 12%)', end: 'none',
    510    expected_uncomputed: 'translate(15%, 9%)',
    511    expected: 'matrix(1, 0, 0, 1, 45, 4.5)',
    512    round_error_ok: true },
    513  { start: 'translateX(-20%)', end: 'none',
    514    expected_uncomputed: 'translateX(-15%)',
    515    expected: 'matrix(1, 0, 0, 1, -45, 0)',
    516    round_error_ok: true },
    517  { start: 'translateY(-40%)', end: 'none',
    518    expected_uncomputed: 'translateY(-30%)',
    519    expected: 'matrix(1, 0, 0, 1, 0, -15)',
    520    round_error_ok: true },
    521  { start: 'none', end: 'rotate(90deg) translate(20%, 20%) rotate(-90deg)',
    522    expected_uncomputed: 'rotate(22.5deg) translate(5%, 5%) rotate(-22.5deg)',
    523    round_error_ok: true },
    524  { start: 'none', end: 'rotate(-90deg) translate(20%, 20%) rotate(90deg)',
    525    expected_uncomputed: 'rotate(-22.5deg) translate(5%, 5%) rotate(22.5deg)',
    526    round_error_ok: true },
    527  // test percent translation using matrix decomposition
    528  { start: 'matrix(1, 0, 0, 1, 0, 0)',
    529    end: 'rotate(90deg) translate(20%, 20%) rotate(-90deg)',
    530    expected: 'matrix(1, 0, 0, 1, -2.5, 15)',
    531    round_error_ok: true },
    532  { start: 'matrix(1, 0, 0, 1, 0, 0)',
    533    end: 'rotate(-90deg) translate(20%, 20%) rotate(90deg)',
    534    expected: 'matrix(1, 0, 0, 1, 2.5, -15)',
    535    round_error_ok: true },
    536  // test calc() in translate
    537  // Note that font-size: is 20px, and that percentages are relative
    538  // to 300px (width) and 50px (height) per the prerequisites in
    539  // property_database.js
    540  { start: 'translateX(20%)', /* 60px */
    541    end: 'translateX(calc(10% + 1em))', /* 30px + 20px = 50px */
    542    expected_uncomputed: 'translateX(calc(17.5% + 0.25em))',
    543    expected: 'matrix(1, 0, 0, 1, 57.5, 0)' },
    544  { start: 'translate(calc(0.75 * 3em + 1.5 * 10%), calc(0.5 * 5em + 0.5 * 8%))', /* 90px, 52px */
    545    end: 'rotate(90deg) translateY(20%) rotate(90deg) translateY(calc(10% + 0.5em)) rotate(180deg)', /* -10px, -15px */
    546    expected: 'matrix(1, 0, 0, 1, 65, 35.25)' },
    547 
    548  // scale
    549  { start: 'scale(2)', end: 'none',
    550    expected_uncomputed: 'scale(1.75)',
    551    expected: 'matrix(1.75, 0, 0, 1.75, 0, 0)' },
    552  { start: 'none', end: 'scale(0.4)',
    553    expected_uncomputed: 'scale(0.85)',
    554    expected: 'matrix(0.85, 0, 0, 0.85, 0, 0)',
    555    round_error_ok: true },
    556  { start: 'scale(2)', end: 'scale(-2)',
    557    expected_uncomputed: 'scale(1)',
    558    expected: 'matrix(1, 0, 0, 1, 0, 0)' },
    559  { start: 'scale(2)', end: 'scale(-6)',
    560    expected_uncomputed: 'scale(0)',
    561    expected: 'matrix(0, 0, 0, 0, 0, 0)' },
    562  { start: 'scale(2, 0.4)', end: 'none',
    563    expected_uncomputed: 'scale(1.75, 0.55)',
    564    expected: 'matrix(1.75, 0, 0, 0.55, 0, 0)',
    565    round_error_ok: true },
    566  { start: 'scaleX(3)', end: 'none',
    567    expected_uncomputed: 'scaleX(2.5)',
    568    expected: 'matrix(2.5, 0, 0, 1, 0, 0)' },
    569  { start: 'scaleY(5)', end: 'none',
    570    expected_uncomputed: 'scaleY(4)',
    571    expected: 'matrix(1, 0, 0, 4, 0, 0)' },
    572  { start: 'scaleZ(5)', end: 'none',
    573    expected_uncomputed: 'scaleZ(4)',
    574    expected: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1)' },
    575  { start: 'none', end: 'scale3D(5, 5, 5)',
    576    expected_uncomputed: 'scale3D(2, 2, 2)',
    577    expected: 'matrix3d(2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1)' },
    578 
    579  // skew
    580  { start: 'skewX(45deg)', end: 'none',
    581    expected_uncomputed: 'skewX(33.75deg)' },
    582  { start: 'skewY(45deg)', end: 'none',
    583    expected_uncomputed: 'skewY(33.75deg)' },
    584  { start: 'skew(45deg)', end: 'none',
    585    expected_uncomputed: 'skew(33.75deg)' },
    586  { start: 'skew(45deg, 45deg)', end: 'none',
    587    expected_uncomputed: 'skew(33.75deg, 33.75deg)' },
    588  { start: 'skewX(45deg)', end: 'skewX(-45deg)',
    589    expected_uncomputed: 'skewX(22.5deg)' },
    590  { start: 'skewX(0)', end: 'skewX(-45deg)',
    591    expected_uncomputed: 'skewX(-11.25deg)' },
    592  { start: 'skewY(45deg)', end: 'skewY(-45deg)',
    593    expected_uncomputed: 'skewY(22.5deg)' },
    594 
    595  // matrix : skewX
    596  { start: 'matrix(1, 0, 3, 1, 0, 0)', end: 'none',
    597    expected: 'matrix(1, 0, ' + 3 * 0.75 + ', 1, 0, 0)',
    598    round_error_ok: true },
    599  { start: 'skewX(0)', end: 'skewX(-45deg) translate(0)',
    600    expected_uncomputed: 'skewX(-11.25deg) translate(0)' },
    601  // matrix : rotate
    602  { start: 'rotate(-30deg)', end: 'matrix(0, 1, -1, 0, 0, 0)',
    603    expected: 'matrix(1, 0, 0, 1, 0, 0)',
    604    round_error_ok: true },
    605  { start: 'rotate(-30deg) translateX(0)',
    606    end: 'translateX(0) rotate(-90deg)',
    607    expected: computeMatrix('rotate(-45deg)'),
    608    round_error_ok: true },
    609  // extended shorter transform list
    610  { start: 'skewY(60deg)', end: 'skewY(-60deg) translateX(0)',
    611    expected_uncomputed: 'skewY(30deg) translateX(0)' },
    612 
    613 
    614  // matrix decomposition
    615 
    616  // Four pairs of the same matrix expressed different ways.
    617  { start: 'matrix(-1, 0, 0, -1, 0, 0)', /* rotate(180deg) */
    618    end: 'matrix(1, 0, 0, 1, 0, 0)',
    619    expected: computeMatrix('rotate(135deg)') },
    620  { start: 'scale(-1)', end: 'none',
    621    expected_uncomputed: 'scale(-0.5)',
    622    expected: 'matrix(-0.5, 0, 0, -0.5, 0, 0)' },
    623  { start: 'rotate(180deg)', end: 'none',
    624    expected_uncomputed: 'rotate(135deg)' },
    625  { start: 'rotate(-180deg)', end: 'none',
    626    expected_uncomputed: 'rotate(-135deg)',
    627    expected: computeMatrix('rotate(225deg)') },
    628 
    629  // matrix followed by scale
    630  { start: 'matrix(2, 0, 0, 2, 10, 20) scale(2)',
    631    end: 'none',
    632    expected: 'matrix(3.0625, 0, 0, 3.0625, 7.5, 15)' },
    633 
    634  // ... and a bunch of similar possibilities.  The spec isn't settled
    635  // here; there are multiple options.  See:
    636  // http://lists.w3.org/Archives/Public/www-style/2010Jun/0602.html
    637  { start: 'matrix(-1, 0, 0, 1, 0, 0)', /* scaleX(-1) */
    638    end: 'matrix(1, 0, 0, 1, 0, 0)',
    639    expected: computeMatrix('scaleX(-0.5)') },
    640 
    641  { start: 'matrix(1, 0, 0, -1, 0, 0)', /* rotate(-180deg) scaleX(-1) */
    642    end: 'matrix(1, 0, 0, 1, 0, 0)',
    643    expected: computeMatrix('rotate(-135deg) scaleX(-0.5)') },
    644 
    645  { start: 'matrix(0, 1, 1, 0, 0, 0)', /* rotate(-90deg) scaleX(-1) */
    646    end: 'matrix(1, 0, 0, 1, 0, 0)',
    647    expected: computeMatrix('rotate(-67.5deg) scaleX(-0.5)') },
    648 
    649  { start: 'matrix(0, -1, 1, 0, 0, 0)', /* rotate(-90deg) */
    650    end: 'matrix(1, 0, 0, 1, 0, 0)',
    651    expected: computeMatrix('rotate(-67.5deg)') },
    652 
    653  { start: 'matrix(0, 1, -1, 0, 0, 0)', /* rotate(90deg) */
    654    end: 'matrix(1, 0, 0, 1, 0, 0)',
    655    expected: computeMatrix('rotate(67.5deg)') },
    656 
    657  { start: 'matrix(0, -1, -1, 0, 0, 0)', /* rotate(90deg) scaleX(-1) */
    658    end: 'matrix(1, 0, 0, 1, 0, 0)',
    659    expected: computeMatrix('rotate(67.5deg) scaleX(-0.5)') },
    660 
    661  // Similar decomposition tests, but with skewX.  I checked visually
    662  // that the sign of the skew was correct by checking visually that
    663  // the animations in
    664  // https://dbaron.org/css/test/2010/transition-negative-determinant
    665  // don't flip when they finish, and then wrote tests corresponding
    666  // to the current code's behavior.
    667  // ... start with four with positive determinants
    668  { start: 'none',
    669    end: 'matrix(1, 0, 1.5, 1, 0, 0)',
    670    /* skewX(atan(1.5)) */
    671    expected: 'matrix(1, 0, ' + 1.5 * 0.25 + ', 1, 0, 0)',
    672    round_error_ok: true },
    673  { start: 'none',
    674    end: 'matrix(-1, 0, 2, -1, 0, 0)',
    675            /* rotate(180deg) skewX(atan(-2)) */
    676    expected: computeMatrix('rotate(45deg) matrix(1, 0, ' + -2 * 0.25 + ', 1, 0, 0)'),
    677    round_error_ok: true },
    678  { start: 'none',
    679    end: 'matrix(0, -1, 1, -3, 0, 0)',
    680            /* rotate(-90deg) skewX(atan(3)) */
    681    expected: computeMatrix('rotate(-22.5deg) matrix(1, 0, ' + 3 * 0.25 + ', 1, 0, 0)'),
    682    round_error_ok: true },
    683  { start: 'none',
    684    end: 'matrix(0, 1, -1, 4, 0, 0)',
    685            /* rotate(90deg) skewX(atan(4)) */
    686    expected: computeMatrix('rotate(22.5deg) matrix(1, 0, ' + 4 * 0.25 + ', 1, 0, 0)'),
    687    round_error_ok: true },
    688  // and then four with negative determinants
    689  { start: 'none',
    690    end: 'matrix(1, 0, 1, -1, 0, 0)',
    691            /* rotate(-180deg) skewX(atan(-1)) scaleX(-1) */
    692    expected: computeMatrix('rotate(-45deg) matrix(1, 0, ' + -1 * 0.25 + ', 1, 0, 0) scaleX(0.5)'),
    693    round_error_ok: true },
    694  { start: 'none',
    695    end: 'matrix(-1, 0, -1, 1, 0, 0)',
    696            /* skewX(atan(-1)) scaleX(-1) */
    697    expected: computeMatrix('matrix(1, 0, ' + -1 * 0.25 + ', 1, 0, 0) scaleX(0.5)') },
    698  { start: 'none',
    699    end: 'matrix(0, 1, 1, -2, 0, 0)',
    700            /* rotate(-90deg) skewX(atan(2)) scaleX(-1) */
    701    expected: computeMatrix('rotate(-22.5deg) matrix(1, 0, ' + 2 * 0.25 + ', 1, 0, 0) scaleX(0.5)'),
    702    round_error_ok: true },
    703  { start: 'none',
    704    end: 'matrix(0, -1, -1, 0.5, 0, 0)',
    705            /* rotate(90deg) skewX(atan(0.5)) scaleX(-1) */
    706    expected: computeMatrix('rotate(22.5deg) matrix(1, 0, ' + 0.5 * 0.25 + ', 1, 0, 0) scaleX(0.5)'),
    707    round_error_ok: true },
    708 
    709  // lists
    710  { start: 'translate(10px) skewY(45deg)',
    711    end: 'translate(30px) skewY(-45deg)',
    712    expected_uncomputed: 'translate(15px) skewY(22.5deg)' },
    713  { start: 'skewY(45deg) rotate(90deg)',
    714    end: 'skewY(-45deg) rotate(90deg)',
    715    expected_uncomputed: 'skewY(22.5deg) rotate(90deg)' },
    716  { start: 'skewX(45deg) rotate(90deg)',
    717    end: 'skewX(-45deg) rotate(90deg)',
    718    expected_uncomputed: 'skewX(22.5deg) rotate(90deg)' },
    719 
    720  // extended lists
    721  { start: 'skewY(45deg) rotate(90deg) translate(0)',
    722    end: 'skewY(-45deg) rotate(90deg)',
    723    expected_uncomputed: 'skewY(22.5deg) rotate(90deg) translate(0)' },
    724  { start: 'skewX(-60deg) rotate(90deg) translate(0)',
    725    end: 'skewX(60deg) rotate(90deg)',
    726    expected_uncomputed: 'skewX(-30deg) rotate(90deg) translate(0)' },
    727 ];
    728 
    729 // We intentionally use a non-default reference-box so we always serialize it.
    730 // Therefore, we can reuse these tests for clip-path and shape-outside.
    731 // Bug 1313619: Add some tests for two basic shapes with an explicit
    732 // reference-box and a default one, for each property (because they use
    733 // different default reference-box).
    734 const basicShapesTests = [
    735  { start: "none", end: "none",
    736    expected: ["none"] },
    737  // none to shape
    738  { start: "none",
    739    end: "circle(500px at 500px 500px) content-box",
    740    expected: ["circle", ["500px at 500px 500px"], "content-box"]
    741  },
    742  { start: "none",
    743    end: "ellipse(500px 500px at 500px 500px) content-box",
    744    expected: ["ellipse", ["500px 500px at 500px 500px"], "content-box"]
    745  },
    746  { start: "none",
    747    end: "polygon(evenodd, 500px 500px, 500px 500px) content-box",
    748    expected: ["polygon", ["evenodd, 500px 500px, 500px 500px"], "content-box"]
    749  },
    750  { start: "none",
    751    end: "inset(500px 500px 500px 500px round 500px 500px) content-box",
    752    expected: ["inset", ["500px round 500px"], "content-box"]
    753  },
    754  // matching functions
    755  { start: "circle(100px)", end: "circle(500px)",
    756    expected: ["circle", ["200px"]] },
    757  { start: "ellipse(100px 100px)", end: "ellipse(500px 500px)",
    758    expected: ["ellipse", ["200px 200px"]] },
    759  { start: "circle(100px at 100px 100px) content-box",
    760    end: "circle(500px at 500px 500px) content-box",
    761    expected: ["circle", ["200px at 200px 200px"], "content-box"]
    762  },
    763  { start: "ellipse(100px 100px at 100px 100px) content-box",
    764    end: "ellipse(500px 500px at 500px 500px) content-box",
    765    expected: ["ellipse", ["200px 200px at 200px 200px"], "content-box"]
    766  },
    767  { start: "polygon(evenodd, 100px 100px, 100px 100px) content-box",
    768    end: "polygon(evenodd, 500px 500px, 500px 500px) content-box",
    769    expected: ["polygon", ["evenodd, 200px 200px, 200px 200px"], "content-box"]
    770  },
    771  { start: "inset(100px 100px 100px 100px round 100px 100px) content-box",
    772    end: "inset(500px 500px 500px 500px round 500px 500px) content-box",
    773    expected: ["inset", ["200px round 200px"], "content-box"]
    774  },
    775  // matching functions percentage
    776  { start: "circle(100%)", end: "circle(500%)",
    777    expected: ["circle", ["200%"]] },
    778  { start: "ellipse(100% 100%)", end: "ellipse(500% 500%)",
    779    expected: ["ellipse", ["200% 200%"]] },
    780  { start: "circle(100% at 100% 100%) content-box",
    781    end: "circle(500% at 500% 500%) content-box",
    782    expected: ["circle", ["200% at 200% 200%"], "content-box"]
    783  },
    784  { start: "ellipse(100% 100% at 100% 100%) content-box",
    785    end: "ellipse(500% 500% at 500% 500%) content-box",
    786    expected: ["ellipse", ["200% 200% at 200% 200%"], "content-box"]
    787  },
    788  { start: "polygon(evenodd, 100% 100%, 100% 100%) content-box",
    789    end: "polygon(evenodd, 500% 500%, 500% 500%) content-box",
    790    expected: ["polygon", ["evenodd, 200% 200%, 200% 200%"], "content-box"]
    791  },
    792  { start: "inset(100% 100% 100% 100% round 100% 100%) content-box",
    793    end: "inset(500% 500% 500% 500% round 500% 500%) content-box",
    794    expected: ["inset", ["200% round 200%"], "content-box"] },
    795  // matching functions with calc() values
    796  { start: "circle(calc(80px + 20px))", end: "circle(calc(200px + 300px))",
    797    expected: ["circle", ["200px"]] },
    798  { start: "circle(calc(80% + 20%))", end: "circle(calc(200% + 300%))",
    799    expected: ["circle", ["200%"]] },
    800  { start: "circle(calc(10px + 20%))", end: "circle(calc(50px + 40%))",
    801    expected: ["circle", ["calc(25% + 20px)"]] },
    802  // matching functions with interpolation between percentage/pixel values
    803  { start: "circle(20px)", end: "circle(100%)",
    804    expected: ["circle", ["calc(25% + 15px)"]] },
    805  { start: "ellipse(100% 100px at 8px 20%) content-box",
    806    end:   "ellipse(40px 4%    at 80% 60px) content-box",
    807    expected: ["ellipse", ["calc(75% + 10px) calc(1% + 75px) at " +
    808                           "calc(20% + 6px) calc(15% + 15px)"],
    809               "content-box"] },
    810  // no interpolation for keywords
    811  { start: "circle()", end: "circle(50px)",
    812    expected: ["circle", ["50px"]] },
    813  { start: "circle(closest-side)", end: "circle(500px)",
    814    expected: ["circle", ["500px"]] },
    815  { start: "circle(farthest-side)", end: "circle(500px)",
    816    expected: ["circle", ["500px"]] },
    817  { start: "circle(500px)", end: "circle(farthest-side)",
    818    expected: ["circle", ["farthest-side"]]},
    819  { start: "circle(500px)", end: "circle(closest-side)",
    820    expected: ["circle", [""]]},
    821  { start: "ellipse()", end: "ellipse(50px 50px)",
    822    expected: ["ellipse", ["50px 50px"]] },
    823  { start: "ellipse(closest-side closest-side)", end: "ellipse(500px 500px)",
    824    expected: ["ellipse", ["500px 500px"]] },
    825  { start: "ellipse(farthest-side closest-side)", end: "ellipse(500px 500px)",
    826    expected: ["ellipse", ["500px 500px"]] },
    827  { start: "ellipse(farthest-side farthest-side)", end: "ellipse(500px 500px)",
    828    expected: ["ellipse", ["500px 500px"]] },
    829  { start: "ellipse(500px 500px)", end: "ellipse(farthest-side farthest-side)",
    830    expected: ["ellipse", ["farthest-side farthest-side"]] },
    831  { start: "ellipse(500px 500px)", end: "ellipse(closest-side closest-side)",
    832    expected: ["ellipse", [""]] },
    833  // mismatching boxes
    834  { start: "circle(100px at 100px 100px) border-box",
    835    end: "circle(500px at 500px 500px) content-box",
    836    expected: ["circle", ["500px at 500px 500px"], "content-box"]
    837  },
    838  { start: "ellipse(100px 100px at 100px 100px) border-box",
    839    end: "ellipse(500px 500px at 500px 500px) content-box",
    840    expected: ["ellipse", ["500px 500px at 500px 500px"], "content-box"]
    841  },
    842  { start: "polygon(evenodd, 100px 100px, 100px 100px) border-box",
    843    end: "polygon(evenodd, 500px 500px, 500px 500px) content-box",
    844    expected: ["polygon", ["evenodd, 500px 500px, 500px 500px"], "content-box"]
    845  },
    846  { start: "inset(100px 100px 100px 100px round 100px 100px) border-box",
    847    end: "inset(500px 500px 500px 500px round 500px 500px) content-box",
    848    expected: ["inset", ["500px round 500px"], "content-box"]
    849  },
    850  // mismatching functions
    851  { start: "circle(100px at 100px 100px) content-box",
    852    end: "ellipse(500px 500px at 500px 500px) content-box",
    853    expected: ["ellipse", ["500px 500px at 500px 500px"], "content-box"]
    854  },
    855  { start: "inset(0px round 20px)", end: "ellipse(500px 500px)",
    856    expected: ["ellipse", ["500px 500px"]]
    857  },
    858  // shape to reference box
    859  { start: "circle(20px)", end: "content-box", expected: ["content-box"] },
    860  { start: "content-box", end: "circle(20px)", expected: ["circle", ["20px"]] },
    861  // url to shape
    862  { start: "circle(20px)", end: "url(http://localhost/a.png)", expected: ["url", ["\"http://localhost/a.png\""]] },
    863  { start: "url(http://localhost/a.png)", end: "circle(20px)", expected: ["circle", ["20px"]] },
    864  // url to none
    865  { start: "none", end: "url(http://localhost/a.png)", expected: ["url", ["\"http://localhost/a.png\""]] },
    866  { start: "http://localhost/a.png", end: "none", expected: ["none"] },
    867 ];
    868 
    869 const basicShapesWithFragmentUrlTests = [
    870  // Fragment url to shape
    871  { start: "circle(20px)", end: "url('#a')", expected: ["url", ["\"#a\""]] },
    872  { start: "url('#a')", end: "circle(20px)", expected: ["circle", ["20px"]] },
    873  // Fragment url to none
    874  { start: "none", end: "url('#a')", expected: ["url", ["\"#a\""]] },
    875  { start: "url('#a')", end: "none", expected: ["none"] },
    876 ];
    877 
    878 // We have a lot of tests in web-platform-tests already, so here we only test
    879 // basic interpolation cases.
    880 const pathFunctionTests = [
    881  { start: "none", end: "none",
    882    expected: ["none"] },
    883  // none to path
    884  { start: "none",
    885    end: "path('M 100 100')",
    886    expected: ["path", '"M 100 100"']
    887  },
    888  // path to none
    889  { start: "path('M 100 100')",
    890    end: "none",
    891    expected: ["none"]
    892  },
    893  // mismatch
    894  {
    895    start: "path('M 0 0 H 100 H 200')",
    896    end: "path('M 0 0 H 500')",
    897    expected: ["path", '"M 0 0 H 500"']
    898  },
    899  {
    900    start: "path('M 0 0 V 100')",
    901    end: "path('M 0 0 H 500')",
    902    expected: ["path", '"M 0 0 H 500"']
    903  },
    904  // match
    905  {
    906    start: "path('M 100 100')",
    907    end: "path('M 100 500')",
    908    expected: ["path", '"M 100 200"']
    909  },
    910  {
    911    start: "path('M 10 10 L 100 100')",
    912    end: "path('M 10 10 L 100 500')",
    913    expected: ["path", '"M 10 10 L 100 200"']
    914  },
    915  {
    916    start: "path('M 10 10 H 100')",
    917    end: "path('M 10 10 H 500')",
    918    expected: ["path", '"M 10 10 H 200"']
    919  },
    920  {
    921    start: "path('M 10 10 V 100')",
    922    end: "path('M 10 10 V 500')",
    923    expected: ["path", '"M 10 10 V 200"']
    924  },
    925  {
    926    start: "path('M 10 10 C 32 42 52 62 120 2200')",
    927    end: "path('M 10 10 C 40 50 60 70 200 3000')",
    928    expected: ["path", '"M 10 10 C 34 44 54 64 140 2400"']
    929  },
    930  {
    931    start: "path('M 10 10 S 45 67 89 123')",
    932    end: "path('M 10 10 S 61 51 113 99')",
    933    expected: ["path", '"M 10 10 S 49 63 95 117"']
    934  },
    935  {
    936    start: "path('M 10 10 Q 32 42 120 2200')",
    937    end: "path('M 10 10 Q 40 50 200 3000')",
    938    expected: ["path", '"M 10 10 Q 34 44 140 2400"']
    939  },
    940  {
    941    start: "path('M 10 10 T 100 200')",
    942    end: "path('M 10 10 T 500 280')",
    943    expected: ["path", '"M 10 10 T 200 220"']
    944  },
    945  {
    946    start: "path('M 10 10 A 10 20 30 0 1 140 450')",
    947    end: "path('M 10 10 A 50 60 70 0 1 380 290')",
    948    expected: ["path", '"M 10 10 A 20 30 40 0 1 200 410"']
    949  },
    950  {
    951    start: "path('M 10 10 A 10 20 30 1 0 140 450')",
    952    end: "path('M 10 10 A 50 60 70 0 1 380 290')",
    953    expected: ["path", '"M 10 10 A 20 30 40 1 1 200 410"']
    954  },
    955  // mix relative and absolute coordinates
    956  {
    957    start: "path('m 10 20 h 30 v 60 h 10 v -10 l 110 60')",
    958    // =="path('M  10  20 H  40 V  80 H  50 V  70 L 160 130')"
    959    end: "path('M 130 140 H 120 V 160 H 130 V 150 L 200 170')",
    960    expected: ["path", '"M 40 50 H 60 V 100 H 70 V 90 L 170 140"']
    961  },
    962 ];
    963 
    964 const clipPathPathFunctionTests = [
    965  // match fill-rule
    966  {
    967    start: "path(nonzero, 'M 100 100')",
    968    end: "path(nonzero, 'M 100 500')",
    969    expected: ["path", '"M 100 200"']
    970  },
    971  {
    972    start: "path(evenodd, 'M 100 100')",
    973    end: "path(evenodd, 'M 100 500')",
    974    expected: ["path", 'evenodd, "M 100 200"']
    975  },
    976  // mismatch fill-rule
    977  {
    978    start: "path(nonzero, 'M 100 100')",
    979    end: "path(evenodd, 'M 100 500')",
    980    expected: ["path", 'evenodd, "M 100 500"']
    981  },
    982 ];
    983 
    984 var filterTests = [
    985  { start: "none", end: "none",
    986    expected: ["none"] },
    987  // function from none (number/length)
    988  { start: "none", end: "brightness(0.5)",
    989    expected: ["brightness", 0.875] },
    990  { start: "none", end: "contrast(0.5)",
    991    expected: ["contrast", 0.875] },
    992  { start: "none", end: "grayscale(0.5)",
    993    expected: ["grayscale", 0.125] },
    994  { start: "none", end: "invert(0.5)",
    995    expected: ["invert", 0.125] },
    996  { start: "none", end: "opacity(0.5)",
    997    expected: ["opacity", 0.875] },
    998  { start: "none", end: "saturate(0.5)",
    999    expected: ["saturate", 0.875] },
   1000  { start: "none", end: "sepia(0.5)",
   1001    expected: ["sepia", 0.125] },
   1002  { start: "none", end: "blur(50px)",
   1003    expected: ["blur", 12.5] },
   1004  // function to none (number/length)
   1005  { start: "brightness(0.5)", end: "none",
   1006    expected: ["brightness", 0.625] },
   1007  { start: "contrast(0.5)", end: "none",
   1008    expected: ["contrast", 0.625] },
   1009  { start: "grayscale(0.5)", end: "none",
   1010    expected: ["grayscale", 0.375] },
   1011  { start: "invert(0.5)", end: "none",
   1012    expected: ["invert", 0.375] },
   1013  { start: "opacity(0.5)", end: "none",
   1014    expected: ["opacity", 0.625] },
   1015  { start: "saturate(0.5)", end: "none",
   1016    expected: ["saturate", 0.625] },
   1017  { start: "sepia(0.5)", end: "none",
   1018    expected: ["sepia", 0.375] },
   1019  { start: "blur(50px)", end: "none",
   1020    expected: ["blur", 37.5] },
   1021  // function to same function (number/length)
   1022  { start: "brightness(0.25)", end: "brightness(0.75)",
   1023    expected: ["brightness", 0.375] },
   1024  { start: "contrast(0.25)", end: "contrast(0.75)",
   1025    expected: ["contrast", 0.375] },
   1026  { start: "grayscale(0.25)", end: "grayscale(0.75)",
   1027    expected: ["grayscale", 0.375] },
   1028  { start: "invert(0.25)", end: "invert(0.75)",
   1029    expected: ["invert", 0.375] },
   1030  { start: "opacity(0.25)", end: "opacity(0.75)",
   1031    expected: ["opacity", 0.375] },
   1032  { start: "saturate(0.25)", end: "saturate(0.75)",
   1033    expected: ["saturate", 0.375] },
   1034  { start: "sepia(0.25)", end: "sepia(0.75)",
   1035    expected: ["sepia", 0.375] },
   1036  { start: "blur(25px)", end: "blur(75px)",
   1037    expected: ["blur", 37.5] },
   1038  // function to same function (percent)
   1039  { start: "brightness(25%)", end: "brightness(75%)",
   1040    expected: ["brightness", 0.375] },
   1041  { start: "contrast(25%)", end: "contrast(75%)",
   1042    expected: ["contrast", 0.375] },
   1043  { start: "grayscale(25%)", end: "grayscale(75%)",
   1044    expected: ["grayscale", 0.375] },
   1045  { start: "invert(25%)", end: "invert(75%)",
   1046    expected: ["invert", 0.375] },
   1047  { start: "opacity(25%)", end: "opacity(75%)",
   1048    expected: ["opacity", 0.375] },
   1049  { start: "saturate(25%)", end: "saturate(75%)",
   1050    expected: ["saturate", 0.375] },
   1051  { start: "sepia(25%)", end: "sepia(75%)",
   1052    expected: ["sepia", 0.375] },
   1053  // function to same function (percent, number/length)
   1054  { start: "brightness(0.25)", end: "brightness(75%)",
   1055    expected: ["brightness", 0.375] },
   1056  { start: "contrast(25%)", end: "contrast(0.75)",
   1057    expected: ["contrast", 0.375] },
   1058  // hue-rotate with different angle values
   1059  { start: "hue-rotate(0deg)", end: "hue-rotate(720deg)",
   1060    expected: ["hue-rotate", "180deg"] },
   1061  { start: "hue-rotate(0rad)", end: "hue-rotate("+4*Math.PI+"rad)",
   1062    expected: ["hue-rotate", "180deg"] },
   1063  { start: "hue-rotate(0grad)", end: "hue-rotate(800grad)",
   1064    expected: ["hue-rotate", "180deg"] },
   1065  { start: "hue-rotate(0turn)", end: "hue-rotate(2turn)",
   1066    expected: ["hue-rotate", "180deg"] },
   1067  { start: "hue-rotate(0deg)", end: "hue-rotate("+4*Math.PI+"rad)",
   1068    expected: ["hue-rotate", "180deg"] },
   1069  { start: "hue-rotate(0turn)", end: "hue-rotate(800grad)",
   1070    expected: ["hue-rotate", "180deg"] },
   1071  { start: "hue-rotate(0grad)", end: "hue-rotate("+4*Math.PI+"rad)",
   1072    expected: ["hue-rotate", "180deg"] },
   1073  { start: "hue-rotate(0grad)", end: "hue-rotate(0turn)",
   1074    expected: ["hue-rotate", "0deg"] },
   1075  // multiple matching functions, same length
   1076  { start: "contrast(25%) brightness(0.25) blur(25px) sepia(75%)",
   1077    end: "contrast(75%) brightness(0.75) blur(75px) sepia(25%)",
   1078    expected: ["contrast", 0.375, "brightness", 0.375, "blur", 37.5, "sepia", 0.625] },
   1079  { start: "invert(25%) brightness(0.25) blur(25px) invert(50%) brightness(0.5) blur(50px)",
   1080    end: "invert(75%) brightness(0.75) blur(75px)",
   1081    expected: ["invert", 0.375, "brightness", 0.375, "blur", 37.5, "invert", 0.375, "brightness", 0.625, "blur", 37.5] },
   1082  // multiple matching functions, different length
   1083  { start: "contrast(25%) brightness(0.5) blur(50px)",
   1084    end: "contrast(75%)",
   1085    expected: ["contrast", 0.375, "brightness", 0.625, "blur", 37.5] },
   1086  // mismatching filter functions
   1087  { start: "contrast(0%)", end: "blur(10px)",
   1088    expected: ["blur", 10] },
   1089  // not supported interpolations
   1090  { start: "none", end: "url('#b')",
   1091    expected: ["url", "\"#b\""] },
   1092  { start: "url('#a')", end: "none",
   1093    expected: ["none"] },
   1094  { start: "url('#a')", end: "url('#b')",
   1095    expected: ["url", "\"#b\""] },
   1096  { start: "url('#a')", end: "blur(10px)",
   1097    expected: ["blur", 10] },
   1098  { start: "blur(10px)", end: "url('#a')",
   1099    expected: ["url", "\"#a\""] },
   1100  { start: "blur(0px) url('#a')", end: "blur(20px)",
   1101    expected: ["blur", 20] },
   1102  { start: "blur(0px)", end: "blur(20px) url('#a')",
   1103    expected: ["blur", 20, "url", "\"#a\""] },
   1104  { start: "contrast(0.25) brightness(0.25) blur(25px)",
   1105    end: "contrast(0.75) url('#a')",
   1106    expected: ["contrast", 0.75, "url", "\"#a\""] },
   1107  { start: "contrast(0.25) brightness(0.25) blur(75px)",
   1108    end: "brightness(0.75) contrast(0.75) blur(25px)",
   1109    expected: ["brightness", 0.75, "contrast", 0.75, "blur", 25] },
   1110  { start: "contrast(0.25) brightness(0.25) blur(25px)",
   1111    end: "contrast(0.75) brightness(0.75) contrast(0.75)",
   1112    expected: ["contrast", 0.75, "brightness", 0.75, "contrast", 0.75] },
   1113  // drop-shadow animation
   1114  { start: "none",
   1115    end: "drop-shadow(rgb(0, 0, 0) 4px 4px 0px)",
   1116    expected: ["drop-shadow", "rgba(0, 0, 0, 0.25) 1px 1px 0px"] },
   1117  { start: "drop-shadow(rgb(0, 0, 0) 0px 0px 0px)",
   1118    end: "drop-shadow(rgb(0, 0, 0) 4px 4px 0px)",
   1119    expected: ["drop-shadow", "rgb(0, 0, 0) 1px 1px 0px"] },
   1120  { start: "drop-shadow(#038000 4px 4px)",
   1121    end: "drop-shadow(8px 8px 8px red)",
   1122    expected: ["drop-shadow", "rgb(66, 96, 0) 5px 5px 2px"] },
   1123  { start: "blur(25px) drop-shadow(8px 8px)",
   1124    end: "blur(75px)",
   1125    expected: ["blur", 37.5, "drop-shadow", "rgba(0, 0, 0, 0.75) 6px 6px 0px"] },
   1126  { start: "blur(75px)",
   1127    end: "blur(25px) drop-shadow(8px 8px)",
   1128    expected: ["blur", 62.5, "drop-shadow", "rgba(0, 0, 0, 0.25) 2px 2px 0px"] },
   1129  { start: "drop-shadow(2px 2px blue)",
   1130    end: "none",
   1131    expected: ["drop-shadow", "rgba(0, 0, 255, 0.75) 1.5px 1.5px 0px"] },
   1132 ];
   1133 
   1134 var prop;
   1135 for (prop in supported_properties) {
   1136  // Test that prop is in the property database.
   1137  ok(prop in gCSSProperties, "property " + prop + " in gCSSProperties");
   1138 
   1139  // Test that the entry has at least one test function.
   1140  ok(supported_properties[prop].length > 0,
   1141     "property " + prop + " must have at least one test function");
   1142 }
   1143 
   1144 // Return a consistent sampling of |count| values out of |array|.
   1145 function sample_array(array, count) {
   1146  if (count <= 0) {
   1147    ok(false, "unexpected count");
   1148    return [];
   1149  }
   1150  var ratio = array.length / count;
   1151  if (ratio <= 1) {
   1152    return array;
   1153  }
   1154  var result = new Array(count);
   1155  for (let i = 0; i < count; ++i) {
   1156    result[i] = array[Math.floor(i * ratio)];
   1157  }
   1158  return result;
   1159 }
   1160 
   1161 // Test that transitions don't do anything (i.e., aren't supported) on
   1162 // the properties not in our test list above (and not transition
   1163 // properties themselves).
   1164 for (prop in gCSSProperties) {
   1165  var prop_info = gCSSProperties[prop];
   1166  if (!(prop in supported_properties) &&
   1167      !skipped_transitionable_properties.includes(prop) &&
   1168      prop_info.type != CSS_TYPE_TRUE_SHORTHAND &&
   1169      prop_info.type != CSS_TYPE_LEGACY_SHORTHAND &&
   1170      !("alias_for" in prop_info) &&
   1171      !prop.match(/^transition-/) &&
   1172      prop != "mask") {
   1173 
   1174    if ("prerequisites" in prop_info) {
   1175      var prereqs = prop_info.prerequisites;
   1176      for (var prereq in prereqs) {
   1177        div.style.setProperty(prereq, prereqs[prereq], "");
   1178      }
   1179    }
   1180 
   1181    var all_values = prop_info.initial_values.concat(prop_info.other_values);
   1182 
   1183    if (all_values.length > 50) {
   1184      // Since we're using an O(N^2) algorithm here, reduce the list of
   1185      // values that we want to test.  (This test is really only testing
   1186      // that somebody didn't make a property animatable without
   1187      // modifying this test.  The odds of somebody doing that without
   1188      // making at least one of the many pairs of values we have left
   1189      // animatable seems pretty low, at least relative to the chance
   1190      // that any pair of the values listed in property_database.js is
   1191      // animatable.)
   1192      //
   1193      // That said, we still try to use all of the start of the list on
   1194      // the assumption that the more basic values are likely to be at
   1195      // the beginning of the list.
   1196      all_values = [].concat(prop_info.initial_values.slice(0,2),
   1197                             sample_array(prop_info.initial_values.slice(2), 6),
   1198                             prop_info.other_values.slice(0, 10),
   1199                             sample_array(prop_info.other_values.slice(10), 40));
   1200    }
   1201 
   1202    var all_computed = [];
   1203    for (var idx in all_values) {
   1204      let val = all_values[idx];
   1205      div.style.setProperty(prop, val, "");
   1206      all_computed.push(cs.getPropertyValue(prop));
   1207    }
   1208    div.style.removeProperty(prop);
   1209 
   1210    div.style.setProperty("transition", prop + " 20s linear", "");
   1211    for (let i = 0; i < all_values.length; ++i) {
   1212      for (let j = i + 1; j < all_values.length; ++j) {
   1213        div.style.setProperty(prop, all_values[i], "");
   1214        is(cs.getPropertyValue(prop), all_computed[i],
   1215           "transitions not supported for property " + prop +
   1216           " value " + all_values[i]);
   1217        div.style.setProperty(prop, all_values[j], "");
   1218        is(cs.getPropertyValue(prop), all_computed[j],
   1219           "transitions not supported for property " + prop +
   1220           " value " + all_values[j]);
   1221      }
   1222    }
   1223 
   1224    div.style.removeProperty("transition");
   1225    div.style.removeProperty(prop);
   1226    if ("prerequisites" in prop_info) {
   1227      var prereqs = prop_info.prerequisites;
   1228      for (var prereq in prereqs) {
   1229        div.style.removeProperty(prereq);
   1230      }
   1231    }
   1232  }
   1233 }
   1234 
   1235 for (let zoomed of [true, false]) {
   1236  info(`testing all supported properties (zoomed: ${zoomed})`);
   1237  // Do 4-second linear transitions with -1 second transition delay and
   1238  // linear timing function so that we can expect the transition to be
   1239  // one quarter of the way through the value space right after changing
   1240  // the property.
   1241  div.style.setProperty("transition-duration", "4s", "");
   1242  div.style.setProperty("transition-delay", "-1s", "");
   1243  div.style.setProperty("transition-timing-function", "linear", "");
   1244  div.style.setProperty("zoom", zoomed ? "2" : "1");
   1245  for (prop in supported_properties) {
   1246    info(`testing ${prop}`);
   1247    var tinfo = supported_properties[prop];
   1248    var prop_info = gCSSProperties[prop];
   1249 
   1250    isnot(prop_info.type, CSS_TYPE_TRUE_SHORTHAND,
   1251          prop + " must not be a shorthand");
   1252    if ("prerequisites" in prop_info) {
   1253      var prereqs = prop_info.prerequisites;
   1254      for (var prereq in prereqs) {
   1255        // We don't want the 19px font-size prereq of line-height, since we
   1256        // want to leave it 20px.
   1257        if (prop != "line-height" || prereq != "font-size") {
   1258          div.style.setProperty(prereq, prereqs[prereq], "");
   1259        }
   1260      }
   1261    }
   1262 
   1263    for (var idx in tinfo) {
   1264      tinfo[idx](prop);
   1265    }
   1266 
   1267    // Make sure to unset the property and stop transitions on it.
   1268    div.style.setProperty("transition-property", "none", "");
   1269    div.style.removeProperty(prop);
   1270    cs.getPropertyValue(prop);
   1271 
   1272    if ("prerequisites" in prop_info) {
   1273      var prereqs = prop_info.prerequisites;
   1274      for (var prereq in prereqs) {
   1275        div.style.removeProperty(prereq);
   1276      }
   1277    }
   1278  }
   1279  div.style.removeProperty("transition");
   1280  div.style.removeProperty("zoom");
   1281 }
   1282 
   1283 function get_distance(prop, v1, v2)
   1284 {
   1285  return SpecialPowers.DOMWindowUtils
   1286           .computeAnimationDistance(div, prop, v1, v2);
   1287 }
   1288 
   1289 function check_distance(prop, start, quarter, end)
   1290 {
   1291  var sq = get_distance(prop, start, quarter);
   1292  var se = get_distance(prop, start, end);
   1293  var qe = get_distance(prop, quarter, end);
   1294 
   1295  ok(Math.abs((sq * 4 - se) / se) < 0.0001, "property '" + prop + "': distance " + sq + " from start '" + start + "' to quarter '" + quarter + "' should be quarter distance " + se + " from start '" + start + "' to end '" + end + "'");
   1296  ok(Math.abs((qe * 4 - se * 3) / se) < 0.0001, "property '" + prop + "': distance " + qe + " from quarter '" + quarter + "' to end '" + end + "' should be three quarters distance " + se + " from start '" + start + "' to end '" + end + "'");
   1297 }
   1298 
   1299 function test_length_transition(prop) {
   1300  div.style.setProperty("transition-property", "none", "");
   1301  div.style.setProperty(prop, "4px", "");
   1302  is(cs.getPropertyValue(prop), "4px",
   1303     "length-valued property " + prop + ": computed value before transition");
   1304  div.style.setProperty("transition-property", prop, "");
   1305  div.style.setProperty(prop, "12px", "");
   1306  is(cs.getPropertyValue(prop), "6px",
   1307     "length-valued property " + prop + ": interpolation of lengths");
   1308  check_distance(prop, "4px", "6px", "12px");
   1309 }
   1310 
   1311 function test_length_clamped(prop) {
   1312  test_length_clamped_or_unclamped(prop, true);
   1313 }
   1314 
   1315 function test_length_unclamped(prop) {
   1316  test_length_clamped_or_unclamped(prop, false);
   1317 }
   1318 
   1319 function test_length_clamped_or_unclamped(prop, is_clamped) {
   1320  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   1321  div.style.setProperty("transition-property", "none", "");
   1322  div.style.setProperty(prop, "0px", "");
   1323  let zero_val = cs.getPropertyValue(prop); // Flushes
   1324  div.style.setProperty("transition-property", prop, "");
   1325  div.style.setProperty(prop, "100px", "");
   1326  (is_clamped ? is : isnot)(cs.getPropertyValue(prop), zero_val,
   1327     "length-valued property " + prop + ": clamping of negatives");
   1328  div.style.setProperty("transition-timing-function", "linear", "");
   1329 }
   1330 
   1331 // Test transition to/from the special 'flex-basis: content' keyword.
   1332 function test_flex_basis_content_transition(prop) {
   1333  is(prop, "flex-basis", "this test function should only be called for 'flex-basis'");
   1334 
   1335  // Test transition from length to 'content':
   1336  div.style.setProperty("transition-property", "none", "");
   1337  div.style.setProperty(prop, "8px", "");
   1338  is(cs.getPropertyValue(prop), "8px",
   1339     "property " + prop + ": computed value before transition to 'content'");
   1340  div.style.setProperty("transition-property", prop, "");
   1341  div.style.setProperty(prop, "content", "");
   1342  is(cs.getPropertyValue(prop), "content",
   1343     "property " + prop + ": transition to 'content' (should be discrete)");
   1344 
   1345  // Test transition from 'content' to length:
   1346  div.style.setProperty("transition-property", "none", "");
   1347  div.style.setProperty(prop, "content", "");
   1348  is(cs.getPropertyValue(prop), "content",
   1349     "property " + prop + ": computed value before transition from 'content'");
   1350  div.style.setProperty("transition-property", prop, "");
   1351  div.style.setProperty(prop, "6px", "");
   1352  is(cs.getPropertyValue(prop), "6px",
   1353     "property " + prop + ": transition from 'content' (should be discrete)");
   1354 }
   1355 
   1356 // Test using float values in the range [0, 1] (e.g. opacity)
   1357 function test_float_zeroToOne_transition(prop) {
   1358  div.style.setProperty("transition-property", "none", "");
   1359  div.style.setProperty(prop, "0.3", "");
   1360  is(cs.getPropertyValue(prop), "0.3",
   1361     "float-valued property " + prop + ": computed value before transition");
   1362  div.style.setProperty("transition-property", prop, "");
   1363  div.style.setProperty(prop, "0.8", "");
   1364  is(cs.getPropertyValue(prop), "0.425",
   1365     "float-valued property " + prop + ": interpolation of floats");
   1366  check_distance(prop, "0.3", "0.425", "0.8");
   1367 }
   1368 
   1369 function test_float_zeroToOne_clamped(prop) {
   1370  test_float_zeroToOne_clamped_or_unclamped(prop, true);
   1371 }
   1372 function test_float_zeroToOne_unclamped(prop) {
   1373  test_float_zeroToOne_clamped_or_unclamped(prop, false);
   1374 }
   1375 
   1376 function test_float_zeroToOne_clamped_or_unclamped(prop, is_clamped) {
   1377  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   1378  div.style.setProperty("transition-property", "none", "");
   1379  div.style.setProperty(prop, "0", "");
   1380  is(cs.getPropertyValue(prop), "0",
   1381     "float-valued property " + prop + ": flush before clamping test");
   1382  div.style.setProperty("transition-property", prop, "");
   1383  div.style.setProperty(prop, "1", "");
   1384  (is_clamped ? is : isnot)(cs.getPropertyValue(prop), "0",
   1385     "float-valued property " + prop + ": clamping of negatives");
   1386  div.style.setProperty("transition-timing-function", "linear", "");
   1387 }
   1388 
   1389 // Test using float values in the range [1, infinity)
   1390 function test_float_aboveOne_transition(prop) {
   1391  div.style.setProperty("transition-property", "none", "");
   1392  div.style.setProperty(prop, "1", "");
   1393  is(cs.getPropertyValue(prop), "1",
   1394     "float-valued property " + prop + ": computed value before transition");
   1395  div.style.setProperty("transition-property", prop, "");
   1396  div.style.setProperty(prop, "2.1", "");
   1397  is(cs.getPropertyValue(prop), "1.275",
   1398     "float-valued property " + prop + ": interpolation of floats");
   1399  check_distance(prop, "1", "1.275", "2.1");
   1400 }
   1401 
   1402 function test_float_aboveZero_clamped(prop) {
   1403  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   1404  div.style.setProperty("transition-property", "none", "");
   1405  div.style.setProperty(prop, "0", "");
   1406  is(cs.getPropertyValue(prop), "0",
   1407     "float-valued property " + prop + ": flush before clamping test");
   1408  div.style.setProperty("transition-property", prop, "");
   1409  div.style.setProperty(prop, "5", "");
   1410  is(cs.getPropertyValue(prop), "0",
   1411     "float-valued property " + prop + ": clamping of negatives");
   1412  div.style.setProperty("transition-timing-function", "linear", "");
   1413 }
   1414 
   1415 function test_percent_transition(prop) {
   1416  div.style.setProperty("transition-property", "none", "");
   1417  div.style.setProperty(prop, "25%", "");
   1418  var av = cs.getPropertyValue(prop);
   1419  var a = any_unit_to_num(av);
   1420  div.style.setProperty(prop, "75%", "");
   1421  var bv = cs.getPropertyValue(prop);
   1422  var b = any_unit_to_num(bv);
   1423  isnot(b, a, "different percentages (" + av + " and " + bv +
   1424              ") should be different for " + prop);
   1425  div.style.setProperty("transition-property", prop, "");
   1426  div.style.setProperty(prop, "25%", "");
   1427  var res = cs.getPropertyValue(prop);
   1428  is(any_unit_to_num(res) * 4, 3 * b + a,
   1429     "percent-valued property " + prop + ": interpolation of percents: " +
   1430     res + " should be a quarter of the way between " + bv + " and " + av);
   1431  ok(has_num(res),
   1432     "percent-valued property " + prop + ": percent computes to number");
   1433  check_distance(prop, "25%", "37.5%", "75%");
   1434 }
   1435 
   1436 function test_percent_clamped(prop) {
   1437  test_percent_clamped_or_unclamped(prop, true);
   1438 }
   1439 
   1440 function test_percent_unclamped(prop) {
   1441  test_percent_clamped_or_unclamped(prop, false);
   1442 }
   1443 
   1444 function test_percent_clamped_or_unclamped(prop, is_clamped) {
   1445  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   1446  div.style.setProperty("transition-property", "none", "");
   1447  div.style.setProperty(prop, "0%", "");
   1448  var zero_val = cs.getPropertyValue(prop); // flushes too
   1449  div.style.setProperty("transition-property", prop, "");
   1450  div.style.setProperty(prop, "150%", "");
   1451  (is_clamped ? is : isnot)(cs.getPropertyValue(prop), zero_val,
   1452     "percent-valued property " + prop + ": clamping of negatives");
   1453  div.style.setProperty("transition-timing-function", "linear", "");
   1454 }
   1455 
   1456 // FIXME: This doesn't deal well with properties for which the resolved value
   1457 // is not the used value, like stroke-dashoffset or stroke-width.
   1458 function test_length_percent_calc_transition(prop) {
   1459  div.style.setProperty("transition-property", "none", "");
   1460  div.style.setProperty(prop, "0%", "");
   1461  var av = cs.getPropertyValue(prop);
   1462  var a = any_unit_to_num(av);
   1463  div.style.setProperty(prop, "100%", "");
   1464  var bv = cs.getPropertyValue(prop);
   1465  var b = any_unit_to_num(bv);
   1466  div.style.setProperty(prop, "100px", "");
   1467  var cv = cs.getPropertyValue(prop);
   1468  var c = any_unit_to_num(cv);
   1469  isnot(b, a, "different percentages (" + av + " and " + bv +
   1470              ") should be different for " + prop);
   1471 
   1472  div.style.setProperty(prop, "50%", "");
   1473  var v1v = cs.getPropertyValue(prop);
   1474  is(any_unit_to_num(v1v) * 2, a + b,
   1475     "computed value before transition for " + prop + ": '" +
   1476     v1v + "' should be halfway " +
   1477     "between '" + av + "' + and '" + bv + "'.");
   1478  div.style.setProperty("transition-property", prop, "");
   1479  div.style.setProperty(prop, "200px", "");
   1480  var v2v = cs.getPropertyValue(prop);
   1481  is(any_unit_to_num(v2v) * 8, 5*a + 3*b + 4*c,
   1482     "interpolation between length and percent for " + prop + ": '"
   1483     + v2v + "'");
   1484 
   1485  div.style.setProperty("transition-property", "none", "");
   1486  div.style.setProperty(prop, "calc(25% + 100px)", "");
   1487  v1v = cs.getPropertyValue(prop);
   1488  is(any_unit_to_num(v1v) * 4, b + 4*c,
   1489     "computed value before transition for " + prop + ": '" + v1v + "'");
   1490  div.style.setProperty("transition-property", prop, "");
   1491  div.style.setProperty(prop, "75%", "");
   1492  v2v = cs.getPropertyValue(prop);
   1493  is(any_unit_to_num(v2v) * 8, 5*a + 3*b + 6*c,
   1494     "interpolation between calc() and percent for " + prop + ": '" +
   1495     v2v + "'");
   1496 
   1497  div.style.setProperty("transition-property", "none", "");
   1498  div.style.setProperty(prop, "150px", "");
   1499  v1v = cs.getPropertyValue(prop);
   1500  is(any_unit_to_num(v1v) * 2, c * 3,
   1501     "computed value before transition for " + prop + ": '" + v1v + "'");
   1502  div.style.setProperty("transition-property", prop, "");
   1503  div.style.setProperty(prop, "calc(50% + 50px)", "");
   1504  v2v = cs.getPropertyValue(prop);
   1505  is(any_unit_to_num(v2v) * 8, 7 * a + b + 10*c,
   1506     "interpolation between length and calc() for " + prop + ": '" +
   1507     v2v + "'");
   1508 
   1509  check_distance(prop, "50%", "calc(37.5% + 50px)", "200px");
   1510  check_distance(prop, "calc(25% + 100px)", "calc(37.5% + 75px)",
   1511                       "75%");
   1512  check_distance(prop, "150px", "calc(125px + 12.5%)",
   1513                       "calc(50% + 50px)");
   1514 }
   1515 
   1516 // This can deal well with properties for which the computed value
   1517 // is not the used value, e.g. translate.
   1518 function test_calc_wrapped_calc_transition(prop) {
   1519  // Test interpolation that computes to calc() (transition from % to px)
   1520  div.style.setProperty("transition-property", "none", "");
   1521  div.style.setProperty(prop, "20%", "");
   1522  is(cs.getPropertyValue(prop), "20%",
   1523     "property " + prop + ": computed value before transition");
   1524  div.style.setProperty("transition-property", prop, "");
   1525  div.style.setProperty(prop, "12px", "");
   1526  is(cs.getPropertyValue(prop), "calc(15% + 3px)",
   1527     "property " + prop + ": interpolation that computes to calc()");
   1528 
   1529  check_distance(prop, "20%", "calc(15% + 3px)", "12px");
   1530 
   1531  // Test interpolation that computes to calc() (transition from px to %)
   1532  div.style.setProperty("transition-property", "none", "");
   1533  div.style.setProperty(prop, "12px", "");
   1534  is(cs.getPropertyValue(prop), "12px",
   1535     "property " + prop + ": computed value before transition");
   1536  div.style.setProperty("transition-property", prop, "");
   1537  div.style.setProperty(prop, "20%", "");
   1538  is(cs.getPropertyValue(prop), "calc(5% + 9px)",
   1539     "property " + prop + ": interpolation that computes to calc()");
   1540 
   1541  check_distance(prop, "12px", "calc(5% + 9px)", "20%");
   1542 
   1543  // Test interpolation between calc() and non-calc()
   1544  div.style.setProperty("transition-property", "none", "");
   1545  div.style.setProperty(prop, "calc(40px + 10%)", "");
   1546  is(cs.getPropertyValue(prop), "calc(10% + 40px)",
   1547     "property " + prop + ": computed value before transition");
   1548  div.style.setProperty("transition-property", prop, "");
   1549  div.style.setProperty(prop, "30%", "");
   1550  is(cs.getPropertyValue(prop), "calc(15% + 30px)",
   1551     "property " + prop + ": interpolation between calc() and non-calc()");
   1552 
   1553  check_distance(prop, "calc(40px + 10%)", "calc(30px + 15%)", "30%");
   1554 
   1555  div.style.setProperty("transition-property", "none", "");
   1556  div.style.setProperty(prop, "16px", "");
   1557  is(cs.getPropertyValue(prop), "16px",
   1558     "property " + prop + ": computed value before transition");
   1559  div.style.setProperty("transition-property", prop, "");
   1560  div.style.setProperty(prop, "calc(8px + 60%)", "");
   1561  is(cs.getPropertyValue(prop), "calc(15% + 14px)",
   1562     "property " + prop + ": interpolation between calc() and non-calc()");
   1563 
   1564  check_distance(prop, "16px", "calc(14px + 15%)", "calc(8px + 60%)");
   1565 }
   1566 
   1567 function test_number_transition(prop) {
   1568  div.style.transitionProperty = 'none';
   1569  div.style[prop] = '10';
   1570  is(cs[prop], '10',
   1571     `number property ${prop}: computed value before transition`);
   1572  div.style.transitionProperty = prop;
   1573  div.style[prop] = '50';
   1574  is(cs[prop], '20', `number property ${prop}: interpolation of numbers`);
   1575  check_distance(prop, '10', '20', '50');
   1576 }
   1577 
   1578 function test_angle_transition(prop) {
   1579  div.style.transitionProperty = 'none';
   1580  div.style[prop] = '45deg';
   1581  is(cs[prop], '45deg',
   1582     `angle property ${prop}: computed value before transition`);
   1583  div.style.transitionProperty = prop;
   1584  div.style[prop] = '145deg';
   1585  is(cs[prop], '70deg',
   1586     `angle property ${prop}: interpolation of angles`);
   1587  check_distance(prop, '45deg', '70deg', '145deg');
   1588 }
   1589 
   1590 function get_color_options(options) {
   1591  let {
   1592    get_color = x => x,
   1593    set_color = x => x,
   1594    is_shorthand = false,
   1595  } = options;
   1596  return { get_color, set_color, is_shorthand };
   1597 }
   1598 
   1599 function test_color_transition(prop, options={}) {
   1600  let { get_color, set_color, is_shorthand } = get_color_options(options);
   1601 
   1602  div.style.setProperty("transition-property", "none", "");
   1603  div.style.setProperty(prop, set_color("rgb(255, 28, 0)"), "");
   1604  is(get_color(cs.getPropertyValue(prop)), "rgb(255, 28, 0)",
   1605     "color-valued property " + prop + ": computed value before transition");
   1606  div.style.setProperty("transition-property", prop, "");
   1607  div.style.setProperty(prop, set_color("rgb(75, 84, 128)"), "");
   1608  is(get_color(cs.getPropertyValue(prop)), "rgb(210, 42, 32)",
   1609     "color-valued property " + prop + ": interpolation of colors");
   1610 
   1611  if (!is_shorthand) {
   1612    check_distance(prop, set_color("rgb(255, 28, 0)"),
   1613                   set_color("rgb(210, 42, 32)"),
   1614                   set_color("rgb(75, 84, 128)"));
   1615  }
   1616 
   1617  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   1618  div.style.setProperty("transition-property", "none", "");
   1619  div.style.setProperty(prop, set_color("rgb(0, 255, 0)"), "");
   1620  var color = get_color(cs.getPropertyValue(prop));
   1621  var vals = color.match(/rgb\(([^, ]*), ([^, ]*), ([^, ]*)\)/);
   1622  is(vals.length, 4,
   1623     "color-valued property " + prop + ": flush before clamping test (length)");
   1624  is(vals[1], "0",
   1625     "color-valued property " + prop + ": flush before clamping test (red)");
   1626  is(vals[2], "255",
   1627     "color-valued property " + prop + ": flush before clamping test (green)");
   1628  is(vals[3], "0",
   1629     "color-valued property " + prop + ": flush before clamping test (blue)");
   1630  div.style.setProperty("transition-property", prop, "");
   1631  div.style.setProperty(prop, set_color("rgb(255, 0, 128)"), "");
   1632  // FIXME: Once we support non-sRGB colors, these tests will need fixing.
   1633  color = get_color(cs.getPropertyValue(prop));
   1634  vals = color.match(/rgb\(([^, ]*), ([^, ]*), ([^, ]*)\)/);
   1635  is(vals.length, 4,
   1636     "color-valued property " + prop + ": clamping of negatives (length)");
   1637  is(vals[1], "0",
   1638     "color-valued property " + prop + ": clamping of negatives (red)");
   1639  is(vals[2], "255",
   1640     "color-valued property " + prop + ": clamping of above-range (green)");
   1641  is(vals[3], "0",
   1642     "color-valued property " + prop + ": clamping of negatives (blue)");
   1643  div.style.setProperty("transition-timing-function", "linear", "");
   1644 }
   1645 
   1646 function test_currentcolor_transition(prop, options={}) {
   1647  let { get_color, set_color } = get_color_options(options);
   1648 
   1649  const msg_prefix = `color-valued property ${prop}: `;
   1650  div.style.setProperty("transition-property", "none", "");
   1651  (prop == "color" ? div.parentNode : div).style.
   1652    setProperty("color", "rgb(128, 0, 0)", "");
   1653  div.style.setProperty(prop, set_color("rgb(0, 0, 128)"), "");
   1654  is(get_color(cs.getPropertyValue(prop)), "rgb(0, 0, 128)",
   1655     msg_prefix + "computed value before transition");
   1656  div.style.setProperty("transition-property", prop, "");
   1657  div.style.setProperty(prop, set_color("currentcolor"), "");
   1658  is(get_color(cs.getPropertyValue(prop)), "rgb(32, 0, 96)",
   1659     msg_prefix + "interpolation of rgb color and currentcolor");
   1660 
   1661  if (prop != "color") {
   1662    div.style.setProperty("transition-property", "none", "");
   1663    div.style.setProperty("color", "rgb(128, 0, 0)", "");
   1664    div.style.setProperty(prop, set_color("rgb(0, 0, 128)"), "");
   1665    is(get_color(cs.getPropertyValue(prop)), "rgb(0, 0, 128)",
   1666       msg_prefix + "computed value before transition");
   1667    div.style.setProperty("transition-property", `color, ${prop}`, "");
   1668    div.style.setProperty("color", "rgb(0, 128, 0)", "");
   1669    div.style.setProperty(prop, set_color("currentcolor"), "");
   1670    is(cs.getPropertyValue("color"), "rgb(96, 32, 0)",
   1671       "interpolation of rgb color property");
   1672    is(get_color(cs.getPropertyValue(prop)), "rgb(24, 8, 96)",
   1673       msg_prefix + "interpolation of rgb color and interpolated currentcolor");
   1674  }
   1675 
   1676  div.style.setProperty("transition-property", "none", "");
   1677  (prop == "color" ? div.parentNode : div).style.
   1678    setProperty("color", "rgba(128, 0, 0, 0.6)", "");
   1679  div.style.setProperty(prop, set_color("rgba(0, 0, 128, 0.8)"), "");
   1680  is(get_color(cs.getPropertyValue(prop)), "rgba(0, 0, 128, 0.8)",
   1681     msg_prefix + "computed value before transition");
   1682  div.style.setProperty("transition-property", prop, "");
   1683  div.style.setProperty(prop, set_color("currentcolor"), "");
   1684  is(get_color(cs.getPropertyValue(prop)), "rgba(26, 0, 102, 0.75)",
   1685     msg_prefix + "interpolation of rgba color and currentcolor");
   1686 
   1687  // It is not possible to check distance, because there is a hidden
   1688  // dimension for ratio of currentcolor.
   1689 
   1690  (prop == "color" ? div.parentNode : div).style.removeProperty("color");
   1691 }
   1692 
   1693 function test_auto_color_transition(prop, options={}) {
   1694  let { get_color, set_color } = get_color_options(options);
   1695 
   1696  const msg_prefix = `color-valued property ${prop}: `;
   1697  const test_color = "rgb(51, 102, 153)";
   1698  div.style.setProperty("transition-property", "none", "");
   1699  div.style.setProperty(prop, "auto", "");
   1700  if (prop == "scrollbar-color") {
   1701    is(cs.getPropertyValue(prop), "auto",
   1702       msg_prefix + "auto should not be resolved to rgb color");
   1703  } else {
   1704    let used_value_of_auto = get_color(cs.getPropertyValue(prop));
   1705    isnot(used_value_of_auto, test_color,
   1706          msg_prefix + "ensure used auto value is different than our test color");
   1707  }
   1708 
   1709  div.style.setProperty("transition-property", prop, "");
   1710  div.style.setProperty(prop, set_color(test_color), "");
   1711  is(get_color(cs.getPropertyValue(prop)), test_color,
   1712     msg_prefix + "not interpolatable between auto and rgb color");
   1713 }
   1714 
   1715 function get_color_from_shorthand_value(value) {
   1716  var m = value.match(/rgba?\([^, ]*, [^, ]*, [^, ]*(?:, [^, ]*)?\)/);
   1717  isnot(m, null, "shorthand property value should contain color");
   1718  return m[0];
   1719 }
   1720 
   1721 function test_color_shorthand_transition(prop) {
   1722  test_color_transition(prop, {
   1723    get_color: get_color_from_shorthand_value,
   1724    is_shorthand: true,
   1725  });
   1726 }
   1727 
   1728 function test_currentcolor_shorthand_transition(prop) {
   1729  test_currentcolor_transition(prop, {
   1730    get_color: get_color_from_shorthand_value,
   1731    is_shorthand: true,
   1732  });
   1733 }
   1734 
   1735 function test_scrollbar_color_transition(prop) {
   1736  function split_colors(value) {
   1737    const colors = value.match(/^(rgba?\(.+?\)) (rgba?\(.+?\))$/);
   1738    isnot(colors, null, "scrollbar-color should consist of two colors");
   1739    return { thumb: colors[1], track: colors[2] };
   1740  }
   1741  const TEST_FUNCS = [
   1742    test_color_transition,
   1743    test_currentcolor_transition,
   1744    test_auto_color_transition,
   1745  ];
   1746  for (let test_func of TEST_FUNCS) {
   1747    test_func(prop, {
   1748      get_color: value => split_colors(value).thumb,
   1749      set_color: value => value + " blue",
   1750    });
   1751    test_func(prop, {
   1752      get_color: value => split_colors(value).track,
   1753      set_color: value => "blue " + value,
   1754    });
   1755  }
   1756 }
   1757 
   1758 function test_shape_or_url_equals(computedValStr, expected)
   1759 {
   1760  // Check simple case "none"
   1761  if (computedValStr == "none" && computedValStr == expected[0]) {
   1762    return true;
   1763  }
   1764  // We will update the expected list in this function for checking the result,
   1765  // so we clone it first to avoid affecting the input parameter.
   1766  var expectedList = expected.slice();
   1767 
   1768  var start = String(computedValStr);
   1769 
   1770  var regBox = /\s*(content\-box|padding\-box|border\-box|margin\-box|view\-box|stroke\-box|fill\-box)/
   1771  var matches = computedValStr.split(regBox);
   1772  var expectRefBox = typeof expectedList[expectedList.length - 1] == "string" &&
   1773                     expectedList[expectedList.length - 1].match(regBox) !== null;
   1774 
   1775  // Found a reference box? Format: "shape()" or "shape() reference-box"
   1776  if (matches.length > 1) {
   1777    // Our split() did actually split the string, which means computedValStr
   1778    // contains a reference box. That reference box should be at the end,
   1779    // which means split() will have produced an empty string as the final
   1780    // entry in |matches|. Let's first ditch that empty string.
   1781    var trailingJunk = matches.pop();
   1782    is(trailingJunk, "", "reference box shouldn't have anything after it");
   1783 
   1784    // Do we expect a reference box?
   1785    if (!expectRefBox) {
   1786      ok(false, "unexpected reference box found");
   1787      matches.pop(); // Get rid of it, so we can test the rest...
   1788    } else {
   1789      is(matches.pop(), expectedList.pop(), "Reference boxes should match");
   1790    }
   1791  } else {
   1792    // No reference box found. Did we expect one?
   1793    if (expectRefBox) {
   1794      ok(false, "expected reference box");
   1795      return false;
   1796    }
   1797  }
   1798  computedValStr = matches[0];
   1799  if (expectedList.length == 0) {
   1800    if (computedValStr == "") {
   1801      return true;
   1802    }
   1803    ok(false, "expected basic shape");
   1804    return false;
   1805  }
   1806 
   1807  // The regular expression does not filter out the last parenthesis.
   1808  // Remove last character for now.
   1809  is(computedValStr.substring(computedValStr.length - 1, computedValStr.length),
   1810                              ')', "Function should have close-paren");
   1811  computedValStr = computedValStr.substring(0, computedValStr.length - 1);
   1812 
   1813  var regShape = /\)*\s*(circle|ellipse|polygon|inset|url)\(/
   1814  matches = computedValStr.split(regShape);
   1815  // First item must be empty. All other items are of functionName, functionValue.
   1816  if (!matches || matches.shift() != "") {
   1817    ok(false, "invalid value or unknown shape function");
   1818    return false;
   1819  }
   1820 
   1821  // Check argument values.
   1822  if (matches[1] != expectedList[1]) {
   1823    ok(false, "function parameters mismatch");
   1824    return false;
   1825  }
   1826 
   1827  return true;
   1828 }
   1829 
   1830 function test_path_function_equals(computedValStr, expectedList)
   1831 {
   1832  // Check simple case "none"
   1833  if (expectedList.length === 1 && computedValStr === expectedList[0]) {
   1834    return true;
   1835  }
   1836 
   1837  var regex = /([a-z]+)\((.*)\)/;
   1838  matches = computedValStr.match(regex)
   1839  if (!matches || matches[0] != computedValStr) {
   1840    ok(false, "Invalid function value");
   1841    return false;
   1842  }
   1843 
   1844  // Bug 1480665: Support ray() for motion path. For now, only path(...) is
   1845  // acceptable.
   1846  if (matches[1] != "path") {
   1847    ok(false, "Only support path function");
   1848    return false;
   1849  }
   1850 
   1851  // Check argument values.
   1852  if (matches[2] != expectedList[1]) {
   1853    ok(false, "Function parameters mismatch");
   1854    return false;
   1855  }
   1856 
   1857  return true;
   1858 }
   1859 
   1860 function filter_function_list_equals(computedValStr, expectedList)
   1861 {
   1862  // Check simple case "none"
   1863  if (computedValStr == "none" && computedValStr == expectedList[0]) {
   1864    return true;
   1865  }
   1866 
   1867  // The regular expression does not filter out the last parenthesis.
   1868  // Remove last character for now.
   1869  is(computedValStr.substring(computedValStr.length - 1, computedValStr.length),
   1870                              ')', "Last character should be close-paren");
   1871  computedValStr = computedValStr.substring(0, computedValStr.length - 1);
   1872 
   1873  var reg = /\)*\s*(blur|brightness|contrast|grayscale|hue\-rotate|invert|opacity|saturate|sepia|drop\-shadow|url)\(/
   1874  var matches = computedValStr.split(reg);
   1875  // First item must be empty. All other items are of functionName, functionValue.
   1876  if (!matches || matches.shift() != "") {
   1877    ok(false, "computed style of 'filter' isn't in the format we expect");
   1878    return false;
   1879  }
   1880 
   1881  // Odd items are the function name, even items the function value.
   1882  if (!matches.length || matches.length % 2 ||
   1883      expectedList.length != matches.length) {
   1884    ok(false, "computed style of 'filter' isn't in the format we expect");
   1885    return false;
   1886  }
   1887  for (let i = 0; i < matches.length; i += 2) {
   1888    var functionName = matches[i];
   1889    var functionValue = matches[i+1];
   1890    var expected = expectedList[i+1]
   1891    var tolerance = 0;
   1892    // Check if we have the expected function.
   1893    if (functionName != expectedList[i]) {
   1894      return false;
   1895    }
   1896    if (functionName == "blur") {
   1897      // Last two characters must be "px".
   1898      if (functionValue.search("px") != functionValue.length - 2) {
   1899        return false;
   1900      }
   1901      functionValue = functionValue.substring(0, functionValue.length - 2);
   1902    } else if (functionName == "hue-rotate") {
   1903      // Just check for string equality.
   1904      return functionValue == expected;
   1905    } else if (functionName == "drop-shadow" || functionName == "url") {
   1906      if (functionValue != expected) {
   1907        return false;
   1908      }
   1909      continue;
   1910    }
   1911    // Check if string is not a number or difference is not in tolerance level.
   1912    if (isNaN(functionValue) ||
   1913      Math.abs(parseFloat(functionValue) - expected) > tolerance) {
   1914      return false;
   1915    }
   1916  }
   1917  return true;
   1918 }
   1919 
   1920 function test_basic_shape_or_url_transition(prop) {
   1921  let tests = basicShapesTests;
   1922  if (prop === "clip-path") {
   1923    // Clip-path won't resolve fragment URLs.
   1924    tests = tests.concat(basicShapesWithFragmentUrlTests);
   1925  }
   1926 
   1927  for (let test of tests) {
   1928    div.style.setProperty("transition-property", "none", "");
   1929    div.style.setProperty(prop, test.start, "");
   1930    cs.getPropertyValue(prop);
   1931    div.style.setProperty("transition-property", prop, "");
   1932    div.style.setProperty(prop, test.end, "");
   1933    var actual = cs.getPropertyValue(prop);
   1934    ok(test_shape_or_url_equals(actual, test.expected),
   1935       prop + " property is " + actual + " expected values of " +
   1936       test.expected);
   1937  }
   1938 }
   1939 
   1940 function test_path_function(prop) {
   1941  let tests = pathFunctionTests;
   1942  if (prop === "clip-path") {
   1943    // The syntax of path() in clip-path has fill-rule, so we have to test more.
   1944    tests = tests.concat(clipPathPathFunctionTests);
   1945  }
   1946 
   1947  for (const test of tests) {
   1948    div.style.setProperty("transition-property", "none", "");
   1949    div.style.setProperty(prop, test.start, "");
   1950    cs.getPropertyValue(prop);
   1951    div.style.setProperty("transition-property", prop, "");
   1952    div.style.setProperty(prop, test.end, "");
   1953    const actual = cs.getPropertyValue(prop);
   1954    ok(test_path_function_equals(actual, test.expected),
   1955       prop + " property is " + actual + " expected values of " +
   1956       test.expected[0] + "(" + test.expected[1] + ")");
   1957  }
   1958 }
   1959 
   1960 function test_filter_transition(prop) {
   1961  for (let i in filterTests) {
   1962    var test = filterTests[i];
   1963    div.style.setProperty("transition-property", "none", "");
   1964    div.style.setProperty(prop, test.start, "");
   1965    cs.getPropertyValue(prop);
   1966    div.style.setProperty("transition-property", prop, "");
   1967    div.style.setProperty(prop, test.end, "");
   1968    var actual = cs.getPropertyValue(prop);
   1969    ok(filter_function_list_equals(actual, test.expected),
   1970                                   "Filter property is " + actual + " expected values of " +
   1971                                   test.expected);
   1972  }
   1973 }
   1974 
   1975 function test_shadow_transition(prop) {
   1976  var origTimingFunc = div.style.getPropertyValue("transition-timing-function");
   1977  var spreadStr = (prop == "box-shadow") ? " 0px" : "";
   1978 
   1979  div.style.setProperty("transition-property", "none", "");
   1980  div.style.setProperty(prop, "none", "");
   1981  is(cs.getPropertyValue(prop), "none",
   1982     "shadow-valued property " + prop + ": computed value before transition");
   1983  div.style.setProperty("transition-property", prop, "");
   1984  div.style.setProperty(prop, "4px 8px 3px red", "");
   1985  is(cs.getPropertyValue(prop), "rgba(255, 0, 0, 0.25) 1px 2px 0.75px" + spreadStr,
   1986     "shadow-valued property " + prop + ": interpolation of shadows");
   1987  check_distance(prop, "none", "rgba(255, 0, 0, 0.25) 1px 2px 0.75px",
   1988                       "4px 8px 3px red");
   1989 
   1990  div.style.setProperty("transition-property", "none", "");
   1991  div.style.setProperty(prop, "#038000 4px 4px, 2px 2px blue", "");
   1992  is(cs.getPropertyValue(prop), "rgb(3, 128, 0) 4px 4px 0px" + spreadStr + ", rgb(0, 0, 255) 2px 2px 0px" + spreadStr,
   1993     "shadow-valued property " + prop + ": computed value before transition");
   1994  div.style.setProperty("transition-property", prop, "");
   1995  div.style.setProperty(prop, "8px 8px 8px red", "");
   1996  is(cs.getPropertyValue(prop), "rgb(66, 96, 0) 5px 5px 2px" + spreadStr + ", rgba(0, 0, 255, 0.75) 1.5px 1.5px 0px" + spreadStr,
   1997     "shadow-valued property " + prop + ": interpolation of shadows");
   1998  check_distance(prop, "#038000 4px 4px, 2px 2px blue",
   1999                       "rgb(66, 96, 0) 5px 5px 2px, rgba(0, 0, 255, 0.75) 1.5px 1.5px 0px",
   2000                       "8px 8px 8px red");
   2001 
   2002  if (prop == "box-shadow") {
   2003    div.style.setProperty(prop, "8px 8px 8px red inset", "");
   2004    is(cs.getPropertyValue(prop), "rgb(255, 0, 0) 8px 8px 8px 0px inset",
   2005       "shadow-valued property " + prop + ": non-interpolable cases");
   2006    div.style.setProperty(prop, "8px 8px 8px 8px red inset", "");
   2007    is(cs.getPropertyValue(prop), "rgb(255, 0, 0) 8px 8px 8px 2px inset",
   2008       "shadow-valued property " + prop + ": interpolation of spread");
   2009    // Leave in same state whether in the |if| or not.
   2010    div.style.setProperty(prop, "8px 8px 8px red", "");
   2011    is(cs.getPropertyValue(prop), "rgb(255, 0, 0) 8px 8px 8px 0px",
   2012       "shadow-valued property " + prop + ": non-interpolable cases");
   2013    check_distance(prop, "8px 8px 8px red inset",
   2014                         "rgb(255, 0, 0) 8px 8px 8px 2px inset",
   2015                         "8px 8px 8px 8px red inset");
   2016  }
   2017 
   2018  // Transition beween values with color and without color.
   2019  div.style.setProperty("transition-property", "none", "");
   2020  div.style.setProperty("color", "rgb(3, 0, 0)", "");
   2021  div.style.setProperty(prop, "2px 2px 2px", "");
   2022  is(cs.getPropertyValue(prop), "rgb(3, 0, 0) 2px 2px 2px" + spreadStr,
   2023     "shadow-valued property " + prop + ": computed value before transition");
   2024  div.style.setProperty("transition-property", prop, "");
   2025  div.style.setProperty(prop, "8px 8px 8px red", "");
   2026  is(cs.getPropertyValue(prop), "rgb(66, 0, 0) 3.5px 3.5px 3.5px" + spreadStr,
   2027     "shadow-valued property " + prop +
   2028     ": interpolation values with/without color");
   2029 
   2030  // Transition beween values without color.
   2031  var defaultColor = cs.getPropertyValue("color") + " ";
   2032  div.style.setProperty("transition-property", "none", "");
   2033  div.style.setProperty(prop, "2px 2px 2px", "");
   2034  is(cs.getPropertyValue(prop), defaultColor + "2px 2px 2px" + spreadStr,
   2035     "shadow-valued property " + prop + ": computed value before transition");
   2036  div.style.setProperty("transition-property", prop, "");
   2037  div.style.setProperty(prop, "6px 14px 10px", "");
   2038  is(cs.getPropertyValue(prop), defaultColor + "3px 5px 4px" + spreadStr,
   2039     "shadow-valued property " + prop + ": interpolation without color");
   2040  check_distance(prop, "2px 2px 2px", "3px 5px 4px", "6px 14px 10px");
   2041 
   2042  // Transition between values with currentcolor transitioning.
   2043  div.style.setProperty("transition-property", "none", "");
   2044  div.style.setProperty("color", "rgb(0, 255, 0)", "");
   2045  div.style.setProperty(prop, "2px 2px 2px", "");
   2046  is(cs.getPropertyValue(prop), "rgb(0, 255, 0) 2px 2px 2px" + spreadStr,
   2047     "shadow-valued property " + prop + ": computed value before transition");
   2048  div.style.setProperty("transition-property", "color, " + prop, "");
   2049  div.style.setProperty("color", "rgb(0, 0, 255)", "");
   2050  div.style.setProperty(prop, "6px 10px 14px red", "");
   2051  is(cs.getPropertyValue(prop), "rgb(64, 143, 48) 3px 4px 5px" + spreadStr,
   2052     "shadow-valued property " + prop + ": interpolation with interpolating" +
   2053     "currentcolor");
   2054 
   2055  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   2056  div.style.setProperty("transition-property", "none", "");
   2057  div.style.setProperty(prop, "0px 0px 0px black", "");
   2058  is(cs.getPropertyValue(prop), "rgb(0, 0, 0) 0px 0px 0px" + spreadStr,
   2059     "shadow-valued property " + prop + ": flush before clamping test");
   2060  div.style.setProperty("transition-property", prop, "");
   2061  div.style.setProperty(prop, "10px 10px 10px black", "");
   2062  var vals = cs.getPropertyValue(prop).split(" ");
   2063  is(vals.length, 6 + (prop == "box-shadow"), "unexpected number of values");
   2064  is(vals.slice(0, 3).join(" "), "rgb(0, 0, 0)",
   2065     "shadow-valued property " + prop + " (color): clamping of negatives");
   2066  isnot(vals[3], "0px",
   2067        "shadow-valued property " + prop + " (x): clamping of negatives");
   2068  isnot(vals[4], "0px",
   2069        "shadow-valued property " + prop + " (y): clamping of negatives");
   2070  is(vals[5], "0px",
   2071     "shadow-valued property " + prop + " (radius): clamping of negatives");
   2072  if (prop == "box-shadow") {
   2073    div.style.setProperty("transition-property", "none", "");
   2074    div.style.setProperty(prop, "0px 0px 0px 0px black", "");
   2075    is(cs.getPropertyValue(prop), "rgb(0, 0, 0) 0px 0px 0px 0px",
   2076       "shadow-valued property " + prop + ": flush before clamping test");
   2077    div.style.setProperty("transition-property", prop, "");
   2078    div.style.setProperty(prop, "10px 10px 10px 10px black", "");
   2079    var vals = cs.getPropertyValue(prop).split(" ");
   2080    is(vals.length, 7, "unexpected number of values");
   2081    is(vals.slice(0, 3).join(" "), "rgb(0, 0, 0)",
   2082       "shadow-valued property " + prop + " (color): clamping of negatives");
   2083    isnot(vals[3], "0px",
   2084          "shadow-valued property " + prop + " (x): clamping of negatives");
   2085    isnot(vals[4], "0px",
   2086          "shadow-valued property " + prop + " (y): clamping of negatives");
   2087    is(vals[5], "0px",
   2088       "shadow-valued property " + prop + " (radius): clamping of negatives");
   2089    isnot(vals[6], "0px",
   2090          "shadow-valued property " + prop + " (spread): clamping of negatives");
   2091  }
   2092 
   2093  // A test case that timing function produces values greater than 1.0.
   2094  div.style.setProperty("transition-timing-function",
   2095                        // This function produces 1.2989961788069297 at 25%.
   2096                        "cubic-bezier(0, 1.5, 0, 1.5)", "");
   2097  div.style.setProperty("transition-property", "none", "");
   2098  div.style.setProperty(prop, "none", "");
   2099  is(cs.getPropertyValue(prop), "none",
   2100     "shadow-valued property " + prop + ": computed value before transition");
   2101  div.style.setProperty("transition-property", prop, "");
   2102  div.style.setProperty(prop, "0px 0px 0px rgba(100, 100, 100, 0.5)", "");
   2103  // The alpha value, 0.5 * 1.2989961788069297 * 255, is 165.622012798, and then
   2104  // converted to 0.649.
   2105  is(cs.getPropertyValue(prop), "rgba(100, 100, 100, 0.649) 0px 0px 0px" + spreadStr,
   2106     "shadow-valued property " + prop + ": interpolation of shadows with " +
   2107     "timing function which produces values greater than 1.0");
   2108 
   2109  div.style.setProperty("transition-timing-function", origTimingFunc, "");
   2110 }
   2111 
   2112 function test_dasharray_transition(prop) {
   2113  div.style.setProperty("transition-property", "none", "");
   2114  div.style.setProperty(prop, "3", "");
   2115  is(cs.getPropertyValue(prop), "3px",
   2116     "dasharray-valued property " + prop +
   2117     ": computed value before transition");
   2118  div.style.setProperty("transition-property", prop, "");
   2119  div.style.setProperty(prop, "15px", "");
   2120  is(cs.getPropertyValue(prop), "6px",
   2121     "dasharray-valued property " + prop + ": interpolation of dasharray");
   2122  check_distance(prop, "3", "6", "15px");
   2123  div.style.setProperty(prop, "none", "");
   2124  is(cs.getPropertyValue(prop), "none",
   2125     "dasharray-valued property " + prop + ": non-interpolability of none");
   2126  div.style.setProperty(prop, "6,8px,4,4", "");
   2127  is(cs.getPropertyValue(prop), "6px, 8px, 4px, 4px",
   2128     "dasharray-valued property " + prop +
   2129     ": computed value before transition");
   2130  div.style.setProperty(prop, "14, 12,16,16px", "");
   2131  is(cs.getPropertyValue(prop), "8px, 9px, 7px, 7px",
   2132     "dasharray-valued property " + prop + ": interpolation of dasharray");
   2133  check_distance(prop, "6,8px,4,4", "8,9,7,7", "14, 12,16,16px");
   2134  div.style.setProperty(prop, "none", "");
   2135  is(cs.getPropertyValue(prop), "none",
   2136     "dasharray-valued property " + prop + ": non-interpolability of none");
   2137  div.style.setProperty(prop, "8,16,4", "");
   2138  is(cs.getPropertyValue(prop), "8px, 16px, 4px",
   2139     "dasharray-valued property " + prop +
   2140     ": computed value before transition");
   2141  div.style.setProperty(prop, "4,8,12,16", "");
   2142  is(cs.getPropertyValue(prop), "7px, 14px, 6px, 10px, 13px, 5px, 9px, 16px, 4px, 8px, 15px, 7px",
   2143     "dasharray-valued property " + prop + ": interpolation of dasharray");
   2144  check_distance(prop, "8,16,4", "7, 14, 6, 10, 13, 5, 9, 16, 4, 8, 15, 7",
   2145                       "4,8,12,16");
   2146  div.style.setProperty(prop, "2,50%,6,10", "");
   2147  is(cs.getPropertyValue(prop),
   2148     "5.75px, calc(12.5% + 10.5px), 6px, 10px, 10.25px, calc(12.5% + 3.75px), 8.25px, 14.5px, 3.5px, calc(12.5% + 6px), 12.75px, 7.75px",
   2149     "dasharray-valued property " + prop + ": interpolability of mixed units");
   2150  div.style.setProperty(prop, "none", "");
   2151  is(cs.getPropertyValue(prop), "none",
   2152     "dasharray-valued property " + prop + ": non-interpolability of none");
   2153  div.style.setProperty(prop, "2,50%,6,10", "");
   2154  is(cs.getPropertyValue(prop), "2px, 50%, 6px, 10px",
   2155     "dasharray-valued property " + prop + ": non-interpolability of none");
   2156  div.style.setProperty(prop, "6,30%,2,2", "");
   2157  is(cs.getPropertyValue(prop), "3px, 45%, 5px, 8px",
   2158     "dasharray-valued property " + prop + ": interpolation of dasharray");
   2159  check_distance(prop, "2,50%,6,10", "3, 45%, 5, 8", "6,30%,2,2");
   2160 
   2161  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   2162  div.style.setProperty("transition-property", "none", "");
   2163  div.style.setProperty(prop, "0,0%", "");
   2164  is(cs.getPropertyValue(prop), "0px, 0%",
   2165     "dasharray-valued property " + prop + ": flush before clamping test");
   2166  div.style.setProperty("transition-property", prop, "");
   2167  div.style.setProperty(prop, "5px, 25%", "");
   2168  is(cs.getPropertyValue(prop), "0px, 0%",
   2169     "dasharray-valued property " + prop + ": clamping of negatives");
   2170  div.style.setProperty("transition-timing-function", "linear", "");
   2171 }
   2172 
   2173 function test_radius_transition(prop) {
   2174  div.style.setProperty("transition-property", "none", "");
   2175 
   2176  // FIXME: Test a square for now, since we haven't updated to the spec
   2177  // for vertical components being relative to the height.
   2178  // Note: We use powers of two here so the floating-point math comes out
   2179  // nicely.
   2180  div.style.setProperty("width", "256px", "");
   2181  div.style.setProperty("height", "256px", "");
   2182  div.style.setProperty("border", "none", "");
   2183  div.style.setProperty("padding", "0", "");
   2184 
   2185  div.style.setProperty(prop, "3px", "");
   2186  is(cs.getPropertyValue(prop), "3px",
   2187     "radius-valued property " + prop +
   2188     ": computed value before transition");
   2189  div.style.setProperty("transition-property", prop, "");
   2190  div.style.setProperty(prop, "15px", "");
   2191  is(cs.getPropertyValue(prop), "6px",
   2192     "radius-valued property " + prop + ": interpolation of radius");
   2193  check_distance(prop, "3px", "6px", "15px");
   2194  div.style.setProperty("transition-property", "none", "");
   2195  div.style.setProperty(prop, "12.5%", "");
   2196  is(cs.getPropertyValue(prop), "12.5%",
   2197     "radius-valued property " + prop + ": computed value before transition");
   2198  div.style.setProperty("transition-property", prop, "");
   2199  div.style.setProperty(prop, "25%", "");
   2200  is(cs.getPropertyValue(prop), "15.625%",
   2201     "radius-valued property " + prop + ": interpolation of radius");
   2202  check_distance(prop, "12.5%", "15.625%", "25%");
   2203  div.style.setProperty("transition-property", "none", "");
   2204  div.style.setProperty(prop, "3px 8px", "");
   2205  is(cs.getPropertyValue(prop), "3px 8px",
   2206     "radius-valued property " + prop + ": computed value before transition");
   2207  div.style.setProperty("transition-property", prop, "");
   2208  div.style.setProperty(prop, "15px 12px", "");
   2209  is(cs.getPropertyValue(prop), "6px 9px",
   2210     "radius-valued property " + prop + ": interpolation of radius");
   2211  check_distance(prop, "3px 8px", "6px 9px", "15px 12px");
   2212  div.style.setProperty("transition-property", "none", "");
   2213  div.style.setProperty(prop, "12.5% 6.25%", "");
   2214  is(cs.getPropertyValue(prop), "12.5% 6.25%",
   2215     "radius-valued property " + prop + ": computed value before transition");
   2216  div.style.setProperty("transition-property", prop, "");
   2217  div.style.setProperty(prop, "25%", "");
   2218  is(cs.getPropertyValue(prop), "15.625% 10.9375%",
   2219     "radius-valued property " + prop + ": interpolation of radius");
   2220  check_distance(prop, "12.5% 6.25%", "15.625% 10.9375%", "25%");
   2221  div.style.setProperty("transition-property", "none", "");
   2222  div.style.setProperty(prop, "6.25% 12.5%", "");
   2223  is(cs.getPropertyValue(prop), "6.25% 12.5%",
   2224     "radius-valued property " + prop + ": computed value before transition");
   2225  div.style.setProperty("transition-property", prop, "");
   2226  div.style.setProperty(prop, "64px 16px", "");
   2227  is(cs.getPropertyValue(prop), "calc(4.6875% + 16px) calc(9.375% + 4px)",
   2228     "radius-valued property " + prop + ": interpolation of radius with mixed units");
   2229  check_distance(prop, "6.25% 12.5%",
   2230                 "calc(4.6875% + 16px) calc(9.375% + 4px)",
   2231                 "64px 16px");
   2232 
   2233  div.style.setProperty("transition-property", "none", "");
   2234  div.style.setProperty(prop, "calc(5px) 10px", "");
   2235  is(cs.getPropertyValue(prop), "5px 10px",
   2236     "radius-valued property " + prop + ": computed value before transition");
   2237  div.style.setProperty("transition-property", prop, "");
   2238  div.style.setProperty(prop, "calc(25px) calc(50px)", "");
   2239  is(cs.getPropertyValue(prop), "10px 20px",
   2240     "radius-valued property " + prop + ": interpolation of radius with calc() units");
   2241 
   2242  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   2243  div.style.setProperty("transition-property", "none", "");
   2244  div.style.setProperty(prop, "0px 0px", "");
   2245  is(cs.getPropertyValue(prop), "0px",
   2246     "radius-valued property " + prop + ": flush before clamping test");
   2247  div.style.setProperty("transition-property", prop, "");
   2248  div.style.setProperty(prop, "10px 20px", "");
   2249  is(cs.getPropertyValue(prop), "0px",
   2250     "radius-valued property " + prop + ": clamping of negatives");
   2251  div.style.setProperty("transition-timing-function", "linear", "");
   2252 
   2253  div.style.removeProperty("width");
   2254  div.style.removeProperty("height");
   2255  div.style.removeProperty("border");
   2256  div.style.removeProperty("padding");
   2257 }
   2258 
   2259 function test_integer_transition(prop) {
   2260  div.style.setProperty("transition-property", "none", "");
   2261  div.style.setProperty(prop, "4", "");
   2262  is(cs.getPropertyValue(prop), "4",
   2263     "integer-valued property " + prop + ": computed value before transition");
   2264  div.style.setProperty("transition-property", prop, "");
   2265  div.style.setProperty(prop, "-14", "");
   2266  is(cs.getPropertyValue(prop), "0",
   2267     "integer-valued property " + prop + ": interpolation of integers");
   2268  check_distance(prop, "6", "1", "-14");
   2269 
   2270  div.style.setProperty("transition-property", "none", "");
   2271  div.style.setProperty(prop, "-4", "");
   2272  is(cs.getPropertyValue(prop), "-4",
   2273     "integer-valued property " + prop + ": computed value before transition");
   2274  div.style.setProperty("transition-property", prop, "");
   2275  div.style.setProperty(prop, "8", "");
   2276  is(cs.getPropertyValue(prop), "-1",
   2277     "integer-valued property " + prop + ": interpolation of integers");
   2278  check_distance(prop, "-4", "-1", "8");
   2279 
   2280  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   2281  div.style.setProperty("transition-property", "none", "");
   2282  div.style.setProperty(prop, "0", "");
   2283  is(cs.getPropertyValue(prop), "0",
   2284     "integer-valued property " + prop + ": flush before clamping test");
   2285  div.style.setProperty("transition-property", prop, "");
   2286  div.style.setProperty(prop, "100", "");
   2287  isnot(cs.getPropertyValue(prop), "0",
   2288        "integer-valued property " + prop + ": clamping of negatives");
   2289  div.style.setProperty("transition-timing-function", "linear", "");
   2290 }
   2291 
   2292 function test_font_weight(prop) {
   2293  is(prop, "font-weight", "only designed for one property");
   2294 
   2295  div.style.setProperty("transition-property", "none", "");
   2296  div.style.setProperty(prop, "normal", "");
   2297  is(cs.getPropertyValue(prop), "400",
   2298     "font-weight property " + prop + ": computed value before transition");
   2299  div.style.setProperty("transition-property", prop, "");
   2300  div.style.setProperty(prop, "900", "");
   2301  is(cs.getPropertyValue(prop), "525",
   2302     "font-weight property " + prop + ": interpolation of font-weights");
   2303  check_distance(prop, "400", "500", "800");
   2304 
   2305  div.style.setProperty("transition-property", "none", "");
   2306  div.style.setProperty(prop, "900", "");
   2307  is(cs.getPropertyValue(prop), "900",
   2308     "font-weight property " + prop + ": computed value before transition");
   2309  div.style.setProperty("transition-property", prop, "");
   2310  div.style.setProperty(prop, "100", "");
   2311  is(cs.getPropertyValue(prop), "700",
   2312     "font-weight property " + prop + ": interpolation of font-weights");
   2313  check_distance(prop, "900", "700", "100");
   2314 
   2315  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   2316  div.style.setProperty("transition-property", "none", "");
   2317  div.style.setProperty(prop, "1", "");
   2318  is(cs.getPropertyValue(prop), "1",
   2319     "font-weight property " + prop + ": flush before clamping test");
   2320  div.style.setProperty("transition-property", prop, "");
   2321  div.style.setProperty(prop, "1000", "");
   2322  is(cs.getPropertyValue(prop), "1",
   2323     "font-weight property " + prop + ": clamping of values");
   2324  div.style.setProperty("transition-property", "none", "");
   2325  div.style.setProperty(prop, "1000", "");
   2326  is(cs.getPropertyValue(prop), "1000",
   2327     "font-weight property " + prop + ": flush before clamping test");
   2328  div.style.setProperty("transition-property", prop, "");
   2329  div.style.setProperty(prop, "1", "");
   2330  is(cs.getPropertyValue(prop), "1000",
   2331     "font-weight property " + prop + ": clamping of values");
   2332  div.style.setProperty("transition-timing-function", "linear", "");
   2333 }
   2334 
   2335 function test_grid_gap(prop) {
   2336  test_length_transition(prop);
   2337  test_length_clamped(prop);
   2338  test_percent_transition(prop);
   2339  test_percent_clamped(prop);
   2340 }
   2341 
   2342 function test_pos_integer_or_keyword_transition(prop, keyword) {
   2343  div.style.setProperty("transition-property", "none", "");
   2344  div.style.setProperty(prop, "4", "");
   2345  is(cs.getPropertyValue(prop), "4",
   2346     "integer-valued property " + prop + ": computed value before transition");
   2347  div.style.setProperty("transition-property", prop, "");
   2348  div.style.setProperty(prop, "11", "");
   2349  is(cs.getPropertyValue(prop), "6",
   2350     "integer-valued property " + prop + ": interpolation of integers");
   2351  check_distance(prop, "4", "6", "12");
   2352  div.style.setProperty(prop, keyword, "");
   2353  is(cs.getPropertyValue(prop), keyword,
   2354     "integer-valued property " + prop + ": " + keyword + " not interpolable");
   2355  div.style.setProperty(prop, "8", "");
   2356  is(cs.getPropertyValue(prop), "8",
   2357     "integer-valued property " + prop + ": computed value before transition");
   2358  div.style.setProperty(prop, "4", "");
   2359  is(cs.getPropertyValue(prop), "7",
   2360     "integer-valued property " + prop + ": interpolation of integers");
   2361  check_distance(prop, "8", "7", "4");
   2362 }
   2363 
   2364 function test_pos_integer_or_auto_transition(prop) {
   2365  return test_pos_integer_or_keyword_transition(prop, "auto");
   2366 }
   2367 
   2368 function test_pos_integer_or_none_transition(prop) {
   2369  return test_pos_integer_or_keyword_transition(prop, "none");
   2370 }
   2371 
   2372 function test_integer_at_least_one_clamping(prop) {
   2373  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   2374  div.style.setProperty("transition-property", "none", "");
   2375  div.style.setProperty(prop, "1", "");
   2376  is(cs.getPropertyValue(prop), "1",
   2377     "integer-valued property " + prop + ": flush before clamping test");
   2378  div.style.setProperty("transition-property", prop, "");
   2379  div.style.setProperty(prop, "5", "");
   2380  is(cs.getPropertyValue(prop), "1",
   2381     "integer-valued property " + prop + ": clamping of negatives");
   2382  div.style.setProperty("transition-timing-function", "linear", "");
   2383 }
   2384 
   2385 function test_length_pair_transition(prop) {
   2386  div.style.setProperty("transition-property", "none", "");
   2387  div.style.setProperty(prop, "4px 6px", "");
   2388  is(cs.getPropertyValue(prop), "4px 6px",
   2389     "length-valued property " + prop + ": computed value before transition");
   2390  div.style.setProperty("transition-property", prop, "");
   2391  div.style.setProperty(prop, "12px 10px", "");
   2392  is(cs.getPropertyValue(prop), "6px 7px",
   2393     "length-valued property " + prop + ": interpolation of lengths");
   2394  check_distance(prop, "4px 6px", "6px 7px", "12px 10px");
   2395 }
   2396 
   2397 function test_length_pair_transition_clamped(prop) {
   2398  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   2399  div.style.setProperty("transition-property", "none", "");
   2400  div.style.setProperty(prop, "0px 0px", "");
   2401  is(cs.getPropertyValue(prop), "0px",
   2402     "length-valued property " + prop + ": flush before clamping test");
   2403  div.style.setProperty("transition-property", prop, "");
   2404  div.style.setProperty(prop, "30px 50px", "");
   2405  is(cs.getPropertyValue(prop), "0px",
   2406     "length-valued property " + prop + ": clamping of negatives");
   2407  div.style.setProperty("transition-timing-function", "linear", "");
   2408 }
   2409 
   2410 function test_length_percent_pair_transition(prop) {
   2411  div.style.setProperty("transition-property", "none", "");
   2412  div.style.setProperty(prop, "4px 50%", "");
   2413  is(cs.getPropertyValue(prop), "4px 5px",
   2414     "length-valued property " + prop + ": computed value before transition");
   2415  div.style.setProperty("transition-property", prop, "");
   2416  div.style.setProperty(prop, "12px 70%", "");
   2417  is(cs.getPropertyValue(prop), "6px 5.5px",
   2418     "length-valued property " + prop + ": interpolation of lengths");
   2419  check_distance(prop, "4px 50%", "6px 55%", "12px 70%");
   2420 }
   2421 
   2422 function test_length_percent_pair_clamped(prop) {
   2423  test_length_percent_pair_clamped_or_unclamped(prop, true);
   2424 }
   2425 
   2426 function test_length_percent_pair_unclamped(prop) {
   2427  test_length_percent_pair_clamped_or_unclamped(prop, false);
   2428 }
   2429 
   2430 function test_length_percent_pair_clamped_or_unclamped(prop, is_clamped) {
   2431  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   2432  div.style.setProperty("transition-property", "none", "");
   2433  div.style.setProperty(prop, "0px 0%", "");
   2434  var is_zero = function(val) {
   2435    if (prop == "transform-origin" || prop == "perspective-origin") {
   2436      // These two properties resolve percentages to pixels.
   2437      return val == "0px 0px";
   2438    }
   2439    return val == "0px 0%";
   2440  }
   2441  ok(is_zero(cs.getPropertyValue(prop)),
   2442     "length+percent-valued property " + prop + ": flush before clamping test");
   2443  div.style.setProperty("transition-property", prop, "");
   2444  div.style.setProperty(prop, "30px 25%", "");
   2445  is(is_zero(cs.getPropertyValue(prop)), is_clamped,
   2446     "length+percent-valued property " + prop + ": clamping of negatives");
   2447  div.style.setProperty("transition-timing-function", "linear", "");
   2448 }
   2449 
   2450 function test_rect_transition(prop) {
   2451  div.style.setProperty("transition-property", "none", "");
   2452  div.style.setProperty(prop, "rect(4px, 16px, 12px, 6px)", "");
   2453  is(cs.getPropertyValue(prop), "rect(4px, 16px, 12px, 6px)",
   2454     "rect-valued property " + prop + ": computed value before transition");
   2455  div.style.setProperty("transition-property", prop, "");
   2456  div.style.setProperty(prop, "rect(0px, 4px, 4px, 2px)", "");
   2457  is(cs.getPropertyValue(prop), "rect(3px, 13px, 10px, 5px)",
   2458     "rect-valued property " + prop + ": interpolation of rects");
   2459  check_distance(prop, "rect(4px, 16px, 12px, 6px)",
   2460                       "rect(3px, 13px, 10px, 5px)",
   2461                       "rect(0px, 4px, 4px, 2px)");
   2462  div.style.setProperty(prop, "rect(0px, 6px, 4px, auto)", "");
   2463  is(cs.getPropertyValue(prop), "rect(0px, 6px, 4px, auto)",
   2464     "rect-valued property " + prop + ": can't interpolate auto components");
   2465  div.style.setProperty(prop, "rect(0px, 6px, 4px, 2px)", "");
   2466  div.style.setProperty(prop, "auto", "");
   2467  is(cs.getPropertyValue(prop), "auto",
   2468     "rect-valued property " + prop + ": can't interpolate auto components");
   2469 
   2470  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   2471  div.style.setProperty("transition-property", "none", "");
   2472  div.style.setProperty(prop, "rect(-10px, 30px, 0px, 0px)", "");
   2473  var vals = cs.getPropertyValue(prop).match(/rect\(([^, ]*), ([^, ]*), ([^, ]*), ([^, ]*)\)/);
   2474  is(vals.length, 5,
   2475     "rect-valued property " + prop + ": flush before clamping test (length)");
   2476  is(vals[1], "-10px",
   2477     "rect-valued property " + prop + ": flush before clamping test (top)");
   2478  is(vals[2], "30px",
   2479     "rect-valued property " + prop + ": flush before clamping test (right)");
   2480  is(vals[3], "0px",
   2481     "rect-valued property " + prop + ": flush before clamping test (bottom)");
   2482  is(vals[4], "0px",
   2483     "rect-valued property " + prop + ": flush before clamping test (left)");
   2484  div.style.setProperty("transition-property", prop, "");
   2485  div.style.setProperty(prop, "rect(0px, 40px, 10px, 10px)", "");
   2486  vals = cs.getPropertyValue(prop).match(/rect\(([^, ]*), ([^, ]*), ([^, ]*), ([^, ]*)\)/);
   2487  is(vals.length, 5,
   2488     "rect-valued property " + prop + ": clamping of negatives (length)");
   2489  isnot(vals[1], "-10px",
   2490     "rect-valued property " + prop + ": clamping of negatives (top)");
   2491  isnot(vals[2], "30px",
   2492     "rect-valued property " + prop + ": clamping of negatives (right)");
   2493  isnot(vals[3], "0px",
   2494     "rect-valued property " + prop + ": clamping of negatives (bottom)");
   2495  isnot(vals[4], "0px",
   2496     "rect-valued property " + prop + ": clamping of negatives (left)");
   2497  div.style.setProperty("transition-timing-function", "linear", "");
   2498 }
   2499 
   2500 function do_test(prop, from_value, to_value, interp_value) {
   2501  div.style.setProperty("transition-property", "none", "");
   2502  div.style.setProperty(prop, from_value, "");
   2503  is(cs.getPropertyValue(prop), from_value,
   2504      "property " + prop + ": computed value before transition");
   2505  div.style.setProperty("transition-property", prop, "");
   2506  div.style.setProperty(prop, to_value, "");
   2507  is(cs.getPropertyValue(prop), interp_value,
   2508      "property " + prop + ": interpolation of " + prop);
   2509 }
   2510 
   2511 function do_negative_test(prop, from_value, to_value, interpolable) {
   2512  div.style.setProperty("transition-property", "none", "");
   2513  div.style.setProperty(prop, from_value, "");
   2514  is(cs.getPropertyValue(prop), from_value,
   2515      "property " + prop + ": flush before clamping test");
   2516  div.style.setProperty("transition-property", prop, "");
   2517  div.style.setProperty(prop, to_value, "");
   2518  is(cs.getPropertyValue(prop), interpolable ? from_value : to_value,
   2519      "property " + prop + ": clamping of negatives");
   2520 }
   2521 
   2522 function do_overone_test(prop, from_value, to_value) {
   2523  div.style.setProperty("transition-property", "none", "");
   2524  div.style.setProperty(prop, from_value, "");
   2525  is(cs.getPropertyValue(prop), from_value,
   2526      "property " + prop + ": flush before clamping test");
   2527  div.style.setProperty("transition-property", prop, "");
   2528  div.style.setProperty(prop, to_value, "");
   2529  is(cs.getPropertyValue(prop), to_value,
   2530      "property " + prop + ": clamping of over-ones");
   2531 }
   2532 
   2533 function test_visibility_transition(prop) {
   2534  do_test(prop, "visible", "hidden", "visible");
   2535  do_test(prop, "hidden", "visible", "visible");
   2536  do_test(prop, "hidden", "collapse", "collapse"); /* not interpolable */
   2537  do_test(prop, "collapse", "hidden", "hidden"); /* not interpolable */
   2538  do_test(prop, "visible", "collapse", "visible");
   2539  do_test(prop, "collapse", "visible", "visible");
   2540 
   2541  isnot(get_distance(prop, "visible", "hidden"), 0,
   2542        "distance between visible and hidden should not be zero");
   2543  isnot(get_distance(prop, "visible", "collapse"), 0,
   2544        "distance between visible and collapse should not be zero");
   2545  is(get_distance(prop, "visible", "visible"), 0,
   2546     "distance between visible and visible should be zero");
   2547  is(get_distance(prop, "hidden", "hidden"), 0,
   2548     "distance between hidden and hidden should be zero");
   2549  is(get_distance(prop, "collapse", "collapse"), 0,
   2550     "distance between collapse and collapse should be zero");
   2551 
   2552  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   2553  do_negative_test(prop, "visible", "hidden", true);
   2554  do_negative_test(prop, "hidden", "visible", true);
   2555  do_negative_test(prop, "hidden", "collapse", false);
   2556  do_negative_test(prop, "collapse", "hidden", false);
   2557  do_negative_test(prop, "visible", "collapse", true);
   2558  do_negative_test(prop, "collapse", "visible", true);
   2559 
   2560  div.style.setProperty("transition-delay", "-3s", "");
   2561  div.style.setProperty("transition-timing-function", FUNC_OVERONE, "");
   2562  do_overone_test(prop, "visible", "hidden");
   2563  do_overone_test(prop, "hidden", "visible");
   2564  do_overone_test(prop, "hidden", "collapse");
   2565  do_overone_test(prop, "collapse", "hidden");
   2566  do_overone_test(prop, "visible", "collapse");
   2567  do_overone_test(prop, "collapse", "visible");
   2568 
   2569  div.style.setProperty("transition-delay", "-1s", "");
   2570  div.style.setProperty("transition-timing-function", "linear", "");
   2571 }
   2572 
   2573 function test_content_visibility_transition(prop) {
   2574  do_test(prop, "visible", "hidden", "visible");
   2575  do_test(prop, "hidden", "visible", "visible");
   2576  do_test(prop, "hidden", "auto", "auto");
   2577  do_test(prop, "auto", "hidden", "auto");
   2578  do_test(prop, "visible", "auto", "auto"); /* not interpolable */
   2579  do_test(prop, "auto", "visible", "visible"); /* not interpolable */
   2580 
   2581  isnot(get_distance(prop, "visible", "hidden"), 0,
   2582        "distance between visible and hidden should not be zero");
   2583  isnot(get_distance(prop, "auto", "hidden"), 0,
   2584        "distance between auto and hidden should not be zero");
   2585  is(get_distance(prop, "visible", "visible"), 0,
   2586     "distance between visible and visible should be zero");
   2587  is(get_distance(prop, "hidden", "hidden"), 0,
   2588     "distance between hidden and hidden should be zero");
   2589  is(get_distance(prop, "auto", "auto"), 0,
   2590     "distance between auto and auto should be zero");
   2591 
   2592  div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   2593  do_negative_test(prop, "visible", "hidden", true);
   2594  do_negative_test(prop, "hidden", "visible", true);
   2595  do_negative_test(prop, "hidden", "auto", true);
   2596  do_negative_test(prop, "auto", "hidden", true);
   2597  do_negative_test(prop, "visible", "auto", false);
   2598  do_negative_test(prop, "auto", "visible", false);
   2599 
   2600  div.style.setProperty("transition-delay", "-3s", "");
   2601  div.style.setProperty("transition-timing-function", FUNC_OVERONE, "");
   2602  do_overone_test(prop, "visible", "hidden");
   2603  do_overone_test(prop, "hidden", "visible");
   2604  do_overone_test(prop, "hidden", "auto");
   2605  do_overone_test(prop, "auto", "hidden");
   2606  do_overone_test(prop, "visible", "auto");
   2607  do_overone_test(prop, "auto", "visible");
   2608 
   2609  div.style.setProperty("transition-delay", "-1s", "");
   2610  div.style.setProperty("transition-timing-function", "linear", "");
   2611 }
   2612 
   2613 function test_background_size_transition(prop) {
   2614  div.style.setProperty("transition-property", "none", "");
   2615  div.style.setProperty(prop, "50% 80%", "");
   2616  is(cs.getPropertyValue(prop), "50% 80%",
   2617     "property " + prop + ": computed value before transition");
   2618  div.style.setProperty("transition-property", prop, "");
   2619  div.style.setProperty(prop, "100% 100%", "");
   2620  is(cs.getPropertyValue(prop), "62.5% 85%",
   2621     "property " + prop + ": interpolation of percents");
   2622  check_distance(prop, "50% 80%", "62.5% 85%", "100% 100%");
   2623  div.style.setProperty(prop, "contain", "");
   2624  is(cs.getPropertyValue(prop), "contain",
   2625     "property " + prop + ": can't interpolate 'contain'");
   2626  test_background_position_size_common(prop, true, true);
   2627 }
   2628 
   2629 function test_background_position_transition(prop) {
   2630  var doesPropTakeListValues = (prop == "background-position") ||
   2631                               (prop == "mask-position");
   2632  var doesPropHaveDistanceComputation = (prop != "background-position") &&
   2633                                        (prop != "mask-position");
   2634 
   2635  // Test interpolation between edge keywords, and between edge keyword and a
   2636  // percent value. (Note: edge keywords are really aliases for percent vals.)
   2637  div.style.setProperty("transition-property", "none", "");
   2638  div.style.setProperty(prop, "center 80%", "");
   2639  is(cs.getPropertyValue(prop), "50% 80%",
   2640     "property " + prop + ": computed value before transition");
   2641  div.style.setProperty("transition-property", prop, "");
   2642  div.style.setProperty(prop, "bottom right", "");
   2643  is(cs.getPropertyValue(prop), "62.5% 85%",
   2644     "property " + prop + ": interpolation of edge keywords & percents");
   2645  if (doesPropHaveDistanceComputation) {
   2646    check_distance(prop, "center 80%", "62.5% 85%", "bottom right");
   2647  }
   2648 
   2649  // Test interpolation between edge keyword *with an offset* and non-keyword
   2650  // values.
   2651  div.style.setProperty("transition-property", "none", "");
   2652  div.style.setProperty(prop, "right 20px bottom 30%", "");
   2653  is(cs.getPropertyValue(prop), "calc(100% - 20px) 70%",
   2654     "property " + prop + ": computed value before transition");
   2655  div.style.setProperty("transition-property", prop, "");
   2656  div.style.setProperty(prop, "calc(40px + 20%) calc(12px + 30%)", "");
   2657  is(cs.getPropertyValue(prop), "calc(80% - 5px) calc(60% + 3px)",
   2658     "property " + prop + ": interpolation of edge keywords w/ offsets & calc");
   2659  if (doesPropHaveDistanceComputation) {
   2660    check_distance(prop, "right 20px bottom 30%",
   2661                         "calc(-5px + 80%) calc(3px + 60%)",
   2662                         "calc(40px + 20%) calc(12px + 30%)");
   2663  }
   2664 
   2665  test_background_position_size_common(prop, doesPropTakeListValues,
   2666                                       doesPropHaveDistanceComputation);
   2667 }
   2668 
   2669 function test_background_position_coord_transition(prop) {
   2670  var endEdge = prop.endsWith("-x") ? "right" : "bottom";
   2671 
   2672  // Test interpolation between edge keywords, and between edge keyword and a
   2673  // percent value. (Note: edge keywords are really aliases for percent vals.)
   2674  div.style.setProperty("transition-property", "none", "");
   2675  div.style.setProperty(prop, "center", "");
   2676  is(cs.getPropertyValue(prop), "50%",
   2677     "property " + prop + ": computed value before transition");
   2678  div.style.setProperty("transition-property", prop, "");
   2679  div.style.setProperty(prop, endEdge, "");
   2680  is(cs.getPropertyValue(prop), "62.5%",
   2681     "property " + prop + ": interpolation of edge keywords & percents");
   2682  check_distance(prop, "center", "62.5%", endEdge);
   2683 
   2684  // Test interpolation between edge keyword *with an offset* and non-keyword
   2685  // values.
   2686  div.style.setProperty("transition-property", "none", "");
   2687  div.style.setProperty(prop, `${endEdge} 20px`, "");
   2688  is(cs.getPropertyValue(prop), "calc(100% - 20px)",
   2689     "property " + prop + ": computed value before transition");
   2690  div.style.setProperty("transition-property", prop, "");
   2691  div.style.setProperty(prop, "calc(40px + 20%)", "");
   2692  is(cs.getPropertyValue(prop), "calc(80% - 5px)",
   2693     "property " + prop + ": interpolation of edge keywords w/ offsets & calc");
   2694  check_distance(prop, `${endEdge} 20px`,
   2695                       "calc(-5px + 80%)",
   2696                       "calc(40px + 20%)");
   2697 
   2698  div.style.setProperty("transition-property", "none", "");
   2699  div.style.setProperty(prop, "10px, 50px, 30px", "");
   2700  is(cs.getPropertyValue(prop), "10px, 50px, 30px",
   2701     "property " + prop + ": computed value before transition");
   2702  div.style.setProperty("transition-property", prop, "");
   2703  div.style.setProperty(prop, "50px, 70px, 30px", "");
   2704  is(cs.getPropertyValue(prop), "20px, 55px, 30px",
   2705     "property " + prop + ": interpolation of lists of lengths");
   2706  check_distance(prop, "10px, 50px, 30px",
   2707                       "20px, 55px, 30px",
   2708                       "50px, 70px, 30px");
   2709 
   2710  div.style.setProperty("transition-property", "none", "");
   2711  div.style.setProperty(prop, "10px, 50%, 30%, 5px", "");
   2712  is(cs.getPropertyValue(prop), "10px, 50%, 30%, 5px",
   2713     "property " + prop + ": computed value before transition");
   2714  div.style.setProperty("transition-property", prop, "");
   2715  div.style.setProperty(prop, "50px, 70%, 30%, 25px", "");
   2716  is(cs.getPropertyValue(prop), "20px, 55%, 30%, 10px",
   2717     "property " + prop + ": interpolation of lists of lengths and percents");
   2718  check_distance(prop, "10px, 50%, 30%, 5px",
   2719                       "20px, 55%, 30%, 10px",
   2720                       "50px, 70%, 30%, 25px");
   2721 
   2722  div.style.setProperty("transition-property", "none", "");
   2723  div.style.setProperty(prop, "20%, 8px", "");
   2724  is(cs.getPropertyValue(prop), "20%, 8px",
   2725     "property " + prop + ": computed value before transition");
   2726  div.style.setProperty("transition-property", prop, "");
   2727  div.style.setProperty(prop, "12px, 40%", "");
   2728  is(cs.getPropertyValue(prop), "calc(15% + 3px), calc(10% + 6px)",
   2729     "property " + prop + ": interpolation that computes to calc()");
   2730  check_distance(prop, "20%, 8px",
   2731                       "calc(3px + 15%), calc(6px + 10%)",
   2732                       "12px, 40%");
   2733 
   2734  div.style.setProperty("transition-property", "none", "");
   2735  div.style.setProperty(prop, "calc(20% + 40px), 8px, calc(20px + 12%)", "");
   2736  is(cs.getPropertyValue(prop), "calc(20% + 40px), 8px, calc(12% + 20px)",
   2737     "property " + prop + ": computed value before transition");
   2738  div.style.setProperty("transition-property", prop, "");
   2739  div.style.setProperty(prop, "12px, calc(20%), calc(8px + 20%)", "");
   2740  is(cs.getPropertyValue(prop), "calc(15% + 33px), calc(5% + 6px), calc(14% + 17px)",
   2741     "property " + prop + ": interpolation that computes to calc()");
   2742  check_distance(prop, "calc(20% + 40px), 8px, calc(20px + 12%)",
   2743                       "calc(33px + 15%), calc(6px + 5%), calc(17px + 14%)",
   2744                       "12px, calc(20%), calc(8px + 20%)");
   2745 }
   2746 
   2747 /**
   2748 * Common tests for 'background-position', 'background-size', and other
   2749 * properties that take CSS value-type 'position' or 'bg-size'.
   2750 *
   2751 *  @param prop The name of the property
   2752 *  @param doesPropTakeListValues
   2753 *         If false, the property is assumed to just take a single 'position' or
   2754 *         'bg-size' value. If true, the property is assumed to also accept
   2755 *         comma-separated list of such values.
   2756 */
   2757 function test_background_position_size_common(prop, doesPropTakeListValues,
   2758                                              doesPropHaveDistanceComputation) {
   2759  // Test non-list values
   2760  div.style.setProperty("transition-property", "none", "");
   2761  div.style.setProperty(prop, "40% 0%", "");
   2762  is(cs.getPropertyValue(prop), "40% 0%",
   2763     "property " + prop + ": computed value before transition");
   2764  div.style.setProperty("transition-property", prop, "");
   2765  div.style.setProperty(prop, "0% 0%", "");
   2766  is(cs.getPropertyValue(prop), "30% 0%",
   2767     "property " + prop + ": interpolation of percentages");
   2768  if (doesPropHaveDistanceComputation) {
   2769    check_distance(prop, "40% 0%", "30% 0%", "0% 0%");
   2770  }
   2771 
   2772  div.style.setProperty("transition-property", "none", "");
   2773  div.style.setProperty(prop, "0% 40%", "");
   2774  is(cs.getPropertyValue(prop), "0% 40%",
   2775     "property " + prop + ": computed value before transition");
   2776  div.style.setProperty("transition-property", prop, "");
   2777  div.style.setProperty(prop, "0% 0%", "");
   2778  is(cs.getPropertyValue(prop), "0% 30%",
   2779     "property " + prop + ": interpolation of percentages");
   2780  if (doesPropHaveDistanceComputation) {
   2781    check_distance(prop, "0% 40%", "0% 30%", "0% 0%");
   2782  }
   2783 
   2784  div.style.setProperty("transition-property", "none", "");
   2785  div.style.setProperty(prop, "10px 40px", "");
   2786  is(cs.getPropertyValue(prop), "10px 40px",
   2787     "property " + prop + ": computed value before transition");
   2788  div.style.setProperty("transition-property", prop, "");
   2789  div.style.setProperty(prop, "50px 0", "");
   2790  is(cs.getPropertyValue(prop), "20px 30px",
   2791     "property " + prop + ": interpolation of lengths");
   2792  if (doesPropHaveDistanceComputation) {
   2793    check_distance(prop, "10px 40px", "20px 30px", "50px 0");
   2794  }
   2795 
   2796  // Test interpolation that computes to to calc() (transition from % to px)
   2797  div.style.setProperty("transition-property", "none", "");
   2798  div.style.setProperty(prop, "20% 40%", "");
   2799  is(cs.getPropertyValue(prop), "20% 40%",
   2800     "property " + prop + ": computed value before transition");
   2801  div.style.setProperty("transition-property", prop, "");
   2802  div.style.setProperty(prop, "12px 20px", "");
   2803  is(cs.getPropertyValue(prop),
   2804     "calc(15% + 3px) calc(30% + 5px)",
   2805     "property " + prop + ": interpolation that computes to calc()");
   2806  if (doesPropHaveDistanceComputation) {
   2807    check_distance(prop, "20% 40%",
   2808                         "calc(3px + 15%) calc(5px + 30%)",
   2809                         "12px 20px");
   2810  }
   2811 
   2812  // Test interpolation that computes to to calc() (transition from px to %)
   2813  div.style.setProperty("transition-property", "none", "");
   2814  div.style.setProperty(prop, "12px 20px", "");
   2815  is(cs.getPropertyValue(prop), "12px 20px",
   2816     "property " + prop + ": computed value before transition");
   2817  div.style.setProperty("transition-property", prop, "");
   2818  div.style.setProperty(prop, "20% 40%", "");
   2819  is(cs.getPropertyValue(prop),
   2820     "calc(5% + 9px) calc(10% + 15px)",
   2821     "property " + prop + ": interpolation that computes to calc()");
   2822  if (doesPropHaveDistanceComputation) {
   2823    check_distance(prop, "12px 20px",
   2824                         "calc(9px + 5%) calc(15px + 10%)",
   2825                         "20% 40%");
   2826  }
   2827 
   2828  // Test interpolation between calc() and non-calc()
   2829  div.style.setProperty("transition-property", "none", "");
   2830  div.style.setProperty(prop, "calc(40px + 10%) 16px", "");
   2831  is(cs.getPropertyValue(prop), "calc(10% + 40px) 16px",
   2832     "property " + prop + ": computed value before transition");
   2833  div.style.setProperty("transition-property", prop, "");
   2834  div.style.setProperty(prop, "30% calc(8px + 60%)", "");
   2835  is(cs.getPropertyValue(prop), "calc(15% + 30px) calc(15% + 14px)",
   2836     "property " + prop + ": interpolation between calc() and non-calc()");
   2837  if (doesPropHaveDistanceComputation) {
   2838    check_distance(prop, "calc(40px + 10%) 16px",
   2839                         "calc(30px + 15%) calc(14px + 15%)",
   2840                         "30% calc(8px + 60%)");
   2841  }
   2842 
   2843  // Test list values, if appropriate
   2844  if (doesPropTakeListValues) {
   2845    div.style.setProperty("transition-property", "none", "");
   2846    div.style.setProperty(prop, "10px 40px, 50px 50px, 30px 20px", "");
   2847    is(cs.getPropertyValue(prop), "10px 40px, 50px 50px, 30px 20px",
   2848       "property " + prop + ": computed value before transition");
   2849    div.style.setProperty("transition-property", prop, "");
   2850    div.style.setProperty(prop, "50px 20px, 70px 50px, 30px 40px", "");
   2851    is(cs.getPropertyValue(prop), "20px 35px, 55px 50px, 30px 25px",
   2852       "property " + prop + ": interpolation of lists of lengths");
   2853    if (doesPropHaveDistanceComputation) {
   2854      check_distance(prop, "10px 40px, 50px 50px, 30px 20px",
   2855                           "20px 35px, 55px 50px, 30px 25px",
   2856                           "50px 20px, 70px 50px, 30px 40px");
   2857    }
   2858    div.style.setProperty("transition-property", "none", "");
   2859    div.style.setProperty(prop, "10px 40%, 50% 50px, 30% 20%, 5px 10px", "");
   2860    is(cs.getPropertyValue(prop), "10px 40%, 50% 50px, 30% 20%, 5px 10px",
   2861       "property " + prop + ": computed value before transition");
   2862    div.style.setProperty("transition-property", prop, "");
   2863    div.style.setProperty(prop, "50px 20%, 70% 50px, 30% 40%, 25px 50px", "");
   2864    is(cs.getPropertyValue(prop), "20px 35%, 55% 50px, 30% 25%, 10px 20px",
   2865       "property " + prop + ": interpolation of lists of lengths and percents");
   2866    if (doesPropHaveDistanceComputation) {
   2867      check_distance(prop, "10px 40%, 50% 50px, 30% 20%, 5px 10px",
   2868                           "20px 35%, 55% 50px, 30% 25%, 10px 20px",
   2869                           "50px 20%, 70% 50px, 30% 40%, 25px 50px");
   2870    }
   2871    div.style.setProperty("transition-property", "none", "");
   2872    div.style.setProperty(prop, "20% 40%, 8px 12px", "");
   2873    is(cs.getPropertyValue(prop), "20% 40%, 8px 12px",
   2874       "property " + prop + ": computed value before transition");
   2875    div.style.setProperty("transition-property", prop, "");
   2876    div.style.setProperty(prop, "12px 20px, 40% 16%", "");
   2877    is(cs.getPropertyValue(prop),
   2878       "calc(15% + 3px) calc(30% + 5px), calc(10% + 6px) calc(4% + 9px)",
   2879       "property " + prop + ": interpolation that computes to calc()");
   2880    if (doesPropHaveDistanceComputation) {
   2881      check_distance(prop, "20% 40%, 8px 12px",
   2882                           "calc(3px + 15%) calc(5px + 30%), calc(6px + 10%) calc(9px + 4%)",
   2883                           "12px 20px, 40% 16%");
   2884    }
   2885    div.style.setProperty("transition-property", "none", "");
   2886    div.style.setProperty(prop, "calc(20% + 40px) calc(40px + 40%), 8px 12%, calc(20px + 12%) calc(24px + 8%)", "");
   2887    is(cs.getPropertyValue(prop),
   2888       "calc(20% + 40px) calc(40% + 40px), 8px 12%, calc(12% + 20px) calc(8% + 24px)",
   2889       "property " + prop + ": computed value before transition");
   2890    div.style.setProperty("transition-property", prop, "");
   2891    div.style.setProperty(prop, "12px 20%, calc(20%) calc(16px + 60%), calc(8px + 20%) calc(40px + 16%)", "");
   2892    is(cs.getPropertyValue(prop),
   2893        "calc(15% + 33px) calc(35% + 30px), calc(5% + 6px) calc(24% + 4px), calc(14% + 17px) calc(10% + 28px)",
   2894       "property " + prop + ": interpolation that computes to calc()");
   2895    if (doesPropHaveDistanceComputation) {
   2896      check_distance(prop, "calc(20% + 40px) calc(40px + 40%), 8px 12%, calc(20px + 12%) calc(24px + 8%)",
   2897                           "calc(33px + 15%) calc(30px + 35%), calc(6px + 5%) calc(4px + 24%), calc(17px + 14%) calc(28px + 10%)",
   2898                           "12px 20%, calc(20%) calc(16px + 60%), calc(8px + 20%) calc(40px + 16%)");
   2899    }
   2900  }
   2901 }
   2902 
   2903 function test_transform_transition(prop) {
   2904  if (div.style.zoom == "2") {
   2905    todo(false, "Resolved transforms don't get correctly un-zoomed in getComputedStyle, see bug 1909280");
   2906    return;
   2907  }
   2908  is(prop, "transform", "Unexpected transform property!  Test needs to be fixed");
   2909  var matrix_re = /^matrix\(([^,]*), ([^,]*), ([^,]*), ([^,]*), ([^,]*), ([^,]*)\)$/;
   2910  for (let i in transformTests) {
   2911    var test = transformTests[i];
   2912    if (!("expected" in test)) {
   2913      var v = test.expected_uncomputed;
   2914      if (v.match(matrix_re) && !test.force_compute) {
   2915        test.expected = v;
   2916      } else {
   2917        test.expected = computeMatrix(v);
   2918      }
   2919    }
   2920  }
   2921 
   2922  for (let i in transformTests) {
   2923    var test = transformTests[i];
   2924    div.style.setProperty("transition-property", "none", "");
   2925    div.style.setProperty(prop, test.start, "");
   2926    cs.getPropertyValue(prop);
   2927    div.style.setProperty("transition-property", prop, "");
   2928    div.style.setProperty(prop, test.end, "");
   2929    var actual = cs.getPropertyValue(prop);
   2930    if (!test.round_error_ok || actual == test.expected) {
   2931      // In most cases, we'll get an exact match, but in some cases
   2932      // there can be a small amount of rounding error.
   2933      is(actual, test.expected,
   2934         "interpolation of transitions: " + test.start + " to " + test.end);
   2935    } else {
   2936      function s(mat) {
   2937        return mat.match(matrix_re).slice(1,7);
   2938      }
   2939      var pass = true;
   2940      var actual_split = s(actual);
   2941      var expected_split = s(test.expected);
   2942      for (let j = 0; j < 6; ++j) {
   2943        // Allow differences of 1 at the sixth decimal place, and allow
   2944        // a drop extra for floating point error from the subtraction.
   2945        if (Math.abs(Number(actual_split[j]) - Number(expected_split[j])) >
   2946              0.0000011) {
   2947          pass = false;
   2948        }
   2949      }
   2950      ok(pass,
   2951         "interpolation of transitions: " + test.start + " to " + test.end +
   2952         ": " + actual + " should approximately equal " + test.expected);
   2953    }
   2954  }
   2955 
   2956  // FIXME: should perhaps test that no clamping occurs
   2957 
   2958  runOMTATest(runAsyncTests, SimpleTest.finish);
   2959 }
   2960 
   2961 function test_rotate_transition(prop) {
   2962  // One value: <angle>
   2963  test_angle_transition(prop);
   2964 
   2965  // With axis: <number> <number> <number> <angle>
   2966  //
   2967  // We don't test for interpolation of the numbers here since it's quite
   2968  // complicated and this is tested by the web-platform tests for this property.
   2969  // Now that we have web-platform tests for animation properties the main
   2970  // purpose of the tests in this file is to check that transitions run on the
   2971  // properties we expect them to.
   2972  div.style.transitionProperty = 'none';
   2973  div.style[prop] = '0 1 0 45deg';
   2974  is(cs[prop], 'y 45deg',
   2975     `rotate property ${prop}: computed value before transition`);
   2976  div.style.transitionProperty = prop;
   2977  div.style[prop] = '0 1 0 145deg';
   2978  is(cs[prop], 'y 70deg',
   2979     `rotate property ${prop}: interpolation of angles`);
   2980  check_distance(prop, '0 1 0 45deg', '0 1 0 70deg', '0 1 0 145deg');
   2981 }
   2982 
   2983 function test_scale_transition(prop) {
   2984  // One value: <number>
   2985  test_number_transition(prop);
   2986 
   2987  // Two values: <number> <number>
   2988  div.style.transitionProperty = 'none';
   2989  div.style[prop] = '10 20';
   2990  is(cs[prop], '10 20',
   2991     `number property ${prop}: computed value before transition`);
   2992  div.style.transitionProperty = prop;
   2993  div.style[prop] = '50 60';
   2994  is(cs[prop], '20 30', `number property ${prop}: interpolation of numbers`);
   2995  check_distance(prop, '10 20', '20 30', '50 60');
   2996 
   2997  // Three values: <number> <number> <number>
   2998  div.style.transitionProperty = 'none';
   2999  div.style[prop] = '10 20 30';
   3000  is(cs[prop], '10 20 30',
   3001     `number property ${prop}: computed value before transition`);
   3002  div.style.transitionProperty = prop;
   3003  div.style[prop] = '50 60 70';
   3004  is(cs[prop], '20 30 40', `number property ${prop}: interpolation of numbers`);
   3005  check_distance(prop, '10 20 30', '20 30 40', '50 60 70');
   3006 }
   3007 
   3008 function test_translate_transition(prop) {
   3009  // One value: <length-percentage>
   3010  test_length_transition(prop);
   3011  test_length_unclamped(prop);
   3012  test_percent_transition(prop);
   3013  test_percent_unclamped(prop);
   3014  test_calc_wrapped_calc_transition(prop);
   3015 
   3016  // Two values: <length-percentage> <length-percentage>
   3017  // Note: Cannot use test_length_percent_pair_transition(prop) because we
   3018  // don't resolve the percentage.
   3019  test_length_pair_transition(prop);
   3020 
   3021  div.style.setProperty("transition-property", "none", "");
   3022  div.style.setProperty(prop, "4px 50%", "");
   3023  is(cs.getPropertyValue(prop), "4px 50%",
   3024    `length-valued property ${prop}: computed value before transition`);
   3025  div.style.setProperty("transition-property", prop, "");
   3026  div.style.setProperty(prop, "12px 70%", "");
   3027  is(cs.getPropertyValue(prop), "6px 55%",
   3028    `length-valued property ${prop}: interpolation of lengths`);
   3029  check_distance(prop, "4px 50%", "6px 55%", "12px 70%");
   3030 
   3031  div.style.setProperty("transition-property", "none", "");
   3032  div.style.setProperty(prop, "4px 50%", "");
   3033  is(cs.getPropertyValue(prop), "4px 50%",
   3034    `length-valued property ${prop}: computed value before transition`);
   3035  div.style.setProperty("transition-property", prop, "");
   3036  div.style.setProperty(prop, "20% 20px", "");
   3037  is(cs.getPropertyValue(prop), "calc(5% + 3px) calc(37.5% + 5px)",
   3038    `length-valued property ${prop}: interpolation of lengths`);
   3039  check_distance(prop, "4px 50%", "calc(5% + 3px) calc(37.5% + 5px)",
   3040                 "20% 20px");
   3041  // We can't use test_length_percent_pair_unclamped here since
   3042  // it assumes that "0px 0px" is serialized as "0px 0px" but
   3043  // translate should serialize it as "0px".
   3044 
   3045  // Three values: <length-percentage> <length-percentage> <length>
   3046  div.style.transitionProperty = 'none';
   3047  div.style[prop] = '10px 200% 30px';
   3048  is(cs[prop], '10px 200% 30px',
   3049     `translate property ${prop}: computed value before transition`);
   3050  div.style.transitionProperty = prop;
   3051  div.style[prop] = '50px 600% 70px';
   3052  is(cs[prop], '20px 300% 40px',
   3053     `translate property ${prop}: interpolation of three values`);
   3054  check_distance(prop, '10px 20px 30px', '20px 30px 40px', '50px 60px 70px');
   3055 }
   3056 
   3057 function test_font_variations_transition(prop) {
   3058  is(prop, "font-variation-settings", "only designed for one property");
   3059 
   3060  div.style.setProperty("transition-property", "none", "");
   3061  div.style.setProperty(prop, "\"wght\" 0, \"wdth\" 1.5", "");
   3062  // Note that computed-style returns the tags in sorted order.
   3063  is(cs.getPropertyValue(prop), "\"wdth\" 1.5, \"wght\" 0",
   3064     "font-variation-settings property " + prop + ": computed value before transition");
   3065  div.style.setProperty("transition-property", prop, "");
   3066  div.style.setProperty(prop, "\"wght\" 2, \"wdth\" 0.5", "");
   3067  is(cs.getPropertyValue(prop), "\"wdth\" 1.25, \"wght\" 0.5",
   3068     "font-variation-settings property " + prop + ": interpolation of font-variation-settings");
   3069  check_distance(prop, "\"wght\" 0, \"wdth\" 1.5", "\"wght\" 0.5, \"wdth\" 1.25", "\"wght\" 2, \"wdth\" 0.5");
   3070 
   3071  div.style.setProperty("transition-property", "none", "");
   3072  div.style.setProperty(prop, "\"wght\" 2, \"wdth\" 0.5", "");
   3073  is(cs.getPropertyValue(prop), "\"wdth\" 0.5, \"wght\" 2",
   3074     "font-variation-settings property " + prop + ": computed value before transition");
   3075  div.style.setProperty("transition-property", prop, "");
   3076  div.style.setProperty(prop, "\"wght\" 0, \"wdth\" 1.5", "");
   3077  is(cs.getPropertyValue(prop), "\"wdth\" 0.75, \"wght\" 1.5",
   3078     "font-variation-settings property " + prop + ": interpolation of font-variation-settings");
   3079  check_distance(prop, "\"wght\" 2, \"wdth\" 0.5", "\"wght\" 1.5, \"wdth\" 0.75", "\"wght\" 0, \"wdth\" 1.5");
   3080 }
   3081 
   3082 function test_aspect_ratio_transition(prop) {
   3083  [
   3084    // No transition between auto and <ratio>.
   3085    { start: "auto", end: "1 / 1",
   3086      expected: "1 / 1" },
   3087    // No transition between auto && <ratio> and <ratio>.
   3088    { start: "auto 1 / 1", end: "1 / 1",
   3089      expected: "1 / 1" },
   3090    // No transition between auto && <ratio> and auto.
   3091    { start: "auto 1 / 1", end: "auto",
   3092      expected: "auto" },
   3093    { start: "1 / 2", end: "8 / 1",
   3094      expected: "1 / 1" },
   3095    { start: "auto 1 / 2", end: "auto 8 / 1",
   3096      expected: "auto 1 / 1" },
   3097  ].forEach(test => {
   3098    div.style.transitionProperty = 'none';
   3099    div.style[prop] = test.start;
   3100    is(cs[prop], test.start,
   3101       `aspect-ratio: computed value before transition`);
   3102    div.style.transitionProperty = prop;
   3103    div.style[prop] = test.end;
   3104    is(cs[prop], test.expected,
   3105       `aspect-ratio: interpolation of aspect-ratio`);
   3106    // We check distance only if there is a transition.
   3107    if (test.end != test.expected) {
   3108      check_distance(prop, test.start, test.expected, test.end);
   3109    }
   3110  });
   3111 }
   3112 
   3113 function test_auto_with_length_transition(prop) {
   3114  div.style.setProperty("transition-property", "none", "");
   3115  div.style.setProperty(prop, "auto 4px", "");
   3116  is(cs.getPropertyValue(prop), "auto 4px",
   3117     "auto+length-valued property " + prop + ": computed value before transition");
   3118  div.style.setProperty("transition-property", prop, "");
   3119  div.style.setProperty(prop, "auto 12px", "");
   3120  is(cs.getPropertyValue(prop), "auto 6px",
   3121     "auto+length-valued property " + prop + ": interpolation of lengths");
   3122  check_distance(prop, "auto 4px", "auto 6px", "auto 12px");
   3123 }
   3124 
   3125 var OMTAdiv;
   3126 var OMTACs;
   3127 
   3128 function prepareForOMTATest() {
   3129  if (OMTAdiv) {
   3130    OMTAdiv.remove();
   3131  }
   3132  OMTAdiv = document.createElement("div");
   3133  OMTAdiv.style = "height:100px; width:100px; background-color:blue;";
   3134  OMTAdiv.style.setProperty("transition-duration", "300s", "");
   3135  OMTAdiv.style.setProperty("transition-timing-function", "linear", "");
   3136  document.body.appendChild(OMTAdiv);
   3137 
   3138  OMTACs = getComputedStyle(OMTAdiv, "");
   3139 }
   3140 
   3141 function runAsyncTests() {
   3142  // These tests check the value on the compositor 2/3rds of the way through
   3143  // the transition.
   3144  // For the transform tests we simply compare the value on the compositor
   3145  // with the computed value, but for the opacity test we check the absolute
   3146  // value as well.
   3147  addAsyncTransformTests();
   3148  addAsyncOpacityTest();
   3149  addAsyncDelayTest();
   3150 
   3151  runAllAsyncAnimTests().then(function() {
   3152    OMTAdiv.style.removeProperty("transition");
   3153    SimpleTest.finish();
   3154  });
   3155 }
   3156 
   3157 function addAsyncTransformTests() {
   3158  transformTests.forEach(function(test) {
   3159    addAsyncAnimTest(function () { return runTransformTest(test); } );
   3160  });
   3161 }
   3162 
   3163 async function runTransformTest(test) {
   3164  prepareForOMTATest();
   3165 
   3166  OMTAdiv.style.setProperty("transition-property", "none", "");
   3167  OMTAdiv.style.setProperty("transform", test.start, "");
   3168  OMTACs.getPropertyValue("transform");
   3169  OMTAdiv.style.setProperty("transition-property", "transform", "");
   3170  OMTAdiv.style.setProperty("transform", test.end, "");
   3171  OMTACs.getPropertyValue("transform");
   3172  await waitForPaints();
   3173 
   3174  // If the start value produced a non-invertible matrix the layer won't be
   3175  // created yet so we need to force an extra sample.
   3176  if (!isTransformInvertible(test.start)) {
   3177    winUtils.advanceTimeAndRefresh(100000);
   3178    await waitForPaints();
   3179    winUtils.advanceTimeAndRefresh(100000);
   3180    await waitForPaints();
   3181  } else {
   3182    winUtils.advanceTimeAndRefresh(200000);
   3183    await waitForPaints();
   3184  }
   3185 
   3186  omta_is_approx(OMTAdiv, "transform", OMTACs.getPropertyValue("transform"),
   3187                 0.0001, RunningOn.Compositor,
   3188                 "compositor transform transition " +
   3189                 "from '" + test.start + "' " +
   3190                 "to '" + test.end + "' " +
   3191                 "at 2/3rds duration matches computed style");
   3192 }
   3193 
   3194 function addAsyncOpacityTest() {
   3195  addAsyncAnimTest(async function() {
   3196    prepareForOMTATest();
   3197 
   3198    OMTAdiv.style.setProperty("transition-property", "none", "");
   3199    OMTAdiv.style.setProperty("opacity", 0, "");
   3200    OMTACs.getPropertyValue("opacity");
   3201    OMTAdiv.style.setProperty("transition-property", "opacity", "");
   3202    OMTAdiv.style.setProperty("opacity", 1, "");
   3203    OMTACs.getPropertyValue("opacity");
   3204 
   3205    await waitForPaints();
   3206 
   3207    winUtils.advanceTimeAndRefresh(200000);
   3208 
   3209    omta_is_approx(OMTAdiv, "opacity", 2/3, 0.00001, RunningOn.Compositor,
   3210                   "compositor opacity transition at 2/3rds duration");
   3211  });
   3212 }
   3213 
   3214 function addAsyncDelayTest() {
   3215  addAsyncAnimTest(async function() {
   3216    prepareForOMTATest();
   3217 
   3218    OMTAdiv.style.setProperty("transition-property", "none", "");
   3219    OMTAdiv.style.setProperty("transition-delay", "100s", "");
   3220    OMTAdiv.style.setProperty("transition-duration", "200s", "");
   3221    OMTAdiv.style.setProperty("transform", "", "");
   3222    OMTACs.getPropertyValue("transform");
   3223    OMTAdiv.style.setProperty("transition-property", "transform", "");
   3224    OMTAdiv.style.setProperty("transform", "translate(100px)", "");
   3225    OMTACs.getPropertyValue("transform");
   3226 
   3227    winUtils.advanceTimeAndRefresh(200000);
   3228    await waitForPaints();
   3229 
   3230    omta_is_approx(OMTAdiv, "transform", { tx: 50 }, 0.0001,
   3231                   RunningOn.Compositor,
   3232                   "compositor transform transition with delay at 1/2"
   3233                   + " duration");
   3234  });
   3235 }
   3236 
   3237 function isTransformInvertible(transformStr) {
   3238  var computedStr = transformStrToComputedStr(transformStr);
   3239  if (!transformStr)
   3240    return false;
   3241  var matrix = convertTo3dMatrix(computedStr);
   3242  if (matrix === null)
   3243    return false;
   3244  return isInvertible(matrix);
   3245 }
   3246 
   3247 function transformStrToComputedStr(transformStr) {
   3248  var div = document.createElement("div");
   3249  div.style.transform = transformStr;
   3250  return window.getComputedStyle(div).transform;
   3251 }
   3252 </script>
   3253 </pre>
   3254 </body>
   3255 </html>