diff --git a/components/style/gecko/generated/bindings.rs b/components/style/gecko/generated/bindings.rs index f0078fa23c2..3cb2183f051 100644 --- a/components/style/gecko/generated/bindings.rs +++ b/components/style/gecko/generated/bindings.rs @@ -172,6 +172,9 @@ unsafe impl Sync for nsStyleQuoteValues {} use gecko_bindings::structs::nsStyleSVG; unsafe impl Send for nsStyleSVG {} unsafe impl Sync for nsStyleSVG {} +use gecko_bindings::structs::nsStyleSVGOpacitySource; +unsafe impl Send for nsStyleSVGOpacitySource {} +unsafe impl Sync for nsStyleSVGOpacitySource {} use gecko_bindings::structs::nsStyleSVGPaint; unsafe impl Send for nsStyleSVGPaint {} unsafe impl Sync for nsStyleSVGPaint {} diff --git a/components/style/gecko/generated/structs_debug.rs b/components/style/gecko/generated/structs_debug.rs index f617c388381..ac9ad2686ce 100644 --- a/components/style/gecko/generated/structs_debug.rs +++ b/components/style/gecko/generated/structs_debug.rs @@ -13741,39 +13741,14 @@ pub mod root { pub mContextPropsBits: u8, pub mContextFlags: u8, } - pub const nsStyleSVG_FILL_OPACITY_SOURCE_MASK: - root::nsStyleSVG__bindgen_ty_1 = - nsStyleSVG__bindgen_ty_1::FILL_OPACITY_SOURCE_MASK; - pub const nsStyleSVG_STROKE_OPACITY_SOURCE_MASK: - root::nsStyleSVG__bindgen_ty_1 = - nsStyleSVG__bindgen_ty_1::STROKE_OPACITY_SOURCE_MASK; - pub const nsStyleSVG_STROKE_DASHARRAY_CONTEXT: - root::nsStyleSVG__bindgen_ty_1 = - nsStyleSVG__bindgen_ty_1::STROKE_DASHARRAY_CONTEXT; - pub const nsStyleSVG_STROKE_DASHOFFSET_CONTEXT: - root::nsStyleSVG__bindgen_ty_1 = - nsStyleSVG__bindgen_ty_1::STROKE_DASHOFFSET_CONTEXT; - pub const nsStyleSVG_STROKE_WIDTH_CONTEXT: root::nsStyleSVG__bindgen_ty_1 - = - nsStyleSVG__bindgen_ty_1::STROKE_WIDTH_CONTEXT; - pub const nsStyleSVG_FILL_OPACITY_SOURCE_SHIFT: - root::nsStyleSVG__bindgen_ty_1 = - nsStyleSVG__bindgen_ty_1::FILL_OPACITY_SOURCE_SHIFT; - pub const nsStyleSVG_STROKE_OPACITY_SOURCE_SHIFT: - root::nsStyleSVG__bindgen_ty_1 = - nsStyleSVG__bindgen_ty_1::STROKE_OPACITY_SOURCE_SHIFT; - #[repr(u32)] - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] - pub enum nsStyleSVG__bindgen_ty_1 { - FILL_OPACITY_SOURCE_MASK = 3, - STROKE_OPACITY_SOURCE_MASK = 12, - STROKE_DASHARRAY_CONTEXT = 16, - STROKE_DASHOFFSET_CONTEXT = 32, - STROKE_WIDTH_CONTEXT = 64, - FILL_OPACITY_SOURCE_SHIFT = 0, - STROKE_OPACITY_SOURCE_SHIFT = 2, - } pub const nsStyleSVG_kHasFinishStyle: bool = false; + pub const nsStyleSVG_FILL_OPACITY_SOURCE_MASK: u8 = 3; + pub const nsStyleSVG_STROKE_OPACITY_SOURCE_MASK: u8 = 12; + pub const nsStyleSVG_STROKE_DASHARRAY_CONTEXT: u8 = 16; + pub const nsStyleSVG_STROKE_DASHOFFSET_CONTEXT: u8 = 32; + pub const nsStyleSVG_STROKE_WIDTH_CONTEXT: u8 = 64; + pub const nsStyleSVG_FILL_OPACITY_SOURCE_SHIFT: u8 = 0; + pub const nsStyleSVG_STROKE_OPACITY_SOURCE_SHIFT: u8 = 2; #[test] fn bindgen_test_layout_nsStyleSVG() { assert_eq!(::std::mem::size_of::() , 128usize , concat ! ( @@ -31553,6 +31528,13 @@ pub mod root { eStyleSVGFallbackType_None = 1, eStyleSVGFallbackType_Color = 2, } + #[repr(u8)] + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + pub enum nsStyleSVGOpacitySource { + eStyleSVGOpacitySource_Normal = 0, + eStyleSVGOpacitySource_ContextFillOpacity = 1, + eStyleSVGOpacitySource_ContextStrokeOpacity = 2, + } #[repr(C)] #[derive(Debug)] pub struct nsStyleSVGPaint { diff --git a/components/style/gecko/generated/structs_release.rs b/components/style/gecko/generated/structs_release.rs index 5fbe19a5847..ad3b9843db6 100644 --- a/components/style/gecko/generated/structs_release.rs +++ b/components/style/gecko/generated/structs_release.rs @@ -13532,39 +13532,14 @@ pub mod root { pub mContextPropsBits: u8, pub mContextFlags: u8, } - pub const nsStyleSVG_FILL_OPACITY_SOURCE_MASK: - root::nsStyleSVG__bindgen_ty_1 = - nsStyleSVG__bindgen_ty_1::FILL_OPACITY_SOURCE_MASK; - pub const nsStyleSVG_STROKE_OPACITY_SOURCE_MASK: - root::nsStyleSVG__bindgen_ty_1 = - nsStyleSVG__bindgen_ty_1::STROKE_OPACITY_SOURCE_MASK; - pub const nsStyleSVG_STROKE_DASHARRAY_CONTEXT: - root::nsStyleSVG__bindgen_ty_1 = - nsStyleSVG__bindgen_ty_1::STROKE_DASHARRAY_CONTEXT; - pub const nsStyleSVG_STROKE_DASHOFFSET_CONTEXT: - root::nsStyleSVG__bindgen_ty_1 = - nsStyleSVG__bindgen_ty_1::STROKE_DASHOFFSET_CONTEXT; - pub const nsStyleSVG_STROKE_WIDTH_CONTEXT: root::nsStyleSVG__bindgen_ty_1 - = - nsStyleSVG__bindgen_ty_1::STROKE_WIDTH_CONTEXT; - pub const nsStyleSVG_FILL_OPACITY_SOURCE_SHIFT: - root::nsStyleSVG__bindgen_ty_1 = - nsStyleSVG__bindgen_ty_1::FILL_OPACITY_SOURCE_SHIFT; - pub const nsStyleSVG_STROKE_OPACITY_SOURCE_SHIFT: - root::nsStyleSVG__bindgen_ty_1 = - nsStyleSVG__bindgen_ty_1::STROKE_OPACITY_SOURCE_SHIFT; - #[repr(u32)] - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] - pub enum nsStyleSVG__bindgen_ty_1 { - FILL_OPACITY_SOURCE_MASK = 3, - STROKE_OPACITY_SOURCE_MASK = 12, - STROKE_DASHARRAY_CONTEXT = 16, - STROKE_DASHOFFSET_CONTEXT = 32, - STROKE_WIDTH_CONTEXT = 64, - FILL_OPACITY_SOURCE_SHIFT = 0, - STROKE_OPACITY_SOURCE_SHIFT = 2, - } pub const nsStyleSVG_kHasFinishStyle: bool = false; + pub const nsStyleSVG_FILL_OPACITY_SOURCE_MASK: u8 = 3; + pub const nsStyleSVG_STROKE_OPACITY_SOURCE_MASK: u8 = 12; + pub const nsStyleSVG_STROKE_DASHARRAY_CONTEXT: u8 = 16; + pub const nsStyleSVG_STROKE_DASHOFFSET_CONTEXT: u8 = 32; + pub const nsStyleSVG_STROKE_WIDTH_CONTEXT: u8 = 64; + pub const nsStyleSVG_FILL_OPACITY_SOURCE_SHIFT: u8 = 0; + pub const nsStyleSVG_STROKE_OPACITY_SOURCE_SHIFT: u8 = 2; #[test] fn bindgen_test_layout_nsStyleSVG() { assert_eq!(::std::mem::size_of::() , 128usize , concat ! ( @@ -31061,6 +31036,13 @@ pub mod root { eStyleSVGFallbackType_None = 1, eStyleSVGFallbackType_Color = 2, } + #[repr(u8)] + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + pub enum nsStyleSVGOpacitySource { + eStyleSVGOpacitySource_Normal = 0, + eStyleSVGOpacitySource_ContextFillOpacity = 1, + eStyleSVGOpacitySource_ContextStrokeOpacity = 2, + } #[repr(C)] #[derive(Debug)] pub struct nsStyleSVGPaint { diff --git a/components/style/gecko_bindings/sugar/ns_style_coord.rs b/components/style/gecko_bindings/sugar/ns_style_coord.rs index be6a2a6f80b..001ab16b6c2 100644 --- a/components/style/gecko_bindings/sugar/ns_style_coord.rs +++ b/components/style/gecko_bindings/sugar/ns_style_coord.rs @@ -54,6 +54,14 @@ impl nsStyleCoord_CalcValue { } } +impl PartialEq for nsStyleCoord_CalcValue { + fn eq(&self, other: &Self) -> bool { + self.mLength == other.mLength && + self.mPercent == other.mPercent && + self.mHasPercent == other.mHasPercent + } +} + impl nsStyleSides { /// Immutably get the `nsStyleCoord`-like object representing the side at /// index `index`. @@ -192,11 +200,11 @@ impl<'a> CoordDataMut for CornersDataMut<'a> { } } -#[derive(Copy, Clone)] /// Enum representing the tagged union that is CoordData. /// /// In release mode this should never actually exist in the code, and will be /// optimized out by threading matches and inlining. +#[derive(Copy, Clone, Debug, PartialEq)] pub enum CoordDataValue { /// eStyleUnit_Null Null, diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 65ee7860d33..be27f6bcdd4 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -576,10 +576,129 @@ 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_opacity(ident, gecko_ffi_name, need_clone=False)"> + <% source_prefix = ident.split("_")[0].upper() + "_OPACITY_SOURCE" %> + + pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { + use gecko_bindings::structs::nsStyleSVG_${source_prefix}_MASK as MASK; + use gecko_bindings::structs::nsStyleSVG_${source_prefix}_SHIFT as SHIFT; + use gecko_bindings::structs::nsStyleSVGOpacitySource::*; + use values::generics::svg::SVGOpacity; + self.gecko.mContextFlags &= !MASK; + match v { + SVGOpacity::Opacity(opacity) => { + self.gecko.mContextFlags |= + (eStyleSVGOpacitySource_Normal as u8) << SHIFT; + self.gecko.${gecko_ffi_name} = opacity; + } + SVGOpacity::ContextFillOpacity => { + self.gecko.mContextFlags |= + (eStyleSVGOpacitySource_ContextFillOpacity as u8) << SHIFT; + self.gecko.${gecko_ffi_name} = 1.; + } + SVGOpacity::ContextStrokeOpacity => { + self.gecko.mContextFlags |= + (eStyleSVGOpacitySource_ContextStrokeOpacity as u8) << SHIFT; + self.gecko.${gecko_ffi_name} = 1.; + } + } + } + + pub fn copy_${ident}_from(&mut self, other: &Self) { + use gecko_bindings::structs::nsStyleSVG_${source_prefix}_MASK as MASK; + self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}; + self.gecko.mContextFlags = + (self.gecko.mContextFlags & !MASK) | + (other.gecko.mContextFlags & MASK); + } + + pub fn reset_${ident}(&mut self, other: &Self) { + self.copy_${ident}_from(other) + } + + pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { + use gecko_bindings::structs::nsStyleSVG_${source_prefix}_MASK as MASK; + use gecko_bindings::structs::nsStyleSVG_${source_prefix}_SHIFT as SHIFT; + use gecko_bindings::structs::nsStyleSVGOpacitySource::*; + use values::generics::svg::SVGOpacity; + + let source = (self.gecko.mContextFlags & MASK) >> SHIFT; + if source == eStyleSVGOpacitySource_Normal as u8 { + return SVGOpacity::Opacity(self.gecko.${gecko_ffi_name}); + } else { + debug_assert_eq!(self.gecko.${gecko_ffi_name}, 1.0); + if source == eStyleSVGOpacitySource_ContextFillOpacity as u8 { + SVGOpacity::ContextFillOpacity + } else { + debug_assert_eq!(source, eStyleSVGOpacitySource_ContextStrokeOpacity as u8); + SVGOpacity::ContextStrokeOpacity + } + } + } + + <%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::SVGPaintKind; + use values::generics::svg::SVGPaintKind; use self::structs::nsStyleSVGPaintType; use self::structs::nsStyleSVGFallbackType; @@ -632,7 +751,7 @@ def set_gecko_property(ffi_name, expr): #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { - use values::generics::{SVGPaint, SVGPaintKind}; + use values::generics::svg::{SVGPaint, SVGPaintKind}; use values::specified::url::SpecifiedUrl; use self::structs::nsStyleSVGPaintType; use self::structs::nsStyleSVGFallbackType; @@ -965,6 +1084,8 @@ impl Clone for ${style_struct.gecko_struct_name} { "Opacity": impl_simple, "Color": impl_color, "RGBAColor": impl_rgba_color, + "SVGLength": impl_svg_length, + "SVGOpacity": impl_svg_opacity, "SVGPaint": impl_svg_paint, "UrlOrNone": impl_css_url, } @@ -4896,7 +5017,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; @@ -4922,27 +5043,41 @@ clip-path ${impl_simple_copy('paint_order', 'mPaintOrder')} - pub fn set_stroke_dasharray(&mut self, v: I) - where I: IntoIterator, - I::IntoIter: ExactSizeIterator - { - let v = v.into_iter(); - unsafe { - bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut self.gecko, v.len() as u32); - } + 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; - 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), + 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 reset_stroke_dasharray(&mut self, other: &Self) { @@ -4950,8 +5085,14 @@ clip-path } 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() { @@ -4965,47 +5106,7 @@ clip-path _ => 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!(), - } + SVGStrokeDashArray::Values(vec) } #[allow(non_snake_case)] diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 1b070e74879..4419e2921fe 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -43,10 +43,10 @@ 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::{SVGPaint, SVGPaintKind}; use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius; use values::generics::effects::Filter; use values::generics::position as generic_position; +use values::generics::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray}; /// A trait used to implement various procedures used during animation. pub trait Animatable: Sized { @@ -3038,6 +3038,123 @@ 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), + } + } +} + +impl Animatable for SVGStrokeDashArray + where LengthType : RepeatableListAnimatable + Clone +{ + #[inline] + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + 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 { + match (self, other) { + (&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other)) => { + this.compute_distance(other) + } + _ => Err(()) + } + } +} + +impl ToAnimatedZero for SVGStrokeDashArray + where LengthType : ToAnimatedZero + Clone +{ + #[inline] + fn to_animated_zero(&self) -> Result { + match self { + &SVGStrokeDashArray::Values(ref values) => { + values.iter().map(ToAnimatedZero::to_animated_zero) + .collect::, ()>>().map(SVGStrokeDashArray::Values) + } + &SVGStrokeDashArray::ContextValue => Ok(SVGStrokeDashArray::ContextValue), + } + } +} + +impl Animatable for SVGOpacity + where OpacityType: Animatable + Clone +{ + #[inline] + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + match (self, other) { + (&SVGOpacity::Opacity(ref this), &SVGOpacity::Opacity(ref other)) => { + this.add_weighted(other, self_portion, other_portion).map(SVGOpacity::Opacity) + } + _ => { + Ok(if self_portion > other_portion { self.clone() } else { other.clone() }) + } + } + } + + #[inline] + fn compute_distance(&self, other: &Self) -> Result { + match (self, other) { + (&SVGOpacity::Opacity(ref this), &SVGOpacity::Opacity(ref other)) => { + this.compute_distance(other) + } + _ => Err(()) + } + } +} + +impl ToAnimatedZero for SVGOpacity + where OpacityType: ToAnimatedZero + Clone +{ + #[inline] + fn to_animated_zero(&self) -> Result { + match self { + &SVGOpacity::Opacity(ref opacity) => + opacity.to_animated_zero().map(SVGOpacity::Opacity), + other => Ok(other.clone()), + } + } +} + <% 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..053dd168629 100644 --- a/components/style/properties/longhand/inherited_svg.mako.rs +++ b/components/style/properties/longhand/inherited_svg.mako.rs @@ -39,7 +39,7 @@ ${helpers.predefined_type( boxed=True, spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingFillPaint")} -${helpers.predefined_type("fill-opacity", "Opacity", "1.0", +${helpers.predefined_type("fill-opacity", "SVGOpacity", "Default::default()", products="gecko", animation_value_type="ComputedValue", spec="https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty")} @@ -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")} @@ -84,26 +85,24 @@ ${helpers.predefined_type("stroke-miterlimit", "Number", "4.0", animation_value_type="ComputedValue", spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty")} -${helpers.predefined_type("stroke-opacity", "Opacity", "1.0", +${helpers.predefined_type("stroke-opacity", "SVGOpacity", "Default::default()", 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", "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 e9ae9474469..75dea7bf87b 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -17,7 +17,7 @@ use std::f64; use std::f64::consts::PI; use std::fmt; use style_traits::ToCss; -use super::{CSSFloat, CSSInteger, RGBA}; +use super::{CSSFloat, CSSInteger}; use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize}; use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent; use super::generics::grid::TrackList as GenericTrackList; @@ -44,6 +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::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray}; pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing}; pub use self::transform::{TimingFunction, TransformOrigin}; @@ -61,6 +62,7 @@ pub mod gecko; pub mod length; pub mod position; pub mod rect; +pub mod svg; pub mod text; pub mod transform; @@ -454,31 +456,6 @@ impl IntegerOrAuto { } } -/// Computed SVG Paint value -pub type SVGPaint = ::values::generics::SVGPaint; -/// Computed SVG Paint Kind value -pub type SVGPaintKind = ::values::generics::SVGPaintKind; - -impl Default for SVGPaint { - fn default() -> Self { - SVGPaint { - kind: ::values::generics::SVGPaintKind::None, - fallback: None, - } - } -} - -impl SVGPaint { - /// Opaque black color - pub fn black() -> Self { - let rgba = RGBA::from_floats(0., 0., 0., 1.); - SVGPaint { - kind: ::values::generics::SVGPaintKind::Color(rgba), - fallback: None, - } - } -} - /// | | pub type LengthOrPercentageOrNumber = Either; diff --git a/components/style/values/computed/svg.rs b/components/style/values/computed/svg.rs new file mode 100644 index 00000000000..9edc7a36220 --- /dev/null +++ b/components/style/values/computed/svg.rs @@ -0,0 +1,62 @@ +/* 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 app_units::Au; +use values::{Either, RGBA}; +use values::computed::{LengthOrPercentageOrNumber, Opacity}; +use values::generics::svg as generic; + +/// Computed SVG Paint value +pub type SVGPaint = generic::SVGPaint; +/// Computed SVG Paint Kind value +pub type SVGPaintKind = generic::SVGPaintKind; + +impl Default for SVGPaint { + fn default() -> Self { + SVGPaint { + kind: generic::SVGPaintKind::None, + fallback: None, + } + } +} + +impl SVGPaint { + /// Opaque black color + pub fn black() -> Self { + let rgba = RGBA::from_floats(0., 0., 0., 1.); + SVGPaint { + kind: generic::SVGPaintKind::Color(rgba), + fallback: None, + } + } +} + +/// | | | context-value +pub type SVGLength = generic::SVGLength; + +impl From for SVGLength { + fn from(length: Au) -> Self { + generic::SVGLength::Length(Either::Second(length.into())) + } +} + +/// [ | | ]# | context-value +pub type SVGStrokeDashArray = generic::SVGStrokeDashArray; + +impl Default for SVGStrokeDashArray { + fn default() -> Self { + generic::SVGStrokeDashArray::Values(vec![]) + } +} + +/// | context-fill-opacity | context-stroke-opacity +pub type SVGOpacity = generic::SVGOpacity; + +impl Default for SVGOpacity { + fn default() -> Self { + generic::SVGOpacity::Opacity(1.) + } +} diff --git a/components/style/values/generics/mod.rs b/components/style/values/generics/mod.rs index 1ad56e08d53..9456f0061c5 100644 --- a/components/style/values/generics/mod.rs +++ b/components/style/values/generics/mod.rs @@ -11,7 +11,6 @@ use parser::{Parse, ParserContext}; use std::fmt; use style_traits::{Comma, OneOrMoreSeparated, ParseError, StyleParseError, ToCss}; use super::CustomIdent; -use values::specified::url::SpecifiedUrl; pub mod background; pub mod basic_shape; @@ -24,6 +23,7 @@ pub mod grid; pub mod image; pub mod position; pub mod rect; +pub mod svg; pub mod text; pub mod transform; @@ -252,90 +252,3 @@ impl ToCss for FontSettingTagFloat { self.0.to_css(dest) } } - - -/// An SVG paint value -/// -/// https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Debug, PartialEq, ToAnimatedValue, ToComputedValue, ToCss)] -pub struct SVGPaint { - /// The paint source - pub kind: SVGPaintKind, - /// The fallback color - pub fallback: Option, -} - -/// An SVG paint value without the fallback -/// -/// Whereas the spec only allows PaintServer -/// to have a fallback, Gecko lets the context -/// properties have a fallback as well. -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Debug, PartialEq, ToAnimatedValue, ToComputedValue, ToCss)] -pub enum SVGPaintKind { - /// `none` - None, - /// `` - Color(ColorType), - /// `url(...)` - PaintServer(SpecifiedUrl), - /// `context-fill` - ContextFill, - /// `context-stroke` - ContextStroke, -} - -impl SVGPaintKind { - /// Parse a keyword value only - fn parse_ident<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { - try_match_ident_ignore_ascii_case! { input.expect_ident()?, - "none" => Ok(SVGPaintKind::None), - "context-fill" => Ok(SVGPaintKind::ContextFill), - "context-stroke" => Ok(SVGPaintKind::ContextStroke), - } - } -} - -/// Parse SVGPaint's fallback. -/// fallback is keyword(none) or Color. -/// https://svgwg.org/svg2-draft/painting.html#SpecifyingPaint -fn parse_fallback<'i, 't, ColorType: Parse>(context: &ParserContext, - input: &mut Parser<'i, 't>) - -> Option { - if input.try(|i| i.expect_ident_matching("none")).is_ok() { - None - } else { - input.try(|i| ColorType::parse(context, i)).ok() - } -} - -impl Parse for SVGPaint { - fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { - if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) { - Ok(SVGPaint { - kind: SVGPaintKind::PaintServer(url), - fallback: parse_fallback(context, input), - }) - } else if let Ok(kind) = input.try(SVGPaintKind::parse_ident) { - if let SVGPaintKind::None = kind { - Ok(SVGPaint { - kind: kind, - fallback: None, - }) - } else { - Ok(SVGPaint { - kind: kind, - fallback: parse_fallback(context, input), - }) - } - } else if let Ok(color) = input.try(|i| ColorType::parse(context, i)) { - Ok(SVGPaint { - kind: SVGPaintKind::Color(color), - fallback: None, - }) - } else { - Err(StyleParseError::UnspecifiedError.into()) - } - } -} diff --git a/components/style/values/generics/svg.rs b/components/style/values/generics/svg.rs new file mode 100644 index 00000000000..0970cd9b4fd --- /dev/null +++ b/components/style/values/generics/svg.rs @@ -0,0 +1,153 @@ +/* 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/. */ + +//! Generic types for CSS values in SVG + +use cssparser::Parser; +use parser::{Parse, ParserContext}; +use std::fmt; +use style_traits::{ParseError, StyleParseError, ToCss}; +use values::specified::url::SpecifiedUrl; + +/// An SVG paint value +/// +/// https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Debug, PartialEq, ToAnimatedValue, ToComputedValue, ToCss)] +pub struct SVGPaint { + /// The paint source + pub kind: SVGPaintKind, + /// The fallback color + pub fallback: Option, +} + +/// An SVG paint value without the fallback +/// +/// Whereas the spec only allows PaintServer +/// to have a fallback, Gecko lets the context +/// properties have a fallback as well. +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Debug, PartialEq, ToAnimatedValue, ToComputedValue, ToCss)] +pub enum SVGPaintKind { + /// `none` + None, + /// `` + Color(ColorType), + /// `url(...)` + PaintServer(SpecifiedUrl), + /// `context-fill` + ContextFill, + /// `context-stroke` + ContextStroke, +} + +impl SVGPaintKind { + /// Parse a keyword value only + fn parse_ident<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { + try_match_ident_ignore_ascii_case! { input.expect_ident()?, + "none" => Ok(SVGPaintKind::None), + "context-fill" => Ok(SVGPaintKind::ContextFill), + "context-stroke" => Ok(SVGPaintKind::ContextStroke), + } + } +} + +/// Parse SVGPaint's fallback. +/// fallback is keyword(none) or Color. +/// https://svgwg.org/svg2-draft/painting.html#SpecifyingPaint +fn parse_fallback<'i, 't, ColorType: Parse>(context: &ParserContext, + input: &mut Parser<'i, 't>) + -> Option { + if input.try(|i| i.expect_ident_matching("none")).is_ok() { + None + } else { + input.try(|i| ColorType::parse(context, i)).ok() + } +} + +impl Parse for SVGPaint { + fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { + if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) { + Ok(SVGPaint { + kind: SVGPaintKind::PaintServer(url), + fallback: parse_fallback(context, input), + }) + } else if let Ok(kind) = input.try(SVGPaintKind::parse_ident) { + if let SVGPaintKind::None = kind { + Ok(SVGPaint { + kind: kind, + fallback: None, + }) + } else { + Ok(SVGPaint { + kind: kind, + fallback: parse_fallback(context, input), + }) + } + } else if let Ok(color) = input.try(|i| ColorType::parse(context, i)) { + Ok(SVGPaint { + 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 { + /// ` | | ` + 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 { + /// `[ | | ]#` + Values(Vec), + /// `context-value` + ContextValue, +} + +impl ToCss for SVGStrokeDashArray where LengthType: ToCss { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match self { + &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") + } + } + &SVGStrokeDashArray::ContextValue => { + dest.write_str("context-value") + } + } + } +} + +/// An SVG opacity value accepts `context-{fill,stroke}-opacity` in +/// addition to opacity value. +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Copy, Debug, PartialEq, HasViewportPercentage, ToComputedValue, ToCss)] +pub enum SVGOpacity { + /// `` + Opacity(OpacityType), + /// `context-fill-opacity` + ContextFillOpacity, + /// `context-stroke-opacity` + ContextStrokeOpacity, +} diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 7c51c3868f8..1728d4113d4 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -43,6 +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::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray}; pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing}; pub use self::transform::{TimingFunction, TransformOrigin}; pub use super::generics::grid::GridLine; @@ -64,6 +65,7 @@ pub mod image; pub mod length; pub mod position; pub mod rect; +pub mod svg; pub mod text; pub mod transform; @@ -703,14 +705,6 @@ pub type TrackList = GenericTrackList; /// ` | ` pub type GridTemplateComponent = GenericGridTemplateComponent; -no_viewport_percentage!(SVGPaint); - -/// Specified SVG Paint value -pub type SVGPaint = ::values::generics::SVGPaint; - -/// Specified SVG Paint Kind value -pub type SVGPaintKind = ::values::generics::SVGPaintKind; - /// | | pub type LengthOrPercentageOrNumber = Either; diff --git a/components/style/values/specified/svg.rs b/components/style/values/specified/svg.rs new file mode 100644 index 00000000000..a89ba3ad15e --- /dev/null +++ b/components/style/values/specified/svg.rs @@ -0,0 +1,108 @@ +/* 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::{CommaWithSpace, ParseError, Separator, StyleParseError}; +use values::generics::svg as generic; +use values::specified::{LengthOrPercentageOrNumber, Opacity}; +use values::specified::color::RGBAColor; + +/// Specified SVG Paint value +pub type SVGPaint = generic::SVGPaint; + +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) + } +} + +/// [ | | ]# | context-value +pub type SVGStrokeDashArray = generic::SVGStrokeDashArray; + +impl Parse for SVGStrokeDashArray { + fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) + -> Result> { + 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) + } + } +} + +/// | context-fill-opacity | context-stroke-opacity +pub type SVGOpacity = generic::SVGOpacity; + +impl Parse for SVGOpacity { + fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) + -> Result> { + if let Ok(opacity) = input.try(|i| Opacity::parse(context, i)) { + Ok(generic::SVGOpacity::Opacity(opacity)) + } else if is_context_value_enabled() { + try_match_ident_ignore_ascii_case! { input.expect_ident()?, + "context-fill-opacity" => Ok(generic::SVGOpacity::ContextFillOpacity), + "context-stroke-opacity" => Ok(generic::SVGOpacity::ContextStrokeOpacity), + } + } else { + Err(StyleParseError::UnspecifiedError.into()) + } + } +}