layout: Fix propagation of overflow to viewport (#39173)

This patch refactors the logic for propagating overflow to the viewport,
fixing various issues:
- Now we won't propagate from the root element if it has no box. Note
the fix isn't observable in Servo because we lack scrollbars.
- If the first `<body>` element has no box, we won't keep searching for
other `<body>` elements. This deviates from the spec, but aligns us with
other browsers.
- We won't propagate from the `<body>` if it has no box. We were already
handling `display: none` but not `display: contents`. This deviates from
the spec, but aligns us with other browsers.

Also, when we flag the root or `<body>` as having propagated `overflow`
to the viewport, we retrieve the `LayoutBoxBase`. Therefore, now we get
the computed style from the `LayoutBoxBase` in a single operation,
instead of first retrieving the style from the DOM element and then
getting the `LayoutBoxBase` from the box.

Testing: Adding more tests. We were only failing one of them, but it's
hard to test the fixes given that we don't show scrollbars. The tests
that were already passing are useful too, e.g. Firefox fails one of
them.

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Oriol Brufau 2025-09-16 22:53:46 +02:00 committed by GitHub
parent 22fbb3458b
commit 4451ce0ef1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 296 additions and 69 deletions

View file

@ -232182,6 +232182,58 @@
{}
]
],
"overflow-body-propagation-013.html": [
"3eb163013e302276845c4e196e74c97f8fd9568e",
[
null,
[
[
"about:blank",
"=="
]
],
{}
]
],
"overflow-body-propagation-014.html": [
"26a505a7833a504edd81ce2ee097dcb3190a68c9",
[
null,
[
[
"/css/css-overflow/overflow-body-propagation-012-ref.html",
"=="
]
],
{}
]
],
"overflow-body-propagation-015.html": [
"a709a1ff442d3a656c04b3da5313b821bc370942",
[
null,
[
[
"/css/css-overflow/overflow-body-propagation-012-ref.html",
"=="
]
],
{}
]
],
"overflow-body-propagation-016.html": [
"35ab59044071343e6b3a31b125af59616327cfcf",
[
null,
[
[
"/css/css-overflow/overflow-body-propagation-012-ref.html",
"=="
]
],
{}
]
],
"overflow-canvas.html": [
"e9529cb0bc81202c5689a507435ea088028a97fc",
[

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Test: HTML with display:none and BODY with overflow:scroll</title>
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-overflow-3/#overflow-propagation">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/12649">
<meta name="assert" content="The <body> doesn't propagate overflow to the
viewport when the root element doesn't generate a box. Therefore, there
shouldn't be scrollbars.">
<link rel="match" href="about:blank">
<style>
:root {
display: none;
scrollbar-color: red red;
}
body {
overflow: scroll;
}
</style>
<body></body>

View file

@ -0,0 +1,42 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Test: two BODY elements</title>
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-overflow-3/#overflow-propagation">
<meta name="assert" content="If there are multiple <body> elements, the
one that propagates `overflow` to the viewport is the first one.
Therefore, the first <body> should get `overflow: visible` and its
overflowing contents should be visible. And the second <body> should
be able to keep `overflow: hidden` and hide its overflowing contents.">
<link rel="match" href="overflow-body-propagation-012-ref.html">
<style>
body {
overflow: hidden;
width: 0px;
height: 0px;
border: solid red;
border-width: 0 400px 200px 0;
margin-bottom: 0;
}
body > div {
background: green;
width: 400px;
height: 200px;
}
#clone {
border-color: green;
margin-top: 0;
}
#clone > div {
background: red;
}
</style>
<body>
<div></div>
</body>
<script>
let clone = document.body.cloneNode(true);
clone.id = "clone";
document.documentElement.appendChild(clone);
</script>

View file

@ -0,0 +1,49 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Test: two BODY elements</title>
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-overflow-3/#overflow-propagation">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1987284">
<meta name="assert" content="If there are multiple <body> elements, the
one that propagates `overflow` to the viewport is the first one.
Here we insert a cloned body before the original one, so the original
needs to stop propagating because the clone will do it instead.
Therefore, the cloned <body> should get `overflow: visible` and its
overflowing contents should be visible. And the original <body> should
be able to keep `overflow: hidden` and hide its overflowing contents.">
<link rel="match" href="overflow-body-propagation-012-ref.html">
<style>
body {
overflow: hidden;
width: 0px;
height: 0px;
border: solid green;
border-width: 0 400px 200px 0;
margin-bottom: 0;
}
body:not(#clone) {
margin-top: 0;
}
body > div {
background: red;
width: 400px;
height: 200px;
}
#clone {
border-color: red;
margin-top: revert;
}
#clone > div {
background: green;
}
</style>
<body>
<div></div>
</body>
<script>
let clone = document.body.cloneNode(true);
clone.id = "clone";
document.documentElement.prepend(clone);
</script>

View file

@ -0,0 +1,49 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Test: two BODY elements</title>
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-overflow-3/#overflow-propagation">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/12644">
<meta name="assert" content="If there are multiple <body> elements, the
one that propagates `overflow` to the viewport would be the first one.
However, the first one has `display: none`, and overflow propagation
can only happen when the element generates a box. Therefore, the viewport
shouldn't get scrollbars from the `overflow: scroll` on the first body.
The second body doesn't propagate either, so it should be able to keep
`overflow: hidden` and hide its overflowing contents.">
<link rel="match" href="overflow-body-propagation-012-ref.html">
<style>
html {
scrollbar-color: red red;
}
body {
overflow: scroll;
width: 0px;
height: 0px;
border: solid red;
border-width: 0 400px 400px 0;
}
body > div {
overflow: hidden;
background: green;
width: 400px;
height: 400px;
}
#clone {
border-color: green;
}
#clone > div {
background: red;
}
</style>
<body>
<div></div>
</body>
<script>
let clone = document.body.cloneNode(true);
clone.id = "clone";
document.documentElement.appendChild(clone);
document.body.style.display = "none";
</script>