gecko/layout/style/test/test_transitions_per_property.html
Brian Birtles 6b9d68b1ff Bug 1026323 - Add a test for a transition with a delay running on the compositor; r=dzbarsky
This patch adds a test for transitions with a delay that run on the compositor.
Currently animations (including transitions) are not sent to the compositor
until they reach the end of their delay phase introducing the possibility that
the behavior might differ for animations with or without delays.

This patch adds a simple test for a transition with a delay. It also fixes an
existing bug in the opacity test. Also, it moves the step where the "transition"
property is removed to the end of the test sequence rather than the end of the
opacity test (which previously happened to occur at the end of the test
sequence).
2014-06-23 14:10:19 +09:00

2048 lines
92 KiB
HTML

<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=435441
-->
<head>
<title>Test for Bug 435441</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="property_database.js"></script>
<script type="text/javascript" src="animation_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style type="text/css">
#display > p { margin-top: 0; margin-bottom: 0; }
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=435441">Mozilla Bug 435441</a>
<!--
fixed-height container so percentage heights compute to different
(i.e., nonzero) values
fixed-width container so that percentages for margin-top and
margin-bottom are all relative to the same size container (rather than
one that depends on whether we're tall enough to need a scrollbar)
Use a 20px font size and line-height so that percentage line-height
and vertical-align doesn't accumulate rounding error.
-->
<div style="height: 50px; width: 300px; font-size: 20px; line-height: 20px">
<div id="display">
</div>
<div id="transformTest" style="height:100px; width:200px; background-color:blue;">
</div>
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 435441 **/
SimpleTest.requestLongerTimeout(2);
SimpleTest.waitForExplicitFinish();
function has_num(str)
{
return !!String(str).match(/^([\d.]+)/);
}
function any_unit_to_num(str)
{
return Number(String(str).match(/^([\d.]+)/)[1]);
}
var FUNC_NEGATIVE = "cubic-bezier(0.25, -2, 0.75, 1)";
var FUNC_OVERONE = "cubic-bezier(0.25, 0, 0.75, 3)";
var supported_properties = {
"border-bottom-left-radius": [ test_radius_transition ],
"border-bottom-right-radius": [ test_radius_transition ],
"border-top-left-radius": [ test_radius_transition ],
"border-top-right-radius": [ test_radius_transition ],
"-moz-box-flex": [ test_float_zeroToOne_transition,
test_float_aboveOne_transition,
test_float_zeroToOne_clamped ],
"box-shadow": [ test_shadow_transition ],
"-moz-column-count": [ test_pos_integer_or_auto_transition,
test_integer_at_least_one_clamping ],
"-moz-column-gap": [ test_length_transition,
test_length_clamped ],
"-moz-column-rule-color": [ test_color_transition,
test_border_color_transition ],
"-moz-column-rule-width": [ test_length_transition,
test_length_clamped ],
"-moz-column-width": [ test_length_transition,
test_length_clamped ],
"-moz-image-region": [ test_rect_transition ],
"-moz-outline-radius-bottomleft": [ test_radius_transition ],
"-moz-outline-radius-bottomright": [ test_radius_transition ],
"-moz-outline-radius-topleft": [ test_radius_transition ],
"-moz-outline-radius-topright": [ test_radius_transition ],
"-moz-text-decoration-color": [ test_color_transition,
test_border_color_transition ],
"background-color": [ test_color_transition ],
"background-position": [ test_background_position_transition,
// FIXME: We don't currently test clamping,
// since background-position uses calc() as
// an intermediate form.
/* test_length_percent_pair_unclamped */ ],
"background-size": [ test_background_size_transition,
// FIXME: We don't currently test clamping,
// since background-size uses calc() as an
// intermediate form.
/* test_length_percent_pair_clamped */ ],
"border-bottom-color": [ test_color_transition,
test_border_color_transition ],
"border-bottom-width": [ test_length_transition,
test_length_clamped ],
"border-left-color": [ test_color_transition,
test_border_color_transition ],
"border-left-width": [ test_length_transition,
test_length_clamped ],
"border-right-color": [ test_color_transition,
test_border_color_transition ],
"border-right-width": [ test_length_transition,
test_length_clamped ],
"border-spacing": [ test_length_pair_transition,
test_length_pair_transition_clamped ],
"border-top-color": [ test_color_transition,
test_border_color_transition ],
"border-top-width": [ test_length_transition,
test_length_clamped ],
"bottom": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_unclamped, test_percent_unclamped ],
"clip": [ test_rect_transition ],
"color": [ test_color_transition ],
"fill": [ test_color_transition ],
"fill-opacity" : [ test_float_zeroToOne_transition,
// opacity is clamped in computed style
// (not parsing/interpolation)
test_float_zeroToOne_clamped ],
"filter" : [ test_filter_transition ],
"flex-basis": [ test_length_transition, test_percent_transition,
test_length_clamped, test_percent_clamped ],
"flex-grow": [ test_float_zeroToOne_transition,
test_float_aboveOne_transition ],
"flex-shrink": [ test_float_zeroToOne_transition,
test_float_aboveOne_transition ],
"flood-color": [ test_color_transition ],
"flood-opacity" : [ test_float_zeroToOne_transition,
// opacity is clamped in computed style
// (not parsing/interpolation)
test_float_zeroToOne_clamped ],
"font-size": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_clamped, test_percent_clamped ],
"font-size-adjust": [ test_float_zeroToOne_transition,
test_float_aboveOne_transition,
/* FIXME: font-size-adjust treats zero specially */
/* test_float_zeroToOne_clamped */ ],
"font-stretch": [ test_font_stretch ],
"font-weight": [ test_font_weight ],
"height": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_clamped, test_percent_clamped ],
"left": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_unclamped, test_percent_unclamped ],
"letter-spacing": [ test_length_transition, test_length_unclamped ],
"lighting-color": [ test_color_transition ],
// NOTE: when calc() is supported on 'line-height', we should add
// test_length_percent_calc_transition.
"line-height": [ test_length_transition, test_percent_transition,
test_length_clamped, test_percent_clamped ],
"margin-bottom": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_unclamped, test_percent_unclamped ],
"margin-left": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_unclamped, test_percent_unclamped ],
"margin-right": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_unclamped, test_percent_unclamped ],
"margin-top": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_unclamped, test_percent_unclamped ],
"marker-offset": [ test_length_transition,
test_length_unclamped ],
"max-height": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_clamped, test_percent_clamped ],
"max-width": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_clamped, test_percent_clamped ],
"min-height": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_clamped, test_percent_clamped ],
"min-width": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_clamped, test_percent_clamped ],
"opacity" : [ test_float_zeroToOne_transition,
// opacity is clamped in computed style
// (not parsing/interpolation)
test_float_zeroToOne_clamped ],
"order": [ test_integer_transition ],
"outline-color": [ test_color_transition ],
"outline-offset": [ test_length_transition, test_length_unclamped ],
"outline-width": [ test_length_transition, test_length_clamped ],
"padding-bottom": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_clamped, test_percent_clamped ],
"padding-left": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_clamped, test_percent_clamped ],
"padding-right": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_clamped, test_percent_clamped ],
"padding-top": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_clamped, test_percent_clamped ],
"perspective": [ test_length_transition ],
"perspective-origin": [ test_length_pair_transition,
test_length_percent_pair_transition,
test_length_percent_pair_unclamped ],
"right": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_unclamped, test_percent_unclamped ],
"stop-color": [ test_color_transition ],
"stop-opacity" : [ test_float_zeroToOne_transition,
// opacity is clamped in computed style
// (not parsing/interpolation)
test_float_zeroToOne_clamped ],
"stroke": [ test_color_transition ],
"stroke-dasharray": [ test_dasharray_transition ],
// NOTE: when calc() is supported on 'stroke-dashoffset', we should
// add test_length_percent_calc_transition.
"stroke-dashoffset": [ test_length_transition, test_percent_transition,
test_length_unclamped, test_percent_unclamped ],
"stroke-miterlimit": [ test_float_aboveOne_transition,
test_float_aboveOne_clamped ],
"stroke-opacity" : [ test_float_zeroToOne_transition,
// opacity is clamped in computed style
// (not parsing/interpolation)
test_float_zeroToOne_clamped ],
// NOTE: when calc() is supported on 'stroke-width', we should add
// test_length_percent_calc_transition.
"stroke-width": [ test_length_transition, test_percent_transition,
test_length_clamped, test_percent_clamped ],
"text-indent": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_unclamped, test_percent_unclamped ],
"text-shadow": [ test_shadow_transition ],
"top": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_unclamped, test_percent_unclamped ],
"transform": [ test_transform_transition ],
"transform-origin": [ test_length_pair_transition,
test_length_percent_pair_transition,
test_length_percent_pair_unclamped ],
"vertical-align": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_unclamped, test_percent_unclamped ],
"visibility": [ test_visibility_transition ],
"width": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_clamped, test_percent_clamped ],
"word-spacing": [ test_length_transition, test_length_unclamped ],
"z-index": [ test_integer_transition, test_pos_integer_or_auto_transition ],
};
var div = document.getElementById("display");
var OMTAdiv = document.getElementById("transformTest");
var cs = getComputedStyle(div, "");
var OMTACs = getComputedStyle(OMTAdiv, "");
var winUtils = SpecialPowers.getDOMWindowUtils(window);
function computeMatrix(v) {
div.style.setProperty("transform", v, "");
var result = cs.getPropertyValue("transform");
div.style.removeProperty("transform");
return result;
}
var c_rot_15 = computeMatrix("rotate(15deg)");
is(c_rot_15.substring(0,6), "matrix", "should compute to matrix value");
var c_rot_60 = computeMatrix("rotate(60deg)");
is(c_rot_60.substring(0,6), "matrix", "should compute to matrix value");
var transformTests = [
// rotate
{ start: 'none', end: 'rotate(60deg)',
expected_uncomputed: 'rotate(15deg)',
expected: c_rot_15 },
{ start: 'rotate(0)', end: 'rotate(60deg)',
expected_uncomputed: 'rotate(15deg)',
expected: c_rot_15 },
{ start: 'rotate(0deg)', end: 'rotate(60deg)',
expected_uncomputed: 'rotate(15deg)',
expected: c_rot_15 },
{ start: 'none', end: c_rot_60,
expected: c_rot_15 },
{ start: 'none', end: 'rotate(360deg)',
expected_uncomputed: 'rotate(90deg)',
expected: computeMatrix('rotate(90deg)') },
{ start: 'none', end: 'rotatez(360deg)',
expected_uncomputed: 'rotate(90deg)',
expected: computeMatrix('rotate(90deg)') },
{ start: 'none', end: 'rotate(720deg)',
expected_uncomputed: 'rotate(180deg)',
expected: computeMatrix('rotate(180deg)') },
{ start: 'none', end: 'rotate(720deg)',
expected_uncomputed: 'rotatez(180deg)',
expected: computeMatrix('rotate(180deg)') },
{ start: 'none', end: 'rotate(1080deg)',
expected_uncomputed: 'rotate(270deg)',
expected: computeMatrix('rotate(270deg)') },
{ start: 'none', end: 'rotate(1080deg)',
expected_uncomputed: 'rotate(270deg)',
expected: computeMatrix('rotatez(270deg)') },
{ start: 'none', end: 'rotate(1440deg)',
expected_uncomputed: 'rotate(360deg)',
expected: computeMatrix('scale(1)'),
round_error_ok: true },
{ start: 'none', end: 'rotatey(60deg)',
expected_uncomputed: 'rotatey(15deg)',
expected: computeMatrix('rotatey(15deg)') },
{ start: 'none', end: 'rotatey(720deg)',
expected_uncomputed: 'rotatey(180deg)',
expected: computeMatrix('rotatey(180deg)') },
{ start: 'none', end: 'rotatex(60deg)',
expected_uncomputed: 'rotatex(15deg)',
expected: computeMatrix('rotatex(15deg)') },
{ start: 'none', end: 'rotatex(720deg)',
expected_uncomputed: 'rotatex(180deg)',
expected: computeMatrix('rotatex(180deg)') },
// translate
{ start: 'translate(20px)', end: 'none',
expected_uncomputed: 'translate(15px)',
expected: 'matrix(1, 0, 0, 1, 15, 0)' },
{ start: 'translate(20px, 12px)', end: 'none',
expected_uncomputed: 'translate(15px, 9px)',
expected: 'matrix(1, 0, 0, 1, 15, 9)' },
{ start: 'translateX(-20px)', end: 'none',
expected_uncomputed: 'translateX(-15px)',
expected: 'matrix(1, 0, 0, 1, -15, 0)' },
{ start: 'translateY(-40px)', end: 'none',
expected_uncomputed: 'translateY(-30px)',
expected: 'matrix(1, 0, 0, 1, 0, -30)' },
{ start: 'translateZ(40px)', end: 'none',
expected_uncomputed: 'translateZ(30px)',
expected: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 30, 1)' },
{ start: 'none', end: 'translate3D(40px, 60px, -40px)',
expected_uncomputed: 'translate3D(10px, 15px, -10px)',
expected: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 15, -10, 1)' },
// percentages are relative to 300px (width) and 50px (height)
// per the prerequisites in property_database.js
{ start: 'translate(20%)', end: 'none',
expected_uncomputed: 'translate(15%)',
expected: 'matrix(1, 0, 0, 1, 45, 0)',
round_error_ok: true },
{ start: 'translate(20%, 12%)', end: 'none',
expected_uncomputed: 'translate(15%, 9%)',
expected: 'matrix(1, 0, 0, 1, 45, 4.5)',
round_error_ok: true },
{ start: 'translateX(-20%)', end: 'none',
expected_uncomputed: 'translateX(-15%)',
expected: 'matrix(1, 0, 0, 1, -45, 0)',
round_error_ok: true },
{ start: 'translateY(-40%)', end: 'none',
expected_uncomputed: 'translateY(-30%)',
expected: 'matrix(1, 0, 0, 1, 0, -15)',
round_error_ok: true },
{ start: 'none', end: 'rotate(90deg) translate(20%, 20%) rotate(-90deg)',
expected_uncomputed: 'rotate(22.5deg) translate(5%, 5%) rotate(-22.5deg)',
round_error_ok: true },
{ start: 'none', end: 'rotate(-90deg) translate(20%, 20%) rotate(90deg)',
expected_uncomputed: 'rotate(-22.5deg) translate(5%, 5%) rotate(22.5deg)',
round_error_ok: true },
// test percent translation using matrix decomposition
{ start: 'rotate(45deg) rotate(-45deg)',
end: 'rotate(90deg) translate(20%, 20%) rotate(-90deg)',
expected: 'matrix(1, 0, 0, 1, -2.5, 15)',
round_error_ok: true },
{ start: 'rotate(45deg) rotate(-45deg)',
end: 'rotate(-90deg) translate(20%, 20%) rotate(90deg)',
expected: 'matrix(1, 0, 0, 1, 2.5, -15)',
round_error_ok: true },
// test calc() in translate
// Note that font-size: is 20px, and that percentages are relative
// to 300px (width) and 50px (height) per the prerequisites in
// property_database.js
{ start: 'translateX(20%)', /* 60px */
end: 'translateX(calc(10% + 1em))', /* 30px + 20px = 50px */
expected_uncomputed: 'translateX(calc(17.5% + 0.25em))',
expected: 'matrix(1, 0, 0, 1, 57.5, 0)' },
{ start: 'translate(calc(0.75 * 3em + 1.5 * 10%), calc(0.5 * 5em + 0.5 * 8%))', /* 90px, 52px */
end: 'rotate(90deg) translateY(20%) rotate(90deg) translateY(calc(10% + 0.5em)) rotate(180deg)', /* -10px, -15px */
expected: 'matrix(1, 0, 0, 1, 65, 35.25)' },
// scale
{ start: 'scale(2)', end: 'none',
expected_uncomputed: 'scale(1.75)',
expected: 'matrix(1.75, 0, 0, 1.75, 0, 0)' },
{ start: 'none', end: 'scale(0.4)',
expected_uncomputed: 'scale(0.85)',
expected: 'matrix(0.85, 0, 0, 0.85, 0, 0)',
round_error_ok: true },
{ start: 'scale(2)', end: 'scale(-2)',
expected_uncomputed: 'scale(1)',
expected: 'matrix(1, 0, 0, 1, 0, 0)' },
{ start: 'scale(2)', end: 'scale(-6)',
expected_uncomputed: 'scale(0)',
expected: 'matrix(0, 0, 0, 0, 0, 0)' },
{ start: 'scale(2, 0.4)', end: 'none',
expected_uncomputed: 'scale(1.75, 0.55)',
expected: 'matrix(1.75, 0, 0, 0.55, 0, 0)',
round_error_ok: true },
{ start: 'scaleX(3)', end: 'none',
expected_uncomputed: 'scaleX(2.5)',
expected: 'matrix(2.5, 0, 0, 1, 0, 0)' },
{ start: 'scaleY(5)', end: 'none',
expected_uncomputed: 'scaleY(4)',
expected: 'matrix(1, 0, 0, 4, 0, 0)' },
{ start: 'scaleZ(5)', end: 'none',
expected_uncomputed: 'scaleZ(4)',
expected: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1)' },
{ start: 'none', end: 'scale3D(5, 5, 5)',
expected_uncomputed: 'scale3D(2, 2, 2)',
expected: 'matrix3d(2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1)' },
// skew
{ start: 'skewX(45deg)', end: 'none',
expected_uncomputed: 'skewX(33.75deg)' },
{ start: 'skewY(45deg)', end: 'none',
expected_uncomputed: 'skewY(33.75deg)' },
{ start: 'skew(45deg)', end: 'none',
expected_uncomputed: 'skew(33.75deg)' },
{ start: 'skew(45deg, 45deg)', end: 'none',
expected_uncomputed: 'skew(33.75deg, 33.75deg)' },
{ start: 'skewX(45deg)', end: 'skewX(-45deg)',
expected_uncomputed: 'skewX(22.5deg)' },
{ start: 'skewX(0)', end: 'skewX(-45deg)',
expected_uncomputed: 'skewX(-11.25deg)' },
{ start: 'skewY(45deg)', end: 'skewY(-45deg)',
expected_uncomputed: 'skewY(22.5deg)' },
// matrix : skewX
{ start: 'matrix(1, 0, 3, 1, 0, 0)', end: 'none',
expected: 'matrix(1, 0, ' + 3 * 0.75 + ', 1, 0, 0)',
round_error_ok: true },
{ start: 'skewX(0)', end: 'skewX(-45deg) translate(0)',
expected: 'matrix(1, 0, -0.25, 1, 0, 0)',
round_error_ok: true },
// matrix : rotate
{ start: 'rotate(-30deg)', end: 'matrix(0, 1, -1, 0, 0, 0)',
expected: 'matrix(1, 0, 0, 1, 0, 0)',
round_error_ok: true },
{ start: 'rotate(-30deg) translateX(0)',
end: 'translateX(0) rotate(-90deg)',
expected: computeMatrix('rotate(-45deg)'),
round_error_ok: true },
// matrix decomposition of skewY
{ start: 'skewY(60deg)', end: 'skewY(-60deg) translateX(0)',
/* rotate(30deg) skewX(60deg)/2 scale(2, 0.5) */
expected: computeMatrix('rotate(30deg) skewX(' + Math.atan(Math.tan(Math.PI * 60/180) / 2) + 'rad) scale(2, 0.5)'),
round_error_ok: true },
// matrix decomposition
// Four pairs of the same matrix expressed different ways.
{ start: 'matrix(-1, 0, 0, -1, 0, 0)', /* rotate(180deg) */
end: 'matrix(1, 0, 0, 1, 0, 0)',
expected: computeMatrix('rotate(135deg)') },
{ start: 'scale(-1)', end: 'none',
expected_uncomputed: 'scale(-0.5)',
expected: 'matrix(-0.5, 0, 0, -0.5, 0, 0)' },
{ start: 'rotate(180deg)', end: 'none',
expected_uncomputed: 'rotate(135deg)' },
{ start: 'rotate(-180deg)', end: 'none',
expected_uncomputed: 'rotate(-135deg)',
expected: computeMatrix('rotate(225deg)') },
// matrix followed by scale
{ start: 'matrix(2, 0, 0, 2, 10, 20) scale(2)',
end: 'none',
expected: 'matrix(3.0625, 0, 0, 3.0625, 7.5, 15)' },
// ... and a bunch of similar possibilities. The spec isn't settled
// here; there are multiple options. See:
// http://lists.w3.org/Archives/Public/www-style/2010Jun/0602.html
{ start: 'matrix(-1, 0, 0, 1, 0, 0)', /* scaleX(-1) */
end: 'matrix(1, 0, 0, 1, 0, 0)',
expected: computeMatrix('scaleX(-0.5)') },
{ start: 'matrix(1, 0, 0, -1, 0, 0)', /* rotate(-180deg) scaleX(-1) */
end: 'matrix(1, 0, 0, 1, 0, 0)',
expected: computeMatrix('rotate(-135deg) scaleX(-0.5)') },
{ start: 'matrix(0, 1, 1, 0, 0, 0)', /* rotate(-90deg) scaleX(-1) */
end: 'matrix(1, 0, 0, 1, 0, 0)',
expected: computeMatrix('rotate(-67.5deg) scaleX(-0.5)') },
{ start: 'matrix(0, -1, 1, 0, 0, 0)', /* rotate(-90deg) */
end: 'matrix(1, 0, 0, 1, 0, 0)',
expected: computeMatrix('rotate(-67.5deg)') },
{ start: 'matrix(0, 1, -1, 0, 0, 0)', /* rotate(90deg) */
end: 'matrix(1, 0, 0, 1, 0, 0)',
expected: computeMatrix('rotate(67.5deg)') },
{ start: 'matrix(0, -1, -1, 0, 0, 0)', /* rotate(90deg) scaleX(-1) */
end: 'matrix(1, 0, 0, 1, 0, 0)',
expected: computeMatrix('rotate(67.5deg) scaleX(-0.5)') },
// Similar decomposition tests, but with skewX. I checked visually
// that the sign of the skew was correct by checking visually that
// the animations in
// http://dbaron.org/css/test/2010/transition-negative-determinant
// don't flip when they finish, and then wrote tests corresponding
// to the current code's behavior.
// ... start with four with positive determinants
{ start: 'none',
end: 'matrix(1, 0, 1.5, 1, 0, 0)',
/* skewX(atan(1.5)) */
expected: 'matrix(1, 0, ' + 1.5 * 0.25 + ', 1, 0, 0)',
round_error_ok: true },
{ start: 'none',
end: 'matrix(-1, 0, 2, -1, 0, 0)',
/* rotate(180deg) skewX(atan(-2)) */
expected: computeMatrix('rotate(45deg) matrix(1, 0, ' + -2 * 0.25 + ', 1, 0, 0)'),
round_error_ok: true },
{ start: 'none',
end: 'matrix(0, -1, 1, -3, 0, 0)',
/* rotate(-90deg) skewX(atan(3)) */
expected: computeMatrix('rotate(-22.5deg) matrix(1, 0, ' + 3 * 0.25 + ', 1, 0, 0)'),
round_error_ok: true },
{ start: 'none',
end: 'matrix(0, 1, -1, 4, 0, 0)',
/* rotate(90deg) skewX(atan(4)) */
expected: computeMatrix('rotate(22.5deg) matrix(1, 0, ' + 4 * 0.25 + ', 1, 0, 0)'),
round_error_ok: true },
// and then four with negative determinants
{ start: 'none',
end: 'matrix(1, 0, 1, -1, 0, 0)',
/* rotate(-180deg) skewX(atan(-1)) scaleX(-1) */
expected: computeMatrix('rotate(-45deg) matrix(1, 0, ' + -1 * 0.25 + ', 1, 0, 0) scaleX(0.5)'),
round_error_ok: true },
{ start: 'none',
end: 'matrix(-1, 0, -1, 1, 0, 0)',
/* skewX(atan(-1)) scaleX(-1) */
expected: computeMatrix('matrix(1, 0, ' + -1 * 0.25 + ', 1, 0, 0) scaleX(0.5)') },
{ start: 'none',
end: 'matrix(0, 1, 1, -2, 0, 0)',
/* rotate(-90deg) skewX(atan(2)) scaleX(-1) */
expected: computeMatrix('rotate(-22.5deg) matrix(1, 0, ' + 2 * 0.25 + ', 1, 0, 0) scaleX(0.5)'),
round_error_ok: true },
{ start: 'none',
end: 'matrix(0, -1, -1, 0.5, 0, 0)',
/* rotate(90deg) skewX(atan(0.5)) scaleX(-1) */
expected: computeMatrix('rotate(22.5deg) matrix(1, 0, ' + 0.5 * 0.25 + ', 1, 0, 0) scaleX(0.5)'),
round_error_ok: true },
// lists vs. matrix decomposition
{ start: 'translate(10px) skewY(45deg)',
end: 'translate(30px) skewY(-45deg)',
expected_uncomputed: 'translate(15px) skewY(22.5deg)' },
{ start: 'skewY(45deg) rotate(90deg)',
end: 'skewY(-45deg) rotate(90deg)',
expected_uncomputed: 'skewY(22.5deg) rotate(90deg)' },
{ start: 'skewY(45deg) rotate(90deg) translate(0)',
end: 'skewY(-45deg) rotate(90deg)',
expected: 'matrix(0, 1, -1, -0.5, 0, 0)',
round_error_ok: true },
{ start: 'skewX(45deg) rotate(90deg)',
end: 'skewX(-45deg) rotate(90deg)',
expected_uncomputed: 'skewX(22.5deg) rotate(90deg)' },
{ start: 'skewX(-60deg) rotate(90deg) translate(0)',
end: 'skewX(60deg) rotate(90deg)',
expected: computeMatrix('rotate(120deg) skewX(' + Math.atan(Math.tan(Math.PI * 60/180) / 2) + 'rad) scale(2, 0.5)'),
round_error_ok: true },
];
var filterTests = [
{ start: "none", end: "none",
expected: ["none"] },
// function from none (number/length)
{ start: "none", end: "brightness(0.5)",
expected: ["brightness", 0.875] },
{ start: "none", end: "contrast(0.5)",
expected: ["contrast", 0.875] },
{ start: "none", end: "grayscale(0.5)",
expected: ["grayscale", 0.125] },
{ start: "none", end: "invert(0.5)",
expected: ["invert", 0.125] },
{ start: "none", end: "opacity(0.5)",
expected: ["opacity", 0.875] },
{ start: "none", end: "saturate(0.5)",
expected: ["saturate", 0.875] },
{ start: "none", end: "sepia(0.5)",
expected: ["sepia", 0.125] },
{ start: "none", end: "blur(50px)",
expected: ["blur", 12.5] },
// function to none (number/length)
{ start: "brightness(0.5)", end: "none",
expected: ["brightness", 0.625] },
{ start: "contrast(0.5)", end: "none",
expected: ["contrast", 0.625] },
{ start: "grayscale(0.5)", end: "none",
expected: ["grayscale", 0.375] },
{ start: "invert(0.5)", end: "none",
expected: ["invert", 0.375] },
{ start: "opacity(0.5)", end: "none",
expected: ["opacity", 0.625] },
{ start: "saturate(0.5)", end: "none",
expected: ["saturate", 0.625] },
{ start: "sepia(0.5)", end: "none",
expected: ["sepia", 0.375] },
{ start: "blur(50px)", end: "none",
expected: ["blur", 37.5] },
// function to same function (number/length)
{ start: "brightness(0.25)", end: "brightness(0.75)",
expected: ["brightness", 0.375] },
{ start: "contrast(0.25)", end: "contrast(0.75)",
expected: ["contrast", 0.375] },
{ start: "grayscale(0.25)", end: "grayscale(0.75)",
expected: ["grayscale", 0.375] },
{ start: "invert(0.25)", end: "invert(0.75)",
expected: ["invert", 0.375] },
{ start: "opacity(0.25)", end: "opacity(0.75)",
expected: ["opacity", 0.375] },
{ start: "saturate(0.25)", end: "saturate(0.75)",
expected: ["saturate", 0.375] },
{ start: "sepia(0.25)", end: "sepia(0.75)",
expected: ["sepia", 0.375] },
{ start: "blur(25px)", end: "blur(75px)",
expected: ["blur", 37.5] },
// function to same function (percent)
{ start: "brightness(25%)", end: "brightness(75%)",
expected: ["brightness", 0.375] },
{ start: "contrast(25%)", end: "contrast(75%)",
expected: ["contrast", 0.375] },
{ start: "grayscale(25%)", end: "grayscale(75%)",
expected: ["grayscale", 0.375] },
{ start: "invert(25%)", end: "invert(75%)",
expected: ["invert", 0.375] },
{ start: "opacity(25%)", end: "opacity(75%)",
expected: ["opacity", 0.375] },
{ start: "saturate(25%)", end: "saturate(75%)",
expected: ["saturate", 0.375] },
{ start: "sepia(25%)", end: "sepia(75%)",
expected: ["sepia", 0.375] },
// function to same function (percent, number/length)
{ start: "brightness(0.25)", end: "brightness(75%)",
expected: ["brightness", 0.375] },
{ start: "contrast(25%)", end: "contrast(0.75)",
expected: ["contrast", 0.375] },
// hue-rotate with different angle values
{ start: "hue-rotate(0deg)", end: "hue-rotate(720deg)",
expected: ["hue-rotate", Math.PI.toFixed(5)] },
{ start: "hue-rotate(0rad)", end: "hue-rotate("+4*Math.PI+"rad)",
expected: ["hue-rotate", Math.PI.toFixed(5)] },
{ start: "hue-rotate(0grad)", end: "hue-rotate(800grad)",
expected: ["hue-rotate", Math.PI.toFixed(5)] },
{ start: "hue-rotate(0turn)", end: "hue-rotate(2turn)",
expected: ["hue-rotate", Math.PI.toFixed(5)] },
{ start: "hue-rotate(0deg)", end: "hue-rotate("+4*Math.PI+"rad)",
expected: ["hue-rotate", Math.PI.toFixed(5)] },
{ start: "hue-rotate(0turn)", end: "hue-rotate(800grad)",
expected: ["hue-rotate", Math.PI.toFixed(5)] },
{ start: "hue-rotate(0grad)", end: "hue-rotate("+4*Math.PI+"rad)",
expected: ["hue-rotate", Math.PI.toFixed(5)] },
{ start: "hue-rotate(0grad)", end: "hue-rotate(0turn)",
expected: ["hue-rotate", 0] },
// multiple matching functions, same length
{ start: "contrast(25%) brightness(0.25) blur(25px) sepia(75%)",
end: "contrast(75%) brightness(0.75) blur(75px) sepia(25%)",
expected: ["contrast", 0.375, "brightness", 0.375, "blur", 37.5, "sepia", 0.625] },
{ start: "invert(25%) brightness(0.25) blur(25px) invert(50%) brightness(0.5) blur(50px)",
end: "invert(75%) brightness(0.75) blur(75px)",
expected: ["invert", 0.375, "brightness", 0.375, "blur", 37.5, "invert", 0.375, "brightness", 0.625, "blur", 37.5] },
// multiple matching functions, different length
{ start: "contrast(25%) brightness(0.5) blur(50px)",
end: "contrast(75%)",
expected: ["contrast", 0.375, "brightness", 0.625, "blur", 37.5] },
// mismatching filter functions
{ start: "contrast(0%)", end: "blur(10px)",
expected: ["blur", 10] },
// not supported interpolations
{ start: "none", end: "url('#b')",
expected: ["url", "\""+document.URL+"#b\""] },
{ start: "url('#a')", end: "none",
expected: ["none"] },
{ start: "url('#a')", end: "url('#b')",
expected: ["url", "\""+document.URL+"#b\""] },
{ start: "url('#a')", end: "blur(10px)",
expected: ["blur", 10] },
{ start: "blur(10px)", end: "url('#a')",
expected: ["url", "\""+document.URL+"#a\""] },
{ start: "blur(0px) url('#a')", end: "blur(20px)",
expected: ["blur", 20] },
{ start: "blur(0px)", end: "blur(20px) url('#a')",
expected: ["blur", 20, "url", "\""+document.URL+"#a\""] },
{ start: "contrast(0.25) brightness(0.25) blur(25px)",
end: "contrast(0.75) url('#a')",
expected: ["contrast", 0.75, "url", "\""+document.URL+"#a\""] },
{ start: "contrast(0.25) brightness(0.25) blur(75px)",
end: "brightness(0.75) contrast(0.75) blur(25px)",
expected: ["brightness", 0.75, "contrast", 0.75, "blur", 25] },
{ start: "contrast(0.25) brightness(0.25) blur(25px)",
end: "contrast(0.75) brightness(0.75) contrast(0.75)",
expected: ["contrast", 0.75, "brightness", 0.75, "contrast", 0.75] },
// drop-shadow animation
{ start: "none",
end: "drop-shadow(rgb(0, 0, 0) 4px 4px 0px)",
expected: ["drop-shadow", "rgba(0, 0, 0, 0.25) 1px 1px 0px"] },
{ start: "drop-shadow(rgb(0, 0, 0) 0px 0px 0px)",
end: "drop-shadow(rgb(0, 0, 0) 4px 4px 0px)",
expected: ["drop-shadow", "rgb(0, 0, 0) 1px 1px 0px"] },
{ start: "drop-shadow(#038000 4px 4px)",
end: "drop-shadow(8px 8px 8px red)",
expected: ["drop-shadow", "rgb(66, 96, 0) 5px 5px 2px"] },
{ start: "blur(25px) drop-shadow(8px 8px)",
end: "blur(75px)",
expected: ["blur", 37.5, "drop-shadow", "rgb(0, 0, 0) 6px 6px 0px"] },
{ start: "blur(75px)",
end: "blur(25px) drop-shadow(8px 8px)",
expected: ["blur", 62.5, "drop-shadow", "rgb(0, 0, 0) 2px 2px 0px"] },
{ start: "drop-shadow(2px 2px blue)",
end: "none",
expected: ["drop-shadow", "rgba(0, 0, 255, 0.75) 1.5px 1.5px 0px"] },
];
var prop;
for (prop in supported_properties) {
// Test that prop is in the property database.
ok(prop in gCSSProperties, "property " + prop + " in gCSSProperties");
// Test that the entry has at least one test function.
ok(supported_properties[prop].length > 0,
"property " + prop + " must have at least one test function");
}
// Return a consistent sampling of |count| values out of |array|.
function sample_array(array, count) {
if (count <= 0) {
ok(false, "unexpected count");
return [];
}
var ratio = array.length / count;
if (ratio <= 1) {
return array;
}
var result = new Array(count);
for (var i = 0; i < count; ++i) {
result[i] = array[Math.floor(i * ratio)];
}
return result;
}
// Test that transitions don't do anything (i.e., aren't supported) on
// the properties not in our test list above (and not transition
// properties themselves).
for (prop in gCSSProperties) {
var info = gCSSProperties[prop];
if (!(prop in supported_properties) &&
info.type != CSS_TYPE_TRUE_SHORTHAND &&
!("alias_for" in info) &&
!prop.match(/^transition-/) &&
// FIXME (Bug 119078): THIS SHOULD REALLY NOT BE NEEDED!
prop != "-moz-binding") {
if ("prerequisites" in info) {
var prereqs = info.prerequisites;
for (var prereq in prereqs) {
div.style.setProperty(prereq, prereqs[prereq], "");
}
}
var all_values = info.initial_values.concat(info.other_values);
if (all_values.length > 50) {
// Since we're using an O(N^2) algorithm here, reduce the list of
// values that we want to test. (This test is really only testing
// that somebody didn't make a property animatable without
// modifying this test. The odds of somebody doing that without
// making at least one of the many pairs of values we have left
// animatable seems pretty low, at least relative to the chance
// that any pair of the values listed in property_database.js is
// animatable.)
//
// That said, we still try to use all of the start of the list on
// the assumption that the more basic values are likely to be at
// the beginning of the list.
all_values = [].concat(info.initial_values.slice(0,2),
sample_array(info.initial_values.slice(2), 6),
info.other_values.slice(0, 10),
sample_array(info.other_values.slice(10), 40));
}
var all_computed = [];
for (var idx in all_values) {
var val = all_values[idx];
div.style.setProperty(prop, val, "");
all_computed.push(cs.getPropertyValue(prop));
}
div.style.removeProperty(prop);
div.style.setProperty("transition", prop + " 20s linear", "");
for (var i = 0; i < all_values.length; ++i) {
for (var j = i + 1; j < all_values.length; ++j) {
div.style.setProperty(prop, all_values[i], "");
is(cs.getPropertyValue(prop), all_computed[i],
"transitions not supported for property " + prop +
" value " + all_values[i]);
div.style.setProperty(prop, all_values[j], "");
is(cs.getPropertyValue(prop), all_computed[j],
"transitions not supported for property " + prop +
" value " + all_values[j]);
}
}
div.style.removeProperty("transition");
div.style.removeProperty(prop);
if ("prerequisites" in info) {
var prereqs = info.prerequisites;
for (var prereq in prereqs) {
div.style.removeProperty(prereq);
}
}
}
}
// Do 4-second linear transitions with -1 second transition delay and
// linear timing function so that we can expect the transition to be
// one quarter of the way through the value space right after changing
// the property.
div.style.setProperty("transition-duration", "4s", "");
div.style.setProperty("transition-delay", "-1s", "");
div.style.setProperty("transition-timing-function", "linear", "");
for (prop in supported_properties) {
var tinfo = supported_properties[prop];
var info = gCSSProperties[prop];
isnot(info.type, CSS_TYPE_TRUE_SHORTHAND,
prop + " must not be a shorthand");
if ("prerequisites" in info) {
var prereqs = info.prerequisites;
for (var prereq in prereqs) {
// We don't want the 19px font-size prereq of line-height, since we
// want to leave it 20px.
if (prop != "line-height" || prereq != "font-size") {
div.style.setProperty(prereq, prereqs[prereq], "");
}
}
}
for (var idx in tinfo) {
tinfo[idx](prop);
}
// Make sure to unset the property and stop transitions on it.
div.style.setProperty("transition-property", "none", "");
div.style.removeProperty(prop);
cs.getPropertyValue(prop);
if ("prerequisites" in info) {
var prereqs = info.prerequisites;
for (var prereq in prereqs) {
div.style.removeProperty(prereq);
}
}
}
div.style.removeProperty("transition");
function get_distance(prop, v1, v2)
{
return SpecialPowers.DOMWindowUtils
.computeAnimationDistance(div, prop, v1, v2);
}
function check_distance(prop, start, quarter, end)
{
var sq = get_distance(prop, start, quarter);
var se = get_distance(prop, start, end);
var qe = get_distance(prop, quarter, end);
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 + "'");
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 + "'");
}
function test_length_transition(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "4px", "");
is(cs.getPropertyValue(prop), "4px",
"length-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "12px", "");
is(cs.getPropertyValue(prop), "6px",
"length-valued property " + prop + ": interpolation of lengths");
check_distance(prop, "4px", "6px", "12px");
}
function test_length_clamped(prop) {
test_length_clamped_or_unclamped(prop, true);
}
function test_length_unclamped(prop) {
test_length_clamped_or_unclamped(prop, false);
}
function test_length_clamped_or_unclamped(prop, is_clamped) {
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "0px", "");
is(cs.getPropertyValue(prop), "0px",
"length-valued property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "100px", "");
(is_clamped ? is : isnot)(cs.getPropertyValue(prop), "0px",
"length-valued property " + prop + ": clamping of negatives");
div.style.setProperty("transition-timing-function", "linear", "");
}
// Test using float values in the range [0, 1] (e.g. opacity)
function test_float_zeroToOne_transition(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "0.3", "");
is(cs.getPropertyValue(prop), "0.3",
"float-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "0.8", "");
is(cs.getPropertyValue(prop), "0.425",
"float-valued property " + prop + ": interpolation of floats");
check_distance(prop, "0.3", "0.425", "0.8");
}
function test_float_zeroToOne_clamped(prop) {
test_float_zeroToOne_clamped_or_unclamped(prop, true);
}
function test_float_zeroToOne_unclamped(prop) {
test_float_zeroToOne_clamped_or_unclamped(prop, false);
}
function test_float_zeroToOne_clamped_or_unclamped(prop, is_clamped) {
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "0", "");
is(cs.getPropertyValue(prop), "0",
"float-valued property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "1", "");
(is_clamped ? is : isnot)(cs.getPropertyValue(prop), "0",
"float-valued property " + prop + ": clamping of negatives");
div.style.setProperty("transition-timing-function", "linear", "");
}
// Test using float values in the range [1, infinity) (e.g. stroke-miterlimit)
function test_float_aboveOne_transition(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "1", "");
is(cs.getPropertyValue(prop), "1",
"float-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "2.1", "");
is(cs.getPropertyValue(prop), "1.275",
"float-valued property " + prop + ": interpolation of floats");
check_distance(prop, "1", "1.275", "2.1");
}
function test_float_aboveOne_clamped(prop) {
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "1", "");
is(cs.getPropertyValue(prop), "1",
"float-valued property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "5", "");
is(cs.getPropertyValue(prop), "1",
"float-valued property " + prop + ": clamping of negatives");
div.style.setProperty("transition-timing-function", "linear", "");
}
function test_percent_transition(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "25%", "");
var av = cs.getPropertyValue(prop);
var a = any_unit_to_num(av);
div.style.setProperty(prop, "75%", "");
var bv = cs.getPropertyValue(prop);
var b = any_unit_to_num(bv);
isnot(b, a, "different percentages (" + av + " and " + bv +
") should be different for " + prop);
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "25%", "");
var res = cs.getPropertyValue(prop);
is(any_unit_to_num(res) * 4, 3 * b + a,
"percent-valued property " + prop + ": interpolation of percents: " +
res + " should be a quarter of the way between " + bv + " and " + av);
ok(has_num(res),
"percent-valued property " + prop + ": percent computes to number");
check_distance(prop, "25%", "37.5%", "75%");
}
function test_percent_clamped(prop) {
test_percent_clamped_or_unclamped(prop, true);
}
function test_percent_unclamped(prop) {
test_percent_clamped_or_unclamped(prop, false);
}
function test_percent_clamped_or_unclamped(prop, is_clamped) {
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "0%", "");
var zero_val = cs.getPropertyValue(prop); // flushes too
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "150%", "");
(is_clamped ? is : isnot)(cs.getPropertyValue(prop), zero_val,
"percent-valued property " + prop + ": clamping of negatives");
div.style.setProperty("transition-timing-function", "linear", "");
}
function test_length_percent_calc_transition(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "0%", "");
var av = cs.getPropertyValue(prop);
var a = any_unit_to_num(av);
div.style.setProperty(prop, "100%", "");
var bv = cs.getPropertyValue(prop);
var b = any_unit_to_num(bv);
div.style.setProperty(prop, "100px", "");
var cv = cs.getPropertyValue(prop);
var c = any_unit_to_num(cv);
isnot(b, a, "different percentages (" + av + " and " + bv +
") should be different for " + prop);
div.style.setProperty(prop, "50%", "");
var v1v = cs.getPropertyValue(prop);
is(any_unit_to_num(v1v) * 2, a + b,
"computed value before transition for " + prop + ": '" +
v1v + "' should be halfway " +
"between '" + av + "' + and '" + bv + "'.");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "200px", "");
var v2v = cs.getPropertyValue(prop);
is(any_unit_to_num(v2v) * 8, 5*a + 3*b + 4*c,
"interpolation between length and percent for " + prop + ": '"
+ v2v + "'");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "calc(25% + 100px)", "");
v1v = cs.getPropertyValue(prop);
is(any_unit_to_num(v1v) * 4, b + 4*c,
"computed value before transition for " + prop + ": '" + v1v + "'");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "75%", "");
v2v = cs.getPropertyValue(prop);
is(any_unit_to_num(v2v) * 8, 5*a + 3*b + 6*c,
"interpolation between calc() and percent for " + prop + ": '" +
v2v + "'");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "150px", "");
v1v = cs.getPropertyValue(prop);
is(any_unit_to_num(v1v) * 2, c * 3,
"computed value before transition for " + prop + ": '" + v1v + "'");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "calc(50% + 50px)", "");
v2v = cs.getPropertyValue(prop);
is(any_unit_to_num(v2v) * 8, 7 * a + b + 10*c,
"interpolation between length and calc() for " + prop + ": '" +
v2v + "'");
check_distance(prop, "50%", "calc(37.5% + 50px)", "200px");
check_distance(prop, "calc(25% + 100px)", "calc(37.5% + 75px)",
"75%");
check_distance(prop, "150px", "calc(125px + 12.5%)",
"calc(50% + 50px)");
}
function test_color_transition(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "rgb(255, 28, 0)", "");
is(cs.getPropertyValue(prop), "rgb(255, 28, 0)",
"color-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "rgb(75, 84, 128)", "");
is(cs.getPropertyValue(prop), "rgb(210, 42, 32)",
"color-valued property " + prop + ": interpolation of colors");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "rgb(128, 64, 0)", "");
(prop == "color" ? div.parentNode : div).style.
setProperty("color", "rgb(0, 0, 128)", "");
is(cs.getPropertyValue(prop), "rgb(128, 64, 0)",
"color-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "currentColor", "");
is(cs.getPropertyValue(prop), "rgb(96, 48, 32)",
"color-valued property " + prop + ": interpolation of currentColor");
check_distance(prop, "rgb(255, 28, 0)", "rgb(210, 42, 32)",
"rgb(75, 84, 128)");
check_distance(prop, "rgb(128, 64, 0)", "rgb(96, 48, 32)", "currentColor");
(prop == "color" ? div.parentNode : div).style.removeProperty("color");
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "rgb(0, 255, 0)", "");
var vals = cs.getPropertyValue(prop).match(/rgb\(([^, ]*), ([^, ]*), ([^, ]*)\)/);
is(vals.length, 4,
"color-valued property " + prop + ": flush before clamping test (length)");
is(vals[1], "0",
"color-valued property " + prop + ": flush before clamping test (red)");
is(vals[2], "255",
"color-valued property " + prop + ": flush before clamping test (green)");
is(vals[3], "0",
"color-valued property " + prop + ": flush before clamping test (blue)");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "rgb(255, 0, 128)", "");
// FIXME: Once we support non-sRGB colors, these tests will need fixing.
vals = cs.getPropertyValue(prop).match(/rgb\(([^, ]*), ([^, ]*), ([^, ]*)\)/);
is(vals.length, 4,
"color-valued property " + prop + ": clamping of negatives (length)");
is(vals[1], "0",
"color-valued property " + prop + ": clamping of negatives (red)");
is(vals[2], "255",
"color-valued property " + prop + ": clamping of above-range (green)");
is(vals[3], "0",
"color-valued property " + prop + ": clamping of negatives (blue)");
div.style.setProperty("transition-timing-function", "linear", "");
}
function test_border_color_transition(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "rgb(128, 64, 0)", "");
div.style.setProperty("color", "rgb(0, 0, 128)", "");
is(cs.getPropertyValue(prop), "rgb(128, 64, 0)",
"color-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.removeProperty(prop);
is(cs.getPropertyValue(prop), "rgb(96, 48, 32)",
"color-valued property " + prop + ": interpolation of initial value");
check_distance(prop, "rgb(128, 64, 0)", "rgb(96, 48, 32)", "initial");
div.style.removeProperty("color");
}
function filter_function_list_equals(computedValStr, expectedList)
{
// Check simple case "none"
if (computedValStr == "none" && computedValStr == expectedList[0]) {
return true;
}
// The regular expression does not filter out the last parenthesis.
// Remove last character for now.
is(computedValStr.substring(computedValStr.length - 1, computedValStr.length),
')', "Check if last character is close-paren");
computedValStr = computedValStr.substring(0, computedValStr.length - 1);
var reg = /\)*\s*(blur|brightness|contrast|grayscale|hue\-rotate|invert|opacity|saturate|sepia|drop\-shadow|url)\(/
var matches = computedValStr.split(reg);
// First item must be empty. All other items are of functionName, functionValue.
if (!matches || matches.shift() != "") {
ok(false, "computed style of 'filter' isn't in the format we expect");
return false;
}
// Odd items are the function name, even items the function value.
if (!matches.length || matches.length % 2 ||
expectedList.length != matches.length) {
ok(false, "computed style of 'filter' isn't in the format we expect");
return false;
}
for (var i = 0; i < matches.length; i += 2) {
var functionName = matches[i];
var functionValue = matches[i+1];
var expected = expectedList[i+1]
var tolerance = 0;
// Check if we have the expected function.
if (functionName != expectedList[i]) {
return false;
}
if (functionName == "blur") {
// Last two characters must be "px".
if (functionValue.search("px") != functionValue.length - 2) {
return false;
}
functionValue = functionValue.substring(0, functionValue.length - 2);
} else if (functionName == "hue-rotate") {
// Last two characters must be "rad".
if (functionValue.search("rad") != functionValue.length - 3) {
return false;
}
tolerance = 0.001;
functionValue = functionValue.substring(0, functionValue.length - 3);
} else if (functionName == "drop-shadow" || functionName == "url") {
if (functionValue != expected) {
return false;
}
continue;
}
// Check if string is not a number or difference is not in tolerance level.
if (isNaN(functionValue) ||
Math.abs(parseFloat(functionValue) - expected) > tolerance) {
return false;
}
}
return true;
}
function test_filter_transition(prop) {
if (!SpecialPowers.getBoolPref("layout.css.filters.enabled")) {
return;
}
for (var i in filterTests) {
var test = filterTests[i];
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, test.start, "");
cs.getPropertyValue(prop);
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, test.end, "");
var actual = cs.getPropertyValue(prop);
ok(filter_function_list_equals(actual, test.expected),
"Filter property is " + actual + " expected values of " +
test.expected);
}
}
function test_shadow_transition(prop) {
var spreadStr = (prop == "box-shadow") ? " 0px" : "";
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "none", "");
is(cs.getPropertyValue(prop), "none",
"shadow-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "4px 8px 3px red", "");
is(cs.getPropertyValue(prop), "rgba(255, 0, 0, 0.25) 1px 2px 0.75px" + spreadStr,
"shadow-valued property " + prop + ": interpolation of shadows");
check_distance(prop, "none", "rgba(255, 0, 0, 0.25) 1px 2px 0.75px",
"4px 8px 3px red");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "#038000 4px 4px, 2px 2px blue", "");
is(cs.getPropertyValue(prop), "rgb(3, 128, 0) 4px 4px 0px" + spreadStr + ", rgb(0, 0, 255) 2px 2px 0px" + spreadStr,
"shadow-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "8px 8px 8px red", "");
is(cs.getPropertyValue(prop), "rgb(66, 96, 0) 5px 5px 2px" + spreadStr + ", rgba(0, 0, 255, 0.75) 1.5px 1.5px 0px" + spreadStr,
"shadow-valued property " + prop + ": interpolation of shadows");
check_distance(prop, "#038000 4px 4px, 2px 2px blue",
"rgb(66, 96, 0) 5px 5px 2px, rgba(0, 0, 255, 0.75) 1.5px 1.5px 0px",
"8px 8px 8px red");
if (prop == "box-shadow") {
div.style.setProperty(prop, "8px 8px 8px red inset", "");
is(cs.getPropertyValue(prop), "rgb(255, 0, 0) 8px 8px 8px 0px inset",
"shadow-valued property " + prop + ": non-interpolable cases");
div.style.setProperty(prop, "8px 8px 8px 8px red inset", "");
is(cs.getPropertyValue(prop), "rgb(255, 0, 0) 8px 8px 8px 2px inset",
"shadow-valued property " + prop + ": interpolation of spread");
// Leave in same state whether in the |if| or not.
div.style.setProperty(prop, "8px 8px 8px red", "");
is(cs.getPropertyValue(prop), "rgb(255, 0, 0) 8px 8px 8px 0px",
"shadow-valued property " + prop + ": non-interpolable cases");
check_distance(prop, "8px 8px 8px red inset",
"rgb(255, 0, 0) 8px 8px 8px 2px inset",
"8px 8px 8px 8px red inset");
}
var defaultColor = cs.getPropertyValue("color") + " ";
div.style.setProperty(prop, "2px 2px 2px", "");
is(cs.getPropertyValue(prop), defaultColor + "2px 2px 2px" + spreadStr,
"shadow-valued property " + prop + ": non-interpolable cases");
div.style.setProperty(prop, "6px 14px 10px", "");
is(cs.getPropertyValue(prop), defaultColor + "3px 5px 4px" + spreadStr,
"shadow-valued property " + prop + ": interpolation without color");
check_distance(prop, "2px 2px 2px", "3px 5px 4px", "6px 14px 10px");
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "0px 0px 0px black", "");
is(cs.getPropertyValue(prop), "rgb(0, 0, 0) 0px 0px 0px" + spreadStr,
"shadow-valued property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "10px 10px 10px black", "");
var vals = cs.getPropertyValue(prop).split(" ");
is(vals.length, 6 + (prop == "box-shadow"), "unexpected number of values");
is(vals.slice(0, 3).join(" "), "rgb(0, 0, 0)",
"shadow-valued property " + prop + " (color): clamping of negatives");
isnot(vals[3], "0px",
"shadow-valued property " + prop + " (x): clamping of negatives");
isnot(vals[4], "0px",
"shadow-valued property " + prop + " (y): clamping of negatives");
is(vals[5], "0px",
"shadow-valued property " + prop + " (radius): clamping of negatives");
if (prop == "box-shadow") {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "0px 0px 0px 0px black", "");
is(cs.getPropertyValue(prop), "rgb(0, 0, 0) 0px 0px 0px 0px",
"shadow-valued property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "10px 10px 10px 10px black", "");
var vals = cs.getPropertyValue(prop).split(" ");
is(vals.length, 7, "unexpected number of values");
is(vals.slice(0, 3).join(" "), "rgb(0, 0, 0)",
"shadow-valued property " + prop + " (color): clamping of negatives");
isnot(vals[3], "0px",
"shadow-valued property " + prop + " (x): clamping of negatives");
isnot(vals[4], "0px",
"shadow-valued property " + prop + " (y): clamping of negatives");
is(vals[5], "0px",
"shadow-valued property " + prop + " (radius): clamping of negatives");
isnot(vals[6], "0px",
"shadow-valued property " + prop + " (spread): clamping of negatives");
}
div.style.setProperty("transition-timing-function", "linear", "");
}
function test_dasharray_transition(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "3", "");
is(cs.getPropertyValue(prop), "3",
"dasharray-valued property " + prop +
": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "15px", "");
is(cs.getPropertyValue(prop), "6",
"dasharray-valued property " + prop + ": interpolation of dasharray");
check_distance(prop, "3", "6", "15px");
div.style.setProperty(prop, "none", "");
is(cs.getPropertyValue(prop), "none",
"dasharray-valued property " + prop + ": non-interpolability of none");
div.style.setProperty(prop, "6,8px,4,4", "");
is(cs.getPropertyValue(prop), "6, 8px, 4, 4",
"dasharray-valued property " + prop +
": computed value before transition");
div.style.setProperty(prop, "14, 12,16,16px", "");
is(cs.getPropertyValue(prop), "8, 9, 7, 7",
"dasharray-valued property " + prop + ": interpolation of dasharray");
check_distance(prop, "6,8px,4,4", "8,9,7,7", "14, 12,16,16px");
div.style.setProperty(prop, "none", "");
is(cs.getPropertyValue(prop), "none",
"dasharray-valued property " + prop + ": non-interpolability of none");
div.style.setProperty(prop, "8,16,4", "");
is(cs.getPropertyValue(prop), "8, 16, 4",
"dasharray-valued property " + prop +
": computed value before transition");
div.style.setProperty(prop, "4,8,12,16", "");
is(cs.getPropertyValue(prop), "7, 14, 6, 10, 13, 5, 9, 16, 4, 8, 15, 7",
"dasharray-valued property " + prop + ": interpolation of dasharray");
check_distance(prop, "8,16,4", "7, 14, 6, 10, 13, 5, 9, 16, 4, 8, 15, 7",
"4,8,12,16");
div.style.setProperty(prop, "2,50%,6,10", "");
is(cs.getPropertyValue(prop), "2, 50%, 6, 10",
"dasharray-valued property " + prop + ": non-interpolability of mixed units");
div.style.setProperty(prop, "6,30%,2,2", "");
is(cs.getPropertyValue(prop), "3, 45%, 5, 8",
"dasharray-valued property " + prop + ": interpolation of dasharray");
check_distance(prop, "2,50%,6,10", "3, 45%, 5, 8", "6,30%,2,2");
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "0,0%", "");
is(cs.getPropertyValue(prop), "0, 0%",
"dasharray-valued property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "5, 25%", "");
is(cs.getPropertyValue(prop), "0, 0%",
"dasharray-valued property " + prop + ": clamping of negatives");
div.style.setProperty("transition-timing-function", "linear", "");
}
function test_radius_transition(prop) {
div.style.setProperty("transition-property", "none", "");
// FIXME: Test a square for now, since we haven't updated to the spec
// for vertical components being relative to the height.
// Note: We use powers of two here so the floating-point math comes out
// nicely.
div.style.setProperty("width", "256px", "");
div.style.setProperty("height", "256px", "");
div.style.setProperty("border", "none", "");
div.style.setProperty("padding", "0", "");
div.style.setProperty(prop, "3px", "");
is(cs.getPropertyValue(prop), "3px",
"radius-valued property " + prop +
": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "15px", "");
is(cs.getPropertyValue(prop), "6px",
"radius-valued property " + prop + ": interpolation of radius");
check_distance(prop, "3px", "6px", "15px");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "12.5%", "");
is(cs.getPropertyValue(prop), "32px",
"radius-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "25%", "");
is(cs.getPropertyValue(prop), "40px",
"radius-valued property " + prop + ": interpolation of radius");
check_distance(prop, "12.5%", "15.625%", "25%");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "3px 8px", "");
is(cs.getPropertyValue(prop), "3px 8px",
"radius-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "15px 12px", "");
is(cs.getPropertyValue(prop), "6px 9px",
"radius-valued property " + prop + ": interpolation of radius");
check_distance(prop, "3px 8px", "6px 9px", "15px 12px");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "12.5% 6.25%", "");
is(cs.getPropertyValue(prop), "32px 16px",
"radius-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "25%", "");
is(cs.getPropertyValue(prop), "40px 28px",
"radius-valued property " + prop + ": interpolation of radius");
check_distance(prop, "12.5% 6.25%", "15.625% 10.9375%", "25%");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "6.25% 12.5%", "");
is(cs.getPropertyValue(prop), "16px 32px",
"radius-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "64px 16px", "");
is(cs.getPropertyValue(prop), "28px",
"radius-valued property " + prop + ": interpolation of radius with mixed units");
check_distance(prop, "6.25% 12.5%",
"calc(4.6875% + 16px) calc(9.375% + 4px)",
"64px 16px");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "calc(5px) 10px", "");
is(cs.getPropertyValue(prop), "5px 10px",
"radius-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "calc(25px) calc(50px)", "");
is(cs.getPropertyValue(prop), "10px 20px",
"radius-valued property " + prop + ": interpolation of radius with calc() units");
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "0px 0px", "");
is(cs.getPropertyValue(prop), "0px",
"radius-valued property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "10px 20px", "");
is(cs.getPropertyValue(prop), "0px",
"radius-valued property " + prop + ": clamping of negatives");
div.style.setProperty("transition-timing-function", "linear", "");
test_length_percent_calc_transition(prop);
div.style.removeProperty("width");
div.style.removeProperty("height");
div.style.removeProperty("border");
div.style.removeProperty("padding");
}
function test_integer_transition(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "4", "");
is(cs.getPropertyValue(prop), "4",
"integer-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "-14", "");
is(cs.getPropertyValue(prop), "-1",
"integer-valued property " + prop + ": interpolation of integers");
check_distance(prop, "6", "1", "-14");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "-4", "");
is(cs.getPropertyValue(prop), "-4",
"integer-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "8", "");
is(cs.getPropertyValue(prop), "-1",
"integer-valued property " + prop + ": interpolation of integers");
check_distance(prop, "-4", "-1", "8");
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "0", "");
is(cs.getPropertyValue(prop), "0",
"integer-valued property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "100", "");
isnot(cs.getPropertyValue(prop), "0",
"integer-valued property " + prop + ": clamping of negatives");
div.style.setProperty("transition-timing-function", "linear", "");
}
function test_font_stretch(prop) {
is(prop, "font-stretch", "only designed for one property");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "normal", "");
is(cs.getPropertyValue(prop), "normal",
"font-stretch property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "ultra-expanded", "");
is(cs.getPropertyValue(prop), "semi-expanded",
"font-stretch property " + prop + ": interpolation of font-stretches");
check_distance(prop, "normal", "semi-expanded", "ultra-expanded");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "expanded", "");
is(cs.getPropertyValue(prop), "expanded",
"font-stretch property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "extra-condensed", "");
is(cs.getPropertyValue(prop), "normal",
"font-stretch property " + prop + ": interpolation of font-stretches");
check_distance(prop, "expanded", "semi-expanded", "condensed");
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "ultra-condensed", "");
is(cs.getPropertyValue(prop), "ultra-condensed",
"font-stretch property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "ultra-expanded", "");
is(cs.getPropertyValue(prop), "ultra-condensed",
"font-stretch property " + prop + ": clamping of values");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "ultra-expanded", "");
is(cs.getPropertyValue(prop), "ultra-expanded",
"font-stretch property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "ultra-condensed", "");
is(cs.getPropertyValue(prop), "ultra-expanded",
"font-stretch property " + prop + ": clamping of values");
div.style.setProperty("transition-timing-function", "linear", "");
}
function test_font_weight(prop) {
is(prop, "font-weight", "only designed for one property");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "normal", "");
is(cs.getPropertyValue(prop), "400",
"font-weight property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "900", "");
is(cs.getPropertyValue(prop), "500",
"font-weight property " + prop + ": interpolation of font-weights");
check_distance(prop, "400", "500", "800");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "900", "");
is(cs.getPropertyValue(prop), "900",
"font-weight property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "100", "");
is(cs.getPropertyValue(prop), "700",
"font-weight property " + prop + ": interpolation of font-weights");
check_distance(prop, "900", "700", "100");
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "100", "");
is(cs.getPropertyValue(prop), "100",
"font-weight property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "900", "");
is(cs.getPropertyValue(prop), "100",
"font-weight property " + prop + ": clamping of values");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "900", "");
is(cs.getPropertyValue(prop), "900",
"font-weight property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "100", "");
is(cs.getPropertyValue(prop), "900",
"font-weight property " + prop + ": clamping of values");
div.style.setProperty("transition-timing-function", "linear", "");
}
function test_pos_integer_or_auto_transition(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "4", "");
is(cs.getPropertyValue(prop), "4",
"integer-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "11", "");
is(cs.getPropertyValue(prop), "5",
"integer-valued property " + prop + ": interpolation of integers");
check_distance(prop, "4", "6", "12");
div.style.setProperty(prop, "auto", "");
is(cs.getPropertyValue(prop), "auto",
"integer-valued property " + prop + ": auto not interpolable");
div.style.setProperty(prop, "8", "");
is(cs.getPropertyValue(prop), "8",
"integer-valued property " + prop + ": computed value before transition");
div.style.setProperty(prop, "4", "");
is(cs.getPropertyValue(prop), "7",
"integer-valued property " + prop + ": interpolation of integers");
check_distance(prop, "8", "7", "4");
}
function test_integer_at_least_one_clamping(prop) {
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "1", "");
is(cs.getPropertyValue(prop), "1",
"integer-valued property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "5", "");
is(cs.getPropertyValue(prop), "1",
"integer-valued property " + prop + ": clamping of negatives");
div.style.setProperty("transition-timing-function", "linear", "");
}
function test_length_pair_transition(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "4px 6px", "");
is(cs.getPropertyValue(prop), "4px 6px",
"length-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "12px 10px", "");
is(cs.getPropertyValue(prop), "6px 7px",
"length-valued property " + prop + ": interpolation of lengths");
check_distance(prop, "4px 6px", "6px 7px", "12px 10px");
}
function test_length_pair_transition_clamped(prop) {
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "0px 0px", "");
is(cs.getPropertyValue(prop), "0px 0px",
"length-valued property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "30px 50px", "");
is(cs.getPropertyValue(prop), "0px 0px",
"length-valued property " + prop + ": clamping of negatives");
div.style.setProperty("transition-timing-function", "linear", "");
}
function test_length_percent_pair_transition(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "4px 50%", "");
is(cs.getPropertyValue(prop), "4px 5px",
"length-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "12px 70%", "");
is(cs.getPropertyValue(prop), "6px 5.5px",
"length-valued property " + prop + ": interpolation of lengths");
check_distance(prop, "4px 50%", "6px 55%", "12px 70%");
}
function test_length_percent_pair_clamped(prop) {
test_length_percent_pair_clamped_or_unclamped(prop, true);
}
function test_length_percent_pair_unclamped(prop) {
test_length_percent_pair_clamped_or_unclamped(prop, false);
}
function test_length_percent_pair_clamped_or_unclamped(prop, is_clamped) {
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "0px 0%", "");
is(cs.getPropertyValue(prop), "0px 0px",
"length+percent-valued property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "30px 25%", "");
(is_clamped ? is : isnot)(cs.getPropertyValue(prop), "0px 0px",
"length+percent-valued property " + prop + ": clamping of negatives");
div.style.setProperty("transition-timing-function", "linear", "");
}
function test_rect_transition(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "rect(4px, 16px, 12px, 6px)", "");
is(cs.getPropertyValue(prop), "rect(4px, 16px, 12px, 6px)",
"rect-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "rect(0px, 4px, 4px, 2px)", "");
is(cs.getPropertyValue(prop), "rect(3px, 13px, 10px, 5px)",
"rect-valued property " + prop + ": interpolation of rects");
check_distance(prop, "rect(4px, 16px, 12px, 6px)",
"rect(3px, 13px, 10px, 5px)",
"rect(0px, 4px, 4px, 2px)");
if (prop == "clip") {
div.style.setProperty(prop, "rect(0px, 6px, 4px, auto)", "");
is(cs.getPropertyValue(prop), "rect(0px, 6px, 4px, auto)",
"rect-valued property " + prop + ": can't interpolate auto components");
div.style.setProperty(prop, "rect(0px, 6px, 4px, 2px)", "");
}
div.style.setProperty(prop, "auto", "");
is(cs.getPropertyValue(prop), "auto",
"rect-valued property " + prop + ": can't interpolate auto components");
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "rect(-10px, 30px, 0px, 0px)", "");
var vals = cs.getPropertyValue(prop).match(/rect\(([^, ]*), ([^, ]*), ([^, ]*), ([^, ]*)\)/);
is(vals.length, 5,
"rect-valued property " + prop + ": flush before clamping test (length)");
is(vals[1], "-10px",
"rect-valued property " + prop + ": flush before clamping test (top)");
is(vals[2], "30px",
"rect-valued property " + prop + ": flush before clamping test (right)");
is(vals[3], "0px",
"rect-valued property " + prop + ": flush before clamping test (bottom)");
is(vals[4], "0px",
"rect-valued property " + prop + ": flush before clamping test (left)");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "rect(0px, 40px, 10px, 10px)", "");
vals = cs.getPropertyValue(prop).match(/rect\(([^, ]*), ([^, ]*), ([^, ]*), ([^, ]*)\)/);
is(vals.length, 5,
"rect-valued property " + prop + ": clamping of negatives (length)");
isnot(vals[1], "-10px",
"rect-valued property " + prop + ": clamping of negatives (top)");
isnot(vals[2], "30px",
"rect-valued property " + prop + ": clamping of negatives (right)");
isnot(vals[3], "0px",
"rect-valued property " + prop + ": clamping of negatives (bottom)");
isnot(vals[4], "0px",
"rect-valued property " + prop + ": clamping of negatives (left)");
div.style.setProperty("transition-timing-function", "linear", "");
}
function test_visibility_transition(prop) {
function do_test(from_value, to_value, interp_value) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, from_value, "");
is(cs.getPropertyValue(prop), from_value,
"visibility property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, to_value, "");
is(cs.getPropertyValue(prop), interp_value,
"visibility property " + prop + ": interpolation of visibility");
}
do_test("visible", "hidden", "visible");
do_test("hidden", "visible", "visible");
do_test("hidden", "collapse", "collapse"); /* not interpolable */
do_test("collapse", "hidden", "hidden"); /* not interpolable */
do_test("visible", "collapse", "visible");
do_test("collapse", "visible", "visible");
isnot(get_distance(prop, "visible", "hidden"), 0,
"distance between visible and hidden should not be zero");
isnot(get_distance(prop, "visible", "collapse"), 0,
"distance between visible and collapse should not be zero");
is(get_distance(prop, "visible", "visible"), 0,
"distance between visible and visible should not be zero");
is(get_distance(prop, "hidden", "hidden"), 0,
"distance between hidden and hidden should not be zero");
is(get_distance(prop, "collapse", "collapse"), 0,
"distance between collapse and collapse should not be zero");
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
function do_negative_test(from_value, to_value, interpolable) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, from_value, "");
is(cs.getPropertyValue(prop), from_value,
"visibility property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, to_value, "");
is(cs.getPropertyValue(prop), interpolable ? from_value : to_value,
"visibility property " + prop + ": clamping of negatives");
}
do_negative_test("visible", "hidden", true);
do_negative_test("hidden", "visible", true);
do_negative_test("hidden", "collapse", false);
do_negative_test("collapse", "hidden", false);
do_negative_test("visible", "collapse", true);
do_negative_test("collapse", "visible", true);
div.style.setProperty("transition-delay", "-3s", "");
div.style.setProperty("transition-timing-function", FUNC_OVERONE, "");
function do_overone_test(from_value, to_value) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, from_value, "");
is(cs.getPropertyValue(prop), from_value,
"visibility property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, to_value, "");
is(cs.getPropertyValue(prop), to_value,
"visibility property " + prop + ": clamping of over-ones");
}
do_overone_test("visible", "hidden");
do_overone_test("hidden", "visible");
do_overone_test("hidden", "collapse");
do_overone_test("collapse", "hidden");
do_overone_test("visible", "collapse");
do_overone_test("collapse", "visible");
div.style.setProperty("transition-delay", "-1s", "");
div.style.setProperty("transition-timing-function", "linear", "");
}
function test_background_size_transition(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "50% 80%", "");
is(cs.getPropertyValue(prop), "50% 80%",
"property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "100% 100%", "");
is(cs.getPropertyValue(prop), "62.5% 85%",
"property " + prop + ": interpolation of percents");
check_distance(prop, "50% 80%", "62.5% 85%", "100% 100%");
div.style.setProperty(prop, "contain", "");
is(cs.getPropertyValue(prop), "contain",
"property " + prop + ": can't interpolate 'contain'");
test_background_position_size_common(prop);
}
function test_background_position_transition(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "center 80%", "");
is(cs.getPropertyValue(prop), "50% 80%",
"property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "bottom right", "");
is(cs.getPropertyValue(prop), "62.5% 85%",
"property " + prop + ": interpolation of percents");
check_distance(prop, "center 80%", "62.5% 85%", "bottom right");
test_background_position_size_common(prop);
}
function test_background_position_size_common(prop) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "40% 0%", "");
is(cs.getPropertyValue(prop), "40% 0%",
"property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "0% 0%", "");
is(cs.getPropertyValue(prop), "30% 0%",
"property " + prop + ": interpolation of percentages");
check_distance(prop, "40% 0%", "30% 0%", "0% 0%");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "0% 40%", "");
is(cs.getPropertyValue(prop), "0% 40%",
"property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "0% 0%", "");
is(cs.getPropertyValue(prop), "0% 30%",
"property " + prop + ": interpolation of percentages");
check_distance(prop, "0% 40%", "0% 30%", "0% 0%");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "10px 40px", "");
is(cs.getPropertyValue(prop), "10px 40px",
"property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "50px 0", "");
is(cs.getPropertyValue(prop), "20px 30px",
"property " + prop + ": interpolation of lengths");
check_distance(prop, "10px 40px", "20px 30px", "50px 0");
div.style.setProperty(prop, "10px 40px, 50px 50px, 30px 20px", "");
is(cs.getPropertyValue(prop), "10px 40px, 50px 50px, 30px 20px",
"property " + prop + ": computed value before transition");
div.style.setProperty(prop, "50px 20px, 70px 50px, 30px 40px", "");
is(cs.getPropertyValue(prop), "20px 35px, 55px 50px, 30px 25px",
"property " + prop + ": interpolation of lists of lengths");
check_distance(prop, "10px 40px, 50px 50px, 30px 20px",
"20px 35px, 55px 50px, 30px 25px",
"50px 20px, 70px 50px, 30px 40px");
div.style.setProperty(prop, "10px 40%, 50% 50px, 30% 20%, 5px 10px", "");
is(cs.getPropertyValue(prop), "10px 40%, 50% 50px, 30% 20%, 5px 10px",
"property " + prop + ": computed value before transition");
div.style.setProperty(prop, "50px 20%, 70% 50px, 30% 40%, 25px 50px", "");
is(cs.getPropertyValue(prop), "20px 35%, 55% 50px, 30% 25%, 10px 20px",
"property " + prop + ": interpolation of lists of lengths and percents");
check_distance(prop, "10px 40%, 50% 50px, 30% 20%, 5px 10px",
"20px 35%, 55% 50px, 30% 25%, 10px 20px",
"50px 20%, 70% 50px, 30% 40%, 25px 50px");
div.style.setProperty(prop, "20% 40%, 8px 12px", "");
is(cs.getPropertyValue(prop), "20% 40%, 8px 12px",
"property " + prop + ": computed value before transition");
div.style.setProperty(prop, "12px 20px, 40% 16%", "");
is(cs.getPropertyValue(prop), "calc(3px + 15%) calc(5px + 30%), calc(6px + 10%) calc(9px + 4%)",
"property " + prop + ": interpolation that computes to calc()");
check_distance(prop, "20% 40%, 8px 12px",
"calc(3px + 15%) calc(5px + 30%), calc(6px + 10%) calc(9px + 4%)",
"12px 20px, 40% 16%");
div.style.setProperty(prop, "calc(20% + 40px) calc(40px + 40%), 8px 12%, calc(20px + 12%) calc(24px + 8%)", "");
is(cs.getPropertyValue(prop), "calc(40px + 20%) calc(40px + 40%), 8px 12%, calc(20px + 12%) calc(24px + 8%)",
"property " + prop + ": computed value before transition");
div.style.setProperty(prop, "12px 20%, calc(20%) calc(16px + 60%), calc(8px + 20%) calc(40px + 16%)", "");
is(cs.getPropertyValue(prop), "calc(33px + 15%) calc(30px + 35%), calc(6px + 5%) calc(4px + 24%), calc(17px + 14%) calc(28px + 10%)",
"property " + prop + ": interpolation that computes to calc()");
check_distance(prop, "calc(20% + 40px) calc(40px + 40%), 8px 12%, calc(20px + 12%) calc(24px + 8%)",
"calc(33px + 15%) calc(30px + 35%), calc(6px + 5%) calc(4px + 24%), calc(17px + 14%) calc(28px + 10%)",
"12px 20%, calc(20%) calc(16px + 60%), calc(8px + 20%) calc(40px + 16%)");
}
function test_transform_transition(prop) {
is(prop, "transform", "Unexpected transform property! Test needs to be fixed");
var matrix_re = /^matrix\(([^,]*), ([^,]*), ([^,]*), ([^,]*), ([^,]*), ([^,]*)\)$/;
for (var i in transformTests) {
var test = transformTests[i];
if (!("expected" in test)) {
var v = test.expected_uncomputed;
if (v.match(matrix_re) && !test.force_compute) {
test.expected = v;
} else {
test.expected = computeMatrix(v);
}
}
}
for (var i in transformTests) {
var test = transformTests[i];
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, test.start, "");
cs.getPropertyValue(prop);
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, test.end, "");
var actual = cs.getPropertyValue(prop);
if (!test.round_error_ok || actual == test.expected) {
// In most cases, we'll get an exact match, but in some cases
// there can be a small amount of rounding error.
is(actual, test.expected,
"interpolation of transitions: " + test.start + " to " + test.end);
} else {
function s(mat) {
return mat.match(matrix_re).slice(1,7);
}
var pass = true;
var actual_split = s(actual);
var expected_split = s(test.expected);
for (var i = 0; i < 6; ++i) {
// Allow differences of 1 at the sixth decimal place, and allow
// a drop extra for floating point error from the subtraction.
if (Math.abs(Number(actual_split[i]) - Number(expected_split[i])) >
0.0000011) {
pass = false;
}
}
ok(pass,
"interpolation of transitions: " + test.start + " to " + test.end +
": " + actual + " should approximately equal " + test.expected);
}
}
// FIXME: should perhaps test that no clamping occurs
runOMTATest(runAsyncTests, SimpleTest.finish);
}
function runAsyncTests() {
// These tests check the value on the compositor 2/3rds of the way through
// the transition.
// For the transform tests we simply compare the value on the compositor
// with the computed value, but for the opacity test we check the absolute
// value as well.
OMTAdiv.style.setProperty("transition-duration", "300s", "");
OMTAdiv.style.setProperty("transition-timing-function", "linear", "");
addAsyncTransformTests();
addAsyncOpacityTest();
addAsyncDelayTest();
runAllAsyncAnimTests().then(function() {
OMTAdiv.style.removeProperty("transition");
SimpleTest.finish();
});
}
function addAsyncTransformTests() {
transformTests.forEach(function(test) {
addAsyncAnimTest(function () { return runTransformTest(test); } );
});
}
function *runTransformTest(test) {
OMTAdiv.style.setProperty("transition-property", "none", "");
OMTAdiv.style.setProperty("transform", test.start, "");
OMTACs.getPropertyValue("transform");
OMTAdiv.style.setProperty("transition-property", "transform", "");
OMTAdiv.style.setProperty("transform", test.end, "");
OMTACs.getPropertyValue("transform");
yield waitForPaints();
// If the start value produced a non-invertible matrix the layer won't be
// created yet so we need to force an extra sample.
if (!isTransformInvertible(test.start)) {
winUtils.advanceTimeAndRefresh(100000);
yield waitForPaints();
winUtils.advanceTimeAndRefresh(100000);
yield waitForPaints();
} else {
winUtils.advanceTimeAndRefresh(200000);
yield waitForPaints();
}
omta_is_approx(OMTAdiv, "transform", OMTACs.getPropertyValue("transform"),
0.0001, RunningOn.Compositor,
"compositor transform transition " +
"from '" + test.start + "' " +
"to '" + test.end + "' " +
"at 2/3rds duration matches computed style");
}
function addAsyncOpacityTest() {
addAsyncAnimTest(function *() {
OMTAdiv.style.setProperty("transition-property", "none", "");
OMTAdiv.style.setProperty("opacity", 0, "");
OMTACs.getPropertyValue("opacity");
OMTAdiv.style.setProperty("transition-property", "opacity", "");
OMTAdiv.style.setProperty("opacity", 1, "");
OMTACs.getPropertyValue("opacity");
yield waitForPaints();
winUtils.advanceTimeAndRefresh(200000);
omta_is_approx(OMTAdiv, "opacity", 2/3, 0.00001, RunningOn.Compositor,
"compositor opacity transition at 2/3rds duration");
});
}
function addAsyncDelayTest() {
addAsyncAnimTest(function *() {
OMTAdiv.style.setProperty("transition-property", "none", "");
OMTAdiv.style.setProperty("transition-delay", "100s", "");
OMTAdiv.style.setProperty("transition-duration", "200s", "");
OMTAdiv.style.setProperty("transform", "", "");
OMTACs.getPropertyValue("transform");
OMTAdiv.style.setProperty("transition-property", "transform", "");
OMTAdiv.style.setProperty("transform", "translate(100px)", "");
OMTACs.getPropertyValue("transform");
winUtils.advanceTimeAndRefresh(200000);
yield waitForPaints();
omta_is_approx(OMTAdiv, "transform", { tx: 50 }, 0.0001,
RunningOn.Compositor,
"compositor transform transition with delay at 1/2"
+ " duration");
});
}
function isTransformInvertible(transformStr) {
var computedStr = transformStrToComputedStr(transformStr);
if (!transformStr)
return false;
var matrix = convertTo3dMatrix(computedStr);
if (matrix === null)
return false;
return isInvertible(matrix);
}
function transformStrToComputedStr(transformStr) {
var div = document.createElement("div");
div.style.transform = transformStr;
return window.getComputedStyle(div).transform;
}
</script>
</pre>
</body>
</html>