summaryrefslogtreecommitdiff
path: root/Userland/Libraries
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2022-03-22 19:13:55 +0100
committerAndreas Kling <kling@serenityos.org>2022-03-22 19:26:51 +0100
commitfa64a7f6cc9a2b89b03e26030fa294684bb8c243 (patch)
tree211da1f4c9884b8cf04ee8f52ec008ab527bec44 /Userland/Libraries
parent74927fd21889f32dd6623db20ce03b883def2891 (diff)
downloadserenity-fa64a7f6cc9a2b89b03e26030fa294684bb8c243.zip
LibWeb: Relax "all children must be either inline or block" invariant
This was implemented too rigidly, which made it impossible to place floats correctly when they occurred in inline flow. The new invariant is "all in-flow children must be either inline or block". Out-of-flow children like floating and absolutely positioned boxes are ignored when deciding when to generate anonymous boxes.
Diffstat (limited to 'Userland/Libraries')
-rw-r--r--Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp37
1 files changed, 33 insertions, 4 deletions
diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp
index 3a678eea0d..47e4de0055 100644
--- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp
+++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp
@@ -26,8 +26,32 @@ namespace Web::Layout {
TreeBuilder::TreeBuilder() = default;
-// The insertion_parent_for_*() functions maintain the invariant that block-level boxes must have either
-// only block-level children or only inline-level children.
+static bool has_inline_or_in_flow_block_children(Layout::Node const& layout_node)
+{
+ for (auto const* child = layout_node.first_child(); child; child = child->next_sibling()) {
+ if (child->is_inline())
+ return true;
+ if (!child->is_floating() && !child->is_absolutely_positioned())
+ return true;
+ }
+ return false;
+}
+
+static bool has_in_flow_block_children(Layout::Node const& layout_node)
+{
+ if (layout_node.children_are_inline())
+ return false;
+ for (auto const* child = layout_node.first_child(); child; child = child->next_sibling()) {
+ if (child->is_inline())
+ continue;
+ if (!child->is_floating() && !child->is_absolutely_positioned())
+ return true;
+ }
+ return false;
+}
+
+// The insertion_parent_for_*() functions maintain the invariant that the in-flow children of
+// block-level boxes must be either all block-level or all inline-level.
static Layout::Node& insertion_parent_for_inline_node(Layout::NodeWithStyle& layout_parent)
{
@@ -38,7 +62,7 @@ static Layout::Node& insertion_parent_for_inline_node(Layout::NodeWithStyle& lay
layout_parent.append_child(layout_parent.create_anonymous_wrapper());
}
- if (!layout_parent.has_children() || layout_parent.children_are_inline())
+ if (!has_in_flow_block_children(layout_parent) || layout_parent.children_are_inline())
return layout_parent;
// Parent has block-level children, insert into an anonymous wrapper block (and create it first if needed)
@@ -50,7 +74,7 @@ static Layout::Node& insertion_parent_for_inline_node(Layout::NodeWithStyle& lay
static Layout::Node& insertion_parent_for_block_node(Layout::Node& layout_parent, Layout::Node& layout_node)
{
- if (!layout_parent.has_children()) {
+ if (!has_inline_or_in_flow_block_children(layout_parent)) {
// Parent block has no children, insert this block into parent.
return layout_parent;
}
@@ -60,6 +84,11 @@ static Layout::Node& insertion_parent_for_block_node(Layout::Node& layout_parent
return layout_parent;
}
+ if (layout_node.is_floating() || layout_node.is_absolutely_positioned()) {
+ // Block is out-of-flow, it can have inline siblings if necessary.
+ return layout_parent;
+ }
+
// Parent block has inline-level children (our siblings).
// First move these siblings into an anonymous wrapper block.
NonnullRefPtrVector<Layout::Node> children;