diff --git a/src/lib.rs b/src/lib.rs index 8c70f8ad..f13b7c48 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13123,6 +13123,68 @@ mod tests { ..Default::default() }, ); + prefix_test( + r#" + @supports ((-webkit-backdrop-filter: blur(10px)) or (backdrop-filter: blur(10px))) { + div { + backdrop-filter: blur(10px); + } + } + "#, + indoc! { r#" + @supports ((-webkit-backdrop-filter: blur(10px)) or (backdrop-filter: blur(10px))) { + div { + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + } + } + "#}, + Browsers { + safari: Some(14 << 16), + ..Default::default() + }, + ); + prefix_test( + r#" + @supports ((-webkit-backdrop-filter: blur(20px)) or (backdrop-filter: blur(10px))) { + div { + backdrop-filter: blur(10px); + } + } + "#, + indoc! { r#" + @supports ((-webkit-backdrop-filter: blur(20px))) or ((-webkit-backdrop-filter: blur(10px)) or (backdrop-filter: blur(10px))) { + div { + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + } + } + "#}, + Browsers { + safari: Some(14 << 16), + ..Default::default() + }, + ); + prefix_test( + r#" + @supports ((-webkit-backdrop-filter: blur(10px)) or (backdrop-filter: blur(10px))) { + div { + backdrop-filter: blur(10px); + } + } + "#, + indoc! { r#" + @supports (backdrop-filter: blur(10px)) { + div { + backdrop-filter: blur(10px); + } + } + "#}, + Browsers { + chrome: Some(80 << 16), + ..Default::default() + }, + ); minify_test( r#" @supports (width: calc(10px * 2)) { diff --git a/src/properties/mod.rs b/src/properties/mod.rs index 6e39fc5d..845140f9 100644 --- a/src/properties/mod.rs +++ b/src/properties/mod.rs @@ -322,7 +322,7 @@ macro_rules! define_properties { } } - fn with_prefix(&self, prefix: VendorPrefix) -> PropertyId<'i> { + pub(crate) fn with_prefix(&self, prefix: VendorPrefix) -> PropertyId<'i> { use PropertyId::*; match self { $( @@ -344,6 +344,26 @@ macro_rules! define_properties { } } + pub(crate) fn add_prefix(&mut self, prefix: VendorPrefix) { + use PropertyId::*; + match self { + $( + $(#[$meta])* + $property$((vp_name!($vp, p)))? => { + macro_rules! get_prefixed { + ($v: ty) => {{ + *p |= prefix; + }}; + () => {{}}; + } + + get_prefixed!($($vp)?) + }, + )+ + _ => {} + } + } + pub(crate) fn set_prefixes_for_targets(&mut self, targets: Targets) { match self { $( diff --git a/src/rules/supports.rs b/src/rules/supports.rs index 68c13e6a..04f1fdd3 100644 --- a/src/rules/supports.rs +++ b/src/rules/supports.rs @@ -1,5 +1,7 @@ //! The `@supports` rule. +use std::collections::HashMap; + use super::Location; use super::{CssRuleList, MinifyContext}; use crate::error::{MinifyError, ParserError, PrinterError}; @@ -159,6 +161,7 @@ impl<'i> Parse<'i> for SupportsCondition<'i> { let in_parens = Self::parse_in_parens(input)?; let mut expected_type = None; let mut conditions = Vec::new(); + let mut seen_declarations = HashMap::new(); loop { let condition = input.try_parse(|input| { @@ -185,14 +188,40 @@ impl<'i> Parse<'i> for SupportsCondition<'i> { if let Ok(condition) = condition { if conditions.is_empty() { - conditions.push(in_parens.clone()) + conditions.push(in_parens.clone()); + if let SupportsCondition::Declaration { property_id, value } = &in_parens { + seen_declarations.insert((property_id.with_prefix(VendorPrefix::None), value.clone()), 0); + } + } + + if let SupportsCondition::Declaration { property_id, value } = condition { + // Merge multiple declarations with the same property id (minus prefix) and value together. + let property_id = property_id.with_prefix(VendorPrefix::None); + let key = (property_id.clone(), value.clone()); + if let Some(index) = seen_declarations.get(&key) { + if let SupportsCondition::Declaration { + property_id: cur_property, + .. + } = &mut conditions[*index] + { + cur_property.add_prefix(property_id.prefix()); + } + } else { + seen_declarations.insert(key, conditions.len()); + conditions.push(SupportsCondition::Declaration { property_id, value }); + } + } else { + conditions.push(condition); } - conditions.push(condition) } else { break; } } + if conditions.len() == 1 { + return Ok(conditions.pop().unwrap()); + } + match expected_type { Some(1) => Ok(SupportsCondition::And(conditions)), Some(2) => Ok(SupportsCondition::Or(conditions)),