Make stroke-dasharray accept context-value. r?manishearth draft
authorXidorn Quan <me@upsuper.org>
Tue, 01 Aug 2017 14:38:24 +1000
changeset 618998 7c35eef13fcce14bcb1982e11bb0a00a92f858c0
parent 618997 447a82b35e031426f06dc980a969d80282177fef
child 618999 b51fab1822bb831be46f3c09e603c1088683425f
push id71529
push userxquan@mozilla.com
push dateTue, 01 Aug 2017 10:17:59 +0000
reviewersmanishearth
milestone56.0a1
Make stroke-dasharray accept context-value. r?manishearth MozReview-Commit-ID: KAjO8PyLfZb
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/inherited_svg.mako.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/computed/svg.rs
servo/components/style/values/generics/svg.rs
servo/components/style/values/specified/mod.rs
servo/components/style/values/specified/svg.rs
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -4589,56 +4589,76 @@ clip-path
             }
 
             self.gecko.mPaintOrder = order;
         }
     }
 
     ${impl_simple_copy('paint_order', 'mPaintOrder')}
 
-    pub fn set_stroke_dasharray<I>(&mut self, v: I)
-        where I: IntoIterator<Item = longhands::stroke_dasharray::computed_value::single_value::T>,
-              I::IntoIter: ExactSizeIterator
-    {
-        let v = v.into_iter();
-        unsafe {
-            bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut self.gecko, v.len() as u32);
-        }
-
-        for (mut gecko, servo) in self.gecko.mStrokeDasharray.iter_mut().zip(v) {
-            match servo {
-                Either::First(number) => gecko.set_value(CoordDataValue::Factor(number)),
-                Either::Second(lop) => gecko.set(lop),
+    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;
+
+        match v {
+            SVGStrokeDashArray::Values(v) => {
+                let v = v.into_iter();
+                self.gecko.mContextFlags &= !CONTEXT_VALUE;
+                unsafe {
+                    bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut self.gecko, v.len() as u32);
+                }
+                for (mut gecko, servo) in self.gecko.mStrokeDasharray.iter_mut().zip(v) {
+                    match servo {
+                        Either::First(number) => gecko.set_value(CoordDataValue::Factor(number)),
+                        Either::Second(lop) => gecko.set(lop),
+                    }
+                }
+            }
+            SVGStrokeDashArray::ContextValue => {
+                self.gecko.mContextFlags |= CONTEXT_VALUE;
+                unsafe {
+                    bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut self.gecko, 0);
+                }
             }
         }
     }
 
     pub fn copy_stroke_dasharray_from(&mut self, other: &Self) {
+        use gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE;
         unsafe {
             bindings::Gecko_nsStyleSVG_CopyDashArray(&mut self.gecko, &other.gecko);
         }
+        self.gecko.mContextFlags =
+            (self.gecko.mContextFlags & !CONTEXT_VALUE) |
+            (other.gecko.mContextFlags & CONTEXT_VALUE);
     }
 
     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;
+
+        if self.gecko.mContextFlags & CONTEXT_VALUE != 0 {
+            debug_assert_eq!(self.gecko.mStrokeDasharray.len(), 0);
+            return SVGStrokeDashArray::ContextValue;
+        }
         let mut vec = vec![];
         for gecko in self.gecko.mStrokeDasharray.iter() {
             match gecko.as_value() {
                 CoordDataValue::Factor(number) => vec.push(Either::First(number)),
                 CoordDataValue::Coord(coord) =>
                     vec.push(Either::Second(LengthOrPercentage::Length(Au(coord)))),
                 CoordDataValue::Percent(p) =>
                     vec.push(Either::Second(LengthOrPercentage::Percentage(Percentage(p)))),
                 CoordDataValue::Calc(calc) =>
                     vec.push(Either::Second(LengthOrPercentage::Calc(calc.into()))),
                 _ => unreachable!(),
             }
         }
