diff --git a/components/style/gecko_bindings/bindings.rs b/components/style/gecko_bindings/bindings.rs index 71f9389a45b..415bf710781 100644 --- a/components/style/gecko_bindings/bindings.rs +++ b/components/style/gecko_bindings/bindings.rs @@ -726,6 +726,14 @@ extern "C" { extern "C" { pub fn Gecko_nsStyleSVGPaint_Reset(paint: *mut nsStyleSVGPaint); } +extern "C" { + pub fn Gecko_nsStyleSVG_SetDashArrayLength(svg: *mut nsStyleSVG, + len: u32); +} +extern "C" { + pub fn Gecko_nsStyleSVG_CopyDashArray(dst: *mut nsStyleSVG, + src: *const nsStyleSVG); +} extern "C" { pub fn Gecko_NewURLValue(uri: ServoBundledURI) -> *mut URLValue; } diff --git a/components/style/parser.rs b/components/style/parser.rs index 12e6290304f..4145650b2c8 100644 --- a/components/style/parser.rs +++ b/components/style/parser.rs @@ -110,6 +110,22 @@ impl Parse for Vec where T: Parse + OneOrMoreCommaSeparated { } } +/// Parse a non-empty space-separated or comma-separated list of objects parsed by parse_one +pub fn parse_space_or_comma_separated(input: &mut Parser, mut parse_one: F) + -> Result, ()> + where F: FnMut(&mut Parser) -> Result { + let first = parse_one(input)?; + let mut vec = vec![first]; + loop { + let _ = input.try(|i| i.expect_comma()); + if let Ok(val) = input.try(|i| parse_one(i)) { + vec.push(val) + } else { + break + } + } + Ok(vec) +} impl Parse for UnicodeRange { fn parse(_context: &ParserContext, input: &mut Parser) -> Result { UnicodeRange::parse(input) diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index f8ec951ab3e..2e60378dd1c 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -2800,7 +2800,7 @@ clip-path <%self:impl_trait style_struct_name="InheritedSVG" - skip_longhands="paint-order" + skip_longhands="paint-order stroke-dasharray" skip_additionals="*"> pub fn set_paint_order(&mut self, v: longhands::paint_order::computed_value::T) { use self::longhands::paint_order; @@ -2825,6 +2825,25 @@ clip-path } ${impl_simple_copy('paint_order', 'mPaintOrder')} + + pub fn set_stroke_dasharray(&mut self, v: longhands::stroke_dasharray::computed_value::T) { + unsafe { + bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut self.gecko, v.0.len() as u32); + } + + for (mut gecko, servo) in self.gecko.mStrokeDasharray.iter_mut().zip(v.0.into_iter()) { + match servo { + Either::First(lop) => gecko.set(lop), + Either::Second(number) => gecko.set_value(CoordDataValue::Factor(number)), + } + } + } + + pub fn copy_stroke_dasharray_from(&mut self, other: &Self) { + unsafe { + bindings::Gecko_nsStyleSVG_CopyDashArray(&mut self.gecko, &other.gecko); + } + } <%self:impl_trait style_struct_name="Color" diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index e89ebaeb21a..c9b26aa6489 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -66,7 +66,8 @@ We assume that the default/initial value is an empty vector for these. `initial_value` need not be defined for these. -<%def name="vector_longhand(name, gecko_only=False, allow_empty=False, delegate_animate=False, **kwargs)"> +<%def name="vector_longhand(name, gecko_only=False, allow_empty=False, + delegate_animate=False, space_separated_allowed=False, **kwargs)"> <%call expr="longhand(name, **kwargs)"> % if not gecko_only: use std::fmt; @@ -86,6 +87,7 @@ use properties::{CSSWideKeyword, DeclaredValue, ShorthandId}; use values::computed::{Context, ToComputedValue}; use values::{computed, specified}; + use values::{Auto, Either, None_, Normal}; ${caller.body()} } @@ -166,16 +168,23 @@ } pub fn parse(context: &ParserContext, input: &mut Parser) -> Result { + use parser::parse_space_or_comma_separated; + + <% + parse_func = "Parser::parse_comma_separated" + if space_separated_allowed: + parse_func = "parse_space_or_comma_separated" + %> % if allow_empty: if input.try(|input| input.expect_ident_matching("none")).is_ok() { Ok(SpecifiedValue(Vec::new())) } else { - input.parse_comma_separated(|parser| { + ${parse_func}(input, |parser| { single_value::parse(context, parser) }).map(SpecifiedValue) } % else: - input.parse_comma_separated(|parser| { + ${parse_func}(input, |parser| { single_value::parse(context, parser) }).map(SpecifiedValue) % endif diff --git a/components/style/properties/longhand/inherited_svg.mako.rs b/components/style/properties/longhand/inherited_svg.mako.rs index 7be800cdf67..61665a65520 100644 --- a/components/style/properties/longhand/inherited_svg.mako.rs +++ b/components/style/properties/longhand/inherited_svg.mako.rs @@ -93,6 +93,22 @@ ${helpers.predefined_type("stroke-opacity", "Opacity", "1.0", products="gecko", animatable=False, spec="https://www.w3.org/TR/SVG11/painting.html#StrokeOpacityProperty")} +${helpers.predefined_type("stroke-dasharray", "LoPOrNumber", "Either::Second(0.0)", + "parse_non_negative", + vector="True", + products="gecko", + animatable="False", + space_separated_allowed="True", + spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")} + +${helpers.predefined_type( + "stroke-dashoffset", "LengthOrPercentage", + "computed::LengthOrPercentage::zero()", + products="gecko", + animatable=True, + boxed=True, + 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", diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 7549c82420f..b505d2364c8 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -259,6 +259,8 @@ impl ToCss for SVGPaint { } } +/// | | +pub type LoPOrNumber = Either; #[derive(Clone, PartialEq, Eq, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 642fb96bec6..f959a63902a 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -824,6 +824,21 @@ impl ToComputedValue for SVGPaintKind { } } +/// | | +pub type LoPOrNumber = Either; + +impl LoPOrNumber { + /// parse a | enforcing that the contents aren't negative + pub fn parse_non_negative(_: &ParserContext, input: &mut Parser) -> Result { + if let Ok(lop) = input.try(LengthOrPercentage::parse_non_negative) { + Ok(Either::First(lop)) + } else if let Ok(num) = input.try(Number::parse_non_negative) { + Ok(Either::Second(num)) + } else { + Err(()) + } + } +} impl HasViewportPercentage for ClipRect { fn has_viewport_percentage(&self) -> bool {