2015-03-14 09:08:21 +03:00
|
|
|
<!DOCTYPE HTML>
|
|
|
|
<html>
|
|
|
|
<!--
|
|
|
|
https://bugzilla.mozilla.org/show_bug.cgi?id=1142686
|
|
|
|
-->
|
|
|
|
<head>
|
|
|
|
<meta charset="utf-8">
|
|
|
|
<title>Test for Bug 1142686</title>
|
2019-04-16 06:50:44 +03:00
|
|
|
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
2015-03-14 09:08:21 +03:00
|
|
|
<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">
|
2017-02-23 00:10:07 +03:00
|
|
|
<script type="application/javascript">
|
2015-03-14 09:08:21 +03:00
|
|
|
"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");
|
|
|
|
|
2020-07-08 07:12:55 +03:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-14 01:59:03 +03:00
|
|
|
// Undoes whatever styling customizations and DOM insertions that a given
|
|
|
|
// testcase has done, to prepare for running the next testcase.
|
2015-03-14 09:08:21 +03:00
|
|
|
function cleanup()
|
|
|
|
{
|
2020-07-14 01:59:03 +03:00
|
|
|
gOuterFlex.style = gMidFlex.style = gInnerBlock.style = "";
|
|
|
|
removeChildrenExcept(gInnerBlock);
|
|
|
|
removeChildrenExcept(gMidFlex, gInnerBlock);
|
|
|
|
removeChildrenExcept(gOuterFlex, gMidFlex);
|
2015-03-14 09:08:21 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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() {
|
2020-07-14 01:59:03 +03:00
|
|
|
gMidFlex.style.border = gInnerBlock.style.border = "3px solid black";
|
2015-03-14 09:08:21 +03:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label : "padding on flex items",
|
|
|
|
addTestStyle : function() {
|
2020-07-14 01:59:03 +03:00
|
|
|
gMidFlex.style.padding = gInnerBlock.style.padding = "5px";
|
2015-03-14 09:08:21 +03:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label : "margin on flex items",
|
|
|
|
addTestStyle : function() {
|
2020-07-14 01:59:03 +03:00
|
|
|
gMidFlex.style.margin = gInnerBlock.style.margin = "2px";
|
2015-03-14 09:08:21 +03:00
|
|
|
},
|
|
|
|
},
|
2020-07-08 07:12:55 +03:00
|
|
|
{
|
|
|
|
// 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() {
|
2020-07-14 01:59:03 +03:00
|
|
|
addNChildren(gOuterFlex, 5);
|
2020-07-08 07:12:55 +03:00
|
|
|
},
|
|
|
|
addReferenceStyle : function() {
|
2020-07-14 01:59:03 +03:00
|
|
|
addNChildren(gOuterFlex, 1);
|
2020-07-08 07:12:55 +03:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// (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() {
|
2020-07-14 01:59:03 +03:00
|
|
|
addNChildren(gMidFlex, 5);
|
2020-07-08 07:12:55 +03:00
|
|
|
},
|
|
|
|
addReferenceStyle : function() {
|
2020-07-14 01:59:03 +03:00
|
|
|
addNChildren(gMidFlex, 1);
|
2020-07-08 07:12:55 +03:00
|
|
|
},
|
|
|
|
},
|
2015-03-14 09:08:21 +03:00
|
|
|
];
|
|
|
|
|
|
|
|
// 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>
|