-        longhands::stroke_dasharray::computed_value::T(vec)
+        SVGStrokeDashArray::Values(vec)
     }
 
     #[allow(non_snake_case)]
     pub fn set__moz_context_properties<I>(&mut self, v: I)
         where I: IntoIterator<Item = longhands::_moz_context_properties::computed_value::single_value::T>,
               I::IntoIter: ExactSizeIterator
     {
         let v = v.into_iter();
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -41,17 +41,17 @@ use values::animated::effects::FilterLis
 use values::animated::effects::TextShadowList as AnimatedTextShadowList;
 use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
 use values::computed::{BorderCornerRadius, ClipRect};
 use values::computed::{CalcLengthOrPercentage, Color, Context, ComputedValueAsSpecified};
 use values::computed::{LengthOrPercentage, MaxLength, MozLength, Percentage, ToComputedValue};
 use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
 use values::generics::effects::Filter;
 use values::generics::position as generic_position;
-use values::generics::svg::{SVGLength, SVGPaint, SVGPaintKind};
+use values::generics::svg::{SVGLength, SVGPaint, SVGPaintKind, SVGStrokeDashArray};
 
 /// A trait used to implement various procedures used during animation.
 pub trait Animatable: Sized {
     /// Performs a weighted sum of this value and |other|. This is used for
     /// interpolation and addition of animation values.
     fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
         -> Result<Self, ()>;
 
@@ -3069,16 +3069,58 @@ impl<LengthType> ToAnimatedZero for SVGL
     fn to_animated_zero(&self) -> Result<Self, ()> {
         match self {
             &SVGLength::Length(ref length) => length.to_animated_zero().map(SVGLength::Length),
             &SVGLength::ContextValue => Ok(SVGLength::ContextValue),
         }
     }
 }
 
+impl<LengthType> Animatable for SVGStrokeDashArray<LengthType>
+    where LengthType : RepeatableListAnimatable + Clone
+{
+    #[inline]
+    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+        match (self, other) {
+            (&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other))=> {
+                this.add_weighted(other, self_portion, other_portion)
+                    .map(SVGStrokeDashArray::Values)
+            }
+            _ => {
+                Ok(if self_portion > other_portion { self.clone() } else { other.clone() })
+            }
+        }
+    }
+
+    #[inline]
+    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+        match (self, other) {
+            (&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other)) => {
+                this.compute_distance(other)
+            }
+            _ => Err(())
+        }
+    }
+}
+
+impl<LengthType> ToAnimatedZero for SVGStrokeDashArray<LengthType>
+    where LengthType : ToAnimatedZero + Clone
+{
+    #[inline]
+    fn to_animated_zero(&self) -> Result<Self, ()> {
+        match self {
+            &SVGStrokeDashArray::Values(ref values) => {
+                values.iter().map(ToAnimatedZero::to_animated_zero)
+                      .collect::<Result<Vec<_>, ()>>().map(SVGStrokeDashArray::Values)
+            }
+            &SVGStrokeDashArray::ContextValue => Ok(SVGStrokeDashArray::ContextValue),
+        }
+    }
+}
+
 <%
     FILTER_FUNCTIONS = [ 'Blur', 'Brightness', 'Contrast', 'Grayscale',
                          'HueRotate', 'Invert', 'Opacity', 'Saturate',
                          'Sepia' ]
 %>
 
 /// https://drafts.fxtf.org/filters/#animation-of-filters
 fn add_weighted_filter_function_impl(from: &AnimatedFilter,
--- a/servo/components/style/properties/longhand/inherited_svg.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_svg.mako.rs
@@ -85,23 +85,20 @@
                           spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty")}
 
 ${helpers.predefined_type("stroke-opacity", "Opacity", "1.0",
                           products="gecko", animation_value_type="ComputedValue",
                           spec="https://www.w3.org/TR/SVG11/painting.html#StrokeOpacityProperty")}
 
 ${helpers.predefined_type(
     "stroke-dasharray",
-    "LengthOrPercentageOrNumber",
-    None,
-    "parse_non_negative",
-    vector=True,
+    "SVGStrokeDashArray",
+    "Default::default()",
     products="gecko",
     animation_value_type="ComputedValue",
-    separator="CommaWithSpace",
     spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing",
 )}
 
 ${helpers.predefined_type(
     "stroke-dashoffset", "SVGLength",
     "Au(0).into()",
     products="gecko",
     animation_value_type="ComputedValue",
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -39,17 +39,17 @@ pub use super::{Auto, Either, None_};
 #[cfg(feature = "gecko")]
 pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use super::specified::{BorderStyle, UrlOrNone};
 pub use super::generics::grid::GridLine;
 pub use super::specified::url::SpecifiedUrl;
 pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNone, LengthOrNumber, LengthOrPercentage};
 pub use self::length::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone, MaxLength, MozLength, Percentage};
 pub use self::position::Position;
-pub use self::svg::{SVGLength, SVGPaint, SVGPaintKind};
+pub use self::svg::{SVGLength, SVGPaint, SVGPaintKind, SVGStrokeDashArray};
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing};
 pub use self::transform::{TimingFunction, TransformOrigin};
 
 pub mod background;
 pub mod basic_shape;
 pub mod border;
 pub mod color;
 pub mod effects;
