Add SVGLength which accepts context-value, and use it for stroke-{width,dashoffset}. r?manishearth draft
authorXidorn Quan <me@upsuper.org>
Tue, 01 Aug 2017 11:08:21 +1000
changeset 618997 447a82b35e031426f06dc980a969d80282177fef
parent 618996 eb2cab7c494937d0e73dfe7470f07369a89867e6
child 618998 7c35eef13fcce14bcb1982e11bb0a00a92f858c0
push id71529
push userxquan@mozilla.com
push dateTue, 01 Aug 2017 10:17:59 +0000
reviewersmanishearth
milestone56.0a1
Add SVGLength which accepts context-value, and use it for stroke-{width,dashoffset}. r?manishearth MozReview-Commit-ID: JP1Ib1ohNNg
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
@@ -556,16 +556,67 @@ def set_gecko_property(ffi_name, expr):
     % if need_clone:
         #[allow(non_snake_case)]
         pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
             convert_nscolor_to_rgba(${get_gecko_property(gecko_ffi_name)})
         }
     % endif
 </%def>
 
+<%def name="impl_svg_length(ident, gecko_ffi_name, need_clone=False)">
+    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
+        use values::generics::svg::SVGLength;
+        use gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE;
+        self.gecko.mContextFlags &= !CONTEXT_VALUE;
+        let length = match v {
+            SVGLength::Length(length) => {
+                self.gecko.mContextFlags &= !CONTEXT_VALUE;
+                length
+            }
+            SVGLength::ContextValue => {
+                self.gecko.mContextFlags |= CONTEXT_VALUE;
+                match longhands::${ident}::get_initial_value() {
+                    SVGLength::Length(length) => length,
+                    _ => unreachable!(),
+                }
+            }
+        };
+        match length {
+            Either::First(number) =>
+                self.gecko.${gecko_ffi_name}.set_value(CoordDataValue::Factor(number)),
+            Either::Second(lop) => self.gecko.${gecko_ffi_name}.set(lop),
+        }
+    }
+
+    pub fn copy_${ident}_from(&mut self, other: &Self) {
+        use gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE;
+        self.gecko.${gecko_ffi_name}.copy_from(&other.gecko.${gecko_ffi_name});
+        self.gecko.mContextFlags =
+            (self.gecko.mContextFlags & !CONTEXT_VALUE) |
+            (other.gecko.mContextFlags & CONTEXT_VALUE);
+    }
+
+    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
+        use values::generics::svg::SVGLength;
+        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(number),
+            CoordDataValue::Coord(coord) => Either::Second(LengthOrPercentage::Length(Au(coord))),
+            CoordDataValue::Percent(p) => Either::Second(LengthOrPercentage::Percentage(Percentage(p))),
+            CoordDataValue::Calc(calc) => Either::Second(LengthOrPercentage::Calc(calc.into())),
+            _ => unreachable!(),
+        };
+        SVGLength::Length(length)
+    }
+</%def>
+
 <%def name="impl_svg_paint(ident, gecko_ffi_name, need_clone=False)">
     #[allow(non_snake_case)]
     pub fn set_${ident}(&mut self, mut v: longhands::${ident}::computed_value::T) {
         use values::generics::svg::SVGPaintKind;
         use self::structs::nsStyleSVGPaintType;
         use self::structs::nsStyleSVGFallbackType;
 
         let ref mut paint = ${get_gecko_property(gecko_ffi_name)};
@@ -911,16 +962,17 @@ impl Clone for ${style_struct.gecko_stru
         "LengthOrNormal": impl_style_coord,
         "MaxLength": impl_style_coord,
         "MozLength": impl_style_coord,
         "Number": impl_simple,
         "Integer": impl_simple,
         "Opacity": impl_simple,
         "Color": impl_color,
         "RGBAColor": impl_rgba_color,
+        "SVGLength": impl_svg_length,
         "SVGPaint": impl_svg_paint,
         "UrlOrNone": impl_css_url,
     }
 
     def longhand_method(longhand):
         args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name,
                     need_clone=longhand.need_clone)
 
@@ -4511,17 +4563,17 @@ clip-path
 
     <% impl_common_image_layer_properties("mask") %>
     <% impl_simple_image_array_property("mode", "mask", "mMask", "mMaskMode", "SVG") %>
     <% impl_simple_image_array_property("composite", "mask", "mMask", "mComposite", "SVG") %>
     <% impl_shape_source("clip_path", "mClipPath") %>
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="InheritedSVG"
-                  skip_longhands="paint-order stroke-dasharray stroke-dashoffset stroke-width -moz-context-properties"
+                  skip_longhands="paint-order stroke-dasharray -moz-context-properties"
                   skip_additionals="*">
     pub fn set_paint_order(&mut self, v: longhands::paint_order::computed_value::T) {
         use self::longhands::paint_order;
 
         if v.0 == 0 {
             self.gecko.mPaintOrder = structs::NS_STYLE_PAINT_ORDER_NORMAL as u8;
         } else {
             let mut order = 0;
@@ -4579,56 +4631,16 @@ clip-path
                 CoordDataValue::Calc(calc) =>
                     vec.push(Either::Second(LengthOrPercentage::Calc(calc.into()))),
                 _ => unreachable!(),
             }
         }
         longhands::stroke_dasharray::computed_value::T(vec)
     }
 
