LibWeb: Add support for trees of pseudo-elements

This is needed for CSS view transitions.
This commit is contained in:
Psychpsyo 2025-03-25 18:13:18 +01:00 committed by Sam Atkins
commit 31301ef08b
Notes: github-actions[bot] 2025-05-13 11:39:50 +00:00
5 changed files with 88 additions and 25 deletions

View file

@ -87,6 +87,9 @@
namespace Web::DOM {
GC_DEFINE_ALLOCATOR(Element::PseudoElement);
GC_DEFINE_ALLOCATOR(Element::PseudoElementTreeNode);
Element::Element(Document& document, DOM::QualifiedName qualified_name)
: ParentNode(document, NodeType::ELEMENT_NODE)
, m_qualified_name(move(qualified_name))
@ -117,9 +120,7 @@ void Element::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_computed_properties);
if (m_pseudo_element_data) {
for (auto& pseudo_element : *m_pseudo_element_data) {
visitor.visit(pseudo_element.cascaded_properties);
visitor.visit(pseudo_element.computed_properties);
visitor.visit(pseudo_element.layout_node);
visitor.visit(pseudo_element.value);
}
}
if (m_registered_intersection_observers) {
@ -128,6 +129,15 @@ void Element::visit_edges(Cell::Visitor& visitor)
}
}
void Element::PseudoElement::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(cascaded_properties);
visitor.visit(computed_properties);
visitor.visit(layout_node);
}
// https://dom.spec.whatwg.org/#dom-element-getattribute
Optional<String> Element::get_attribute(FlyString const& name) const
{
@ -1337,9 +1347,9 @@ bool Element::affected_by_pseudo_class(CSS::PseudoClass pseudo_class) const
}
if (m_pseudo_element_data) {
for (auto& pseudo_element : *m_pseudo_element_data) {
if (!pseudo_element.computed_properties)
if (!pseudo_element.value->computed_properties)
continue;
if (pseudo_element.computed_properties->has_attempted_match_against_pseudo_class(pseudo_class))
if (pseudo_element.value->computed_properties->has_attempted_match_against_pseudo_class(pseudo_class))
return true;
}
}
@ -1506,7 +1516,7 @@ bool Element::has_pseudo_elements() const
{
if (m_pseudo_element_data) {
for (auto& pseudo_element : *m_pseudo_element_data) {
if (pseudo_element.layout_node)
if (pseudo_element.value->layout_node)
return true;
}
}
@ -1517,7 +1527,7 @@ void Element::clear_pseudo_element_nodes(Badge<Layout::TreeBuilder>)
{
if (m_pseudo_element_data) {
for (auto& pseudo_element : *m_pseudo_element_data) {
pseudo_element.layout_node = nullptr;
pseudo_element.value->layout_node = nullptr;
}
}
}
@ -1526,15 +1536,12 @@ void Element::serialize_pseudo_elements_as_json(JsonArraySerializer<StringBuilde
{
if (!m_pseudo_element_data)
return;
for (size_t i = 0; i < m_pseudo_element_data->size(); ++i) {
auto& pseudo_element = (*m_pseudo_element_data)[i].layout_node;
if (!pseudo_element)
continue;
for (auto& pseudo_element : m_pseudo_element_data->keys()) {
auto object = MUST(children_array.add_object());
MUST(object.add("name"sv, MUST(String::formatted("::{}", CSS::pseudo_element_name(static_cast<CSS::PseudoElement>(i))))));
MUST(object.add("name"sv, MUST(String::formatted("::{}", CSS::pseudo_element_name(pseudo_element)))));
MUST(object.add("type"sv, "pseudo-element"));
MUST(object.add("parent-id"sv, unique_id().value()));
MUST(object.add("pseudo-element"sv, i));
MUST(object.add("pseudo-element"sv, to_underlying(pseudo_element)));
MUST(object.finish());
}
}
@ -2857,9 +2864,8 @@ void Element::set_pseudo_element_computed_properties(CSS::PseudoElement pseudo_e
if (!m_pseudo_element_data && !style)
return;
if (!CSS::Selector::PseudoElementSelector::is_known_pseudo_element_type(pseudo_element)) {
if (!CSS::Selector::PseudoElementSelector::is_known_pseudo_element_type(pseudo_element))
return;
}
ensure_pseudo_element(pseudo_element).computed_properties = style;
}
@ -2881,7 +2887,11 @@ Optional<Element::PseudoElement&> Element::get_pseudo_element(CSS::PseudoElement
return {};
}
return m_pseudo_element_data->at(to_underlying(type));
auto pseudo_element = m_pseudo_element_data->get(type);
if (!pseudo_element.has_value())
return {};
return *(pseudo_element.value());
}
Element::PseudoElement& Element::ensure_pseudo_element(CSS::PseudoElement type) const
@ -2891,7 +2901,15 @@ Element::PseudoElement& Element::ensure_pseudo_element(CSS::PseudoElement type)
VERIFY(CSS::Selector::PseudoElementSelector::is_known_pseudo_element_type(type));
return m_pseudo_element_data->at(to_underlying(type));
if (!m_pseudo_element_data->get(type).has_value()) {
if (is_pseudo_element_root(type)) {
m_pseudo_element_data->set(type, heap().allocate<PseudoElementTreeNode>());
} else {
m_pseudo_element_data->set(type, heap().allocate<PseudoElement>());
}
}
return *(m_pseudo_element_data->get(type).value());
}
void Element::set_custom_properties(Optional<CSS::PseudoElement> pseudo_element, HashMap<FlyString, CSS::StyleProperty> custom_properties)

View file

@ -527,15 +527,26 @@ private:
GC::Ptr<CSS::ComputedProperties> m_computed_properties;
HashMap<FlyString, CSS::StyleProperty> m_custom_properties;
struct PseudoElement {
struct PseudoElement : public JS::Cell {
GC_CELL(PseudoElement, JS::Cell);
GC_DECLARE_ALLOCATOR(PseudoElement);
GC::Ptr<Layout::NodeWithStyle> layout_node;
GC::Ptr<CSS::CascadedProperties> cascaded_properties;
GC::Ptr<CSS::ComputedProperties> computed_properties;
HashMap<FlyString, CSS::StyleProperty> custom_properties;
private:
virtual void visit_edges(JS::Cell::Visitor&) override;
};
// TODO: CSS::Selector::PseudoElement includes a lot of pseudo-elements that exist in shadow trees,
// and so we don't want to include data for them here.
using PseudoElementData = Array<PseudoElement, to_underlying(CSS::PseudoElement::KnownPseudoElementCount)>;
// https://drafts.csswg.org/css-view-transitions/#pseudo-element-tree
struct PseudoElementTreeNode
: public PseudoElement
, TreeNode<PseudoElementTreeNode> {
GC_CELL(PseudoElementTreeNode, PseudoElement);
GC_DECLARE_ALLOCATOR(PseudoElementTreeNode);
};
using PseudoElementData = HashMap<CSS::PseudoElement, GC::Ref<PseudoElement>>;
mutable OwnPtr<PseudoElementData> m_pseudo_element_data;
Optional<PseudoElement&> get_pseudo_element(CSS::PseudoElement) const;
PseudoElement& ensure_pseudo_element(CSS::PseudoElement) const;
@ -618,7 +629,10 @@ inline bool Element::has_pseudo_element(CSS::PseudoElement type) const
return false;
if (!CSS::Selector::PseudoElementSelector::is_known_pseudo_element_type(type))
return false;
return m_pseudo_element_data->at(to_underlying(type)).layout_node;
auto pseudo_element = m_pseudo_element_data->get(type);
if (!pseudo_element.has_value())
return false;
return pseudo_element.value()->layout_node;
}
WebIDL::ExceptionOr<QualifiedName> validate_and_extract(JS::Realm&, Optional<FlyString> namespace_, FlyString const& qualified_name);