From ffe41dcabb1a952362e1f071ab86ca0491e4dcbc Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Tue, 6 Jun 2023 21:40:51 +0000 Subject: [PATCH] Bug 1820070 - Part 2: Add at into ray() in style system. r=emilio 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 --- layout/base/MotionPathUtils.cpp | 6 +- layout/base/MotionPathUtils.h | 13 ++-- layout/style/ServoBindings.h | 1 - layout/style/ServoBindings.toml | 2 +- layout/style/test/property_database.js | 4 +- .../style/values/computed/motion.rs | 11 +++- .../style/values/generics/motion.rs | 64 +++++++++++++------ .../style/values/generics/position.rs | 6 ++ .../style/values/specified/motion.rs | 38 +++++++---- servo/ports/geckolib/cbindgen.toml | 1 + .../motion/parsing/offset-path-computed.html | 5 ++ 11 files changed, 105 insertions(+), 46 deletions(-) diff --git a/layout/base/MotionPathUtils.cpp b/layout/base/MotionPathUtils.cpp index 85a4b030b218..f1fdb020868d 100644 --- a/layout/base/MotionPathUtils.cpp +++ b/layout/base/MotionPathUtils.cpp @@ -166,7 +166,7 @@ static CSSCoord ComputeRayPathLength(const StyleRaySize aRaySizeType, return 0.0; } -static CSSCoord ComputeRayUsedDistance(const RayFunction& aRay, +static CSSCoord ComputeRayUsedDistance(const StyleRayFunction& aRay, const LengthPercentage& aDistance, const CSSCoord& aPathLength, const CSSSize& aBorderBoxSize) { @@ -317,7 +317,7 @@ static OffsetPathData GenerateOffsetPathData(const nsIFrame* aFrame) { return OffsetPathData::Path(pathData, gfxPath.forget()); } case StyleOffsetPath::Tag::Ray: - return OffsetPathData::Ray(path.AsRay(), RayReferenceData(aFrame)); + return OffsetPathData::Ray(*path.AsRay(), RayReferenceData(aFrame)); case StyleOffsetPath::Tag::None: return OffsetPathData::None(); default: @@ -362,7 +362,7 @@ static OffsetPathData GenerateOffsetPathData( return OffsetPathData::Path(pathData, path.forget()); } case StyleOffsetPath::Tag::Ray: - return OffsetPathData::Ray(aPath.AsRay(), aRayReferenceData); + return OffsetPathData::Ray(*aPath.AsRay(), aRayReferenceData); case StyleOffsetPath::Tag::None: default: return OffsetPathData::None(); diff --git a/layout/base/MotionPathUtils.h b/layout/base/MotionPathUtils.h index 286a435c4d5c..5ca22c053c91 100644 --- a/layout/base/MotionPathUtils.h +++ b/layout/base/MotionPathUtils.h @@ -22,8 +22,6 @@ class TransformReferenceBox; namespace mozilla { -using RayFunction = StyleRayFunction; - namespace layers { class MotionPathData; class PathCommand; @@ -70,7 +68,7 @@ struct OffsetPathData { }; struct RayData { - const RayFunction* mRay; + const StyleRayFunction* mRay; RayReferenceData mData; }; @@ -87,11 +85,12 @@ struct OffsetPathData { return OffsetPathData(std::move(aGfxPath), !path.empty() && path.rbegin()->IsClosePath()); } - static OffsetPathData Ray(const RayFunction& aRay, + static OffsetPathData Ray(const StyleRayFunction& aRay, const RayReferenceData& aData) { return OffsetPathData(&aRay, aData); } - static OffsetPathData Ray(const RayFunction& aRay, RayReferenceData&& aData) { + static OffsetPathData Ray(const StyleRayFunction& aRay, + RayReferenceData&& aData) { return OffsetPathData(&aRay, std::move(aData)); } @@ -152,9 +151,9 @@ struct OffsetPathData { OffsetPathData() : mType(Type::None) {} OffsetPathData(already_AddRefed&& aPath, bool aIsClosed) : mType(Type::Path), mPath{std::move(aPath), aIsClosed} {} - OffsetPathData(const RayFunction* aRay, RayReferenceData&& aRef) + OffsetPathData(const StyleRayFunction* aRay, RayReferenceData&& aRef) : mType(Type::Ray), mRay{aRay, std::move(aRef)} {} - OffsetPathData(const RayFunction* aRay, const RayReferenceData& aRef) + OffsetPathData(const StyleRayFunction* aRay, const RayReferenceData& aRef) : mType(Type::Ray), mRay{aRay, aRef} {} OffsetPathData& operator=(const OffsetPathData&) = delete; OffsetPathData& operator=(OffsetPathData&&) = delete; diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h index e2fd709f6bf6..80caacc319f7 100644 --- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -102,7 +102,6 @@ GROUP_RULE_FUNCS_UNLOCKED(Container) bool Servo_##type_##_Deserialize(mozilla::ipc::ByteBuf* input, type_* v); \ bool Servo_##type_##_Serialize(const type_* v, mozilla::ipc::ByteBuf* output); -using RayFunction = StyleRayFunction; BASIC_SERDE_FUNCS(LengthPercentage) BASIC_SERDE_FUNCS(StyleRotate) BASIC_SERDE_FUNCS(StyleScale) diff --git a/layout/style/ServoBindings.toml b/layout/style/ServoBindings.toml index 5ec116f642fc..89b8dd84faeb 100644 --- a/layout/style/ServoBindings.toml +++ b/layout/style/ServoBindings.toml @@ -451,7 +451,7 @@ cbindgen-types = [ { gecko = "StyleOffsetPosition", servo = "crate::values::computed::motion::OffsetPosition" }, { gecko = "StyleOffsetRotate", servo = "crate::values::computed::motion::OffsetRotate" }, { gecko = "StylePathCommand", servo = "crate::values::specified::svg_path::PathCommand" }, - { gecko = "StyleRayFunction", servo = "crate::values::generics::motion::RayFunction" }, + { gecko = "StyleRayFunction", servo = "crate::values::computed::motion::RayFunction" }, { gecko = "StyleUnicodeRange", servo = "cssparser::UnicodeRange" }, { gecko = "StyleOverflowWrap", servo = "crate::values::computed::OverflowWrap" }, { gecko = "StyleWordBreak", servo = "crate::values::computed::WordBreak" }, diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index f50649c2419d..9322be9e4208 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -13454,7 +13454,9 @@ if (IsCSSPropertyPrefEnabled("layout.css.motion-path.enabled")) { "ray(200grad farthest-corner)", "ray(sides 180deg)", "ray(contain farthest-side 180deg)", - "ray(calc(180deg - 45deg) farthest-side)" + "ray(calc(180deg - 45deg) farthest-side)", + "ray(0deg at center center)", + "ray(at 10% 10% 1rad)" ); gCSSProperties["offset-path"]["invalid_values"].push( diff --git a/servo/components/style/values/computed/motion.rs b/servo/components/style/values/computed/motion.rs index 704d10b68ab8..8fd578bb72c9 100644 --- a/servo/components/style/values/computed/motion.rs +++ b/servo/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/servo/components/style/values/generics/motion.rs b/servo/components/style/values/generics/motion.rs index 0767fd76e76d..92c1d004bb8a 100644 --- a/servo/components/style/values/generics/motion.rs +++ b/servo/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/servo/components/style/values/generics/position.rs b/servo/components/style/values/generics/position.rs index 5832833fa348..a6282f463bb7 100644 --- a/servo/components/style/values/generics/position.rs +++ b/servo/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/servo/components/style/values/specified/motion.rs b/servo/components/style/values/specified/motion.rs index cf223800f429..c1ace2cea602 100644 --- a/servo/components/style/values/specified/motion.rs +++ b/servo/components/style/values/specified/motion.rs @@ -7,26 +7,29 @@ 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; -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 !static_prefs::pref!("layout.css.motion-path-ray.enabled") { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); } @@ -34,13 +37,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; } @@ -54,6 +58,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; } @@ -64,8 +79,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()), }) } } @@ -75,7 +91,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() { @@ -90,7 +106,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()) diff --git a/servo/ports/geckolib/cbindgen.toml b/servo/ports/geckolib/cbindgen.toml index 69bcfaf61292..aed71725d135 100644 --- a/servo/ports/geckolib/cbindgen.toml +++ b/servo/ports/geckolib/cbindgen.toml @@ -115,6 +115,7 @@ include = [ "OffsetPath", "OffsetPosition", "OffsetRotate", + "RayFunction", "UnicodeRange", "UserSelect", "Float", diff --git a/testing/web-platform/tests/css/motion/parsing/offset-path-computed.html b/testing/web-platform/tests/css/motion/parsing/offset-path-computed.html index c2f9d4bce90f..7b5a8c164386 100644 --- a/testing/web-platform/tests/css/motion/parsing/offset-path-computed.html +++ b/testing/web-platform/tests/css/motion/parsing/offset-path-computed.html @@ -21,6 +21,11 @@ test_computed_value("offset-path", "ray(200grad farthest-side)", "ray(180deg far test_computed_value("offset-path", "ray(270deg farthest-corner contain)"); test_computed_value("offset-path", "ray(-720deg sides)"); test_computed_value("offset-path", "ray(calc(180deg - 45deg) farthest-side)", "ray(135deg farthest-side)"); +test_computed_value("offset-path", "ray(0deg at 100px 100px)"); +test_computed_value("offset-path", "ray(0deg sides at center center)", "ray(0deg sides at 50% 50%)"); +test_computed_value("offset-path", "ray(0deg at center center sides)", "ray(0deg sides at 50% 50%)"); +test_computed_value("offset-path", "ray(0deg at center center contain)", "ray(0deg contain at 50% 50%)"); +test_computed_value("offset-path", "ray(at 10px 10px 0deg contain)", "ray(0deg contain at 10px 10px)"); // It's unclear about the normalization at computed time, so we accept both // cases for now.