mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-15 21:57:04 +00:00
SVG import: :not() rules test only the rules indexed for the element
set_by_not_rules tested every rule with a :not() against every element, while matched_bytes, which charges the import's selector budget, counts only the rules Index::candidates gives for the element. A style sheet of many class-keyed :not() rules made the pre-pass test rules x elements selectors that the budget never saw. It now loops over the same candidates, which hold every rule that can match the element, so the properties it returns are unchanged.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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#"<rect id="r{i}" class="c{i}"/>"#)).collect();
|
||||
let svg = format!(r#"<svg xmlns="http://www.w3.org/2000/svg"><style>{rules}</style>{rects}<rect id="skip" class="c0 skip"/></svg>"#);
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user