tor-browser

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

test_flexbox_reflow_counts.html (6305B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <!--
      4 https://bugzilla.mozilla.org/show_bug.cgi?id=1142686
      5 -->
      6 <head>
      7  <meta charset="utf-8">
      8  <title>Test for Bug 1142686</title>
      9  <script src="/tests/SimpleTest/SimpleTest.js"></script>
     10  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     11  <style>
     12    .flex {
     13      display: flex;
     14    }
     15    #outerFlex {
     16      border: 1px solid black;
     17    }
     18  </style>
     19 </head>
     20 <body>
     21 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1142686">Mozilla Bug 1142686</a>
     22 <div id="display">
     23  <div id="content">
     24    <div class="flex" id="outerFlex">
     25      <div class="flex" id="midFlex">
     26        <div id="innerBlock">
     27        </div>
     28      </div>
     29    </div>
     30  </div>
     31 </div>
     32 <pre id="test">
     33 <script type="application/javascript">
     34 "use strict";
     35 
     36 /** Test for Bug 1142686 */
     37 
     38 /**
     39 * This test checks how many reflows are required, when we make a change inside
     40 * a set of two nested flex containers, with various styles applied to
     41 * the containers & innermost child.  Some flex layout operations require two
     42 * passes (which can cause exponential blowup). This test is intended to verify
     43 * that certain configurations do *not* require two-pass layout, by comparing
     44 * the reflow-count for a more-complex scenario against a less-complex scenario.
     45 *
     46 * We have two nested flex containers around an initially-empty block. For each
     47 * measurement, we put some text in the block, and we see how many frame-reflow
     48 * operations occur as a result.
     49 */
     50 
     51 const gUtils = SpecialPowers.getDOMWindowUtils(window);
     52 
     53 // The elements:
     54 const gOuterFlex = document.getElementById("outerFlex");
     55 const gMidFlex = document.getElementById("midFlex");
     56 const gInnerBlock = document.getElementById("innerBlock");
     57 
     58 // This cleanup helper-function removes all children from 'parent'
     59 // except for 'childToPreserve' (if specified)
     60 function removeChildrenExcept(parent, childToPreserve)
     61 {
     62  if (childToPreserve && childToPreserve.parentNode != parent) {
     63    // This is just a sanity/integrity-check -- if this fails, it's probably a
     64    // bug in this test rather than in the code.  I'm not checking this via
     65    // e.g. "is(childToPreserve.parentNode, parent)", because this *should*
     66    // always pass, and each "pass" is not interesting here since it's a
     67    // sanity-check.  It's only interesting/noteworthy if it fails. So, to
     68    // avoid bloating this test's passed-subtest-count & output, we only bother
     69    // reporting on this in the case where something's wrong.
     70    ok(false, "bug in test; 'childToPreserve' should be child of 'parent'");
     71  }
     72 
     73  // For simplicity, we just remove *all children* and then reappend
     74  // childToPreserve as the sole child.
     75  while (parent.firstChild) {
     76    parent.removeChild(parent.firstChild);
     77  }
     78  if (childToPreserve) {
     79    parent.appendChild(childToPreserve);
     80  }
     81 }
     82 
     83 // Appends 'childCount' new children to 'parent'
     84 function addNChildren(parent, childCount)
     85 {
     86  for (let i = 0; i < childCount; i++) {
     87    let newChild = document.createElement("div");
     88    // Give the new child some text so it's got a nonzero content-size:
     89    newChild.append("a");
     90    parent.appendChild(newChild);
     91  }
     92 }
     93 
     94 // Undoes whatever styling customizations and DOM insertions that a given
     95 // testcase has done, to prepare for running the next testcase.
     96 function cleanup()
     97 {
     98  gOuterFlex.style = gMidFlex.style = gInnerBlock.style = "";
     99  removeChildrenExcept(gInnerBlock);
    100  removeChildrenExcept(gMidFlex, gInnerBlock);
    101  removeChildrenExcept(gOuterFlex, gMidFlex);
    102 }
    103 
    104 // Each testcase here has a label (used in test output), a function to set up
    105 // the testcase, and (optionally) a function to set up the reference case.
    106 let gTestcases = [
    107 {
    108    label : "border on flex items",
    109    addTestStyle : function() {
    110      gMidFlex.style.border = gInnerBlock.style.border = "3px solid black";
    111    },
    112 },
    113 {
    114    label : "padding on flex items",
    115    addTestStyle : function() {
    116      gMidFlex.style.padding = gInnerBlock.style.padding = "5px";
    117    },
    118 },
    119 {
    120    label : "margin on flex items",
    121    addTestStyle : function() {
    122      gMidFlex.style.margin = gInnerBlock.style.margin = "2px";
    123    },
    124 },
    125 {
    126    // When we make a change in one flex item, the number of reflows should not
    127    // scale with its number of siblings (as long as those siblings' sizes
    128    // aren't impacted by the change):
    129    label : "additional flex items in outer flex container",
    130 
    131    // Compare 5 bonus flex items vs. 1 bonus flex item:
    132    addTestStyle : function() {
    133      addNChildren(gOuterFlex, 5);
    134    },
    135    addReferenceStyle : function() {
    136      addNChildren(gOuterFlex, 1);
    137    },
    138 },
    139 {
    140    // (As above, but now the bonus flex items are one step deeper in the tree,
    141    // on the nested flex container rather than the outer one)
    142    label : "additional flex items in nested flex container",
    143    addTestStyle : function() {
    144      addNChildren(gMidFlex, 5);
    145    },
    146    addReferenceStyle : function() {
    147      addNChildren(gMidFlex, 1);
    148    },
    149 },
    150 ];
    151 
    152 // Flush layout & return the global frame-reflow-count
    153 function getReflowCount()
    154 {
    155  let unusedVal = gOuterFlex.offsetHeight; // flush layout
    156  return gUtils.framesReflowed;
    157 }
    158 
    159 // This function adds some text inside of gInnerBlock, and returns the number
    160 // of frames that need to be reflowed as a result.
    161 function makeTweakAndCountReflows()
    162 {
    163  let beforeCount = getReflowCount();
    164  gInnerBlock.appendChild(document.createTextNode("hello"));
    165  let afterCount = getReflowCount();
    166 
    167  let numReflows = afterCount - beforeCount;
    168  if (numReflows <= 0) {
    169    ok(false, "something's wrong -- we should've reflowed *something*");
    170  }
    171  return numReflows;
    172 }
    173 
    174 // Given a testcase (from gTestcases), this function verifies that the
    175 // testcase scenario requires the same number of reflows as the reference
    176 // scenario.
    177 function runOneTest(aTestcase)
    178 {
    179  aTestcase.addTestStyle();
    180  let numTestcaseReflows = makeTweakAndCountReflows();
    181  cleanup();
    182 
    183  if (aTestcase.addReferenceStyle) {
    184    aTestcase.addReferenceStyle();
    185  }
    186  let numReferenceReflows = makeTweakAndCountReflows();
    187  cleanup();
    188 
    189  is(numTestcaseReflows, numReferenceReflows,
    190     "Testcase & reference case should require same number of reflows" +
    191     " (testcase label: '" + aTestcase.label + "')");
    192 }
    193 
    194 gTestcases.forEach(runOneTest);
    195 
    196 </script>
    197 </pre>
    198 </body>
    199 </html>