mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-07 08:39:22 +00:00
LibWeb: Avoid many style invalidations on DOM attribute mutation
Many times, attribute mutation doesn't necessitate a full style invalidation on the element. However, the conditions are pretty elaborate, so this first version has a lot of false positives. We only need to invalidate style when any of these things apply: 1. The change may affect the match state of a selector somewhere. 2. The change may affect presentational hints applied to the element. For (1) in this first version, we have a fixed list of attribute names that may affect selectors. We also collect all names referenced by attribute selectors anywhere in the document. For (2), we add a new Element::is_presentational_hint() virtual that tells us whether a given attribute name is a presentational hint. This drastically reduces style work on many websites. As an example, https://cnn.com/ is once again browseable.
This commit is contained in:
parent
b11bdd4022
commit
b981e6f7bc
Notes:
github-actions[bot]
2024-12-24 16:18:00 +00:00
Author: https://github.com/awesomekling
Commit: b981e6f7bc
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3034
56 changed files with 377 additions and 37 deletions
|
@ -573,6 +573,7 @@ CSS::RequiredInvalidationAfterStyleChange Element::recompute_inherited_style()
|
|||
invalidation |= CSS::compute_property_invalidation(property_id, old_value, new_value);
|
||||
}
|
||||
|
||||
document().style_computer().compute_font(*computed_properties, this, {});
|
||||
document().style_computer().absolutize_values(*computed_properties);
|
||||
|
||||
layout_node()->apply_style(*computed_properties);
|
||||
|
@ -1899,26 +1900,53 @@ ErrorOr<void> Element::scroll_into_view(Optional<Variant<bool, ScrollIntoViewOpt
|
|||
// FIXME: 8. Optionally perform some other action that brings the element to the user’s attention.
|
||||
}
|
||||
|
||||
static bool attribute_name_may_affect_selectors(Element const& element, FlyString const& attribute_name)
|
||||
{
|
||||
// FIXME: We could make these cases more narrow by making the conditions more elaborate.
|
||||
if (attribute_name == HTML::AttributeNames::id
|
||||
|| attribute_name == HTML::AttributeNames::class_
|
||||
|| attribute_name == HTML::AttributeNames::dir
|
||||
|| attribute_name == HTML::AttributeNames::lang
|
||||
|| attribute_name == HTML::AttributeNames::checked
|
||||
|| attribute_name == HTML::AttributeNames::disabled
|
||||
|| attribute_name == HTML::AttributeNames::readonly
|
||||
|| attribute_name == HTML::AttributeNames::switch_
|
||||
|| attribute_name == HTML::AttributeNames::href
|
||||
|| attribute_name == HTML::AttributeNames::open
|
||||
|| attribute_name == HTML::AttributeNames::placeholder) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return element.document().style_computer().has_attribute_selector(attribute_name);
|
||||
}
|
||||
|
||||
void Element::invalidate_style_after_attribute_change(FlyString const& attribute_name)
|
||||
{
|
||||
// FIXME: Only invalidate if the attribute can actually affect style.
|
||||
|
||||
// OPTIMIZATION: For the `style` attribute, unless it's referenced by an attribute selector,
|
||||
// only invalidate the element itself, then let inheritance propagate to descendants.
|
||||
if (attribute_name == HTML::AttributeNames::style
|
||||
&& !document().style_computer().has_attribute_selector(HTML::AttributeNames::style)) {
|
||||
set_needs_style_update(true);
|
||||
for_each_shadow_including_descendant([](Node& node) {
|
||||
if (!node.is_element())
|
||||
if (attribute_name == HTML::AttributeNames::style) {
|
||||
if (!document().style_computer().has_attribute_selector(HTML::AttributeNames::style)) {
|
||||
set_needs_style_update(true);
|
||||
for_each_shadow_including_descendant([](Node& node) {
|
||||
if (!node.is_element())
|
||||
return TraversalDecision::Continue;
|
||||
auto& element = static_cast<Element&>(node);
|
||||
element.set_needs_inherited_style_update(true);
|
||||
return TraversalDecision::Continue;
|
||||
auto& element = static_cast<Element&>(node);
|
||||
element.set_needs_inherited_style_update(true);
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
invalidate_style(StyleInvalidationReason::ElementAttributeChange);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
invalidate_style(StyleInvalidationReason::ElementAttributeChange);
|
||||
if (is_presentational_hint(attribute_name)
|
||||
|| attribute_name_may_affect_selectors(*this, attribute_name)) {
|
||||
invalidate_style(StyleInvalidationReason::ElementAttributeChange);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool Element::is_hidden() const
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue