4 https://bugzilla.mozilla.org/show_bug.cgi?id=1131371
8 <title>Test for Bug
1131371</title>
9 <script src=
"/tests/SimpleTest/SimpleTest.js"></script>
10 <link rel=
"stylesheet" href=
"/tests/SimpleTest/test.css"/>
13 <a target=
"_blank" href=
"https://bugzilla.mozilla.org/show_bug.cgi?id=1131371">Mozilla Bug
1131371</a>
15 #elemWithScrollbars { overflow: scroll }
16 .flexScrollerWithTransformedItems {
22 .flexScrollerWithTransformedItems div {
26 will-change: transform;
34 #tableCellWithAbsPosChild {
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">
50 <div id=
"flexItemMovementTarget"></div>
52 <div class=
"flexScrollerWithTransformedItems">
56 <div id=
"flexItemMovementTarget2"></div>
57 <div class=
"absposFlexItem"></div>
59 <input id=
"inputElem">
60 <textarea id=
"textareaElem">
66 <select id=
"selectElem">
71 <button id=
"buttonElem">
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>
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.
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",
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()",
198 // * Changing 'overflow' on <body> should cause reflow,
199 // but not frame reconstruction
202 /* beforeStyle: implicitly 'overflow:visible' */
203 afterStyle
: "overflow: hidden",
204 expectConstruction
: false,
209 /* beforeStyle: implicitly 'overflow:visible' */
210 afterStyle
: "overflow: scroll",
211 expectConstruction
: false,
216 beforeStyle
: "overflow: hidden",
217 afterStyle
: "overflow: auto",
218 expectConstruction
: false,
223 beforeStyle
: "overflow: hidden",
224 afterStyle
: "overflow: scroll",
225 expectConstruction
: false,
230 beforeStyle
: "overflow: hidden",
231 afterStyle
: "overflow: visible",
232 expectConstruction
: false,
237 beforeStyle
: "overflow: auto",
238 afterStyle
: "overflow: hidden",
239 expectConstruction
: false,
244 beforeStyle
: "overflow: visible",
245 afterStyle
: "overflow: hidden",
246 expectConstruction
: false,
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,
260 elem
: document
.documentElement
,
261 beforeStyle
: "overflow: visible",
262 afterStyle
: "overflow: auto",
263 expectConstruction
: false,
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,
276 beforeStyle
: "overflow: auto",
277 afterStyle
: "overflow: visible",
278 expectConstruction
: 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,
290 beforeStyle
: "overflow: auto",
291 afterStyle
: "overflow: scroll",
292 expectConstruction
: false,
297 beforeStyle
: "overflow: hidden",
298 afterStyle
: "overflow: auto",
299 expectConstruction
: true,
303 beforeStyle
: "overflow: auto",
304 afterStyle
: "overflow: hidden",
305 expectConstruction
: false,
309 beforeStyle
: "overflow: hidden",
310 afterStyle
: "overflow: scroll",
311 expectConstruction
: true,
315 beforeStyle
: "overflow: scroll",
316 afterStyle
: "overflow: hidden",
317 expectConstruction
: false,
322 elem
: elemWithoutScrollbars
,
323 beforeStyle
: "scrollbar-width: auto",
324 afterStyle
: "scrollbar-width: none",
325 expectConstruction
: false,
329 elem
: elemWithScrollbars
,
330 beforeStyle
: "scrollbar-width: auto",
331 afterStyle
: "scrollbar-width: none",
332 expectConstruction
: false,
336 // Not the scrolling element, so nothing should happen.
338 beforeStyle
: "scrollbar-width: none",
339 afterStyle
: "scrollbar-width: auto",
340 expectConstruction
: false,
344 // Not the scrolling element, so nothing should happen.
346 beforeStyle
: "scrollbar-width: auto",
347 afterStyle
: "scrollbar-width: none",
348 expectConstruction
: false,
352 elem
: document
.documentElement
,
353 beforeStyle
: "scrollbar-width: none;",
354 afterStyle
: "scrollbar-width: auto",
355 expectConstruction
: false,
359 elem
: document
.documentElement
,
360 beforeStyle
: "overflow: scroll; scrollbar-width: none;",
361 afterStyle
: "overflow: scroll; scrollbar-width: auto",
362 expectConstruction
: false,
366 elem
: document
.documentElement
,
367 beforeStyle
: "overflow: scroll; scrollbar-width: auto;",
368 afterStyle
: "overflow: scroll; scrollbar-width: none",
369 expectConstruction
: false,
373 // * Changing 'display' should cause frame construction and reflow.
375 beforeStyle
: "display: inline",
376 afterStyle
: "display: table",
377 expectConstruction
: 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,
391 beforeStyle
: "position: absolute",
392 afterStyle
: "position: fixed",
393 expectConstruction
: true,
397 beforeStyle
: "position: relative",
398 afterStyle
: "position: fixed",
399 expectConstruction
: 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",
411 // This doesn't change whether we're a containing block, shouldn't reframe.
413 afterStyle
: "position: sticky",
414 beforeStyle
: "position: relative",
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",
427 elem
: elemWithFixedPosChild
,
428 afterStyle
: "position: sticky",
429 beforeStyle
: "position: relative",
433 elem
: elemWithFixedPosChild
,
434 afterStyle
: "position: static",
435 beforeStyle
: "position: relative",
439 elem
: elemWithFixedPosChild
,
440 afterStyle
: "position: static",
441 beforeStyle
: "position: sticky",
445 // Even if we're a scroll frame.
446 elem
: elemWithFixedPosChild
,
447 afterStyle
: "position: static; overflow: auto;",
448 beforeStyle
: "position: relative; overflow: auto;",
453 afterStyle
: "position: static;",
454 beforeStyle
: "position: relative;",
459 afterStyle
: "filter: none",
460 beforeStyle
: "filter: saturate(1)",
464 // These ones do though.
466 elem
: elemWithAbsPosChild
,
467 afterStyle
: "position: static",
468 beforeStyle
: "position: relative",
469 expectConstruction
: true,
473 elem
: elemWithAbsPosChild
,
474 afterStyle
: "position: static",
475 beforeStyle
: "position: sticky",
476 expectConstruction
: true,
480 elem
: elemWithAbsPosChild
,
481 afterStyle
: "position: static; overflow: auto;",
482 beforeStyle
: "position: relative; overflow: auto;",
483 expectConstruction
: true,
487 elem
: tableCellWithAbsPosChild
,
488 afterStyle
: "position: static;",
489 beforeStyle
: "position: relative;",
490 expectConstruction
: 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,
502 // <select> can't contain abspos / floating children so shouldn't reframe
503 // when changing containing block-ness.
506 afterStyle
: "transform: translateX(1px)",
507 expectConstruction
: false,
512 afterStyle
: "position: relative",
513 expectConstruction
: false,
517 // <button> shouldn't be reframed either in the absence of positioned descendants.
520 afterStyle
: "transform: translateX(1px)",
521 expectConstruction
: false,
526 afterStyle
: "position: relative",
527 expectConstruction
: false,
531 elem
: buttonElemWithAbsPosChild
,
532 afterStyle
: "position: relative",
533 expectConstruction
: 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,
545 elem
: document
.documentElement
,
546 beforeStyle
: "scroll-behavior: smooth",
547 afterStyle
: "scroll-behavior: auto",
548 expectConstruction
: false,
553 /* beforeStyle: implicitly 'scroll-behavior: auto' */
554 afterStyle
: "scroll-behavior: smooth",
555 expectConstruction
: false,
560 beforeStyle
: "scroll-behavior: smooth",
561 afterStyle
: "scroll-behavior: auto",
562 expectConstruction
: 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,
574 elem
: document
.documentElement
,
575 /* beforeStyle: implicitly 'scroll-snap-type: none' */
576 afterStyle
: "scroll-snap-type: x proximity",
577 expectConstruction
: false,
581 elem
: document
.documentElement
,
582 beforeStyle
: "scroll-snap-type: y mandatory",
583 afterStyle
: "scroll-snap-type: none",
584 expectConstruction
: false,
589 /* beforeStyle: implicitly 'scroll-snap-type: none' */
590 afterStyle
: "scroll-snap-type: y mandatory",
591 expectConstruction
: false,
596 /* beforeStyle: implicitly 'scroll-snap-type: none' */
597 afterStyle
: "scroll-snap-type: x proximity",
598 expectConstruction
: false,
603 beforeStyle
: "scroll-snap-type: y mandatory",
604 afterStyle
: "scroll-snap-type: none",
605 expectConstruction
: false,
610 beforeStyle
: "overflow: auto",
611 afterStyle
: "overflow: hidden",
612 expectConstruction
: false,
617 beforeStyle
: "overflow: auto",
618 afterStyle
: "overflow: hidden",
619 expectConstruction
: false,
623 elem
: flexItemMovementTarget
,
624 beforeStyle
: "transform: translateX(0)",
625 afterStyle
: "transform: translateX(-100px)",
626 expectConstruction
: false,
630 elem
: flexItemMovementTarget2
,
631 beforeStyle
: "transform: translateX(0)",
632 afterStyle
: "transform: translateX(-100px)",
633 expectConstruction
: 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
)
644 let msg
= aMsgPrefix
;
647 msg
+= "should cause " + aCountDescription
;
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");
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
,
703 elem
.setAttribute("style", oldStyle
);
705 elem
.removeAttribute("style");
708 unusedVal
= elem
.offsetHeight
; // flush layout
711 gTestcases
.forEach(runOneTest
);