-    pub fn set_stroke_dashoffset(&mut self, v: longhands::stroke_dashoffset::computed_value::T) {
-        match v {
-            Either::First(number) => self.gecko.mStrokeDashoffset.set_value(CoordDataValue::Factor(number)),
-            Either::Second(lop) => self.gecko.mStrokeDashoffset.set(lop),
-        }
-    }
-
-    ${impl_coord_copy('stroke_dashoffset', 'mStrokeDashoffset')}
-
-    pub fn clone_stroke_dashoffset(&self) -> longhands::stroke_dashoffset::computed_value::T {
-        use values::computed::LengthOrPercentage;
-        match self.gecko.mStrokeDashoffset.as_value() {
-            CoordDataValue::Factor(number) => Either::First(number),
-            CoordDataValue::Coord(coord) => Either::Second(LengthOrPercentage::Length(Au(coord))),
-            CoordDataValue::Percent(p) => Either::Second(LengthOrPercentage::Percentage(Percentage(p))),
-            CoordDataValue::Calc(calc) => Either::Second(LengthOrPercentage::Calc(calc.into())),
-            _ => unreachable!(),
-        }
-    }
-
-    pub fn set_stroke_width(&mut self, v: longhands::stroke_width::computed_value::T) {
-        match v {
-            Either::First(number) => self.gecko.mStrokeWidth.set_value(CoordDataValue::Factor(number)),
-            Either::Second(lop) => self.gecko.mStrokeWidth.set(lop),
-        }
-    }
-
-    ${impl_coord_copy('stroke_width', 'mStrokeWidth')}
-
-    pub fn clone_stroke_width(&self) -> longhands::stroke_width::computed_value::T {
-        use values::computed::LengthOrPercentage;
-        match self.gecko.mStrokeWidth.as_value() {
-            CoordDataValue::Factor(number) => Either::First(number),
-            CoordDataValue::Coord(coord) => Either::Second(LengthOrPercentage::Length(Au(coord))),
-            CoordDataValue::Percent(p) => Either::Second(LengthOrPercentage::Percentage(Percentage(p))),
-            CoordDataValue::Calc(calc) => Either::Second(LengthOrPercentage::Calc(calc.into())),
-            _ => unreachable!(),
-        }
-    }
-
     #[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();
         unsafe {
             bindings::Gecko_nsStyleSVG_SetContextPropertiesLength(&mut self.gecko, v.len() as u32);
--- 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::{SVGPaint, SVGPaintKind};
+use values::generics::svg::{SVGLength, SVGPaint, SVGPaintKind};
 
 /// 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, ()>;
 
@@ -3033,16 +3033,52 @@ impl ToAnimatedZero for IntermediateSVGP
             SVGPaintKind::None |
             SVGPaintKind::ContextFill |
             SVGPaintKind::ContextStroke => Ok(self.clone()),
             _ => Err(()),
         }
     }
 }
 
+impl<LengthType> Animatable for SVGLength<LengthType>
+        where LengthType: Animatable + Clone
+{
+    #[inline]
+    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+        match (self, other) {
+            (&SVGLength::Length(ref this), &SVGLength::Length(ref other)) => {
+                this.add_weighted(&other, self_portion, other_portion).map(SVGLength::Length)
+            }
+            _ => {
+                Ok(if self_portion > other_portion { self.clone() } else { other.clone() })
+            }
+        }
+    }
+
+    #[inline]
+    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+        match (self, other) {
+            (&SVGLength::Length(ref this), &SVGLength::Length(ref other)) => {
+                this.compute_distance(other)
+            }
+            _ => Err(())
+        }
+    }
+}
+
+impl<LengthType> ToAnimatedZero for SVGLength<LengthType> where LengthType : ToAnimatedZero {
+    #[inline]
+    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),
+        }
+    }
+}
+
 <%
     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
