LibWeb: Compute pseudo-element style when computing element style

Previously, pseudo-elements had their style computed while the layout
tree was being built. Instead, do so inside Element::recompute_style(),
using the same invalidation mechanism that the element itself uses.

This also has the effect of invalidating the layout much less often.
This commit is contained in:
Sam Atkins 2024-07-30 15:55:21 +01:00 committed by Alexander Kalenik
commit 3abd3ef5e2
Notes: github-actions[bot] 2024-07-31 10:16:35 +00:00
3 changed files with 54 additions and 28 deletions

View file

@ -551,41 +551,31 @@ CSS::RequiredInvalidationAfterStyleChange Element::recompute_style()
else
invalidation = CSS::RequiredInvalidationAfterStyleChange::full();
if (!invalidation.is_none())
set_computed_css_values(move(new_computed_css_values));
// Any document change that can cause this element's style to change, could also affect its pseudo-elements.
// So determine if any pseudo-elements currently exist, or should now exist, and if so, invalidate everything.
// (If we're already invalidating everything, we don't need to do further checks for this.)
if (!invalidation.is_full()) {
bool pseudo_elements_dirty = false;
for (auto i = 0; i < to_underlying(CSS::Selector::PseudoElement::Type::KnownPseudoElementCount); i++) {
style_computer.push_ancestor(*this);
if (m_pseudo_element_data) {
for (auto& pseudo_element : *m_pseudo_element_data) {
if (pseudo_element.layout_node) {
pseudo_elements_dirty = true;
break;
}
}
}
auto pseudo_element = static_cast<CSS::Selector::PseudoElement::Type>(i);
auto pseudo_element_style = pseudo_element_computed_css_values(pseudo_element);
auto new_pseudo_element_style = style_computer.compute_pseudo_element_style_if_needed(*this, pseudo_element);
if (!pseudo_elements_dirty) {
for (auto i = 0; i < to_underlying(CSS::Selector::PseudoElement::Type::KnownPseudoElementCount); i++) {
auto style = style_computer.compute_pseudo_element_style_if_needed(*this, static_cast<CSS::Selector::PseudoElement::Type>(i));
if (style) {
pseudo_elements_dirty = true;
break;
}
}
}
if (pseudo_elements_dirty)
// TODO: Can we be smarter about invalidation?
if (pseudo_element_style && new_pseudo_element_style) {
invalidation |= compute_required_invalidation(*pseudo_element_style, *new_pseudo_element_style);
} else if (pseudo_element_style || new_pseudo_element_style) {
invalidation = CSS::RequiredInvalidationAfterStyleChange::full();
}
set_pseudo_element_computed_css_values(pseudo_element, move(new_pseudo_element_style));
style_computer.pop_ancestor(*this);
}
if (invalidation.is_none())
return invalidation;
m_computed_css_values = move(new_computed_css_values);
computed_css_values_changed();
if (invalidation.repaint)
document().set_needs_to_resolve_paint_only_properties();
@ -594,6 +584,24 @@ CSS::RequiredInvalidationAfterStyleChange Element::recompute_style()
layout_node()->apply_style(*m_computed_css_values);
if (invalidation.repaint && paintable())
paintable()->set_needs_display();
// Do the same for pseudo-elements.
for (auto i = 0; i < to_underlying(CSS::Selector::PseudoElement::Type::KnownPseudoElementCount); i++) {
auto pseudo_element_type = static_cast<CSS::Selector::PseudoElement::Type>(i);
auto pseudo_element = get_pseudo_element(pseudo_element_type);
if (!pseudo_element.has_value() || !pseudo_element->layout_node)
continue;
auto pseudo_element_style = pseudo_element_computed_css_values(pseudo_element_type);
if (!pseudo_element_style)
continue;
if (auto* node_with_style = dynamic_cast<Layout::NodeWithStyle*>(pseudo_element->layout_node.ptr())) {
node_with_style->apply_style(*pseudo_element_style);
if (invalidation.repaint && node_with_style->paintable())
node_with_style->paintable()->set_needs_display();
}
}
}
return invalidation;
@ -2253,6 +2261,21 @@ void Element::set_computed_css_values(RefPtr<CSS::StyleProperties> style)
computed_css_values_changed();
}
void Element::set_pseudo_element_computed_css_values(CSS::Selector::PseudoElement::Type pseudo_element, RefPtr<CSS::StyleProperties> style)
{
if (!m_pseudo_element_data && !style)
return;
ensure_pseudo_element(pseudo_element).computed_css_values = move(style);
}
RefPtr<CSS::StyleProperties> Element::pseudo_element_computed_css_values(CSS::Selector::PseudoElement::Type type)
{
auto pseudo_element = get_pseudo_element(type);
if (pseudo_element.has_value())
return pseudo_element->computed_css_values;
return nullptr;
}
Optional<Element::PseudoElement&> Element::get_pseudo_element(CSS::Selector::PseudoElement::Type type) const
{
if (!m_pseudo_element_data)