summaryrefslogtreecommitdiffstats
path: root/layout/style/test/test_flexbox_reflow_counts.html
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /layout/style/test/test_flexbox_reflow_counts.html
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'layout/style/test/test_flexbox_reflow_counts.html')
-rw-r--r--layout/style/test/test_flexbox_reflow_counts.html199
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>