Skip to content

Commit ac829cf

Browse files
committed
LibWeb: Hoist anonymous wrappers out of parent inline nodes
When we generate pseudo elements, we create anonymous wrappers that might end up in an InlineNode, even if they have `display: block` set. This causes them not to be rendered. Do not rely on inline continuation logic for these anonymous wrappers, but rather find the first layout parent that's not an InlineNode and insert it into that. Fixes #5042.
1 parent 660e1b8 commit ac829cf

File tree

3 files changed

+63
-29
lines changed

3 files changed

+63
-29
lines changed

Libraries/LibWeb/Layout/TreeBuilder.cpp

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
#include <LibWeb/CSS/PseudoElement.h>
1818
#include <LibWeb/CSS/StyleComputer.h>
1919
#include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
20-
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
2120
#include <LibWeb/DOM/Document.h>
2221
#include <LibWeb/DOM/Element.h>
2322
#include <LibWeb/DOM/ParentNode.h>
@@ -27,6 +26,7 @@
2726
#include <LibWeb/HTML/HTMLSlotElement.h>
2827
#include <LibWeb/Layout/FieldSetBox.h>
2928
#include <LibWeb/Layout/ImageBox.h>
29+
#include <LibWeb/Layout/InlineNode.h>
3030
#include <LibWeb/Layout/ListItemBox.h>
3131
#include <LibWeb/Layout/ListItemMarkerBox.h>
3232
#include <LibWeb/Layout/Node.h>
@@ -101,51 +101,53 @@ static Layout::Node& insertion_parent_for_inline_node(Layout::NodeWithStyle& lay
101101
static Layout::Node& insertion_parent_for_block_node(Layout::NodeWithStyle& layout_parent, Layout::Node& layout_node)
102102
{
103103
// Inline is fine for in-flow block children; we'll maintain the (non-)inline invariant after insertion.
104-
if (layout_parent.is_inline() && layout_parent.display().is_flow_inside() && !layout_node.is_out_of_flow())
104+
if (!layout_node.is_anonymous() && layout_parent.is_inline() && layout_parent.display().is_flow_inside() && !layout_node.is_out_of_flow())
105105
return layout_parent;
106106

107-
if (!has_inline_or_in_flow_block_children(layout_parent)) {
108-
// Parent block has no children, insert this block into parent.
109-
return layout_parent;
110-
}
107+
// Make sure we're not inserting into an inline node, since those do not support block nodes.
108+
auto* new_parent = &layout_parent;
109+
while (is<InlineNode>(new_parent))
110+
new_parent = new_parent->parent();
111111

112-
if (layout_node.is_out_of_flow()
113-
&& !layout_parent.display().is_flex_inside()
114-
&& !layout_parent.display().is_grid_inside()
115-
&& !layout_parent.last_child()->is_generated_for_pseudo_element()
116-
&& layout_parent.last_child()->is_anonymous()
117-
&& layout_parent.last_child()->children_are_inline()) {
118-
// Block is out-of-flow & previous sibling was wrapped in an anonymous block.
119-
// Join the previous sibling inside the anonymous block.
120-
return *layout_parent.last_child();
121-
}
122-
123-
if (!layout_parent.children_are_inline()) {
124-
// Parent block has block-level children, insert this block into parent.
125-
return layout_parent;
126-
}
112+
// If the parent block has no children, insert this block into parent.
113+
if (!has_inline_or_in_flow_block_children(*new_parent))
114+
return *new_parent;
127115

116+
// If the block is out-of-flow,
128117
if (layout_node.is_out_of_flow()) {
129-
// Block is out-of-flow, it can have inline siblings if necessary.
130-
return layout_parent;
118+
// And the parent's last child is an anonymous block, join that anonymous block.
119+
if (!new_parent->display().is_flex_inside()
120+
&& !new_parent->display().is_grid_inside()
121+
&& !new_parent->last_child()->is_generated_for_pseudo_element()
122+
&& new_parent->last_child()->is_anonymous()
123+
&& new_parent->last_child()->children_are_inline()) {
124+
return *new_parent->last_child();
125+
}
126+
127+
// Otherwise, insert this block into parent.
128+
return *new_parent;
131129
}
132130

131+
// If the parent block has block-level children, insert this block into parent.
132+
if (!new_parent->children_are_inline())
133+
return *new_parent;
134+
133135
// Parent block has inline-level children (our siblings); wrap these siblings into an anonymous wrapper block.
134-
auto wrapper = layout_parent.create_anonymous_wrapper();
136+
auto wrapper = new_parent->create_anonymous_wrapper();
135137
wrapper->set_children_are_inline(true);
136138

137-
for (GC::Ptr<Node> child = layout_parent.first_child(); child;) {
139+
for (GC::Ptr<Node> child = new_parent->first_child(); child;) {
138140
GC::Ptr<Node> next_child = child->next_sibling();
139-
layout_parent.remove_child(*child);
141+
new_parent->remove_child(*child);
140142
wrapper->append_child(*child);
141143
child = next_child;
142144
}
143145

144-
layout_parent.set_children_are_inline(false);
145-
layout_parent.append_child(wrapper);
146+
new_parent->set_children_are_inline(false);
147+
new_parent->append_child(wrapper);
146148

147149
// Then it's safe to insert this block into parent.
148-
return layout_parent;
150+
return *new_parent;
149151
}
150152

151153
void TreeBuilder::insert_node_into_inline_or_block_ancestor(Layout::Node& node, CSS::Display display, AppendOrPrepend mode)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] children: not-inline
2+
BlockContainer <html> at [0,0] [0+0+0 800 0+0+0] [0+0+0 84 0+0+0] [BFC] children: not-inline
3+
BlockContainer <body> at [8,8] [8+0+0 784 0+0+8] [8+0+0 68 0+0+8] children: not-inline
4+
BlockContainer <(anonymous)> at [8,8] [0+0+0 784 0+0+0] [0+0+0 18 0+0+0] children: inline
5+
InlineNode <span> at [8,8] [0+0+0 54.796875 0+0+0] [0+0+0 18 0+0+0]
6+
frag 0 from TextNode start: 0, length: 6, rect: [8,8 54.796875x18] baseline: 13.796875
7+
"foobar"
8+
TextNode <#text> (not painted)
9+
BlockContainer <(anonymous)> at [8,26] [0+0+0 50 0+0+734] [0+0+0 50 0+0+0] children: inline
10+
TextNode <#text> (not painted)
11+
12+
ViewportPaintable (Viewport<#document>) [0,0 800x600]
13+
PaintableWithLines (BlockContainer<HTML>) [0,0 800x84]
14+
PaintableWithLines (BlockContainer<BODY>) [8,8 784x68]
15+
PaintableWithLines (BlockContainer(anonymous)) [8,8 784x18]
16+
PaintableWithLines (InlineNode<SPAN>) [8,8 54.796875x18]
17+
TextPaintable (TextNode<#text>)
18+
PaintableWithLines (BlockContainer(anonymous)) [8,26 50x50]
19+
20+
SC for Viewport<#document> [0,0 800x600] [children: 1] (z-index: auto)
21+
SC for BlockContainer<HTML> [0,0 800x84] [children: 0] (z-index: auto)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<style>
3+
span::after {
4+
background: green;
5+
content: "";
6+
display: block;
7+
height: 50px;
8+
width: 50px;
9+
}
10+
</style>
11+
<span>foobar

0 commit comments

Comments
 (0)