diff options
Diffstat (limited to 'layout/style/test/test_flexbox_reflow_counts.html')
-rw-r--r-- | layout/style/test/test_flexbox_reflow_counts.html | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/layout/style/test/test_flexbox_reflow_counts.html b/layout/style/test/test_flexbox_reflow_counts.html new file mode 100644 index 0000000000..a8f4913f7d --- /dev/null +++ b/layout/style/test/test_flexbox_reflow_counts.html @@ -0,0 +1,199 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1142686 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1142686</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <style> + .flex { + display: flex; + } + #outerFlex { + border: 1px solid black; + } + </style> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1142686">Mozilla Bug 1142686</a> +<div id="display"> + <div id="content"> + <div class="flex" id="outerFlex"> + <div class="flex" id="midFlex"> + <div id="innerBlock"> + </div> + </div> + </div> + </div> +</div> +<pre id="test"> +<script type="application/javascript"> +"use strict"; + +/** Test for Bug 1142686 **/ + +/** + * This test checks how many reflows are required, when we make a change inside + * a set of two nested flex containers, with various styles applied to + * the containers & innermost child. Some flex layout operations require two + * passes (which can cause exponential blowup). This test is intended to verify + * that certain configurations do *not* require two-pass layout, by comparing + * the reflow-count for a more-complex scenario against a less-complex scenario. + * + * We have two nested flex containers around an initially-empty block. For each + * measurement, we put some text in the block, and we see how many frame-reflow + * operations occur as a result. + */ + +const gUtils = SpecialPowers.getDOMWindowUtils(window); + +// The elements: +const gOuterFlex = document.getElementById("outerFlex"); +const gMidFlex = document.getElementById("midFlex"); +const gInnerBlock = document.getElementById("innerBlock"); + +// This cleanup helper-function removes all children from 'parent' +// except for 'childToPreserve' (if specified) +function removeChildrenExcept(parent, childToPreserve) +{ + if (childToPreserve && childToPreserve.parentNode != parent) { + // This is just a sanity/integrity-check -- if this fails, it's probably a + // bug in this test rather than in the code. I'm not checking this via + // e.g. "is(childToPreserve.parentNode, parent)", because this *should* + // always pass, and each "pass" is not interesting here since it's a + // sanity-check. It's only interesting/noteworthy if it fails. So, to + // avoid bloating this test's passed-subtest-count & output, we only bother + // reporting on this in the case where something's wrong. + ok(false, "bug in test; 'childToPreserve' should be child of 'parent'"); + } + + // For simplicity, we just remove *all children* and then reappend + // childToPreserve as the sole child. + while (parent.firstChild) { + parent.removeChild(parent.firstChild); + } + if (childToPreserve) { + parent.appendChild(childToPreserve); + } +} + +// Appends 'childCount' new children to 'parent' +function addNChildren(parent, childCount) +{ + for (let i = 0; i < childCount; i++) { + let newChild = document.createElement("div"); + // Give the new child some text so it's got a nonzero content-size: + newChild.append("a"); + parent.appendChild(newChild); + } +} + +// Undoes whatever styling customizations and DOM insertions that a given +// testcase has done, to prepare for running the next testcase. +function cleanup() +{ + gOuterFlex.style = gMidFlex.style = gInnerBlock.style = ""; + removeChildrenExcept(gInnerBlock); + removeChildrenExcept(gMidFlex, gInnerBlock); + removeChildrenExcept(gOuterFlex, gMidFlex); +} + +// Each testcase here has a label (used in test output), a function to set up +// the testcase, and (optionally) a function to set up the reference case. +let gTestcases = [ + { + label : "border on flex items", + addTestStyle : function() { + gMidFlex.style.border = gInnerBlock.style.border = "3px solid black"; + }, + }, + { + label : "padding on flex items", + addTestStyle : function() { + gMidFlex.style.padding = gInnerBlock.style.padding = "5px"; + }, + }, + { + label : "margin on flex items", + addTestStyle : function() { + gMidFlex.style.margin = gInnerBlock.style.margin = "2px"; + }, + }, + { + // When we make a change in one flex item, the number of reflows should not + // scale with its number of siblings (as long as those siblings' sizes + // aren't impacted by the change): + label : "additional flex items in outer flex container", + + // Compare 5 bonus flex items vs. 1 bonus flex item: + addTestStyle : function() { + addNChildren(gOuterFlex, 5); + }, + addReferenceStyle : function() { + addNChildren(gOuterFlex, 1); + }, + }, + { + // (As above, but now the bonus flex items are one step deeper in the tree, + // on the nested flex container rather than the outer one) + label : "additional flex items in nested flex container", + addTestStyle : function() { + addNChildren(gMidFlex, 5); + }, + addReferenceStyle : function() { + addNChildren(gMidFlex, 1); + }, + }, +]; + +// Flush layout & return the global frame-reflow-count +function getReflowCount() +{ + let unusedVal = gOuterFlex.offsetHeight; // flush layout + return gUtils.framesReflowed; +} + +// This function adds some text inside of gInnerBlock, and returns the number +// of frames that need to be reflowed as a result. +function makeTweakAndCountReflows() +{ + let beforeCount = getReflowCount(); + gInnerBlock.appendChild(document.createTextNode("hello")); + let afterCount = getReflowCount(); + + let numReflows = afterCount - beforeCount; + if (numReflows <= 0) { + ok(false, "something's wrong -- we should've reflowed *something*"); + } + return numReflows; +} + +// Given a testcase (from gTestcases), this function verifies that the +// testcase scenario requires the same number of reflows as the reference +// scenario. +function runOneTest(aTestcase) +{ + aTestcase.addTestStyle(); + let numTestcaseReflows = makeTweakAndCountReflows(); + cleanup(); + + if (aTestcase.addReferenceStyle) { + aTestcase.addReferenceStyle(); + } + let numReferenceReflows = makeTweakAndCountReflows(); + cleanup(); + + is(numTestcaseReflows, numReferenceReflows, + "Testcase & reference case should require same number of reflows" + + " (testcase label: '" + aTestcase.label + "')"); +} + +gTestcases.forEach(runOneTest); + +</script> +</pre> +</body> +</html> |