mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 04:09:13 +00:00
LibWeb: Optimize style invalidation caused by DOM structural changes
With this change, siblings of an inserted node are no longer invalidated unless the insertion could potentially affect their style. By "potentially affected," we mean elements that are evaluated against the following selectors during matching: - Sibling combinators (+ or ~) - Pseudo-classes :first-child and :last-child - Pseudo-classes :nth-child, :nth-last-child, :nth-of-type, and :nth-last-of-type
This commit is contained in:
parent
eef678223a
commit
61c952fb43
Notes:
github-actions[bot]
2025-02-06 19:08:36 +00:00
Author: https://github.com/kalenikaliaksandr
Commit: 61c952fb43
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3476
Reviewed-by: https://github.com/AtkinsSJ
4 changed files with 42 additions and 4 deletions
|
@ -495,8 +495,14 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
|||
return focused_element && element.is_inclusive_ancestor_of(*focused_element);
|
||||
}
|
||||
case CSS::PseudoClass::FirstChild:
|
||||
if (context.collect_per_element_selector_involvement_metadata) {
|
||||
const_cast<DOM::Element&>(element).set_affected_by_first_or_last_child_pseudo_class(true);
|
||||
}
|
||||
return !element.previous_element_sibling();
|
||||
case CSS::PseudoClass::LastChild:
|
||||
if (context.collect_per_element_selector_involvement_metadata) {
|
||||
const_cast<DOM::Element&>(element).set_affected_by_first_or_last_child_pseudo_class(true);
|
||||
}
|
||||
return !element.next_element_sibling();
|
||||
case CSS::PseudoClass::OnlyChild:
|
||||
return !(element.previous_element_sibling() || element.next_element_sibling());
|
||||
|
@ -585,6 +591,10 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
|||
if (!parent)
|
||||
return false;
|
||||
|
||||
if (context.collect_per_element_selector_involvement_metadata) {
|
||||
const_cast<DOM::Element&>(element).set_affected_by_nth_child_pseudo_class(true);
|
||||
}
|
||||
|
||||
auto matches_selector_list = [&context, shadow_host](CSS::SelectorList const& list, DOM::Element const& element) {
|
||||
if (list.is_empty())
|
||||
return true;
|
||||
|
@ -1011,11 +1021,17 @@ bool matches(CSS::Selector const& selector, int component_list_index, DOM::Eleme
|
|||
return matches(selector, component_list_index - 1, static_cast<DOM::Element const&>(*parent), shadow_host, context, scope, selector_kind, anchor);
|
||||
}
|
||||
case CSS::Selector::Combinator::NextSibling:
|
||||
if (context.collect_per_element_selector_involvement_metadata) {
|
||||
const_cast<DOM::Element&>(element).set_affected_by_sibling_combinator(true);
|
||||
}
|
||||
VERIFY(component_list_index != 0);
|
||||
if (auto* sibling = element.previous_element_sibling())
|
||||
return matches(selector, component_list_index - 1, *sibling, shadow_host, context, scope, selector_kind, anchor);
|
||||
return false;
|
||||
case CSS::Selector::Combinator::SubsequentSibling:
|
||||
if (context.collect_per_element_selector_involvement_metadata) {
|
||||
const_cast<DOM::Element&>(element).set_affected_by_sibling_combinator(true);
|
||||
}
|
||||
VERIFY(component_list_index != 0);
|
||||
for (auto* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) {
|
||||
if (matches(selector, component_list_index - 1, *sibling, shadow_host, context, scope, selector_kind, anchor))
|
||||
|
|
|
@ -494,6 +494,11 @@ CSS::RequiredInvalidationAfterStyleChange Element::recompute_style()
|
|||
{
|
||||
VERIFY(parent());
|
||||
|
||||
m_affected_by_has_pseudo_class_in_subject_position = false;
|
||||
m_affected_by_sibling_combinator = false;
|
||||
m_affected_by_first_or_last_child_pseudo_class = false;
|
||||
m_affected_by_nth_child_pseudo_class = false;
|
||||
|
||||
auto& style_computer = document().style_computer();
|
||||
auto new_computed_properties = style_computer.compute_style(*this);
|
||||
|
||||
|
|
|
@ -423,6 +423,20 @@ public:
|
|||
bool affected_by_has_pseudo_class_in_subject_position() const { return m_affected_by_has_pseudo_class_in_subject_position; }
|
||||
void set_affected_by_has_pseudo_class_in_subject_position(bool value) { m_affected_by_has_pseudo_class_in_subject_position = value; }
|
||||
|
||||
bool affected_by_sibling_combinator() const { return m_affected_by_sibling_combinator; }
|
||||
void set_affected_by_sibling_combinator(bool value) { m_affected_by_sibling_combinator = value; }
|
||||
|
||||
bool affected_by_first_or_last_child_pseudo_class() const { return m_affected_by_first_or_last_child_pseudo_class; }
|
||||
void set_affected_by_first_or_last_child_pseudo_class(bool value) { m_affected_by_first_or_last_child_pseudo_class = value; }
|
||||
|
||||
bool affected_by_nth_child_pseudo_class() const { return m_affected_by_nth_child_pseudo_class; }
|
||||
void set_affected_by_nth_child_pseudo_class(bool value) { m_affected_by_nth_child_pseudo_class = value; }
|
||||
|
||||
bool style_affected_by_structural_changes() const
|
||||
{
|
||||
return affected_by_sibling_combinator() || affected_by_first_or_last_child_pseudo_class() || affected_by_nth_child_pseudo_class();
|
||||
}
|
||||
|
||||
protected:
|
||||
Element(Document&, DOM::QualifiedName);
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
|
@ -514,6 +528,9 @@ private:
|
|||
bool m_rendered_in_top_layer { false };
|
||||
bool m_style_uses_css_custom_properties { false };
|
||||
bool m_affected_by_has_pseudo_class_in_subject_position { false };
|
||||
bool m_affected_by_sibling_combinator { false };
|
||||
bool m_affected_by_first_or_last_child_pseudo_class { false };
|
||||
bool m_affected_by_nth_child_pseudo_class { false };
|
||||
|
||||
OwnPtr<CSS::CountersSet> m_counters_set;
|
||||
|
||||
|
|
|
@ -439,14 +439,14 @@ void Node::invalidate_style(StyleInvalidationReason reason)
|
|||
|
||||
if (reason == StyleInvalidationReason::NodeInsertBefore || reason == StyleInvalidationReason::NodeRemove) {
|
||||
for (auto* sibling = previous_sibling(); sibling; sibling = sibling->previous_sibling()) {
|
||||
if (sibling->is_element())
|
||||
sibling->set_entire_subtree_needs_style_update(true);
|
||||
if (auto* element = as_if<Element>(sibling); element && element->style_affected_by_structural_changes())
|
||||
element->set_entire_subtree_needs_style_update(true);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* sibling = next_sibling(); sibling; sibling = sibling->next_sibling()) {
|
||||
if (sibling->is_element())
|
||||
sibling->set_entire_subtree_needs_style_update(true);
|
||||
if (auto* element = as_if<Element>(sibling); element && element->style_affected_by_structural_changes())
|
||||
element->set_entire_subtree_needs_style_update(true);
|
||||
}
|
||||
|
||||
for (auto* ancestor = parent_or_shadow_host(); ancestor; ancestor = ancestor->parent_or_shadow_host())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue