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#"{rects}"#); + 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);