diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2017-08-28 10:13:13 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-28 10:13:13 -0500 |
commit | a266e96d28b7960b4fb0b236c3e8e93cf70f09b0 (patch) | |
tree | 139e748d7cb22f0796640677119ee0ca65e3d067 | |
parent | 96b4e064a1f0b0fc1ee2811d0c1b528f85a718c6 (diff) | |
parent | ba4136b5a8da58a6130aa3fab0654764bcdda25f (diff) | |
download | servo-a266e96d28b7960b4fb0b236c3e8e93cf70f09b0.tar.gz servo-a266e96d28b7960b4fb0b236c3e8e93cf70f09b0.zip |
Auto merge of #18239 - servo:derive-all-the-things, r=emilio
Refactor how we handle trait bounds in style_derive
<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/18239)
<!-- Reviewable:end -->
27 files changed, 649 insertions, 844 deletions
diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index e3c831c8bc9..4c2057f61d9 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -48,9 +48,10 @@ use values::animated::effects::TextShadowList as AnimatedTextShadowList; use values::computed::{Angle, BorderCornerRadius, CalcLengthOrPercentage}; use values::computed::{ClipRect, Context, ComputedUrl, ComputedValueAsSpecified}; use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; -use values::computed::{LengthOrPercentageOrNone, MaxLength, MozLength, NonNegativeAu}; +use values::computed::{LengthOrPercentageOrNone, MaxLength, NonNegativeAu}; use values::computed::{NonNegativeNumber, Number, NumberOrPercentage, Percentage}; use values::computed::{PositiveIntegerOrAuto, ToComputedValue}; +#[cfg(feature = "gecko")] use values::computed::MozLength; use values::computed::length::{NonNegativeLengthOrAuto, NonNegativeLengthOrNormal}; use values::computed::length::NonNegativeLengthOrPercentage; use values::distance::{ComputeSquaredDistance, SquaredDistance}; @@ -811,73 +812,6 @@ impl Animate for CalcLengthOrPercentage { } } -/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc -impl Animate for LengthOrPercentage { - #[inline] - fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { - match (self, other) { - ( - &LengthOrPercentage::Length(ref this), - &LengthOrPercentage::Length(ref other), - ) => { - Ok(LengthOrPercentage::Length(this.animate(other, procedure)?)) - }, - ( - &LengthOrPercentage::Percentage(ref this), - &LengthOrPercentage::Percentage(ref other), - ) => { - Ok(LengthOrPercentage::Percentage(this.animate(other, procedure)?)) - }, - (this, other) => { - // Special handling for zero values since these should not require calc(). - if this.is_definitely_zero() { - return other.to_animated_zero()?.animate(other, procedure); - } - if other.is_definitely_zero() { - return this.animate(&this.to_animated_zero()?, procedure); - } - - let this = CalcLengthOrPercentage::from(*this); - let other = CalcLengthOrPercentage::from(*other); - Ok(LengthOrPercentage::Calc(this.animate(&other, procedure)?)) - } - } - } -} - -/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc -impl Animate for LengthOrPercentageOrAuto { - #[inline] - fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { - match (self, other) { - ( - &LengthOrPercentageOrAuto::Length(ref this), - &LengthOrPercentageOrAuto::Length(ref other), - ) => { - Ok(LengthOrPercentageOrAuto::Length(this.animate(other, procedure)?)) - }, - ( - &LengthOrPercentageOrAuto::Percentage(ref this), - &LengthOrPercentageOrAuto::Percentage(ref other), - ) => { - Ok(LengthOrPercentageOrAuto::Percentage( - this.animate(other, procedure)?, - )) - }, - (&LengthOrPercentageOrAuto::Auto, &LengthOrPercentageOrAuto::Auto) => { - Ok(LengthOrPercentageOrAuto::Auto) - }, - (this, other) => { - let this: Option<CalcLengthOrPercentage> = From::from(*this); - let other: Option<CalcLengthOrPercentage> = From::from(*other); - Ok(LengthOrPercentageOrAuto::Calc( - this.animate(&other, procedure)?.ok_or(())?, - )) - }, - } - } -} - impl ToAnimatedZero for LengthOrPercentageOrAuto { #[inline] fn to_animated_zero(&self) -> Result<Self, ()> { @@ -892,39 +826,6 @@ impl ToAnimatedZero for LengthOrPercentageOrAuto { } } -/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc -impl Animate for LengthOrPercentageOrNone { - #[inline] - fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { - match (self, other) { - ( - &LengthOrPercentageOrNone::Length(ref this), - &LengthOrPercentageOrNone::Length(ref other), - ) => { - Ok(LengthOrPercentageOrNone::Length(this.animate(other, procedure)?)) - }, - ( - &LengthOrPercentageOrNone::Percentage(ref this), - &LengthOrPercentageOrNone::Percentage(ref other), - ) => { - Ok(LengthOrPercentageOrNone::Percentage( - this.animate(other, procedure)?, - )) - } - (&LengthOrPercentageOrNone::None, &LengthOrPercentageOrNone::None) => { - Ok(LengthOrPercentageOrNone::None) - }, - (this, other) => { - let this = <Option<CalcLengthOrPercentage>>::from(*this); - let other = <Option<CalcLengthOrPercentage>>::from(*other); - Ok(LengthOrPercentageOrNone::Calc( - this.animate(&other, procedure)?.ok_or(())?, - )) - }, - } - } -} - impl ToAnimatedZero for LengthOrPercentageOrNone { #[inline] fn to_animated_zero(&self) -> Result<Self, ()> { @@ -939,54 +840,6 @@ impl ToAnimatedZero for LengthOrPercentageOrNone { } } -/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc -impl Animate for MozLength { - #[inline] - fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { - match (self, other) { - ( - &MozLength::LengthOrPercentageOrAuto(ref this), - &MozLength::LengthOrPercentageOrAuto(ref other), - ) => { - Ok(MozLength::LengthOrPercentageOrAuto( - this.animate(other, procedure)?, - )) - } - _ => Err(()), - } - } -} - -impl ToAnimatedZero for MozLength { - #[inline] - fn to_animated_zero(&self) -> Result<Self, ()> { - match *self { - MozLength::LengthOrPercentageOrAuto(ref length) => { - Ok(MozLength::LengthOrPercentageOrAuto(length.to_animated_zero()?)) - }, - _ => Err(()) - } - } -} - -/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc -impl Animate for MaxLength { - #[inline] - fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { - match (self, other) { - ( - &MaxLength::LengthOrPercentageOrNone(ref this), - &MaxLength::LengthOrPercentageOrNone(ref other), - ) => { - Ok(MaxLength::LengthOrPercentageOrNone( - this.animate(other, procedure)?, - )) - }, - _ => Err(()), - } - } -} - impl ToAnimatedZero for MaxLength { #[inline] fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) } @@ -2431,27 +2284,6 @@ pub type IntermediateSVGPaint = SVGPaint<AnimatedRGBA, ComputedUrl>; /// Animated SVGPaintKind pub type IntermediateSVGPaintKind = SVGPaintKind<AnimatedRGBA, ComputedUrl>; -impl Animate for IntermediateSVGPaint { - #[inline] - fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { - Ok(IntermediateSVGPaint { - kind: self.kind.animate(&other.kind, procedure)?, - fallback: self.fallback.animate(&other.fallback, procedure)?, - }) - } -} - -impl ComputeSquaredDistance for IntermediateSVGPaint { - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { - // FIXME(nox): This should be derived. - Ok( - self.kind.compute_squared_distance(&other.kind)? + - self.fallback.compute_squared_distance(&other.fallback)?, - ) - } -} - impl ToAnimatedZero for IntermediateSVGPaint { #[inline] fn to_animated_zero(&self) -> Result<Self, ()> { @@ -2462,56 +2294,6 @@ impl ToAnimatedZero for IntermediateSVGPaint { } } -impl Animate for IntermediateSVGPaintKind { - #[inline] - fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { - match (self, other) { - (&SVGPaintKind::Color(ref this), &SVGPaintKind::Color(ref other)) => { - Ok(SVGPaintKind::Color(this.animate(other, procedure)?)) - }, - (&SVGPaintKind::ContextFill, &SVGPaintKind::ContextFill) => Ok(SVGPaintKind::ContextFill), - (&SVGPaintKind::ContextStroke, &SVGPaintKind::ContextStroke) => Ok(SVGPaintKind::ContextStroke), - _ => { - // FIXME: Context values should be interpolable with colors, - // Gecko doesn't implement this behavior either. - Err(()) - } - } - } -} - -impl ComputeSquaredDistance for IntermediateSVGPaintKind { - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { - match (self, other) { - (&SVGPaintKind::Color(ref this), &SVGPaintKind::Color(ref other)) => { - this.compute_squared_distance(other) - } - (&SVGPaintKind::None, &SVGPaintKind::None) | - (&SVGPaintKind::ContextFill, &SVGPaintKind::ContextFill) | - (&SVGPaintKind::ContextStroke, &SVGPaintKind::ContextStroke) => { - Ok(SquaredDistance::Value(0.)) - }, - _ => Err(()) - } - } -} - -impl ToAnimatedZero for IntermediateSVGPaintKind { - #[inline] - fn to_animated_zero(&self) -> Result<Self, ()> { - match *self { - SVGPaintKind::Color(ref color) => { - Ok(SVGPaintKind::Color(color.to_animated_zero()?)) - }, - SVGPaintKind::None | - SVGPaintKind::ContextFill | - SVGPaintKind::ContextStroke => Ok(self.clone()), - _ => Err(()), - } - } -} - impl From<NonNegativeLengthOrPercentage> for NumberOrPercentage { fn from(lop: NonNegativeLengthOrPercentage) -> NumberOrPercentage { lop.0.into() diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index e4d257d02a6..7dc51dca3d4 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -1116,12 +1116,12 @@ ${helpers.single_keyword_system("font-variant-caps", pub mod computed_value { use values::CSSFloat; - use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero}; - use values::distance::{ComputeSquaredDistance, SquaredDistance}; + use values::animated::{ToAnimatedValue, ToAnimatedZero}; #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - #[derive(Clone, Copy, Debug, PartialEq, ToCss)] + #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToCss)] pub enum T { + #[animation(error)] None, Number(CSSFloat), } @@ -1136,27 +1136,6 @@ ${helpers.single_keyword_system("font-variant-caps", } } - impl Animate for T { - fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { - match (self, other) { - (&T::Number(ref number), &T::Number(ref other)) => { - Ok(T::Number(number.animate(other, procedure)?)) - }, - _ => Err(()), - } - } - } - - impl ComputeSquaredDistance for T { - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { - match (self, other) { - (&T::Number(ref this), &T::Number(ref other)) => this.compute_squared_distance(other), - _ => Err(()), - } - } - } - impl ToAnimatedZero for T { #[inline] fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) } @@ -2265,11 +2244,8 @@ https://drafts.csswg.org/css-fonts-4/#low-level-font-variation-settings-control- predefined_type="Number" gecko_ffi_name="mScriptSizeMultiplier" spec="Internal (not web-exposed)" internal="True"> - use values::computed::ComputedValueAsSpecified; pub use self::computed_value::T as SpecifiedValue; - impl ComputedValueAsSpecified for SpecifiedValue {} - pub mod computed_value { pub type T = f32; } diff --git a/components/style/values/animated/effects.rs b/components/style/values/animated/effects.rs index cdde2894549..eb03bd9565b 100644 --- a/components/style/values/animated/effects.rs +++ b/components/style/values/animated/effects.rs @@ -139,21 +139,6 @@ impl ToAnimatedValue for ComputedTextShadowList { } } -// FIXME(nox): This could be derived if we implement Animate for bool. -impl Animate for BoxShadow { - #[inline] - fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { - if self.inset != other.inset { - return Err(()); - } - Ok(BoxShadow { - base: self.base.animate(&other.base, procedure)?, - spread: self.spread.animate(&other.spread, procedure)?, - inset: self.inset, - }) - } -} - impl ComputeSquaredDistance for BoxShadow { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { @@ -167,17 +152,6 @@ impl ComputeSquaredDistance for BoxShadow { } } -impl ToAnimatedZero for BoxShadow { - #[inline] - fn to_animated_zero(&self) -> Result<Self, ()> { - Ok(BoxShadow { - base: self.base.to_animated_zero()?, - spread: self.spread.to_animated_zero()?, - inset: self.inset, - }) - } -} - impl ToAnimatedValue for ComputedFilterList { type AnimatedValue = FilterList; diff --git a/components/style/values/animated/mod.rs b/components/style/values/animated/mod.rs index 71f6e2bd4db..c2d42192fdd 100644 --- a/components/style/values/animated/mod.rs +++ b/components/style/values/animated/mod.rs @@ -28,7 +28,19 @@ use values::specified::url::SpecifiedUrl; pub mod color; pub mod effects; -/// Animating from one value to another. +/// Animate from one value to another. +/// +/// This trait is derivable with `#[derive(Animate)]`. The derived +/// implementation uses a `match` expression with identical patterns for both +/// `self` and `other`, calling `Animate::animate` on each fields of the values. +/// If a field is annotated with `#[animation(constant)]`, the two values should +/// be equal or an error is returned. +/// +/// If a variant is annotated with `#[animation(error)]`, the corresponding +/// `match` arm is not generated. +/// +/// If the two values are not similar, an error is returned unless a fallback +/// function has been specified through `#[animate(fallback)]`. pub trait Animate: Sized { /// Animate a value towards another one, given an animation procedure. fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>; @@ -51,6 +63,8 @@ pub enum Procedure { /// Conversion between computed values and intermediate values for animations. /// /// Notably, colors are represented as four floats during animations. +/// +/// This trait is derivable with `#[derive(ToAnimatedValue)]`. pub trait ToAnimatedValue { /// The type of the animated value. type AnimatedValue; @@ -66,6 +80,13 @@ pub trait ToAnimatedValue { pub trait AnimatedValueAsComputed {} /// Returns a value similar to `self` that represents zero. +/// +/// This trait is derivable with `#[derive(ToAnimatedValue)]`. If a field is +/// annotated with `#[animation(constant)]`, a clone of its value will be used +/// instead of calling `ToAnimatedZero::to_animated_zero` on it. +/// +/// If a variant is annotated with `#[animation(error)]`, the corresponding +/// `match` arm is not generated. pub trait ToAnimatedZero: Sized { /// Returns a value that, when added with an underlying value, will produce the underlying /// value. This is used for SMIL animation's "by-animation" where SMIL first interpolates from diff --git a/components/style/values/computed/angle.rs b/components/style/values/computed/angle.rs index d5461d1cbf9..467a9501196 100644 --- a/components/style/values/computed/angle.rs +++ b/components/style/values/computed/angle.rs @@ -12,8 +12,10 @@ use values::animated::{Animate, Procedure}; use values::distance::{ComputeSquaredDistance, SquaredDistance}; /// A computed angle. +#[animate(fallback = "Self::animate_fallback")] #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] -#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, PartialOrd, ToAnimatedZero)] +#[derive(Animate, Clone, Copy, Debug, HasViewportPercentage, PartialEq)] +#[derive(PartialOrd, ToAnimatedZero)] pub enum Angle { /// An angle with degree unit. Degree(CSSFloat), @@ -62,26 +64,11 @@ impl Angle { pub fn zero() -> Self { Angle::Radian(0.0) } -} -/// https://drafts.csswg.org/css-transitions/#animtype-number -impl Animate for Angle { + /// https://drafts.csswg.org/css-transitions/#animtype-number #[inline] - fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { - match (self, other) { - (&Angle::Degree(ref this), &Angle::Degree(ref other)) => { - Ok(Angle::Degree(this.animate(other, procedure)?)) - }, - (&Angle::Gradian(ref this), &Angle::Gradian(ref other)) => { - Ok(Angle::Gradian(this.animate(other, procedure)?)) - }, - (&Angle::Turn(ref this), &Angle::Turn(ref other)) => { - Ok(Angle::Turn(this.animate(other, procedure)?)) - }, - _ => { - Ok(Angle::from_radians(self.radians().animate(&other.radians(), procedure)?)) - }, - } + fn animate_fallback(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { + Ok(Angle::from_radians(self.radians().animate(&other.radians(), procedure)?)) } } diff --git a/components/style/values/computed/background.rs b/components/style/values/computed/background.rs index 82b6ba74f28..f3180c54ebe 100644 --- a/components/style/values/computed/background.rs +++ b/components/style/values/computed/background.rs @@ -6,7 +6,7 @@ use properties::animated_properties::RepeatableListAnimatable; use properties::longhands::background_size::computed_value::T as BackgroundSizeList; -use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero}; +use values::animated::{ToAnimatedValue, ToAnimatedZero}; use values::computed::length::LengthOrPercentageOrAuto; use values::generics::background::BackgroundSize as GenericBackgroundSize; @@ -15,23 +15,6 @@ pub type BackgroundSize = GenericBackgroundSize<LengthOrPercentageOrAuto>; impl RepeatableListAnimatable for BackgroundSize {} -impl Animate for BackgroundSize { - fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { - match (self, other) { - ( - &GenericBackgroundSize::Explicit { width: self_width, height: self_height }, - &GenericBackgroundSize::Explicit { width: other_width, height: other_height }, - ) => { - Ok(GenericBackgroundSize::Explicit { - width: self_width.animate(&other_width, procedure)?, - height: self_height.animate(&other_height, procedure)?, - }) - } - _ => Err(()), - } - } -} - impl ToAnimatedZero for BackgroundSize { #[inline] fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) } diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs index a4a51b7bae6..952e213ccb9 100644 --- a/components/style/values/computed/image.rs +++ b/components/style/values/computed/image.rs @@ -18,8 +18,7 @@ use values::generics::image::{CompatMode, ColorStop as GenericColorStop, EndingS use values::generics::image::{Gradient as GenericGradient, GradientItem as GenericGradientItem}; use values::generics::image::{Image as GenericImage, GradientKind as GenericGradientKind}; use values::generics::image::{LineDirection as GenericLineDirection, MozImageRect as GenericMozImageRect}; -use values::specified::image::{Gradient as SpecifiedGradient, LineDirection as SpecifiedLineDirection}; -use values::specified::image::{GradientKind as SpecifiedGradientKind}; +use values::specified::image::LineDirection as SpecifiedLineDirection; use values::specified::position::{X, Y}; /// A computed image layer. @@ -181,55 +180,3 @@ impl ToComputedValue for SpecifiedLineDirection { } } } - -impl ToComputedValue for SpecifiedGradient { - type ComputedValue = Gradient; - - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - Self::ComputedValue { - kind: self.kind.to_computed_value(context), - items: self.items.to_computed_value(context), - repeating: self.repeating, - compat_mode: self.compat_mode - } - } - - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - Self { - kind: SpecifiedGradientKind::from_computed_value(&computed.kind), - items: ToComputedValue::from_computed_value(&computed.items), - repeating: computed.repeating, - compat_mode: computed.compat_mode - } - } -} - -impl ToComputedValue for SpecifiedGradientKind { - type ComputedValue = GradientKind; - - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - match self { - &GenericGradientKind::Linear(ref line_direction) => { - GenericGradientKind::Linear(line_direction.to_computed_value(context)) - }, - &GenericGradientKind::Radial(ref ending_shape, ref position, ref angle) => { - GenericGradientKind::Radial(ending_shape.to_computed_value(context), - position.to_computed_value(context), - angle.map(|angle| angle.to_computed_value(context))) - } - } - } - - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - match *computed { - GenericGradientKind::Linear(line_direction) => { - GenericGradientKind::Linear(SpecifiedLineDirection::from_computed_value(&line_direction)) - }, - GenericGradientKind::Radial(ending_shape, position, angle) => { - GenericGradientKind::Radial(ToComputedValue::from_computed_value(&ending_shape), - ToComputedValue::from_computed_value(&position), - angle.map(|angle| ToComputedValue::from_computed_value(&angle))) - } - } - } -} diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index b4f4cfc5101..d29f5aa87fc 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -11,7 +11,7 @@ use style_traits::ToCss; use style_traits::values::specified::AllowedLengthType; use super::{Number, ToComputedValue, Context, Percentage}; use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified}; -use values::animated::ToAnimatedZero; +use values::animated::{Animate, Procedure, ToAnimatedZero}; use values::computed::{NonNegativeAu, NonNegativeNumber}; use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::NonNegative; @@ -64,26 +64,16 @@ impl ToComputedValue for specified::Length { } } -#[derive(Clone, Copy, Debug, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[allow(missing_docs)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Copy, Debug, PartialEq, ToAnimatedZero)] pub struct CalcLengthOrPercentage { + #[animation(constant)] pub clamping_mode: AllowedLengthType, length: Au, pub percentage: Option<Percentage>, } -impl ToAnimatedZero for CalcLengthOrPercentage { - #[inline] - fn to_animated_zero(&self) -> Result<Self, ()> { - Ok(CalcLengthOrPercentage { - clamping_mode: self.clamping_mode, - length: self.length.to_animated_zero()?, - percentage: self.percentage.to_animated_zero()?, - }) - } -} - impl ComputeSquaredDistance for CalcLengthOrPercentage { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { @@ -285,28 +275,47 @@ impl ToComputedValue for specified::CalcLengthOrPercentage { } #[allow(missing_docs)] +#[animate(fallback = "Self::animate_fallback")] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, PartialEq, ToAnimatedZero, ToCss)] +#[css(derive_debug)] +#[derive(Animate, Clone, ComputeSquaredDistance, Copy, PartialEq)] +#[derive(ToAnimatedZero, ToCss)] +#[distance(fallback = "Self::compute_squared_distance_fallback")] pub enum LengthOrPercentage { Length(Au), Percentage(Percentage), Calc(CalcLengthOrPercentage), } -impl ComputeSquaredDistance for LengthOrPercentage { - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { - match (self, other) { - (&LengthOrPercentage::Length(ref this), &LengthOrPercentage::Length(ref other)) => { - this.compute_squared_distance(other) - }, - (&LengthOrPercentage::Percentage(ref this), &LengthOrPercentage::Percentage(ref other)) => { - this.compute_squared_distance(other) - }, - (this, other) => { - CalcLengthOrPercentage::compute_squared_distance(&(*this).into(), &(*other).into()) - } +impl LengthOrPercentage { + /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc + fn animate_fallback( + &self, + other: &Self, + procedure: Procedure, + ) -> Result<Self, ()> { + // Special handling for zero values since these should not require calc(). + if self.is_definitely_zero() { + return other.to_animated_zero()?.animate(other, procedure); } + if other.is_definitely_zero() { + return self.animate(&self.to_animated_zero()?, procedure); + } + + let this = CalcLengthOrPercentage::from(*self); + let other = CalcLengthOrPercentage::from(*other); + Ok(LengthOrPercentage::Calc(this.animate(&other, procedure)?)) + } + + #[inline] + fn compute_squared_distance_fallback( + &self, + other: &Self, + ) -> Result<SquaredDistance, ()> { + CalcLengthOrPercentage::compute_squared_distance( + &(*self).into(), + &(*other).into(), + ) } } @@ -379,16 +388,6 @@ impl LengthOrPercentage { } } -impl fmt::Debug for LengthOrPercentage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - LengthOrPercentage::Length(length) => write!(f, "{:?}", length), - LengthOrPercentage::Percentage(percentage) => write!(f, "{}%", percentage.0 * 100.), - LengthOrPercentage::Calc(calc) => write!(f, "{:?}", calc), - } - } -} - impl ToComputedValue for specified::LengthOrPercentage { type ComputedValue = LengthOrPercentage; @@ -426,8 +425,11 @@ impl ToComputedValue for specified::LengthOrPercentage { } #[allow(missing_docs)] +#[animate(fallback = "Self::animate_fallback")] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, PartialEq, ToCss)] +#[css(derive_debug)] +#[derive(Animate, Clone, ComputeSquaredDistance, Copy, PartialEq, ToCss)] +#[distance(fallback = "Self::compute_squared_distance_fallback")] pub enum LengthOrPercentageOrAuto { Length(Au), Percentage(Percentage), @@ -435,20 +437,29 @@ pub enum LengthOrPercentageOrAuto { Calc(CalcLengthOrPercentage), } -impl ComputeSquaredDistance for LengthOrPercentageOrAuto { - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { - match (self, other) { - (&LengthOrPercentageOrAuto::Length(ref this), &LengthOrPercentageOrAuto::Length(ref other)) => { - this.compute_squared_distance(other) - }, - (&LengthOrPercentageOrAuto::Percentage(ref this), &LengthOrPercentageOrAuto::Percentage(ref other)) => { - this.compute_squared_distance(other) - }, - (this, other) => { - <Option<CalcLengthOrPercentage>>::compute_squared_distance(&(*this).into(), &(*other).into()) - } - } +impl LengthOrPercentageOrAuto { + /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc + fn animate_fallback( + &self, + other: &Self, + procedure: Procedure, + ) -> Result<Self, ()> { + let this = <Option<CalcLengthOrPercentage>>::from(*self); + let other = <Option<CalcLengthOrPercentage>>::from(*other); + Ok(LengthOrPercentageOrAuto::Calc( + this.animate(&other, procedure)?.ok_or(())?, + )) + } + + #[inline] + fn compute_squared_distance_fallback( + &self, + other: &Self, + ) -> Result<SquaredDistance, ()> { + <Option<CalcLengthOrPercentage>>::compute_squared_distance( + &(*self).into(), + &(*other).into(), + ) } } @@ -467,17 +478,6 @@ impl LengthOrPercentageOrAuto { } } -impl fmt::Debug for LengthOrPercentageOrAuto { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - LengthOrPercentageOrAuto::Length(length) => write!(f, "{:?}", length), - LengthOrPercentageOrAuto::Percentage(percentage) => write!(f, "{}%", percentage.0 * 100.), - LengthOrPercentageOrAuto::Auto => write!(f, "auto"), - LengthOrPercentageOrAuto::Calc(calc) => write!(f, "{:?}", calc), - } - } -} - impl ToComputedValue for specified::LengthOrPercentageOrAuto { type ComputedValue = LengthOrPercentageOrAuto; @@ -521,8 +521,11 @@ impl ToComputedValue for specified::LengthOrPercentageOrAuto { } #[allow(missing_docs)] +#[animate(fallback = "Self::animate_fallback")] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, PartialEq, ToCss)] +#[css(derive_debug)] +#[derive(Animate, Clone, ComputeSquaredDistance, Copy, PartialEq, ToCss)] +#[distance(fallback = "Self::compute_squared_distance_fallback")] pub enum LengthOrPercentageOrNone { Length(Au), Percentage(Percentage), @@ -530,20 +533,28 @@ pub enum LengthOrPercentageOrNone { None, } -impl ComputeSquaredDistance for LengthOrPercentageOrNone { - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { - match (self, other) { - (&LengthOrPercentageOrNone::Length(ref this), &LengthOrPercentageOrNone::Length(ref other)) => { - this.compute_squared_distance(other) - }, - (&LengthOrPercentageOrNone::Percentage(ref this), &LengthOrPercentageOrNone::Percentage(ref other)) => { - this.compute_squared_distance(other) - }, - (this, other) => { - <Option<CalcLengthOrPercentage>>::compute_squared_distance(&(*this).into(), &(*other).into()) - } - } +impl LengthOrPercentageOrNone { + /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc + fn animate_fallback( + &self, + other: &Self, + procedure: Procedure, + ) -> Result<Self, ()> { + let this = <Option<CalcLengthOrPercentage>>::from(*self); + let other = <Option<CalcLengthOrPercentage>>::from(*other); + Ok(LengthOrPercentageOrNone::Calc( + this.animate(&other, procedure)?.ok_or(())?, + )) + } + + fn compute_squared_distance_fallback( + &self, + other: &Self, + ) -> Result<SquaredDistance, ()> { + <Option<CalcLengthOrPercentage>>::compute_squared_distance( + &(*self).into(), + &(*other).into(), + ) } } @@ -559,17 +570,6 @@ impl LengthOrPercentageOrNone { } } -impl fmt::Debug for LengthOrPercentageOrNone { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - LengthOrPercentageOrNone::Length(length) => write!(f, "{:?}", length), - LengthOrPercentageOrNone::Percentage(percentage) => write!(f, "{}%", percentage.0 * 100.), - LengthOrPercentageOrNone::Calc(calc) => write!(f, "{:?}", calc), - LengthOrPercentageOrNone::None => write!(f, "none"), - } - } -} - impl ToComputedValue for specified::LengthOrPercentageOrNone { type ComputedValue = LengthOrPercentageOrNone; @@ -695,28 +695,14 @@ pub type NonNegativeLengthOrNumber = Either<NonNegativeLength, NonNegativeNumber /// See values/specified/length.rs for more details. #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, Debug, PartialEq, ToCss)] +#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq)] +#[derive(ToAnimatedZero, ToCss)] pub enum MozLength { LengthOrPercentageOrAuto(LengthOrPercentageOrAuto), + #[animation(error)] ExtremumLength(ExtremumLength), } -impl ComputeSquaredDistance for MozLength { - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { - match (self, other) { - (&MozLength::LengthOrPercentageOrAuto(ref this), &MozLength::LengthOrPercentageOrAuto(ref other)) => { - this.compute_squared_distance(other) - }, - _ => { - // FIXME(nox): Should this return `Ok(SquaredDistance::Value(1.))` - // when `self` and `other` are the same extremum value? - Err(()) - }, - } - } -} - impl MozLength { /// Returns the `auto` value. pub fn auto() -> Self { @@ -755,28 +741,13 @@ impl ToComputedValue for specified::MozLength { /// See values/specified/length.rs for more details. #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, Debug, PartialEq, ToCss)] +#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToCss)] pub enum MaxLength { LengthOrPercentageOrNone(LengthOrPercentageOrNone), + #[animation(error)] ExtremumLength(ExtremumLength), } -impl ComputeSquaredDistance for MaxLength { - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { - match (self, other) { - (&MaxLength::LengthOrPercentageOrNone(ref this), &MaxLength::LengthOrPercentageOrNone(ref other)) => { - this.compute_squared_distance(other) - }, - _ => { - // FIXME(nox): Should this return `Ok(SquaredDistance::Value(1.))` - // when `self` and `other` are the same extremum value? - Err(()) - }, - } - } -} - impl MaxLength { /// Returns the `none` value. pub fn none() -> Self { diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 8a15ef1162c..86aaf29b1c5 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -199,6 +199,11 @@ impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> Iterator for ComputedVecIter< } /// A trait to represent the conversion between computed and specified values. +/// +/// This trait is derivable with `#[derive(ToComputedValue)]`. The derived +/// implementation just calls `ToComputedValue::to_computed_value` on each field +/// of the passed value, or `Clone::clone` if the field is annotated with +/// `#[compute(clone)]`. pub trait ToComputedValue { /// The computed value type we're going to be converted to. type ComputedValue; @@ -327,6 +332,7 @@ impl<T> ToComputedValue for T impl ComputedValueAsSpecified for Atom {} impl ComputedValueAsSpecified for bool {} +impl ComputedValueAsSpecified for f32 {} impl ComputedValueAsSpecified for specified::BorderStyle {} diff --git a/components/style/values/distance.rs b/components/style/values/distance.rs index d4eb91f112f..351f1eb021f 100644 --- a/components/style/values/distance.rs +++ b/components/style/values/distance.rs @@ -10,6 +10,17 @@ use std::iter::Sum; use std::ops::Add; /// A trait to compute squared distances between two animatable values. +/// +/// This trait is derivable with `#[derive(ComputeSquaredDistance)]`. The derived +/// implementation uses a `match` expression with identical patterns for both +/// `self` and `other`, calling `ComputeSquaredDistance::compute_squared_distance` +/// on each fields of the values. +/// +/// If a variant is annotated with `#[animation(error)]`, the corresponding +/// `match` arm is not generated. +/// +/// If the two values are not similar, an error is returned unless a fallback +/// function has been specified through `#[distance(fallback)]`. pub trait ComputeSquaredDistance { /// Computes the squared distance between two animatable values. fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()>; diff --git a/components/style/values/generics/background.rs b/components/style/values/generics/background.rs index 8a79691f3b9..abcfb655eec 100644 --- a/components/style/values/generics/background.rs +++ b/components/style/values/generics/background.rs @@ -5,8 +5,9 @@ //! Generic types for CSS values related to backgrounds. /// A generic value for the `background-size` property. -#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)] +#[derive(HasViewportPercentage, PartialEq, ToComputedValue, ToCss)] pub enum BackgroundSize<LengthOrPercentageOrAuto> { /// `<width> <height>` Explicit { @@ -16,8 +17,10 @@ pub enum BackgroundSize<LengthOrPercentageOrAuto> { height: LengthOrPercentageOrAuto }, /// `cover` + #[animation(error)] Cover, /// `contain` + #[animation(error)] Contain, } diff --git a/components/style/values/generics/basic_shape.rs b/components/style/values/generics/basic_shape.rs index 99de026d706..019ed6599e2 100644 --- a/components/style/values/generics/basic_shape.rs +++ b/components/style/values/generics/basic_shape.rs @@ -44,11 +44,18 @@ add_impls_for_keyword_enum!(ShapeBox); /// A shape source, for some reference box. #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)] +#[derive(Animate, Clone, Debug, PartialEq, ToComputedValue, ToCss)] pub enum ShapeSource<BasicShape, ReferenceBox, Url> { + #[animation(error)] Url(Url), - Shape(BasicShape, Option<ReferenceBox>), + Shape( + BasicShape, + #[animation(constant)] + Option<ReferenceBox>, + ), + #[animation(error)] Box(ReferenceBox), + #[animation(error)] None, } @@ -94,10 +101,13 @@ pub struct Ellipse<H, V, LengthOrPercentage> { /// https://drafts.csswg.org/css-shapes/#typedef-shape-radius #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToComputedValue, ToCss)] +#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, PartialEq)] +#[derive(ToComputedValue, ToCss)] pub enum ShapeRadius<LengthOrPercentage> { Length(LengthOrPercentage), + #[animation(error)] ClosestSide, + #[animation(error)] FarthestSide, } @@ -123,29 +133,6 @@ define_css_keyword_enum!(FillRule: ); add_impls_for_keyword_enum!(FillRule); -// FIXME(nox): This should be derivable, but we need to implement Animate -// on the T types. -impl<B, T, U> Animate for ShapeSource<B, T, U> -where - B: Animate, - T: Clone + PartialEq, -{ - fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { - match (self, other) { - ( - &ShapeSource::Shape(ref this, ref this_box), - &ShapeSource::Shape(ref other, ref other_box), - ) if this_box == other_box => { - Ok(ShapeSource::Shape( - this.animate(other, procedure)?, - this_box.clone(), - )) - }, - _ => Err(()), - } - } -} - // FIXME(nox): Implement ComputeSquaredDistance for T types and stop // using PartialEq here, this will let us derive this impl. impl<B, T, U> ComputeSquaredDistance for ShapeSource<B, T, U> @@ -191,20 +178,6 @@ impl<L> ToCss for InsetRect<L> } } -impl<L> Animate for ShapeRadius<L> -where - L: Animate, -{ - fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { - match (self, other) { - (&ShapeRadius::Length(ref this), &ShapeRadius::Length(ref other)) => { - Ok(ShapeRadius::Length(this.animate(other, procedure)?)) - }, - _ => Err(()), - } - } -} - impl<L> Default for ShapeRadius<L> { #[inline] fn default() -> Self { ShapeRadius::ClosestSide } diff --git a/components/style/values/generics/effects.rs b/components/style/values/generics/effects.rs index 3004c21f9b2..5d95ae19a8f 100644 --- a/components/style/values/generics/effects.rs +++ b/components/style/values/generics/effects.rs @@ -11,13 +11,15 @@ use values::specified::url::SpecifiedUrl; /// A generic value for a single `box-shadow`. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToAnimatedValue)] +#[derive(Animate, Clone, Debug, HasViewportPercentage, PartialEq)] +#[derive(ToAnimatedValue, ToAnimatedZero)] pub struct BoxShadow<Color, SizeLength, BlurShapeLength, ShapeLength> { /// The base shadow. pub base: SimpleShadow<Color, SizeLength, BlurShapeLength>, /// The spread radius. pub spread: ShapeLength, /// Whether this is an inset box shadow. + #[animation(constant)] pub inset: bool, } diff --git a/components/style/values/generics/grid.rs b/components/style/values/generics/grid.rs index 91b1e922d14..87f427f6cb3 100644 --- a/components/style/values/generics/grid.rs +++ b/components/style/values/generics/grid.rs @@ -10,7 +10,7 @@ use parser::{Parse, ParserContext}; use std::{fmt, mem, usize}; use style_traits::{ToCss, ParseError, StyleParseError}; use values::{CSSFloat, CustomIdent, serialize_dimension}; -use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue}; +use values::computed::ComputedValueAsSpecified; use values::specified::Integer; use values::specified::grid::parse_line_names; @@ -137,13 +137,14 @@ define_css_keyword_enum!{ TrackKeyword: "max-content" => MaxContent, "min-content" => MinContent } +impl ComputedValueAsSpecified for TrackKeyword {} -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] /// A track breadth for explicit grid track sizing. It's generic solely to /// avoid re-implementing it for the computed type. /// /// https://drafts.csswg.org/css-grid/#typedef-track-breadth +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Debug, PartialEq, ToComputedValue)] pub enum TrackBreadth<L> { /// The generic type is almost always a non-negative `<length-percentage>` Breadth(L), @@ -176,35 +177,12 @@ impl<L: ToCss> ToCss for TrackBreadth<L> { } } -impl<L: ToComputedValue> ToComputedValue for TrackBreadth<L> { - type ComputedValue = TrackBreadth<L::ComputedValue>; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - match *self { - TrackBreadth::Breadth(ref lop) => TrackBreadth::Breadth(lop.to_computed_value(context)), - TrackBreadth::Flex(fr) => TrackBreadth::Flex(fr), - TrackBreadth::Keyword(k) => TrackBreadth::Keyword(k), - } - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - match *computed { - TrackBreadth::Breadth(ref lop) => - TrackBreadth::Breadth(ToComputedValue::from_computed_value(lop)), - TrackBreadth::Flex(fr) => TrackBreadth::Flex(fr), - TrackBreadth::Keyword(k) => TrackBreadth::Keyword(k), - } - } -} - /// A `<track-size>` type for explicit grid track sizing. Like `<track-breadth>`, this is /// generic only to avoid code bloat. It only takes `<length-percentage>` /// /// https://drafts.csswg.org/css-grid/#typedef-track-size #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Debug, HasViewportPercentage, PartialEq)] +#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToComputedValue)] pub enum TrackSize<L> { /// A flexible `<track-breadth>` Breadth(TrackBreadth<L>), @@ -286,39 +264,6 @@ impl<L: ToCss> ToCss for TrackSize<L> { } } -impl<L: ToComputedValue> ToComputedValue for TrackSize<L> { - type ComputedValue = TrackSize<L::ComputedValue>; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - match *self { - TrackSize::Breadth(ref b) => match *b { - // <flex> outside `minmax()` expands to `mimmax(auto, <flex>)` - // https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-flex - TrackBreadth::Flex(f) => - TrackSize::Minmax(TrackBreadth::Keyword(TrackKeyword::Auto), TrackBreadth::Flex(f)), - _ => TrackSize::Breadth(b.to_computed_value(context)), - }, - TrackSize::Minmax(ref b_1, ref b_2) => - TrackSize::Minmax(b_1.to_computed_value(context), b_2.to_computed_value(context)), - TrackSize::FitContent(ref lop) => TrackSize::FitContent(lop.to_computed_value(context)), - } - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - match *computed { - TrackSize::Breadth(ref b) => - TrackSize::Breadth(ToComputedValue::from_computed_value(b)), - TrackSize::Minmax(ref b_1, ref b_2) => - TrackSize::Minmax(ToComputedValue::from_computed_value(b_1), - ToComputedValue::from_computed_value(b_2)), - TrackSize::FitContent(ref lop) => - TrackSize::FitContent(ToComputedValue::from_computed_value(lop)), - } - } -} - /// Helper function for serializing identifiers with a prefix and suffix, used /// for serializing <line-names> (in grid). pub fn concat_serialize_idents<W>(prefix: &str, suffix: &str, @@ -382,8 +327,8 @@ no_viewport_percentage!(RepeatCount); /// /// It can also hold `repeat()` function parameters, which expands into the respective /// values in its computed form. -#[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Debug, PartialEq, ToComputedValue)] pub struct TrackRepeat<L> { /// The number of times for the value to be repeated (could also be `auto-fit` or `auto-fill`) pub count: RepeatCount, @@ -392,6 +337,7 @@ pub struct TrackRepeat<L> { /// If there's no `<line-names>`, then it's represented by an empty vector. /// For N `<track-size>` values, there will be N+1 `<line-names>`, and so this vector's /// length is always one value more than that of the `<track-size>`. + #[compute(clone)] pub line_names: Box<[Box<[CustomIdent]>]>, /// `<track-size>` values. pub track_sizes: Vec<TrackSize<L>>, @@ -479,31 +425,6 @@ impl<L: Clone> TrackRepeat<L> { } } } -impl<L: ToComputedValue> ToComputedValue for TrackRepeat<L> { - type ComputedValue = TrackRepeat<L::ComputedValue>; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - TrackRepeat { - count: self.count, - track_sizes: self.track_sizes.iter() - .map(|val| val.to_computed_value(context)) - .collect(), - line_names: self.line_names.clone(), - } - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - TrackRepeat { - count: computed.count, - track_sizes: computed.track_sizes.iter() - .map(ToComputedValue::from_computed_value) - .collect(), - line_names: computed.line_names.clone(), - } - } -} /// The type of a `<track-list>` as determined during parsing. /// @@ -533,8 +454,8 @@ impl ComputedValueAsSpecified for TrackListType {} /// A grid `<track-list>` type. /// /// https://drafts.csswg.org/css-grid/#typedef-track-list -#[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Debug, PartialEq, ToComputedValue)] pub struct TrackList<T> { /// The type of this `<track-list>` (auto, explicit or general). /// @@ -548,6 +469,7 @@ pub struct TrackList<T> { /// If there's no `<line-names>`, then it's represented by an empty vector. /// For N values, there will be N+1 `<line-names>`, and so this vector's /// length is always one value more than that of the `<track-size>`. + #[compute(clone)] pub line_names: Box<[Box<[CustomIdent]>]>, /// `<auto-repeat>` value. There can only be one `<auto-repeat>` in a TrackList. pub auto_repeat: Option<TrackRepeat<T>>, @@ -698,8 +620,8 @@ no_viewport_percentage!(LineNameList); /// Variants for `<grid-template-rows> | <grid-template-columns>` /// Subgrid deferred to Level 2 spec due to lack of implementation. /// But it's implemented in gecko, so we have to as well. -#[derive(Clone, Debug, PartialEq, ToCss)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)] pub enum GridTemplateComponent<L> { /// `none` value. None, diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs index 867364e3df3..3e977759d70 100644 --- a/components/style/values/generics/image.rs +++ b/components/style/values/generics/image.rs @@ -35,16 +35,18 @@ pub enum Image<Gradient, MozImageRect, ImageUrl> { /// A CSS gradient. /// https://drafts.csswg.org/css-images/#gradients -#[derive(Clone, Debug, HasViewportPercentage, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToComputedValue)] pub struct Gradient<LineDirection, Length, LengthOrPercentage, Position, Color, Angle> { /// Gradients can be linear or radial. pub kind: GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle>, /// The color stops and interpolation hints. pub items: Vec<GradientItem<Color, LengthOrPercentage>>, /// True if this is a repeating gradient. + #[compute(clone)] pub repeating: bool, /// Compatibility mode. + #[compute(clone)] pub compat_mode: CompatMode, } @@ -61,8 +63,8 @@ pub enum CompatMode { } /// A gradient kind. -#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)] pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle> { /// A linear gradient. Linear(LineDirection), diff --git a/components/style/values/generics/svg.rs b/components/style/values/generics/svg.rs index bc3cb5fda18..87b0a74bc23 100644 --- a/components/style/values/generics/svg.rs +++ b/components/style/values/generics/svg.rs @@ -16,7 +16,8 @@ use values::distance::{ComputeSquaredDistance, SquaredDistance}; /// /// https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Debug, PartialEq, ToAnimatedValue, ToComputedValue, ToCss)] +#[derive(Animate, Clone, ComputeSquaredDistance, Debug, PartialEq)] +#[derive(ToAnimatedValue, ToComputedValue, ToCss)] pub struct SVGPaint<ColorType, UrlPaintServer> { /// The paint source pub kind: SVGPaintKind<ColorType, UrlPaintServer>, @@ -30,13 +31,15 @@ pub struct SVGPaint<ColorType, UrlPaintServer> { /// to have a fallback, Gecko lets the context /// properties have a fallback as well. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Debug, PartialEq, ToAnimatedValue, ToComputedValue, ToCss)] +#[derive(Animate, Clone, ComputeSquaredDistance, Debug, PartialEq)] +#[derive(ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)] pub enum SVGPaintKind<ColorType, UrlPaintServer> { /// `none` None, /// `<color>` Color(ColorType), /// `url(...)` + #[animation(error)] PaintServer(UrlPaintServer), /// `context-fill` ContextFill, diff --git a/components/style/values/specified/grid.rs b/components/style/values/specified/grid.rs index 92a442a1cf0..72ce404d25c 100644 --- a/components/style/values/specified/grid.rs +++ b/components/style/values/specified/grid.rs @@ -11,7 +11,6 @@ use std::ascii::AsciiExt; use std::mem; use style_traits::{HasViewportPercentage, ParseError, StyleParseError}; use values::{CSSFloat, CustomIdent}; -use values::computed::{self, Context, ToComputedValue}; use values::generics::grid::{GridTemplateComponent, RepeatCount, TrackBreadth, TrackKeyword, TrackRepeat}; use values::generics::grid::{LineNameList, TrackSize, TrackList, TrackListType}; use values::specified::LengthOrPercentage; @@ -286,42 +285,6 @@ impl HasViewportPercentage for TrackList<LengthOrPercentage> { } } - -impl ToComputedValue for TrackList<LengthOrPercentage> { - type ComputedValue = TrackList<computed::LengthOrPercentage>; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - let mut values = Vec::with_capacity(self.values.len() + 1); - for value in self.values.iter().map(|val| val.to_computed_value(context)) { - values.push(value); - } - - TrackList { - list_type: self.list_type.to_computed_value(context), - values: values, - line_names: self.line_names.clone(), - auto_repeat: self.auto_repeat.clone().map(|repeat| repeat.to_computed_value(context)), - } - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - let mut values = Vec::with_capacity(computed.values.len() + 1); - for value in computed.values.iter().map(ToComputedValue::from_computed_value) { - values.push(value); - } - - TrackList { - list_type: computed.list_type, - values: values, - line_names: computed.line_names.clone(), - auto_repeat: computed.auto_repeat.clone().map(|ref repeat| TrackRepeat::from_computed_value(repeat)), - } - } -} - - impl Parse for GridTemplateComponent<LengthOrPercentage> { // FIXME: Derive Parse (probably with None_) fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> { @@ -354,27 +317,3 @@ impl HasViewportPercentage for GridTemplateComponent<LengthOrPercentage> { } } } - -impl ToComputedValue for GridTemplateComponent<LengthOrPercentage> { - type ComputedValue = GridTemplateComponent<computed::LengthOrPercentage>; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - match *self { - GridTemplateComponent::None => GridTemplateComponent::None, - GridTemplateComponent::TrackList(ref l) => GridTemplateComponent::TrackList(l.to_computed_value(context)), - GridTemplateComponent::Subgrid(ref n) => GridTemplateComponent::Subgrid(n.to_computed_value(context)), - } - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - match *computed { - GridTemplateComponent::None => GridTemplateComponent::None, - GridTemplateComponent::TrackList(ref l) => - GridTemplateComponent::TrackList(ToComputedValue::from_computed_value(l)), - GridTemplateComponent::Subgrid(ref n) => - GridTemplateComponent::Subgrid(ToComputedValue::from_computed_value(n)), - } - } -} diff --git a/components/style_derive/animate.rs b/components/style_derive/animate.rs index 94d14336087..d4fe5c94a53 100644 --- a/components/style_derive/animate.rs +++ b/components/style_derive/animate.rs @@ -3,21 +3,22 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use cg; -use quote; -use syn; +use quote::Tokens; +use syn::{DeriveInput, Path}; -pub fn derive(input: syn::DeriveInput) -> quote::Tokens { +pub fn derive(input: DeriveInput) -> Tokens { let name = &input.ident; let trait_path = &["values", "animated", "Animate"]; let (impl_generics, ty_generics, mut where_clause) = cg::trait_parts(&input, trait_path); + let input_attrs = cg::parse_input_attrs::<AnimateInputAttrs>(&input); let variants = cg::variants(&input); let mut match_body = quote!(); let mut append_error_clause = variants.len() > 1; match_body.append_all(variants.iter().flat_map(|variant| { - let attrs = cg::parse_variant_attrs::<AnimateAttrs>(variant); - if attrs.error { + let variant_attrs = cg::parse_variant_attrs::<AnimationVariantAttrs>(variant); + if variant_attrs.error { append_error_clause = true; return None; } @@ -28,11 +29,32 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { let mut computations = quote!(); let iter = result_info.iter().zip(this_info.iter().zip(&other_info)); computations.append_all(iter.map(|(result, (this, other))| { - where_clause.predicates.push( - cg::where_predicate(this.field.ty.clone(), trait_path), - ); - quote! { - let #result = ::values::animated::Animate::animate(#this, #other, procedure)?; + let field_attrs = cg::parse_field_attrs::<AnimationFieldAttrs>(&result.field); + if field_attrs.constant { + if cg::is_parameterized(&result.field.ty, where_clause.params, None) { + where_clause.inner.predicates.push(cg::where_predicate( + result.field.ty.clone(), + &["std", "cmp", "PartialEq"], + None, + )); + where_clause.inner.predicates.push(cg::where_predicate( + result.field.ty.clone(), + &["std", "clone", "Clone"], + None, + )); + } + quote! { + if #this != #other { + return Err(()); + } + let #result = ::std::clone::Clone::clone(#this); + } + } else { + where_clause.add_trait_bound(&result.field.ty); + quote! { + let #result = + ::values::animated::Animate::animate(#this, #other, procedure)?; + } } })); Some(quote! { @@ -44,7 +66,13 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { })); if append_error_clause { - match_body = quote! { #match_body, _ => Err(()), }; + if let Some(fallback) = input_attrs.fallback { + match_body.append(quote! { + (this, other) => #fallback(this, other, procedure) + }); + } else { + match_body.append(quote! { _ => Err(()) }); + } } quote! { @@ -64,8 +92,20 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { } } -#[derive(Default, FromVariant)] #[darling(attributes(animate), default)] -pub struct AnimateAttrs { +#[derive(Default, FromDeriveInput)] +struct AnimateInputAttrs { + fallback: Option<Path>, +} + +#[darling(attributes(animation), default)] +#[derive(Default, FromVariant)] +pub struct AnimationVariantAttrs { pub error: bool, } + +#[darling(attributes(animation), default)] +#[derive(Default, FromField)] +pub struct AnimationFieldAttrs { + pub constant: bool, +} diff --git a/components/style_derive/cg.rs b/components/style_derive/cg.rs index b670da16737..6937d5836d2 100644 --- a/components/style_derive/cg.rs +++ b/components/style_derive/cg.rs @@ -2,17 +2,86 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use darling::FromVariant; -use quote::Tokens; +use darling::{FromDeriveInput, FromField, FromVariant}; +use quote::{ToTokens, Tokens}; use std::borrow::Cow; +use std::collections::HashSet; use std::iter; -use syn::{AngleBracketedParameterData, Body, DeriveInput, Ident, ImplGenerics}; -use syn::{Path, PathParameters, PathSegment, PolyTraitRef, QSelf}; -use syn::{TraitBoundModifier, Ty, TyGenerics, TyParam, TyParamBound, TypeBinding}; -use syn::{Variant, WhereBoundPredicate, WhereClause, WherePredicate}; +use syn::{self, AngleBracketedParameterData, Body, DeriveInput, Field, Ident}; +use syn::{ImplGenerics, Path, PathParameters, PathSegment, PolyTraitRef}; +use syn::{QSelf, TraitBoundModifier, Ty, TyGenerics, TyParam, TyParamBound}; +use syn::{TypeBinding, Variant, WhereBoundPredicate, WherePredicate}; use syn::visit::{self, Visitor}; use synstructure::{self, BindOpts, BindStyle, BindingInfo}; +pub struct WhereClause<'input, 'path> { + pub inner: syn::WhereClause, + pub params: &'input [TyParam], + trait_path: &'path [&'path str], + trait_output: Option<&'path str>, + bounded_types: HashSet<Ty>, +} + +impl<'input, 'path> ToTokens for WhereClause<'input, 'path> { + fn to_tokens(&self, tokens: &mut Tokens) { + self.inner.to_tokens(tokens); + } +} + +impl<'input, 'path> WhereClause<'input, 'path> { + pub fn add_trait_bound(&mut self, ty: &Ty) { + let trait_path = self.trait_path; + let params = self.params; + let mut found = self.trait_output.map(|_| HashSet::new()); + if self.bounded_types.contains(&ty) { + return; + } + if !is_parameterized(&ty, params, found.as_mut()) { + return; + } + self.bounded_types.insert(ty.clone()); + + let output = if let Some(output) = self.trait_output { + output + } else { + self.inner.predicates.push(where_predicate(ty.clone(), trait_path, None)); + return; + }; + + if let Ty::Path(None, ref path) = *ty { + if path_to_ident(path).is_some() { + self.inner.predicates.push(where_predicate(ty.clone(), trait_path, None)); + return; + } + } + + let output_type = map_type_params(ty, params, &mut |ident| { + let ty = Ty::Path(None, ident.clone().into()); + fmap_output_type(ty, trait_path, output) + }); + + let pred = where_predicate( + ty.clone(), + trait_path, + Some((output, output_type)), + ); + + self.inner.predicates.push(pred); + + if let Some(found) = found { + for ident in found { + let ty = Ty::Path(None, ident.into()); + if !self.bounded_types.contains(&ty) { + self.bounded_types.insert(ty.clone()); + self.inner.predicates.push( + where_predicate(ty, trait_path, None), + ); + }; + } + } + } +} + pub fn fmap_match<F>( input: &DeriveInput, bind_style: BindStyle, @@ -35,23 +104,36 @@ where }) } -pub fn fmap_trait_parts<'a>( - input: &'a DeriveInput, +fn fmap_output_type( + ty: Ty, trait_path: &[&str], trait_output: &str, -) -> (ImplGenerics<'a>, TyGenerics<'a>, WhereClause, Path) { - let (impl_generics, ty_generics, where_clause) = trait_parts(input, trait_path); +) -> Ty { + Ty::Path( + Some(QSelf { + ty: Box::new(ty), + position: trait_path.len(), + }), + path(trait_path.iter().chain(iter::once(&trait_output))), + ) +} + +pub fn fmap_trait_parts<'input, 'path>( + input: &'input DeriveInput, + trait_path: &'path [&'path str], + trait_output: &'path str, +) -> (ImplGenerics<'input>, TyGenerics<'input>, WhereClause<'input, 'path>, Path) { + let (impl_generics, ty_generics, mut where_clause) = trait_parts(input, trait_path); + where_clause.trait_output = Some(trait_output); let output_ty = PathSegment { ident: input.ident.clone(), parameters: PathParameters::AngleBracketed(AngleBracketedParameterData { lifetimes: input.generics.lifetimes.iter().map(|l| l.lifetime.clone()).collect(), types: input.generics.ty_params.iter().map(|ty| { - Ty::Path( - Some(QSelf { - ty: Box::new(Ty::Path(None, ty.ident.clone().into())), - position: trait_path.len(), - }), - path(trait_path.iter().chain(iter::once(&trait_output))), + fmap_output_type( + Ty::Path(None, ty.ident.clone().into()), + trait_path, + trait_output, ) }).collect(), .. Default::default() @@ -60,72 +142,106 @@ pub fn fmap_trait_parts<'a>( (impl_generics, ty_generics, where_clause, output_ty) } -fn fmap_trait_where_predicate( - bounded_ty: Ty, - trait_path: &[&str], - trait_output: Option<(&str, Ty)>, -) -> WherePredicate { - WherePredicate::BoundPredicate(WhereBoundPredicate { - bound_lifetimes: vec![], - bounded_ty, - bounds: vec![TyParamBound::Trait( - PolyTraitRef { - bound_lifetimes: vec![], - trait_ref: fmap_trait_ref(trait_path, trait_output), - }, - TraitBoundModifier::None - )], - }) -} - -fn fmap_trait_ref(path: &[&str], output: Option<(&str, Ty)>) -> Path { - let (name, parent) = path.split_last().unwrap(); - let last_segment = PathSegment { - ident: (*name).into(), - parameters: PathParameters::AngleBracketed( - AngleBracketedParameterData { - bindings: output.into_iter().map(|(param, ty)| { - TypeBinding { ident: param.into(), ty } - }).collect(), - .. Default::default() - } - ) - }; - Path { - global: true, - segments: { - parent - .iter() - .cloned() - .map(Into::into) - .chain(iter::once(last_segment)) - .collect() - }, - } -} - -pub fn is_parameterized(ty: &Ty, params: &[TyParam]) -> bool { - struct IsParameterized<'a> { +pub fn is_parameterized( + ty: &Ty, + params: &[TyParam], + found: Option<&mut HashSet<Ident>>, +) -> bool { + struct IsParameterized<'a, 'b> { params: &'a [TyParam], has_free: bool, + found: Option<&'b mut HashSet<Ident>>, } - impl<'a> Visitor for IsParameterized<'a> { + impl<'a, 'b> Visitor for IsParameterized<'a, 'b> { fn visit_path(&mut self, path: &Path) { - if !path.global && path.segments.len() == 1 { - if self.params.iter().any(|param| param.ident == path.segments[0].ident) { + if let Some(ident) = path_to_ident(path) { + if self.params.iter().any(|param| param.ident == ident) { self.has_free = true; + if let Some(ref mut found) = self.found { + found.insert(ident.clone()); + } } } visit::walk_path(self, path); } } - let mut visitor = IsParameterized { params: params, has_free: false }; + let mut visitor = IsParameterized { params, has_free: false, found }; visitor.visit_ty(ty); visitor.has_free } +pub fn map_type_params<F>(ty: &Ty, params: &[TyParam], f: &mut F) -> Ty +where + F: FnMut(&Ident) -> Ty, +{ + match *ty { + Ty::Slice(ref ty) => Ty::Slice(Box::new(map_type_params(ty, params, f))), + Ty::Array(ref ty, ref expr) => { + Ty::Array(Box::new(map_type_params(ty, params, f)), expr.clone()) + }, + Ty::Never => Ty::Never, + Ty::Tup(ref items) => { + Ty::Tup(items.iter().map(|ty| map_type_params(ty, params, f)).collect()) + }, + Ty::Path(None, ref path) => { + if let Some(ident) = path_to_ident(path) { + if params.iter().any(|param| param.ident == ident) { + return f(ident); + } + } + Ty::Path(None, map_type_params_in_path(path, params, f)) + } + Ty::Path(ref qself, ref path) => { + Ty::Path( + qself.as_ref().map(|qself| { + QSelf { + ty: Box::new(map_type_params(&qself.ty, params, f)), + position: qself.position, + } + }), + map_type_params_in_path(path, params, f), + ) + }, + Ty::Paren(ref ty) => Ty::Paren(Box::new(map_type_params(ty, params, f))), + ref ty => panic!("type {:?} cannot be mapped yet", ty), + } +} + +fn map_type_params_in_path<F>(path: &Path, params: &[TyParam], f: &mut F) -> Path +where + F: FnMut(&Ident) -> Ty, +{ + Path { + global: path.global, + segments: path.segments.iter().map(|segment| { + PathSegment { + ident: segment.ident.clone(), + parameters: match segment.parameters { + PathParameters::AngleBracketed(ref data) => { + PathParameters::AngleBracketed(AngleBracketedParameterData { + lifetimes: data.lifetimes.clone(), + types: data.types.iter().map(|ty| { + map_type_params(ty, params, f) + }).collect(), + bindings: data.bindings.iter().map(|binding| { + TypeBinding { + ident: binding.ident.clone(), + ty: map_type_params(&binding.ty, params, f), + } + }).collect(), + }) + }, + ref parameters => { + panic!("parameters {:?} cannot be mapped yet", parameters) + }, + }, + } + }).collect(), + } +} + pub fn path<S>(segments: S) -> Path where S: IntoIterator, @@ -137,13 +253,46 @@ where } } +fn path_to_ident(path: &Path) -> Option<&Ident> { + match *path { + Path { global: false, ref segments } if segments.len() == 1 => { + if segments[0].parameters.is_empty() { + Some(&segments[0].ident) + } else { + None + } + }, + _ => None, + } +} + +pub fn parse_field_attrs<A>(field: &Field) -> A +where + A: FromField, +{ + match A::from_field(field) { + Ok(attrs) => attrs, + Err(e) => panic!("failed to parse field attributes: {}", e), + } +} + +pub fn parse_input_attrs<A>(input: &DeriveInput) -> A +where + A: FromDeriveInput, +{ + match A::from_derive_input(input) { + Ok(attrs) => attrs, + Err(e) => panic!("failed to parse input attributes: {}", e), + } +} + pub fn parse_variant_attrs<A>(variant: &Variant) -> A where A: FromVariant, { match A::from_variant(variant) { Ok(attrs) => attrs, - Err(e) => panic!("failed to parse attributes: {}", e), + Err(e) => panic!("failed to parse variant attributes: {}", e), } } @@ -159,22 +308,47 @@ pub fn ref_pattern<'a>( ) } -pub fn trait_parts<'a>( - input: &'a DeriveInput, - trait_path: &[&str], -) -> (ImplGenerics<'a>, TyGenerics<'a>, WhereClause) { +pub fn trait_parts<'input, 'path>( + input: &'input DeriveInput, + trait_path: &'path [&'path str], +) -> (ImplGenerics<'input>, TyGenerics<'input>, WhereClause<'input, 'path>) { let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let mut where_clause = where_clause.clone(); - for param in &input.generics.ty_params { - where_clause.predicates.push(fmap_trait_where_predicate( - Ty::Path(None, param.ident.clone().into()), - trait_path, - None, - )); - } + let where_clause = WhereClause { + inner: where_clause.clone(), + params: &input.generics.ty_params, + trait_path, + trait_output: None, + bounded_types: HashSet::new() + }; (impl_generics, ty_generics, where_clause) } +fn trait_ref(path: &[&str], output: Option<(&str, Ty)>) -> Path { + let (name, parent) = path.split_last().unwrap(); + let last_segment = PathSegment { + ident: (*name).into(), + parameters: PathParameters::AngleBracketed( + AngleBracketedParameterData { + bindings: output.into_iter().map(|(param, ty)| { + TypeBinding { ident: param.into(), ty } + }).collect(), + .. Default::default() + } + ) + }; + Path { + global: true, + segments: { + parent + .iter() + .cloned() + .map(Into::into) + .chain(iter::once(last_segment)) + .collect() + }, + } +} + pub fn value<'a>( name: &Ident, variant: &'a Variant, @@ -215,16 +389,20 @@ pub fn variants(input: &DeriveInput) -> Cow<[Variant]> { } } -pub fn where_predicate(ty: Ty, segments: &[&str]) -> WherePredicate { +pub fn where_predicate( + bounded_ty: Ty, + trait_path: &[&str], + trait_output: Option<(&str, Ty)>, +) -> WherePredicate { WherePredicate::BoundPredicate(WhereBoundPredicate { bound_lifetimes: vec![], - bounded_ty: ty, + bounded_ty, bounds: vec![TyParamBound::Trait( PolyTraitRef { bound_lifetimes: vec![], - trait_ref: path(segments), + trait_ref: trait_ref(trait_path, trait_output), }, - TraitBoundModifier::None, + TraitBoundModifier::None )], }) } diff --git a/components/style_derive/compute_squared_distance.rs b/components/style_derive/compute_squared_distance.rs index 860e568ea88..cbdd9c62acc 100644 --- a/components/style_derive/compute_squared_distance.rs +++ b/components/style_derive/compute_squared_distance.rs @@ -2,22 +2,23 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use animate::AnimateAttrs; +use animate::AnimationVariantAttrs; use cg; -use quote; -use syn; +use quote::Tokens; +use syn::{DeriveInput, Path}; -pub fn derive(input: syn::DeriveInput) -> quote::Tokens { +pub fn derive(input: DeriveInput) -> Tokens { let name = &input.ident; let trait_path = &["values", "distance", "ComputeSquaredDistance"]; let (impl_generics, ty_generics, mut where_clause) = cg::trait_parts(&input, trait_path); + let input_attrs = cg::parse_input_attrs::<DistanceInputAttrs>(&input); let variants = cg::variants(&input); let mut match_body = quote!(); let mut append_error_clause = variants.len() > 1; match_body.append_all(variants.iter().map(|variant| { - let attrs = cg::parse_variant_attrs::<AnimateAttrs>(variant); + let attrs = cg::parse_variant_attrs::<AnimationVariantAttrs>(variant); if attrs.error { append_error_clause = true; return None; @@ -30,9 +31,7 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { } else { let mut sum = quote!(); sum.append_separated(this_info.iter().zip(&other_info).map(|(this, other)| { - where_clause.predicates.push( - cg::where_predicate(this.field.ty.clone(), trait_path), - ); + where_clause.add_trait_bound(&this.field.ty); quote! { ::values::distance::ComputeSquaredDistance::compute_squared_distance(#this, #other)? } @@ -47,7 +46,13 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { })); if append_error_clause { - match_body = quote! { #match_body, _ => Err(()), }; + if let Some(fallback) = input_attrs.fallback { + match_body.append(quote! { + (this, other) => #fallback(this, other) + }); + } else { + match_body.append(quote! { _ => Err(()) }); + } } quote! { @@ -65,3 +70,9 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { } } } + +#[darling(attributes(distance), default)] +#[derive(Default, FromDeriveInput)] +struct DistanceInputAttrs { + fallback: Option<Path>, +} diff --git a/components/style_derive/has_viewport_percentage.rs b/components/style_derive/has_viewport_percentage.rs index 5f26d988b8a..18856576fb8 100644 --- a/components/style_derive/has_viewport_percentage.rs +++ b/components/style_derive/has_viewport_percentage.rs @@ -19,11 +19,10 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { None => return Some(quote!(false)), Some(pair) => pair, }; + where_clause.add_trait_bound(&first.field.ty); let mut expr = quote!(::style_traits::HasViewportPercentage::has_viewport_percentage(#first)); for binding in rest { - where_clause.predicates.push( - cg::where_predicate(binding.field.ty.clone(), trait_path), - ); + where_clause.add_trait_bound(&binding.field.ty); expr = quote!(#expr || ::style_traits::HasViewportPercentage::has_viewport_percentage(#binding)); } Some(expr) diff --git a/components/style_derive/lib.rs b/components/style_derive/lib.rs index e589759f813..f98829e60bf 100644 --- a/components/style_derive/lib.rs +++ b/components/style_derive/lib.rs @@ -19,13 +19,13 @@ mod to_animated_zero; mod to_computed_value; mod to_css; -#[proc_macro_derive(Animate, attributes(animation))] +#[proc_macro_derive(Animate, attributes(animate, animation))] pub fn derive_animate(stream: TokenStream) -> TokenStream { let input = syn::parse_derive_input(&stream.to_string()).unwrap(); animate::derive(input).to_string().parse().unwrap() } -#[proc_macro_derive(ComputeSquaredDistance, attributes(animation))] +#[proc_macro_derive(ComputeSquaredDistance, attributes(animation, distance))] pub fn derive_compute_squared_distance(stream: TokenStream) -> TokenStream { let input = syn::parse_derive_input(&stream.to_string()).unwrap(); compute_squared_distance::derive(input).to_string().parse().unwrap() @@ -43,13 +43,13 @@ pub fn derive_to_animated_value(stream: TokenStream) -> TokenStream { to_animated_value::derive(input).to_string().parse().unwrap() } -#[proc_macro_derive(ToAnimatedZero)] +#[proc_macro_derive(ToAnimatedZero, attributes(animation))] pub fn derive_to_animated_zero(stream: TokenStream) -> TokenStream { let input = syn::parse_derive_input(&stream.to_string()).unwrap(); to_animated_zero::derive(input).to_string().parse().unwrap() } -#[proc_macro_derive(ToComputedValue)] +#[proc_macro_derive(ToComputedValue, attributes(compute))] pub fn derive_to_computed_value(stream: TokenStream) -> TokenStream { let input = syn::parse_derive_input(&stream.to_string()).unwrap(); to_computed_value::derive(input).to_string().parse().unwrap() diff --git a/components/style_derive/to_animated_value.rs b/components/style_derive/to_animated_value.rs index 5a66ddf65a4..7865ba597e9 100644 --- a/components/style_derive/to_animated_value.rs +++ b/components/style_derive/to_animated_value.rs @@ -9,18 +9,16 @@ use synstructure::BindStyle; pub fn derive(input: syn::DeriveInput) -> quote::Tokens { let name = &input.ident; - let (impl_generics, ty_generics, where_clause, animated_value_type) = - cg::fmap_trait_parts( - &input, - &["values", "animated", "ToAnimatedValue"], - "AnimatedValue", - ); + let trait_path = &["values", "animated", "ToAnimatedValue"]; + let (impl_generics, ty_generics, mut where_clause, animated_value_type) = + cg::fmap_trait_parts(&input, trait_path, "AnimatedValue"); - let to_body = cg::fmap_match(&input, BindStyle::Move, |field| { - quote!(::values::animated::ToAnimatedValue::to_animated_value(#field)) + let to_body = cg::fmap_match(&input, BindStyle::Move, |binding| { + where_clause.add_trait_bound(&binding.field.ty); + quote!(::values::animated::ToAnimatedValue::to_animated_value(#binding)) }); - let from_body = cg::fmap_match(&input, BindStyle::Move, |field| { - quote!(::values::animated::ToAnimatedValue::from_animated_value(#field)) + let from_body = cg::fmap_match(&input, BindStyle::Move, |binding| { + quote!(::values::animated::ToAnimatedValue::from_animated_value(#binding)) }); quote! { diff --git a/components/style_derive/to_animated_zero.rs b/components/style_derive/to_animated_zero.rs index d9e67a8a9fe..be1210faa7a 100644 --- a/components/style_derive/to_animated_zero.rs +++ b/components/style_derive/to_animated_zero.rs @@ -2,20 +2,51 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use animate::{AnimationVariantAttrs, AnimationFieldAttrs}; use cg; use quote; use syn; -use synstructure::BindStyle; +use synstructure::{self, BindStyle}; pub fn derive(input: syn::DeriveInput) -> quote::Tokens { let name = &input.ident; - let (impl_generics, ty_generics, where_clause) = cg::trait_parts( - &input, - &["values", "animated", "ToAnimatedZero"], - ); + let trait_path = &["values", "animated", "ToAnimatedZero"]; + let (impl_generics, ty_generics, mut where_clause) = + cg::trait_parts(&input, trait_path); - let to_body = cg::fmap_match(&input, BindStyle::Ref, |field| { - quote! { ::values::animated::ToAnimatedZero::to_animated_zero(#field)? } + let bind_opts = BindStyle::Ref.into(); + let to_body = synstructure::each_variant(&input, &bind_opts, |bindings, variant| { + let attrs = cg::parse_variant_attrs::<AnimationVariantAttrs>(variant); + if attrs.error { + return Some(quote! { Err(()) }); + } + let name = cg::variant_ctor(&input, variant); + let (mapped, mapped_bindings) = cg::value(&name, variant, "mapped"); + let bindings_pairs = bindings.into_iter().zip(mapped_bindings); + let mut computations = quote!(); + computations.append_all(bindings_pairs.map(|(binding, mapped_binding)| { + let field_attrs = cg::parse_field_attrs::<AnimationFieldAttrs>(&binding.field); + if field_attrs.constant { + if cg::is_parameterized(&binding.field.ty, where_clause.params, None) { + where_clause.inner.predicates.push(cg::where_predicate( + binding.field.ty.clone(), + &["std", "clone", "Clone"], + None, + )); + } + quote! { + let #mapped_binding = ::std::clone::Clone::clone(#binding); + } + } else { + where_clause.add_trait_bound(&binding.field.ty); + quote! { + let #mapped_binding = + ::values::animated::ToAnimatedZero::to_animated_zero(#binding)?; + } + } + })); + computations.append(quote! { Ok(#mapped) }); + Some(computations) }); quote! { @@ -23,9 +54,9 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { #[allow(unused_variables)] #[inline] fn to_animated_zero(&self) -> Result<Self, ()> { - Ok(match *self { + match *self { #to_body - }) + } } } } diff --git a/components/style_derive/to_computed_value.rs b/components/style_derive/to_computed_value.rs index 967f3320a37..7e4db276edd 100644 --- a/components/style_derive/to_computed_value.rs +++ b/components/style_derive/to_computed_value.rs @@ -3,24 +3,43 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use cg; -use quote; -use syn; +use quote::Tokens; +use syn::DeriveInput; use synstructure::BindStyle; -pub fn derive(input: syn::DeriveInput) -> quote::Tokens { +pub fn derive(input: DeriveInput) -> Tokens { let name = &input.ident; - let (impl_generics, ty_generics, where_clause, computed_value_type) = - cg::fmap_trait_parts( - &input, - &["values", "computed", "ToComputedValue"], - "ComputedValue", - ); + let trait_path = &["values", "computed", "ToComputedValue"]; + let (impl_generics, ty_generics, mut where_clause, computed_value_type) = + cg::fmap_trait_parts(&input, trait_path, "ComputedValue"); - let to_body = cg::fmap_match(&input, BindStyle::Ref, |field| { - quote!(::values::computed::ToComputedValue::to_computed_value(#field, context)) + let to_body = cg::fmap_match(&input, BindStyle::Ref, |binding| { + let attrs = cg::parse_field_attrs::<ComputedValueAttrs>(&binding.field); + if attrs.clone { + if cg::is_parameterized(&binding.field.ty, where_clause.params, None) { + where_clause.inner.predicates.push(cg::where_predicate( + binding.field.ty.clone(), + &["std", "clone", "Clone"], + None, + )); + } + quote! { ::std::clone::Clone::clone(#binding) } + } else { + where_clause.add_trait_bound(&binding.field.ty); + quote! { + ::values::computed::ToComputedValue::to_computed_value(#binding, context) + } + } }); - let from_body = cg::fmap_match(&input, BindStyle::Ref, |field| { - quote!(::values::computed::ToComputedValue::from_computed_value(#field)) + let from_body = cg::fmap_match(&input, BindStyle::Ref, |binding| { + let attrs = cg::parse_field_attrs::<ComputedValueAttrs>(&binding.field); + if attrs.clone { + quote! { ::std::clone::Clone::clone(#binding) } + } else { + quote! { + ::values::computed::ToComputedValue::from_computed_value(#binding) + } + } }); quote! { @@ -44,3 +63,9 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { } } } + +#[darling(attributes(compute), default)] +#[derive(Default, FromField)] +struct ComputedValueAttrs { + clone: bool, +} diff --git a/components/style_derive/to_css.rs b/components/style_derive/to_css.rs index 444c42e37da..6ae438ccbd2 100644 --- a/components/style_derive/to_css.rs +++ b/components/style_derive/to_css.rs @@ -3,29 +3,26 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use cg; -use quote; -use syn; +use quote::Tokens; +use syn::DeriveInput; use synstructure; -pub fn derive(input: syn::DeriveInput) -> quote::Tokens { +pub fn derive(input: DeriveInput) -> Tokens { let name = &input.ident; let trait_path = &["style_traits", "ToCss"]; let (impl_generics, ty_generics, mut where_clause) = cg::trait_parts(&input, trait_path); + let input_attrs = cg::parse_input_attrs::<CssInputAttrs>(&input); let style = synstructure::BindStyle::Ref.into(); let match_body = synstructure::each_variant(&input, &style, |bindings, variant| { let mut identifier = to_css_identifier(variant.ident.as_ref()); - let css_attrs = cg::parse_variant_attrs::<CssAttrs>(variant); - let separator = if css_attrs.comma { ", " } else { " " }; + let variant_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(variant); + let separator = if variant_attrs.comma { ", " } else { " " }; let mut expr = if !bindings.is_empty() { let mut expr = quote! {}; for binding in bindings { - if cg::is_parameterized(&binding.field.ty, &input.generics.ty_params) { - where_clause.predicates.push( - cg::where_predicate(binding.field.ty.clone(), trait_path), - ); - } + where_clause.add_trait_bound(&binding.field.ty); expr = quote! { #expr writer.item(#binding)?; @@ -41,7 +38,7 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { ::std::fmt::Write::write_str(dest, #identifier) } }; - if css_attrs.function { + if variant_attrs.function { identifier.push_str("("); expr = quote! { ::std::fmt::Write::write_str(dest, #identifier)?; @@ -52,7 +49,7 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { Some(expr) }); - quote! { + let mut impls = quote! { impl #impl_generics ::style_traits::ToCss for #name #ty_generics #where_clause { #[allow(unused_variables)] #[inline] @@ -65,12 +62,32 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { } } } + }; + + if input_attrs.derive_debug { + impls.append(quote! { + impl #impl_generics ::std::fmt::Debug for #name #ty_generics #where_clause { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + ::style_traits::ToCss::to_css(self, f) + } + } + }); } + + impls } -#[derive(Default, FromVariant)] #[darling(attributes(css), default)] -struct CssAttrs { +#[derive(Default, FromDeriveInput)] +struct CssInputAttrs { + derive_debug: bool, + function: bool, + comma: bool, +} + +#[darling(attributes(css), default)] +#[derive(Default, FromVariant)] +struct CssVariantAttrs { function: bool, comma: bool, } diff --git a/components/style_traits/values.rs b/components/style_traits/values.rs index 5e4c093fd73..94e59e26bbe 100644 --- a/components/style_traits/values.rs +++ b/components/style_traits/values.rs @@ -18,8 +18,12 @@ use std::fmt::{self, Write}; /// of their name; /// * unit variants whose name starts with "Moz" or "Webkit" are prepended /// with a "-"; -/// * variants with fields get serialised as the space-separated serialisations -/// of their fields. +/// * if `#[css(comma)]` is found on a variant, its fields are separated by +/// commas, otherwise, by spaces; +/// * if `#[css(function)]` is found on a variant, the variant name gets +/// serialised like unit variants and its fields are surrounded by parentheses; +/// * finally, one can put `#[css(derive_debug)]` on the whole type, to +/// implement `Debug` by a single call to `ToCss::to_css`. pub trait ToCss { /// Serialize `self` in CSS syntax, writing to `dest`. fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write; |