mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-10 10:09:14 +00:00
LibWeb: Invalidate sibling style for :only-child
and :*-of-type
After f7a3f785a8
, sibling nodes' styles
were no longer invalidated after a node was removed. This reuses the
flag for `:first-child` and `:last-child` to indicate that a node's
style might be affected by any structural change in its siblings.
Fixes #4631.
Resolves the `:only-child` ACID3 failure as documented in #1231.
This commit is contained in:
parent
74f133293d
commit
c56f7d9cde
Notes:
github-actions[bot]
2025-05-07 11:56:29 +00:00
Author: https://github.com/gmta
Commit: c56f7d9cde
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4632
Reviewed-by: https://github.com/kalenikaliaksandr ✅
5 changed files with 52 additions and 7 deletions
|
@ -480,15 +480,18 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
||||||
}
|
}
|
||||||
case CSS::PseudoClass::FirstChild:
|
case CSS::PseudoClass::FirstChild:
|
||||||
if (context.collect_per_element_selector_involvement_metadata) {
|
if (context.collect_per_element_selector_involvement_metadata) {
|
||||||
const_cast<DOM::Element&>(element).set_affected_by_first_or_last_child_pseudo_class(true);
|
const_cast<DOM::Element&>(element).set_affected_by_sibling_position_or_count_pseudo_class(true);
|
||||||
}
|
}
|
||||||
return !element.previous_element_sibling();
|
return !element.previous_element_sibling();
|
||||||
case CSS::PseudoClass::LastChild:
|
case CSS::PseudoClass::LastChild:
|
||||||
if (context.collect_per_element_selector_involvement_metadata) {
|
if (context.collect_per_element_selector_involvement_metadata) {
|
||||||
const_cast<DOM::Element&>(element).set_affected_by_first_or_last_child_pseudo_class(true);
|
const_cast<DOM::Element&>(element).set_affected_by_sibling_position_or_count_pseudo_class(true);
|
||||||
}
|
}
|
||||||
return !element.next_element_sibling();
|
return !element.next_element_sibling();
|
||||||
case CSS::PseudoClass::OnlyChild:
|
case CSS::PseudoClass::OnlyChild:
|
||||||
|
if (context.collect_per_element_selector_involvement_metadata) {
|
||||||
|
const_cast<DOM::Element&>(element).set_affected_by_sibling_position_or_count_pseudo_class(true);
|
||||||
|
}
|
||||||
return !(element.previous_element_sibling() || element.next_element_sibling());
|
return !(element.previous_element_sibling() || element.next_element_sibling());
|
||||||
case CSS::PseudoClass::Empty: {
|
case CSS::PseudoClass::Empty: {
|
||||||
if (!element.has_children())
|
if (!element.has_children())
|
||||||
|
@ -514,10 +517,19 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
||||||
case CSS::PseudoClass::Scope:
|
case CSS::PseudoClass::Scope:
|
||||||
return scope ? &element == scope : is<HTML::HTMLHtmlElement>(element);
|
return scope ? &element == scope : is<HTML::HTMLHtmlElement>(element);
|
||||||
case CSS::PseudoClass::FirstOfType:
|
case CSS::PseudoClass::FirstOfType:
|
||||||
|
if (context.collect_per_element_selector_involvement_metadata) {
|
||||||
|
const_cast<DOM::Element&>(element).set_affected_by_sibling_position_or_count_pseudo_class(true);
|
||||||
|
}
|
||||||
return !previous_sibling_with_same_tag_name(element);
|
return !previous_sibling_with_same_tag_name(element);
|
||||||
case CSS::PseudoClass::LastOfType:
|
case CSS::PseudoClass::LastOfType:
|
||||||
|
if (context.collect_per_element_selector_involvement_metadata) {
|
||||||
|
const_cast<DOM::Element&>(element).set_affected_by_sibling_position_or_count_pseudo_class(true);
|
||||||
|
}
|
||||||
return !next_sibling_with_same_tag_name(element);
|
return !next_sibling_with_same_tag_name(element);
|
||||||
case CSS::PseudoClass::OnlyOfType:
|
case CSS::PseudoClass::OnlyOfType:
|
||||||
|
if (context.collect_per_element_selector_involvement_metadata) {
|
||||||
|
const_cast<DOM::Element&>(element).set_affected_by_sibling_position_or_count_pseudo_class(true);
|
||||||
|
}
|
||||||
return !previous_sibling_with_same_tag_name(element) && !next_sibling_with_same_tag_name(element);
|
return !previous_sibling_with_same_tag_name(element) && !next_sibling_with_same_tag_name(element);
|
||||||
case CSS::PseudoClass::Lang:
|
case CSS::PseudoClass::Lang:
|
||||||
return matches_lang_pseudo_class(element, pseudo_class.languages);
|
return matches_lang_pseudo_class(element, pseudo_class.languages);
|
||||||
|
|
|
@ -620,7 +620,7 @@ CSS::RequiredInvalidationAfterStyleChange Element::recompute_style()
|
||||||
m_affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator = false;
|
m_affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator = false;
|
||||||
m_affected_by_direct_sibling_combinator = false;
|
m_affected_by_direct_sibling_combinator = false;
|
||||||
m_affected_by_indirect_sibling_combinator = false;
|
m_affected_by_indirect_sibling_combinator = false;
|
||||||
m_affected_by_first_or_last_child_pseudo_class = false;
|
m_affected_by_sibling_position_or_count_pseudo_class = false;
|
||||||
m_affected_by_nth_child_pseudo_class = false;
|
m_affected_by_nth_child_pseudo_class = false;
|
||||||
m_sibling_invalidation_distance = 0;
|
m_sibling_invalidation_distance = 0;
|
||||||
|
|
||||||
|
|
|
@ -453,8 +453,8 @@ public:
|
||||||
bool affected_by_indirect_sibling_combinator() const { return m_affected_by_indirect_sibling_combinator; }
|
bool affected_by_indirect_sibling_combinator() const { return m_affected_by_indirect_sibling_combinator; }
|
||||||
void set_affected_by_indirect_sibling_combinator(bool value) { m_affected_by_indirect_sibling_combinator = value; }
|
void set_affected_by_indirect_sibling_combinator(bool value) { m_affected_by_indirect_sibling_combinator = value; }
|
||||||
|
|
||||||
bool affected_by_first_or_last_child_pseudo_class() const { return m_affected_by_first_or_last_child_pseudo_class; }
|
bool affected_by_sibling_position_or_count_pseudo_class() const { return m_affected_by_sibling_position_or_count_pseudo_class; }
|
||||||
void set_affected_by_first_or_last_child_pseudo_class(bool value) { m_affected_by_first_or_last_child_pseudo_class = value; }
|
void set_affected_by_sibling_position_or_count_pseudo_class(bool value) { m_affected_by_sibling_position_or_count_pseudo_class = value; }
|
||||||
|
|
||||||
bool affected_by_nth_child_pseudo_class() const { return m_affected_by_nth_child_pseudo_class; }
|
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; }
|
void set_affected_by_nth_child_pseudo_class(bool value) { m_affected_by_nth_child_pseudo_class = value; }
|
||||||
|
@ -464,7 +464,7 @@ public:
|
||||||
|
|
||||||
bool style_affected_by_structural_changes() const
|
bool style_affected_by_structural_changes() const
|
||||||
{
|
{
|
||||||
return affected_by_direct_sibling_combinator() || affected_by_indirect_sibling_combinator() || affected_by_first_or_last_child_pseudo_class() || affected_by_nth_child_pseudo_class();
|
return affected_by_direct_sibling_combinator() || affected_by_indirect_sibling_combinator() || affected_by_sibling_position_or_count_pseudo_class() || affected_by_nth_child_pseudo_class();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t number_of_owned_list_items() const;
|
size_t number_of_owned_list_items() const;
|
||||||
|
@ -573,7 +573,7 @@ private:
|
||||||
bool m_affected_by_has_pseudo_class_in_non_subject_position : 1 { false };
|
bool m_affected_by_has_pseudo_class_in_non_subject_position : 1 { false };
|
||||||
bool m_affected_by_direct_sibling_combinator : 1 { false };
|
bool m_affected_by_direct_sibling_combinator : 1 { false };
|
||||||
bool m_affected_by_indirect_sibling_combinator : 1 { false };
|
bool m_affected_by_indirect_sibling_combinator : 1 { false };
|
||||||
bool m_affected_by_first_or_last_child_pseudo_class : 1 { false };
|
bool m_affected_by_sibling_position_or_count_pseudo_class : 1 { false };
|
||||||
bool m_affected_by_nth_child_pseudo_class : 1 { false };
|
bool m_affected_by_nth_child_pseudo_class : 1 { false };
|
||||||
bool m_affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator : 1 { false };
|
bool m_affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator : 1 { false };
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
div#a p: rgb(0, 128, 0)
|
||||||
|
div#b p: rgb(0, 128, 0)
|
||||||
|
div#c p: rgb(0, 128, 0)
|
|
@ -0,0 +1,30 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<script src="../include.js"></script>
|
||||||
|
<style>
|
||||||
|
div#a p:only-child {
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
div#b p:first-of-type {
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
div#c p:last-of-type {
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id="a"><p>foo</p><p>bar</p></div>
|
||||||
|
<div id="b"><p>foo</p><p>bar</p></div>
|
||||||
|
<div id="c"><p>foo</p><p>bar</p></div>
|
||||||
|
<script>
|
||||||
|
test(() => {
|
||||||
|
document.body.offsetWidth; // force layout
|
||||||
|
|
||||||
|
document.querySelector('div#a p:last-child').remove();
|
||||||
|
println(`div#a p: ${getComputedStyle(document.querySelector('div#a p')).backgroundColor}`);
|
||||||
|
|
||||||
|
document.querySelector('div#b p:first-child').remove();
|
||||||
|
println(`div#b p: ${getComputedStyle(document.querySelector('div#b p')).backgroundColor}`);
|
||||||
|
|
||||||
|
document.querySelector('div#c p:last-child').remove();
|
||||||
|
println(`div#c p: ${getComputedStyle(document.querySelector('div#c p')).backgroundColor}`);
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue