diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp index a1030279977..7d40221d176 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.cpp +++ b/Userland/Libraries/LibWeb/DOM/Element.cpp @@ -501,4 +501,34 @@ void Element::children_changed() set_needs_style_update(true); } +void Element::set_pseudo_element_node(Badge, CSS::Selector::PseudoElement pseudo_element, RefPtr pseudo_element_node) +{ + m_pseudo_element_nodes[to_underlying(pseudo_element)] = move(pseudo_element_node); +} + +RefPtr Element::get_pseudo_element_node(CSS::Selector::PseudoElement pseudo_element) const +{ + return m_pseudo_element_nodes[to_underlying(pseudo_element)]; +} + +void Element::clear_pseudo_element_nodes(Badge) +{ + m_pseudo_element_nodes.fill(nullptr); +} + +void Element::serialize_pseudo_elements_as_json(JsonArraySerializer& children_array) const +{ + for (size_t i = 0; i < m_pseudo_element_nodes.size(); ++i) { + auto& pseudo_element_node = m_pseudo_element_nodes[i]; + if (!pseudo_element_node) + continue; + auto object = MUST(children_array.add_object()); + MUST(object.add("name", String::formatted("::{}", CSS::pseudo_element_name(static_cast(i))))); + MUST(object.add("type", "pseudo-element")); + MUST(object.add("parent-id", id())); + MUST(object.add("pseudo-element", i)); + MUST(object.finish()); + } +} + } diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h index 94cc4d7cf8b..7c0914aeff4 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.h +++ b/Userland/Libraries/LibWeb/DOM/Element.h @@ -21,6 +21,7 @@ #include #include #include +#include namespace Web::DOM { @@ -131,6 +132,11 @@ public: static RefPtr create_layout_node_for_display_type(DOM::Document&, CSS::Display const&, NonnullRefPtr, Element*); + void set_pseudo_element_node(Badge, CSS::Selector::PseudoElement, RefPtr); + RefPtr get_pseudo_element_node(CSS::Selector::PseudoElement) const; + void clear_pseudo_element_nodes(Badge); + void serialize_pseudo_elements_as_json(JsonArraySerializer& children_array) const; + protected: virtual void children_changed() override; @@ -150,6 +156,8 @@ private: Vector m_classes; RefPtr m_shadow_root; + + Array, CSS::Selector::PseudoElementCount> m_pseudo_element_nodes; }; template<> diff --git a/Userland/Libraries/LibWeb/DOM/Node.cpp b/Userland/Libraries/LibWeb/DOM/Node.cpp index acfcaca0456..1b52e3199e2 100644 --- a/Userland/Libraries/LibWeb/DOM/Node.cpp +++ b/Userland/Libraries/LibWeb/DOM/Node.cpp @@ -776,6 +776,13 @@ void Node::serialize_tree_as_json(JsonObjectSerializer& object) c child.serialize_tree_as_json(child_object); MUST(child_object.finish()); }); + + // Pseudo-elements don't have DOM nodes,so we have to add them separately. + if (is_element()) { + auto const* element = static_cast(this); + element->serialize_pseudo_elements_as_json(children); + } + MUST(children.finish()); } } diff --git a/Userland/Libraries/LibWeb/DOMTreeModel.cpp b/Userland/Libraries/LibWeb/DOMTreeModel.cpp index ccde46a4c77..28c7aedf3e3 100644 --- a/Userland/Libraries/LibWeb/DOMTreeModel.cpp +++ b/Userland/Libraries/LibWeb/DOMTreeModel.cpp @@ -126,6 +126,8 @@ GUI::Variant DOMTreeModel::data(const GUI::ModelIndex& index, GUI::ModelRole rol // Then we won't need to have a GUI::TreeView& member anymore. if (type == "comment"sv) return m_tree_view.palette().syntax_comment(); + if (type == "pseudo-element"sv) + return m_tree_view.palette().syntax_type(); return {}; } diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp index fe70eaa3162..bb453a7e43f 100644 --- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp +++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp @@ -99,6 +99,7 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context& if (is(dom_node)) { auto& element = static_cast(dom_node); + element.clear_pseudo_element_nodes({}); auto style = style_computer.compute_style(element); if (style->display().is_none()) return; @@ -193,20 +194,26 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context& }; 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(CSS::Selector::PseudoElement::Before)) { + element.set_pseudo_element_node({}, CSS::Selector::PseudoElement::Before, before_node); insert_node_into_inline_or_block_ancestor(before_node, true); - if (auto after_node = create_pseudo_element_if_needed(CSS::Selector::PseudoElement::After)) + } + if (auto after_node = create_pseudo_element_if_needed(CSS::Selector::PseudoElement::After)) { + element.set_pseudo_element_node({}, CSS::Selector::PseudoElement::After, after_node); insert_node_into_inline_or_block_ancestor(after_node); + } pop_parent(); } if (is(*layout_node)) { + auto& element = static_cast(dom_node); int child_index = layout_node->parent()->index_of_child(*layout_node).value(); - auto marker_style = style_computer.compute_style(static_cast(dom_node), CSS::Selector::PseudoElement::Marker); + auto marker_style = style_computer.compute_style(element, CSS::Selector::PseudoElement::Marker); auto list_item_marker = adopt_ref(*new ListItemMarkerBox(document, layout_node->computed_values().list_style_type(), child_index + 1, *marker_style)); if (layout_node->first_child()) list_item_marker->set_inline(layout_node->first_child()->is_inline()); static_cast(*layout_node).set_marker(list_item_marker); + element.set_pseudo_element_node({}, CSS::Selector::PseudoElement::Marker, list_item_marker); layout_node->append_child(move(list_item_marker)); } }