diff options
Diffstat (limited to 'servo/components/style/values/specified')
-rw-r--r-- | servo/components/style/values/specified/animation.rs | 38 | ||||
-rw-r--r-- | servo/components/style/values/specified/calc.rs | 57 | ||||
-rw-r--r-- | servo/components/style/values/specified/color.rs | 335 | ||||
-rw-r--r-- | servo/components/style/values/specified/font.rs | 14 | ||||
-rw-r--r-- | servo/components/style/values/specified/mod.rs | 2 |
5 files changed, 328 insertions, 118 deletions
diff --git a/servo/components/style/values/specified/animation.rs b/servo/components/style/values/specified/animation.rs index e7bbf26fb3..5a1f5003f3 100644 --- a/servo/components/style/values/specified/animation.rs +++ b/servo/components/style/values/specified/animation.rs @@ -112,6 +112,44 @@ impl TransitionProperty { } } +/// A specified value for <transition-behavior-value>. +/// +/// https://drafts.csswg.org/css-transitions-2/#transition-behavior-property +#[derive( + Clone, + Copy, + Debug, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(u8)] +pub enum TransitionBehavior { + /// Transitions will not be started for discrete properties, only for interpolable properties. + Normal, + /// Transitions will be started for discrete properties as well as interpolable properties. + AllowDiscrete, +} + +impl TransitionBehavior { + /// Return normal, the initial value. + #[inline] + pub fn normal() -> Self { + Self::Normal + } + + /// Return true if it is normal. + #[inline] + pub fn is_normal(&self) -> bool { + matches!(*self, Self::Normal) + } +} + /// https://drafts.csswg.org/css-animations/#animation-iteration-count #[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem)] pub enum AnimationIterationCount { diff --git a/servo/components/style/values/specified/calc.rs b/servo/components/style/values/specified/calc.rs index 2660864319..17f043ac58 100644 --- a/servo/components/style/values/specified/calc.rs +++ b/servo/components/style/values/specified/calc.rs @@ -6,7 +6,6 @@ //! //! [calc]: https://drafts.csswg.org/css-values/#calc-notation -use crate::color::parsing::{AngleOrNumber, NumberOrPercentage}; use crate::parser::ParserContext; use crate::values::generics::calc::{ self as generic, CalcNodeLeaf, CalcUnits, MinMaxOp, ModRemOp, PositivePercentageBasis, @@ -185,7 +184,11 @@ impl generic::CalcNodeLeaf for Leaf { let self_negative = self.is_negative(); if self_negative != other.is_negative() { - return Some(if self_negative { cmp::Ordering::Less } else { cmp::Ordering::Greater }); + return Some(if self_negative { + cmp::Ordering::Less + } else { + cmp::Ordering::Greater + }); } match (self, other) { @@ -482,7 +485,7 @@ impl CalcNode { /// Parse a top-level `calc` expression, with all nested sub-expressions. /// /// This is in charge of parsing, for example, `2 + 3 * 100%`. - fn parse<'i, 't>( + pub fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, function: MathFunction, @@ -524,13 +527,18 @@ impl CalcNode { } let value = Self::parse_argument(context, input, allowed_units)?; - input.expect_comma()?; - let step = Self::parse_argument(context, input, allowed_units)?; + + // <step> defaults to the number 1 if not provided + // https://drafts.csswg.org/css-values-4/#funcdef-round + let step = input.try_parse(|input| { + input.expect_comma()?; + Self::parse_argument(context, input, allowed_units) + }); Ok(Self::Round { strategy: strategy.unwrap_or(RoundingStrategy::Nearest), value: Box::new(value), - step: Box::new(step), + step: Box::new(step.unwrap_or(Self::Leaf(Leaf::Number(1.0)))), }) }, MathFunction::Mod | MathFunction::Rem => { @@ -1046,41 +1054,4 @@ impl CalcNode { .to_resolution() .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) } - - /// Convenience parsing function for `<number>` or `<percentage>`. - pub fn parse_number_or_percentage<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - function: MathFunction, - ) -> Result<NumberOrPercentage, ParseError<'i>> { - let node = Self::parse(context, input, function, CalcUnits::PERCENTAGE)?; - - if let Ok(value) = node.to_number() { - return Ok(NumberOrPercentage::Number { value }); - } - - match node.to_percentage() { - Ok(unit_value) => Ok(NumberOrPercentage::Percentage { unit_value }), - Err(()) => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), - } - } - - /// Convenience parsing function for `<number>` or `<angle>`. - pub fn parse_angle_or_number<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - function: MathFunction, - ) -> Result<AngleOrNumber, ParseError<'i>> { - let node = Self::parse(context, input, function, CalcUnits::ANGLE)?; - - if let Ok(angle) = node.to_angle() { - let degrees = angle.degrees(); - return Ok(AngleOrNumber::Angle { degrees }); - } - - match node.to_number() { - Ok(value) => Ok(AngleOrNumber::Number { value }), - Err(()) => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), - } - } } diff --git a/servo/components/style/values/specified/color.rs b/servo/components/style/values/specified/color.rs index 3a19a2f4a3..3694b4e9bc 100644 --- a/servo/components/style/values/specified/color.rs +++ b/servo/components/style/values/specified/color.rs @@ -5,19 +5,21 @@ //! Specified color values. use super::AllowQuirks; -use crate::color::parsing::{ - self, AngleOrNumber, Color as CSSParserColor, FromParsedColor, NumberOrPercentage, -}; +use crate::color::component::ColorComponent; +use crate::color::convert::normalize_hue; +use crate::color::parsing::{self, FromParsedColor, NumberOrAngle, NumberOrPercentage}; use crate::color::{mix::ColorInterpolationMethod, AbsoluteColor, ColorSpace}; use crate::media_queries::Device; use crate::parser::{Parse, ParserContext}; use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue}; +use crate::values::generics::calc::CalcUnits; use crate::values::generics::color::{ ColorMixFlags, GenericCaretColor, GenericColorMix, GenericColorOrAuto, }; -use crate::values::specified::calc::CalcNode; +use crate::values::specified::calc::{CalcNode, Leaf}; use crate::values::specified::Percentage; use crate::values::{normalize, CustomIdent}; +use cssparser::color::OPAQUE; use cssparser::{color::PredefinedColorSpace, BasicParseErrorKind, ParseErrorKind, Parser, Token}; use itoa; use std::fmt::{self, Write}; @@ -428,94 +430,217 @@ impl SystemColor { } } +impl<T> From<ColorComponent<T>> for Option<T> { + fn from(value: ColorComponent<T>) -> Self { + match value { + ColorComponent::None => None, + ColorComponent::Value(value) => Some(value), + } + } +} + +impl ColorComponent<NumberOrPercentage> { + #[inline] + fn into_alpha(self) -> Option<f32> { + match self { + ColorComponent::None => None, + ColorComponent::Value(number_or_percentage) => { + Some(normalize(number_or_percentage.to_number(1.0)).clamp(0.0, OPAQUE)) + }, + } + } +} + impl FromParsedColor for Color { fn from_current_color() -> Self { Color::CurrentColor } - fn from_rgba(r: u8, g: u8, b: u8, a: f32) -> Self { - AbsoluteColor::srgb_legacy(r, g, b, a).into() + fn from_rgba( + red: ColorComponent<u8>, + green: ColorComponent<u8>, + blue: ColorComponent<u8>, + alpha: ColorComponent<NumberOrPercentage>, + ) -> Self { + macro_rules! c { + ($c:expr) => {{ + match $c { + ColorComponent::None => 0u8, + ColorComponent::Value(value) => value, + } + }}; + } + + // Legacy rgb() doesn't support "none" alpha values and falls back to 0. + let alpha = alpha.into_alpha().unwrap_or(0.0); + + AbsoluteColor::srgb_legacy(c!(red), c!(green), c!(blue), alpha).into() } fn from_hsl( - hue: Option<f32>, - saturation: Option<f32>, - lightness: Option<f32>, - alpha: Option<f32>, + hue: ColorComponent<NumberOrAngle>, + saturation: ColorComponent<NumberOrPercentage>, + lightness: ColorComponent<NumberOrPercentage>, + alpha: ColorComponent<NumberOrPercentage>, ) -> Self { - AbsoluteColor::new(ColorSpace::Hsl, hue, saturation, lightness, alpha).into() + // Percent reference range for S and L: 0% = 0.0, 100% = 100.0 + const LIGHTNESS_RANGE: f32 = 100.0; + const SATURATION_RANGE: f32 = 100.0; + + let hue = hue.map_value(|angle| normalize_hue(angle.degrees())); + let saturation = + saturation.map_value(|s| s.to_number(SATURATION_RANGE).clamp(0.0, SATURATION_RANGE)); + let lightness = + lightness.map_value(|l| l.to_number(LIGHTNESS_RANGE).clamp(0.0, LIGHTNESS_RANGE)); + + AbsoluteColor::new( + ColorSpace::Hsl, + hue, + saturation, + lightness, + alpha.into_alpha(), + ) + .into() } fn from_hwb( - hue: Option<f32>, - whiteness: Option<f32>, - blackness: Option<f32>, - alpha: Option<f32>, + hue: ColorComponent<NumberOrAngle>, + whiteness: ColorComponent<NumberOrPercentage>, + blackness: ColorComponent<NumberOrPercentage>, + alpha: ColorComponent<NumberOrPercentage>, ) -> Self { - AbsoluteColor::new(ColorSpace::Hwb, hue, whiteness, blackness, alpha).into() + // Percent reference range for W and B: 0% = 0.0, 100% = 100.0 + const WHITENESS_RANGE: f32 = 100.0; + const BLACKNESS_RANGE: f32 = 100.0; + + let hue = hue.map_value(|angle| normalize_hue(angle.degrees())); + let whiteness = + whiteness.map_value(|w| w.to_number(WHITENESS_RANGE).clamp(0.0, WHITENESS_RANGE)); + let blackness = + blackness.map_value(|b| b.to_number(BLACKNESS_RANGE).clamp(0.0, BLACKNESS_RANGE)); + + AbsoluteColor::new( + ColorSpace::Hwb, + hue, + whiteness, + blackness, + alpha.into_alpha(), + ) + .into() } fn from_lab( - lightness: Option<f32>, - a: Option<f32>, - b: Option<f32>, - alpha: Option<f32>, + lightness: ColorComponent<NumberOrPercentage>, + a: ColorComponent<NumberOrPercentage>, + b: ColorComponent<NumberOrPercentage>, + alpha: ColorComponent<NumberOrPercentage>, ) -> Self { - AbsoluteColor::new(ColorSpace::Lab, lightness, a, b, alpha).into() + // for L: 0% = 0.0, 100% = 100.0 + // for a and b: -100% = -125, 100% = 125 + const LIGHTNESS_RANGE: f32 = 100.0; + const A_B_RANGE: f32 = 125.0; + + let lightness = lightness.map_value(|l| l.to_number(LIGHTNESS_RANGE)); + let a = a.map_value(|a| a.to_number(A_B_RANGE)); + let b = b.map_value(|b| b.to_number(A_B_RANGE)); + + AbsoluteColor::new(ColorSpace::Lab, lightness, a, b, alpha.into_alpha()).into() } fn from_lch( - lightness: Option<f32>, - chroma: Option<f32>, - hue: Option<f32>, - alpha: Option<f32>, + lightness: ColorComponent<NumberOrPercentage>, + chroma: ColorComponent<NumberOrPercentage>, + hue: ColorComponent<NumberOrAngle>, + alpha: ColorComponent<NumberOrPercentage>, ) -> Self { - AbsoluteColor::new(ColorSpace::Lch, lightness, chroma, hue, alpha).into() + // for L: 0% = 0.0, 100% = 100.0 + // for C: 0% = 0, 100% = 150 + const LIGHTNESS_RANGE: f32 = 100.0; + const CHROMA_RANGE: f32 = 150.0; + + let lightness = lightness.map_value(|l| l.to_number(LIGHTNESS_RANGE)); + let chroma = chroma.map_value(|c| c.to_number(CHROMA_RANGE)); + let hue = hue.map_value(|angle| normalize_hue(angle.degrees())); + + AbsoluteColor::new(ColorSpace::Lch, lightness, chroma, hue, alpha.into_alpha()).into() } fn from_oklab( - lightness: Option<f32>, - a: Option<f32>, - b: Option<f32>, - alpha: Option<f32>, + lightness: ColorComponent<NumberOrPercentage>, + a: ColorComponent<NumberOrPercentage>, + b: ColorComponent<NumberOrPercentage>, + alpha: ColorComponent<NumberOrPercentage>, ) -> Self { - AbsoluteColor::new(ColorSpace::Oklab, lightness, a, b, alpha).into() + // for L: 0% = 0.0, 100% = 1.0 + // for a and b: -100% = -0.4, 100% = 0.4 + const LIGHTNESS_RANGE: f32 = 1.0; + const A_B_RANGE: f32 = 0.4; + + let lightness = lightness.map_value(|l| l.to_number(LIGHTNESS_RANGE)); + let a = a.map_value(|a| a.to_number(A_B_RANGE)); + let b = b.map_value(|b| b.to_number(A_B_RANGE)); + + AbsoluteColor::new(ColorSpace::Oklab, lightness, a, b, alpha.into_alpha()).into() } fn from_oklch( - lightness: Option<f32>, - chroma: Option<f32>, - hue: Option<f32>, - alpha: Option<f32>, + lightness: ColorComponent<NumberOrPercentage>, + chroma: ColorComponent<NumberOrPercentage>, + hue: ColorComponent<NumberOrAngle>, + alpha: ColorComponent<NumberOrPercentage>, ) -> Self { - AbsoluteColor::new(ColorSpace::Oklch, lightness, chroma, hue, alpha).into() + // for L: 0% = 0.0, 100% = 1.0 + // for C: 0% = 0.0 100% = 0.4 + const LIGHTNESS_RANGE: f32 = 1.0; + const CHROMA_RANGE: f32 = 0.4; + + let lightness = lightness.map_value(|l| l.to_number(LIGHTNESS_RANGE)); + let chroma = chroma.map_value(|c| c.to_number(CHROMA_RANGE)); + let hue = hue.map_value(|angle| normalize_hue(angle.degrees())); + + AbsoluteColor::new( + ColorSpace::Oklch, + lightness, + chroma, + hue, + alpha.into_alpha(), + ) + .into() } fn from_color_function( color_space: PredefinedColorSpace, - c1: Option<f32>, - c2: Option<f32>, - c3: Option<f32>, - alpha: Option<f32>, + c1: ColorComponent<NumberOrPercentage>, + c2: ColorComponent<NumberOrPercentage>, + c3: ColorComponent<NumberOrPercentage>, + alpha: ColorComponent<NumberOrPercentage>, ) -> Self { - AbsoluteColor::new(color_space.into(), c1, c2, c3, alpha).into() + let c1 = c1.map_value(|c| c.to_number(1.0)); + let c2 = c2.map_value(|c| c.to_number(1.0)); + let c3 = c3.map_value(|c| c.to_number(1.0)); + + AbsoluteColor::new(color_space.into(), c1, c2, c3, alpha.into_alpha()).into() } } struct ColorParser<'a, 'b: 'a>(&'a ParserContext<'b>); + impl<'a, 'b: 'a, 'i: 'a> parsing::ColorParser<'i> for ColorParser<'a, 'b> { type Output = Color; - type Error = StyleParseErrorKind<'i>; - fn parse_angle_or_number<'t>( + fn parse_number_or_angle<'t>( &self, input: &mut Parser<'i, 't>, - ) -> Result<AngleOrNumber, ParseError<'i>> { + allow_none: bool, + ) -> Result<ColorComponent<NumberOrAngle>, ParseError<'i>> { use crate::values::specified::Angle; let location = input.current_source_location(); let token = input.next()?.clone(); - match token { + Ok(match token { + Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case("none") => { + ColorComponent::None + }, Token::Dimension { value, ref unit, .. } => { @@ -526,44 +651,125 @@ impl<'a, 'b: 'a, 'i: 'a> parsing::ColorParser<'i> for ColorParser<'a, 'b> { Err(()) => return Err(location.new_unexpected_token_error(token.clone())), }; - Ok(AngleOrNumber::Angle { degrees }) + ColorComponent::Value(NumberOrAngle::Angle { degrees }) }, - Token::Number { value, .. } => Ok(AngleOrNumber::Number { value }), + Token::Number { value, .. } => ColorComponent::Value(NumberOrAngle::Number { value }), Token::Function(ref name) => { let function = CalcNode::math_function(self.0, name, location)?; - CalcNode::parse_angle_or_number(self.0, input, function) + let node = CalcNode::parse(self.0, input, function, CalcUnits::ANGLE)?; + + // If we can resolve the calc node, then use the value. + match node.resolve() { + Ok(Leaf::Number(value)) => { + ColorComponent::Value(NumberOrAngle::Number { value }) + }, + Ok(Leaf::Angle(angle)) => ColorComponent::Value(NumberOrAngle::Angle { + degrees: angle.degrees(), + }), + _ => { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + }, + } }, t => return Err(location.new_unexpected_token_error(t)), - } + }) } - fn parse_percentage<'t>(&self, input: &mut Parser<'i, 't>) -> Result<f32, ParseError<'i>> { - Ok(Percentage::parse(self.0, input)?.get()) + fn parse_percentage<'t>( + &self, + input: &mut Parser<'i, 't>, + allow_none: bool, + ) -> Result<ColorComponent<f32>, ParseError<'i>> { + let location = input.current_source_location(); + + Ok(match *input.next()? { + Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case("none") => { + ColorComponent::None + }, + Token::Percentage { unit_value, .. } => ColorComponent::Value(unit_value), + Token::Function(ref name) => { + let function = CalcNode::math_function(self.0, name, location)?; + let node = CalcNode::parse(self.0, input, function, CalcUnits::PERCENTAGE)?; + + // If we can resolve the calc node, then use the value. + let Ok(resolved_leaf) = node.resolve() else { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + }; + if let Leaf::Percentage(value) = resolved_leaf { + ColorComponent::Value(value) + } else { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + }, + ref t => return Err(location.new_unexpected_token_error(t.clone())), + }) } - fn parse_number<'t>(&self, input: &mut Parser<'i, 't>) -> Result<f32, ParseError<'i>> { - use crate::values::specified::Number; + fn parse_number<'t>( + &self, + input: &mut Parser<'i, 't>, + allow_none: bool, + ) -> Result<ColorComponent<f32>, ParseError<'i>> { + let location = input.current_source_location(); + + Ok(match *input.next()? { + Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case("none") => { + ColorComponent::None + }, + Token::Number { value, .. } => ColorComponent::Value(value), + Token::Function(ref name) => { + let function = CalcNode::math_function(self.0, name, location)?; + let node = CalcNode::parse(self.0, input, function, CalcUnits::empty())?; - Ok(Number::parse(self.0, input)?.get()) + // If we can resolve the calc node, then use the value. + let Ok(resolved_leaf) = node.resolve() else { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + }; + if let Leaf::Number(value) = resolved_leaf { + ColorComponent::Value(value) + } else { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + }, + ref t => return Err(location.new_unexpected_token_error(t.clone())), + }) } fn parse_number_or_percentage<'t>( &self, input: &mut Parser<'i, 't>, - ) -> Result<NumberOrPercentage, ParseError<'i>> { + allow_none: bool, + ) -> Result<ColorComponent<NumberOrPercentage>, ParseError<'i>> { let location = input.current_source_location(); - match *input.next()? { - Token::Number { value, .. } => Ok(NumberOrPercentage::Number { value }), + Ok(match *input.next()? { + Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case("none") => { + ColorComponent::None + }, + Token::Number { value, .. } => { + ColorComponent::Value(NumberOrPercentage::Number { value }) + }, Token::Percentage { unit_value, .. } => { - Ok(NumberOrPercentage::Percentage { unit_value }) + ColorComponent::Value(NumberOrPercentage::Percentage { unit_value }) }, Token::Function(ref name) => { let function = CalcNode::math_function(self.0, name, location)?; - CalcNode::parse_number_or_percentage(self.0, input, function) + let node = CalcNode::parse(self.0, input, function, CalcUnits::PERCENTAGE)?; + + // If we can resolve the calc node, then use the value. + let Ok(resolved_leaf) = node.resolve() else { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + }; + if let Leaf::Percentage(unit_value) = resolved_leaf { + ColorComponent::Value(NumberOrPercentage::Percentage { unit_value }) + } else if let Leaf::Number(value) = resolved_leaf { + ColorComponent::Value(NumberOrPercentage::Number { value }) + } else { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } }, ref t => return Err(location.new_unexpected_token_error(t.clone())), - } + }) } } @@ -700,7 +906,7 @@ impl ToCss for Color { W: Write, { match *self { - Color::CurrentColor => cssparser::ToCss::to_css(&CSSParserColor::CurrentColor, dest), + Color::CurrentColor => dest.write_str("currentcolor"), Color::Absolute(ref absolute) => absolute.to_css(dest), Color::ColorMix(ref mix) => mix.to_css(dest), Color::LightDark(ref ld) => ld.to_css(dest), @@ -772,7 +978,12 @@ impl Color { loc: &cssparser::SourceLocation, ) -> Result<Self, ParseError<'i>> { match cssparser::color::parse_hash_color(bytes) { - Ok((r, g, b, a)) => Ok(Self::from_rgba(r, g, b, a)), + Ok((r, g, b, a)) => Ok(Self::from_rgba( + r.into(), + g.into(), + b.into(), + ColorComponent::Value(NumberOrPercentage::Number { value: a }), + )), Err(()) => Err(loc.new_custom_error(StyleParseErrorKind::UnspecifiedError)), } } diff --git a/servo/components/style/values/specified/font.rs b/servo/components/style/values/specified/font.rs index 2435682ce3..db3d871a0f 100644 --- a/servo/components/style/values/specified/font.rs +++ b/servo/components/style/values/specified/font.rs @@ -450,16 +450,6 @@ impl ToComputedValue for FontStretch { } } -#[cfg(feature = "gecko")] -fn math_depth_enabled(_context: &ParserContext) -> bool { - static_prefs::pref!("layout.css.math-depth.enabled") -} - -#[cfg(feature = "servo")] -fn math_depth_enabled(_context: &ParserContext) -> bool { - false -} - /// CSS font keywords #[derive( Animate, @@ -496,7 +486,7 @@ pub enum FontSizeKeyword { XXXLarge, /// Indicate whether to apply font-size: math is specified so that extra /// scaling due to math-depth changes is applied during the cascade. - #[parse(condition = "math_depth_enabled")] + #[cfg(feature="gecko")] Math, #[css(skip)] None, @@ -1018,7 +1008,7 @@ impl FontSize { return Ok(FontSize::Length(lp)); } - if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(context, i)) { + if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) { return Ok(FontSize::Keyword(KeywordInfo::new(kw))); } diff --git a/servo/components/style/values/specified/mod.rs b/servo/components/style/values/specified/mod.rs index 7fc76b3c07..1a12ca56e7 100644 --- a/servo/components/style/values/specified/mod.rs +++ b/servo/components/style/values/specified/mod.rs @@ -33,7 +33,7 @@ pub use self::angle::{AllowUnitlessZeroAngle, Angle}; pub use self::animation::{ AnimationIterationCount, AnimationName, AnimationTimeline, AnimationPlayState, AnimationFillMode, AnimationComposition, AnimationDirection, ScrollAxis, - ScrollTimelineName, TransitionProperty, ViewTimelineInset + ScrollTimelineName, TransitionBehavior, TransitionProperty, ViewTimelineInset }; pub use self::background::{BackgroundRepeat, BackgroundSize}; pub use self::basic_shape::FillRule; |