diff --git a/dom/svg/SVGGeometryElement.cpp b/dom/svg/SVGGeometryElement.cpp index 149dae8ba4e0..57b641f631e3 100644 --- a/dom/svg/SVGGeometryElement.cpp +++ b/dom/svg/SVGGeometryElement.cpp @@ -201,7 +201,9 @@ bool SVGGeometryElement::IsPointInStroke(const DOMPointInit& aPoint) { SVGGeometryProperty::DoForComputedStyle(this, [&](const ComputedStyle* s) { // Per spec, we should take vector-effect into account. if (s->StyleSVGReset()->HasNonScalingStroke()) { - auto mat = SVGContentUtils::GetCTM(this); + auto mat = s->StyleSVGReset()->mVectorEffect.IsScreen() + ? SVGContentUtils::GetScreenCTM(this) + : SVGContentUtils::GetCTM(this); if (mat.HasNonTranslation()) { // We have non-scaling-stroke as well as a non-translation transform. // We should transform the path first then apply the stroke on the diff --git a/layout/style/ServoBindings.toml b/layout/style/ServoBindings.toml index 6334af42e3a7..2d07183281ba 100644 --- a/layout/style/ServoBindings.toml +++ b/layout/style/ServoBindings.toml @@ -174,7 +174,6 @@ rusty-enums = [ "mozilla::StyleMaskMode", "mozilla::StyleScrollBehavior", "mozilla::StyleColorInterpolation", - "mozilla::StyleVectorEffect", "mozilla::StyleBackfaceVisibility", "mozilla::StyleBlend", "mozilla::StyleMaskComposite", @@ -588,6 +587,9 @@ cbindgen-types = [ { gecko = "StylePageOrientation", servo = "crate::values::generics::page::PageOrientation" }, { gecko = "StylePageSize", servo = "crate::values::computed::page::PageSize" }, { gecko = "StyleDProperty", servo = "crate::values::specified::svg::DProperty" }, + { gecko = "StyleVectorEffectType", servo = "crate::values::specified::svg::VectorEffectType" }, + { gecko = "StyleCoordinateSpace", servo = "crate::values::specified::svg::CoordinateSpace" }, + { gecko = "StyleVectorEffect", servo = "crate::values::specified::svg::VectorEffect" }, { gecko = "StyleImageRendering", servo = "crate::values::computed::ImageRendering" }, { gecko = "StylePrintColorAdjust", servo = "crate::values::computed::PrintColorAdjust" }, { gecko = "StyleForcedColorAdjust", servo = "crate::values::computed::ForcedColorAdjust" }, diff --git a/layout/style/nsStyleConsts.h b/layout/style/nsStyleConsts.h index a16f921383ac..43e22fe490ce 100644 --- a/layout/style/nsStyleConsts.h +++ b/layout/style/nsStyleConsts.h @@ -555,9 +555,6 @@ enum class StyleColorInterpolation : uint8_t { Linearrgb = 2, }; -// vector-effect -enum class StyleVectorEffect : uint8_t { None = 0, NonScalingStroke = 1 }; - // 3d Transforms - Backface visibility enum class StyleBackfaceVisibility : uint8_t { Hidden = 0, Visible = 1 }; diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 5018a61c80c0..639746b4edbc 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -909,7 +909,6 @@ nsStyleSVGReset::nsStyleSVGReset() mLightingColor(StyleColor::White()), mStopOpacity(1.0f), mFloodOpacity(1.0f), - mVectorEffect(StyleVectorEffect::None), mMaskType(StyleMaskType::Luminance), mD(StyleDProperty::None()) { MOZ_COUNT_CTOR(nsStyleSVGReset); diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 3eaae4047ae4..4300ea3a3ddc 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1912,7 +1912,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleSVGReset { bool HasMask() const; bool HasNonScalingStroke() const { - return mVectorEffect == mozilla::StyleVectorEffect::NonScalingStroke; + return mVectorEffect.HasNonScalingStroke(); } // geometry properties diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 118e3a7d1346..ab3c8fdcbe1a 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -9787,8 +9787,18 @@ var gCSSProperties = { applies_to_first_letter: true, applies_to_first_line: true, initial_values: ["none"], - other_values: ["non-scaling-stroke"], - invalid_values: [], + other_values: [ + "non-scaling-stroke", + "non-scaling-stroke viewport", + "non-scaling-stroke screen", + ], + invalid_values: [ + "none non-scaling-stroke", + "non-scaling-stroke viewport screen", + "none viewport", + "none screen", + "viewport non-scaling-stroke", + ], }, "-moz-window-dragging": { domProp: "MozWindowDragging", diff --git a/layout/svg/SVGUtils.cpp b/layout/svg/SVGUtils.cpp index 11c9028fb58f..704403e81b5c 100644 --- a/layout/svg/SVGUtils.cpp +++ b/layout/svg/SVGUtils.cpp @@ -1078,8 +1078,11 @@ bool SVGUtils::GetNonScalingStrokeTransform(const nsIFrame* aFrame, MOZ_ASSERT(aFrame->GetContent()->IsSVGElement(), "should be an SVG element"); - *aUserToOuterSVG = ThebesMatrix( - SVGContentUtils::GetCTM(static_cast(aFrame->GetContent()))); + SVGElement* content = static_cast(aFrame->GetContent()); + *aUserToOuterSVG = + ThebesMatrix(aFrame->StyleSVGReset()->mVectorEffect.IsScreen() + ? SVGContentUtils::GetScreenCTM(content) + : SVGContentUtils::GetCTM(content)); return aUserToOuterSVG->HasNonTranslation() && !aUserToOuterSVG->IsSingular(); } diff --git a/servo/components/style/properties/data.py b/servo/components/style/properties/data.py index c4599b2e08b4..b84e2b4b86cb 100644 --- a/servo/components/style/properties/data.py +++ b/servo/components/style/properties/data.py @@ -617,6 +617,7 @@ class Longhand(Property): "TouchAction", "TransformStyle", "UserSelect", + "VectorEffect", "WordBreak", "XSpan", "XTextScale", diff --git a/servo/components/style/properties/longhands/svg.mako.rs b/servo/components/style/properties/longhands/svg.mako.rs index 12998d553bf4..300a8667f8b2 100644 --- a/servo/components/style/properties/longhands/svg.mako.rs +++ b/servo/components/style/properties/longhands/svg.mako.rs @@ -4,11 +4,11 @@ <%namespace name="helpers" file="/helpers.mako.rs" /> -${helpers.single_keyword( +${helpers.predefined_type( "vector-effect", - "none non-scaling-stroke", + "VectorEffect", + "computed::VectorEffect::none()", engines="gecko", - gecko_enum_prefix="StyleVectorEffect", animation_value_type="discrete", spec="https://svgwg.org/svg2-draft/coords.html#VectorEffects", affects="layout", diff --git a/servo/components/style/values/computed/mod.rs b/servo/components/style/values/computed/mod.rs index 03138bba20cb..840715ecbcc5 100644 --- a/servo/components/style/values/computed/mod.rs +++ b/servo/components/style/values/computed/mod.rs @@ -104,7 +104,7 @@ pub use self::rect::NonNegativeLengthOrNumberRect; pub use self::resolution::Resolution; pub use self::svg::{DProperty, MozContextProperties}; pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind}; -pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; +pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth, VectorEffect}; pub use self::text::HyphenateCharacter; pub use self::text::TextUnderlinePosition; pub use self::text::{InitialLetter, LetterSpacing, LineBreak, TextIndent}; diff --git a/servo/components/style/values/computed/svg.rs b/servo/components/style/values/computed/svg.rs index 6efdfca36b7b..c7bb772360e6 100644 --- a/servo/components/style/values/computed/svg.rs +++ b/servo/components/style/values/computed/svg.rs @@ -10,7 +10,7 @@ use crate::values::computed::{LengthPercentage, NonNegativeLengthPercentage, Opa use crate::values::generics::svg as generic; use crate::Zero; -pub use crate::values::specified::{DProperty, MozContextProperties, SVGPaintOrder}; +pub use crate::values::specified::{DProperty, MozContextProperties, SVGPaintOrder, VectorEffect}; /// Computed SVG Paint value pub type SVGPaint = generic::GenericSVGPaint; diff --git a/servo/components/style/values/specified/mod.rs b/servo/components/style/values/specified/mod.rs index 6cdd2156f898..ed061d3aad65 100644 --- a/servo/components/style/values/specified/mod.rs +++ b/servo/components/style/values/specified/mod.rs @@ -96,7 +96,7 @@ pub use self::rect::NonNegativeLengthOrNumberRect; pub use self::resolution::Resolution; pub use self::svg::{DProperty, MozContextProperties}; pub use self::svg::{SVGLength, SVGOpacity, SVGPaint}; -pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; +pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth, VectorEffect}; pub use self::svg_path::SVGPathData; pub use self::text::HyphenateCharacter; pub use self::text::RubyPosition; diff --git a/servo/components/style/values/specified/svg.rs b/servo/components/style/values/specified/svg.rs index 8ab2dbb2234c..f60ece83ce28 100644 --- a/servo/components/style/values/specified/svg.rs +++ b/servo/components/style/values/specified/svg.rs @@ -402,3 +402,122 @@ impl Parse for DProperty { Ok(DProperty::Path(path_data)) } } + +#[derive( + Clone, + Copy, + Debug, + Default, + Eq, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[css(bitflags(single = "none", mixed = "non-scaling-stroke"))] +#[repr(C)] +/// Values for vector-effect: +/// https://svgwg.org/svg2-draft/coords.html#VectorEffects +pub struct VectorEffectType(u8); +bitflags! { + impl VectorEffectType: u8 { + /// `none` + const NONE = 0; + /// `non-scaling-stroke` + const NON_SCALING_STROKE = 1 << 0; + } +} + +#[allow(missing_docs)] +impl VectorEffectType { + pub fn is_none(&self) -> bool { + *self == VectorEffectType::NONE + } +} + +#[derive( + Clone, + Copy, + Debug, + Default, + Eq, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(C)] +/// co-ordinate space for vector-effect: +/// https://svgwg.org/svg2-draft/coords.html#VectorEffects +pub enum CoordinateSpace { + #[default] + /// `viewport` + Viewport, + /// `screen` + Screen, +} + +#[allow(missing_docs)] +impl CoordinateSpace { + pub fn is_viewport(&self) -> bool { + *self == Self::Viewport + } +} + +#[derive( + Clone, + Copy, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToCss, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[repr(C)] +/// Specified value for the vector-effect property. +/// (The spec grammar gives +/// `none | [ non-scaling-stroke | non-scaling-size | non-rotation | fixed-position ]+ [ viewport | screen ]?`.) +/// https://svgwg.org/svg2-draft/coords.html#VectorEffects +pub struct VectorEffect { + /// none or non-scaling-stroke + pub effect_type: VectorEffectType, + /// screen or viewport + #[css(skip_if = "CoordinateSpace::is_viewport")] + pub coordinate_space: CoordinateSpace, +} + +#[allow(missing_docs)] +impl VectorEffect { + #[inline] + pub fn none() -> Self { + Self { + effect_type: VectorEffectType::NONE, + coordinate_space: CoordinateSpace::Viewport, + } + } +} + +impl Parse for VectorEffect { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + let effect_type = VectorEffectType::parse(context, input)?; + if effect_type.is_none() { + return Ok(Self::none()); + } + let coordinate_space = input.try_parse(CoordinateSpace::parse).unwrap_or(CoordinateSpace::Viewport); + Ok(Self { effect_type, coordinate_space }) + } +} diff --git a/servo/ports/geckolib/cbindgen.toml b/servo/ports/geckolib/cbindgen.toml index 217fcc0c2d6c..1ba036f65ded 100644 --- a/servo/ports/geckolib/cbindgen.toml +++ b/servo/ports/geckolib/cbindgen.toml @@ -307,6 +307,7 @@ include = [ "ComputedLinearStop", "PiecewiseLinearFunction", "BeforeFlag", + "VectorEffect", "XTextScale", "Zoom", "TransitionProperty", @@ -555,6 +556,15 @@ renaming_overrides_prefixing = true inline bool IsRight() const; """ +"VectorEffect" = """ + StyleVectorEffect() + : effect_type(StyleVectorEffectType::NONE), + coordinate_space(StyleCoordinateSpace::Viewport) {} + + bool HasNonScalingStroke() const { return bool(effect_type & StyleVectorEffectType::NON_SCALING_STROKE); } + bool IsScreen() const { return coordinate_space == StyleCoordinateSpace::Screen; } +""" + # TODO(emilio): Add hooks to cbindgen to be able to generate [[nodiscard]] # on the functions. "Owned" = """ diff --git a/testing/web-platform/tests/svg/painting/reftests/non-scaling-stroke-003.html b/testing/web-platform/tests/svg/painting/reftests/non-scaling-stroke-003.html new file mode 100644 index 000000000000..147bf814d30f --- /dev/null +++ b/testing/web-platform/tests/svg/painting/reftests/non-scaling-stroke-003.html @@ -0,0 +1,33 @@ + + +non-scaling-stroke with screen transform + + + + + + + + + diff --git a/testing/web-platform/tests/svg/styling/vector-effect-invalid.html b/testing/web-platform/tests/svg/styling/vector-effect-invalid.html new file mode 100644 index 000000000000..ec49b88c5995 --- /dev/null +++ b/testing/web-platform/tests/svg/styling/vector-effect-invalid.html @@ -0,0 +1,23 @@ + + + + +vector-effect test: parsing vector-effect with invalid values + + + + + + + + + diff --git a/testing/web-platform/tests/svg/styling/vector-effect-valid.html b/testing/web-platform/tests/svg/styling/vector-effect-valid.html new file mode 100644 index 000000000000..9563db0523ba --- /dev/null +++ b/testing/web-platform/tests/svg/styling/vector-effect-valid.html @@ -0,0 +1,19 @@ + + + + +vector-effect test: parsing vector-effect with invalid values + + + + + + + + +