@@ -59,18 +59,18 @@
     "stroke", "SVGPaint",
     "Default::default()",
     products="gecko",
     animation_value_type="IntermediateSVGPaint",
     boxed=True,
     spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint")}
 
 ${helpers.predefined_type(
-    "stroke-width", "LengthOrPercentageOrNumber",
-    "Either::First(1.0)",
+    "stroke-width", "SVGLength",
+    "Au::from_px(1).into()",
     "parse_non_negative",
     products="gecko",
     animation_value_type="ComputedValue",
     spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth")}
 
 ${helpers.single_keyword("stroke-linecap", "butt round square",
                          products="gecko", animation_value_type="discrete",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty")}
@@ -96,18 +96,18 @@
     vector=True,
     products="gecko",
     animation_value_type="ComputedValue",
     separator="CommaWithSpace",
     spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing",
 )}
 
 ${helpers.predefined_type(
-    "stroke-dashoffset", "LengthOrPercentageOrNumber",
-    "Either::First(0.0)",
+    "stroke-dashoffset", "SVGLength",
+    "Au(0).into()",
     products="gecko",
     animation_value_type="ComputedValue",
     spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}
 
 // Section 14 - Clipping, Masking and Compositing
 ${helpers.single_keyword("clip-rule", "nonzero evenodd",
                          products="gecko",
                          gecko_enum_prefix="StyleFillRule",
--- 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::{SVGPaint, SVGPaintKind};
+pub use self::svg::{SVGLength, SVGPaint, SVGPaintKind};
 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
@@ -1,15 +1,17 @@
 /* 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/. */
 
 //! Computed types for SVG properties.
 
-use values::RGBA;
+use app_units::Au;
+use values::{Either, RGBA};
+use values::computed::LengthOrPercentageOrNumber;
 use values::generics::svg as generic;
 
 /// Computed SVG Paint value
 pub type SVGPaint = generic::SVGPaint<RGBA>;
 /// Computed SVG Paint Kind value
 pub type SVGPaintKind = generic::SVGPaintKind<RGBA>;
 
 impl Default for SVGPaint {
@@ -26,8 +28,17 @@ impl SVGPaint {
     pub fn black() -> Self {
         let rgba = RGBA::from_floats(0., 0., 0., 1.);
         SVGPaint {
             kind: generic::SVGPaintKind::Color(rgba),
             fallback: None,
         }
     }
 }
+
+/// <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()))
+    }
+}
--- a/servo/components/style/values/generics/svg.rs
+++ b/servo/components/style/values/generics/svg.rs
@@ -89,8 +89,18 @@ impl<ColorType: Parse> Parse for SVGPain
                 kind: SVGPaintKind::Color(color),
                 fallback: None,
             })
         } else {
             Err(StyleParseError::UnspecifiedError.into())
         }
     }
 }
+
+/// An SVG length value supports `context-value` in addition to length.
+#[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,
+}
--- 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::{SVGPaint, SVGPaintKind};
+pub use self::svg::{SVGLength, SVGPaint, SVGPaintKind};
 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,16 +1,68 @@
 /* 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 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);
 
 /// Specified SVG Paint Kind value
 pub type SVGPaintKind = generic::SVGPaintKind<RGBAColor>;
+
+#[cfg(feature = "gecko")]
+fn is_context_value_enabled() -> bool {
+    use gecko_bindings::structs::mozilla;
+    unsafe { mozilla::StylePrefs_sOpentypeSVGEnabled }
+}
+#[cfg(not(feature = "gecko"))]
+fn is_context_value_enabled() -> bool {
+    false
+}
+
+fn parse_context_value<'i, 't, T>(input: &mut Parser<'i, 't>, value: T)
+                                  -> Result<T, ParseError<'i>> {
+    if is_context_value_enabled() {
+        if input.expect_ident_matching("context-value").is_ok() {
+            return Ok(value);
+        }
+    }
+    Err(StyleParseError::UnspecifiedError.into())
+}
+
+/// <length> | <percentage> | <number> | context-value
+pub type SVGLength = generic::SVGLength<LengthOrPercentageOrNumber>;
+
+impl Parse for SVGLength {
+    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
+                     -> Result<Self, ParseError<'i>> {
+        input.try(|i| LengthOrPercentageOrNumber::parse(context, i))
+             .map(Into::into)
+             .or_else(|_| parse_context_value(input, generic::SVGLength::ContextValue))
+    }
+}
+
+impl SVGLength {
+    /// parse a non-negative SVG length
+    pub fn parse_non_negative<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
+                                      -> Result<Self, ParseError<'i>> {
+        input.try(|i| LengthOrPercentageOrNumber::parse_non_negative(context, i))
+             .map(Into::into)
+             .or_else(|_| parse_context_value(input, generic::SVGLength::ContextValue))
+    }
+}
+
+impl From<LengthOrPercentageOrNumber> for SVGLength {
+    fn from(length: LengthOrPercentageOrNumber) -> Self {
+        generic::SVGLength::Length(length)
+    }
+}