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>