diff --git a/components/style/properties/data.py b/components/style/properties/data.py index a7a3479974b..e771607b6d9 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -332,6 +332,7 @@ class Longhand(object): "MozScriptMinSize", "MozScriptSizeMultiplier", "NonNegativeNumber", + "OffsetRotate", "Opacity", "OutlineStyle", "Overflow", diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index d6c3366c4b7..da9e5b2e767 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -384,6 +384,18 @@ ${helpers.predefined_type( servo_restyle_damage="reflow_out_of_flow" )} +// Motion Path Module Level 1 +${helpers.predefined_type( + "offset-rotate", + "OffsetRotate", + "computed::OffsetRotate::auto()", + products="gecko", + animation_value_type="none", + gecko_pref="layout.css.motion-path.enabled", + spec="https://drafts.fxtf.org/motion-1/#offset-rotate-property", + servo_restyle_damage="reflow_out_of_flow" +)} + // CSSOM View Module // https://www.w3.org/TR/cssom-view-1/ ${helpers.single_keyword( diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index b8e3c0eca17..add2ad1a40a 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -65,7 +65,7 @@ pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageO pub use self::list::ListStyleType; pub use self::list::MozListReversed; pub use self::list::{QuotePair, Quotes}; -pub use self::motion::OffsetPath; +pub use self::motion::{OffsetPath, OffsetRotate}; pub use self::outline::OutlineStyle; pub use self::percentage::{NonNegativePercentage, Percentage}; pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, ZIndex}; diff --git a/components/style/values/computed/motion.rs b/components/style/values/computed/motion.rs index a5f1fa885c3..932d8074262 100644 --- a/components/style/values/computed/motion.rs +++ b/components/style/values/computed/motion.rs @@ -4,7 +4,41 @@ //! Computed types for CSS values that are related to motion path. +use crate::values::computed::Angle; +use crate::Zero; + /// A computed offset-path. The computed value is as specified value. /// /// https://drafts.fxtf.org/motion-1/#offset-path-property pub use crate::values::specified::motion::OffsetPath; + +#[inline] +fn is_auto_zero_angle(auto: &bool, angle: &Angle) -> bool { + *auto && angle.is_zero() +} + +/// A computed offset-rotate. +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)] +#[repr(C)] +pub struct OffsetRotate { + /// If auto is false, this is a fixed angle which indicates a + /// constant clockwise rotation transformation applied to it by this + /// specified rotation angle. Otherwise, the angle will be added to + /// the angle of the direction in layout. + #[css(represents_keyword)] + pub auto: bool, + /// The angle value. + #[css(contextual_skip_if = "is_auto_zero_angle")] + pub angle: Angle, +} + +impl OffsetRotate { + /// Returns "auto 0deg". + #[inline] + pub fn auto() -> Self { + OffsetRotate { + auto: true, + angle: Zero::zero(), + } + } +} diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 4cb7305fab8..409f5abe161 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -66,7 +66,7 @@ pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageO pub use self::list::ListStyleType; pub use self::list::MozListReversed; pub use self::list::{QuotePair, Quotes}; -pub use self::motion::OffsetPath; +pub use self::motion::{OffsetPath, OffsetRotate}; pub use self::outline::OutlineStyle; pub use self::percentage::Percentage; pub use self::position::{GridAutoFlow, GridTemplateAreas, Position}; diff --git a/components/style/values/specified/motion.rs b/components/style/values/specified/motion.rs index 73072ac6199..97609ea20b0 100644 --- a/components/style/values/specified/motion.rs +++ b/components/style/values/specified/motion.rs @@ -5,7 +5,10 @@ //! Specified types for CSS values that are related to motion path. use crate::parser::{Parse, ParserContext}; -use crate::values::specified::SVGPathData; +use crate::values::computed::motion::OffsetRotate as ComputedOffsetRotate; +use crate::values::computed::{Context, ToComputedValue}; +use crate::values::specified::{Angle, SVGPathData}; +use crate::Zero; use cssparser::Parser; use style_traits::{ParseError, StyleParseErrorKind}; @@ -75,3 +78,104 @@ impl Parse for OffsetPath { }) } } + +/// The direction of offset-rotate. +#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] +#[repr(u8)] +pub enum OffsetRotateDirection { + /// Unspecified direction keyword. + #[css(skip)] + None, + /// 0deg offset (face forward). + Auto, + /// 180deg offset (face backward). + Reverse, +} + +impl OffsetRotateDirection { + /// Returns true if it is none (i.e. the keyword is not specified). + #[inline] + fn is_none(&self) -> bool { + *self == OffsetRotateDirection::None + } +} + +#[inline] +fn direction_specified_and_angle_is_zero(direction: &OffsetRotateDirection, angle: &Angle) -> bool { + !direction.is_none() && angle.is_zero() +} + +/// The specified offset-rotate. +/// The syntax is: "[ auto | reverse ] || " +/// +/// https://drafts.fxtf.org/motion-1/#offset-rotate-property +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] +pub struct OffsetRotate { + /// [auto | reverse]. + #[css(skip_if = "OffsetRotateDirection::is_none")] + direction: OffsetRotateDirection, + /// . + /// If direction is None, this is a fixed angle which indicates a + /// constant clockwise rotation transformation applied to it by this + /// specified rotation angle. Otherwise, the angle will be added to + /// the angle of the direction in layout. + #[css(contextual_skip_if = "direction_specified_and_angle_is_zero")] + angle: Angle, +} + +impl Parse for OffsetRotate { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + let location = input.current_source_location(); + let mut direction = input.try(OffsetRotateDirection::parse); + let angle = input.try(|i| Angle::parse(context, i)); + if direction.is_err() { + // The direction and angle could be any order, so give it a change to parse + // direction again. + direction = input.try(OffsetRotateDirection::parse); + } + + if direction.is_err() && angle.is_err() { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + Ok(OffsetRotate { + direction: direction.unwrap_or(OffsetRotateDirection::None), + angle: angle.unwrap_or(Zero::zero()), + }) + } +} + +impl ToComputedValue for OffsetRotate { + type ComputedValue = ComputedOffsetRotate; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + use crate::values::computed::Angle as ComputedAngle; + + ComputedOffsetRotate { + auto: !self.direction.is_none(), + angle: if self.direction == OffsetRotateDirection::Reverse { + // The computed value should always convert "reverse" into "auto". + // e.g. "reverse calc(20deg + 10deg)" => "auto 210deg" + self.angle.to_computed_value(context) + ComputedAngle::from_degrees(180.0) + } else { + self.angle.to_computed_value(context) + }, + } + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + OffsetRotate { + direction: if computed.auto { + OffsetRotateDirection::Auto + } else { + OffsetRotateDirection::None + }, + angle: ToComputedValue::from_computed_value(&computed.angle), + } + } +}