diff --git a/crates/svg/src/import/css.rs b/crates/svg/src/import/css.rs
index b5f1d80320..7bf440cc73 100644
--- a/crates/svg/src/import/css.rs
+++ b/crates/svg/src/import/css.rs
@@ -556,10 +556,12 @@ impl Styles {
}
/// The properties rules with a `:not()` set on `n`, each with its value after the cascade.
+ /// Only the rules [`Index::candidates`] gives for `n` are tested, as in [`Styles::matched_bytes`].
pub(super) fn set_by_not_rules(&self, n: XNode) -> Vec<(String, String)> {
let declared = self.declared(n);
let mut out: Vec<(String, String)> = Vec::new();
- for r in self.rules.iter().filter(|r| r.selector.has_not() && r.selector.matches(n)) {
+ let rules = self.index.candidates(n).into_iter().filter_map(|i| self.rules.get(i));
+ for r in rules.filter(|r| r.selector.has_not() && r.selector.matches(n)) {
for (k, ..) in r.decls.iter() {
if !out.iter().any(|(o, _)| o == k)
&& let Some(v) = declared.get(k)
diff --git a/crates/svg/src/import/css/tests.rs b/crates/svg/src/import/css/tests.rs
index 2e78c4da98..5c9e305c27 100644
--- a/crates/svg/src/import/css/tests.rs
+++ b/crates/svg/src/import/css/tests.rs
@@ -334,6 +334,26 @@ fn not_selectors() {
);
}
+/// The `:not()` rules filed under classes an element lacks are not tested against it, so
+/// [`Styles::set_by_not_rules`] tests no more rules than [`Styles::matched_bytes`] counts.
+#[test]
+fn not_rules_test_only_the_indexed_candidates() {
+ const N: usize = 2_000;
+ let rules: String = (0..N).map(|i| format!(".c{i}:not(.skip) {{ fill: #{i:06x} }}\n")).collect();
+ let rects: String = (0..N).map(|i| format!(r#""#)).collect();
+ let svg = format!(r#""#);
+ with_doc(&svg, |doc, st| {
+ let before = selector_tests();
+ for i in 0..N {
+ assert_eq!(st.set_by_not_rules(by_id(doc, &format!("r{i}"))), [("fill".to_string(), format!("#{i:06x}"))]);
+ }
+ assert!(st.set_by_not_rules(by_id(doc, "skip")).is_empty());
+ // Testing every `:not()` rule against every element would test N × N selectors.
+ let tests = selector_tests() - before;
+ assert!(tests <= 3 * N as u64, "finding the :not() rules of {N} elements tested {tests} selectors");
+ });
+}
+
#[test]
fn decl_comments_are_stripped() {
let d = |k: &str, v: &str| (k.to_string(), v.to_string(), false);