mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-24 10:09:32 -06:00
Batch fix: svg: a selector's class checks count against the CSS matching budget (#2159)
This commit is contained in:
@@ -635,14 +635,16 @@ impl<'a, 'i> Parser<'a, 'i> {
|
||||
if !self.charge_css(buckets.iter().map(|b| b.len()).fold(0, usize::saturating_add)) {
|
||||
return;
|
||||
}
|
||||
let mut matching: Vec<&Rule> = buckets
|
||||
.iter()
|
||||
.flat_map(|b| b.iter())
|
||||
.filter_map(|&i| css.rules.get(i))
|
||||
let candidates = || buckets.iter().flat_map(|b| b.iter()).filter_map(|&i| css.rules.get(i));
|
||||
// A selector like `.z.z.z…` checks each of its classes: charge them too.
|
||||
if !self.charge_css(candidates().map(|r| r.classes.len()).fold(0, usize::saturating_add)) {
|
||||
return;
|
||||
}
|
||||
let mut matching: Vec<&Rule> = candidates()
|
||||
.filter(|r| {
|
||||
r.tag.as_deref().is_none_or(|t| t == tag)
|
||||
&& r.id.as_deref().is_none_or(|i| Some(i) == id)
|
||||
&& r.classes.iter().all(|c| classes.contains(&c.as_str()))
|
||||
&& r.classes.iter().all(|c| classes.binary_search(&c.as_str()).is_ok())
|
||||
})
|
||||
.collect();
|
||||
matching.sort_by_key(|r| (r.specificity, r.order));
|
||||
|
||||
@@ -379,6 +379,22 @@ fn huge_class_lists_count_against_the_matching_budget() {
|
||||
assert!(parse(svg(&few).as_bytes()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_selector_with_many_classes_counts_against_the_matching_budget() {
|
||||
// `.z.z.z…` lands in one class bucket, but each match checks every class it lists.
|
||||
let rule = ".z".repeat(300_000);
|
||||
let uses: String = (0..200).map(|i| format!(r##"<use href="#r" x="{i}"/>"##)).collect();
|
||||
let svg = |uses: &str| {
|
||||
format!(
|
||||
r##"<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10"><style>{rule}{{fill:red}}</style><defs><rect id="r" width="1" height="1" class="z"/></defs>{uses}</svg>"##
|
||||
)
|
||||
};
|
||||
let e = parse(svg(&uses).as_bytes()).unwrap_err();
|
||||
assert!(e.to_string().contains("<style> rules"), "{e}");
|
||||
let few: String = uses.split_inclusive("/>").take(10).collect();
|
||||
assert!(parse(svg(&few).as_bytes()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matching_work_past_the_budget_errors_without_doing_it() {
|
||||
// Within both sheet caps, yet one rectangle would apply 10 000 x 5 001 declarations.
|
||||
|
||||
Reference in New Issue
Block a user