commit bb3e3ab5e511ddd9e4718c1445a6a6710281e955
parent 33f299d69f9b260232493c7fc76621625e535d63
Author: David Shin <dshin@mozilla.com>
Date: Wed, 8 Oct 2025 20:19:22 +0000
Bug 1993226: Do not coerce non-subject unknown results. r=firefox-style-system-reviewers,emilio
This optimization is not sound. See bugs 1874042 and 1964575.
Differential Revision: https://phabricator.services.mozilla.com/D268012
Diffstat:
3 files changed, 63 insertions(+), 18 deletions(-)
diff --git a/servo/components/selectors/matching.rs b/servo/components/selectors/matching.rs
@@ -826,24 +826,8 @@ where
selector_iter, element
);
- let matches_compound_selector = {
- let result = matches_compound_selector(&mut selector_iter, element, context, rightmost);
- // We only care for unknown match in the first subject in compound - in the context of comparison
- // invalidation, ancestors/previous sibling being an unknown match doesn't matter - we must
- // invalidate to guarantee correctness.
- if result == KleeneValue::Unknown && first_subject_compound == SubjectOrPseudoElement::No {
- debug_assert!(
- context
- .matching_for_invalidation_comparison()
- .unwrap_or(false),
- "How did we return unknown?"
- );
- // Coerce the result to matched.
- KleeneValue::from(!context.in_negation())
- } else {
- result
- }
- };
+ let matches_compound_selector =
+ matches_compound_selector(&mut selector_iter, element, context, rightmost);
let Some(combinator) = selector_iter.next_sequence() else {
return match matches_compound_selector {
diff --git a/testing/web-platform/tests/css/selectors/invalidation/negated-first-of-type-in-nonsubject-position.html b/testing/web-platform/tests/css/selectors/invalidation/negated-first-of-type-in-nonsubject-position.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!doctype html>
+<meta charset="utf-8">
+<html class="reftest-wait">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1993226">
+<link rel="author" title="David Shin" href="mailto:dshin@mozilla.com">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+.c {
+ width: 100px;
+ height: 100px;
+ background: green;
+}
+.b:not(.ancestor:first-of-type .a) .c {
+ background: red;
+}
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class=ancestor>
+ <div id=dut class=b>
+ <div class=c></div>
+ </div>
+</div>
+<script>
+onload = () => {
+ dut.classList.toggle('a');
+ dut.getBoundingClientRect(); // Ensure layout flush.
+ document.documentElement.classList.remove('reftest-wait');
+};
+</script>
diff --git a/testing/web-platform/tests/css/selectors/invalidation/negated-has-in-nonsubject-position.html b/testing/web-platform/tests/css/selectors/invalidation/negated-has-in-nonsubject-position.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!doctype html>
+<meta charset="utf-8">
+<html class="reftest-wait">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1993226">
+<link rel="author" title="David Shin" href="mailto:dshin@mozilla.com">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+.c {
+ width: 100px;
+ height: 100px;
+ background: green;
+}
+.b:not(.ancestor:has(+ .sibling) .a) .c {
+ background: red;
+}
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class=ancestor>
+ <div id=dut class=b>
+ <div class=c></div>
+ </div>
+</div>
+<div class=sibling></div>
+<script>
+onload = () => {
+ dut.classList.toggle('a');
+ dut.getBoundingClientRect(); // Ensure layout flush.
+ document.documentElement.classList.remove('reftest-wait');
+};
+</script>