LibWeb: Assign sticky insets to Layout::InlineNode

Before this change we were ignoring boxes with `display: inline` while
assigning sticky insets. This was not correct because inline boxes are
allowed to have sticky positioning.

Fixes:
https://github.com/LadybirdBrowser/ladybird/issues/3718
https://github.com/LadybirdBrowser/ladybird/issues/3507
https://github.com/LadybirdBrowser/ladybird/issues/3133
This commit is contained in:
Aliaksandr Kalenik 2025-02-27 16:56:43 +01:00 committed by Alexander Kalenik
commit da5d4e9f6a
Notes: github-actions[bot] 2025-02-27 18:56:39 +00:00
3 changed files with 81 additions and 26 deletions

View file

@ -445,38 +445,42 @@ void LayoutState::commit(Box& root)
for (auto& it : used_values_per_layout_node) { for (auto& it : used_values_per_layout_node) {
auto& used_values = *it.value; auto& used_values = *it.value;
if (!used_values.node().is_box()) auto& node = used_values.node();
continue; for (auto& paintable : node.paintables()) {
auto const& box = static_cast<Layout::Box const&>(used_values.node()); Painting::PaintableBox* paintable_box = nullptr;
if (is<Painting::PaintableBox>(paintable))
paintable_box = &static_cast<Painting::PaintableBox&>(paintable);
auto& paintable_box = const_cast<Painting::PaintableBox&>(*box.paintable_box()); if (!paintable_box)
continue;
if (box.is_sticky_position()) { if (node.is_sticky_position()) {
// https://drafts.csswg.org/css-position/#insets // https://drafts.csswg.org/css-position/#insets
// For sticky positioned boxes, the inset is instead relative to the relevant scrollports size. Negative values are allowed. // For sticky positioned boxes, the inset is instead relative to the relevant scrollports size. Negative values are allowed.
auto sticky_insets = make<Painting::PaintableBox::StickyInsets>(); auto sticky_insets = make<Painting::PaintableBox::StickyInsets>();
auto const& inset = box.computed_values().inset(); auto const& inset = node.computed_values().inset();
auto const* nearest_scrollable_ancestor = paintable_box.nearest_scrollable_ancestor(); auto const* nearest_scrollable_ancestor = paintable_box->nearest_scrollable_ancestor();
CSSPixelSize scrollport_size; CSSPixelSize scrollport_size;
if (nearest_scrollable_ancestor) { if (nearest_scrollable_ancestor) {
scrollport_size = nearest_scrollable_ancestor->absolute_rect().size(); scrollport_size = nearest_scrollable_ancestor->absolute_rect().size();
} }
if (!inset.top().is_auto()) { if (!inset.top().is_auto()) {
sticky_insets->top = inset.top().to_px(box, scrollport_size.height()); sticky_insets->top = inset.top().to_px(node, scrollport_size.height());
}
if (!inset.right().is_auto()) {
sticky_insets->right = inset.right().to_px(node, scrollport_size.width());
}
if (!inset.bottom().is_auto()) {
sticky_insets->bottom = inset.bottom().to_px(node, scrollport_size.height());
}
if (!inset.left().is_auto()) {
sticky_insets->left = inset.left().to_px(node, scrollport_size.width());
}
paintable_box->set_sticky_insets(move(sticky_insets));
} }
if (!inset.right().is_auto()) {
sticky_insets->right = inset.right().to_px(box, scrollport_size.width());
}
if (!inset.bottom().is_auto()) {
sticky_insets->bottom = inset.bottom().to_px(box, scrollport_size.height());
}
if (!inset.left().is_auto()) {
sticky_insets->left = inset.left().to_px(box, scrollport_size.width());
}
paintable_box.set_sticky_insets(move(sticky_insets));
} }
} }
} }

View file

@ -73,6 +73,7 @@ struct LayoutState {
struct UsedValues { struct UsedValues {
NodeWithStyle const& node() const { return *m_node; } NodeWithStyle const& node() const { return *m_node; }
NodeWithStyle& node() { return const_cast<NodeWithStyle&>(*m_node); }
void set_node(NodeWithStyle&, UsedValues const* containing_block_used_values); void set_node(NodeWithStyle&, UsedValues const* containing_block_used_values);
UsedValues const* containing_block_used_values() const { return m_containing_block_used_values; } UsedValues const* containing_block_used_values() const { return m_containing_block_used_values; }

View file

@ -0,0 +1,50 @@
<!DOCTYPE html>
<head>
<style>
.container {
width: 80%;
margin: auto;
}
.sticky-inline {
display: inline;
position: sticky;
top: 10px;
background: yellow;
padding: 5px;
font-weight: bold;
}
.content {
margin-top: 20px;
}
</style>
</head>
<body>
<div class="container">
<p>
This is an example of an inline box with <span class="sticky-inline">position: sticky</span>.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum euismod, nulla id consequat facilisis,
libero risus placerat lacus, sed fermentum nisl nunc non sapien. Vivamus scelerisque feugiat dui, ac
venenatis ligula ultricies vel. Duis id nunc in felis feugiat cursus. Pellentesque habitant morbi tristique
senectus et netus et malesuada fames ac turpis egestas.
Curabitur at felis ac massa aliquet dictum. Ut auctor ligula non ante interdum, a euismod dui tristique. Nam
consequat, massa ut ultrices fermentum, lacus libero vulputate nulla, sit amet vehicula erat odio a metus.
Nulla vitae lectus sed erat scelerisque sodales.
</p>
<p>
This is an example of an inline box with <span class="sticky-inline">position: sticky</span>.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum euismod, nulla id consequat facilisis,
libero risus placerat lacus, sed fermentum nisl nunc non sapien. Vivamus scelerisque feugiat dui, ac
venenatis ligula ultricies vel. Duis id nunc in felis feugiat cursus. Pellentesque habitant morbi tristique
senectus et netus et malesuada fames ac turpis egestas.
Curabitur at felis ac massa aliquet dictum. Ut auctor ligula non ante interdum, a euismod dui tristique. Nam
consequat, massa ut ultrices fermentum, lacus libero vulputate nulla, sit amet vehicula erat odio a metus.
Nulla vitae lectus sed erat scelerisque sodales.
</p>
</div>
</body>
</html>