diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp index 2f3b00fdc7..74671a810d 100644 --- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp +++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp @@ -110,6 +110,70 @@ static Layout::Node& insertion_parent_for_block_node(Layout::NodeWithStyle& layo return layout_parent; } +void TreeBuilder::insert_node_into_inline_or_block_ancestor(Layout::Node& node, AppendOrPrepend mode) +{ + if (node.is_inline() && !(node.is_inline_block() && m_ancestor_stack.last().computed_values().display().is_flex_inside())) { + // Inlines can be inserted into the nearest ancestor. + auto& insertion_point = insertion_parent_for_inline_node(m_ancestor_stack.last()); + if (mode == AppendOrPrepend::Prepend) + insertion_point.prepend_child(node); + else + insertion_point.append_child(node); + insertion_point.set_children_are_inline(true); + } else { + // Non-inlines can't be inserted into an inline parent, so find the nearest non-inline ancestor. + auto& nearest_non_inline_ancestor = [&]() -> Layout::NodeWithStyle& { + for (auto& ancestor : m_ancestor_stack.in_reverse()) { + if (!ancestor.is_inline() || ancestor.is_inline_block()) + return ancestor; + } + VERIFY_NOT_REACHED(); + }(); + auto& insertion_point = insertion_parent_for_block_node(nearest_non_inline_ancestor, node); + if (mode == AppendOrPrepend::Prepend) + insertion_point.prepend_child(node); + else + insertion_point.append_child(node); + + // After inserting an in-flow block-level box into a parent, mark the parent as having non-inline children. + if (!node.is_floating() && !node.is_absolutely_positioned()) + insertion_point.set_children_are_inline(false); + } +} + +RefPtr TreeBuilder::create_pseudo_element_if_needed(DOM::Element& element, CSS::Selector::PseudoElement pseudo_element) +{ + auto& document = element.document(); + auto& style_computer = document.style_computer(); + + auto pseudo_element_style = style_computer.compute_style(element, pseudo_element); + auto pseudo_element_content = pseudo_element_style->content(); + auto pseudo_element_display = pseudo_element_style->display(); + // ::before and ::after only exist if they have content. `content: normal` computes to `none` for them. + // We also don't create them if they are `display: none`. + if (pseudo_element_display.is_none() + || pseudo_element_content.type == CSS::ContentData::Type::Normal + || pseudo_element_content.type == CSS::ContentData::Type::None) + return nullptr; + + if (auto pseudo_element_node = DOM::Element::create_layout_node_for_display_type(document, pseudo_element_display, move(pseudo_element_style), nullptr)) { + pseudo_element_node->set_generated(true); + // FIXME: Handle images, and multiple values + if (pseudo_element_content.type == CSS::ContentData::Type::String) { + auto* text = document.heap().allocate(document.realm(), document, pseudo_element_content.data); + auto text_node = adopt_ref(*new TextNode(document, *text)); + push_parent(verify_cast(*pseudo_element_node)); + insert_node_into_inline_or_block_ancestor(text_node, AppendOrPrepend::Append); + pop_parent(); + } else { + TODO(); + } + return pseudo_element_node.ptr(); + } + + return nullptr; +}; + void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context& context) { // If the parent doesn't have a layout node, we don't need one either. @@ -149,42 +213,12 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context& if (!layout_node) return; - auto insert_node_into_inline_or_block_ancestor = [this](auto& node, bool prepend = false) { - if (node->is_inline() && !(node->is_inline_block() && m_ancestor_stack.last().computed_values().display().is_flex_inside())) { - // Inlines can be inserted into the nearest ancestor. - auto& insertion_point = insertion_parent_for_inline_node(m_ancestor_stack.last()); - if (prepend) - insertion_point.prepend_child(*node); - else - insertion_point.append_child(*node); - insertion_point.set_children_are_inline(true); - } else { - // Non-inlines can't be inserted into an inline parent, so find the nearest non-inline ancestor. - auto& nearest_non_inline_ancestor = [&]() -> Layout::NodeWithStyle& { - for (auto& ancestor : m_ancestor_stack.in_reverse()) { - if (!ancestor.is_inline() || ancestor.is_inline_block()) - return ancestor; - } - VERIFY_NOT_REACHED(); - }(); - auto& insertion_point = insertion_parent_for_block_node(nearest_non_inline_ancestor, *node); - if (prepend) - insertion_point.prepend_child(*node); - else - insertion_point.append_child(*node); - - // After inserting an in-flow block-level box into a parent, mark the parent as having non-inline children. - if (!node->is_floating() && !node->is_absolutely_positioned()) - insertion_point.set_children_are_inline(false); - } - }; - if (!dom_node.parent_or_shadow_host()) { m_layout_root = layout_node; } else if (layout_node->is_svg_box()) { m_ancestor_stack.last().append_child(*layout_node); } else { - insert_node_into_inline_or_block_ancestor(layout_node); + insert_node_into_inline_or_block_ancestor(*layout_node, AppendOrPrepend::Append); } auto* shadow_root = is(dom_node) ? verify_cast(dom_node).shadow_root() : nullptr; @@ -202,43 +236,14 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context& // Add nodes for the ::before and ::after pseudo-elements. if (is(dom_node)) { auto& element = static_cast(dom_node); - auto create_pseudo_element_if_needed = [&](CSS::Selector::PseudoElement pseudo_element) -> RefPtr { - auto pseudo_element_style = style_computer.compute_style(element, pseudo_element); - auto pseudo_element_content = pseudo_element_style->content(); - auto pseudo_element_display = pseudo_element_style->display(); - // ::before and ::after only exist if they have content. `content: normal` computes to `none` for them. - // We also don't create them if they are `display: none`. - if (pseudo_element_display.is_none() - || pseudo_element_content.type == CSS::ContentData::Type::Normal - || pseudo_element_content.type == CSS::ContentData::Type::None) - return nullptr; - - if (auto pseudo_element_node = DOM::Element::create_layout_node_for_display_type(document, pseudo_element_display, move(pseudo_element_style), nullptr)) { - pseudo_element_node->set_generated(true); - // FIXME: Handle images, and multiple values - if (pseudo_element_content.type == CSS::ContentData::Type::String) { - auto* text = document.heap().allocate(document.realm(), document, pseudo_element_content.data); - auto text_node = adopt_ref(*new TextNode(document, *text)); - push_parent(verify_cast(*pseudo_element_node)); - insert_node_into_inline_or_block_ancestor(text_node); - pop_parent(); - } else { - TODO(); - } - return pseudo_element_node.ptr(); - } - - return nullptr; - }; - push_parent(verify_cast(*layout_node)); - if (auto before_node = create_pseudo_element_if_needed(CSS::Selector::PseudoElement::Before)) { + if (auto before_node = create_pseudo_element_if_needed(element, CSS::Selector::PseudoElement::Before)) { element.set_pseudo_element_node({}, CSS::Selector::PseudoElement::Before, before_node.ptr()); - insert_node_into_inline_or_block_ancestor(before_node, true); + insert_node_into_inline_or_block_ancestor(*before_node, AppendOrPrepend::Prepend); } - if (auto after_node = create_pseudo_element_if_needed(CSS::Selector::PseudoElement::After)) { + if (auto after_node = create_pseudo_element_if_needed(element, CSS::Selector::PseudoElement::After)) { element.set_pseudo_element_node({}, CSS::Selector::PseudoElement::After, after_node.ptr()); - insert_node_into_inline_or_block_ancestor(after_node); + insert_node_into_inline_or_block_ancestor(*after_node, AppendOrPrepend::Append); } pop_parent(); } diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.h b/Userland/Libraries/LibWeb/Layout/TreeBuilder.h index 45be0c0837..72e75fb72f 100644 --- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.h +++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.h @@ -40,6 +40,13 @@ private: void generate_missing_child_wrappers(NodeWithStyle& root); void generate_missing_parents(NodeWithStyle& root); + enum class AppendOrPrepend { + Append, + Prepend, + }; + void insert_node_into_inline_or_block_ancestor(Layout::Node&, AppendOrPrepend); + RefPtr create_pseudo_element_if_needed(DOM::Element&, CSS::Selector::PseudoElement); + RefPtr m_layout_root; Vector m_ancestor_stack; };