diff --git a/components/style/properties/longhand/inherited_svg.mako.rs b/components/style/properties/longhand/inherited_svg.mako.rs index 2cc878a1a29..11e39bd0917 100644 --- a/components/style/properties/longhand/inherited_svg.mako.rs +++ b/components/style/properties/longhand/inherited_svg.mako.rs @@ -130,151 +130,11 @@ ${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)", animation_value_type="discrete", spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")} -<%helpers:longhand name="paint-order" - animation_value_type="discrete" - gecko_pref="svg.paint-order.enabled" - products="gecko" - spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder"> - use std::fmt; - use style_traits::ToCss; - - /// The specified value for a single CSS paint-order property. - #[repr(u8)] - #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, ToCss)] - pub enum PaintOrder { - Normal = 0, - Fill = 1, - Stroke = 2, - Markers = 3, - } - - /// Number of non-normal components. - const COUNT: u8 = 3; - - /// Number of bits for each component - const SHIFT: u8 = 2; - - /// Mask with above bits set - const MASK: u8 = 0b11; - - /// The specified value is tree `PaintOrder` values packed into the - /// bitfields below, as a six-bit field, of 3 two-bit pairs - /// - /// Each pair can be set to FILL, STROKE, or MARKERS - /// Lowest significant bit pairs are highest priority. - /// `normal` is the empty bitfield. The three pairs are - /// never zero in any case other than `normal`. - /// - /// Higher priority values, i.e. the values specified first, - /// will be painted first (and may be covered by paintings of lower priority) - #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)] - pub struct SpecifiedValue(pub u8); - - impl SpecifiedValue { - fn normal() -> Self { - SpecifiedValue(0) - } - } - - pub mod computed_value { - pub use super::SpecifiedValue as T; - } - - pub fn get_initial_value() -> SpecifiedValue { - SpecifiedValue::normal() - } - - impl SpecifiedValue { - fn order_at(&self, pos: u8) -> PaintOrder { - // Safe because PaintOrder covers all possible patterns. - unsafe { ::std::mem::transmute((self.0 >> pos * SHIFT) & MASK) } - } - } - - pub fn parse<'i, 't>( - _context: &ParserContext, - input: &mut Parser<'i, 't> - ) -> Result> { - if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) { - return Ok(SpecifiedValue::normal()) - } - - let mut value = 0; - // bitfield representing what we've seen so far - // bit 1 is fill, bit 2 is stroke, bit 3 is markers - let mut seen = 0; - let mut pos = 0; - - loop { - let result: Result<_, ParseError> = input.try(|input| { - try_match_ident_ignore_ascii_case! { input, - "fill" => Ok(PaintOrder::Fill), - "stroke" => Ok(PaintOrder::Stroke), - "markers" => Ok(PaintOrder::Markers), - } - }); - - match result { - Ok(val) => { - if (seen & (1 << val as u8)) != 0 { - // don't parse the same ident twice - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - } - - value |= (val as u8) << (pos * SHIFT); - seen |= 1 << (val as u8); - pos += 1; - } - Err(_) => break, - } - } - - if value == 0 { - // Couldn't find any keyword - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - } - - // fill in rest - for i in pos..COUNT { - for paint in 0..COUNT { - // if not seen, set bit at position, mark as seen - if (seen & (1 << paint)) == 0 { - seen |= 1 << paint; - value |= paint << (i * SHIFT); - break; - } - } - } - - Ok(SpecifiedValue(value)) - } - - impl ToCss for SpecifiedValue { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - if self.0 == 0 { - return dest.write_str("normal") - } - - let mut last_pos_to_serialize = 0; - for i in (1..COUNT).rev() { - let component = self.order_at(i); - let earlier_component = self.order_at(i - 1); - if component < earlier_component { - last_pos_to_serialize = i - 1; - break; - } - } - - for pos in 0..last_pos_to_serialize + 1 { - if pos != 0 { - dest.write_str(" ")? - } - self.order_at(pos).to_css(dest)?; - } - Ok(()) - } - } - +${helpers.predefined_type("paint-order", "SVGPaintOrder", "computed::SVGPaintOrder::normal()", + products="gecko", + animation_value_type="discrete", + gecko_pref="svg.paint-order.enabled", + spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder")} <%helpers:vector_longhand name="-moz-context-properties" animation_value_type="none" diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 7141dba4e12..a127b8e58b0 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -60,7 +60,8 @@ pub use self::list::ListStyleType; pub use self::outline::OutlineStyle; pub use self::percentage::Percentage; pub use self::position::{Position, GridAutoFlow, GridTemplateAreas}; -pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray, SVGWidth}; +pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind}; +pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; pub use self::table::XSpan; pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextAlign, TextOverflow, WordSpacing}; pub use self::time::Time; diff --git a/components/style/values/computed/svg.rs b/components/style/values/computed/svg.rs index e478599f069..56f47f098bc 100644 --- a/components/style/values/computed/svg.rs +++ b/components/style/values/computed/svg.rs @@ -11,6 +11,8 @@ use values::computed::{NonNegativeNumber, NonNegativeLengthOrPercentage, Number} use values::computed::Opacity; use values::generics::svg as generic; +pub use values::specified::SVGPaintOrder; + /// Computed SVG Paint value pub type SVGPaint = generic::SVGPaint; /// Computed SVG Paint Kind value diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index d5c93a1fc1c..e2b16607957 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -56,7 +56,8 @@ pub use self::outline::OutlineStyle; pub use self::rect::LengthOrNumberRect; pub use self::percentage::Percentage; pub use self::position::{Position, PositionComponent, GridAutoFlow, GridTemplateAreas}; -pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray, SVGWidth}; +pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind}; +pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; pub use self::table::XSpan; pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextDecorationLine}; pub use self::text::{TextAlign, TextAlignKeyword, TextOverflow, WordSpacing}; diff --git a/components/style/values/specified/svg.rs b/components/style/values/specified/svg.rs index 8f6b62a71e9..63f14b741e0 100644 --- a/components/style/values/specified/svg.rs +++ b/components/style/values/specified/svg.rs @@ -6,7 +6,8 @@ use cssparser::Parser; use parser::{Parse, ParserContext}; -use style_traits::{CommaWithSpace, ParseError, Separator, StyleParseErrorKind}; +use std::fmt; +use style_traits::{CommaWithSpace, ParseError, Separator, StyleParseErrorKind, ToCss}; use values::generics::svg as generic; use values::specified::{LengthOrPercentage, NonNegativeLengthOrPercentage, NonNegativeNumber}; use values::specified::{Number, Opacity, SpecifiedUrl}; @@ -124,3 +125,138 @@ impl Parse for SVGOpacity { } } } + +/// The specified value for a single CSS paint-order property. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, ToCss)] +pub enum PaintOrder { + /// `normal` variant + Normal = 0, + /// `fill` variant + Fill = 1, + /// `stroke` variant + Stroke = 2, + /// `markers` variant + Markers = 3, +} + +/// Number of non-normal components +const PAINT_ORDER_COUNT: u8 = 3; + +/// Number of bits for each component +const PAINT_ORDER_SHIFT: u8 = 2; + +/// Mask with above bits set +const PAINT_ORDER_MASK: u8 = 0b11; + +/// The specified value is tree `PaintOrder` values packed into the +/// bitfields below, as a six-bit field, of 3 two-bit pairs +/// +/// Each pair can be set to FILL, STROKE, or MARKERS +/// Lowest significant bit pairs are highest priority. +/// `normal` is the empty bitfield. The three pairs are +/// never zero in any case other than `normal`. +/// +/// Higher priority values, i.e. the values specified first, +/// will be painted first (and may be covered by paintings of lower priority) +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)] +pub struct SVGPaintOrder(pub u8); + +impl SVGPaintOrder { + /// Get default `paint-order` with `0` + pub fn normal() -> Self { + SVGPaintOrder(0) + } + + /// Get variant of `paint-order` + pub fn order_at(&self, pos: u8) -> PaintOrder { + // Safe because PaintOrder covers all possible patterns. + unsafe { ::std::mem::transmute((self.0 >> pos * PAINT_ORDER_SHIFT) & PAINT_ORDER_MASK) } + } +} + +impl Parse for SVGPaintOrder { + fn parse<'i, 't>( + _context: &ParserContext, + input: &mut Parser<'i, 't> + ) -> Result> { + if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) { + return Ok(SVGPaintOrder::normal()) + } + + let mut value = 0; + // bitfield representing what we've seen so far + // bit 1 is fill, bit 2 is stroke, bit 3 is markers + let mut seen = 0; + let mut pos = 0; + + loop { + let result: Result<_, ParseError> = input.try(|input| { + try_match_ident_ignore_ascii_case! { input, + "fill" => Ok(PaintOrder::Fill), + "stroke" => Ok(PaintOrder::Stroke), + "markers" => Ok(PaintOrder::Markers), + } + }); + + match result { + Ok(val) => { + if (seen & (1 << val as u8)) != 0 { + // don't parse the same ident twice + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } + + value |= (val as u8) << (pos * PAINT_ORDER_SHIFT); + seen |= 1 << (val as u8); + pos += 1; + } + Err(_) => break, + } + } + + if value == 0 { + // Couldn't find any keyword + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } + + // fill in rest + for i in pos..PAINT_ORDER_COUNT { + for paint in 0..PAINT_ORDER_COUNT { + // if not seen, set bit at position, mark as seen + if (seen & (1 << paint)) == 0 { + seen |= 1 << paint; + value |= paint << (i * PAINT_ORDER_SHIFT); + break; + } + } + } + + Ok(SVGPaintOrder(value)) + } +} + +impl ToCss for SVGPaintOrder { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + if self.0 == 0 { + return dest.write_str("normal") + } + + let mut last_pos_to_serialize = 0; + for i in (1..PAINT_ORDER_COUNT).rev() { + let component = self.order_at(i); + let earlier_component = self.order_at(i - 1); + if component < earlier_component { + last_pos_to_serialize = i - 1; + break; + } + } + + for pos in 0..last_pos_to_serialize + 1 { + if pos != 0 { + dest.write_str(" ")? + } + self.order_at(pos).to_css(dest)?; + } + Ok(()) + } +}