From 2fef5d1a17765b96951d2cdf1a4479ab0be74681 Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Tue, 6 Jun 2023 21:40:51 +0000 Subject: [PATCH] style: Add at into ray() in style system We reuse PositionOrAuto here, and let "auto" represent the situation when the author omits "at " because it has a special meaning. https://drafts.fxtf.org/motion-1/#valdef-ray-at-position Note: No need to update css/motion/parsing/offset-path-parsing-valid.html because Blink added some to the upstream repo already. Differential Revision: https://phabricator.services.mozilla.com/D179860 --- components/style/values/computed/motion.rs | 11 +++- components/style/values/generics/motion.rs | 64 ++++++++++++++------ components/style/values/generics/position.rs | 6 ++ components/style/values/specified/motion.rs | 38 ++++++++---- 4 files changed, 86 insertions(+), 33 deletions(-) diff --git a/components/style/values/computed/motion.rs b/components/style/values/computed/motion.rs index 704d10b68ab..8fd578bb72c 100644 --- a/components/style/values/computed/motion.rs +++ b/components/style/values/computed/motion.rs @@ -4,12 +4,17 @@ //! Computed types for CSS values that are related to motion path. -use crate::values::computed::{Angle, LengthPercentage}; -use crate::values::generics::motion::{GenericOffsetPath, GenericOffsetPosition}; +use crate::values::computed::{Angle, LengthPercentage, Position}; +use crate::values::generics::motion::{ + GenericOffsetPath, GenericOffsetPosition, GenericRayFunction, +}; use crate::Zero; +/// The computed value of ray() function. +pub type RayFunction = GenericRayFunction; + /// The computed value of `offset-path`. -pub type OffsetPath = GenericOffsetPath; +pub type OffsetPath = GenericOffsetPath; /// The computed value of `offset-position`. pub type OffsetPosition = GenericOffsetPosition; diff --git a/components/style/values/generics/motion.rs b/components/style/values/generics/motion.rs index 0767fd76e76..92c1d004bb8 100644 --- a/components/style/values/generics/motion.rs +++ b/components/style/values/generics/motion.rs @@ -5,15 +5,19 @@ //! Generic types for CSS Motion Path. use crate::values::animated::ToAnimatedZero; -use crate::values::generics::position::GenericPosition; +use crate::values::generics::position::{GenericPosition, GenericPositionOrAuto}; use crate::values::specified::SVGPathData; +use std::fmt::{self, Write}; +use style_traits::{CssWriter, ToCss}; /// The in ray() function. /// /// https://drafts.fxtf.org/motion-1/#valdef-offsetpath-size #[allow(missing_docs)] #[derive( + Animate, Clone, + ComputeSquaredDistance, Copy, Debug, Deserialize, @@ -36,15 +40,7 @@ pub enum RaySize { Sides, } -impl RaySize { - /// Returns true if it is the default value. - #[inline] - pub fn is_default(&self) -> bool { - *self == RaySize::ClosestSide - } -} - -/// The `ray()` function, `ray( [ && && contain? ] )` +/// The `ray()` function, `ray( [ && && contain? && [at ]? ] )` /// /// https://drafts.fxtf.org/motion-1/#valdef-offsetpath-ray #[derive( @@ -58,25 +54,54 @@ impl RaySize { Serialize, SpecifiedValueInfo, ToComputedValue, - ToCss, ToResolvedValue, ToShmem, )] #[repr(C)] -pub struct RayFunction { +pub struct GenericRayFunction { /// The bearing angle with `0deg` pointing up and positive angles /// representing clockwise rotation. pub angle: Angle, /// Decide the path length used when `offset-distance` is expressed /// as a percentage. - #[animation(constant)] - #[css(skip_if = "RaySize::is_default")] pub size: RaySize, /// Clamp `offset-distance` so that the box is entirely contained /// within the path. #[animation(constant)] - #[css(represents_keyword)] pub contain: bool, + /// The "at " part. If omitted, we use auto to represent it. + pub position: GenericPositionOrAuto, +} + +pub use self::GenericRayFunction as RayFunction; + +impl ToCss for RayFunction +where + Angle: ToCss, + Position: ToCss, +{ + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + self.angle.to_css(dest)?; + + if !matches!(self.size, RaySize::ClosestSide) { + dest.write_char(' ')?; + self.size.to_css(dest)?; + } + + if self.contain { + dest.write_str(" contain")?; + } + + if !matches!(self.position, GenericPositionOrAuto::Auto) { + dest.write_str(" at ")?; + self.position.to_css(dest)?; + } + + Ok(()) + } } /// The offset-path value. @@ -98,15 +123,16 @@ pub struct RayFunction { ToShmem, )] #[repr(C, u8)] -pub enum GenericOffsetPath { +pub enum GenericOffsetPath { // We could merge SVGPathData into ShapeSource, so we could reuse them. However, // we don't want to support other value for offset-path, so use SVGPathData only for now. /// Path value for path(). #[css(function)] Path(SVGPathData), /// ray() function, which defines a path in the polar coordinate system. + /// Use Box<> to make sure the size of offset-path is not too large. #[css(function)] - Ray(RayFunction), + Ray(Box), /// None value. #[animation(error)] None, @@ -115,7 +141,7 @@ pub enum GenericOffsetPath { pub use self::GenericOffsetPath as OffsetPath; -impl OffsetPath { +impl OffsetPath { /// Return None. #[inline] pub fn none() -> Self { @@ -123,7 +149,7 @@ impl OffsetPath { } } -impl ToAnimatedZero for OffsetPath { +impl ToAnimatedZero for OffsetPath { #[inline] fn to_animated_zero(&self) -> Result { Err(()) diff --git a/components/style/values/generics/position.rs b/components/style/values/generics/position.rs index 5832833fa34..a6282f463bb 100644 --- a/components/style/values/generics/position.rs +++ b/components/style/values/generics/position.rs @@ -101,6 +101,12 @@ impl PositionOrAuto { pub fn auto() -> Self { PositionOrAuto::Auto } + + /// Return true if it is 'auto'. + #[inline] + pub fn is_auto(&self) -> bool { + matches!(self, PositionOrAuto::Auto) + } } /// A generic value for the `z-index` property. diff --git a/components/style/values/specified/motion.rs b/components/style/values/specified/motion.rs index bf36e973232..f61cb4cb871 100644 --- a/components/style/values/specified/motion.rs +++ b/components/style/values/specified/motion.rs @@ -7,20 +7,21 @@ use crate::parser::{Parse, ParserContext}; use crate::values::computed::motion::OffsetRotate as ComputedOffsetRotate; use crate::values::computed::{Context, ToComputedValue}; -use crate::values::generics::motion::{ - GenericOffsetPath, GenericOffsetPosition, RayFunction, RaySize, -}; +use crate::values::generics::motion as generics; use crate::values::specified::position::{HorizontalPosition, VerticalPosition}; -use crate::values::specified::{Angle, SVGPathData}; +use crate::values::specified::{Angle, Position}; use crate::Zero; use cssparser::Parser; use style_traits::{ParseError, StyleParseErrorKind}; +/// The specified value of ray() function. +pub type RayFunction = generics::GenericRayFunction; + /// The specified value of `offset-path`. -pub type OffsetPath = GenericOffsetPath; +pub type OffsetPath = generics::GenericOffsetPath; /// The specified value of `offset-position`. -pub type OffsetPosition = GenericOffsetPosition; +pub type OffsetPosition = generics::GenericOffsetPosition; #[cfg(feature = "gecko")] fn is_ray_enabled() -> bool { @@ -31,11 +32,13 @@ fn is_ray_enabled() -> bool { false } -impl Parse for RayFunction { +impl Parse for RayFunction { fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { + use crate::values::specified::PositionOrAuto; + if !is_ray_enabled() { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); } @@ -43,13 +46,14 @@ impl Parse for RayFunction { let mut angle = None; let mut size = None; let mut contain = false; + let mut position = None; loop { if angle.is_none() { angle = input.try_parse(|i| Angle::parse(context, i)).ok(); } if size.is_none() { - size = input.try_parse(RaySize::parse).ok(); + size = input.try_parse(generics::RaySize::parse).ok(); if size.is_some() { continue; } @@ -63,6 +67,17 @@ impl Parse for RayFunction { continue; } } + + if position.is_none() { + if input.try_parse(|i| i.expect_ident_matching("at")).is_ok() { + let pos = Position::parse(context, input)?; + position = Some(PositionOrAuto::Position(pos)); + } + + if position.is_some() { + continue; + } + } break; } @@ -73,8 +88,9 @@ impl Parse for RayFunction { Ok(RayFunction { angle: angle.unwrap(), // If no is specified it defaults to closest-side. - size: size.unwrap_or(RaySize::ClosestSide), + size: size.unwrap_or(generics::RaySize::ClosestSide), contain, + position: position.unwrap_or(PositionOrAuto::auto()), }) } } @@ -84,7 +100,7 @@ impl Parse for OffsetPath { context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { - use crate::values::specified::svg_path::AllowEmpty; + use crate::values::specified::svg_path::{AllowEmpty, SVGPathData}; // Parse none. if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { @@ -99,7 +115,7 @@ impl Parse for OffsetPath { // Bug 1186329: Implement the parser for , , // and . "path" => SVGPathData::parse(i, AllowEmpty::No).map(OffsetPath::Path), - "ray" => RayFunction::parse(context, i).map(OffsetPath::Ray), + "ray" => RayFunction::parse(context, i).map(|v| OffsetPath::Ray(Box::new(v))), _ => { Err(location.new_custom_error( StyleParseErrorKind::UnexpectedFunction(function.clone())