From 5e77ba9bf49ee2d16b32c690336a3b0fd996e3c1 Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Mon, 16 Sep 2019 23:24:48 +0000 Subject: [PATCH] style: Support ray() in offset-path and make it animatable. 1. Add `generics::motion::OffsetPath`, and use specified `Angle` and computed `Angle` to define specified `OffsetPath` and computed `OffsetPath`. 2. Add `ray` function into `OffsetPath`. We also tweak the degree from 150deg to 135deg in wpt (e.g. offset-path-ray-001.html and others) to avoid floating point precision issues. For example: ``` // offset-path: ray(150deg ...); // offset-distance: 20px; matrix: { {0.500000 0.866025 0.000000 0.000000}, {-0.866025 0.500000 0.000000 0.000000}, {0.000000 0.000000 1.000000 0.000000}, {10.000000 17.320509 0.000000 1.000000} } // rotate(60deg) translate(20px) matrix: { {0.500000 0.866025 0.000000 0.000000}, {-0.866025 0.500000 0.000000 0.000000}, {0.000000 0.000000 1.000000 0.000000}, {10.000000 17.320507 0.000000 1.000000} } ``` Their translate parts, 17.320509 vs 17.320507, are almost the same (only tiny difference), which may cause the reftest failed. Differential Revision: https://phabricator.services.mozilla.com/D42721 --- components/style/gecko/conversions.rs | 27 ++--- components/style/values/computed/motion.rs | 7 +- components/style/values/generics/mod.rs | 1 + components/style/values/generics/motion.rs | 110 ++++++++++++++++++++ components/style/values/specified/motion.rs | 80 +++++++------- 5 files changed, 172 insertions(+), 53 deletions(-) create mode 100644 components/style/values/generics/motion.rs diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 5b777e89033..e2b959431a4 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -42,7 +42,7 @@ impl nsStyleImage { image_rect.left, ); } - }, + } GenericImage::Element(ref element) => unsafe { bindings::Gecko_SetImageElement(self, element.as_ptr()); }, @@ -76,16 +76,16 @@ impl nsStyleImage { left: rect.3, }))) } - }, + } nsStyleImageType::eStyleImageType_Gradient => { let gradient: &Gradient = &**self.__bindgen_anon_1.mGradient.as_ref(); Some(GenericImage::Gradient(Box::new(gradient.clone()))) - }, + } nsStyleImageType::eStyleImageType_Element => { use crate::gecko_string_cache::Atom; let atom = bindings::Gecko_GetImageElement(self); Some(GenericImage::Element(Atom::from_raw(atom))) - }, + } } } @@ -128,13 +128,13 @@ pub mod basic_shape { Some(self.mReferenceBox.into()) }; Some(ShapeSource::Shape(shape, reference_box)) - }, + } StyleShapeSourceType::Image => None, StyleShapeSourceType::Path => { let path = self.to_svg_path().expect("expect an SVGPathData"); let fill = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr }.mFillRule; Some(ShapeSource::Path(Path { fill, path })) - }, + } } } @@ -144,7 +144,7 @@ pub mod basic_shape { StyleShapeSourceType::Path => { let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr }; Some(SVGPathData(gecko_path.mPath.clone())) - }, + } _ => None, } } @@ -187,14 +187,15 @@ pub mod basic_shape { impl<'a> From<&'a StyleShapeSource> for OffsetPath { fn from(other: &'a StyleShapeSource) -> Self { + use crate::values::generics::motion::GenericOffsetPath; match other.mType { - StyleShapeSourceType::Path => { - OffsetPath::Path(other.to_svg_path().expect("Cannot convert to SVGPathData")) - }, + StyleShapeSourceType::Path => GenericOffsetPath::Path( + other.to_svg_path().expect("Cannot convert to SVGPathData"), + ), StyleShapeSourceType::None => OffsetPath::none(), - StyleShapeSourceType::Shape | - StyleShapeSourceType::Box | - StyleShapeSourceType::Image => unreachable!("Unsupported offset-path type"), + StyleShapeSourceType::Shape + | StyleShapeSourceType::Box + | StyleShapeSourceType::Image => unreachable!("Unsupported offset-path type"), } } } diff --git a/components/style/values/computed/motion.rs b/components/style/values/computed/motion.rs index e5f82f46558..88a5b7c7280 100644 --- a/components/style/values/computed/motion.rs +++ b/components/style/values/computed/motion.rs @@ -5,12 +5,11 @@ //! Computed types for CSS values that are related to motion path. use crate::values::computed::Angle; +use crate::values::generics::motion::GenericOffsetPath; 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; +/// The computed value of `offset-path`. +pub type OffsetPath = GenericOffsetPath; #[inline] fn is_auto_zero_angle(auto: &bool, angle: &Angle) -> bool { diff --git a/components/style/values/generics/mod.rs b/components/style/values/generics/mod.rs index 04de85cbcf2..4db80abc8a8 100644 --- a/components/style/values/generics/mod.rs +++ b/components/style/values/generics/mod.rs @@ -29,6 +29,7 @@ pub mod font; pub mod grid; pub mod image; pub mod length; +pub mod motion; pub mod position; pub mod rect; pub mod size; diff --git a/components/style/values/generics/motion.rs b/components/style/values/generics/motion.rs new file mode 100644 index 00000000000..e9ccc045186 --- /dev/null +++ b/components/style/values/generics/motion.rs @@ -0,0 +1,110 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! Generic types for CSS Motion Path. + +use crate::values::specified::SVGPathData; + +/// The in ray() function. +/// +/// https://drafts.fxtf.org/motion-1/#valdef-offsetpath-size +#[allow(missing_docs)] +#[derive( + Clone, + Copy, + Debug, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToAnimatedZero, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(u8)] +pub enum RaySize { + ClosestSide, + ClosestCorner, + FarthestSide, + FarthestCorner, + Sides, +} + +/// The `ray()` function, `ray( [ && && contain? ] )` +/// +/// https://drafts.fxtf.org/motion-1/#valdef-offsetpath-ray +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToAnimatedZero, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(C)] +pub struct RayFunction { + /// 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)] + 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 offset-path value. +/// +/// https://drafts.fxtf.org/motion-1/#offset-path-property +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToAnimatedZero, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(C, u8)] +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. + #[css(function)] + Ray(RayFunction), + /// None value. + #[animation(error)] + None, + // Bug 1186329: Implement , , and . +} + +pub use self::GenericOffsetPath as OffsetPath; + +impl OffsetPath { + /// Return None. + #[inline] + pub fn none() -> Self { + OffsetPath::None + } +} diff --git a/components/style/values/specified/motion.rs b/components/style/values/specified/motion.rs index 8d6f7809fdb..68800c405dd 100644 --- a/components/style/values/specified/motion.rs +++ b/components/style/values/specified/motion.rs @@ -7,46 +7,53 @@ 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, RayFunction, RaySize}; use crate::values::specified::{Angle, SVGPathData}; use crate::Zero; use cssparser::Parser; use style_traits::{ParseError, StyleParseErrorKind}; -/// The offset-path value. -/// -/// https://drafts.fxtf.org/motion-1/#offset-path-property -#[derive( - Animate, - Clone, - ComputeSquaredDistance, - Debug, - MallocSizeOf, - PartialEq, - SpecifiedValueInfo, - ToAnimatedZero, - ToComputedValue, - ToCss, - ToResolvedValue, - ToShmem, -)] -#[repr(C, u8)] -pub enum OffsetPath { - // 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), - /// None value. - #[animation(error)] - None, - // Bug 1186329: Implement ray(), , , and . -} +/// The specified value of `offset-path`. +pub type OffsetPath = GenericOffsetPath; -impl OffsetPath { - /// Return None. - #[inline] - pub fn none() -> Self { - OffsetPath::None +impl Parse for RayFunction { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + let mut angle = None; + let mut size = None; + let mut contain = false; + loop { + if angle.is_none() { + angle = input.try(|i| Angle::parse(context, i)).ok(); + } + + if size.is_none() { + size = input.try(RaySize::parse).ok(); + if size.is_some() { + continue; + } + } + + if !contain { + contain = input.try(|i| i.expect_ident_matching("contain")).is_ok(); + if contain { + continue; + } + } + break; + } + + if angle.is_none() || size.is_none() { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + Ok(RayFunction { + angle: angle.unwrap(), + size: size.unwrap(), + contain, + }) } } @@ -65,9 +72,10 @@ impl Parse for OffsetPath { let function = input.expect_function()?.clone(); input.parse_nested_block(move |i| { match_ignore_ascii_case! { &function, - // Bug 1186329: Implement the parser for ray(), , , + // Bug 1186329: Implement the parser for , , // and . - "path" => SVGPathData::parse(context, i).map(OffsetPath::Path), + "path" => SVGPathData::parse(context, i).map(GenericOffsetPath::Path), + "ray" => RayFunction::parse(context, i).map(GenericOffsetPath::Ray), _ => { Err(location.new_custom_error( StyleParseErrorKind::UnexpectedFunction(function.clone())