tor-browser

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

test_transitions.html (28460B)


      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  <script src="/tests/SimpleTest/SimpleTest.js"></script>
      9  <script type="application/javascript" src="animation_utils.js"></script>
     10  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     11  <style type="text/css">
     12 
     13  #display p { margin-top: 0; margin-bottom: 0; }
     14  #display .before, #display .after {
     15    width: -moz-fit-content; border: 1px solid black;
     16  }
     17  #display .before::before, #display .after::after {
     18    display: block;
     19    width: 0;
     20    text-indent: 0;
     21  }
     22  #display .before.started::before, #display .after.started::after {
     23    width: 100px;
     24    text-indent: 100px;
     25    transition: 8s width ease-in-out, 8s text-indent ease-in-out;
     26  }
     27  #display .before::before {
     28    content: "Before";
     29  }
     30  #display .after::after {
     31    content: "After";
     32  }
     33 
     34  </style>
     35 </head>
     36 <body>
     37 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=435441">Mozilla Bug 435441</a>
     38 <div id="display">
     39 
     40 </div>
     41 <pre id="test">
     42 <script type="application/javascript">
     43 
     44 /** Test for Bug 435441 */
     45 
     46 // Run tests simultaneously so we don't have to take up too much time.
     47 SimpleTest.waitForExplicitFinish();
     48 SimpleTest.requestFlakyTimeout("untriaged");
     49 var gTestsRunning = 0;
     50 function TestStarted() { ++gTestsRunning; }
     51 function TestFinished() { if (--gTestsRunning == 0) SimpleTest.finish(); }
     52 
     53 // An array of arrays of functions to be called at the outer index number
     54 // of seconds after the present.
     55 var gFutureCalls = [];
     56 
     57 function add_future_call(index, func)
     58 {
     59    if (!(index in gFutureCalls)) {
     60        gFutureCalls[index] = [];
     61    }
     62    gFutureCalls[index].push(func);
     63    TestStarted();
     64 }
     65 var gStartTime1, gStartTime2;
     66 var gCurrentTime;
     67 var gSetupComplete = false;
     68 
     69 function process_future_calls(index)
     70 {
     71    var calls = gFutureCalls[index];
     72    if (!calls)
     73        return;
     74    gCurrentTime = Date.now();
     75    for (var i = 0; i < calls.length; ++i) {
     76        calls[i]();
     77        TestFinished();
     78    }
     79 }
     80 
     81 var timingFunctions = {
     82  // a map from the value of 'transition-timing-function' to an array of
     83  // the portions this function yields at 0 (always 0), 1/4, 1/2, and
     84  // 3/4 and all (always 1) of the way through the time of the
     85  // transition.  Each portion is represented as a value and an
     86  // acceptable error tolerance (based on a time error of 1%) for that
     87  // value.
     88 
     89  // ease
     90  "ease": bezier(0.25, 0.1, 0.25, 1),
     91  "cubic-bezier(0.25, 0.1, 0.25, 1.0)": bezier(0.25, 0.1, 0.25, 1),
     92 
     93  // linear and various synonyms for it
     94  "linear": function(x) { return x; },
     95  "cubic-bezier(0.0, 0.0, 1.0, 1.0)": function(x) { return x; },
     96  "cubic-bezier(0, 0, 1, 1)": function(x) { return x; },
     97  "cubic-bezier(0, 0, 0, 0.0)": function(x) { return x; },
     98  "cubic-bezier(1.0, 1, 0, 0)": function(x) { return x; },
     99 
    100  // ease-in
    101  "ease-in": bezier(0.42, 0, 1, 1),
    102  "cubic-bezier(0.42, 0, 1.0, 1.0)": bezier(0.42, 0, 1, 1),
    103 
    104  // ease-out
    105  "ease-out": bezier(0, 0, 0.58, 1),
    106  "cubic-bezier(0, 0, 0.58, 1.0)": bezier(0, 0, 0.58, 1),
    107 
    108  // ease-in-out
    109  "ease-in-out": bezier(0.42, 0, 0.58, 1),
    110  "cubic-bezier(0.42, 0, 0.58, 1.0)": bezier(0.42, 0, 0.58, 1),
    111 
    112  // other cubic-bezier values
    113  "cubic-bezier(0.4, 0.1, 0.7, 0.95)": bezier(0.4, 0.1, 0.7, 0.95),
    114  "cubic-bezier(1, 0, 0, 1)": bezier(1, 0, 0, 1),
    115  "cubic-bezier(0, 1, 1, 0)": bezier(0, 1, 1, 0),
    116 
    117 };
    118 
    119 var div = document.getElementById("display");
    120 
    121 // Set up all the elements on which we are going to initiate transitions.
    122 
    123 // We have two reference elements to check the expected timing range.
    124 // They both have 16s linear transitions from 0 to 1000px.
    125 // This means they move through 62.5 pixels per second.
    126 const REF_PX_PER_SEC = 62.5;
    127 function make_reference_p() {
    128    let p = document.createElement("p");
    129    p.appendChild(document.createTextNode("reference"));
    130    p.style.textIndent = "0px";
    131    p.style.transition = "16s text-indent linear";
    132    div.appendChild(p);
    133    return p;
    134 }
    135 var earlyref = make_reference_p();
    136 var earlyrefcs = getComputedStyle(earlyref, "");
    137 
    138 // Test all timing functions using a set of 8-second transitions, which
    139 // we check at times 0, 2s, 4s, 6s, and 8s.
    140 var tftests = [];
    141 for (let tf in timingFunctions) {
    142    let p = document.createElement("p");
    143    let t = document.createTextNode("transition-timing-function: " + tf);
    144    p.appendChild(t);
    145    p.style.textIndent = "0px";
    146    p.style.transition = "8s text-indent linear";
    147    p.style.transitionTimingFunction = tf;
    148    div.appendChild(p);
    149    is(getComputedStyle(p, "").textIndent, "0px",
    150       "should be zero before changing value");
    151    tftests.push([ p, tf ]);
    152 }
    153 
    154 // Check that the timing function continues even when we restyle in the
    155 // middle.
    156 var interrupt_tests = [];
    157 for (var restyleParent of [true, false]) {
    158    for (let itime = 2; itime < 8; itime += 2) {
    159        let p = document.createElement("p");
    160        let t = document.createTextNode("interrupt on " +
    161                                        (restyleParent ? "parent" : "node itself") +
    162                                        " at " + itime + "s");
    163        p.appendChild(t);
    164        p.style.textIndent = "0px";
    165        p.style.transition = "8s text-indent cubic-bezier(0, 1, 1, 0)";
    166        if (restyleParent) {
    167          let d = document.createElement("div");
    168          d.appendChild(p);
    169          div.appendChild(d);
    170        } else {
    171          div.appendChild(p);
    172        }
    173        is(getComputedStyle(p, "").textIndent, "0px",
    174           "should be zero before changing value");
    175        setTimeout("interrupt_tests[" + interrupt_tests.length + "]" +
    176                   "[0]" + (restyleParent ? ".parentNode" : "") +
    177                   ".style.color = 'blue';" +
    178                   "check_interrupt_tests()", itime*1000);
    179        interrupt_tests.push([ p, itime ]);
    180    }
    181 }
    182 
    183 // Test transition-delay values of -4s through 4s on a 4s transition
    184 // with 'ease-out' timing function.
    185 var delay_tests = {};
    186 for (let d = -4; d <= 4; ++d) {
    187    let p = document.createElement("p");
    188    let delay = d + "s";
    189    let t = document.createTextNode("transition-delay: " + delay);
    190    p.appendChild(t);
    191    p.style.marginLeft = "0px";
    192    p.style.transition = "4s margin-left ease-out " + delay;
    193    div.appendChild(p);
    194    is(getComputedStyle(p, "").marginLeft, "0px",
    195       "should be zero before changing value");
    196    delay_tests[d] = p;
    197 }
    198 
    199 // Test transition-delay values of -4s through 4s on a 4s transition
    200 // with duration of zero.
    201 var delay_zero_tests = {};
    202 for (let d = -4; d <= 4; ++d) {
    203    let p = document.createElement("p");
    204    let delay = d + "s";
    205    let t = document.createTextNode("transition-delay: " + delay);
    206    p.appendChild(t);
    207    p.style.marginLeft = "0px";
    208    p.style.transition = "0s margin-left linear " + delay;
    209    div.appendChild(p);
    210    is(getComputedStyle(p, "").marginLeft, "0px",
    211       "should be zero before changing value");
    212    delay_zero_tests[d] = p;
    213 }
    214 
    215 // Test that changing the value on an already-running transition to the
    216 // value it currently happens to have resets the transition.
    217 function make_reset_test(transition, description)
    218 {
    219    let p = document.createElement("p");
    220    let t = document.createTextNode(description);
    221    p.appendChild(t);
    222    p.style.marginLeft = "0px";
    223    p.style.transition = transition;
    224    div.appendChild(p);
    225    is(getComputedStyle(p, "").marginLeft, "0px",
    226       "should be zero before changing value");
    227    return p;
    228 }
    229 var reset_test = make_reset_test("4s margin-left ease-out 4s", "transition-delay reset to starting point");
    230 var reset_test_reference = make_reset_test("4s margin-left linear -3s", "reference for previous test (reset test)");
    231 
    232 // Test that transitions on descendants start correctly when the
    233 // inherited value is itself transitioning.  In other words, when
    234 // ancestor and descendant both have a transition for the same property,
    235 // and the descendant inherits the property from the ancestor, the
    236 // descendant's transition starts as specified, based on the concepts of
    237 // the before-change style, the after-change style, and the
    238 // after-transition style.
    239 var descendant_tests = [
    240    { parent_transition: "",
    241      child_transition: "4s text-indent" },
    242    { parent_transition: "4s text-indent",
    243      child_transition: "" },
    244    { parent_transition: "4s text-indent",
    245      child_transition: "16s text-indent" },
    246    { parent_transition: "4s text-indent",
    247      child_transition: "1s text-indent" },
    248    { parent_transition: "8s letter-spacing",
    249      child_transition: "4s text-indent" },
    250    { parent_transition: "4s text-indent",
    251      child_transition: "8s letter-spacing" },
    252    { parent_transition: "4s text-indent",
    253      child_transition: "8s all" },
    254    { parent_transition: "8s text-indent",
    255      child_transition: "4s all" },
    256    // examples with positive and negative delay
    257    { parent_transition: "4s text-indent 1s",
    258      child_transition: "8s text-indent" },
    259    { parent_transition: "4s text-indent -1s",
    260      child_transition: "8s text-indent" }
    261 ];
    262 
    263 for (let i in descendant_tests) {
    264    let test = descendant_tests[i];
    265    test.parentNode = document.createElement("div");
    266    test.childNode = document.createElement("p");
    267    test.parentNode.appendChild(test.childNode);
    268    test.childNode.appendChild(document.createTextNode(
    269        "parent with \"" + test.parent_transition + "\" and " +
    270        "child with \"" + test.child_transition + "\""));
    271    test.parentNode.style.transition = test.parent_transition;
    272    test.childNode.style.transition = test.child_transition;
    273    test.parentNode.style.textIndent = "50px"; // transition from 50 to 150
    274    test.parentNode.style.letterSpacing = "10px"; // transition from 10 to 5
    275    div.appendChild(test.parentNode);
    276    var parentCS = getComputedStyle(test.parentNode, "");
    277    var childCS = getComputedStyle(test.childNode, "");
    278    is(parentCS.textIndent, "50px",
    279       "parent text-indent should be 50px before changing");
    280    is(parentCS.letterSpacing, "10px",
    281       "parent letter-spacing should be 10px before changing");
    282    is(childCS.textIndent, "50px",
    283       "child text-indent should be 50px before changing");
    284    is(childCS.letterSpacing, "10px",
    285       "child letter-spacing should be 10px before changing");
    286    test.childCS = childCS;
    287 }
    288 
    289 // For all of these transitions, the transition for margin-left should
    290 // have a duration of 8s, and the default timing function (ease) and
    291 // delay (0).
    292 // This is because we're implementing the proposal in
    293 // http://lists.w3.org/Archives/Public/www-style/2009Aug/0109.html
    294 var number_tests = [
    295  { style: "transition: 4s margin, 8s margin-left" },
    296  { style: "transition: 4s margin-left, 8s margin" },
    297  { style: "transition-property: margin-left; " +
    298             "transition-duration: 8s, 2s" },
    299  { style: "transition-property: margin-left, margin-left; " +
    300             "transition-duration: 2s, 8s" },
    301  { style: "transition-property: margin-left, margin-left, margin-left; " +
    302             "transition-duration: 8s, 2s" },
    303  { style: "transition-property: margin-left; " +
    304             "transition-duration: 8s, 16s" },
    305  { style: "transition-property: margin-left, margin-left; " +
    306             "transition-duration: 16s, 8s" },
    307  { style: "transition-property: margin-left, margin-left, margin-left; " +
    308             "transition-duration: 8s, 16s" },
    309  { style: "transition-property: text-indent,word-spacing,margin-left; " +
    310             "transition-duration: 8s; " +
    311             "transition-delay: 0, 8s" },
    312  { style: "transition-property: text-indent,word-spacing,margin-left; " +
    313             "transition-duration: 8s, 16s; " +
    314             "transition-delay: 8s, 8s, 0, 8s, 8s, 8s" },
    315 ];
    316 
    317 for (let i in number_tests) {
    318    let test = number_tests[i];
    319    let p = document.createElement("p");
    320    p.setAttribute("style", test.style);
    321    let t = document.createTextNode(test.style);
    322    p.appendChild(t);
    323    p.style.marginLeft = "100px";
    324    div.appendChild(p);
    325    is(getComputedStyle(p, "").marginLeft, "100px",
    326       "should be 100px before changing value");
    327    test.node = p;
    328 }
    329 
    330 // Test transitions that are also from-display:none, to-display:none, and
    331 // display:none throughout.
    332 var from_none_test, to_none_test, always_none_test;
    333 function make_display_test(initially_none, text)
    334 {
    335    let p = document.createElement("p");
    336    p.appendChild(document.createTextNode(text));
    337    p.style.textIndent = "0px";
    338    p.style.transition = "8s text-indent ease-in-out";
    339    if (initially_none)
    340        p.style.display = "none";
    341    div.appendChild(p);
    342    return p;
    343 }
    344 from_none_test   = make_display_test(true,  "transition from display:none");
    345 to_none_test     = make_display_test(false, "transition to display:none");
    346 always_none_test = make_display_test(true,  "transition always display:none");
    347 var display_tests = [ from_none_test, to_none_test, always_none_test ];
    348 
    349 // Test transitions on pseudo-elements
    350 var before_test, after_test;
    351 function make_pseudo_elem_test(pseudo)
    352 {
    353    let p = document.createElement("p");
    354    p.className = pseudo;
    355    div.appendChild(p);
    356    return {"pseudo": pseudo, element: p};
    357 }
    358 before_test = make_pseudo_elem_test("before");
    359 after_test = make_pseudo_elem_test("after");
    360 var pseudo_element_tests = [ before_test, after_test ];
    361 
    362 // FIXME (Bug 522599): Test a transition that reverses partway through.
    363 
    364 var lateref = make_reference_p();
    365 var laterefcs = getComputedStyle(lateref, "");
    366 
    367 // flush style changes
    368 var x = getComputedStyle(div, "").width;
    369 
    370 // Start our timer as close as possible to when we start the first
    371 // transition.
    372 // Do not use setInterval because once it gets off in time, it stays off.
    373 for (let i = 1; i <= 8; ++i) {
    374    setTimeout(process_future_calls, i * 1000, i);
    375 }
    376 gStartTime1 = Date.now(); // set before any transitions have started
    377 
    378 // Start all the transitions.
    379 earlyref.style.textIndent = "1000px";
    380 for (let test in tftests) {
    381    let p = tftests[test][0];
    382    p.style.textIndent = "100px";
    383 }
    384 for (let test in interrupt_tests) {
    385    let p = interrupt_tests[test][0];
    386    p.style.textIndent = "100px";
    387 }
    388 for (let d in delay_tests) {
    389    let p = delay_tests[d];
    390    p.style.marginLeft = "100px";
    391 }
    392 for (let d in delay_zero_tests) {
    393    let p = delay_zero_tests[d];
    394    p.style.marginLeft = "100px";
    395 }
    396 reset_test.style.marginLeft = "100px";
    397 reset_test_reference.style.marginLeft = "100px";
    398 for (let i in descendant_tests) {
    399    let test = descendant_tests[i];
    400    test.parentNode.style.textIndent = "150px";
    401    test.parentNode.style.letterSpacing = "5px";
    402 }
    403 for (let i in number_tests) {
    404    let test = number_tests[i];
    405    test.node.style.marginLeft = "50px";
    406 }
    407 from_none_test.style.textIndent = "100px";
    408 from_none_test.style.display = "";
    409 to_none_test.style.textIndent = "100px";
    410 to_none_test.style.display = "none";
    411 always_none_test.style.textIndent = "100px";
    412 for (let i in pseudo_element_tests) {
    413    let test = pseudo_element_tests[i];
    414    test.element.classList.add("started");
    415 }
    416 lateref.style.textIndent = "1000px";
    417 
    418 // flush style changes
    419 x = getComputedStyle(div, "").width;
    420 
    421 gStartTime2 = Date.now(); // set after all transitions have started
    422 gCurrentTime = gStartTime2;
    423 
    424 /**
    425 * Assert that a transition whose timing function yields the bezier
    426 * |func|, running from |start_time| to |end_time| (both in seconds
    427 * relative to when the transitions were started) should have produced
    428 * computed value |cval| given that the transition was from
    429 * |start_value| to |end_value| (both numbers in CSS pixels).
    430 */
    431 function check_transition_value(func, start_time, end_time,
    432                                start_value, end_value, cval, desc,
    433                                xfail)
    434 {
    435    /**
    436     * Compute the value at a given time |elapsed|, by normalizing the
    437     * input to the timing function using start_time and end_time and
    438     * then turning the output into a value using start_value and
    439     * end_value.
    440     *
    441     * The |error_direction| argument should be either -1, 0, or 1,
    442     * suggesting adding on a little bit of error, to allow for the
    443     * cubic-bezier calculation being an approximation.  The amount of
    444     * error is proportional to the slope of the timing function, since
    445     * the error is added to the *input* of the timing function (after
    446     * normalization to 0-1 based on start_time and end_time).
    447     */
    448    function value_at(elapsed, error_direction) {
    449        var time_portion = (elapsed - start_time) / (end_time - start_time);
    450        if (time_portion < 0)
    451            time_portion = 0;
    452        else if (time_portion > 1)
    453            time_portion = 1;
    454        // Assume a small error since bezier computation can be off slightly.
    455        // (This test's computation is probably more accurate than Mozilla's.)
    456        var value_portion = func(time_portion + error_direction * 0.0005);
    457        if (value_portion < 0)
    458            value_portion = 0;
    459        else if (value_portion > 1)
    460            value_portion = 1;
    461        var value = (1 - value_portion) * start_value + value_portion * end_value;
    462        if (start_value > end_value)
    463            error_direction = -error_direction;
    464        // Computed values get rounded to 1/60th of a pixel.
    465        return value + error_direction * 0.02;
    466    }
    467 
    468    var time_range; // in seconds
    469    var uns_range; // |range| before being sorted (so errors give it
    470                   // in the original order
    471    if (!gSetupComplete) {
    472        // No timers involved
    473        time_range = [0, 0];
    474        if (start_time < 0) {
    475            uns_range = [ value_at(0, -1), value_at(0, 1) ];
    476        } else {
    477            var val = value_at(0, 0);
    478            uns_range = [val, val];
    479        }
    480    } else {
    481        time_range = [ px_to_num(earlyrefcs.textIndent) / REF_PX_PER_SEC,
    482                       px_to_num(laterefcs.textIndent) / REF_PX_PER_SEC ];
    483        // seconds
    484        uns_range = [ value_at(time_range[0], -1),
    485                      value_at(time_range[1], 1) ];
    486    }
    487    var range = uns_range.concat(). /* concat to clone array */
    488                  sort(function compareNumbers(a,b) { return a - b; });
    489    var actual = px_to_num(cval);
    490 
    491    var fn = ok;
    492    if (xfail && xfail(range))
    493      fn = todo;
    494 
    495    fn(range[0] <= actual && actual <= range[1],
    496       desc + ": computed value " + cval + " should be between " +
    497       uns_range[0].toFixed(6) + "px and " + uns_range[1].toFixed(6) +
    498       "px at time between " + time_range[0] + "s and " + time_range[1] + "s.");
    499 }
    500 
    501 function check_ref_range()
    502 {
    503    // This is the only test where we compare the progress of the
    504    // transitions to an actual time; we need considerable tolerance at
    505    // the low end (we are using half a second).
    506    var expected_range = [ (gCurrentTime - gStartTime2 - 40) / 16,
    507                           (Date.now() - gStartTime1 + 20) / 16 ];
    508    if (expected_range[0] > 1000) {
    509        expected_range[0] = 1000;
    510    }
    511    if (expected_range[1] > 1000) {
    512        expected_range[1] = 1000;
    513    }
    514    function check(desc, value) {
    515        // The timing on the unit test VMs is not reliable, so make this
    516        // test report PASS when it succeeds and TODO when it fails.
    517        var passed = expected_range[0] <= value && value <= expected_range[1];
    518        (passed ? ok : todo)(passed,
    519           desc + ": computed value " + value + "px should be between " +
    520           expected_range[0].toFixed(6) + "px and " +
    521           expected_range[1].toFixed(6) + "px at time between " +
    522           expected_range[0]/REF_PX_PER_SEC + "s and " +
    523           expected_range[1]/REF_PX_PER_SEC + "s.");
    524    }
    525    check("early reference", px_to_num(earlyrefcs.textIndent));
    526    check("late reference", px_to_num(laterefcs.textIndent));
    527 }
    528 
    529 for (let i = 1; i <= 8; ++i) {
    530    add_future_call(i, check_ref_range);
    531 }
    532 
    533 function check_tf_test()
    534 {
    535    for (var test in tftests) {
    536        var p = tftests[test][0];
    537        var tf = tftests[test][1];
    538 
    539        check_transition_value(timingFunctions[tf], 0, 8, 0, 100,
    540                               getComputedStyle(p, "").textIndent,
    541                               "timing function test for timing function " + tf);
    542 
    543    }
    544 
    545    check_interrupt_tests();
    546 }
    547 
    548 check_tf_test();
    549 add_future_call(2, check_tf_test);
    550 add_future_call(4, check_tf_test);
    551 add_future_call(6, check_tf_test);
    552 add_future_call(8, check_tf_test);
    553 
    554 function check_interrupt_tests()
    555 {
    556    for (let test in interrupt_tests) {
    557        var p = interrupt_tests[test][0];
    558        var itime = interrupt_tests[test][1];
    559 
    560        check_transition_value(timingFunctions["cubic-bezier(0, 1, 1, 0)"],
    561                               0, 8, 0, 100,
    562                               getComputedStyle(p, "").textIndent,
    563                               "interrupt " +
    564                               (p.parentNode == div ? "" : "on parent ") +
    565                               "test for time " + itime + "s");
    566    }
    567 }
    568 
    569 // check_interrupt_tests is called from check_tf_test and from
    570 // where we reset the interrupts
    571 
    572 function check_delay_test(time)
    573 {
    574    let tf = timingFunctions["ease-out"];
    575    for (let d in delay_tests) {
    576        let p = delay_tests[d];
    577 
    578        check_transition_value(tf, Number(d), Number(d) + 4, 0, 100,
    579                               getComputedStyle(p, "").marginLeft,
    580                               "delay test for delay " + d + "s");
    581    }
    582 }
    583 
    584 check_delay_test(0);
    585 for (let i = 1; i <= 8; ++i) {
    586    add_future_call(i, check_delay_test);
    587 }
    588 
    589 function check_delay_zero_test(time)
    590 {
    591    for (let d in delay_zero_tests) {
    592        let p = delay_zero_tests[d];
    593 
    594        time_range = [ px_to_num(earlyrefcs.textIndent) / REF_PX_PER_SEC,
    595                       px_to_num(laterefcs.textIndent) / REF_PX_PER_SEC ];
    596        var m = getComputedStyle(p, "").marginLeft;
    597        var desc = "delay_zero test for delay " + d + "s";
    598        if (time_range[0] < d && time_range[1] < d) {
    599            is(m, "0px", desc);
    600        } else if ((time_range[0] > d && time_range[1] > d) ||
    601                   (d == 0 && time == 0)) {
    602            is(m, "100px", desc);
    603        }
    604    }
    605 }
    606 
    607 check_delay_zero_test(0);
    608 for (let i = 1; i <= 8; ++i) {
    609    add_future_call(i, check_delay_zero_test);
    610 }
    611 
    612 function reset_reset_test(time)
    613 {
    614    reset_test.style.marginLeft = "0px";
    615 }
    616 function check_reset_test(time)
    617 {
    618    is(getComputedStyle(reset_test, "").marginLeft, "0px",
    619       "reset test value at time " + time + "s.");
    620 }
    621 check_reset_test(0);
    622 // reset the reset test right now so we don't have to worry about clock skew
    623 // To make sure that this is valid, check that a pretty-much-identical test is
    624 // already transitioning.
    625 is(getComputedStyle(reset_test_reference, "").marginLeft, "75px",
    626   "reset test reference value");
    627 reset_reset_test();
    628 check_reset_test(0);
    629 for (let i = 1; i <= 8; ++i) {
    630    (function(j) {
    631        add_future_call(j, function() { check_reset_test(j); });
    632    })(i);
    633 }
    634 
    635 check_descendant_tests();
    636 add_future_call(2, check_descendant_tests);
    637 add_future_call(6, check_descendant_tests);
    638 
    639 function check_descendant_tests() {
    640    // text-indent: transition from 50px to 150px
    641    // letter-spacing: transition from 10px to 5px
    642    var values = {};
    643    values["text-indent"] = [ 50, 150 ];
    644    values["letter-spacing"] = [ 10, 5 ];
    645    let tf = timingFunctions.ease;
    646 
    647    var time = px_to_num(earlyrefcs.textIndent) / REF_PX_PER_SEC;
    648 
    649    for (let i in descendant_tests) {
    650        let test = descendant_tests[i];
    651 
    652        /* ti=text-indent, ls=letter-spacing */
    653        var child_ti_duration = 0;
    654        var child_ls_duration = 0;
    655        var child_ti_delay = 0;
    656        var child_ls_delay = 0;
    657 
    658        if (test.parent_transition != "") {
    659            var props = test.parent_transition.split(" ");
    660            var duration = parseInt(props[0]);
    661            var delay = (props.length > 2) ? parseInt(props[2]) : 0;
    662            var property = props[1];
    663            if (property == "text-indent") {
    664                child_ti_duration = duration;
    665                child_ti_delay = delay;
    666            } else if (property == "letter-spacing") {
    667                child_ls_duration = duration;
    668                child_ls_delay = delay;
    669            } else {
    670                ok(false, "fix this test (unexpected transition-property " +
    671                          property + " on parent)");
    672            }
    673        }
    674 
    675        if (test.child_transition != "") {
    676            var props = test.child_transition.split(" ");
    677            var duration = parseInt(props[0]);
    678            var delay = (props.length > 2) ? parseInt(props[2]) : 0;
    679            var property = props[1];
    680            if (property != "text-indent" && property != "letter-spacing" &&
    681                property != "all") {
    682                ok(false, "fix this test (unexpected transition-property " +
    683                          property + " on child)");
    684            }
    685 
    686            // Override the parent's transition with the child's as long
    687            // as the child transition is still running.
    688            if (property != "letter-spacing" && duration + delay > time) {
    689                child_ti_duration = duration;
    690                child_ti_delay = delay;
    691            }
    692            if (property != "text-indent" && duration + delay > time) {
    693                child_ls_duration = duration;
    694                child_ls_delay = delay;
    695            }
    696        }
    697 
    698        var time_portions = {
    699          "text-indent":
    700            { duration: child_ti_duration, delay: child_ti_delay },
    701          "letter-spacing":
    702            { duration: child_ls_duration, delay: child_ls_delay },
    703        };
    704 
    705        for (var prop in {"text-indent": true, "letter-spacing": true}) {
    706            var time_portion = time_portions[prop];
    707 
    708            if (time_portion.duration == 0) {
    709                time_portion.duration = 0.01;
    710                time_portion.delay = -1;
    711            }
    712 
    713            check_transition_value(tf, time_portion.delay,
    714                                   time_portion.delay + time_portion.duration,
    715                                   values[prop][0], values[prop][1],
    716                                   test.childCS.getPropertyValue(prop),
    717                                   `descendant test #${Number(i)+1}, property ${prop}`);
    718        }
    719    }
    720 }
    721 
    722 function check_number_tests()
    723 {
    724    let tf = timingFunctions.ease;
    725    for (let d in number_tests) {
    726        let test = number_tests[d];
    727        let p = test.node;
    728 
    729        check_transition_value(tf, 0, 8, 100, 50,
    730                               getComputedStyle(p, "").marginLeft,
    731                               "number of transitions test for style " +
    732                                 test.style);
    733    }
    734 }
    735 
    736 check_number_tests(0);
    737 add_future_call(2, check_number_tests);
    738 add_future_call(4, check_number_tests);
    739 add_future_call(6, check_number_tests);
    740 add_future_call(8, check_number_tests);
    741 
    742 function check_display_tests(time)
    743 {
    744    for (let i in display_tests) {
    745        let p = display_tests[i];
    746 
    747        // There is no transition if the old or new style is display:none, so
    748        // the computed value is always the end value.
    749        var computedValue = getComputedStyle(p, "").textIndent;
    750        is(computedValue, "100px",
    751           "display test for test with " + p.childNodes[0].data +
    752           ": computed value " + computedValue + " should be 100px.");
    753    }
    754 }
    755 
    756 check_display_tests(0);
    757 add_future_call(2, function() { check_display_tests(2); });
    758 add_future_call(4, function() { check_display_tests(4); });
    759 add_future_call(6, function() { check_display_tests(6); });
    760 add_future_call(8, function() { check_display_tests(8); });
    761 
    762 function check_pseudo_element_tests(time)
    763 {
    764    let tf = timingFunctions["ease-in-out"];
    765    for (let i in pseudo_element_tests) {
    766        let test = pseudo_element_tests[i];
    767 
    768        check_transition_value(tf, 0, 8, 0, 100,
    769                               getComputedStyle(test.element, "").width,
    770                               "::"+test.pseudo+" test");
    771        check_transition_value(tf, 0, 8, 0, 100,
    772                               getComputedStyle(test.element,
    773                                                "::"+test.pseudo).textIndent,
    774                               "::"+test.pseudo+" indent test");
    775    }
    776 }
    777 check_pseudo_element_tests(0);
    778 add_future_call(2, function() { check_pseudo_element_tests(2); });
    779 add_future_call(4, function() { check_pseudo_element_tests(4); });
    780 add_future_call(6, function() { check_pseudo_element_tests(6); });
    781 add_future_call(8, function() { check_pseudo_element_tests(8); });
    782 
    783 gSetupComplete = true;
    784 </script>
    785 </pre>
    786 </body>
    787 </html>