test_dynamic_reflow_root_disallowal.html (28910B)
1 <!DOCTYPE HTML> 2 <html> 3 <!-- 4 https://bugzilla.mozilla.org/show_bug.cgi?id=1508420 5 --> 6 <head> 7 <meta charset="utf-8"> 8 <title> 9 Test for Bug 1508420: Cases where a frame isn't allowed to be a dynamic 10 reflow root 11 </title> 12 <script src="/tests/SimpleTest/SimpleTest.js"></script> 13 <script src="/tests/SimpleTest/WindowSnapshot.js"></script> 14 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> 15 </head> 16 <body onload="main()"> 17 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1508420">Mozilla Bug 1508420</a> 18 <p id="display"> 19 <!-- Here's the iframe that we'll do all of our testing/snapshotting in: --> 20 <iframe srcdoc="<!DOCTYPE html><body></body>"></iframe> 21 </p> 22 <script type="application/javascript"> 23 /** Test for Bug 1508420 */ 24 /** 25 * This test exercises various cases where we exclude a frame from being 26 * flagged as a dynamic reflow root. (We prevent this because we know that 27 * there are cases where we'd produce incorrect layout if we initiated reflow 28 * from the frame in question.) 29 * 30 * Roughly, the idea in each subtest here is to do the following: 31 * 1) Set up a scenario with some condition that we think should prevent a 32 * particular frame from being flagged as a dynamic reflow root. 33 * 2) Make a dynamic tweak that we expect would result in broken layout, if 34 * we had allowed the frame in question to be a dynamic reflow root. 35 * Take a snapshot. 36 * 3) Force a full reconstruct + reflow of the document's frames (by 37 * toggling "display:none" on the root element). Take another snapshot. 38 * 4) Assert that snapshots look the same -- i.e. that our incremental 39 * reflow didn't produce the wrong layout. 40 * 41 * Ideally, every condition in ReflowInput::InitDynamicReflowRoot() 42 * should have a corresponding subtest here (and the subtest should fail if 43 * we remove the condition from InitDynamicReflowRoot). 44 */ 45 46 // Styles that are sufficient to make a typical element into a reflow root. 47 // We apply these styles to "reflow root candidates" throughout this test 48 // (and then add other styles that should make the candidate ineligible, 49 // typically). 50 const gReflowRootCandidateStyles = 51 "display: flow-root; will-change: transform; width: 10px; height: 10px;"; 52 53 // Some convenience globals for the document inside the iframe: 54 // (initialized in 'main' after the iframe gets a chance to load) 55 // -------------------------------------------------------------- 56 let gFWindow; 57 let gFDoc; 58 let gFBody; 59 60 // Some utility functions used in each test function: 61 // -------------------------------------------------- 62 function createStyledDiv(divStyleStr, divInnerText) { 63 let div = gFDoc.createElement("div"); 64 div.style.cssText = divStyleStr; 65 if (typeof divInnerText !== "undefined") { 66 div.innerText = divInnerText; 67 } 68 return div; 69 } 70 71 // This function takes an initial snapshot, then a second snapshot after 72 // invoking the given tweakFunc, and finally a third after forcing the frame 73 // tree to be reconstructed from scratch. Then it compares the snapshots to 74 // validate that the tweak did produce a visible change, & that the 75 // after-tweak rendering looks the same in the last two snapshots. 76 function tweakAndCompareSnapshots(tweakFunc, descPrefix) { 77 let snapPreTweak = snapshotWindow(gFWindow, false); 78 let descPreTweak = descPrefix + "-initial-rendering"; 79 80 // Now we invoke the tweak (changing the size of some content inside the 81 // reflow root candidate). If this influences the size of the candidate 82 // itself, and we fail to do any reflow outside of the candidate because 83 // we made it a reflow root, then we expect to end up with a broken layout 84 // due to a parent or sibling not having been resized/repositioned. 85 // We'll discover that when comparing snapIncReflow against snapFullReflow 86 // below. 87 tweakFunc(); 88 89 let snapIncReflow = snapshotWindow(gFWindow, false); 90 let descIncReflow = descPrefix + "-after-tweak-inc-reflow"; 91 92 // Now we trigger a "full" reflow (not incremental), by forcing 93 // frame reconstruction all the way from the body element. This should 94 // force us to reflow from the actual document root, even if we have 95 // promoted any frames to be dynamic reflow roots. 96 gFBody.style.display = "none"; 97 gFBody.offsetTop; // flush layout 98 gFBody.style.display = ""; 99 let snapFullReflow = snapshotWindow(gFWindow, false); 100 let descFullReflow = descPrefix + "-after-tweak-full-reflow"; 101 102 assertSnapshots(snapIncReflow, snapPreTweak, false, null, 103 descIncReflow, descPreTweak); 104 assertSnapshots(snapIncReflow, snapFullReflow, true, null, 105 descIncReflow, descFullReflow); 106 } 107 108 // Test functions (called from "main"), with a subtest array in most cases: 109 // ------------------------------------------------------------------------ 110 111 // Subtests for intrinsic size keywords (and equivalent, e.g. percentages) as 112 // values for width/height/{min,max}-{width,height} on reflow root candidates: 113 let intrinsicSizeSubtests = [ 114 { desc: "width-auto", 115 candStyle: "width:auto", 116 }, 117 { desc: "width-pct", 118 candStyle: "width:80%", 119 }, 120 { desc: "width-calc-pct", 121 candStyle: "width:calc(10px + 80%)", 122 }, 123 { desc: "width-min-content", 124 candStyle: "width:-moz-min-content; width:min-content;", 125 }, 126 { desc: "width-max-content", 127 candStyle: "width:-moz-max-content; width:max-content;", 128 }, 129 { desc: "min-width-min-content", 130 candStyle: "min-width:-moz-min-content; min-width:min-content;", 131 }, 132 { desc: "min-width-max-content", 133 candStyle: "min-width:-moz-max-content; min-width:max-content;", 134 }, 135 { desc: "max-width-min-content", 136 // Note: hardcoded 'width' here must be larger than what 'inner' 137 // gets resized to, so that max-width gets a chance to clamp. 138 candStyle: "width: 50px; \ 139 max-width:-moz-min-content; max-width:min-content;", 140 }, 141 { desc: "max-width-max-content", 142 candStyle: "width: 50px; \ 143 max-width:-moz-max-content; max-width:max-content;", 144 }, 145 { desc: "height-auto", 146 candStyle: "height:auto", 147 }, 148 { desc: "height-pct", 149 candStyle: "height:80%", 150 }, 151 { desc: "height-calc-pct", 152 candStyle: "height:calc(10px + 80%)", 153 }, 154 { desc: "height-min-content", 155 candStyle: "height:-moz-min-content; height:min-content;", 156 }, 157 { desc: "height-max-content", 158 candStyle: "height:-moz-max-content; height:max-content;", 159 }, 160 { desc: "min-height-min-content", 161 candStyle: "min-height:-moz-min-content; min-height:min-content;", 162 }, 163 { desc: "min-height-max-content", 164 candStyle: "min-height:-moz-max-content; min-height:max-content;", 165 }, 166 { desc: "max-height-min-content", 167 // Note: hardcoded 'height' here must be larger than what 'inner' 168 // gets resized to, so that max-height gets a chance to clamp. 169 candStyle: "height: 50px; \ 170 max-height:-moz-min-content; max-height:min-content;", 171 }, 172 { desc: "max-height-max-content", 173 candStyle: "height: 50px; \ 174 max-height:-moz-max-content; max-height:max-content;", 175 }, 176 ]; 177 178 // Intrinsic heights (e.g. 'height:auto') should prevent 179 // an element from being a reflow root. 180 function runIntrinsicSizeSubtest(subtest) { 181 // Run each testcase in horizontal & vertical writing mode: 182 for (let wmVal of ["horizontal-tb", "vertical-lr"]) { 183 gFBody.style.writingMode = wmVal; 184 185 // Short version of WM, for use in logging for snapshot comparison below: 186 let wmDesc = (wmVal == "horizontal-tb" ? "-horizWM" : "-vertWM"); 187 188 // This outer div is intrinsically sized, and it needs to be reflowed 189 // when the size of its child (the reflow root candidate) changes. 190 let outer = createStyledDiv("border: 2px solid teal; \ 191 inline-size: -moz-max-content; \ 192 inline-size: max-content"); 193 // The reflow root candidate: 194 let cand = createStyledDiv(gReflowRootCandidateStyles + 195 subtest.candStyle); 196 197 // Something whose size we can adjust, inside the reflow root candidate: 198 let inner = createStyledDiv("height:20px; width:20px; \ 199 border: 1px solid purple"); 200 201 cand.appendChild(inner); 202 outer.appendChild(cand); 203 gFBody.appendChild(outer); 204 205 let tweakFunc = function() { 206 inner.style.width = inner.style.height = "40px"; 207 }; 208 209 tweakAndCompareSnapshots(tweakFunc, subtest.desc + wmDesc); 210 211 // clean up 212 outer.remove(); 213 gFBody.style.writingMode = ""; 214 } 215 } 216 217 let flexItemSubtests = [ 218 { desc: "flex-basis-content", 219 candStyle: "flex-basis:content;", 220 }, 221 { desc: "flex-basis-min-content", 222 candStyle: "flex-basis:-moz-min-content;flex-basis:min-content;", 223 }, 224 { desc: "flex-basis-auto-width-auto", 225 candStyle: "flex-basis:auto;width:auto;", 226 }, 227 // For percent flex-basis, we're concerned with cases where the percent 228 // triggers content-based sizing during the flex container's intrinsic 229 // sizing step. So we need to get the container to be intrinsically sized; 230 // hence the use of the (optional) "isContainerIntrinsicallySized" flag. 231 // FIXME(bug 1548078): the following two tests fail to produce a rendering difference: 232 // { desc: "flex-basis-pct", 233 // candStyle: "flex-basis:80%;", 234 // isContainerIntrinsicallySized: true, 235 // }, 236 // { desc: "flex-basis-calc-pct", 237 // candStyle: "flex-basis:calc(10px + 80%);", 238 // isContainerIntrinsicallySized: true, 239 // }, 240 { desc: "flex-basis-from-pct-isize", 241 candStyle: "inline-size:80%", 242 isContainerIntrinsicallySized: true, 243 }, 244 { desc: "flex-basis-from-calc-pct-isize", 245 candStyle: "inline-size:calc(10px + 80%);", 246 isContainerIntrinsicallySized: true, 247 }, 248 // Testing the magic "min-main-size:auto" keyword 249 // and other intrinsic min/max sizes 250 { desc: "flex-min-inline-size-auto", 251 candStyle: "flex:0 5px; inline-size:auto; min-inline-size:auto", 252 }, 253 { desc: "flex-min-inline-size-min-content", 254 candStyle: "flex:0 5px; inline-size:auto; min-inline-size:min-content", 255 }, 256 { desc: "flex-min-block-size-auto", 257 candStyle: "flex:0 5px; block-size:auto; min-block-size:auto", 258 isContainerColumnOriented: true, 259 }, 260 { desc: "flex-min-block-size-auto", 261 candStyle: "flex:0 5px; block-size:auto; min-block-size:min-content", 262 isContainerColumnOriented: true, 263 }, 264 ]; 265 266 // Content-dependent flex-basis values should prevent a flex item 267 // from being a reflow root. 268 function runFlexItemSubtest(subtest) { 269 // We create a flex container with two flex items: 270 // - a simple flex item that just absorbs all extra space 271 // - the reflow root candidate 272 let containerSizeVal = subtest.isContainerIntrinsicallySized ? 273 "max-content" : "100px"; 274 let containerSizeDecl = 275 "inline-size: " + containerSizeVal + "; " + 276 "block-size: " + containerSizeVal + ";"; 277 let containerFlexDirectionDecl = "flex-direction: " + 278 (subtest.isContainerColumnOriented ? "column" : "row") + ";" 279 280 let flexContainer = createStyledDiv("display: flex; \ 281 border: 2px solid teal; " + 282 containerSizeDecl + 283 containerFlexDirectionDecl); 284 285 let simpleItem = createStyledDiv("border: 1px solid gray; \ 286 background: yellow; \ 287 min-inline-size: 10px; \ 288 flex: 1"); 289 290 // The reflow root candidate 291 // (Note that we use min-width:0/min-height:0 by default, but subtests 292 // might override that with other values in 'candStyle'.) 293 let cand = createStyledDiv(gReflowRootCandidateStyles + 294 " min-width: 0; min-height: 0; " + 295 subtest.candStyle); 296 297 // Something whose size we can adjust, inside the reflow root candidate: 298 let inner = createStyledDiv("height:20px; width:20px"); 299 300 cand.appendChild(inner); 301 flexContainer.appendChild(simpleItem); 302 flexContainer.appendChild(cand); 303 gFBody.appendChild(flexContainer); 304 305 let tweakFunc = function() { 306 inner.style.width = inner.style.height = "40px"; 307 }; 308 tweakAndCompareSnapshots(tweakFunc, subtest.desc); 309 310 flexContainer.remove(); // clean up 311 } 312 313 let gridItemSubtests = [ 314 { desc: "grid-pct-inline-isize", 315 candStyle: "inline-size:80%", 316 isContainerIntrinsicallySized: true, 317 }, 318 { desc: "grid-calc-pct-inline-isize", 319 candStyle: "inline-size:calc(10px + 80%);", 320 isContainerIntrinsicallySized: true, 321 }, 322 { desc: "grid-min-inline-size-min-content", 323 candStyle: "min-inline-size:min-content", 324 }, 325 ]; 326 327 // 'auto' and intrinsic size keywords on some properties should prevent 328 // a grid item from becoming a reflow root. 329 function runGridItemSubtest(subtest) { 330 // We create a 4x4 grid container with two grid items: 331 // - a simple grid item that just absorbs all extra space 332 // - the reflow root candidate 333 let containerSizeVal = subtest.isContainerIntrinsicallySized ? 334 "max-content" : "100px"; 335 let containerSizeDecl = 336 "inline-size: " + containerSizeVal + "; " + 337 "block-size: " + containerSizeVal + ";"; 338 let containerGridDirectionDecl = "grid-auto-flow: " + 339 (subtest.isContainerColumnOriented ? "column" : "row") + ";" 340 let gridContainer = createStyledDiv("display: grid; \ 341 grid: 1fr auto / 1fr auto; \ 342 border: 2px solid teal; " + 343 containerSizeDecl + 344 containerGridDirectionDecl); 345 346 let simpleItem = createStyledDiv("border: 1px solid gray; \ 347 background: yellow;"); 348 349 // The reflow root candidate 350 let cand = createStyledDiv(gReflowRootCandidateStyles + 351 "background: blue; " + 352 "grid-area:2/2; " + 353 "min-width: 10px; min-height: 10px; " + 354 subtest.candStyle); 355 // Something whose size we can adjust, inside the reflow root candidate: 356 let inner = createStyledDiv("height:20px; width:20px;"); 357 358 cand.appendChild(inner); 359 gridContainer.appendChild(simpleItem); 360 gridContainer.appendChild(cand); 361 gFBody.appendChild(gridContainer); 362 363 let tweakFunc = function() { 364 inner.style.width = inner.style.height = "40px"; 365 }; 366 tweakAndCompareSnapshots(tweakFunc, subtest.desc); 367 368 gridContainer.remove(); // clean up 369 } 370 371 let gridContainerSubtests = [ 372 { desc: "grid-column-start", 373 candStyle: "grid-column-start:2", 374 }, 375 { desc: "grid-column-end", 376 candStyle: "grid-column-end:3", 377 }, 378 { desc: "grid-row-start", 379 candStyle: "grid-row-start:2", 380 }, 381 { desc: "grid-row-end", 382 candStyle: "grid-row-end:3", 383 }, 384 ]; 385 386 // Test that changes to grid item properties that affect grid container 387 // layout causes a grid container reflow when the item is a reflow root. 388 function runGridContainerSubtest(subtest) { 389 // We create a 4x4 grid container with one grid item: 390 // - a reflow root grid item that we'll tweak from 391 // the list above. By default it's placed at 1,1 392 // but after the tweak it should be placed elsewhere 393 let gridContainer = createStyledDiv("display: grid; \ 394 width: 100px; \ 395 height: 100px; \ 396 grid: 1fr 10px / 1fr 10px; \ 397 border: 2px solid teal"); 398 // The reflow root candidate 399 let cand = createStyledDiv(gReflowRootCandidateStyles + 400 "background: blue; " + 401 " min-width: 10px; min-height: 10px; "); 402 403 gridContainer.appendChild(cand); 404 gFBody.appendChild(gridContainer); 405 406 let tweakFunc = function() { 407 cand.style.cssText += "; " + subtest.candStyle; 408 }; 409 tweakAndCompareSnapshots(tweakFunc, subtest.desc); 410 411 gridContainer.remove(); // clean up 412 } 413 414 let gridSubgridSubtests = [ 415 { desc: "subgrid", 416 candStyle: "grid: subgrid / subgrid", 417 }, 418 { desc: "subgrid-rows", 419 candStyle: "grid: subgrid / 20px", 420 }, 421 { desc: "subgrid-columns", 422 candStyle: "grid: 20px / subgrid", 423 }, 424 ]; 425 426 // Test that a subgrid is not a reflow root. 427 function runGridSubgridSubtest(subtest) { 428 // We create a 4x4 grid container a with one grid item: 429 // - a reflow root display:grid that we'll style as a subgrid from 430 // the list above. We place an item inside it that we'll tweak 431 // the size of, which should affect the outer grid track sizes. 432 let gridContainer = createStyledDiv("display: grid; \ 433 width: 100px; \ 434 height: 100px; \ 435 grid: 1fr auto / 1fr auto; \ 436 border: 2px solid teal"); 437 // The reflow root candidate 438 let cand = createStyledDiv(gReflowRootCandidateStyles + 439 "display: grid;" + 440 "grid-area: 2/2;" + 441 "background: blue;" + 442 "min-width: 10px; min-height: 10px;" + 443 subtest.candStyle); 444 445 // Something whose size we can adjust, inside the subgrid: 446 let inner = createStyledDiv("height:20px; width:20px;"); 447 448 cand.appendChild(inner); 449 gridContainer.appendChild(cand); 450 gFBody.appendChild(gridContainer); 451 452 let tweakFunc = function() { 453 inner.style.width = inner.style.height = "40px"; 454 }; 455 tweakAndCompareSnapshots(tweakFunc, subtest.desc); 456 457 gridContainer.remove(); // clean up 458 } 459 460 let tableSubtests = [ 461 { desc: "table", 462 /* Testing the default "display:table" styling that runTableTest uses: */ 463 candStyle: "", 464 }, 465 { desc: "inline-table", 466 candStyle: "display:inline-table;", 467 }, 468 { desc: "table-caption", 469 candStyle: "display:table-caption;", 470 }, 471 { desc: "table-cell", 472 candStyle: "display:table-cell;", 473 }, 474 { desc: "table-column", 475 candStyle: "display:table-column;", 476 isColumn: true, 477 }, 478 { desc: "table-column-group", 479 candStyle: "display:table-column-group;", 480 isColumn: true, 481 }, 482 { desc: "table-row", 483 candStyle: "display:table-row;", 484 }, 485 { desc: "table-row-group", 486 candStyle: "display:table-row-group;", 487 }, 488 ]; 489 490 function runTableSubtest(subtest) { 491 let outer = createStyledDiv(""); 492 let shrinkWrapIB = createStyledDiv("display: inline-block; \ 493 border: 2px solid teal"); 494 let cand = createStyledDiv("display: table; \ 495 width: 1px; height: 1px; \ 496 will-change: transform; \ 497 border: 1px solid purple;" + 498 subtest.candStyle); 499 let inner = createStyledDiv("display: block; \ 500 width: 10px; height: 10px; \ 501 background: pink;"); 502 if (subtest.isColumn) { 503 // The candidate is a table-column / table-column-group, so 504 // the inner content that we tweak shouldn't be inside of it. 505 // Create an explicit table, separately, and put the candidate 506 // (the column/column-group) and the tweakable inner element 507 // both inside of that explicit table. 508 let table = createStyledDiv("display: table"); 509 table.appendChild(inner); 510 table.appendChild(cand); 511 shrinkWrapIB.appendChild(table); 512 } else { 513 // The candidate is a table or some other table part 514 // that can hold content. Just put the tweakable inner 515 // element directly inside of it, and let anonymous table parts 516 // be generated as-needed. 517 cand.appendChild(inner); 518 shrinkWrapIB.appendChild(cand); 519 } 520 521 outer.appendChild(gFDoc.createTextNode("a")); 522 outer.appendChild(shrinkWrapIB); 523 gFBody.appendChild(outer); 524 525 let tweakFunc = function() { 526 inner.style.width = inner.style.height = "40px"; 527 }; 528 tweakAndCompareSnapshots(tweakFunc, subtest.desc); 529 530 outer.remove(); // clean up 531 } 532 533 let inlineSubtests = [ 534 { desc: "inline", 535 candStyle: "display:inline", 536 }, 537 ]; 538 function runInlineSubtest(subtest) { 539 let outer = createStyledDiv(""); 540 let shrinkWrapIB = createStyledDiv("display: inline-block; \ 541 border: 2px solid teal"); 542 let cand = createStyledDiv(gReflowRootCandidateStyles + 543 subtest.candStyle); 544 let inner = createStyledDiv("display: inline-block; \ 545 width: 20px; height: 20px; \ 546 background: pink;"); 547 548 cand.appendChild(inner); 549 shrinkWrapIB.appendChild(cand); 550 outer.appendChild(gFDoc.createTextNode("a")); 551 outer.appendChild(shrinkWrapIB); 552 gFBody.appendChild(outer); 553 554 let tweakFunc = function() { 555 inner.style.width = inner.style.height = "40px"; 556 }; 557 tweakAndCompareSnapshots(tweakFunc, subtest.desc); 558 559 outer.remove(); // clean up 560 } 561 562 let rubySubtests = [ 563 { desc: "ruby", 564 candStyle: "display:ruby", 565 }, 566 { desc: "ruby-base", 567 candStyle: "display:ruby-base", 568 }, 569 { desc: "ruby-base-container", 570 candStyle: "display:ruby-base-container", 571 }, 572 { desc: "ruby-text", 573 candStyle: "display:ruby-text", 574 }, 575 { desc: "ruby-text-container", 576 candStyle: "display:ruby-text-container", 577 }, 578 ]; 579 580 function runRubySubtest(subtest) { 581 let outer = createStyledDiv(""); 582 let shrinkWrapIB = createStyledDiv("display: inline-block; \ 583 border: 2px solid teal"); 584 let cand = createStyledDiv(gReflowRootCandidateStyles + 585 subtest.candStyle); 586 let inner = createStyledDiv("display: inline-block; \ 587 width: 20px; height: 20px; \ 588 background: pink;"); 589 590 cand.appendChild(inner); 591 shrinkWrapIB.appendChild(cand); 592 outer.appendChild(gFDoc.createTextNode("a")); 593 outer.appendChild(shrinkWrapIB); 594 gFBody.appendChild(outer); 595 596 let tweakFunc = function() { 597 inner.style.width = inner.style.height = "40px"; 598 }; 599 tweakAndCompareSnapshots(tweakFunc, subtest.desc); 600 601 outer.remove(); // clean up 602 } 603 604 function runFixedPosTest() { 605 // We reset the 'will-change' value on the candidate (overriding 606 // 'will-change:transform'), so that it won't be a fixed-pos CB. We also 607 // give the candidate some margins to shift it away from the origin, to 608 // make it visually clearer that its child's fixed-pos offsets are being 609 // resolved against the viewport rather than against the candidate div. 610 let cand = createStyledDiv(gReflowRootCandidateStyles + 611 "will-change: initial; \ 612 margin: 20px 0 0 30px; \ 613 border: 2px solid black;"); 614 615 let inner = createStyledDiv("height: 20px; width: 20px; \ 616 background: pink;"); 617 let fixedPos = createStyledDiv("position: fixed; \ 618 width: 10px; height: 10px; \ 619 background: gray;"); 620 621 cand.appendChild(inner); 622 cand.appendChild(fixedPos); 623 gFBody.appendChild(cand); 624 625 // For our tweak, we'll adjust the size of "inner". This change impacts 626 // the position of the "fixedPos" placeholder (specifically, its static 627 // position), so this will require an incremental reflow that is rooted at 628 // the viewport (the containing block of "fixedPos") in order to produce 629 // the correct final layout. This is why "cand" isn't allowed to be a 630 // reflow root. 631 let tweakFunc = function() { 632 inner.style.width = inner.style.height = "40px"; 633 }; 634 tweakAndCompareSnapshots(tweakFunc, "fixed-pos"); 635 636 cand.remove(); // clean up 637 } 638 639 function runMarginCollapseTest() { 640 let outer = createStyledDiv("background: lime"); 641 642 // We use 'display:block' on the candidate (overriding 'display:flow-root') 643 // so that it won't be a block formatting context. (See usage/definition of 644 // NS_BLOCK_BFC_STATE_BITS in our c++ layout code.) 645 let cand = createStyledDiv(gReflowRootCandidateStyles + 646 "display: block; \ 647 background: purple;"); 648 // We'll add border to this div in the "tweak" function, which will break 649 // the stack of margin collapsed divs. 650 let divWithEventualBorder = createStyledDiv(""); 651 let divWithMargin = createStyledDiv("margin-top: 30px; \ 652 width: 10px; height: 10px; \ 653 background: pink;"); 654 655 divWithEventualBorder.appendChild(divWithMargin); 656 cand.appendChild(divWithEventualBorder); 657 outer.appendChild(cand); 658 gFBody.appendChild(outer); 659 660 // For our tweak, we'll add a border around "divWithEventualBorder", which 661 // prevents the margin (on "divWithMargin") from collapsing all the way up 662 // to the outermost div wrapper (which it does, before the tweak). 663 // So: this tweak effectively moves the y-position towards 0, for all 664 // div wrappers outside the new border. This includes "outer", the parent 665 // of our reflow root candidate. So: if we mistakenly allow "cand" to be a 666 // reflow root, then we probably would neglect to adjust the position of 667 // "outer" when reacting to this tweak (and we'd catch that & report a 668 // test failure in our screenshot comparisons below). 669 let tweakFunc = function() { 670 divWithEventualBorder.style.border = "2px solid black"; 671 }; 672 tweakAndCompareSnapshots(tweakFunc, "margin-uncollapse"); 673 674 outer.remove(); // clean up 675 } 676 677 function runFloatTest() { 678 let outer = createStyledDiv(""); 679 680 // We use 'display:block' on the candidate (overriding 'display:flow-root') 681 // so that it won't be a block formatting context. (See usage/definition of 682 // NS_BLOCK_BFC_STATE_BITS in our c++ layout code.) 683 // This allows floats inside the candidate to affect the position of 684 // inline-level content outside of it. 685 let cand = createStyledDiv(gReflowRootCandidateStyles + 686 "display: block; \ 687 border: 2px solid black;"); 688 let floatChild = createStyledDiv("float: left; \ 689 width: 60px; height: 60px; \ 690 background: pink;"); 691 let inlineBlock = createStyledDiv("display: inline-block; \ 692 width: 80px; height: 80px; \ 693 background: teal"); 694 cand.appendChild(floatChild); 695 outer.appendChild(cand); 696 outer.appendChild(inlineBlock); 697 gFBody.appendChild(outer); 698 699 let tweakFunc = function() { 700 floatChild.style.width = floatChild.style.height = "40px"; 701 }; 702 tweakAndCompareSnapshots(tweakFunc, "float"); 703 704 outer.remove(); // clean up 705 } 706 707 function main() { 708 SimpleTest.waitForExplicitFinish(); 709 710 // Initialize our convenience aliases: 711 gFWindow = frames[0].window; 712 gFDoc = frames[0].document; 713 gFBody = frames[0].document.body; 714 715 for (let subtest of intrinsicSizeSubtests) { 716 runIntrinsicSizeSubtest(subtest); 717 } 718 for (let subtest of flexItemSubtests) { 719 runFlexItemSubtest(subtest); 720 } 721 for (let subtest of gridContainerSubtests) { 722 runGridContainerSubtest(subtest); 723 } 724 for (let subtest of gridSubgridSubtests) { 725 runGridSubgridSubtest(subtest); 726 } 727 for (let subtest of gridItemSubtests) { 728 runGridItemSubtest(subtest); 729 } 730 for (let subtest of tableSubtests) { 731 runTableSubtest(subtest); 732 } 733 for (let subtest of inlineSubtests) { 734 runInlineSubtest(subtest); 735 } 736 for (let subtest of rubySubtests) { 737 runRubySubtest(subtest); 738 } 739 runFixedPosTest(); 740 runMarginCollapseTest(); 741 runFloatTest(); 742 743 SimpleTest.finish(); 744 } 745 </script> 746 </body> 747 </html>