Bug 1338388 - Part 9: stylo: Support stroke-dasharray and stroke-dashoffset; r?heycam
MozReview-Commit-ID: 4QKKzJ1DVYP
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -1074,16 +1074,29 @@ Gecko_nsStyleSVGPaint_SetURLValue(nsStyl
aPaint->SetPaintServer(url.get(), NS_RGB(0, 0, 0));
}
void Gecko_nsStyleSVGPaint_Reset(nsStyleSVGPaint* aPaint)
{
aPaint->SetNone();
}
+void
+Gecko_nsStyleSVG_SetDashArrayLength(nsStyleSVG* aSvg, uint32_t aLen)
+{
+ aSvg->mStrokeDasharray.Clear();
+ aSvg->mStrokeDasharray.SetLength(aLen);
+}
+
+void
+Gecko_nsStyleSVG_CopyDashArray(nsStyleSVG* aDst, const nsStyleSVG* aSrc)
+{
+ aDst->mStrokeDasharray = aSrc->mStrokeDasharray;
+}
+
css::URLValue*
Gecko_NewURLValue(ServoBundledURI aURI)
{
RefPtr<css::URLValue> url = aURI.IntoCssUrl();
return url.forget().take();
}
NS_IMPL_THREADSAFE_FFI_REFCOUNTING(css::URLValue, CSSURLValue);
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -293,16 +293,19 @@ void Gecko_StyleClipPath_SetURLValue(moz
void Gecko_ResetFilters(nsStyleEffects* effects, size_t new_len);
void Gecko_CopyFiltersFrom(nsStyleEffects* aSrc, nsStyleEffects* aDest);
void Gecko_nsStyleFilter_SetURLValue(nsStyleFilter* effects, ServoBundledURI uri);
void Gecko_nsStyleSVGPaint_CopyFrom(nsStyleSVGPaint* dest, const nsStyleSVGPaint* src);
void Gecko_nsStyleSVGPaint_SetURLValue(nsStyleSVGPaint* paint, ServoBundledURI uri);
void Gecko_nsStyleSVGPaint_Reset(nsStyleSVGPaint* paint);
+void Gecko_nsStyleSVG_SetDashArrayLength(nsStyleSVG* svg, uint32_t len);
+void Gecko_nsStyleSVG_CopyDashArray(nsStyleSVG* dst, const nsStyleSVG* src);
+
mozilla::css::URLValue* Gecko_NewURLValue(ServoBundledURI uri);
NS_DECL_THREADSAFE_FFI_REFCOUNTING(mozilla::css::URLValue, CSSURLValue);
void Gecko_FillAllBackgroundLists(nsStyleImageLayers* layers, uint32_t max_len);
void Gecko_FillAllMaskLists(nsStyleImageLayers* layers, uint32_t max_len);
NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsStyleCoord::Calc, Calc);
nsCSSShadowArray* Gecko_NewCSSShadowArray(uint32_t len);
--- a/servo/components/style/gecko_bindings/bindings.rs
+++ b/servo/components/style/gecko_bindings/bindings.rs
@@ -718,16 +718,24 @@ extern "C" {
extern "C" {
pub fn Gecko_nsStyleSVGPaint_SetURLValue(paint: *mut nsStyleSVGPaint,
uri: ServoBundledURI);
}
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;
}
extern "C" {
pub fn Gecko_AddRefCSSURLValueArbitraryThread(aPtr: *mut URLValue);
}
extern "C" {
pub fn Gecko_ReleaseCSSURLValueArbitraryThread(aPtr: *mut URLValue);
}
--- a/servo/components/style/parser.rs
+++ b/servo/components/style/parser.rs
@@ -105,13 +105,29 @@ pub trait Parse : Sized {
}
impl<T> Parse for Vec<T> where T: Parse + OneOrMoreCommaSeparated {
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
input.parse_comma_separated(|input| T::parse(context, input))
}
}
+/// Parse a non-empty space-separated or comma-separated list of objects parsed by parse_one
+pub fn parse_space_or_comma_separated<F, T>(input: &mut Parser, mut parse_one: F)
+ -> Result<Vec<T>, ()>
+ where F: FnMut(&mut Parser) -> Result<T, ()> {
+ 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<Self, ()> {
UnicodeRange::parse(input)
}
}
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -2795,17 +2795,17 @@ clip-path
use gecko_bindings::bindings::Gecko_CopyClipPathValueFrom;
unsafe {
Gecko_CopyClipPathValueFrom(&mut self.gecko.mClipPath, &other.gecko.mClipPath);
}
}
</%self:impl_trait>
<%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;
if v.0 == 0 {
self.gecko.mPaintOrder = structs::NS_STYLE_PAINT_ORDER_NORMAL as u8;
} else {
let mut order = 0;
@@ -2820,16 +2820,35 @@ clip-path
order |= geckoval << (pos * structs::NS_STYLE_PAINT_ORDER_BITWIDTH as u8);
}
self.gecko.mPaintOrder = order;
}
}
${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>
<%self:impl_trait style_struct_name="Color"
skip_longhands="*">
pub fn set_color(&mut self, v: longhands::color::computed_value::T) {
let result = convert_rgba_to_nscolor(&v);
${set_gecko_property("mColor", "result")}
}
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -61,17 +61,17 @@
to True for cases where Servo takes a single value
and Stylo supports vector values.
Setting allow_empty to False allows for cases where the vector
is empty. The grammar for these is usually "none | <thing> [ , <thing> ]*".
We assume that the default/initial value is an empty vector for these.
`initial_value` need not be defined for these.
</%doc>
-<%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;
use values::HasViewportPercentage;
use style_traits::ToCss;
impl HasViewportPercentage for SpecifiedValue {
fn has_viewport_percentage(&self) -> bool {
@@ -81,16 +81,17 @@
}
pub mod single_value {
use cssparser::Parser;
use parser::{Parse, ParserContext, ParserContextExtraData};
use properties::{CSSWideKeyword, DeclaredValue, ShorthandId};
use values::computed::{Context, ToComputedValue};
use values::{computed, specified};
+ use values::{Auto, Either, None_, Normal};
${caller.body()}
}
/// The definition of the computed value for ${name}.
pub mod computed_value {
pub use super::single_value::computed_value as single_value;
pub use self::single_value::T as SingleComputedValue;
/// The computed value, effectively a list of single values.
@@ -161,26 +162,33 @@
% if allow_empty:
computed_value::T(vec![])
% else:
computed_value::T(vec![single_value::get_initial_value()])
% endif
}
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+ 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
}
pub use self::single_value::SpecifiedValue as SingleSpecifiedValue;
impl ToComputedValue for SpecifiedValue {
--- a/servo/components/style/properties/longhand/inherited_svg.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_svg.mako.rs
@@ -88,16 +88,32 @@
needs_context=False,
animatable=False,
spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty")}
${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",
gecko_enum_prefix="StyleFillRule",
gecko_inexhaustive=True,
animatable=False,
spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty")}
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -254,16 +254,18 @@ impl ToCss for SVGPaint {
self.kind.to_css(dest)?;
if let Some(ref fallback) = self.fallback {
fallback.to_css(dest)?;
}
Ok(())
}
}
+/// <length> | <percentage> | <number>
+pub type LoPOrNumber = Either<LengthOrPercentage, Number>;
#[derive(Clone, PartialEq, Eq, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
/// A computed cliprect for clip and image-region
pub struct ClipRect {
pub top: Au,
pub right: Option<Au>,
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -819,16 +819,31 @@ impl ToComputedValue for SVGPaintKind {
}
super::computed::SVGPaintKind::PaintServer(ref server) => {
SVGPaintKind::PaintServer(ToComputedValue::from_computed_value(server))
}
}
}
}
+/// <length> | <percentage> | <number>
+pub type LoPOrNumber = Either<LengthOrPercentage, Number>;
+
+impl LoPOrNumber {
+ /// parse a <length-percentage> | <number> enforcing that the contents aren't negative
+ pub fn parse_non_negative(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+ 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 {
self.top.has_viewport_percentage() ||
self.right.as_ref().map_or(false, |x| x.has_viewport_percentage()) ||
self.bottom.as_ref().map_or(false, |x| x.has_viewport_percentage()) ||
self.left.has_viewport_percentage()
}