diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 3c22ab1a8bb0..7df1e1fab678 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -19,7 +19,7 @@ pub use super::{Auto, Either, None_}; #[cfg(feature = "gecko")] pub use super::specified::{AlignJustifyContent, AlignJustifySelf}; pub use super::specified::{Angle, BorderStyle, GridLine, Time, UrlOrNone}; -pub use super::specified::url::UrlExtraData; +pub use super::specified::url::{SpecifiedUrl, UrlExtraData}; pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto}; pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNone, LengthOrNone}; pub use self::position::Position; @@ -185,6 +185,61 @@ pub type Number = CSSFloat; pub type Opacity = CSSFloat; +/// An SVG paint value +/// +/// https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +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. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum SVGPaintKind { + /// `none` + None, + /// `` + Color(CSSColor), + /// `url(...)` + PaintServer(SpecifiedUrl), + /// `context-fill` + ContextFill, + /// `context-stroke` + ContextStroke, +} + +impl ToCss for SVGPaintKind { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + SVGPaintKind::None => dest.write_str("none"), + SVGPaintKind::ContextStroke => dest.write_str("context-stroke"), + SVGPaintKind::ContextFill => dest.write_str("context-fill"), + SVGPaintKind::Color(ref color) => color.to_css(dest), + SVGPaintKind::PaintServer(ref server) => server.to_css(dest), + } + } +} + +impl ToCss for SVGPaint { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + self.kind.to_css(dest)?; + if let Some(ref fallback) = self.fallback { + fallback.to_css(dest)?; + } + Ok(()) + } +} + + #[derive(Clone, PartialEq, Eq, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[allow(missing_docs)] diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index e79d68b77774..2987f1cf4592 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -670,6 +670,158 @@ impl Shadow { } } +/// An SVG paint value +/// +/// https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +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. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum SVGPaintKind { + /// `none` + None, + /// `` + Color(CSSColor), + /// `url(...)` + PaintServer(SpecifiedUrl), + /// `context-fill` + ContextFill, + /// `context-stroke` + ContextStroke, +} + +impl SVGPaintKind { + fn parse_ident(input: &mut Parser) -> Result { + Ok(match_ignore_ascii_case! { input.expect_ident()?, + "none" => SVGPaintKind::None, + "context-fill" => SVGPaintKind::ContextFill, + "context-stroke" => SVGPaintKind::ContextStroke, + _ => return Err(()) + }) + } +} + +impl Parse for SVGPaint { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) { + let fallback = input.try(|i| CSSColor::parse(context, i)); + Ok(SVGPaint { + kind: SVGPaintKind::PaintServer(url), + fallback: fallback.ok(), + }) + } else if let Ok(kind) = input.try(SVGPaintKind::parse_ident) { + if kind == SVGPaintKind::None { + Ok(SVGPaint { + kind: kind, + fallback: None, + }) + } else { + let fallback = input.try(|i| CSSColor::parse(context, i)); + Ok(SVGPaint { + kind: kind, + fallback: fallback.ok(), + }) + } + } else if let Ok(color) = input.try(|i| CSSColor::parse(context, i)) { + Ok(SVGPaint { + kind: SVGPaintKind::Color(color), + fallback: None, + }) + } else { + Err(()) + } + } +} + +impl ToCss for SVGPaintKind { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + SVGPaintKind::None => dest.write_str("none"), + SVGPaintKind::ContextStroke => dest.write_str("context-stroke"), + SVGPaintKind::ContextFill => dest.write_str("context-fill"), + SVGPaintKind::Color(ref color) => color.to_css(dest), + SVGPaintKind::PaintServer(ref server) => server.to_css(dest), + } + } +} + +impl ToCss for SVGPaint { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + self.kind.to_css(dest)?; + if let Some(ref fallback) = self.fallback { + fallback.to_css(dest)?; + } + Ok(()) + } +} + + +impl ToComputedValue for SVGPaint { + type ComputedValue = super::computed::SVGPaint; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + super::computed::SVGPaint { + kind: self.kind.to_computed_value(context), + fallback: self.fallback.as_ref().map(|f| f.to_computed_value(context)) + } + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + SVGPaint { + kind: ToComputedValue::from_computed_value(&computed.kind), + fallback: computed.fallback.as_ref().map(ToComputedValue::from_computed_value) + } + } +} + +impl ToComputedValue for SVGPaintKind { + type ComputedValue = super::computed::SVGPaintKind; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + SVGPaintKind::None => super::computed::SVGPaintKind::None, + SVGPaintKind::ContextStroke => super::computed::SVGPaintKind::ContextStroke, + SVGPaintKind::ContextFill => super::computed::SVGPaintKind::ContextFill, + SVGPaintKind::Color(ref color) => { + super::computed::SVGPaintKind::Color(color.to_computed_value(context)) + } + SVGPaintKind::PaintServer(ref server) => { + super::computed::SVGPaintKind::PaintServer(server.to_computed_value(context)) + } + } + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + super::computed::SVGPaintKind::None => SVGPaintKind::None, + super::computed::SVGPaintKind::ContextStroke => SVGPaintKind::ContextStroke, + super::computed::SVGPaintKind::ContextFill => SVGPaintKind::ContextFill, + super::computed::SVGPaintKind::Color(ref color) => { + SVGPaintKind::Color(ToComputedValue::from_computed_value(color)) + } + super::computed::SVGPaintKind::PaintServer(ref server) => { + SVGPaintKind::PaintServer(ToComputedValue::from_computed_value(server)) + } + } + } +} + impl HasViewportPercentage for ClipRect { fn has_viewport_percentage(&self) -> bool {