mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
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.
This commit is contained in:
parent
660e1b8cd2
commit
ac829cf60a
Notes:
github-actions[bot]
2025-11-11 09:50:59 +00:00
Author: https://github.com/gmta
Commit: ac829cf60a
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6739
Reviewed-by: https://github.com/lpas
3 changed files with 63 additions and 29 deletions
|
|
@ -17,7 +17,6 @@
|
|||
#include <LibWeb/CSS/PseudoElement.h>
|
||||
#include <LibWeb/CSS/StyleComputer.h>
|
||||
#include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/DOM/ParentNode.h>
|
||||
|
|
@ -27,6 +26,7 @@
|
|||
#include <LibWeb/HTML/HTMLSlotElement.h>
|
||||
#include <LibWeb/Layout/FieldSetBox.h>
|
||||
#include <LibWeb/Layout/ImageBox.h>
|
||||
#include <LibWeb/Layout/InlineNode.h>
|
||||
#include <LibWeb/Layout/ListItemBox.h>
|
||||
#include <LibWeb/Layout/ListItemMarkerBox.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
|
|
@ -101,51 +101,53 @@ static Layout::Node& insertion_parent_for_inline_node(Layout::NodeWithStyle& lay
|
|||
static Layout::Node& insertion_parent_for_block_node(Layout::NodeWithStyle& layout_parent, Layout::Node& layout_node)
|
||||
{
|
||||
// Inline is fine for in-flow block children; we'll maintain the (non-)inline invariant after insertion.
|
||||
if (layout_parent.is_inline() && layout_parent.display().is_flow_inside() && !layout_node.is_out_of_flow())
|
||||
if (!layout_node.is_anonymous() && layout_parent.is_inline() && layout_parent.display().is_flow_inside() && !layout_node.is_out_of_flow())
|
||||
return layout_parent;
|
||||
|
||||
if (!has_inline_or_in_flow_block_children(layout_parent)) {
|
||||
// Parent block has no children, insert this block into parent.
|
||||
return layout_parent;
|
||||
}
|
||||
// Make sure we're not inserting into an inline node, since those do not support block nodes.
|
||||
auto* new_parent = &layout_parent;
|
||||
while (is<InlineNode>(new_parent))
|
||||
new_parent = new_parent->parent();
|
||||
|
||||
if (layout_node.is_out_of_flow()
|
||||
&& !layout_parent.display().is_flex_inside()
|
||||
&& !layout_parent.display().is_grid_inside()
|
||||
&& !layout_parent.last_child()->is_generated_for_pseudo_element()
|
||||
&& layout_parent.last_child()->is_anonymous()
|
||||
&& layout_parent.last_child()->children_are_inline()) {
|
||||
// Block is out-of-flow & previous sibling was wrapped in an anonymous block.
|
||||
// Join the previous sibling inside the anonymous block.
|
||||
return *layout_parent.last_child();
|
||||
}
|
||||
|
||||
if (!layout_parent.children_are_inline()) {
|
||||
// Parent block has block-level children, insert this block into parent.
|
||||
return layout_parent;
|
||||
}
|
||||
// If the parent block has no children, insert this block into parent.
|
||||
if (!has_inline_or_in_flow_block_children(*new_parent))
|
||||
return *new_parent;
|
||||
|
||||
// If the block is out-of-flow,
|
||||
if (layout_node.is_out_of_flow()) {
|
||||
// Block is out-of-flow, it can have inline siblings if necessary.
|
||||
return layout_parent;
|
||||
// And the parent's last child is an anonymous block, join that anonymous block.
|
||||
if (!new_parent->display().is_flex_inside()
|
||||
&& !new_parent->display().is_grid_inside()
|
||||
&& !new_parent->last_child()->is_generated_for_pseudo_element()
|
||||
&& new_parent->last_child()->is_anonymous()
|
||||
&& new_parent->last_child()->children_are_inline()) {
|
||||
return *new_parent->last_child();
|
||||
}
|
||||
|
||||
// Otherwise, insert this block into parent.
|
||||
return *new_parent;
|
||||
}
|
||||
|
||||
// If the parent block has block-level children, insert this block into parent.
|
||||
if (!new_parent->children_are_inline())
|
||||
return *new_parent;
|
||||
|
||||
// Parent block has inline-level children (our siblings); wrap these siblings into an anonymous wrapper block.
|
||||
auto wrapper = layout_parent.create_anonymous_wrapper();
|
||||
auto wrapper = new_parent->create_anonymous_wrapper();
|
||||
wrapper->set_children_are_inline(true);
|
||||
|
||||
for (GC::Ptr<Node> child = layout_parent.first_child(); child;) {
|
||||
for (GC::Ptr<Node> child = new_parent->first_child(); child;) {
|
||||
GC::Ptr<Node> next_child = child->next_sibling();
|
||||
layout_parent.remove_child(*child);
|
||||
new_parent->remove_child(*child);
|
||||
wrapper->append_child(*child);
|
||||
child = next_child;
|
||||
}
|
||||
|
||||
layout_parent.set_children_are_inline(false);
|
||||
layout_parent.append_child(wrapper);
|
||||
new_parent->set_children_are_inline(false);
|
||||
new_parent->append_child(wrapper);
|
||||
|
||||
// Then it's safe to insert this block into parent.
|
||||
return layout_parent;
|
||||
return *new_parent;
|
||||
}
|
||||
|
||||
void TreeBuilder::insert_node_into_inline_or_block_ancestor(Layout::Node& node, CSS::Display display, AppendOrPrepend mode)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] children: not-inline
|
||||
BlockContainer <html> at [0,0] [0+0+0 800 0+0+0] [0+0+0 84 0+0+0] [BFC] children: not-inline
|
||||
BlockContainer <body> at [8,8] [8+0+0 784 0+0+8] [8+0+0 68 0+0+8] children: not-inline
|
||||
BlockContainer <(anonymous)> at [8,8] [0+0+0 784 0+0+0] [0+0+0 18 0+0+0] children: inline
|
||||
InlineNode <span> at [8,8] [0+0+0 54.796875 0+0+0] [0+0+0 18 0+0+0]
|
||||
frag 0 from TextNode start: 0, length: 6, rect: [8,8 54.796875x18] baseline: 13.796875
|
||||
"foobar"
|
||||
TextNode <#text> (not painted)
|
||||
BlockContainer <(anonymous)> at [8,26] [0+0+0 50 0+0+734] [0+0+0 50 0+0+0] children: inline
|
||||
TextNode <#text> (not painted)
|
||||
|
||||
ViewportPaintable (Viewport<#document>) [0,0 800x600]
|
||||
PaintableWithLines (BlockContainer<HTML>) [0,0 800x84]
|
||||
PaintableWithLines (BlockContainer<BODY>) [8,8 784x68]
|
||||
PaintableWithLines (BlockContainer(anonymous)) [8,8 784x18]
|
||||
PaintableWithLines (InlineNode<SPAN>) [8,8 54.796875x18]
|
||||
TextPaintable (TextNode<#text>)
|
||||
PaintableWithLines (BlockContainer(anonymous)) [8,26 50x50]
|
||||
|
||||
SC for Viewport<#document> [0,0 800x600] [children: 1] (z-index: auto)
|
||||
SC for BlockContainer<HTML> [0,0 800x84] [children: 0] (z-index: auto)
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<style>
|
||||
span::after {
|
||||
background: green;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
}
|
||||
</style>
|
||||
<span>foobar
|
||||
Loading…
Add table
Add a link
Reference in a new issue