LibWeb: Propagate overflow modes from <html> or <body> to viewport

This patch implements "Overflow Viewport Propagation" from CSS-OVERFLOW.
It fixes an issue where many websites were not scrollable because they
had `overflow: scroll` on the body element and we didn't propagate it.
This commit is contained in:
Andreas Kling 2023-08-03 12:26:43 +02:00
parent 09eed8eea2
commit 4011a107a4
Notes: sideshowbarker 2024-07-17 02:08:15 +09:00
5 changed files with 76 additions and 0 deletions

View file

@ -0,0 +1,10 @@
Viewport <#document> at (0,0) content-size 800x600 [BFC] children: not-inline
BlockContainer <html> at (0,0) content-size 800x2016 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 784x2000 children: not-inline
BlockContainer <div> at (8,8) content-size 100x2000 children: inline
TextNode <#text>
PaintableWithLines (Viewport<#document>) [0,0 800x600] overflow: [0,0 800x2016]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x2016]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x2000]
PaintableWithLines (BlockContainer<DIV>) [8,8 100x2000]

View file

@ -0,0 +1,10 @@
Viewport <#document> at (0,0) content-size 800x600 [BFC] children: not-inline
BlockContainer <html> at (0,0) content-size 800x616 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 784x600 children: not-inline
BlockContainer <div.long> at (8,8) content-size 784x2000 children: inline
TextNode <#text>
PaintableWithLines (Viewport<#document>) [0,0 800x600] overflow: [0,0 800x2008]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x616] overflow: [0,0 800x2008]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x600] overflow: [8,8 784x2000]
PaintableWithLines (BlockContainer<DIV>.long) [8,8 784x2000]

View file

@ -0,0 +1,12 @@
<!doctype html><style>
html {
overflow: visible;
}
body {
overflow: scroll;
}
div {
width: 100px;
height: 2000px;
}
</style><body><div>

View file

@ -0,0 +1,10 @@
<!doctype html><style>
body {
height: 100%;
overflow: scroll;
}
.long {
height: 2000px;
background: orange;
}
</style><body><div class="long">

View file

@ -917,6 +917,38 @@ void Document::invalidate_layout()
schedule_layout_update();
}
static void propagate_overflow_to_viewport(Element& root_element, Layout::Viewport& viewport)
{
// https://drafts.csswg.org/css-overflow-3/#overflow-propagation
// UAs must apply the overflow-* values set on the root element to the viewport
// when the root elements display value is not none.
auto* overflow_origin_node = root_element.layout_node();
auto& viewport_computed_values = const_cast<CSS::MutableComputedValues&>(static_cast<CSS::MutableComputedValues const&>(static_cast<CSS::ComputedValues const&>(viewport.computed_values())));
// However, when the root element is an [HTML] html element (including XML syntax for HTML)
// whose overflow value is visible (in both axes), and that element has as a child
// a body element whose display value is also not none,
// user agents must instead apply the overflow-* values of the first such child element to the viewport.
if (root_element.is_html_html_element()) {
auto* root_element_layout_node = root_element.layout_node();
auto& root_element_computed_values = const_cast<CSS::MutableComputedValues&>(static_cast<CSS::MutableComputedValues const&>(static_cast<CSS::ComputedValues const&>(root_element_layout_node->computed_values())));
if (root_element_computed_values.overflow_x() == CSS::Overflow::Visible && root_element_computed_values.overflow_y() == CSS::Overflow::Visible) {
auto* body_element = root_element.first_child_of_type<HTML::HTMLBodyElement>();
if (body_element && body_element->layout_node())
overflow_origin_node = body_element->layout_node();
}
}
// NOTE: This is where we assign the chosen overflow values to the viewport.
auto& overflow_origin_computed_values = const_cast<CSS::MutableComputedValues&>(static_cast<CSS::MutableComputedValues const&>(static_cast<CSS::ComputedValues const&>(overflow_origin_node->computed_values())));
viewport_computed_values.set_overflow_x(overflow_origin_computed_values.overflow_x());
viewport_computed_values.set_overflow_y(overflow_origin_computed_values.overflow_y());
// The element from which the value is propagated must then have a used overflow value of visible.
overflow_origin_computed_values.set_overflow_x(CSS::Overflow::Visible);
overflow_origin_computed_values.set_overflow_y(CSS::Overflow::Visible);
}
void Document::update_layout()
{
// NOTE: If our parent document needs a relayout, we must do that *first*.
@ -943,6 +975,8 @@ void Document::update_layout()
m_layout_root = verify_cast<Layout::Viewport>(*tree_builder.build(*this));
}
propagate_overflow_to_viewport(*document_element(), *m_layout_root);
Layout::LayoutState layout_state;
{