Skip to content

Commit

Permalink
LibWeb: Check all siblings in ancestors chain while invalidating :has()
Browse files Browse the repository at this point in the history
Fixes underinvalidaiton of `:has()` selectors with sibling combinators.
  • Loading branch information
kalenikaliaksandr committed Feb 12, 2025
1 parent 7dbef8d commit 976af84
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 18 deletions.
33 changes: 15 additions & 18 deletions Libraries/LibWeb/DOM/Node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,16 +411,17 @@ void Node::invalidate_ancestors_affected_by_has_in_subject_position()
element.set_needs_style_update(true);
}

// If any previous sibling ancestor was tested against selectors like ".a:has(+ .b)" or ".a:has(~ .b)"
auto* parent = ancestor->parent_or_shadow_host();
if (!parent)
return;

// If any ancestor's sibling was tested against selectors like ".a:has(+ .b)" or ".a:has(~ .b)"
// its style might be affected by the change in descendant node.
for (auto* previous_sibling = element.previous_sibling(); previous_sibling; previous_sibling = previous_sibling->previous_sibling()) {
if (!previous_sibling->is_element())
continue;
auto& element = static_cast<Element&>(*previous_sibling);
if (element.affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator()) {
parent->for_each_child_of_type<Element>([&](auto& element) {
if (element.affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator())
element.set_needs_style_update(true);
}
}
return IterationDecision::Continue;
});
}
}

Expand All @@ -431,17 +432,13 @@ void Node::invalidate_style(StyleInvalidationReason reason)

if (document().style_computer().may_have_has_selectors()) {
if (reason == StyleInvalidationReason::NodeRemove) {
for (auto* previous_sibling = this->previous_sibling(); previous_sibling; previous_sibling = previous_sibling->previous_sibling()) {
if (!previous_sibling->is_element())
continue;
auto& element = static_cast<Element&>(*previous_sibling);
if (element.affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator()) {
element.set_needs_style_update(true);
}
}

if (auto parent = parent_or_shadow_host(); parent) {
if (auto* parent = parent_or_shadow_host(); parent) {
document().schedule_ancestors_style_invalidation_due_to_presence_of_has(*parent);
parent->for_each_child_of_type<Element>([&](auto& element) {
if (element.affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator())
element.set_needs_style_update(true);
return IterationDecision::Continue;
});
}
} else {
document().schedule_ancestors_style_invalidation_due_to_presence_of_has(*this);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
background-color before: rgb(204, 204, 255)
background-color after: rgb(238, 238, 255)
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<style>
div {
min-height: 50px;
min-width: 50px;
margin: 5px;
padding: 5px;
border: 1px solid #ccc;
color: grey;
}

#parent {
border: 2px solid black;
padding: 10px;
}

.blue {
background-color: #eef;
}

.f_has_scope {
background-color: #efe;
border: 2px dotted green;
}

.invalid {
background-color: #ffc;
border: 2px solid orange;
}

.blue:has(~ #indirect_next:is(.p + .f_has_scope ~ .g)) {
color: blue;
background-color: #ccf;
border: 2px solid blue;
}
</style>
<div id="parent">
<div class="p"></div>
<div id="previous" class="f_has_scope"></div>
<div id="has_scope" class="blue"></div>
<div id="indirect_next" class="g"></div>
</div>
<script>
test(() => {
const blue = document.getElementById("has_scope");
println(`background-color before: ${getComputedStyle(blue).backgroundColor}`);

let insertBefore = document.getElementById("previous");
let parent = insertBefore.parentElement;

let div = document.createElement("div");
div.classList.add("invalid");

parent.insertBefore(div, insertBefore);

println(`background-color after: ${getComputedStyle(blue).backgroundColor}`);
});
</script>

0 comments on commit 976af84

Please sign in to comment.