--- a/servo/components/style/values/computed/svg.rs
+++ b/servo/components/style/values/computed/svg.rs
@@ -37,8 +37,17 @@ impl SVGPaint {
 /// <length> | <percentage> | <number> | context-value
 pub type SVGLength = generic::SVGLength<LengthOrPercentageOrNumber>;
 
 impl From<Au> for SVGLength {
     fn from(length: Au) -> Self {
         generic::SVGLength::Length(Either::Second(length.into()))
     }
 }
+
+/// [ <length> | <percentage> | <number> ]# | context-value
+pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<LengthOrPercentageOrNumber>;
+
+impl Default for SVGStrokeDashArray {
+    fn default() -> Self {
+        generic::SVGStrokeDashArray::Values(vec![])
+    }
+}
--- a/servo/components/style/values/generics/svg.rs
+++ b/servo/components/style/values/generics/svg.rs
@@ -99,8 +99,18 @@ impl<ColorType: Parse> Parse for SVGPain
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Copy, Debug, PartialEq, HasViewportPercentage, ToComputedValue, ToCss)]
 pub enum SVGLength<LengthType> {
     /// `<length> | <percentage> | <number>`
     Length(LengthType),
     /// `context-value`
     ContextValue,
 }
+
+/// Generic value for stroke-dasharray.
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Clone, Debug, PartialEq, HasViewportPercentage, ToComputedValue)]
+pub enum SVGStrokeDashArray<LengthType> {
+    /// `[ <length> | <percentage> | <number> ]#`
+    Values(Vec<LengthType>),
+    /// `context-value`
+    ContextValue,
+}
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -38,17 +38,17 @@ pub use self::image::{ColorStop, EndingS
 pub use self::image::{GradientItem, GradientKind, Image, ImageLayer, MozImageRect};
 pub use self::length::{AbsoluteLength, CalcLengthOrPercentage, CharacterWidth};
 pub use self::length::{FontRelativeLength, Length, LengthOrNone, LengthOrNumber};
 pub use self::length::{LengthOrPercentage, LengthOrPercentageOrAuto};
 pub use self::length::{LengthOrPercentageOrNone, MaxLength, MozLength};
 pub use self::length::{NoCalcLength, Percentage, ViewportPercentageLength};
 pub use self::rect::LengthOrNumberRect;
 pub use self::position::{Position, PositionComponent};
-pub use self::svg::{SVGLength, SVGPaint, SVGPaintKind};
+pub use self::svg::{SVGLength, SVGPaint, SVGPaintKind, SVGStrokeDashArray};
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing};
 pub use self::transform::{TimingFunction, TransformOrigin};
 pub use super::generics::grid::GridLine;
 pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
 
 #[cfg(feature = "gecko")]
 pub mod align;
 pub mod background;
--- a/servo/components/style/values/specified/svg.rs
+++ b/servo/components/style/values/specified/svg.rs
@@ -1,17 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Specified types for SVG properties.
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
-use style_traits::{ParseError, StyleParseError};
+use std::fmt;
+use style_traits::{CommaWithSpace, ParseError, Separator, StyleParseError, ToCss};
 use values::generics::svg as generic;
 use values::specified::LengthOrPercentageOrNumber;
 use values::specified::color::RGBAColor;
 
 /// Specified SVG Paint value
 pub type SVGPaint = generic::SVGPaint<RGBAColor>;
 
 no_viewport_percentage!(SVGPaint);
@@ -61,8 +62,49 @@ impl SVGLength {
     }
 }
 
 impl From<LengthOrPercentageOrNumber> for SVGLength {
     fn from(length: LengthOrPercentageOrNumber) -> Self {
         generic::SVGLength::Length(length)
     }
 }
+
+/// [ <length> | <percentage> | <number> ]# | context-value
+pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<LengthOrPercentageOrNumber>;
+
+impl Parse for SVGStrokeDashArray {
+    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
+                     -> Result<Self, ParseError<'i>> {
+        if let Ok(values) = input.try(|i| CommaWithSpace::parse(i, |i| {
+            LengthOrPercentageOrNumber::parse_non_negative(context, i)
+        })) {
+            Ok(generic::SVGStrokeDashArray::Values(values))
+        } else if let Ok(_) = input.try(|i| i.expect_ident_matching("none")) {
+            Ok(generic::SVGStrokeDashArray::Values(vec![]))
+        } else {
+            parse_context_value(input, generic::SVGStrokeDashArray::ContextValue)
+        }
+    }
+}
+
+impl ToCss for SVGStrokeDashArray {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+        match self {
+            &generic::SVGStrokeDashArray::Values(ref values) => {
+                let mut iter = values.iter();
+                if let Some(first) = iter.next() {
+                    first.to_css(dest)?;
+                    for item in iter {
+                        dest.write_str(", ")?;
+                        item.to_css(dest)?;
+                    }
+                    Ok(())
+                } else {
+                    dest.write_str("none")
+                }
+            }
+            &generic::SVGStrokeDashArray::ContextValue => {
+                dest.write_str("context-value")
+            }
+        }
+    }
+}