diff --git a/src/lib.rs b/src/lib.rs index 5822b9b6..76685d6f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1677,6 +1677,137 @@ mod tests { ..Browsers::default() }, ); + + prefix_test( + r#" + .foo { + border-width: 22px; + border-width: max(2cqw, 22px); + } + "#, + indoc! {r#" + .foo { + border-width: 22px; + border-width: max(2cqw, 22px); + } + "# + }, + Browsers { + safari: Some(14 << 16), + ..Browsers::default() + }, + ); + prefix_test( + r#" + .foo { + border-width: 22px; + border-width: max(2cqw, 22px); + } + "#, + indoc! {r#" + .foo { + border-width: max(2cqw, 22px); + } + "# + }, + Browsers { + safari: Some(16 << 16), + ..Browsers::default() + }, + ); + prefix_test( + r#" + .foo { + border-color: #4263eb; + border-color: color(display-p3 0 .5 1); + } + "#, + indoc! {r#" + .foo { + border-color: #4263eb; + border-color: color(display-p3 0 .5 1); + } + "# + }, + Browsers { + chrome: Some(99 << 16), + ..Browsers::default() + }, + ); + prefix_test( + r#" + .foo { + border-color: #4263eb; + border-color: color(display-p3 0 .5 1); + } + "#, + indoc! {r#" + .foo { + border-color: color(display-p3 0 .5 1); + } + "# + }, + Browsers { + safari: Some(16 << 16), + ..Browsers::default() + }, + ); + prefix_test( + r#" + .foo { + border: 1px solid #4263eb; + border-color: color(display-p3 0 .5 1); + } + "#, + indoc! {r#" + .foo { + border: 1px solid #4263eb; + border-color: color(display-p3 0 .5 1); + } + "# + }, + Browsers { + chrome: Some(99 << 16), + ..Browsers::default() + }, + ); + prefix_test( + r#" + .foo { + border: 1px solid #4263eb; + border-color: color(display-p3 0 .5 1); + } + "#, + indoc! {r#" + .foo { + border: 1px solid #4263eb; + border-color: color(display-p3 0 .5 1); + } + "# + }, + Browsers { + chrome: Some(99 << 16), + ..Browsers::default() + }, + ); + prefix_test( + r#" + .foo { + border: 1px solid #4263eb; + border-color: color(display-p3 0 .5 1); + } + "#, + indoc! {r#" + .foo { + border: 1px solid color(display-p3 0 .5 1); + } + "# + }, + Browsers { + safari: Some(16 << 16), + ..Browsers::default() + }, + ); } #[test] @@ -3029,6 +3160,25 @@ mod tests { ..Browsers::default() }, ); + prefix_test( + r#" + .foo { + margin: 22px; + margin: max(2cqw, 22px); + } + "#, + indoc! {r#" + .foo { + margin: 22px; + margin: max(2cqw, 22px); + } + "# + }, + Browsers { + safari: Some(14 << 16), + ..Browsers::default() + }, + ); } #[test] diff --git a/src/properties/border.rs b/src/properties/border.rs index d527001d..722b8949 100644 --- a/src/properties/border.rs +++ b/src/properties/border.rs @@ -1,5 +1,7 @@ //! CSS properties related to borders. +#![allow(non_upper_case_globals)] + use super::border_image::*; use super::border_radius::*; use crate::compat::Feature; @@ -12,13 +14,14 @@ use crate::printer::Printer; use crate::properties::custom::UnparsedProperty; use crate::properties::{Property, PropertyId}; use crate::targets::Browsers; -use crate::traits::{FallbackValues, Parse, PropertyHandler, Shorthand, ToCss}; +use crate::traits::{FallbackValues, IsCompatible, Parse, PropertyHandler, Shorthand, ToCss}; use crate::values::color::{ColorFallbackKind, CssColor}; use crate::values::length::*; use crate::values::rect::Rect; use crate::values::size::Size2D; #[cfg(feature = "visitor")] use crate::visitor::Visit; +use bitflags::bitflags; use cssparser::*; /// A value for the [border-width](https://www.w3.org/TR/css-backgrounds-3/#border-width) property. @@ -47,6 +50,15 @@ impl Default for BorderSideWidth { } } +impl IsCompatible for BorderSideWidth { + fn is_compatible(&self, browsers: Browsers) -> bool { + match self { + BorderSideWidth::Length(length) => length.is_compatible(browsers), + _ => true, + } + } +} + impl<'i> Parse<'i> for BorderSideWidth { fn parse<'t>(input: &mut Parser<'i, 't>) -> Result>> { if let Ok(length) = input.try_parse(|i| Length::parse(i)) { @@ -110,6 +122,12 @@ impl Default for LineStyle { } } +impl IsCompatible for LineStyle { + fn is_compatible(&self, _browsers: Browsers) -> bool { + true + } +} + /// A generic type that represents the `border` and `outline` shorthand properties. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "visitor", derive(Visit))] @@ -499,6 +517,55 @@ impl BorderShorthand { } } +bitflags! { + struct BorderProperty: u32 { + const BorderTopColor = 1 << 0; + const BorderBottomColor = 1 << 1; + const BorderLeftColor = 1 << 2; + const BorderRightColor = 1 << 3; + const BorderBlockStartColor = 1 << 4; + const BorderBlockEndColor = 1 << 5; + const BorderBlockColor = Self::BorderBlockStartColor.bits | Self::BorderBlockEndColor.bits; + const BorderInlineStartColor = 1 << 6; + const BorderInlineEndColor = 1 << 7; + const BorderInlineColor = Self::BorderInlineStartColor.bits | Self::BorderInlineEndColor.bits; + const BorderTopWidth = 1 << 8; + const BorderBottomWidth = 1 << 9; + const BorderLeftWidth = 1 << 10; + const BorderRightWidth = 1 << 11; + const BorderBlockStartWidth = 1 << 12; + const BorderBlockEndWidth = 1 << 13; + const BorderBlockWidth = Self::BorderBlockStartWidth.bits | Self::BorderBlockEndWidth.bits; + const BorderInlineStartWidth = 1 << 14; + const BorderInlineEndWidth = 1 << 15; + const BorderInlineWidth = Self::BorderInlineStartWidth.bits | Self::BorderInlineEndWidth.bits; + const BorderTopStyle = 1 << 16; + const BorderBottomStyle = 1 << 17; + const BorderLeftStyle = 1 << 18; + const BorderRightStyle = 1 << 19; + const BorderBlockStartStyle = 1 << 20; + const BorderBlockEndStyle = 1 << 21; + const BorderBlockStyle = Self::BorderBlockStartStyle.bits | Self::BorderBlockEndStyle.bits; + const BorderInlineStartStyle = 1 << 22; + const BorderInlineEndStyle = 1 << 23; + const BorderInlineStyle = Self::BorderInlineStartStyle.bits | Self::BorderInlineEndStyle.bits; + const BorderTop = Self::BorderTopColor.bits | Self::BorderTopWidth.bits | Self::BorderTopStyle.bits; + const BorderBottom = Self::BorderBottomColor.bits | Self::BorderBottomWidth.bits | Self::BorderBottomStyle.bits; + const BorderLeft = Self::BorderLeftColor.bits | Self::BorderLeftWidth.bits | Self::BorderLeftStyle.bits; + const BorderRight = Self::BorderRightColor.bits | Self::BorderRightWidth.bits | Self::BorderRightStyle.bits; + const BorderBlockStart = Self::BorderBlockStartColor.bits | Self::BorderBlockStartWidth.bits | Self::BorderBlockStartStyle.bits; + const BorderBlockEnd = Self::BorderBlockEndColor.bits | Self::BorderBlockEndWidth.bits | Self::BorderBlockEndStyle.bits; + const BorderInlineStart = Self::BorderInlineStartColor.bits | Self::BorderInlineStartWidth.bits | Self::BorderInlineStartStyle.bits; + const BorderInlineEnd = Self::BorderInlineEndColor.bits | Self::BorderInlineEndWidth.bits | Self::BorderInlineEndStyle.bits; + const BorderBlock = Self::BorderBlockStart.bits | Self::BorderBlockEnd.bits; + const BorderInline = Self::BorderInlineStart.bits | Self::BorderInlineEnd.bits; + const BorderWidth = Self::BorderLeftWidth.bits | Self::BorderRightWidth.bits | Self::BorderTopWidth.bits | Self::BorderBottomWidth.bits; + const BorderStyle = Self::BorderLeftStyle.bits | Self::BorderRightStyle.bits | Self::BorderTopStyle.bits | Self::BorderBottomStyle.bits; + const BorderColor = Self::BorderLeftColor.bits | Self::BorderRightColor.bits | Self::BorderTopColor.bits | Self::BorderBottomColor.bits; + const Border = Self::BorderWidth.bits | Self::BorderStyle.bits | Self::BorderColor.bits; + } +} + #[derive(Debug)] pub(crate) struct BorderHandler<'i> { targets: Option, @@ -513,6 +580,7 @@ pub(crate) struct BorderHandler<'i> { category: PropertyCategory, border_image_handler: BorderImageHandler<'i>, border_radius_handler: BorderRadiusHandler<'i>, + flushed_properties: BorderProperty, has_any: bool, } @@ -531,6 +599,7 @@ impl<'i> BorderHandler<'i> { category: PropertyCategory::default(), border_image_handler: BorderImageHandler::new(targets), border_radius_handler: BorderRadiusHandler::new(targets), + flushed_properties: BorderProperty::empty(), has_any: false, } } @@ -545,11 +614,21 @@ impl<'i> PropertyHandler<'i> for BorderHandler<'i> { ) -> bool { use Property::*; - macro_rules! property { + macro_rules! flush { ($key: ident, $prop: ident, $val: expr, $category: ident) => {{ if PropertyCategory::$category != self.category { self.flush(dest, context); } + + if self.$key.$prop.is_some() && matches!(context.targets, Some(targets) if !$val.is_compatible(targets)) { + self.flush(dest, context); + } + }}; + } + + macro_rules! property { + ($key: ident, $prop: ident, $val: expr, $category: ident) => {{ + flush!($key, $prop, $val, $category); self.$key.$prop = Some($val.clone()); self.category = PropertyCategory::$category; self.has_any = true; @@ -633,10 +712,10 @@ impl<'i> PropertyHandler<'i> for BorderHandler<'i> { set_border!(border_inline_end, val, Logical); } BorderWidth(val) => { - self.border_top.width = Some(val.top.clone()); - self.border_right.width = Some(val.right.clone()); - self.border_bottom.width = Some(val.bottom.clone()); - self.border_left.width = Some(val.left.clone()); + property!(border_top, width, val.top, Physical); + property!(border_right, width, val.right, Physical); + property!(border_bottom, width, val.bottom, Physical); + property!(border_left, width, val.left, Physical); self.border_block_start.width = None; self.border_block_end.width = None; self.border_inline_start.width = None; @@ -644,10 +723,10 @@ impl<'i> PropertyHandler<'i> for BorderHandler<'i> { self.has_any = true; } BorderStyle(val) => { - self.border_top.style = Some(val.top.clone()); - self.border_right.style = Some(val.right.clone()); - self.border_bottom.style = Some(val.bottom.clone()); - self.border_left.style = Some(val.left.clone()); + property!(border_top, style, val.top, Physical); + property!(border_right, style, val.right, Physical); + property!(border_bottom, style, val.bottom, Physical); + property!(border_left, style, val.left, Physical); self.border_block_start.style = None; self.border_block_end.style = None; self.border_inline_start.style = None; @@ -655,10 +734,10 @@ impl<'i> PropertyHandler<'i> for BorderHandler<'i> { self.has_any = true; } BorderColor(val) => { - self.border_top.color = Some(val.top.clone()); - self.border_right.color = Some(val.right.clone()); - self.border_bottom.color = Some(val.bottom.clone()); - self.border_left.color = Some(val.left.clone()); + property!(border_top, color, val.top, Physical); + property!(border_right, color, val.right, Physical); + property!(border_bottom, color, val.bottom, Physical); + property!(border_left, color, val.left, Physical); self.border_block_start.color = None; self.border_block_end.color = None; self.border_inline_start.color = None; @@ -699,6 +778,7 @@ impl<'i> PropertyHandler<'i> for BorderHandler<'i> { fn finalize(&mut self, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) { self.flush(dest, context); + self.flushed_properties = BorderProperty::empty(); self.border_image_handler.finalize(dest, context); self.border_radius_handler.finalize(dest, context); } @@ -720,16 +800,25 @@ impl<'i> BorderHandler<'i> { }}; } + macro_rules! push { + ($prop: ident, $val: expr) => {{ + self.flushed_properties.insert(BorderProperty::$prop); + dest.push(Property::$prop($val)); + }}; + } + macro_rules! fallbacks { ($prop: ident => $val: expr) => {{ let mut val = $val; if let Some(targets) = self.targets { - let fallbacks = val.get_fallbacks(targets); - for fallback in fallbacks { - dest.push(Property::$prop(fallback)) + if !self.flushed_properties.contains(BorderProperty::$prop) { + let fallbacks = val.get_fallbacks(targets); + for fallback in fallbacks { + dest.push(Property::$prop(fallback)) + } } } - dest.push(Property::$prop(val)) + push!($prop, val); }}; } @@ -743,7 +832,7 @@ impl<'i> BorderHandler<'i> { }; (BorderInlineStartWidth => $val: expr) => { if logical_supported { - dest.push(Property::BorderInlineStartWidth($val)); + push!(BorderInlineStartWidth, $val); } else { logical_prop!(BorderLeftWidth, border_left_width, BorderRightWidth, border_right_width, $val); } @@ -757,7 +846,7 @@ impl<'i> BorderHandler<'i> { }; (BorderInlineStartStyle => $val: expr) => { if logical_supported { - dest.push(Property::BorderInlineStartStyle($val)); + push!(BorderInlineStartStyle, $val); } else { logical_prop!(BorderLeftStyle, border_left_style, BorderRightStyle, border_right_style, $val); } @@ -771,7 +860,7 @@ impl<'i> BorderHandler<'i> { }; (BorderInlineEndWidth => $val: expr) => { if logical_supported { - dest.push(Property::BorderInlineEndWidth($val)); + push!(BorderInlineEndWidth, $val); } else { logical_prop!(BorderRightWidth, border_right_width, BorderLeftWidth, border_left_width, $val); } @@ -785,7 +874,7 @@ impl<'i> BorderHandler<'i> { }; (BorderInlineEndStyle => $val: expr) => { if logical_supported { - dest.push(Property::BorderInlineEndStyle($val)); + push!(BorderInlineEndStyle, $val); } else { logical_prop!(BorderRightStyle, border_right_style, BorderLeftStyle, border_left_style, $val); } @@ -799,9 +888,9 @@ impl<'i> BorderHandler<'i> { }; (BorderBlockStartWidth => $val: expr) => { if logical_supported { - dest.push(Property::BorderBlockStartWidth($val)); + push!(BorderBlockStartWidth, $val); } else { - dest.push(Property::BorderTopWidth($val)); + push!(BorderTopWidth, $val); } }; (BorderBlockStartColor => $val: expr) => { @@ -813,9 +902,9 @@ impl<'i> BorderHandler<'i> { }; (BorderBlockStartStyle => $val: expr) => { if logical_supported { - dest.push(Property::BorderBlockStartStyle($val)); + push!(BorderBlockStartStyle, $val); } else { - dest.push(Property::BorderTopStyle($val)); + push!(BorderTopStyle, $val); } }; (BorderBlockEnd => $val: expr) => { @@ -827,9 +916,9 @@ impl<'i> BorderHandler<'i> { }; (BorderBlockEndWidth => $val: expr) => { if logical_supported { - dest.push(Property::BorderBlockEndWidth($val)); + push!(BorderBlockEndWidth, $val); } else { - dest.push(Property::BorderBottomWidth($val)); + push!(BorderBottomWidth, $val); } }; (BorderBlockEndColor => $val: expr) => { @@ -841,9 +930,9 @@ impl<'i> BorderHandler<'i> { }; (BorderBlockEndStyle => $val: expr) => { if logical_supported { - dest.push(Property::BorderBlockEndStyle($val)); + push!(BorderBlockEndStyle, $val); } else { - dest.push(Property::BorderBottomStyle($val)); + push!(BorderBottomStyle, $val); } }; (BorderLeftColor => $val: expr) => { @@ -901,7 +990,7 @@ impl<'i> BorderHandler<'i> { fallbacks!(Border => $val); }; ($prop: ident => $val: expr) => { - dest.push(Property::$prop($val)) + push!($prop, $val); }; } diff --git a/src/properties/margin_padding.rs b/src/properties/margin_padding.rs index 93d25ce1..1d9e08c8 100644 --- a/src/properties/margin_padding.rs +++ b/src/properties/margin_padding.rs @@ -163,7 +163,7 @@ size_shorthand! { } macro_rules! side_handler { - ($name: ident, $top: ident, $bottom: ident, $left: ident, $right: ident, $block_start: ident, $block_end: ident, $inline_start: ident, $inline_end: ident, $shorthand: ident, $block_shorthand: ident, $inline_shorthand: ident, $logical_shorthand: literal $(, $feature: ident, $shorthand_feature: ident)?) => { + ($name: ident, $top: ident, $bottom: ident, $left: ident, $right: ident, $block_start: ident, $block_end: ident, $inline_start: ident, $inline_end: ident, $shorthand: ident, $block_shorthand: ident, $inline_shorthand: ident, $shorthand_category: ident $(, $feature: ident, $shorthand_feature: ident)?) => { #[derive(Debug, Default)] pub(crate) struct $name<'i> { top: Option, @@ -249,10 +249,10 @@ macro_rules! side_handler { logical_property!(inline_end, Property::$inline_end(val.inline_end.clone())); }, $shorthand(val) => { - flush!(top, val.top, Physical); - flush!(right, val.right, Physical); - flush!(bottom, val.bottom, Physical); - flush!(left, val.left, Physical); + flush!(top, val.top, $shorthand_category); + flush!(right, val.right, $shorthand_category); + flush!(bottom, val.bottom, $shorthand_category); + flush!(left, val.left, $shorthand_category); self.top = Some(val.top.clone()); self.right = Some(val.right.clone()); self.bottom = Some(val.bottom.clone()); @@ -302,7 +302,7 @@ macro_rules! side_handler { let right = std::mem::take(&mut self.right); let logical_supported = true $(&& context.is_supported(Feature::$feature))?; - if (!$logical_shorthand || logical_supported) && top.is_some() && bottom.is_some() && left.is_some() && right.is_some() { + if (PropertyCategory::$shorthand_category != PropertyCategory::Logical || logical_supported) && top.is_some() && bottom.is_some() && left.is_some() && right.is_some() { dest.push(Property::$shorthand($shorthand { top: top.unwrap(), right: right.unwrap(), @@ -422,7 +422,7 @@ side_handler!( Margin, MarginBlock, MarginInline, - false, + Physical, LogicalMargin, LogicalMarginShorthand ); @@ -440,7 +440,7 @@ side_handler!( Padding, PaddingBlock, PaddingInline, - false, + Physical, LogicalPadding, LogicalPaddingShorthand ); @@ -458,7 +458,7 @@ side_handler!( ScrollMargin, ScrollMarginBlock, ScrollMarginInline, - false + Physical ); side_handler!( @@ -474,7 +474,7 @@ side_handler!( ScrollPadding, ScrollPaddingBlock, ScrollPaddingInline, - false + Physical ); side_handler!( @@ -490,7 +490,7 @@ side_handler!( Inset, InsetBlock, InsetInline, - true, + Logical, LogicalInset, LogicalInset ); diff --git a/src/values/color.rs b/src/values/color.rs index d2cf89e2..7c88892e 100644 --- a/src/values/color.rs +++ b/src/values/color.rs @@ -13,7 +13,7 @@ use crate::printer::Printer; use crate::properties::PropertyId; use crate::rules::supports::SupportsCondition; use crate::targets::Browsers; -use crate::traits::{FallbackValues, Parse, ToCss}; +use crate::traits::{FallbackValues, IsCompatible, Parse, ToCss}; #[cfg(feature = "visitor")] use crate::visitor::{Visit, VisitTypes, Visitor}; use bitflags::bitflags; @@ -375,6 +375,22 @@ impl CssColor { } } +impl IsCompatible for CssColor { + fn is_compatible(&self, browsers: Browsers) -> bool { + match self { + CssColor::CurrentColor | CssColor::RGBA(_) | CssColor::Float(..) => true, + CssColor::LAB(lab) => match &**lab { + LABColor::LAB(..) | LABColor::LCH(..) => Feature::LabColors.is_compatible(browsers), + LABColor::OKLAB(..) | LABColor::OKLCH(..) => Feature::OklabColors.is_compatible(browsers), + }, + CssColor::Predefined(predefined) => match &**predefined { + PredefinedColor::DisplayP3(..) => Feature::P3Colors.is_compatible(browsers), + _ => Feature::ColorFunction.is_compatible(browsers), + }, + } + } +} + impl FallbackValues for CssColor { fn get_fallbacks(&mut self, targets: Browsers) -> Vec { let fallbacks = self.get_necessary_fallbacks(targets);