<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1508420
-->
<head>
  <meta charset="utf-8">
  <title>
    Test for Bug 1508420: Cases where a frame isn't allowed to be a dynamic
    reflow root
  </title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body onload="main()">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1508420">Mozilla Bug 1508420</a>
<p id="display">
  <!-- Here's the iframe that we'll do all of our testing/snapshotting in: -->
  <iframe srcdoc="<!DOCTYPE html><body></body>"></iframe>
</p>
<script type="application/javascript">
  /** Test for Bug 1508420 **/
  /**
   * This test exercises various cases where we exclude a frame from being
   * flagged as a dynamic reflow root. (We prevent this because we know that
   * there are cases where we'd produce incorrect layout if we initiated reflow
   * from the frame in question.)
   *
   * Roughly, the idea in each subtest here is to do the following:
   *  1) Set up a scenario with some condition that we think should prevent a
   *     particular frame from being flagged as a dynamic reflow root.
   *  2) Make a dynamic tweak that we expect would result in broken layout, if
   *     we had allowed the frame in question to be a dynamic reflow root.
   *     Take a snapshot.
   *  3) Force a full reconstruct + reflow of the document's frames (by
   *     toggling "display:none" on the root element). Take another snapshot.
   *  4) Assert that snapshots look the same -- i.e. that our incremental
   *     reflow didn't produce the wrong layout.
   *
   * Ideally, every condition in ReflowInput::InitDynamicReflowRoot()
   * should have a corresponding subtest here (and the subtest should fail if
   * we remove the condition from InitDynamicReflowRoot).
   */

  // Styles that are sufficient to make a typical element into a reflow root.
  // We apply these styles to "reflow root candidates" throughout this test
  // (and then add other styles that should make the candidate ineligible,
  // typically).
  const gReflowRootCandidateStyles =
    "display: flow-root; will-change: transform; width: 10px; height: 10px;";

  // Some convenience globals for the document inside the iframe:
  // (initialized in 'main' after the iframe gets a chance to load)
  // --------------------------------------------------------------
  let gFWindow;
  let gFDoc;
  let gFBody;

  // Some utility functions used in each test function:
  // --------------------------------------------------
  function createStyledDiv(divStyleStr, divInnerText) {
    let div = gFDoc.createElement("div");
    div.style.cssText = divStyleStr;
    if (typeof divInnerText !== "undefined") {
      div.innerText = divInnerText;
    }
    return div;
  }

  // This function takes an initial snapshot, then a second snapshot after
  // invoking the given tweakFunc, and finally a third after forcing the frame
  // tree to be reconstructed from scratch. Then it compares the snapshots to
  // validate that the tweak did produce a visible change, & that the
  // after-tweak rendering looks the same in the last two snapshots.
  function tweakAndCompareSnapshots(tweakFunc, descPrefix) {
    let snapPreTweak = snapshotWindow(gFWindow, false);
    let descPreTweak = descPrefix + "-initial-rendering";

    // Now we invoke the tweak (changing the size of some content inside the
    // reflow root candidate). If this influences the size of the candidate
    // itself, and we fail to do any reflow outside of the candidate because
    // we made it a reflow root, then we expect to end up with a broken layout
    // due to a parent or sibling not having been resized/repositioned.
    // We'll discover that when comparing snapIncReflow against snapFullReflow
    // below.
    tweakFunc();

    let snapIncReflow = snapshotWindow(gFWindow, false);
    let descIncReflow = descPrefix + "-after-tweak-inc-reflow";

    // Now we trigger a "full" reflow (not incremental), by forcing
    // frame reconstruction all the way from the body element. This should
    // force us to reflow from the actual document root, even if we have
    // promoted any frames to be dynamic reflow roots.
    gFBody.style.display = "none";
    gFBody.offsetTop; // flush layout
    gFBody.style.display = "";
    let snapFullReflow = snapshotWindow(gFWindow, false);
    let descFullReflow = descPrefix + "-after-tweak-full-reflow";

    assertSnapshots(snapIncReflow, snapPreTweak, false, null,
                    descIncReflow, descPreTweak);
    assertSnapshots(snapIncReflow, snapFullReflow, true, null,
                    descIncReflow, descFullReflow);
  }

  // Test functions (called from "main"), with a subtest array in most cases:
  // ------------------------------------------------------------------------

  // Subtests for intrinsic size keywords (and equivalent, e.g. percentages) as
  // values for width/height/{min,max}-{width,height} on reflow root candidates:
  let intrinsicSizeSubtests = [
    { desc: "width-auto",
      candStyle: "width:auto",
    },
    { desc: "width-pct",
      candStyle: "width:80%",
    },
    { desc: "width-calc-pct",
      candStyle: "width:calc(10px + 80%)",
    },
    { desc: "width-min-content",
      candStyle: "width:-moz-min-content; width:min-content;",
    },
    { desc: "width-max-content",
      candStyle: "width:-moz-max-content; width:max-content;",
    },
    { desc: "min-width-min-content",
      candStyle: "min-width:-moz-min-content; min-width:min-content;",
    },
    { desc: "min-width-max-content",
      candStyle: "min-width:-moz-max-content; min-width:max-content;",
    },
    { desc: "max-width-min-content",
      // Note: hardcoded 'width' here must be larger than what 'inner'
      // gets resized to, so that max-width gets a chance to clamp.
      candStyle: "width: 50px; \
                  max-width:-moz-min-content; max-width:min-content;",
    },
    { desc: "max-width-max-content",
      candStyle: "width: 50px; \
                  max-width:-moz-max-content; max-width:max-content;",
    },
    { desc: "height-auto",
      candStyle: "height:auto",
    },
    { desc: "height-pct",
      candStyle: "height:80%",
    },
    { desc: "height-calc-pct",
      candStyle: "height:calc(10px + 80%)",
    },
    { desc: "height-min-content",
      candStyle: "height:-moz-min-content; height:min-content;",
    },
    { desc: "height-max-content",
      candStyle: "height:-moz-max-content; height:max-content;",
    },
    { desc: "min-height-min-content",
      candStyle: "min-height:-moz-min-content; min-height:min-content;",
    },
    { desc: "min-height-max-content",
      candStyle: "min-height:-moz-max-content; min-height:max-content;",
    },
    { desc: "max-height-min-content",
      // Note: hardcoded 'height' here must be larger than what 'inner'
      // gets resized to, so that max-height gets a chance to clamp.
      candStyle: "height: 50px; \
                  max-height:-moz-min-content; max-height:min-content;",
    },
    { desc: "max-height-max-content",
      candStyle: "height: 50px; \
                  max-height:-moz-max-content; max-height:max-content;",
    },
  ];

  // Intrinsic heights (e.g. 'height:auto') should prevent
  // an element from being a reflow root.
  function runIntrinsicSizeSubtest(subtest) {
    // Run each testcase in horizontal & vertical writing mode:
    for (let wmVal of ["horizontal-tb", "vertical-lr"]) {
      gFBody.style.writingMode = wmVal;

      // Short version of WM, for use in logging for snapshot comparison below:
      let wmDesc = (wmVal == "horizontal-tb" ? "-horizWM" : "-vertWM");

      // This outer div is intrinsically sized, and it needs to be reflowed
      // when the size of its child (the reflow root candidate) changes.
      let outer = createStyledDiv("border: 2px solid teal; \
                                   inline-size: -moz-max-content; \
                                   inline-size: max-content");
      // The reflow root candidate:
      let cand = createStyledDiv(gReflowRootCandidateStyles +
                                 subtest.candStyle);

      // Something whose size we can adjust, inside the reflow root candidate:
      let inner = createStyledDiv("height:20px; width:20px; \
                                   border: 1px solid purple");

      cand.appendChild(inner);
      outer.appendChild(cand);
      gFBody.appendChild(outer);

      let tweakFunc = function() {
        inner.style.width = inner.style.height = "40px";
      };

      tweakAndCompareSnapshots(tweakFunc, subtest.desc + wmDesc);

      // clean up
      outer.remove();
      gFBody.style.writingMode = "";
    }
  }

  let flexItemSubtests = [
    { desc: "flex-basis-content",
      candStyle: "flex-basis:content;",
    },
    { desc: "flex-basis-min-content",
      candStyle: "flex-basis:-moz-min-content;flex-basis:min-content;",
    },
    { desc: "flex-basis-auto-width-auto",
      candStyle: "flex-basis:auto;width:auto;",
    },
    // For percent flex-basis, we're concerned with cases where the percent
    // triggers content-based sizing during the flex container's intrinsic
    // sizing step.  So we need to get the container to be intrinsically sized;
    // hence the use of the (optional) "isContainerIntrinsicallySized" flag.
// FIXME(bug 1548078): the following two tests fail to produce a rendering difference:
//    { desc: "flex-basis-pct",
//      candStyle: "flex-basis:80%;",
//      isContainerIntrinsicallySized: true,
//    },
//    { desc: "flex-basis-calc-pct",
//      candStyle: "flex-basis:calc(10px + 80%);",
//      isContainerIntrinsicallySized: true,
//    },
    { desc: "flex-basis-from-pct-isize",
      candStyle: "inline-size:80%",
      isContainerIntrinsicallySized: true,
    },
    { desc: "flex-basis-from-calc-pct-isize",
      candStyle: "inline-size:calc(10px + 80%);",
      isContainerIntrinsicallySized: true,
    },
    // Testing the magic "min-main-size:auto" keyword
    // and other intrinsic min/max sizes
    { desc: "flex-min-inline-size-auto",
      candStyle: "flex:0 5px; inline-size:auto; min-inline-size:auto",
    },
    { desc: "flex-min-inline-size-min-content",
      candStyle: "flex:0 5px; inline-size:auto; min-inline-size:min-content",
    },
    { desc: "flex-min-block-size-auto",
      candStyle: "flex:0 5px; block-size:auto; min-block-size:auto",
      isContainerColumnOriented: true,
    },
    { desc: "flex-min-block-size-auto",
      candStyle: "flex:0 5px; block-size:auto; min-block-size:min-content",
      isContainerColumnOriented: true,
    },
  ];

  // Content-dependent flex-basis values should prevent a flex item
  // from being a reflow root.
  function runFlexItemSubtest(subtest) {
    // We create a flex container with two flex items:
    //  - a simple flex item that just absorbs all extra space
    //  - the reflow root candidate
    let containerSizeVal = subtest.isContainerIntrinsicallySized ?
      "max-content" : "100px";
    let containerSizeDecl =
      "inline-size: " + containerSizeVal + "; " +
      "block-size: " + containerSizeVal + ";";
    let containerFlexDirectionDecl = "flex-direction: " +
      (subtest.isContainerColumnOriented ? "column" : "row") + ";"

    let flexContainer = createStyledDiv("display: flex; \
                                         border: 2px solid teal; " +
                                         containerSizeDecl +
                                         containerFlexDirectionDecl);

    let simpleItem = createStyledDiv("border: 1px solid gray; \
                                      background: yellow; \
                                      min-inline-size: 10px; \
                                      flex: 1");

    // The reflow root candidate
    // (Note that we use min-width:0/min-height:0 by default, but subtests
    // might override that with other values in 'candStyle'.)
    let cand = createStyledDiv(gReflowRootCandidateStyles +
                               " min-width: 0; min-height: 0; " +
                               subtest.candStyle);

    // Something whose size we can adjust, inside the reflow root candidate:
    let inner = createStyledDiv("height:20px; width:20px");

    cand.appendChild(inner);
    flexContainer.appendChild(simpleItem);
    flexContainer.appendChild(cand);
    gFBody.appendChild(flexContainer);

    let tweakFunc = function() {
      inner.style.width = inner.style.height = "40px";
    };
    tweakAndCompareSnapshots(tweakFunc, subtest.desc);

    flexContainer.remove(); // clean up
  }

  let gridItemSubtests = [
    { desc: "grid-pct-inline-isize",
      candStyle: "inline-size:80%",
      isContainerIntrinsicallySized: true,
    },
    { desc: "grid-calc-pct-inline-isize",
      candStyle: "inline-size:calc(10px + 80%);",
      isContainerIntrinsicallySized: true,
    },
    { desc: "grid-min-inline-size-min-content",
      candStyle: "min-inline-size:min-content",
    },
  ];

  // 'auto' and intrinsic size keywords on some properties should prevent
  // a grid item from becoming a reflow root.
  function runGridItemSubtest(subtest) {
    // We create a 4x4 grid container with two grid items:
    //  - a simple grid item that just absorbs all extra space
    //  - the reflow root candidate
    let containerSizeVal = subtest.isContainerIntrinsicallySized ?
      "max-content" : "100px";
    let containerSizeDecl =
      "inline-size: " + containerSizeVal + "; " +
      "block-size: " + containerSizeVal + ";";
    let containerGridDirectionDecl = "grid-auto-flow: " +
      (subtest.isContainerColumnOriented ? "column" : "row") + ";"
    let gridContainer = createStyledDiv("display: grid; \
                                         grid: 1fr auto / 1fr auto; \
                                         border: 2px solid teal; " +
                                         containerSizeDecl +
                                         containerGridDirectionDecl);

    let simpleItem = createStyledDiv("border: 1px solid gray; \
                                      background: yellow;");

    // The reflow root candidate
    let cand = createStyledDiv(gReflowRootCandidateStyles +
                               "background: blue; " +
                               "grid-area:2/2; " +
                               "min-width: 10px; min-height: 10px; " +
                               subtest.candStyle);
    // Something whose size we can adjust, inside the reflow root candidate:
    let inner = createStyledDiv("height:20px; width:20px;");

    cand.appendChild(inner);
    gridContainer.appendChild(simpleItem);
    gridContainer.appendChild(cand);
    gFBody.appendChild(gridContainer);

    let tweakFunc = function() {
      inner.style.width = inner.style.height = "40px";
    };
    tweakAndCompareSnapshots(tweakFunc, subtest.desc);

    gridContainer.remove(); // clean up
  }

  let gridContainerSubtests = [
    { desc: "grid-column-start",
      candStyle: "grid-column-start:2",
    },
    { desc: "grid-column-end",
      candStyle: "grid-column-end:3",
    },
    { desc: "grid-row-start",
      candStyle: "grid-row-start:2",
    },
    { desc: "grid-row-end",
      candStyle: "grid-row-end:3",
    },
  ];

  // Test that changes to grid item properties that affect grid container
  // layout causes a grid container reflow when the item is a reflow root.
  function runGridContainerSubtest(subtest) {
    // We create a 4x4 grid container with one grid item:
    //  - a reflow root grid item that we'll tweak from
    //    the list above. By default it's placed at 1,1
    //    but after the tweak it should be placed elsewhere
    let gridContainer = createStyledDiv("display: grid; \
                                         width: 100px; \
                                         height: 100px; \
                                         grid: 1fr 10px / 1fr 10px; \
                                         border: 2px solid teal");
    // The reflow root candidate
    let cand = createStyledDiv(gReflowRootCandidateStyles +
                               "background: blue; " +
                               " min-width: 10px; min-height: 10px; ");

    gridContainer.appendChild(cand);
    gFBody.appendChild(gridContainer);

    let tweakFunc = function() {
      cand.style.cssText += "; " + subtest.candStyle;
    };
    tweakAndCompareSnapshots(tweakFunc, subtest.desc);

    gridContainer.remove(); // clean up
  }

  let gridSubgridSubtests = [
    { desc: "subgrid",
      candStyle: "grid: subgrid / subgrid",
    },
    { desc: "subgrid-rows",
      candStyle: "grid: subgrid / 20px",
    },
    { desc: "subgrid-columns",
      candStyle: "grid: 20px / subgrid",
    },
  ];

  // Test that a subgrid is not a reflow root.
  function runGridSubgridSubtest(subtest) {
    // We create a 4x4 grid container a with one grid item:
    //  - a reflow root display:grid that we'll style as a subgrid from
    //    the list above. We place an item inside it that we'll tweak
    //    the size of, which should affect the outer grid track sizes.
    let gridContainer = createStyledDiv("display: grid; \
                                         width: 100px; \
                                         height: 100px; \
                                         grid: 1fr auto / 1fr auto; \
                                         border: 2px solid teal");
    // The reflow root candidate
    let cand = createStyledDiv(gReflowRootCandidateStyles +
                               "display: grid;" +
                               "grid-area: 2/2;" +
                               "background: blue;" +
                               "min-width: 10px; min-height: 10px;" +
                               subtest.candStyle);

    // Something whose size we can adjust, inside the subgrid:
    let inner = createStyledDiv("height:20px; width:20px;");

    cand.appendChild(inner);
    gridContainer.appendChild(cand);
    gFBody.appendChild(gridContainer);

    let tweakFunc = function() {
      inner.style.width = inner.style.height = "40px";
    };
    tweakAndCompareSnapshots(tweakFunc, subtest.desc);

    gridContainer.remove(); // clean up
  }

  let tableSubtests = [
    { desc: "table",
      /* Testing the default "display:table" styling that runTableTest uses: */
      candStyle: "",
    },
    { desc: "inline-table",
      candStyle: "display:inline-table;",
    },
    { desc: "table-caption",
      candStyle: "display:table-caption;",
    },
    { desc: "table-cell",
      candStyle: "display:table-cell;",
    },
    { desc: "table-column",
      candStyle: "display:table-column;",
      isColumn: true,
    },
    { desc: "table-column-group",
      candStyle: "display:table-column-group;",
      isColumn: true,
    },
    { desc: "table-row",
      candStyle: "display:table-row;",
    },
    { desc: "table-row-group",
      candStyle: "display:table-row-group;",
    },
  ];

  function runTableSubtest(subtest) {
    let outer = createStyledDiv("");
    let shrinkWrapIB = createStyledDiv("display: inline-block; \
                                        border: 2px solid teal");
    let cand = createStyledDiv("display: table; \
                                width: 1px; height: 1px; \
                                will-change: transform; \
                                border: 1px solid purple;" +
                                subtest.candStyle);
    let inner = createStyledDiv("display: block; \
                                 width: 10px; height: 10px; \
                                 background: pink;");
    if (subtest.isColumn) {
      // The candidate is a table-column / table-column-group, so
      // the inner content that we tweak shouldn't be inside of it.
      // Create an explicit table, separately, and put the candidate
      // (the column/column-group) and the tweakable inner element
      // both inside of that explicit table.
      let table = createStyledDiv("display: table");
      table.appendChild(inner);
      table.appendChild(cand);
      shrinkWrapIB.appendChild(table);
    } else {
      // The candidate is a table or some other table part
      // that can hold content. Just put the tweakable inner
      // element directly inside of it, and let anonymous table parts
      // be generated as-needed.
      cand.appendChild(inner);
      shrinkWrapIB.appendChild(cand);
    }

    outer.appendChild(gFDoc.createTextNode("a"));
    outer.appendChild(shrinkWrapIB);
    gFBody.appendChild(outer);

    let tweakFunc = function() {
      inner.style.width = inner.style.height = "40px";
    };
    tweakAndCompareSnapshots(tweakFunc, subtest.desc);

    outer.remove(); // clean up
  }

  let inlineSubtests = [
    { desc: "inline",
      candStyle: "display:inline",
    },
  ];
  function runInlineSubtest(subtest) {
    let outer = createStyledDiv("");
    let shrinkWrapIB = createStyledDiv("display: inline-block; \
                                        border: 2px solid teal");
    let cand = createStyledDiv(gReflowRootCandidateStyles +
                               subtest.candStyle);
    let inner = createStyledDiv("display: inline-block; \
                                 width: 20px; height: 20px; \
                                 background: pink;");

    cand.appendChild(inner);
    shrinkWrapIB.appendChild(cand);
    outer.appendChild(gFDoc.createTextNode("a"));
    outer.appendChild(shrinkWrapIB);
    gFBody.appendChild(outer);

    let tweakFunc = function() {
      inner.style.width = inner.style.height = "40px";
    };
    tweakAndCompareSnapshots(tweakFunc, subtest.desc);

    outer.remove(); // clean up
  }

  let rubySubtests = [
    { desc: "ruby",
      candStyle: "display:ruby",
    },
    { desc: "ruby-base",
      candStyle: "display:ruby-base",
    },
    { desc: "ruby-base-container",
      candStyle: "display:ruby-base-container",
    },
    { desc: "ruby-text",
      candStyle: "display:ruby-text",
    },
    { desc: "ruby-text-container",
      candStyle: "display:ruby-text-container",
    },
  ];

  function runRubySubtest(subtest) {
    let outer = createStyledDiv("");
    let shrinkWrapIB = createStyledDiv("display: inline-block; \
                                        border: 2px solid teal");
    let cand = createStyledDiv(gReflowRootCandidateStyles +
                               subtest.candStyle);
    let inner = createStyledDiv("display: inline-block; \
                                 width: 20px; height: 20px; \
                                 background: pink;");

    cand.appendChild(inner);
    shrinkWrapIB.appendChild(cand);
    outer.appendChild(gFDoc.createTextNode("a"));
    outer.appendChild(shrinkWrapIB);
    gFBody.appendChild(outer);

    let tweakFunc = function() {
      inner.style.width = inner.style.height = "40px";
    };
    tweakAndCompareSnapshots(tweakFunc, subtest.desc);

    outer.remove(); // clean up
  }

  function runFixedPosTest() {
    // We reset the 'will-change' value on the candidate (overriding
    // 'will-change:transform'), so that it won't be a fixed-pos CB. We also
    // give the candidate some margins to shift it away from the origin, to
    // make it visually clearer that its child's fixed-pos offsets are being
    // resolved against the viewport rather than against the candidate div.
    let cand = createStyledDiv(gReflowRootCandidateStyles +
                               "will-change: initial; \
                                margin: 20px 0 0 30px; \
                                border: 2px solid black;");

    let inner = createStyledDiv("height: 20px; width: 20px; \
                                 background: pink;");
    let fixedPos = createStyledDiv("position: fixed; \
                                    width: 10px; height: 10px; \
                                    background: gray;");

    cand.appendChild(inner);
    cand.appendChild(fixedPos);
    gFBody.appendChild(cand);

    // For our tweak, we'll adjust the size of "inner". This change impacts
    // the position of the "fixedPos" placeholder (specifically, its static
    // position), so this will require an incremental reflow that is rooted at
    // the viewport (the containing block of "fixedPos") in order to produce
    // the correct final layout. This is why "cand" isn't allowed to be a
    // reflow root.
    let tweakFunc = function() {
      inner.style.width = inner.style.height = "40px";
    };
    tweakAndCompareSnapshots(tweakFunc, "fixed-pos");

    cand.remove(); // clean up
  }

  function runMarginCollapseTest() {
    let outer = createStyledDiv("background: lime");

    // We use 'display:block' on the candidate (overriding 'display:flow-root')
    // so that it won't be a block formatting context. (See usage/definition of
    // NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS in our c++ layout code.)
    let cand = createStyledDiv(gReflowRootCandidateStyles +
                               "display: block; \
                                background: purple;");
    // We'll add border to this div in the "tweak" function, which will break
    // the stack of margin collapsed divs.
    let divWithEventualBorder = createStyledDiv("");
    let divWithMargin = createStyledDiv("margin-top: 30px; \
                                         width: 10px; height: 10px; \
                                         background: pink;");

    divWithEventualBorder.appendChild(divWithMargin);
    cand.appendChild(divWithEventualBorder);
    outer.appendChild(cand);
    gFBody.appendChild(outer);

    // For our tweak, we'll add a border around "divWithEventualBorder", which
    // prevents the margin (on "divWithMargin") from collapsing all the way up
    // to the outermost div wrapper (which it does, before the tweak).
    // So: this tweak effectively moves the y-position towards 0, for all
    // div wrappers outside the new border. This includes "outer", the parent
    // of our reflow root candidate. So: if we mistakenly allow "cand" to be a
    // reflow root, then we probably would neglect to adjust the position of
    // "outer" when reacting to this tweak (and we'd catch that & report a
    // test failure in our screenshot comparisons below).
    let tweakFunc = function() {
      divWithEventualBorder.style.border = "2px solid black";
    };
    tweakAndCompareSnapshots(tweakFunc, "margin-uncollapse");

    outer.remove(); // clean up
  }

  function runFloatTest() {
    let outer = createStyledDiv("");

    // We use 'display:block' on the candidate (overriding 'display:flow-root')
    // so that it won't be a block formatting context. (See usage/definition of
    // NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS in our c++ layout code.)
    // This allows floats inside the candidate to affect the position of
    // inline-level content outside of it.
    let cand = createStyledDiv(gReflowRootCandidateStyles +
                               "display: block; \
                                border: 2px solid black;");
    let floatChild = createStyledDiv("float: left; \
                                     width: 60px; height: 60px; \
                                     background: pink;");
    let inlineBlock = createStyledDiv("display: inline-block; \
                                       width: 80px; height: 80px; \
                                       background: teal");
    cand.appendChild(floatChild);
    outer.appendChild(cand);
    outer.appendChild(inlineBlock);
    gFBody.appendChild(outer);

    let tweakFunc = function() {
      floatChild.style.width = floatChild.style.height = "40px";
    };
    tweakAndCompareSnapshots(tweakFunc, "float");

    outer.remove(); // clean up
  }

  function main() {
    SimpleTest.waitForExplicitFinish();

    // Initialize our convenience aliases:
    gFWindow = frames[0].window;
    gFDoc = frames[0].document;
    gFBody = frames[0].document.body;

    for (let subtest of intrinsicSizeSubtests) {
      runIntrinsicSizeSubtest(subtest);
    }
    for (let subtest of flexItemSubtests) {
      runFlexItemSubtest(subtest);
    }
    for (let subtest of gridContainerSubtests) {
      runGridContainerSubtest(subtest);
    }
    for (let subtest of gridSubgridSubtests) {
      runGridSubgridSubtest(subtest);
    }
    for (let subtest of gridItemSubtests) {
      runGridItemSubtest(subtest);
    }
    for (let subtest of tableSubtests) {
      runTableSubtest(subtest);
    }
    for (let subtest of inlineSubtests) {
      runInlineSubtest(subtest);
    }
    for (let subtest of rubySubtests) {
      runRubySubtest(subtest);
    }
    runFixedPosTest();
    runMarginCollapseTest();
    runFloatTest();

    SimpleTest.finish();
  }
</script>
</body>
</html>