diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index a3109a1f226..e85bc8ec310 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -670,10 +670,11 @@ pub mod basic_shape { use values::computed::position; use values::computed::url::ComputedUrl; use values::generics::basic_shape::{BasicShape as GenericBasicShape, InsetRect, Polygon}; - use values::generics::basic_shape::{Circle, Ellipse, FillRule, PolygonCoord}; + use values::generics::basic_shape::{Circle, Ellipse, FillRule, Path, PolygonCoord}; use values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource}; use values::generics::border::BorderRadius as GenericBorderRadius; use values::generics::rect::Rect; + use values::specified::SVGPathData; impl StyleShapeSource { /// Convert StyleShapeSource to ShapeSource except URL and Image @@ -698,7 +699,34 @@ pub mod basic_shape { Some(ShapeSource::Shape(shape, reference_box)) }, StyleShapeSourceType::URL | StyleShapeSourceType::Image => None, - StyleShapeSourceType::Path => None, + StyleShapeSourceType::Path => { + let path = self.to_svg_path().expect("expect an SVGPathData"); + let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr }; + let fill = if gecko_path.mFillRule == StyleFillRule::Evenodd { + FillRule::Evenodd + } else { + FillRule::Nonzero + }; + Some(ShapeSource::Path(Path { fill, path })) + }, + } + } + + /// Generate a SVGPathData from StyleShapeSource if possible. + fn to_svg_path(&self) -> Option { + use gecko_bindings::structs::StylePathCommand; + use values::specified::svg_path::PathCommand; + match self.mType { + StyleShapeSourceType::Path => { + let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr }; + let result: Vec = + gecko_path.mPath.iter().map(|gecko: &StylePathCommand| { + // unsafe: cbindgen ensures the representation is the same. + unsafe{ ::std::mem::transmute(*gecko) } + }).collect(); + Some(SVGPathData::new(result.into_boxed_slice())) + }, + _ => None, } } } @@ -742,17 +770,9 @@ pub mod basic_shape { impl<'a> From<&'a StyleShapeSource> for OffsetPath { fn from(other: &'a StyleShapeSource) -> Self { - use gecko_bindings::structs::StylePathCommand; - use values::specified::svg_path::{SVGPathData, PathCommand}; match other.mType { StyleShapeSourceType::Path => { - let gecko_path = unsafe { &*other.__bindgen_anon_1.mSVGPath.as_ref().mPtr }; - let result: Vec = - gecko_path.mPath.iter().map(|gecko: &StylePathCommand| { - // unsafe: cbindgen ensures the representation is the same. - unsafe{ ::std::mem::transmute(*gecko) } - }).collect(); - OffsetPath::Path(SVGPathData::new(result.into_boxed_slice())) + OffsetPath::Path(other.to_svg_path().expect("Cannot convert to SVGPathData")) }, StyleShapeSourceType::None => OffsetPath::none(), StyleShapeSourceType::Shape | diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 76d61838708..9e9a6835a42 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -3683,27 +3683,16 @@ fn static_assert() { ${impl_simple_type_with_conversion("touch_action")} pub fn set_offset_path(&mut self, v: longhands::offset_path::computed_value::T) { - use gecko_bindings::bindings::{Gecko_NewStyleMotion, Gecko_NewStyleSVGPath}; - use gecko_bindings::bindings::Gecko_SetStyleMotion; + use gecko_bindings::bindings::{Gecko_NewStyleMotion, Gecko_SetStyleMotion}; use gecko_bindings::structs::StyleShapeSourceType; + use values::generics::basic_shape::FillRule; use values::specified::OffsetPath; let motion = unsafe { Gecko_NewStyleMotion().as_mut().unwrap() }; match v { OffsetPath::None => motion.mOffsetPath.mType = StyleShapeSourceType::None, - OffsetPath::Path(servo_path) => { - motion.mOffsetPath.mType = StyleShapeSourceType::Path; - let gecko_path = unsafe { - let ref mut source = motion.mOffsetPath; - Gecko_NewStyleSVGPath(source); - &mut source.__bindgen_anon_1.mSVGPath.as_mut().mPtr.as_mut().unwrap().mPath - }; - unsafe { gecko_path.set_len(servo_path.commands().len() as u32) }; - debug_assert_eq!(gecko_path.len(), servo_path.commands().len()); - for (servo, gecko) in servo_path.commands().iter().zip(gecko_path.iter_mut()) { - // unsafe: cbindgen ensures the representation is the same. - *gecko = unsafe { transmute(*servo) }; - } + OffsetPath::Path(p) => { + set_style_svg_path(&mut motion.mOffsetPath, &p, FillRule::Nonzero) }, } unsafe { Gecko_SetStyleMotion(&mut self.gecko.mMotion, motion) }; @@ -4982,6 +4971,35 @@ fn static_assert() { } +// Set SVGPathData to StyleShapeSource. +fn set_style_svg_path( + shape_source: &mut structs::mozilla::StyleShapeSource, + servo_path: &values::specified::svg_path::SVGPathData, + fill: values::generics::basic_shape::FillRule, +) { + use gecko_bindings::bindings::Gecko_NewStyleSVGPath; + use gecko_bindings::structs::StyleShapeSourceType; + + // Setup type. + shape_source.mType = StyleShapeSourceType::Path; + + // Setup path. + let gecko_path = unsafe { + Gecko_NewStyleSVGPath(shape_source); + &mut shape_source.__bindgen_anon_1.mSVGPath.as_mut().mPtr.as_mut().unwrap() + }; + unsafe { gecko_path.mPath.set_len(servo_path.commands().len() as u32) }; + debug_assert_eq!(gecko_path.mPath.len(), servo_path.commands().len()); + for (servo, gecko) in servo_path.commands().iter().zip(gecko_path.mPath.iter_mut()) { + // unsafe: cbindgen ensures the representation is the same. + *gecko = unsafe { transmute(*servo) }; + } + + // Setup fill-rule. + // unsafe: cbindgen ensures the representation is the same. + gecko_path.mFillRule = unsafe { transmute(fill) }; +} + <%def name="impl_shape_source(ident, gecko_ffi_name)"> pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { use gecko_bindings::bindings::{Gecko_NewBasicShape, Gecko_DestroyShapeSource}; @@ -5021,6 +5039,7 @@ fn static_assert() { ${ident}.mReferenceBox = reference.into(); ${ident}.mType = StyleShapeSourceType::Box; } + ShapeSource::Path(p) => set_style_svg_path(${ident}, &p.path, p.fill), ShapeSource::Shape(servo_shape, maybe_box) => { fn init_shape(${ident}: &mut StyleShapeSource, basic_shape_type: StyleBasicShapeType) -> &mut StyleBasicShape { diff --git a/components/style/values/generics/basic_shape.rs b/components/style/values/generics/basic_shape.rs index ee7455e777d..0eccf011c71 100644 --- a/components/style/values/generics/basic_shape.rs +++ b/components/style/values/generics/basic_shape.rs @@ -12,6 +12,7 @@ use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::border::BorderRadius; use values::generics::position::Position; use values::generics::rect::Rect; +use values::specified::SVGPathData; /// A clipping shape, for `clip-path`. pub type ClippingShape = ShapeSource; @@ -54,6 +55,9 @@ pub enum ShapeSource { #[animation(error)] Box(ReferenceBox), #[animation(error)] + #[css(function)] + Path(Path), + #[animation(error)] None, } @@ -144,6 +148,19 @@ pub enum FillRule { Evenodd, } +/// The path function defined in css-shape-2. +/// +/// https://drafts.csswg.org/css-shapes-2/#funcdef-path +#[css(comma)] +#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)] +pub struct Path { + /// The filling rule for the svg path. + #[css(skip_if = "fill_is_default")] + pub fill: FillRule, + /// The svg path data. + pub path: SVGPathData, +} + // FIXME(nox): Implement ComputeSquaredDistance for T types and stop // using PartialEq here, this will let us derive this impl. impl ComputeSquaredDistance for ShapeSource diff --git a/components/style/values/specified/basic_shape.rs b/components/style/values/specified/basic_shape.rs index 2fa693a1978..824c13f1f01 100644 --- a/components/style/values/specified/basic_shape.rs +++ b/components/style/values/specified/basic_shape.rs @@ -14,9 +14,11 @@ use std::fmt::{self, Write}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; use values::computed::Percentage; use values::generics::basic_shape as generic; -use values::generics::basic_shape::{FillRule, GeometryBox, PolygonCoord, ShapeBox, ShapeSource}; +use values::generics::basic_shape::{FillRule, GeometryBox, Path, PolygonCoord}; +use values::generics::basic_shape::{ShapeBox, ShapeSource}; use values::generics::rect::Rect; use values::specified::LengthOrPercentage; +use values::specified::SVGPathData; use values::specified::border::BorderRadius; use values::specified::image::Image; use values::specified::position::{HorizontalPosition, Position, PositionComponent}; @@ -47,12 +49,42 @@ pub type ShapeRadius = generic::ShapeRadius; /// The specified value of `Polygon` pub type Polygon = generic::Polygon; -impl Parse for ShapeSource +impl Parse for ClippingShape { + #[inline] + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + // |clip-path:path()| is a chrome-only property value support for now. `path()` is + // defined in css-shape-2, but the spec is not stable enough, and we haven't decided + // to make it public yet. However, it has some benefits for the front-end, so we + // implement it. + if context.chrome_rules_enabled() { + if let Ok(p) = input.try(|i| Path::parse(context, i)) { + return Ok(ShapeSource::Path(p)); + } + } + Self::parse_internal(context, input) + } +} + +impl Parse for FloatAreaShape { + #[inline] + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + Self::parse_internal(context, input) + } +} + +impl ShapeSource where ReferenceBox: Parse, ImageOrUrl: Parse, { - fn parse<'i, 't>( + /// The internal parser for ShapeSource. + fn parse_internal<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { @@ -393,3 +425,29 @@ impl Polygon { }) } } + +impl Parse for Path { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + input.expect_function_matching("path")?; + input.parse_nested_block(|i| Self::parse_function_arguments(context, i)) + } +} + +impl Path { + /// Parse the inner arguments of a `path` function. + fn parse_function_arguments<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + let fill = input.try(|i| -> Result<_, ParseError> { + let fill = FillRule::parse(i)?; + i.expect_comma()?; + Ok(fill) + }).unwrap_or_default(); + let path = SVGPathData::parse(context, input)?; + Ok(Path { fill, path }) + } +}