summaryrefslogtreecommitdiffstats
path: root/layout/style/test/test_dynamic_change_causing_reflow.html
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/test/test_dynamic_change_causing_reflow.html')
-rw-r--r--layout/style/test/test_dynamic_change_causing_reflow.html413
1 files changed, 413 insertions, 0 deletions
diff --git a/layout/style/test/test_dynamic_change_causing_reflow.html b/layout/style/test/test_dynamic_change_causing_reflow.html
new file mode 100644
index 0000000000..65129a04a1
--- /dev/null
+++ b/layout/style/test/test_dynamic_change_causing_reflow.html
@@ -0,0 +1,413 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1131371
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1131371</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1131371">Mozilla Bug 1131371</a>
+<div id="display">
+ <div id="content">
+ </div>
+ <div id="elemWithAbsPosChild"><div style="position:absolute"></div></div>
+ <div id="elemWithFixedPosChild"><div style="position:fixed"></div></div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+"use strict";
+
+/** Test for Bug 1131371 **/
+
+const elemWithAbsPosChild = document.getElementById("elemWithAbsPosChild");
+const elemWithFixedPosChild = document.getElementById("elemWithFixedPosChild");
+
+/**
+ * This test verifies that certain style changes do or don't cause reflow
+ * and/or frame construction. We do this by checking the framesReflowed &
+ * framesConstructed counts, before & after a style-change, and verifying
+ * that any change to these counts is in line with our expectations.
+ *
+ * Each entry in gTestcases contains these member-values:
+ * - beforeStyle (optional): initial value to use for "style" attribute.
+ * - afterStyle: value to change the "style" attribute to.
+ *
+ * Testcases may also include two optional member-values to express that reflow
+ * and/or frame construction *are* in fact expected:
+ * - expectConstruction (optional): if set to something truthy, then we expect
+ * frame construction to occur when afterStyle is set. Otherwise, we
+ * expect that frame construction should *not* occur.
+ * - expectReflow (optional): if set to something truthy, then we expect
+ * reflow to occur when afterStyle is set. Otherwise, we expect that
+ * reflow should *not* occur.
+ */
+const gTestcases = [
+ // Things that shouldn't cause reflow:
+ // -----------------------------------
+ // * Adding an outline (e.g. for focus ring).
+ {
+ afterStyle: "outline: 1px dotted black",
+ },
+
+ // * Changing between completely different outlines.
+ {
+ beforeStyle: "outline: 2px solid black",
+ afterStyle: "outline: 6px dashed yellow",
+ },
+
+ // * Adding a box-shadow.
+ {
+ afterStyle: "box-shadow: inset 3px 3px gray",
+ },
+ {
+ afterStyle: "box-shadow: 0px 0px 10px 30px blue"
+ },
+
+ // * Changing between completely different box-shadow values,
+ // e.g. from an upper-left shadow to a bottom-right shadow:
+ {
+ beforeStyle: "box-shadow: -15px -20px teal",
+ afterStyle: "box-shadow: 30px 40px yellow",
+ },
+
+ // * Adding a text-shadow.
+ {
+ afterStyle: "text-shadow: 3px 3px gray",
+ },
+ {
+ afterStyle: "text-shadow: 0px 0px 10px blue"
+ },
+
+ // * Changing between completely different text-shadow values,
+ // e.g. from an upper-left shadow to a bottom-right shadow:
+ {
+ beforeStyle: "text-shadow: -15px -20px teal",
+ afterStyle: "text-shadow: 30px 40px yellow",
+ },
+
+ // * switching overflow between things that shouldn't create scrollframes.
+ {
+ beforeStyle: "overflow: visible",
+ afterStyle: "overflow: clip",
+ },
+
+ // Things that *should* cause reflow:
+ // ----------------------------------
+ // (e.g. to make sure our counts are actually measuring something)
+
+ // * Changing 'height' should cause reflow, but not frame construction.
+ {
+ beforeStyle: "height: 10px",
+ afterStyle: "height: 15px",
+ expectReflow: true,
+ },
+
+ // * Changing 'shape-outside' on a non-floating box should not cause anything to happen.
+ {
+ beforeStyle: "shape-outside: none",
+ afterStyle: "shape-outside: circle()",
+ },
+
+ // * Changing 'shape-outside' should cause reflow, but not frame construction.
+ {
+ beforeStyle: "float: left; shape-outside: none",
+ afterStyle: "float: left; shape-outside: circle()",
+ expectReflow: true,
+ },
+
+ // * Changing 'overflow' on <body> should cause reflow,
+ // but not frame reconstruction
+ {
+ elem: document.body,
+ /* beforeStyle: implicitly 'overflow:visible' */
+ afterStyle: "overflow: hidden",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+ {
+ elem: document.body,
+ /* beforeStyle: implicitly 'overflow:visible' */
+ afterStyle: "overflow: scroll",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+ {
+ elem: document.body,
+ beforeStyle: "overflow: hidden",
+ afterStyle: "overflow: auto",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+ {
+ elem: document.body,
+ beforeStyle: "overflow: hidden",
+ afterStyle: "overflow: scroll",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+ {
+ elem: document.body,
+ beforeStyle: "overflow: hidden",
+ afterStyle: "overflow: visible",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+ {
+ elem: document.body,
+ beforeStyle: "overflow: auto",
+ afterStyle: "overflow: hidden",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+ {
+ elem: document.body,
+ beforeStyle: "overflow: visible",
+ afterStyle: "overflow: hidden",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+
+ // * Changing 'overflow' on <html> should cause reflow,
+ // but not frame reconstruction
+ {
+ elem: document.documentElement,
+ /* beforeStyle: implicitly 'overflow:visible' */
+ afterStyle: "overflow: auto",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+ {
+ elem: document.documentElement,
+ beforeStyle: "overflow: visible",
+ afterStyle: "overflow: auto",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+
+ // * Setting 'overflow' on arbitrary node should cause reflow as well as
+ // frame reconstruction
+ {
+ /* beforeStyle: implicitly 'overflow:visible' */
+ afterStyle: "overflow: auto",
+ expectConstruction: true,
+ expectReflow: true,
+ },
+ {
+ beforeStyle: "overflow: auto",
+ afterStyle: "overflow: visible",
+ expectConstruction: true,
+ expectReflow: true,
+ },
+
+ // * but only reflow if we don't need to construct / unconstruct a new frame.
+ {
+ beforeStyle: "overflow: scroll",
+ afterStyle: "overflow: auto",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+ {
+ beforeStyle: "overflow: auto",
+ afterStyle: "overflow: scroll",
+ expectConstruction: false,
+ expectReflow: true,
+ },
+
+ // * FIXME(emilio, bug 1590247): Changes to or from hidden
+ // reconstruct, as we optimize out the scrollbars completely in the overflow:
+ // hidden case, but we could do better.
+ {
+ beforeStyle: "overflow: hidden",
+ afterStyle: "overflow: auto",
+ expectConstruction: true,
+ expectReflow: true,
+ },
+ {
+ beforeStyle: "overflow: auto",
+ afterStyle: "overflow: hidden",
+ expectConstruction: true,
+ expectReflow: true,
+ },
+ {
+ beforeStyle: "overflow: hidden",
+ afterStyle: "overflow: scroll",
+ expectConstruction: true,
+ expectReflow: true,
+ },
+ {
+ beforeStyle: "overflow: scroll",
+ afterStyle: "overflow: hidden",
+ expectConstruction: true,
+ expectReflow: true,
+ },
+ // * Changing 'display' should cause frame construction and reflow.
+ {
+ beforeStyle: "display: inline",
+ afterStyle: "display: table",
+ expectConstruction: true,
+ expectReflow: true,
+ },
+
+
+ // * Position changes trigger a reframe, unless whether we're a containing
+ // block doesn't change, in which case we just need to reflow.
+ {
+ beforeStyle: "position: static",
+ afterStyle: "position: absolute",
+ expectConstruction: true,
+ expectReflow: true,
+ },
+ {
+ beforeStyle: "position: absolute",
+ afterStyle: "position: fixed",
+ expectConstruction: true,
+ expectReflow: true,
+ },
+ {
+ beforeStyle: "position: relative",
+ afterStyle: "position: fixed",
+ expectConstruction: true,
+ expectReflow: true,
+ },
+
+ // This doesn't change whether we're a containing block because there are no
+ // abspos descendants.
+ {
+ afterStyle: "position: static",
+ beforeStyle: "position: relative",
+ expectReflow: true,
+ },
+
+ // This doesn't change whether we're a containing block, shouldn't reframe.
+ {
+ afterStyle: "position: sticky",
+ beforeStyle: "position: relative",
+ expectReflow: true,
+ },
+
+ // These don't change whether we're a containing block for our
+ // absolutely-positioned child, so shouldn't reframe.
+ {
+ elem: elemWithAbsPosChild,
+ afterStyle: "position: sticky",
+ beforeStyle: "position: relative",
+ expectReflow: true,
+ },
+ {
+ elem: elemWithFixedPosChild,
+ afterStyle: "position: sticky",
+ beforeStyle: "position: relative",
+ expectReflow: true,
+ },
+ {
+ elem: elemWithFixedPosChild,
+ afterStyle: "position: static",
+ beforeStyle: "position: relative",
+ expectReflow: true,
+ },
+ {
+ elem: elemWithFixedPosChild,
+ afterStyle: "position: static",
+ beforeStyle: "position: sticky",
+ expectReflow: true,
+ },
+
+ // These ones do though.
+ {
+ elem: elemWithAbsPosChild,
+ afterStyle: "position: static",
+ beforeStyle: "position: relative",
+ expectConstruction: true,
+ expectReflow: true,
+ },
+ {
+ elem: elemWithAbsPosChild,
+ afterStyle: "position: static",
+ beforeStyle: "position: sticky",
+ expectConstruction: true,
+ expectReflow: true,
+ },
+];
+
+// Helper function to let us call either "is" or "isnot" & assemble
+// the failure message, based on the provided parameters.
+function checkFinalCount(aFinalCount, aExpectedCount,
+ aExpectChange, aMsgPrefix, aCountDescription)
+{
+ let compareFunc;
+ let msg = aMsgPrefix;
+ if (aExpectChange) {
+ compareFunc = isnot;
+ msg += "should cause " + aCountDescription;
+ } else {
+ compareFunc = is;
+ msg += "should not cause " + aCountDescription;
+ }
+
+ compareFunc(aFinalCount, aExpectedCount, msg);
+}
+
+// Vars used in runOneTest that we really only have to look up once:
+const gUtils = SpecialPowers.getDOMWindowUtils(window);
+const gElem = document.getElementById("content");
+
+function runOneTest(aTestcase)
+{
+ // sanity-check that we have the one main thing we need:
+ if (!aTestcase.afterStyle) {
+ ok(false, "testcase is missing an 'afterStyle' to change to");
+ return;
+ }
+
+ // Figure out which element we'll be tweaking (defaulting to gElem)
+ let elem = aTestcase.elem ?
+ aTestcase.elem : gElem;
+
+ // Verify that 'style' attribute is unset (avoid causing ourselves trouble):
+ if (elem.hasAttribute("style")) {
+ ok(false,
+ "test element has 'style' attribute already set! We're going to stomp " +
+ "on whatever's there when we clean up...");
+ }
+
+ // Set the "before" style, and compose the first part of the message
+ // to be used in our "is"/"isnot" invocations:
+ let msgPrefix = "Changing style ";
+ if (aTestcase.beforeStyle) {
+ elem.setAttribute("style", aTestcase.beforeStyle);
+ msgPrefix += "from '" + aTestcase.beforeStyle + "' ";
+ }
+ msgPrefix += "to '" + aTestcase.afterStyle + "' ";
+ msgPrefix += "on " + elem.nodeName + " ";
+
+ // Establish initial counts:
+ let unusedVal = elem.offsetHeight; // flush layout
+ let origFramesConstructed = gUtils.framesConstructed;
+ let origFramesReflowed = gUtils.framesReflowed;
+
+ // Make the change and flush:
+ elem.setAttribute("style", aTestcase.afterStyle);
+ unusedVal = elem.offsetHeight; // flush layout
+
+ // Make our is/isnot assertions about whether things should have changed:
+ checkFinalCount(gUtils.framesConstructed, origFramesConstructed,
+ aTestcase.expectConstruction, msgPrefix,
+ "frame construction");
+ checkFinalCount(gUtils.framesReflowed, origFramesReflowed,
+ aTestcase.expectReflow, msgPrefix,
+ "reflow");
+
+ // Clean up!
+ elem.removeAttribute("style");
+}
+
+gTestcases.forEach(runOneTest);
+
+</script>
+</pre>
+</body>
+</html>