Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / layout / style / test / test_dynamic_change_causing_reflow.html
blobbf3b7578b84f31c60c3db298d97657c31c3b59bd
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;
22 .flexScrollerWithTransformedItems div {
23 min-width: 50px;
24 min-height: 50px;
25 margin: 10px;
26 will-change: transform;
28 .absposFlexItem {
29 left: 250px;
30 top: 0;
31 position: absolute;
33 #tableCell,
34 #tableCellWithAbsPosChild {
35 display: table-cell;
37 </style>
38 <div id="display">
39 <div id="content">
40 </div>
41 <div id="elemWithAbsPosChild"><div style="position:absolute"></div></div>
42 <div id="elemWithFixedPosChild"><div style="position:fixed"></div></div>
43 <div id="elemWithScrollbars"></div>
44 <div id="elemWithoutScrollbars"></div>
45 <div class="flexScrollerWithTransformedItems">
46 <div></div>
47 <div></div>
48 <div></div>
49 <div></div>
50 <div id="flexItemMovementTarget"></div>
51 </div>
52 <div class="flexScrollerWithTransformedItems">
53 <div></div>
54 <div></div>
55 <div></div>
56 <div id="flexItemMovementTarget2"></div>
57 <div class="absposFlexItem"></div>
58 </div>
59 <input id="inputElem">
60 <textarea id="textareaElem">
61 Some
62 lines
64 text
65 </textarea>
66 <select id="selectElem">
67 <option>A</option>
68 <option>B</option>
69 <option>C</option>
70 </select>
71 <button id="buttonElem">
72 Something
73 </button>
74 <button id="buttonElemWithAbsPosChild"><div style="position:absolute"></div></button>
75 <div id="tableCell"></div>
76 <div id="tableCellWithAbsPosChild">
77 <div style="position: absolute"></div>
78 </div>
79 </div>
80 <pre id="test">
81 <script>
82 "use strict";
84 /** Test for Bug 1131371 **/
86 const elemWithAbsPosChild = document.getElementById("elemWithAbsPosChild");
87 const elemWithFixedPosChild = document.getElementById("elemWithFixedPosChild");
88 const elemWithScrollbars = document.getElementById("elemWithScrollbars");
89 const elemWithoutScrollbars = document.getElementById("elemWithoutScrollbars");
90 const inputElem = document.getElementById("inputElem");
91 const textareaElem = document.getElementById("textareaElem");
92 const selectElem = document.getElementById("selectElem");
93 const buttonElem = document.getElementById("buttonElem");
94 const buttonElemWithAbsPosChild = document.getElementById("buttonElemWithAbsPosChild");
95 const tableCell = document.getElementById("tableCell");
96 const tableCellWithAbsPosChild = document.getElementById("tableCellWithAbsPosChild");
98 for (let scroller of document.querySelectorAll(".flexScrollerWithTransformedItems")) {
99 scroller.scrollLeft = 10000;
102 const flexItemMovementTarget = document.getElementById("flexItemMovementTarget");
103 const flexItemMovementTarget2 = document.getElementById("flexItemMovementTarget2");
106 * This test verifies that certain style changes do or don't cause reflow
107 * and/or frame construction. We do this by checking the framesReflowed &
108 * framesConstructed counts, before & after a style-change, and verifying
109 * that any change to these counts is in line with our expectations.
111 * Each entry in gTestcases contains these member-values:
112 * - beforeStyle (optional): initial value to use for "style" attribute.
113 * - afterStyle: value to change the "style" attribute to.
115 * Testcases may also include two optional member-values to express that reflow
116 * and/or frame construction *are* in fact expected:
117 * - expectConstruction (optional): if set to something truthy, then we expect
118 * frame construction to occur when afterStyle is set. Otherwise, we
119 * expect that frame construction should *not* occur.
120 * - expectReflow (optional): if set to something truthy, then we expect
121 * reflow to occur when afterStyle is set. Otherwise, we expect that
122 * reflow should *not* occur.
124 const gTestcases = [
125 // Things that shouldn't cause reflow:
126 // -----------------------------------
127 // * Adding an outline (e.g. for focus ring).
129 afterStyle: "outline: 1px dotted black",
132 // * Changing between completely different outlines.
134 beforeStyle: "outline: 2px solid black",
135 afterStyle: "outline: 6px dashed yellow",
138 // * Adding a box-shadow.
140 afterStyle: "box-shadow: inset 3px 3px gray",
143 afterStyle: "box-shadow: 0px 0px 10px 30px blue"
146 // * Changing between completely different box-shadow values,
147 // e.g. from an upper-left shadow to a bottom-right shadow:
149 beforeStyle: "box-shadow: -15px -20px teal",
150 afterStyle: "box-shadow: 30px 40px yellow",
153 // * Adding a text-shadow.
155 afterStyle: "text-shadow: 3px 3px gray",
158 afterStyle: "text-shadow: 0px 0px 10px blue"
161 // * Changing between completely different text-shadow values,
162 // e.g. from an upper-left shadow to a bottom-right shadow:
164 beforeStyle: "text-shadow: -15px -20px teal",
165 afterStyle: "text-shadow: 30px 40px yellow",
168 // * switching overflow between things that shouldn't create scrollframes.
170 beforeStyle: "overflow: visible",
171 afterStyle: "overflow: clip",
174 // Things that *should* cause reflow:
175 // ----------------------------------
176 // (e.g. to make sure our counts are actually measuring something)
178 // * Changing 'height' should cause reflow, but not frame construction.
180 beforeStyle: "height: 10px",
181 afterStyle: "height: 15px",
182 expectReflow: true,
185 // * Changing 'shape-outside' on a non-floating box should not cause anything to happen.
187 beforeStyle: "shape-outside: none",
188 afterStyle: "shape-outside: circle()",
191 // * Changing 'shape-outside' should cause reflow, but not frame construction.
193 beforeStyle: "float: left; shape-outside: none",
194 afterStyle: "float: left; shape-outside: circle()",
195 expectReflow: true,
198 // * Changing 'overflow' on <body> should cause reflow,
199 // but not frame reconstruction
201 elem: document.body,
202 /* beforeStyle: implicitly 'overflow:visible' */
203 afterStyle: "overflow: hidden",
204 expectConstruction: false,
205 expectReflow: true,
208 elem: document.body,
209 /* beforeStyle: implicitly 'overflow:visible' */
210 afterStyle: "overflow: scroll",
211 expectConstruction: false,
212 expectReflow: true,
215 elem: document.body,
216 beforeStyle: "overflow: hidden",
217 afterStyle: "overflow: auto",
218 expectConstruction: false,
219 expectReflow: true,
222 elem: document.body,
223 beforeStyle: "overflow: hidden",
224 afterStyle: "overflow: scroll",
225 expectConstruction: false,
226 expectReflow: true,
229 elem: document.body,
230 beforeStyle: "overflow: hidden",
231 afterStyle: "overflow: visible",
232 expectConstruction: false,
233 expectReflow: true,
236 elem: document.body,
237 beforeStyle: "overflow: auto",
238 afterStyle: "overflow: hidden",
239 expectConstruction: false,
240 expectReflow: true,
243 elem: document.body,
244 beforeStyle: "overflow: visible",
245 afterStyle: "overflow: hidden",
246 expectConstruction: false,
247 expectReflow: true,
250 // * Changing 'overflow' on <html> should cause reflow,
251 // but not frame reconstruction
253 elem: document.documentElement,
254 /* beforeStyle: implicitly 'overflow:visible' */
255 afterStyle: "overflow: auto",
256 expectConstruction: false,
257 expectReflow: true,
260 elem: document.documentElement,
261 beforeStyle: "overflow: visible",
262 afterStyle: "overflow: auto",
263 expectConstruction: false,
264 expectReflow: true,
267 // * Setting 'overflow' on arbitrary node should cause reflow as well as
268 // frame reconstruction
270 /* beforeStyle: implicitly 'overflow:visible' */
271 afterStyle: "overflow: auto",
272 expectConstruction: true,
273 expectReflow: true,
276 beforeStyle: "overflow: auto",
277 afterStyle: "overflow: visible",
278 expectConstruction: true,
279 expectReflow: true,
282 // * but only reflow if we don't need to construct / unconstruct a new frame.
284 beforeStyle: "overflow: scroll",
285 afterStyle: "overflow: auto",
286 expectConstruction: false,
287 expectReflow: true,
290 beforeStyle: "overflow: auto",
291 afterStyle: "overflow: scroll",
292 expectConstruction: false,
293 expectReflow: true,
297 beforeStyle: "overflow: hidden",
298 afterStyle: "overflow: auto",
299 expectConstruction: true,
300 expectReflow: true,
303 beforeStyle: "overflow: auto",
304 afterStyle: "overflow: hidden",
305 expectConstruction: false,
306 expectReflow: true,
309 beforeStyle: "overflow: hidden",
310 afterStyle: "overflow: scroll",
311 expectConstruction: true,
312 expectReflow: true,
315 beforeStyle: "overflow: scroll",
316 afterStyle: "overflow: hidden",
317 expectConstruction: false,
318 expectReflow: true,
322 elem: elemWithoutScrollbars,
323 beforeStyle: "scrollbar-width: auto",
324 afterStyle: "scrollbar-width: none",
325 expectConstruction: false,
326 expectReflow: false,
329 elem: elemWithScrollbars,
330 beforeStyle: "scrollbar-width: auto",
331 afterStyle: "scrollbar-width: none",
332 expectConstruction: false,
333 expectReflow: true,
336 // Not the scrolling element, so nothing should happen.
337 elem: document.body,
338 beforeStyle: "scrollbar-width: none",
339 afterStyle: "scrollbar-width: auto",
340 expectConstruction: false,
341 expectReflow: false,
344 // Not the scrolling element, so nothing should happen.
345 elem: document.body,
346 beforeStyle: "scrollbar-width: auto",
347 afterStyle: "scrollbar-width: none",
348 expectConstruction: false,
349 expectReflow: false,
352 elem: document.documentElement,
353 beforeStyle: "scrollbar-width: none;",
354 afterStyle: "scrollbar-width: auto",
355 expectConstruction: false,
356 expectReflow: true,
359 elem: document.documentElement,
360 beforeStyle: "overflow: scroll; scrollbar-width: none;",
361 afterStyle: "overflow: scroll; scrollbar-width: auto",
362 expectConstruction: false,
363 expectReflow: true,
366 elem: document.documentElement,
367 beforeStyle: "overflow: scroll; scrollbar-width: auto;",
368 afterStyle: "overflow: scroll; scrollbar-width: none",
369 expectConstruction: false,
370 expectReflow: true,
373 // * Changing 'display' should cause frame construction and reflow.
375 beforeStyle: "display: inline",
376 afterStyle: "display: table",
377 expectConstruction: true,
378 expectReflow: true,
382 // * Position changes trigger a reframe, unless whether we're a containing
383 // block doesn't change, in which case we just need to reflow.
385 beforeStyle: "position: static",
386 afterStyle: "position: absolute",
387 expectConstruction: true,
388 expectReflow: true,
391 beforeStyle: "position: absolute",
392 afterStyle: "position: fixed",
393 expectConstruction: true,
394 expectReflow: true,
397 beforeStyle: "position: relative",
398 afterStyle: "position: fixed",
399 expectConstruction: true,
400 expectReflow: true,
403 // This doesn't change whether we're a containing block because there are no
404 // abspos descendants.
406 afterStyle: "position: static",
407 beforeStyle: "position: relative",
408 expectReflow: true,
411 // This doesn't change whether we're a containing block, shouldn't reframe.
413 afterStyle: "position: sticky",
414 beforeStyle: "position: relative",
415 expectReflow: true,
418 // These don't change whether we're a containing block for our
419 // absolutely-positioned child, so shouldn't reframe.
421 elem: elemWithAbsPosChild,
422 afterStyle: "position: sticky",
423 beforeStyle: "position: relative",
424 expectReflow: true,
427 elem: elemWithFixedPosChild,
428 afterStyle: "position: sticky",
429 beforeStyle: "position: relative",
430 expectReflow: true,
433 elem: elemWithFixedPosChild,
434 afterStyle: "position: static",
435 beforeStyle: "position: relative",
436 expectReflow: true,
439 elem: elemWithFixedPosChild,
440 afterStyle: "position: static",
441 beforeStyle: "position: sticky",
442 expectReflow: true,
445 // Even if we're a scroll frame.
446 elem: elemWithFixedPosChild,
447 afterStyle: "position: static; overflow: auto;",
448 beforeStyle: "position: relative; overflow: auto;",
449 expectReflow: true,
452 elem: tableCell,
453 afterStyle: "position: static;",
454 beforeStyle: "position: relative;",
455 expectReflow: true,
458 elem: tableCell,
459 afterStyle: "filter: none",
460 beforeStyle: "filter: saturate(1)",
461 expectReflow: false,
464 // These ones do though.
466 elem: elemWithAbsPosChild,
467 afterStyle: "position: static",
468 beforeStyle: "position: relative",
469 expectConstruction: true,
470 expectReflow: true,
473 elem: elemWithAbsPosChild,
474 afterStyle: "position: static",
475 beforeStyle: "position: sticky",
476 expectConstruction: true,
477 expectReflow: true,
480 elem: elemWithAbsPosChild,
481 afterStyle: "position: static; overflow: auto;",
482 beforeStyle: "position: relative; overflow: auto;",
483 expectConstruction: true,
484 expectReflow: true,
487 elem: tableCellWithAbsPosChild,
488 afterStyle: "position: static;",
489 beforeStyle: "position: relative;",
490 expectConstruction: true,
491 expectReflow: true,
494 // Adding transform to a scrollframe without abspos / fixedpos children shouldn't reframe.
496 elem: elemWithScrollbars,
497 afterStyle: "transform: translateX(1px)",
498 expectConstruction: false,
499 expectReflow: false,
502 // <select> can't contain abspos / floating children so shouldn't reframe
503 // when changing containing block-ness.
505 elem: selectElem,
506 afterStyle: "transform: translateX(1px)",
507 expectConstruction: false,
508 expectReflow: false,
511 elem: selectElem,
512 afterStyle: "position: relative",
513 expectConstruction: false,
514 expectReflow: true,
517 // <button> shouldn't be reframed either in the absence of positioned descendants.
519 elem: buttonElem,
520 afterStyle: "transform: translateX(1px)",
521 expectConstruction: false,
522 expectReflow: false,
525 elem: buttonElem,
526 afterStyle: "position: relative",
527 expectConstruction: false,
528 expectReflow: true,
531 elem: buttonElemWithAbsPosChild,
532 afterStyle: "position: relative",
533 expectConstruction: true,
534 expectReflow: true,
536 // changing scroll-behavior should not cause reflow or frame construction
538 elem: document.documentElement,
539 /* beforeStyle: implicitly 'scroll-behavior: auto' */
540 afterStyle: "scroll-behavior: smooth",
541 expectConstruction: false,
542 expectReflow: false,
545 elem: document.documentElement,
546 beforeStyle: "scroll-behavior: smooth",
547 afterStyle: "scroll-behavior: auto",
548 expectConstruction: false,
549 expectReflow: false,
552 elem: document.body,
553 /* beforeStyle: implicitly 'scroll-behavior: auto' */
554 afterStyle: "scroll-behavior: smooth",
555 expectConstruction: false,
556 expectReflow: false,
559 elem: document.body,
560 beforeStyle: "scroll-behavior: smooth",
561 afterStyle: "scroll-behavior: auto",
562 expectConstruction: false,
563 expectReflow: false,
565 // changing scroll-snap-type should not cause reflow or frame construction
567 elem: document.documentElement,
568 /* beforeStyle: implicitly 'scroll-snap-type: none' */
569 afterStyle: "scroll-snap-type: y mandatory",
570 expectConstruction: false,
571 expectReflow: false,
574 elem: document.documentElement,
575 /* beforeStyle: implicitly 'scroll-snap-type: none' */
576 afterStyle: "scroll-snap-type: x proximity",
577 expectConstruction: false,
578 expectReflow: false,
581 elem: document.documentElement,
582 beforeStyle: "scroll-snap-type: y mandatory",
583 afterStyle: "scroll-snap-type: none",
584 expectConstruction: false,
585 expectReflow: false,
588 elem: document.body,
589 /* beforeStyle: implicitly 'scroll-snap-type: none' */
590 afterStyle: "scroll-snap-type: y mandatory",
591 expectConstruction: false,
592 expectReflow: false,
595 elem: document.body,
596 /* beforeStyle: implicitly 'scroll-snap-type: none' */
597 afterStyle: "scroll-snap-type: x proximity",
598 expectConstruction: false,
599 expectReflow: false,
602 elem: document.body,
603 beforeStyle: "scroll-snap-type: y mandatory",
604 afterStyle: "scroll-snap-type: none",
605 expectConstruction: false,
606 expectReflow: false,
609 elem: inputElem,
610 beforeStyle: "overflow: auto",
611 afterStyle: "overflow: hidden",
612 expectConstruction: false,
613 expectReflow: true,
616 elem: textareaElem,
617 beforeStyle: "overflow: auto",
618 afterStyle: "overflow: hidden",
619 expectConstruction: false,
620 expectReflow: true,
623 elem: flexItemMovementTarget,
624 beforeStyle: "transform: translateX(0)",
625 afterStyle: "transform: translateX(-100px)",
626 expectConstruction: false,
627 expectReflow: false,
630 elem: flexItemMovementTarget2,
631 beforeStyle: "transform: translateX(0)",
632 afterStyle: "transform: translateX(-100px)",
633 expectConstruction: false,
634 expectReflow: false,
638 // Helper function to let us call either "is" or "isnot" & assemble
639 // the failure message, based on the provided parameters.
640 function checkFinalCount(aFinalCount, aExpectedCount,
641 aExpectChange, aMsgPrefix, aCountDescription)
643 let compareFunc;
644 let msg = aMsgPrefix;
645 if (aExpectChange) {
646 compareFunc = isnot;
647 msg += "should cause " + aCountDescription;
648 } else {
649 compareFunc = is;
650 msg += "should not cause " + aCountDescription;
653 compareFunc(aFinalCount, aExpectedCount, msg);
656 // Vars used in runOneTest that we really only have to look up once:
657 const gUtils = SpecialPowers.getDOMWindowUtils(window);
658 const gElem = document.getElementById("content");
660 function runOneTest(aTestcase)
662 // sanity-check that we have the one main thing we need:
663 if (!aTestcase.afterStyle) {
664 ok(false, "testcase is missing an 'afterStyle' to change to");
665 return;
668 // Figure out which element we'll be tweaking (defaulting to gElem)
669 let elem = aTestcase.elem ? aTestcase.elem : gElem;
671 // Verify that 'style' attribute is unset (avoid causing ourselves trouble):
672 const oldStyle = elem.getAttribute("style");
674 // Set the "before" style, and compose the first part of the message
675 // to be used in our "is"/"isnot" invocations:
676 let msgPrefix = "Changing style ";
677 if (aTestcase.beforeStyle) {
678 elem.setAttribute("style", aTestcase.beforeStyle);
679 msgPrefix += "from '" + aTestcase.beforeStyle + "' ";
681 msgPrefix += "to '" + aTestcase.afterStyle + "' ";
682 msgPrefix += "on " + elem.nodeName + " ";
684 // Establish initial counts:
685 let unusedVal = elem.offsetHeight; // flush layout
686 let origFramesConstructed = gUtils.framesConstructed;
687 let origFramesReflowed = gUtils.framesReflowed;
689 // Make the change and flush:
690 elem.setAttribute("style", aTestcase.afterStyle);
691 unusedVal = elem.offsetHeight; // flush layout
693 // Make our is/isnot assertions about whether things should have changed:
694 checkFinalCount(gUtils.framesConstructed, origFramesConstructed,
695 aTestcase.expectConstruction, msgPrefix,
696 "frame construction");
697 checkFinalCount(gUtils.framesReflowed, origFramesReflowed,
698 aTestcase.expectReflow, msgPrefix,
699 "reflow");
701 // Clean up!
702 if (oldStyle) {
703 elem.setAttribute("style", oldStyle);
704 } else {
705 elem.removeAttribute("style");
708 unusedVal = elem.offsetHeight; // flush layout
711 gTestcases.forEach(runOneTest);
713 </script>
714 </pre>
715 </body>
716 </html>