diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index f77a90b9e19..dc2f8df4ab1 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -576,6 +576,64 @@ def set_gecko_property(ffi_name, expr): % endif +<%def name="impl_svg_length(ident, gecko_ffi_name, need_clone=False)"> + // When context-value is used on an SVG length, the corresponding flag is + // 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 gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as 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!("Initial value should not be context-value"), + } + } + }; + 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 reset_${ident}(&mut self, other: &Self) { + self.copy_${ident}_from(other) + } + + 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!("Unexpected coordinate {:?} in ${ident}", + self.gecko.${gecko_ffi_name}.as_value()), + }; + SVGLength::Length(length) + } + + <%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) { @@ -965,6 +1023,7 @@ impl Clone for ${style_struct.gecko_struct_name} { "Opacity": impl_simple, "Color": impl_color, "RGBAColor": impl_rgba_color, + "SVGLength": impl_svg_length, "SVGPaint": impl_svg_paint, "UrlOrNone": impl_css_url, } @@ -4896,7 +4955,7 @@ clip-path <%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; @@ -4968,46 +5027,6 @@ clip-path 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(&mut self, v: I) where I: IntoIterator, diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index a5ccad0830c..e457a9c7b06 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -46,7 +46,7 @@ use values::computed::{LengthOrPercentage, MaxLength, MozLength, Percentage, ToC 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 { @@ -3038,6 +3038,42 @@ impl ToAnimatedZero for IntermediateSVGPaintKind { } } +impl Animatable for SVGLength + where LengthType: Animatable + Clone +{ + #[inline] + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + 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 { + match (self, other) { + (&SVGLength::Length(ref this), &SVGLength::Length(ref other)) => { + this.compute_distance(other) + } + _ => Err(()) + } + } +} + +impl ToAnimatedZero for SVGLength where LengthType : ToAnimatedZero { + #[inline] + fn to_animated_zero(&self) -> Result { + 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', diff --git a/components/style/properties/longhand/inherited_svg.mako.rs b/components/style/properties/longhand/inherited_svg.mako.rs index 0ead8db90ef..89e12afa594 100644 --- a/components/style/properties/longhand/inherited_svg.mako.rs +++ b/components/style/properties/longhand/inherited_svg.mako.rs @@ -64,10 +64,11 @@ ${helpers.predefined_type( 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", + boxed="True", animation_value_type="ComputedValue", spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth")} @@ -101,9 +102,10 @@ ${helpers.predefined_type( )} ${helpers.predefined_type( - "stroke-dashoffset", "LengthOrPercentageOrNumber", - "Either::First(0.0)", + "stroke-dashoffset", "SVGLength", + "Au(0).into()", products="gecko", + boxed="True", animation_value_type="ComputedValue", spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")} diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index f345128d43e..2c57ee214bb 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -44,7 +44,7 @@ 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}; diff --git a/components/style/values/computed/svg.rs b/components/style/values/computed/svg.rs index 8b3247aad31..2a25922be91 100644 --- a/components/style/values/computed/svg.rs +++ b/components/style/values/computed/svg.rs @@ -4,7 +4,9 @@ //! 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 @@ -31,3 +33,12 @@ impl SVGPaint { } } } + +/// | | | context-value +pub type SVGLength = generic::SVGLength; + +impl From for SVGLength { + fn from(length: Au) -> Self { + generic::SVGLength::Length(Either::Second(length.into())) + } +} diff --git a/components/style/values/generics/svg.rs b/components/style/values/generics/svg.rs index f1a27c60903..3310673383f 100644 --- a/components/style/values/generics/svg.rs +++ b/components/style/values/generics/svg.rs @@ -94,3 +94,13 @@ impl Parse for SVGPaint { } } } + +/// 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 { + /// ` | | ` + Length(LengthType), + /// `context-value` + ContextValue, +} diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 348b98e2655..232c61426c0 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -43,7 +43,7 @@ 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; diff --git a/components/style/values/specified/svg.rs b/components/style/values/specified/svg.rs index 2390c383c40..2d2b75f1c0c 100644 --- a/components/style/values/specified/svg.rs +++ b/components/style/values/specified/svg.rs @@ -4,7 +4,11 @@ //! 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 @@ -14,3 +18,54 @@ no_viewport_percentage!(SVGPaint); /// Specified SVG Paint Kind value pub type SVGPaintKind = generic::SVGPaintKind; + +#[cfg(feature = "gecko")] +fn is_context_value_enabled() -> bool { + // The prefs can only be mutated on the main thread, so it is safe + // to read whenever we are on the main thread or the main thread is + // blocked. + 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> { + if is_context_value_enabled() { + if input.expect_ident_matching("context-value").is_ok() { + return Ok(value); + } + } + Err(StyleParseError::UnspecifiedError.into()) +} + +/// | | | context-value +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)) + .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> { + input.try(|i| LengthOrPercentageOrNumber::parse_non_negative(context, i)) + .map(Into::into) + .or_else(|_| parse_context_value(input, generic::SVGLength::ContextValue)) + } +} + +impl From for SVGLength { + fn from(length: LengthOrPercentageOrNumber) -> Self { + generic::SVGLength::Length(length) + } +}