Skip to content

Commit

Permalink
Implement vendor prefixing for @supports
Browse files Browse the repository at this point in the history
Fixes #341
  • Loading branch information
devongovett committed Dec 18, 2022
1 parent fc3e08a commit 787f46f
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 85 deletions.
49 changes: 45 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10339,6 +10339,47 @@ mod tests {
}
"#},
);
prefix_test(
r#"
@supports (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()
},
);
minify_test(
r#"
@supports (width: calc(10px * 2)) {
.test {
width: calc(10px * 2);
}
}
"#,
"@supports (width:calc(10px * 2)){.test{width:20px}}",
);
minify_test(
r#"
@supports (color: hsl(0deg, 0%, 0%)) {
.test {
color: hsl(0deg, 0%, 0%);
}
}
"#,
"@supports (color:hsl(0deg, 0%, 0%)){.test{color:#000}}",
);
}

#[test]
Expand Down Expand Up @@ -10490,19 +10531,19 @@ mod tests {
);
minify_test(
"@import url(foo.css) supports(display: flex);",
"@import \"foo.css\" supports(display: flex);",
"@import \"foo.css\" supports(display:flex);",
);
minify_test(
"@import url(foo.css) supports(display: flex) print;",
"@import \"foo.css\" supports(display: flex) print;",
"@import \"foo.css\" supports(display:flex) print;",
);
minify_test(
"@import url(foo.css) supports(not (display: flex));",
"@import \"foo.css\" supports(not (display: flex));",
"@import \"foo.css\" supports(not (display:flex));",
);
minify_test(
"@import url(foo.css) supports((display: flex));",
"@import \"foo.css\" supports(display: flex);",
"@import \"foo.css\" supports(display:flex);",
);
minify_test("@charset \"UTF-8\"; @import url(foo.css);", "@import \"foo.css\";");
minify_test("@layer foo; @import url(foo.css);", "@layer foo;@import \"foo.css\";");
Expand Down
74 changes: 16 additions & 58 deletions src/properties/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,28 +267,6 @@ macro_rules! define_properties {

impl<'i> ToCss for PropertyId<'i> {
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError> where W: std::fmt::Write {
use PropertyId::*;

let (name, prefix) = match self {
$(
$(#[$meta])*
$property$((vp_name!($vp, prefix)))? => {
macro_rules! get_prefix {
($v: ty) => {
*prefix
};
() => {
VendorPrefix::None
};
}

($name, get_prefix!($($vp)?))
},
)+
All => ("all", VendorPrefix::None),
Custom(name) => (name.as_ref(), VendorPrefix::None),
};

let mut first = true;
macro_rules! delim {
() => {
Expand All @@ -301,27 +279,20 @@ macro_rules! define_properties {
};
}

macro_rules! write {
($p: expr) => {
if prefix.contains($p) {
delim!();
$p.to_css(dest)?;
dest.write_str(name)?;
}
};
let name = self.name();
for p in self.prefix().or_none() {
delim!();
p.to_css(dest)?;
dest.write_str(name)?;
}

write!(VendorPrefix::WebKit);
write!(VendorPrefix::Moz);
write!(VendorPrefix::Ms);
write!(VendorPrefix::O);
write!(VendorPrefix::None);
Ok(())
}
}

impl<'i> PropertyId<'i> {
fn prefix(&self) -> VendorPrefix {
/// Returns the vendor prefix for this property id.
pub fn prefix(&self) -> VendorPrefix {
use PropertyId::*;
match self {
$(
Expand Down Expand Up @@ -366,7 +337,7 @@ macro_rules! define_properties {
}
}

fn set_prefixes_for_targets(&mut self, targets: Option<Browsers>) {
pub(crate) fn set_prefixes_for_targets(&mut self, targets: Browsers) {
match self {
$(
$(#[$meta])*
Expand All @@ -376,9 +347,7 @@ macro_rules! define_properties {
($v: ty, $u: literal) => {};
($v: ty) => {{
if prefix.contains(VendorPrefix::None) {
if let Some(targets) = targets {
*prefix = Feature::$property.prefixes_for(targets);
}
*prefix = Feature::$property.prefixes_for(targets);
};
}};
() => {};
Expand Down Expand Up @@ -677,25 +646,14 @@ macro_rules! define_properties {
return Ok(())
}
};

macro_rules! write {
($p: expr) => {
if prefix.contains($p) {
start!();
$p.to_css(dest)?;
dest.write_str(name)?;
dest.delim(':', false)?;
self.value_to_css(dest)?;
write_important!();
}
}
for p in prefix {
start!();
p.to_css(dest)?;
dest.write_str(name)?;
dest.delim(':', false)?;
self.value_to_css(dest)?;
write_important!();
}

write!(VendorPrefix::WebKit);
write!(VendorPrefix::Moz);
write!(VendorPrefix::Ms);
write!(VendorPrefix::O);
write!(VendorPrefix::None);
Ok(())
}

Expand Down
22 changes: 10 additions & 12 deletions src/properties/transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,16 +267,10 @@ impl<'i> TransitionHandler<'i> {
};

// Expand vendor prefixes into multiple transitions.
let prefix = property_id.prefix();
let mut b = 1 << (7 - prefix.bits().leading_zeros());
while b != 0 {
let p = VendorPrefix::from_bits_truncate(b);
if prefix.contains(p) {
let mut t = transition.clone();
t.property = property_id.with_prefix(p);
transitions.push(t);
}
b >>= 1;
for p in property_id.prefix().or_none() {
let mut t = transition.clone();
t.property = property_id.with_prefix(p);
transitions.push(t);
}
}
transitions
Expand Down Expand Up @@ -398,7 +392,9 @@ fn expand_properties<'i>(
}
_ => {
// Expand vendor prefixes for targets.
properties[i].set_prefixes_for_targets(targets);
if let Some(targets) = targets {
properties[i].set_prefixes_for_targets(targets);
}

// Expand mask properties, which use different vendor-prefixed names.
if let (Some(targets), Some(property_id)) = (targets, get_webkit_mask_property(&properties[i])) {
Expand All @@ -409,7 +405,9 @@ fn expand_properties<'i>(
}

if let Some(rtl_properties) = &mut rtl_properties {
rtl_properties[i].set_prefixes_for_targets(targets);
if let Some(targets) = targets {
rtl_properties[i].set_prefixes_for_targets(targets);
}

if let (Some(targets), Some(property_id)) = (targets, get_webkit_mask_property(&rtl_properties[i])) {
if Feature::MaskBorder.prefixes_for(targets).contains(VendorPrefix::WebKit) {
Expand Down
2 changes: 1 addition & 1 deletion src/rules/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl<'i> ToCss for ImportRule<'i> {
dest.write_str(" supports")?;
if matches!(
supports,
SupportsCondition::Declaration(_) | SupportsCondition::Parens(_)
SupportsCondition::Declaration { .. } | SupportsCondition::Parens(_)
) {
supports.to_css(dest)?;
} else {
Expand Down
72 changes: 65 additions & 7 deletions src/rules/supports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ use super::{CssRuleList, MinifyContext};
use crate::error::{MinifyError, ParserError, PrinterError};
use crate::parser::DefaultAtRule;
use crate::printer::Printer;
use crate::properties::PropertyId;
use crate::rules::{StyleContext, ToCssWithContext};
use crate::targets::Browsers;
use crate::traits::{Parse, ToCss};
use crate::values::string::CowArcStr;
use crate::vendor_prefix::VendorPrefix;
use crate::visitor::Visit;
use cssparser::*;

Expand All @@ -31,6 +34,10 @@ impl<'i, T> SupportsRule<'i, T> {
context: &mut MinifyContext<'_, 'i>,
parent_is_unused: bool,
) -> Result<(), MinifyError> {
if let Some(targets) = context.targets {
self.condition.set_prefixes_for_targets(targets)
}

self.rules.minify(context, parent_is_unused)
}
}
Expand Down Expand Up @@ -78,8 +85,13 @@ pub enum SupportsCondition<'i> {
#[skip_type]
Or(Vec<SupportsCondition<'i>>),
/// A declaration to evaluate.
#[cfg_attr(feature = "serde", serde(borrow))]
Declaration(CowArcStr<'i>),
Declaration {
/// The property id for the declaration.
#[cfg_attr(feature = "serde", serde(borrow))]
property_id: PropertyId<'i>,
/// The raw value of the declaration.
value: CowArcStr<'i>,
},
/// A selector to evaluate.
Selector(CowArcStr<'i>),
// FontTechnology()
Expand Down Expand Up @@ -112,6 +124,24 @@ impl<'i> SupportsCondition<'i> {
*self = SupportsCondition::Parens(Box::new(SupportsCondition::Or(vec![self.clone(), b.clone()])))
}
}

fn set_prefixes_for_targets(&mut self, targets: &Browsers) {
match self {
SupportsCondition::Not(cond) | SupportsCondition::Parens(cond) => cond.set_prefixes_for_targets(targets),
SupportsCondition::And(items) | SupportsCondition::Or(items) => {
for item in items {
item.set_prefixes_for_targets(targets);
}
}
SupportsCondition::Declaration { property_id, .. } => {
let prefix = property_id.prefix();
if prefix.is_empty() || prefix.contains(VendorPrefix::None) {
property_id.set_prefixes_for_targets(*targets);
}
}
_ => {}
}
}
}

impl<'i> Parse<'i> for SupportsCondition<'i> {
Expand Down Expand Up @@ -213,11 +243,15 @@ impl<'i> SupportsCondition<'i> {
pub(crate) fn parse_declaration<'t>(
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i, ParserError<'i>>> {
let pos = input.position();
input.expect_ident()?;
let property_id = PropertyId::parse(input)?;
input.expect_colon()?;
input.skip_whitespace();
let pos = input.position();
input.expect_no_error_token()?;
Ok(SupportsCondition::Declaration(input.slice_from(pos).into()))
Ok(SupportsCondition::Declaration {
property_id,
value: input.slice_from(pos).into(),
})
}
}

Expand Down Expand Up @@ -260,9 +294,33 @@ impl<'i> ToCss for SupportsCondition<'i> {
condition.to_css(dest)?;
dest.write_char(')')
}
SupportsCondition::Declaration(decl) => {
SupportsCondition::Declaration { property_id, value } => {
dest.write_char('(')?;
dest.write_str(&decl)?;

let prefix = property_id.prefix().or_none();
if prefix != VendorPrefix::None {
dest.write_char('(')?;
}

let name = property_id.name();
let mut first = true;
for p in prefix {
if first {
first = false;
} else {
dest.write_str(") or (")?;
}

p.to_css(dest)?;
serialize_name(name, dest)?;
dest.delim(':', false)?;
dest.write_str(value)?;
}

if prefix != VendorPrefix::None {
dest.write_char(')')?;
}

dest.write_char(')')
}
SupportsCondition::Selector(sel) => {
Expand Down
10 changes: 7 additions & 3 deletions src/values/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::compat::Feature;
use crate::error::{ParserError, PrinterError};
use crate::macros::enum_property;
use crate::printer::Printer;
use crate::properties::PropertyId;
use crate::rules::supports::SupportsCondition;
use crate::targets::Browsers;
use crate::traits::{FallbackValues, Parse, ToCss};
Expand Down Expand Up @@ -194,12 +195,15 @@ impl ColorFallbackKind {

pub(crate) fn supports_condition<'i>(&self) -> SupportsCondition<'i> {
let s = match *self {
ColorFallbackKind::P3 => "color: color(display-p3 0 0 0)",
ColorFallbackKind::LAB => "color: lab(0% 0 0)",
ColorFallbackKind::P3 => "color(display-p3 0 0 0)",
ColorFallbackKind::LAB => "lab(0% 0 0)",
_ => unreachable!(),
};

SupportsCondition::Declaration(s.into())
SupportsCondition::Declaration {
property_id: PropertyId::Color,
value: s.into(),
}
}
}

Expand Down

0 comments on commit 787f46f

Please sign in to comment.