Bug 1958768 - rollup patch of bug 1904891 and bug 1903546 to fix non-scaling-stroke in nested svg cases r=emilio a=diannaS

Differential Revision: https://phabricator.services.mozilla.com/D244710
This commit is contained in:
longsonr 2025-04-14 19:25:39 +00:00
parent 22baf2b144
commit 08c68403f0
18 changed files with 140 additions and 23 deletions

View file

@ -489,7 +489,9 @@ SVGViewportElement* SVGContentUtils::GetNearestViewportElement(
return nullptr;
}
static gfx::Matrix GetCTMInternal(SVGElement* aElement, bool aScreenCTM,
enum class CTMType { NearestViewport, OuterViewport, Screen };
static gfx::Matrix GetCTMInternal(SVGElement* aElement, CTMType aCTMType,
bool aHaveRecursed) {
auto getLocalTransformHelper =
[](SVGElement const* e, bool shouldIncludeChildToUserSpace) -> gfxMatrix {
@ -530,7 +532,8 @@ static gfx::Matrix GetCTMInternal(SVGElement* aElement, bool aScreenCTM,
!ancestor->IsSVGElement(nsGkAtoms::foreignObject)) {
element = static_cast<SVGElement*>(ancestor);
matrix *= getLocalTransformHelper(element, true);
if (!aScreenCTM && SVGContentUtils::EstablishesViewport(element)) {
if (aCTMType == CTMType::NearestViewport &&
SVGContentUtils::EstablishesViewport(element)) {
if (!element->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol)) {
NS_ERROR("New (SVG > 1.1) SVG viewport establishing element?");
return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
@ -540,7 +543,7 @@ static gfx::Matrix GetCTMInternal(SVGElement* aElement, bool aScreenCTM,
}
ancestor = ancestor->GetFlattenedTreeParent();
}
if (!aScreenCTM) {
if (aCTMType == CTMType::NearestViewport) {
// didn't find a nearestViewportElement
return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
}
@ -568,13 +571,16 @@ static gfx::Matrix GetCTMInternal(SVGElement* aElement, bool aScreenCTM,
int32_t appUnitsPerCSSPixel = AppUnitsPerCSSPixel();
tm.PostTranslate(NSAppUnitsToFloatPixels(bp.left, appUnitsPerCSSPixel),
NSAppUnitsToFloatPixels(bp.top, appUnitsPerCSSPixel));
if (aCTMType == CTMType::OuterViewport) {
return tm;
}
}
if (!ancestor || !ancestor->IsElement()) {
return tm;
}
if (auto* ancestorSVG = SVGElement::FromNode(ancestor)) {
return tm * GetCTMInternal(ancestorSVG, true, true);
return tm * GetCTMInternal(ancestorSVG, aCTMType, true);
}
nsIFrame* parentFrame = frame->GetParent();
if (!parentFrame) {
@ -612,16 +618,20 @@ static gfx::Matrix GetCTMInternal(SVGElement* aElement, bool aScreenCTM,
}
return nearestSVGAncestor
? tm * GetCTMInternal(static_cast<SVGElement*>(nearestSVGAncestor),
true, true)
aCTMType, true)
: tm;
}
gfx::Matrix SVGContentUtils::GetCTM(SVGElement* aElement) {
return GetCTMInternal(aElement, false, false);
return GetCTMInternal(aElement, CTMType::NearestViewport, false);
}
gfx::Matrix SVGContentUtils::GetOuterViewportCTM(SVGElement* aElement) {
return GetCTMInternal(aElement, CTMType::OuterViewport, false);
}
gfx::Matrix SVGContentUtils::GetScreenCTM(SVGElement* aElement) {
return GetCTMInternal(aElement, true, false);
return GetCTMInternal(aElement, CTMType::Screen, false);
}
void SVGContentUtils::RectilinearGetStrokeBounds(

View file

@ -197,6 +197,8 @@ class SVGContentUtils {
static Matrix GetCTM(dom::SVGElement* aElement);
static Matrix GetOuterViewportCTM(dom::SVGElement* aElement);
static Matrix GetScreenCTM(dom::SVGElement* aElement);
/**

View file

@ -201,7 +201,7 @@ 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 = SVGContentUtils::GetOuterViewportCTM(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

View file

@ -174,7 +174,6 @@ rusty-enums = [
"mozilla::StyleMaskMode",
"mozilla::StyleScrollBehavior",
"mozilla::StyleColorInterpolation",
"mozilla::StyleVectorEffect",
"mozilla::StyleBackfaceVisibility",
"mozilla::StyleBlend",
"mozilla::StyleMaskComposite",
@ -591,6 +590,7 @@ 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 = "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" },

View file

@ -549,9 +549,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 };

View file

@ -907,7 +907,7 @@ nsStyleSVGReset::nsStyleSVGReset()
mLightingColor(StyleColor::White()),
mStopOpacity(1.0f),
mFloodOpacity(1.0f),
mVectorEffect(StyleVectorEffect::None),
mVectorEffect(StyleVectorEffect::NONE),
mMaskType(StyleMaskType::Luminance),
mD(StyleDProperty::None()) {
MOZ_COUNT_CTOR(nsStyleSVGReset);

View file

@ -1910,7 +1910,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleSVGReset {
bool HasMask() const;
bool HasNonScalingStroke() const {
return mVectorEffect == mozilla::StyleVectorEffect::NonScalingStroke;
return mVectorEffect.HasNonScalingStroke();
}
// geometry properties

View file

@ -9788,7 +9788,7 @@ var gCSSProperties = {
applies_to_first_line: true,
initial_values: ["none"],
other_values: ["non-scaling-stroke"],
invalid_values: [],
invalid_values: ["none non-scaling-stroke"],
},
"-moz-window-dragging": {
domProp: "MozWindowDragging",

View file

@ -1078,8 +1078,9 @@ bool SVGUtils::GetNonScalingStrokeTransform(const nsIFrame* aFrame,
MOZ_ASSERT(aFrame->GetContent()->IsSVGElement(), "should be an SVG element");
*aUserToOuterSVG = ThebesMatrix(
SVGContentUtils::GetCTM(static_cast<SVGElement*>(aFrame->GetContent())));
SVGElement* content = static_cast<SVGElement*>(aFrame->GetContent());
*aUserToOuterSVG =
ThebesMatrix(SVGContentUtils::GetOuterViewportCTM(content));
return aUserToOuterSVG->HasNonTranslation() && !aUserToOuterSVG->IsSingular();
}

View file

@ -617,6 +617,7 @@ class Longhand(Property):
"TouchAction",
"TransformStyle",
"UserSelect",
"VectorEffect",
"WordBreak",
"XSpan",
"XTextScale",

View file

@ -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",

View file

@ -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};

View file

@ -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<Color, ComputedUrl>;

View file

@ -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;

View file

@ -402,3 +402,39 @@ 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)]
/// https://svgwg.org/svg2-draft/coords.html#VectorEffects
pub struct VectorEffect(u8);
bitflags! {
impl VectorEffect: u8 {
/// `none`
const NONE = 0;
/// `non-scaling-stroke`
const NON_SCALING_STROKE = 1 << 0;
}
}
impl VectorEffect {
/// Returns the initial value of vector-effect
#[inline]
pub fn none() -> Self {
Self::NONE
}
}

View file

@ -300,6 +300,7 @@ include = [
"ComputedLinearStop",
"PiecewiseLinearFunction",
"BeforeFlag",
"VectorEffect",
"XTextScale",
"Zoom",
"TransitionProperty",
@ -547,6 +548,10 @@ renaming_overrides_prefixing = true
inline bool IsRight() const;
"""
"VectorEffect" = """
bool HasNonScalingStroke() const { return bool(*this & StyleVectorEffect::NON_SCALING_STROKE); }
"""
# TODO(emilio): Add hooks to cbindgen to be able to generate [[nodiscard]]
# on the functions.
"Owned" = """

View file

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<title>non-scaling-stroke with outer viewport transform</title>
<link rel="help" href="https://svgwg.org/svg2-draft/painting.html#PaintingVectorEffects" />
<link rel="match" href="green-100x100.svg" />
<body>
<style>
body {
border: none;
margin: 0;
width: 200px;
height: 200px;
}
svg {
transform-origin: center;
transform-bxox: fill-box;
}
#outer {
width: 100px;
height: 100px;
transform: scale(2);
}
#inner {
transform: scale(0.5);
}
rect {
fill: red;
stroke: green;
stroke-width: 75px;
vector-effect: non-scaling-stroke;
}
</style>
<svg id="outer">
<svg id="inner">
<rect width="75" height="75"/>
</svg>
</svg>
</body>
</html>

View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vector-effect test: parsing vector-effect with invalid values</title>
<link rel="help" href="https://www.w3.org/TR/SVG2/coords.html#VectorEffects">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
</head>
<body>
<script>
test_invalid_value("vector-effect", "none none");
test_invalid_value("vector-effect", "none non-scaling-stroke");
test_invalid_value("vector-effect", "non-scaling-stroke viewport screen");
test_invalid_value("vector-effect", "none viewport");
test_invalid_value("vector-effect", "none screen");
test_invalid_value("vector-effect", "viewport");
test_invalid_value("vector-effect", "screen");
test_invalid_value("vector-effect", "screen non-scaling-stroke");
// The following were removed by https://github.com/w3c/svgwg/issues/582
test_invalid_value("vector-effect", "non-scaling-stroke viewport");
test_invalid_value("vector-effect", "non-scaling-stroke screen");
</script>
</body>
</html>