test_dynamic_change_causing_reflow.html (27966B)
1 <!DOCTYPE HTML> 2 <html> 3 <!-- 4 https://bugzilla.mozilla.org/show_bug.cgi?id=1131371 5 --> 6 <head> 7 <meta charset="utf-8"> 8 <title>Test for Bug 1131371</title> 9 <script src="/tests/SimpleTest/SimpleTest.js"></script> 10 <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> 11 </head> 12 <body> 13 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1131371">Mozilla Bug 1131371</a> 14 <style> 15 #elemWithScrollbars { overflow: scroll } 16 .flexScrollerWithTransformedItems { 17 display: flex; 18 overflow: hidden; 19 width: 200px; 20 position: relative; 21 } 22 .flexScrollerWithTransformedItems div { 23 min-width: 50px; 24 min-height: 50px; 25 margin: 10px; 26 will-change: transform; 27 } 28 .absposFlexItem { 29 left: 250px; 30 top: 0; 31 position: absolute; 32 } 33 #tableCell, 34 #tableCellWithAbsPosChild { 35 display: table-cell; 36 } 37 .blockmargin { 38 margin: 25px 0; 39 } 40 </style> 41 <div id="display"> 42 <div id="content"> 43 </div> 44 <div id="elemWithAbsPosChild"><div style="position:absolute"></div></div> 45 <div id="elemWithFixedPosChild"><div style="position:fixed"></div></div> 46 <div id="elemWithScrollbars"></div> 47 <div id="elemWithoutScrollbars"></div> 48 <div class="flexScrollerWithTransformedItems"> 49 <div></div> 50 <div></div> 51 <div></div> 52 <div></div> 53 <div id="flexItemMovementTarget"></div> 54 </div> 55 <div class="flexScrollerWithTransformedItems"> 56 <div></div> 57 <div></div> 58 <div></div> 59 <div id="flexItemMovementTarget2"></div> 60 <div class="absposFlexItem"></div> 61 </div> 62 <input id="inputElem" type="image"> 63 <textarea id="textareaElem"> 64 Some 65 lines 66 of 67 text 68 </textarea> 69 <select id="selectElem"> 70 <option>A</option> 71 <option>B</option> 72 <option>C</option> 73 </select> 74 <button id="buttonElem"> 75 Something 76 </button> 77 <button id="buttonElemWithAbsPosChild"><div style="position:absolute"></div></button> 78 <div id="tableCell"></div> 79 <div id="tableCellWithAbsPosChild"> 80 <div style="position: absolute"></div> 81 </div> 82 <div id="containNode"> 83 <div></div> 84 </div> 85 <div id="containNodeWithAbsPosChild"> 86 <div style="position: absolute"></div> 87 </div> 88 <div id="containNodeWithFixedPosChild"> 89 <div style="position: fixed"></div> 90 </div> 91 <div id="containNodeWithFloatingChild"> 92 <div style="float: left"></div> 93 </div> 94 <div id="containNodeWithMarginCollapsing" class="blockmargin"> 95 <div class="blockmargin"></div> 96 </div> 97 <div id="contentVisibilityNode"> 98 <div></div> 99 </div> 100 </div> 101 <pre id="test"> 102 <script> 103 "use strict"; 104 105 /** Test for Bug 1131371 */ 106 107 const elemWithAbsPosChild = document.getElementById("elemWithAbsPosChild"); 108 const elemWithFixedPosChild = document.getElementById("elemWithFixedPosChild"); 109 const elemWithScrollbars = document.getElementById("elemWithScrollbars"); 110 const elemWithoutScrollbars = document.getElementById("elemWithoutScrollbars"); 111 const inputElem = document.getElementById("inputElem"); 112 const textareaElem = document.getElementById("textareaElem"); 113 const selectElem = document.getElementById("selectElem"); 114 const buttonElem = document.getElementById("buttonElem"); 115 const buttonElemWithAbsPosChild = document.getElementById("buttonElemWithAbsPosChild"); 116 const tableCell = document.getElementById("tableCell"); 117 const tableCellWithAbsPosChild = document.getElementById("tableCellWithAbsPosChild"); 118 119 for (let scroller of document.querySelectorAll(".flexScrollerWithTransformedItems")) { 120 scroller.scrollLeft = 10000; 121 } 122 123 const flexItemMovementTarget = document.getElementById("flexItemMovementTarget"); 124 const flexItemMovementTarget2 = document.getElementById("flexItemMovementTarget2"); 125 126 /** 127 * This test verifies that certain style changes do or don't cause reflow 128 * and/or frame construction. We do this by checking the framesReflowed & 129 * framesConstructed counts, before & after a style-change, and verifying 130 * that any change to these counts is in line with our expectations. 131 * 132 * Each entry in gTestcases contains these member-values: 133 * - beforeStyle (optional): initial value to use for "style" attribute. 134 * - afterStyle: value to change the "style" attribute to. 135 * 136 * Testcases may also include two optional member-values to express that reflow 137 * and/or frame construction *are* in fact expected: 138 * - expectConstruction (optional): if set to something truthy, then we expect 139 * frame construction to occur when afterStyle is set. Otherwise, we 140 * expect that frame construction should *not* occur. 141 * - expectReflow (optional): if set to something truthy, then we expect 142 * reflow to occur when afterStyle is set. Otherwise, we expect that 143 * reflow should *not* occur. 144 */ 145 const gTestcases = [ 146 // Things that shouldn't cause reflow: 147 // ----------------------------------- 148 // * Adding an outline (e.g. for focus ring). 149 { 150 afterStyle: "outline: 1px dotted black", 151 }, 152 153 // * Changing between completely different outlines. 154 { 155 beforeStyle: "outline: 2px solid black", 156 afterStyle: "outline: 6px dashed yellow", 157 }, 158 159 // * Adding a box-shadow. 160 { 161 afterStyle: "box-shadow: inset 3px 3px gray", 162 }, 163 { 164 afterStyle: "box-shadow: 0px 0px 10px 30px blue" 165 }, 166 167 // * Changing between completely different box-shadow values, 168 // e.g. from an upper-left shadow to a bottom-right shadow: 169 { 170 beforeStyle: "box-shadow: -15px -20px teal", 171 afterStyle: "box-shadow: 30px 40px yellow", 172 }, 173 174 // * Adding a text-shadow. 175 { 176 afterStyle: "text-shadow: 3px 3px gray", 177 }, 178 { 179 afterStyle: "text-shadow: 0px 0px 10px blue" 180 }, 181 182 // * Changing between completely different text-shadow values, 183 // e.g. from an upper-left shadow to a bottom-right shadow: 184 { 185 beforeStyle: "text-shadow: -15px -20px teal", 186 afterStyle: "text-shadow: 30px 40px yellow", 187 }, 188 189 // * switching overflow between things that shouldn't create scrollframes. 190 { 191 beforeStyle: "overflow: visible", 192 afterStyle: "overflow: clip", 193 }, 194 195 // Things that *should* cause reflow: 196 // ---------------------------------- 197 // (e.g. to make sure our counts are actually measuring something) 198 199 // * Changing 'height' should cause reflow, but not frame construction. 200 { 201 beforeStyle: "height: 10px", 202 afterStyle: "height: 15px", 203 expectReflow: true, 204 }, 205 206 // * Changing 'shape-outside' on a non-floating box should not cause anything to happen. 207 { 208 beforeStyle: "shape-outside: none", 209 afterStyle: "shape-outside: circle()", 210 }, 211 212 // * Changing 'shape-outside' should cause reflow, but not frame construction. 213 { 214 beforeStyle: "float: left; shape-outside: none", 215 afterStyle: "float: left; shape-outside: circle()", 216 expectReflow: true, 217 }, 218 219 // * Changing 'overflow' on <body> should cause reflow, 220 // but not frame reconstruction 221 { 222 elem: document.body, 223 /* beforeStyle: implicitly 'overflow:visible' */ 224 afterStyle: "overflow: hidden", 225 expectConstruction: false, 226 expectReflow: true, 227 }, 228 { 229 elem: document.body, 230 /* beforeStyle: implicitly 'overflow:visible' */ 231 afterStyle: "overflow: scroll", 232 expectConstruction: false, 233 expectReflow: true, 234 }, 235 { 236 elem: document.body, 237 beforeStyle: "overflow: hidden", 238 afterStyle: "overflow: auto", 239 expectConstruction: false, 240 expectReflow: true, 241 }, 242 { 243 elem: document.body, 244 beforeStyle: "overflow: hidden", 245 afterStyle: "overflow: scroll", 246 expectConstruction: false, 247 expectReflow: true, 248 }, 249 { 250 elem: document.body, 251 beforeStyle: "overflow: hidden", 252 afterStyle: "overflow: visible", 253 expectConstruction: false, 254 expectReflow: true, 255 }, 256 { 257 elem: document.body, 258 beforeStyle: "overflow: auto", 259 afterStyle: "overflow: hidden", 260 expectConstruction: false, 261 expectReflow: true, 262 }, 263 { 264 elem: document.body, 265 beforeStyle: "overflow: visible", 266 afterStyle: "overflow: hidden", 267 expectConstruction: false, 268 expectReflow: true, 269 }, 270 271 // * Changing 'overflow' on <html> should cause reflow, 272 // but not frame reconstruction 273 { 274 elem: document.documentElement, 275 /* beforeStyle: implicitly 'overflow:visible' */ 276 afterStyle: "overflow: auto", 277 expectConstruction: false, 278 expectReflow: true, 279 }, 280 { 281 elem: document.documentElement, 282 beforeStyle: "overflow: visible", 283 afterStyle: "overflow: auto", 284 expectConstruction: false, 285 expectReflow: true, 286 }, 287 288 // * Setting 'overflow' on arbitrary node should cause reflow as well as 289 // frame reconstruction 290 { 291 /* beforeStyle: implicitly 'overflow:visible' */ 292 afterStyle: "overflow: auto", 293 expectConstruction: true, 294 expectReflow: true, 295 }, 296 { 297 beforeStyle: "overflow: auto", 298 afterStyle: "overflow: visible", 299 expectConstruction: true, 300 expectReflow: true, 301 }, 302 303 // * but only reflow if we don't need to construct / unconstruct a new frame. 304 { 305 beforeStyle: "overflow: scroll", 306 afterStyle: "overflow: auto", 307 expectConstruction: false, 308 expectReflow: true, 309 }, 310 { 311 beforeStyle: "overflow: auto", 312 afterStyle: "overflow: scroll", 313 expectConstruction: false, 314 expectReflow: true, 315 }, 316 317 { 318 beforeStyle: "overflow: hidden", 319 afterStyle: "overflow: auto", 320 expectConstruction: true, 321 expectReflow: true, 322 }, 323 { 324 beforeStyle: "overflow: auto", 325 afterStyle: "overflow: hidden", 326 expectConstruction: false, 327 expectReflow: true, 328 }, 329 { 330 beforeStyle: "overflow: hidden", 331 afterStyle: "overflow: scroll", 332 expectConstruction: true, 333 expectReflow: true, 334 }, 335 { 336 beforeStyle: "overflow: scroll", 337 afterStyle: "overflow: hidden", 338 expectConstruction: false, 339 expectReflow: true, 340 }, 341 342 { 343 elem: elemWithoutScrollbars, 344 beforeStyle: "scrollbar-width: auto", 345 afterStyle: "scrollbar-width: none", 346 expectConstruction: false, 347 expectReflow: false, 348 }, 349 { 350 elem: elemWithScrollbars, 351 beforeStyle: "scrollbar-width: auto", 352 afterStyle: "scrollbar-width: none", 353 expectConstruction: false, 354 expectReflow: true, 355 }, 356 { 357 // Not the scrolling element, so nothing should happen. 358 elem: document.body, 359 beforeStyle: "scrollbar-width: none", 360 afterStyle: "scrollbar-width: auto", 361 expectConstruction: false, 362 expectReflow: false, 363 }, 364 { 365 // Not the scrolling element, so nothing should happen. 366 elem: document.body, 367 beforeStyle: "scrollbar-width: auto", 368 afterStyle: "scrollbar-width: none", 369 expectConstruction: false, 370 expectReflow: false, 371 }, 372 { 373 elem: document.documentElement, 374 beforeStyle: "scrollbar-width: none;", 375 afterStyle: "scrollbar-width: auto", 376 expectConstruction: false, 377 expectReflow: true, 378 }, 379 { 380 elem: document.documentElement, 381 beforeStyle: "overflow: scroll; scrollbar-width: none;", 382 afterStyle: "overflow: scroll; scrollbar-width: auto", 383 expectConstruction: false, 384 expectReflow: true, 385 }, 386 { 387 elem: document.documentElement, 388 beforeStyle: "overflow: scroll; scrollbar-width: auto;", 389 afterStyle: "overflow: scroll; scrollbar-width: none", 390 expectConstruction: false, 391 expectReflow: true, 392 }, 393 394 // * Changing 'display' should cause frame construction and reflow. 395 { 396 beforeStyle: "display: inline", 397 afterStyle: "display: table", 398 expectConstruction: true, 399 expectReflow: true, 400 }, 401 402 403 // * Position changes trigger a reframe, unless whether we're a containing 404 // block doesn't change, in which case we just need to reflow. 405 { 406 beforeStyle: "position: static", 407 afterStyle: "position: absolute", 408 expectConstruction: true, 409 expectReflow: true, 410 }, 411 { 412 beforeStyle: "position: absolute", 413 afterStyle: "position: fixed", 414 expectConstruction: true, 415 expectReflow: true, 416 }, 417 { 418 beforeStyle: "position: relative", 419 afterStyle: "position: fixed", 420 expectConstruction: true, 421 expectReflow: true, 422 }, 423 424 // This doesn't change whether we're a containing block because there are no 425 // abspos descendants. 426 { 427 afterStyle: "position: static", 428 beforeStyle: "position: relative", 429 expectReflow: true, 430 }, 431 432 // This doesn't change whether we're a containing block, shouldn't reframe. 433 { 434 afterStyle: "position: sticky", 435 beforeStyle: "position: relative", 436 expectReflow: true, 437 }, 438 439 // These don't change whether we're a containing block for our 440 // absolutely-positioned child, so shouldn't reframe. 441 { 442 elem: elemWithAbsPosChild, 443 afterStyle: "position: sticky", 444 beforeStyle: "position: relative", 445 expectReflow: true, 446 }, 447 { 448 elem: elemWithFixedPosChild, 449 afterStyle: "position: sticky", 450 beforeStyle: "position: relative", 451 expectReflow: true, 452 }, 453 { 454 elem: elemWithFixedPosChild, 455 afterStyle: "position: static", 456 beforeStyle: "position: relative", 457 expectReflow: true, 458 }, 459 { 460 elem: elemWithFixedPosChild, 461 afterStyle: "position: static", 462 beforeStyle: "position: sticky", 463 expectReflow: true, 464 }, 465 { 466 // Even if we're a scroll frame. 467 elem: elemWithFixedPosChild, 468 afterStyle: "position: static; overflow: auto;", 469 beforeStyle: "position: relative; overflow: auto;", 470 expectReflow: true, 471 }, 472 { 473 elem: tableCell, 474 afterStyle: "position: static;", 475 beforeStyle: "position: relative;", 476 expectReflow: true, 477 }, 478 { 479 elem: tableCell, 480 afterStyle: "filter: none", 481 beforeStyle: "filter: saturate(1)", 482 expectReflow: false, 483 }, 484 485 // These ones do though. 486 { 487 elem: elemWithAbsPosChild, 488 afterStyle: "position: static", 489 beforeStyle: "position: relative", 490 expectConstruction: true, 491 expectReflow: true, 492 }, 493 { 494 elem: elemWithAbsPosChild, 495 afterStyle: "position: static", 496 beforeStyle: "position: sticky", 497 expectConstruction: true, 498 expectReflow: true, 499 }, 500 { 501 elem: elemWithAbsPosChild, 502 afterStyle: "position: static; overflow: auto;", 503 beforeStyle: "position: relative; overflow: auto;", 504 expectConstruction: true, 505 expectReflow: true, 506 }, 507 { 508 elem: tableCellWithAbsPosChild, 509 afterStyle: "position: static;", 510 beforeStyle: "position: relative;", 511 expectConstruction: true, 512 expectReflow: true, 513 }, 514 515 // Adding transform to a scrollframe without abspos / fixedpos children shouldn't reframe. 516 { 517 elem: elemWithScrollbars, 518 afterStyle: "transform: translateX(1px)", 519 expectConstruction: false, 520 expectReflow: false, 521 }, 522 523 // <select> can't contain abspos / floating children so shouldn't reframe 524 // when changing containing block-ness. 525 { 526 elem: selectElem, 527 afterStyle: "transform: translateX(1px)", 528 expectConstruction: false, 529 expectReflow: false, 530 }, 531 { 532 elem: selectElem, 533 afterStyle: "position: relative", 534 expectConstruction: false, 535 expectReflow: true, 536 }, 537 538 // <button> shouldn't be reframed either in the absence of positioned descendants. 539 { 540 elem: buttonElem, 541 afterStyle: "transform: translateX(1px)", 542 expectConstruction: false, 543 expectReflow: false, 544 }, 545 { 546 elem: buttonElem, 547 afterStyle: "position: relative", 548 expectConstruction: false, 549 expectReflow: true, 550 }, 551 { 552 elem: buttonElemWithAbsPosChild, 553 afterStyle: "position: relative", 554 expectConstruction: true, 555 expectReflow: true, 556 }, 557 // changing scroll-behavior should not cause reflow or frame construction 558 { 559 elem: document.documentElement, 560 /* beforeStyle: implicitly 'scroll-behavior: auto' */ 561 afterStyle: "scroll-behavior: smooth", 562 expectConstruction: false, 563 expectReflow: false, 564 }, 565 { 566 elem: document.documentElement, 567 beforeStyle: "scroll-behavior: smooth", 568 afterStyle: "scroll-behavior: auto", 569 expectConstruction: false, 570 expectReflow: false, 571 }, 572 { 573 elem: document.body, 574 /* beforeStyle: implicitly 'scroll-behavior: auto' */ 575 afterStyle: "scroll-behavior: smooth", 576 expectConstruction: false, 577 expectReflow: false, 578 }, 579 { 580 elem: document.body, 581 beforeStyle: "scroll-behavior: smooth", 582 afterStyle: "scroll-behavior: auto", 583 expectConstruction: false, 584 expectReflow: false, 585 }, 586 // changing scroll-snap-type should not cause reflow or frame construction 587 { 588 elem: document.documentElement, 589 /* beforeStyle: implicitly 'scroll-snap-type: none' */ 590 afterStyle: "scroll-snap-type: y mandatory", 591 expectConstruction: false, 592 expectReflow: false, 593 }, 594 { 595 elem: document.documentElement, 596 /* beforeStyle: implicitly 'scroll-snap-type: none' */ 597 afterStyle: "scroll-snap-type: x proximity", 598 expectConstruction: false, 599 expectReflow: false, 600 }, 601 { 602 elem: document.documentElement, 603 beforeStyle: "scroll-snap-type: y mandatory", 604 afterStyle: "scroll-snap-type: none", 605 expectConstruction: false, 606 expectReflow: false, 607 }, 608 { 609 elem: document.body, 610 /* beforeStyle: implicitly 'scroll-snap-type: none' */ 611 afterStyle: "scroll-snap-type: y mandatory", 612 expectConstruction: false, 613 expectReflow: false, 614 }, 615 { 616 elem: document.body, 617 /* beforeStyle: implicitly 'scroll-snap-type: none' */ 618 afterStyle: "scroll-snap-type: x proximity", 619 expectConstruction: false, 620 expectReflow: false, 621 }, 622 { 623 elem: document.body, 624 beforeStyle: "scroll-snap-type: y mandatory", 625 afterStyle: "scroll-snap-type: none", 626 expectConstruction: false, 627 expectReflow: false, 628 }, 629 { 630 elem: inputElem, 631 beforeStyle: "overflow: auto", 632 afterStyle: "overflow: hidden", 633 expectConstruction: false, 634 expectReflow: true, 635 }, 636 { 637 elem: textareaElem, 638 beforeStyle: "overflow: auto", 639 afterStyle: "overflow: hidden", 640 expectConstruction: false, 641 expectReflow: true, 642 }, 643 { 644 elem: flexItemMovementTarget, 645 beforeStyle: "transform: translateX(0)", 646 afterStyle: "transform: translateX(-100px)", 647 expectConstruction: false, 648 expectReflow: false, 649 }, 650 { 651 elem: flexItemMovementTarget2, 652 beforeStyle: "transform: translateX(0)", 653 afterStyle: "transform: translateX(-100px)", 654 expectConstruction: false, 655 expectReflow: false, 656 }, 657 // Style containment affects counters so we need to re-frame. 658 // For other containments, we only need to reflow. 659 { 660 elem: containNode, 661 beforeStyle: "contain: none", 662 afterStyle: "contain: style", 663 expectConstruction: true, 664 expectReflow: true, 665 }, 666 { 667 elem: containNode, 668 beforeStyle: "contain: style", 669 afterStyle: "contain: none", 670 expectConstruction: true, 671 expectReflow: true, 672 }, 673 { 674 elem: containNode, 675 beforeStyle: "contain: none", 676 afterStyle: "contain: paint", 677 expectConstruction: false, 678 expectReflow: true, 679 }, 680 { 681 elem: containNode, 682 beforeStyle: "contain: paint", 683 afterStyle: "contain: none", 684 expectConstruction: false, 685 expectReflow: true, 686 }, 687 { 688 elem: containNode, 689 beforeStyle: "contain: none", 690 afterStyle: "contain: layout", 691 expectConstruction: false, 692 expectReflow: true, 693 }, 694 { 695 elem: containNode, 696 beforeStyle: "contain: layout", 697 afterStyle: "contain: none", 698 expectConstruction: false, 699 expectReflow: true, 700 }, 701 { 702 elem: containNode, 703 beforeStyle: "contain: none", 704 afterStyle: "contain: size", 705 expectConstruction: false, 706 expectReflow: true, 707 }, 708 { 709 elem: containNode, 710 beforeStyle: "contain: size", 711 afterStyle: "contain: none", 712 expectConstruction: false, 713 expectReflow: true, 714 }, 715 // paint/layout containment boxes establish an absolute positioning 716 // containing block, so need a re-frame to handle abs/fixed positioned boxes. 717 { 718 elem: containNodeWithAbsPosChild, 719 beforeStyle: "contain: none", 720 afterStyle: "contain: paint", 721 expectConstruction: true, 722 expectReflow: true, 723 }, 724 { 725 elem: containNodeWithAbsPosChild, 726 beforeStyle: "contain: paint", 727 afterStyle: "contain: none", 728 expectConstruction: true, 729 expectReflow: true, 730 }, 731 { 732 elem: containNodeWithAbsPosChild, 733 beforeStyle: "contain: none", 734 afterStyle: "contain: layout", 735 expectConstruction: true, 736 expectReflow: true, 737 }, 738 { 739 elem: containNodeWithAbsPosChild, 740 beforeStyle: "contain: layout", 741 afterStyle: "contain: none", 742 expectConstruction: true, 743 expectReflow: true, 744 }, 745 { 746 elem: containNodeWithAbsPosChild, 747 beforeStyle: "contain: none", 748 afterStyle: "contain: size", 749 expectConstruction: false, 750 expectReflow: true, 751 }, 752 { 753 elem: containNodeWithAbsPosChild, 754 beforeStyle: "contain: size", 755 afterStyle: "contain: none", 756 expectConstruction: false, 757 expectReflow: true, 758 }, 759 { 760 elem: containNodeWithFixedPosChild, 761 beforeStyle: "contain: none", 762 afterStyle: "contain: paint", 763 expectConstruction: true, 764 expectReflow: true, 765 }, 766 { 767 elem: containNodeWithFixedPosChild, 768 beforeStyle: "contain: paint", 769 afterStyle: "contain: none", 770 expectConstruction: true, 771 expectReflow: true, 772 }, 773 { 774 elem: containNodeWithFixedPosChild, 775 beforeStyle: "contain: none", 776 afterStyle: "contain: layout", 777 expectConstruction: true, 778 expectReflow: true, 779 }, 780 { 781 elem: containNodeWithFixedPosChild, 782 beforeStyle: "contain: layout", 783 afterStyle: "contain: none", 784 expectConstruction: true, 785 expectReflow: true, 786 }, 787 { 788 elem: containNodeWithFixedPosChild, 789 beforeStyle: "contain: none", 790 afterStyle: "contain: size", 791 expectConstruction: false, 792 expectReflow: true, 793 }, 794 { 795 elem: containNodeWithFixedPosChild, 796 beforeStyle: "contain: size", 797 afterStyle: "contain: none", 798 expectConstruction: false, 799 expectReflow: true, 800 }, 801 // paint/layout containment boxes establish an independent formatting context 802 // but floats and margin collapsing can be handled without reconstruction. 803 { 804 elem: containNodeWithFloatingChild, 805 beforeStyle: "contain: none", 806 afterStyle: "contain: paint", 807 expectConstruction: false, 808 expectReflow: true, 809 }, 810 { 811 elem: containNodeWithFloatingChild, 812 beforeStyle: "contain: paint", 813 afterStyle: "contain: none", 814 expectConstruction: false, 815 expectReflow: true, 816 }, 817 { 818 elem: containNodeWithFloatingChild, 819 beforeStyle: "contain: none", 820 afterStyle: "contain: layout", 821 expectConstruction: false, 822 expectReflow: true, 823 }, 824 { 825 elem: containNodeWithFloatingChild, 826 beforeStyle: "contain: layout", 827 afterStyle: "contain: none", 828 expectConstruction: false, 829 expectReflow: true, 830 }, 831 { 832 elem: containNodeWithFloatingChild, 833 beforeStyle: "contain: none", 834 afterStyle: "contain: size", 835 expectConstruction: false, 836 expectReflow: true, 837 }, 838 { 839 elem: containNodeWithFloatingChild, 840 beforeStyle: "contain: size", 841 afterStyle: "contain: none", 842 expectConstruction: false, 843 expectReflow: true, 844 }, 845 { 846 elem: containNodeWithMarginCollapsing, 847 beforeStyle: "contain: none", 848 afterStyle: "contain: paint", 849 expectConstruction: false, 850 expectReflow: true, 851 }, 852 { 853 elem: containNodeWithMarginCollapsing, 854 beforeStyle: "contain: paint", 855 afterStyle: "contain: none", 856 expectConstruction: false, 857 expectReflow: true, 858 }, 859 { 860 elem: containNodeWithMarginCollapsing, 861 beforeStyle: "contain: none", 862 afterStyle: "contain: layout", 863 expectConstruction: false, 864 expectReflow: true, 865 }, 866 { 867 elem: containNodeWithMarginCollapsing, 868 beforeStyle: "contain: layout", 869 afterStyle: "contain: none", 870 expectConstruction: false, 871 expectReflow: true, 872 }, 873 // content-visibility: auto/hidden implies style containment contrary to 874 // content-visibility: visible, so we generally need a re-frame (see above) 875 // when going from one case to the other. 876 { 877 elem: contentVisibilityNode, 878 beforeStyle: "content-visibility: visible", 879 afterStyle: "content-visibility: hidden", 880 expectConstruction: true, 881 expectReflow: true, 882 }, 883 { 884 elem: contentVisibilityNode, 885 beforeStyle: "content-visibility: hidden", 886 afterStyle: "content-visibility: visible", 887 expectConstruction: true, 888 expectReflow: true, 889 }, 890 { 891 elem: contentVisibilityNode, 892 beforeStyle: "content-visibility: visible", 893 afterStyle: "content-visibility: auto", 894 expectConstruction: true, 895 expectReflow: true, 896 }, 897 { 898 elem: contentVisibilityNode, 899 beforeStyle: "content-visibility: auto", 900 afterStyle: "content-visibility: visible", 901 expectConstruction: true, 902 expectReflow: true, 903 }, 904 { 905 elem: contentVisibilityNode, 906 beforeStyle: "content-visibility: hidden", 907 afterStyle: "content-visibility: auto", 908 expectConstruction: false, 909 expectReflow: true, 910 }, 911 { 912 elem: contentVisibilityNode, 913 beforeStyle: "content-visibility: auto", 914 afterStyle: "content-visibility: hidden", 915 expectConstruction: false, 916 expectReflow: true, 917 }, 918 // However that's not the case if we force style containment explicitly. 919 { 920 elem: contentVisibilityNode, 921 beforeStyle: "content-visibility: visible; contain: style", 922 afterStyle: "content-visibility: hidden; contain: style", 923 expectConstruction: false, 924 expectReflow: true, 925 }, 926 { 927 elem: contentVisibilityNode, 928 beforeStyle: "content-visibility: hidden; contain: style", 929 afterStyle: "content-visibility: visible; contain: style", 930 expectConstruction: false, 931 expectReflow: true, 932 }, 933 ]; 934 935 // Helper function to let us call either "is" or "isnot" & assemble 936 // the failure message, based on the provided parameters. 937 function checkFinalCount(aFinalCount, aExpectedCount, 938 aExpectChange, aMsgPrefix, aCountDescription) 939 { 940 let compareFunc; 941 let msg = aMsgPrefix; 942 if (aExpectChange) { 943 compareFunc = isnot; 944 msg += "should cause " + aCountDescription; 945 } else { 946 compareFunc = is; 947 msg += "should not cause " + aCountDescription; 948 } 949 950 compareFunc(aFinalCount, aExpectedCount, msg); 951 } 952 953 // Vars used in runOneTest that we really only have to look up once: 954 const gUtils = SpecialPowers.getDOMWindowUtils(window); 955 const gElem = document.getElementById("content"); 956 957 function runOneTest(aTestcase) 958 { 959 // sanity-check that we have the one main thing we need: 960 if (!aTestcase.afterStyle) { 961 ok(false, "testcase is missing an 'afterStyle' to change to"); 962 return; 963 } 964 965 // Figure out which element we'll be tweaking (defaulting to gElem) 966 let elem = aTestcase.elem ? aTestcase.elem : gElem; 967 968 // Verify that 'style' attribute is unset (avoid causing ourselves trouble): 969 const oldStyle = elem.getAttribute("style"); 970 971 // Set the "before" style, and compose the first part of the message 972 // to be used in our "is"/"isnot" invocations: 973 let msgPrefix = "Changing style "; 974 if (aTestcase.beforeStyle) { 975 elem.setAttribute("style", aTestcase.beforeStyle); 976 msgPrefix += "from '" + aTestcase.beforeStyle + "' "; 977 } 978 msgPrefix += "to '" + aTestcase.afterStyle + "' "; 979 msgPrefix += "on " + elem.nodeName + " "; 980 981 // Establish initial counts: 982 let unusedVal = elem.offsetHeight; // flush layout 983 let origFramesConstructed = gUtils.framesConstructed; 984 let origFramesReflowed = gUtils.framesReflowed; 985 986 // Make the change and flush: 987 elem.setAttribute("style", aTestcase.afterStyle); 988 unusedVal = elem.offsetHeight; // flush layout 989 990 // Make our is/isnot assertions about whether things should have changed: 991 checkFinalCount(gUtils.framesConstructed, origFramesConstructed, 992 aTestcase.expectConstruction, msgPrefix, 993 "frame construction"); 994 checkFinalCount(gUtils.framesReflowed, origFramesReflowed, 995 aTestcase.expectReflow, msgPrefix, 996 "reflow"); 997 998 // Clean up! 999 if (oldStyle) { 1000 elem.setAttribute("style", oldStyle); 1001 } else { 1002 elem.removeAttribute("style"); 1003 } 1004 1005 unusedVal = elem.offsetHeight; // flush layout 1006 } 1007 1008 gTestcases.forEach(runOneTest); 1009 1010 </script> 1011 </pre> 1012 </body> 1013 </html>