From 1c574cf93b1aaab41efe56ceedfd8e0c1c63b931 Mon Sep 17 00:00:00 2001 From: Mantaroh Yoshinaga Date: Wed, 12 Jul 2017 13:37:53 +0900 Subject: [PATCH] Add SVGLengthOrPercentageOrNumber for stroke-*. We need to use enum instead of Either since we can't interpolate stroke-* between unitless length and unit length(e.g. '5' -> '10px'). This coomit make following: * Introduce SVGLengthOrPercentageOrNumber on computed and specified values. * Make SVGLengthOrPercentageOrNumber animatable. * Make stroke-dasharray not-accumulate. --- components/style/properties/gecko.mako.rs | 50 +++--- .../helpers/animated_properties.mako.rs | 149 +++++++++++++++++- components/style/values/computed/length.rs | 7 + components/style/values/computed/svg.rs | 43 +++-- components/style/values/generics/svg.rs | 49 ++++++ components/style/values/specified/svg.rs | 33 ++-- 6 files changed, 286 insertions(+), 45 deletions(-) diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 682e2cfb39a8..6f1b0d2a74ff 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -588,7 +588,7 @@ def set_gecko_property(ffi_name, expr): // set on mContextFlags, and the length field is set to the initial value. pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { - use values::generics::svg::SVGLength; + use values::generics::svg::{SVGLength, SvgLengthOrPercentageOrNumber}; use gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE; let length = match v { SVGLength::Length(length) => { @@ -604,9 +604,10 @@ def set_gecko_property(ffi_name, expr): } }; match length { - Either::First(number) => - self.gecko.${gecko_ffi_name}.set_value(CoordDataValue::Factor(From::from(number))), - Either::Second(lop) => self.gecko.${gecko_ffi_name}.set(lop), + SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop) => + self.gecko.${gecko_ffi_name}.set(lop), + SvgLengthOrPercentageOrNumber::Number(num) => + self.gecko.${gecko_ffi_name}.set_value(CoordDataValue::Factor(num.into())), } } @@ -623,21 +624,28 @@ def set_gecko_property(ffi_name, expr): } pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { - use values::generics::svg::SVGLength; + use values::generics::svg::{SVGLength, SvgLengthOrPercentageOrNumber}; use values::computed::LengthOrPercentage; use gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE; if (self.gecko.mContextFlags & CONTEXT_VALUE) != 0 { return SVGLength::ContextValue; } let length = match self.gecko.${gecko_ffi_name}.as_value() { - CoordDataValue::Factor(number) => Either::First(From::from(number)), - CoordDataValue::Coord(coord) => Either::Second(From::from(LengthOrPercentage::Length(Au(coord)))), - CoordDataValue::Percent(p) => Either::Second(From::from(LengthOrPercentage::Percentage(Percentage(p)))), - CoordDataValue::Calc(calc) => Either::Second(From::from(LengthOrPercentage::Calc(calc.into()))), + CoordDataValue::Factor(number) => + SvgLengthOrPercentageOrNumber::Number(number), + CoordDataValue::Coord(coord) => + SvgLengthOrPercentageOrNumber::LengthOrPercentage( + LengthOrPercentage::Length(Au(coord))), + CoordDataValue::Percent(p) => + SvgLengthOrPercentageOrNumber::LengthOrPercentage( + LengthOrPercentage::Percentage(Percentage(p))), + CoordDataValue::Calc(calc) => + SvgLengthOrPercentageOrNumber::LengthOrPercentage( + LengthOrPercentage::Calc(calc.into())), _ => unreachable!("Unexpected coordinate {:?} in ${ident}", self.gecko.${gecko_ffi_name}.as_value()), }; - SVGLength::Length(length) + SVGLength::Length(length.into()) } @@ -5141,7 +5149,7 @@ clip-path pub fn set_stroke_dasharray(&mut self, v: longhands::stroke_dasharray::computed_value::T) { use gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE; - use values::generics::svg::SVGStrokeDashArray; + use values::generics::svg::{SVGStrokeDashArray, SvgLengthOrPercentageOrNumber}; match v { SVGStrokeDashArray::Values(v) => { @@ -5152,8 +5160,10 @@ clip-path } for (gecko, servo) in self.gecko.mStrokeDasharray.iter_mut().zip(v) { match servo { - Either::First(number) => gecko.set_value(CoordDataValue::Factor(number.into())), - Either::Second(lop) => gecko.set(lop), + SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop) => + gecko.set(lop), + SvgLengthOrPercentageOrNumber::Number(num) => + gecko.set_value(CoordDataValue::Factor(num.into())), } } } @@ -5183,7 +5193,7 @@ clip-path pub fn clone_stroke_dasharray(&self) -> longhands::stroke_dasharray::computed_value::T { use gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE; use values::computed::LengthOrPercentage; - use values::generics::svg::SVGStrokeDashArray; + use values::generics::svg::{SVGStrokeDashArray, SvgLengthOrPercentageOrNumber}; if self.gecko.mContextFlags & CONTEXT_VALUE != 0 { debug_assert_eq!(self.gecko.mStrokeDasharray.len(), 0); @@ -5192,13 +5202,17 @@ clip-path let mut vec = vec![]; for gecko in self.gecko.mStrokeDasharray.iter() { match gecko.as_value() { - CoordDataValue::Factor(number) => vec.push(Either::First(number.into())), + CoordDataValue::Factor(number) => + vec.push(SvgLengthOrPercentageOrNumber::Number(number.into())), CoordDataValue::Coord(coord) => - vec.push(Either::Second(LengthOrPercentage::Length(Au(coord)).into())), + vec.push(SvgLengthOrPercentageOrNumber::LengthOrPercentage( + LengthOrPercentage::Length(Au(coord)).into())), CoordDataValue::Percent(p) => - vec.push(Either::Second(LengthOrPercentage::Percentage(Percentage(p)).into())), + vec.push(SvgLengthOrPercentageOrNumber::LengthOrPercentage( + LengthOrPercentage::Percentage(Percentage(p)).into())), CoordDataValue::Calc(calc) => - vec.push(Either::Second(LengthOrPercentage::Calc(calc.into()).into())), + vec.push(SvgLengthOrPercentageOrNumber::LengthOrPercentage( + LengthOrPercentage::Calc(calc.into()).into())), _ => unreachable!(), } } diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 38ac83a941d8..1e3558482ec1 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -43,18 +43,20 @@ use values::animated::effects::BoxShadowList as AnimatedBoxShadowList; use values::animated::effects::Filter as AnimatedFilter; use values::animated::effects::FilterList as AnimatedFilterList; use values::animated::effects::TextShadowList as AnimatedTextShadowList; -use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; -use values::computed::{BorderCornerRadius, ClipRect}; -use values::computed::{CalcLengthOrPercentage, Context, ComputedValueAsSpecified, ComputedUrl}; -use values::computed::{LengthOrPercentage, MaxLength, MozLength, Percentage, ToComputedValue}; -use values::computed::{NonNegativeAu, NonNegativeNumber, PositiveIntegerOrAuto}; +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::{NonNegativeNumber, Number, NumberOrPercentage, Percentage}; +use values::computed::{PositiveIntegerOrAuto, ToComputedValue}; use values::computed::length::{NonNegativeLengthOrAuto, NonNegativeLengthOrNormal}; use values::computed::length::NonNegativeLengthOrPercentage; use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::{GreaterThanOrEqualToOne, NonNegative}; use values::generics::effects::Filter; use values::generics::position as generic_position; -use values::generics::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray}; +use values::generics::svg::{SVGLength, SvgLengthOrPercentageOrNumber, SVGPaint}; +use values::generics::svg::{SVGPaintKind, SVGStrokeDashArray, SVGOpacity}; /// A trait used to implement various procedures used during animation. pub trait Animatable: Sized { @@ -781,6 +783,7 @@ impl ToAnimatedZero for AnimationValue { impl RepeatableListAnimatable for LengthOrPercentage {} impl RepeatableListAnimatable for Either {} impl RepeatableListAnimatable for Either {} +impl RepeatableListAnimatable for SvgLengthOrPercentageOrNumber {} macro_rules! repeated_vec_impl { ($($ty:ty),*) => { @@ -1016,7 +1019,12 @@ impl Animatable for LengthOrPercentage { impl ToAnimatedZero for LengthOrPercentage { #[inline] fn to_animated_zero(&self) -> Result { - Ok(LengthOrPercentage::zero()) + match self { + &LengthOrPercentage::Length(_) | &LengthOrPercentage::Calc(_) => + Ok(LengthOrPercentage::zero()), + &LengthOrPercentage::Percentage(_) => + Ok(LengthOrPercentage::Percentage(Percentage::zero())), + } } } @@ -2496,6 +2504,120 @@ impl ToAnimatedZero for IntermediateSVGPaintKind { } } +impl From for NumberOrPercentage { + fn from(lop: NonNegativeLengthOrPercentage) -> NumberOrPercentage { + lop.0.into() + } +} + +impl From for NumberOrPercentage { + fn from(num: NonNegativeNumber) -> NumberOrPercentage { + num.0.into() + } +} + +impl From for NumberOrPercentage { + fn from(lop: LengthOrPercentage) -> NumberOrPercentage { + match lop { + LengthOrPercentage::Length(len) => NumberOrPercentage::Number(len.to_f32_px()), + LengthOrPercentage::Percentage(p) => NumberOrPercentage::Percentage(p), + LengthOrPercentage::Calc(_) => { + panic!("We dont't expected calc interpolation for SvgLengthOrPercentageOrNumber"); + }, + } + } +} + +impl From for NumberOrPercentage { + fn from(num: Number) -> NumberOrPercentage { + NumberOrPercentage::Number(num) + } +} + +fn convert_to_number_or_percentage( + from: SvgLengthOrPercentageOrNumber) + -> NumberOrPercentage + where LengthOrPercentageType: Into, + NumberType: Into +{ + match from { + SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop) => { + lop.into() + } + SvgLengthOrPercentageOrNumber::Number(num) => { + num.into() + } + } +} + +fn convert_from_number_or_percentage( + from: NumberOrPercentage) + -> SvgLengthOrPercentageOrNumber + where LengthOrPercentageType: From, + NumberType: From +{ + match from { + NumberOrPercentage::Number(num) => + SvgLengthOrPercentageOrNumber::Number(num.into()), + NumberOrPercentage::Percentage(p) => + SvgLengthOrPercentageOrNumber::LengthOrPercentage( + (LengthOrPercentage::Percentage(p)).into()) + } +} + +impl Animatable for + SvgLengthOrPercentageOrNumber + where LengthOrPercentageType: Animatable + Into + From + Copy, + NumberType: Animatable + Into + From, + SvgLengthOrPercentageOrNumber: Copy, + LengthOrPercentage: From +{ + #[inline] + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + if self.has_calc() || other.has_calc() { + // TODO: We need to treat calc value. + // https://bugzilla.mozilla.org/show_bug.cgi?id=1386967 + return Err(()); + } + + let from_value = convert_to_number_or_percentage(*self); + let to_value = convert_to_number_or_percentage(*other); + + match (from_value, to_value) { + (NumberOrPercentage::Number(from), + NumberOrPercentage::Number(to)) => { + from.add_weighted(&to, self_portion, other_portion) + .map(|num| NumberOrPercentage::Number(num)) + .map(|nop| convert_from_number_or_percentage(nop)) + }, + (NumberOrPercentage::Percentage(from), + NumberOrPercentage::Percentage(to)) => { + from.add_weighted(&to, self_portion, other_portion) + .map(|p| NumberOrPercentage::Percentage(p)) + .map(|nop| convert_from_number_or_percentage(nop)) + }, + _ => Err(()), + } + } +} + +impl ToAnimatedZero for + SvgLengthOrPercentageOrNumber + where LengthOrPercentageType: ToAnimatedZero, NumberType: ToAnimatedZero +{ + #[inline] + fn to_animated_zero(&self) -> Result { + match self { + &SvgLengthOrPercentageOrNumber::LengthOrPercentage(ref lop) => + lop.to_animated_zero() + .map(SvgLengthOrPercentageOrNumber::LengthOrPercentage), + &SvgLengthOrPercentageOrNumber::Number(ref num) => + num.to_animated_zero() + .map(SvgLengthOrPercentageOrNumber::Number), + } + } +} + impl Animatable for SVGLength where LengthType: Animatable + Clone { @@ -2522,6 +2644,7 @@ impl ToAnimatedZero for SVGLength where LengthType : ToA } } +/// https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty impl Animatable for SVGStrokeDashArray where LengthType : RepeatableListAnimatable + Clone { @@ -2537,6 +2660,18 @@ impl Animatable for SVGStrokeDashArray } } } + + /// stroke-dasharray is non-additive + #[inline] + fn add(&self, _other: &Self) -> Result { + Err(()) + } + + /// stroke-dasharray is non-additive + #[inline] + fn accumulate(&self, _other: &Self, _count: u64) -> Result { + Err(()) + } } impl ToAnimatedZero for SVGStrokeDashArray diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index fbe3b56e7248..a8287fa9991f 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -606,6 +606,13 @@ impl From for NonNegativeLengthOrPercentage { } } +impl From for LengthOrPercentage { + #[inline] + fn from(lop: NonNegativeLengthOrPercentage) -> LengthOrPercentage { + lop.0 + } +} + impl NonNegativeLengthOrPercentage { /// Get zero value. #[inline] diff --git a/components/style/values/computed/svg.rs b/components/style/values/computed/svg.rs index 88cdbd5009e7..b4d66b3cec56 100644 --- a/components/style/values/computed/svg.rs +++ b/components/style/values/computed/svg.rs @@ -5,10 +5,10 @@ //! Computed types for SVG properties. use app_units::Au; -use values::{Either, RGBA}; -use values::computed::{LengthOrPercentageOrNumber, Opacity}; -use values::computed::{NonNegativeAu, NonNegativeLengthOrPercentageOrNumber}; -use values::computed::ComputedUrl; +use values::RGBA; +use values::computed::{ComputedUrl, LengthOrPercentage, NonNegativeAu}; +use values::computed::{NonNegativeNumber, NonNegativeLengthOrPercentage, Number}; +use values::computed::Opacity; use values::generics::svg as generic; /// Computed SVG Paint value @@ -36,26 +36,51 @@ impl SVGPaint { } } +/// A value of | | for stroke-dashoffset. +/// https://www.w3.org/TR/SVG11/painting.html#StrokeProperties +pub type SvgLengthOrPercentageOrNumber = + generic::SvgLengthOrPercentageOrNumber; + /// | | | context-value -pub type SVGLength = generic::SVGLength; +pub type SVGLength = generic::SVGLength; impl From for SVGLength { fn from(length: Au) -> Self { - generic::SVGLength::Length(Either::Second(length.into())) + generic::SVGLength::Length( + generic::SvgLengthOrPercentageOrNumber::LengthOrPercentage(length.into())) + } +} + +/// A value of | | for stroke-width/stroke-dasharray. +/// https://www.w3.org/TR/SVG11/painting.html#StrokeProperties +pub type NonNegativeSvgLengthOrPercentageOrNumber = + generic::SvgLengthOrPercentageOrNumber; + +impl Into for SvgLengthOrPercentageOrNumber { + fn into(self) -> NonNegativeSvgLengthOrPercentageOrNumber { + match self { + generic::SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop) =>{ + generic::SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop.into()) + }, + generic::SvgLengthOrPercentageOrNumber::Number(num) => { + generic::SvgLengthOrPercentageOrNumber::Number(num.into()) + }, + } } } /// An non-negative wrapper of SVGLength. -pub type SVGWidth = generic::SVGLength; +pub type SVGWidth = generic::SVGLength; impl From for SVGWidth { fn from(length: NonNegativeAu) -> Self { - generic::SVGLength::Length(Either::Second(length.into())) + generic::SVGLength::Length( + generic::SvgLengthOrPercentageOrNumber::LengthOrPercentage(length.into())) } } /// [ | | ]# | context-value -pub type SVGStrokeDashArray = generic::SVGStrokeDashArray; +pub type SVGStrokeDashArray = generic::SVGStrokeDashArray; impl Default for SVGStrokeDashArray { fn default() -> Self { diff --git a/components/style/values/generics/svg.rs b/components/style/values/generics/svg.rs index 60f76950aee0..631e6374e2d4 100644 --- a/components/style/values/generics/svg.rs +++ b/components/style/values/generics/svg.rs @@ -8,6 +8,8 @@ use cssparser::Parser; use parser::{Parse, ParserContext}; use std::fmt; use style_traits::{ParseError, StyleParseError, ToCss}; +use values::computed::length::LengthOrPercentage; + /// An SVG paint value /// @@ -95,6 +97,53 @@ impl Parse for SVGPaint | | for svg which allow unitless length. +/// https://www.w3.org/TR/SVG11/painting.html#StrokeProperties +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Copy, Debug, PartialEq, ToCss, HasViewportPercentage)] +#[derive(ToComputedValue, ToAnimatedValue, ComputeSquaredDistance)] +pub enum SvgLengthOrPercentageOrNumber { + /// | + LengthOrPercentage(LengthOrPercentageType), + /// + Number(NumberType), +} + +impl SvgLengthOrPercentageOrNumber + where LengthOrPercentage: From, + LengthOrPercentageType: Copy +{ + /// return true if this struct has calc value. + pub fn has_calc(&self) -> bool { + match self { + &SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop) => { + match LengthOrPercentage::from(lop) { + LengthOrPercentage::Calc(_) => true, + _ => false, + } + }, + _ => false, + } + } +} + +/// Parsing the SvgLengthOrPercentageOrNumber. At first, we need to parse number +/// since prevent converting to the length. +impl Parse for + SvgLengthOrPercentageOrNumber { + fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) + -> Result> { + if let Ok(num) = input.try(|i| NumberType::parse(context, i)) { + return Ok(SvgLengthOrPercentageOrNumber::Number(num)); + } + + if let Ok(lop) = input.try(|i| LengthOrPercentageType::parse(context, i)) { + return Ok(SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop)); + } + Err(StyleParseError::UnspecifiedError.into()) + } +} + /// An SVG length value supports `context-value` in addition to length. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq)] diff --git a/components/style/values/specified/svg.rs b/components/style/values/specified/svg.rs index 980162ccd2da..178b68927939 100644 --- a/components/style/values/specified/svg.rs +++ b/components/style/values/specified/svg.rs @@ -8,7 +8,8 @@ use cssparser::Parser; use parser::{Parse, ParserContext}; use style_traits::{CommaWithSpace, ParseError, Separator, StyleParseError}; use values::generics::svg as generic; -use values::specified::{LengthOrPercentageOrNumber, NonNegativeLengthOrPercentageOrNumber, Opacity, SpecifiedUrl}; +use values::specified::{LengthOrPercentage, NonNegativeLengthOrPercentage, NonNegativeNumber}; +use values::specified::{Number, Opacity, SpecifiedUrl}; use values::specified::color::RGBAColor; /// Specified SVG Paint value @@ -42,50 +43,60 @@ fn parse_context_value<'i, 't, T>(input: &mut Parser<'i, 't>, value: T) Err(StyleParseError::UnspecifiedError.into()) } +/// A value of | | for stroke-dashoffset. +/// https://www.w3.org/TR/SVG11/painting.html#StrokeProperties +pub type SvgLengthOrPercentageOrNumber = + generic::SvgLengthOrPercentageOrNumber; + /// | | | context-value -pub type SVGLength = generic::SVGLength; +pub type SVGLength = generic::SVGLength; impl Parse for SVGLength { fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { - input.try(|i| LengthOrPercentageOrNumber::parse(context, i)) + input.try(|i| SvgLengthOrPercentageOrNumber::parse(context, i)) .map(Into::into) .or_else(|_| parse_context_value(input, generic::SVGLength::ContextValue)) } } -impl From for SVGLength { - fn from(length: LengthOrPercentageOrNumber) -> Self { +impl From for SVGLength { + fn from(length: SvgLengthOrPercentageOrNumber) -> Self { generic::SVGLength::Length(length) } } +/// A value of | | for stroke-width/stroke-dasharray. +/// https://www.w3.org/TR/SVG11/painting.html#StrokeProperties +pub type NonNegativeSvgLengthOrPercentageOrNumber = + generic::SvgLengthOrPercentageOrNumber; + /// A non-negative version of SVGLength. -pub type SVGWidth = generic::SVGLength; +pub type SVGWidth = generic::SVGLength; impl Parse for SVGWidth { fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { - input.try(|i| NonNegativeLengthOrPercentageOrNumber::parse(context, i)) + input.try(|i| NonNegativeSvgLengthOrPercentageOrNumber::parse(context, i)) .map(Into::into) .or_else(|_| parse_context_value(input, generic::SVGLength::ContextValue)) } } -impl From for SVGWidth { - fn from(length: NonNegativeLengthOrPercentageOrNumber) -> Self { +impl From for SVGWidth { + fn from(length: NonNegativeSvgLengthOrPercentageOrNumber) -> Self { generic::SVGLength::Length(length) } } /// [ | | ]# | context-value -pub type SVGStrokeDashArray = generic::SVGStrokeDashArray; +pub type SVGStrokeDashArray = generic::SVGStrokeDashArray; impl Parse for SVGStrokeDashArray { fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { if let Ok(values) = input.try(|i| CommaWithSpace::parse(i, |i| { - NonNegativeLengthOrPercentageOrNumber::parse(context, i) + NonNegativeSvgLengthOrPercentageOrNumber::parse(context, i) })) { Ok(generic::SVGStrokeDashArray::Values(values)) } else if let Ok(_) = input.try(|i| i.expect_ident_matching("none")) {