mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-15 20:33:10 +00:00
LibWeb: Split out helper functions in TreeBuilder::create_layout_tree()
Let's make this function a little easier to understand by splitting out helpers into separate functions.
This commit is contained in:
parent
1e26d3d02d
commit
4fdfaff4ca
|
@ -110,6 +110,70 @@ static Layout::Node& insertion_parent_for_block_node(Layout::NodeWithStyle& layo
|
||||||
return layout_parent;
|
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<Layout::Node> 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<DOM::Text>(document.realm(), document, pseudo_element_content.data);
|
||||||
|
auto text_node = adopt_ref(*new TextNode(document, *text));
|
||||||
|
push_parent(verify_cast<NodeWithStyle>(*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)
|
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.
|
// 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)
|
if (!layout_node)
|
||||||
return;
|
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()) {
|
if (!dom_node.parent_or_shadow_host()) {
|
||||||
m_layout_root = layout_node;
|
m_layout_root = layout_node;
|
||||||
} else if (layout_node->is_svg_box()) {
|
} else if (layout_node->is_svg_box()) {
|
||||||
m_ancestor_stack.last().append_child(*layout_node);
|
m_ancestor_stack.last().append_child(*layout_node);
|
||||||
} else {
|
} 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::Element>(dom_node) ? verify_cast<DOM::Element>(dom_node).shadow_root() : nullptr;
|
auto* shadow_root = is<DOM::Element>(dom_node) ? verify_cast<DOM::Element>(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.
|
// Add nodes for the ::before and ::after pseudo-elements.
|
||||||
if (is<DOM::Element>(dom_node)) {
|
if (is<DOM::Element>(dom_node)) {
|
||||||
auto& element = static_cast<DOM::Element&>(dom_node);
|
auto& element = static_cast<DOM::Element&>(dom_node);
|
||||||
auto create_pseudo_element_if_needed = [&](CSS::Selector::PseudoElement pseudo_element) -> RefPtr<Node> {
|
|
||||||
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<DOM::Text>(document.realm(), document, pseudo_element_content.data);
|
|
||||||
auto text_node = adopt_ref(*new TextNode(document, *text));
|
|
||||||
push_parent(verify_cast<NodeWithStyle>(*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<NodeWithStyle>(*layout_node));
|
push_parent(verify_cast<NodeWithStyle>(*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());
|
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());
|
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();
|
pop_parent();
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,13 @@ private:
|
||||||
void generate_missing_child_wrappers(NodeWithStyle& root);
|
void generate_missing_child_wrappers(NodeWithStyle& root);
|
||||||
void generate_missing_parents(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<Layout::Node> create_pseudo_element_if_needed(DOM::Element&, CSS::Selector::PseudoElement);
|
||||||
|
|
||||||
RefPtr<Layout::Node> m_layout_root;
|
RefPtr<Layout::Node> m_layout_root;
|
||||||
Vector<Layout::NodeWithStyle&> m_ancestor_stack;
|
Vector<Layout::NodeWithStyle&> m_ancestor_stack;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue