Bug 1914221 - Map pattern/gradientTransform to the transform property. r=longsonr

And fix a typo in a test which got me confused.

Differential Revision: https://phabricator.services.mozilla.com/D220728
This commit is contained in:
Emilio Cobos Álvarez
2024-09-02 17:26:20 +00:00
parent d4df66e699
commit 83dfd545a3
14 changed files with 76 additions and 99 deletions

View File

@@ -1334,38 +1334,43 @@ void SVGElement::UpdateMappedDeclarationBlock() {
const bool lengthAffectsStyle = const bool lengthAffectsStyle =
SVGGeometryProperty::ElementMapsLengthsToStyle(this); SVGGeometryProperty::ElementMapsLengthsToStyle(this);
bool sawTransform = false; bool sawTransform = false;
uint32_t i = 0; uint32_t i = 0;
while (BorrowedAttrInfo info = GetAttrInfoAt(i++)) { while (BorrowedAttrInfo info = GetAttrInfoAt(i++)) {
const nsAttrName* attrName = info.mName; const nsAttrName* attrName = info.mName;
if (!attrName->IsAtom() || !IsAttributeMapped(attrName->Atom())) { if (!attrName->IsAtom()) {
continue; continue;
} }
if (attrName->Atom() == nsGkAtoms::lang && nsAtom* nameAtom = attrName->Atom();
if (!IsAttributeMapped(nameAtom)) {
continue;
}
if (nameAtom == nsGkAtoms::lang &&
HasAttr(kNameSpaceID_XML, nsGkAtoms::lang)) { HasAttr(kNameSpaceID_XML, nsGkAtoms::lang)) {
// xml:lang has precedence, and will get set via Gecko_GetXMLLangValue(). // xml:lang has precedence, and will get set via Gecko_GetXMLLangValue().
continue; continue;
} }
if (lengthAffectsStyle) { if (lengthAffectsStyle) {
auto const* length = GetAnimatedLength(attrName->Atom()); auto const* length = GetAnimatedLength(nameAtom);
if (length && length->HasBaseVal()) { if (length && length->HasBaseVal()) {
// This is an element with geometry property set via SVG attribute, // This is an element with geometry property set via SVG attribute,
// and the attribute is already successfully parsed. We want to go // and the attribute is already successfully parsed. We want to go
// through the optimized path to tell the style system the result // through the optimized path to tell the style system the result
// directly, rather than let it parse the same thing again. // directly, rather than let it parse the same thing again.
mappedAttrParser.TellStyleAlreadyParsedResult(attrName->Atom(), mappedAttrParser.TellStyleAlreadyParsedResult(nameAtom, *length);
*length);
continue; continue;
} }
} }
if (attrName->Atom() == nsGkAtoms::transform) { if (nameAtom == nsGkAtoms::transform ||
nameAtom == nsGkAtoms::patternTransform ||
nameAtom == nsGkAtoms::gradientTransform) {
sawTransform = true; sawTransform = true;
const auto* transform = GetAnimatedTransformList(); const auto* transform = GetAnimatedTransformList();
MOZ_ASSERT(GetTransformListAttrName() == nsGkAtoms::transform); MOZ_ASSERT(GetTransformListAttrName() == nameAtom);
MOZ_ASSERT(transform); MOZ_ASSERT(transform);
// We want to go through the optimized path to tell the style system the // We want to go through the optimized path to tell the style system the
// result directly, rather than let it parse the same thing again. // result directly, rather than let it parse the same thing again.
@@ -1373,7 +1378,7 @@ void SVGElement::UpdateMappedDeclarationBlock() {
continue; continue;
} }
if (attrName->Atom() == nsGkAtoms::d) { if (nameAtom == nsGkAtoms::d) {
const auto* path = GetAnimPathSegList(); const auto* path = GetAnimPathSegList();
// Note: Only SVGPathElement has d attribute. // Note: Only SVGPathElement has d attribute.
MOZ_ASSERT( MOZ_ASSERT(
@@ -1399,7 +1404,7 @@ void SVGElement::UpdateMappedDeclarationBlock() {
nsAutoString value; nsAutoString value;
info.mValue->ToString(value); info.mValue->ToString(value);
mappedAttrParser.ParseMappedAttrValue(attrName->Atom(), value); mappedAttrParser.ParseMappedAttrValue(nameAtom, value);
} }
// We need to map the SVG view's transform if we haven't mapped it already. // We need to map the SVG view's transform if we haven't mapped it already.
@@ -2082,21 +2087,15 @@ void SVGElement::DidChangeTransformList(
void SVGElement::DidAnimateTransformList(int32_t aModType) { void SVGElement::DidAnimateTransformList(int32_t aModType) {
MOZ_ASSERT(GetTransformListAttrName(), MOZ_ASSERT(GetTransformListAttrName(),
"Animating non-existent transform data?"); "Animating non-existent transform data?");
const auto* animTransformList = GetAnimatedTransformList();
nsAtom* transformAttr = GetTransformListAttrName(); const auto* animateMotion = GetAnimateMotionTransform();
if (transformAttr == nsGkAtoms::transform) { if (animateMotion ||
const auto* animTransformList = GetAnimatedTransformList(); (animTransformList && animTransformList->IsAnimating())) {
const auto* animateMotion = GetAnimateMotionTransform(); SMILOverrideStyle()->SetSMILValue(eCSSProperty_transform, animTransformList,
if (animateMotion || animateMotion);
(animTransformList && animTransformList->IsAnimating())) { } else {
SMILOverrideStyle()->SetSMILValue(eCSSProperty_transform, SMILOverrideStyle()->ClearSMILValue(eCSSProperty_transform);
animTransformList, animateMotion);
} else {
SMILOverrideStyle()->ClearSMILValue(eCSSProperty_transform);
}
return;
} }
DidAnimateAttribute(kNameSpaceID_None, transformAttr);
} }
SVGElement::StringAttributesInfo SVGElement::GetStringInfo() { SVGElement::StringAttributesInfo SVGElement::GetStringInfo() {

View File

@@ -208,6 +208,11 @@ already_AddRefed<DOMSVGAnimatedLength> SVGRadialGradientElement::Fr() {
return mLengthAttributes[ATTR_FR].ToDOMAnimatedLength(this); return mLengthAttributes[ATTR_FR].ToDOMAnimatedLength(this);
} }
bool SVGGradientElement::IsAttributeMapped(const nsAtom* aAttribute) const {
return aAttribute == nsGkAtoms::gradientTransform ||
SVGElement::IsAttributeMapped(aAttribute);
}
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// SVGElement methods // SVGElement methods

View File

@@ -45,11 +45,12 @@ class SVGGradientElement : public SVGGradientElementBase {
// nsIContent // nsIContent
nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override = 0; nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override = 0;
virtual SVGAnimatedTransformList* GetAnimatedTransformList( SVGAnimatedTransformList* GetAnimatedTransformList(
uint32_t aFlags = 0) override; uint32_t aFlags = 0) override;
nsStaticAtom* GetTransformListAttrName() const override { nsStaticAtom* GetTransformListAttrName() const override {
return nsGkAtoms::gradientTransform; return nsGkAtoms::gradientTransform;
} }
NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override;
// WebIDL // WebIDL
already_AddRefed<DOMSVGAnimatedEnumeration> GradientUnits(); already_AddRefed<DOMSVGAnimatedEnumeration> GradientUnits();

View File

@@ -66,6 +66,11 @@ already_AddRefed<SVGAnimatedRect> SVGPatternElement::ViewBox() {
return mViewBox.ToSVGAnimatedRect(this); return mViewBox.ToSVGAnimatedRect(this);
} }
bool SVGPatternElement::IsAttributeMapped(const nsAtom* aAttribute) const {
return aAttribute == nsGkAtoms::patternTransform ||
SVGPatternElementBase::IsAttributeMapped(aAttribute);
}
already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
SVGPatternElement::PreserveAspectRatio() { SVGPatternElement::PreserveAspectRatio() {
return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this); return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this);

View File

@@ -41,6 +41,7 @@ class SVGPatternElement final : public SVGPatternElementBase {
public: public:
// nsIContent interface // nsIContent interface
nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override; nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override;
// SVGSVGElement methods: // SVGSVGElement methods:
bool HasValidDimensions() const override; bool HasValidDimensions() const override;

View File

@@ -8,18 +8,12 @@
<rect y="50" width="50" height="50" fill="red"/> <rect y="50" width="50" height="50" fill="red"/>
<rect x="50" y="50" width="50" height="50" fill="blue"/> <rect x="50" y="50" width="50" height="50" fill="blue"/>
</pattern> </pattern>
<pattern id="patternNotRotated" width="1" height="1">
<rect width="50" height="50" fill="blue"/>
<rect x="50" width="50" height="50" fill="red"/>
<rect y="50" width="50" height="50" fill="red"/>
<rect x="50" y="50" width="50" height="50" fill="blue"/>
</pattern>
</defs> </defs>
<rect width="100" height="100" stroke="black" fill="url(#patternRotated)"/> <rect width="100" height="100" stroke="black" fill="url(#patternRotated)"/>
<g transform="translate(100)"> <g transform="translate(100)">
<rect width="100" height="100" stroke="black" fill="url(#patternRotated)"/> <rect width="100" height="100" stroke="black" fill="url(#patternRotated)"/>
</g> </g>
<g transform="translate(200)"> <g transform="translate(200)">
<rect width="100" height="100" stroke="black" fill="url(#patternNotRotated)"/> <rect width="100" height="100" stroke="black" fill="url(#patternRotated)"/>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1017 B

After

Width:  |  Height:  |  Size: 731 B

View File

@@ -37,9 +37,8 @@ function addTransform()
which are not defined on this element are inherited by this element.'). which are not defined on this element are inherited by this element.').
Hence this pattern should look IDENTICAL to patternBase. --> Hence this pattern should look IDENTICAL to patternBase. -->
<pattern xlink:href="#patternBase" id="patternRefWithoutTransform"/> <pattern xlink:href="#patternBase" id="patternRefWithoutTransform"/>
<!-- 3. References the base pattern but patternTransform is defined (although <!-- There's no way to differentiate an explicitly specified (but empty)
empty) and hence the patternTransform should NOT be inherited and this transform from no transform, so this should look IDENTICAL to patternBase -->
pattern should look DIFFERENT to patternBase. -->
<pattern xlink:href="#patternBase" id="patternRefWithTransform" <pattern xlink:href="#patternBase" id="patternRefWithTransform"
patternTransform=""/> patternTransform=""/>
<!-- The case of a patternTransform being supplied by animation is covered by <!-- The case of a patternTransform being supplied by animation is covered by

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -108,15 +108,11 @@ uint16_t SVGGradientFrame::GetSpreadMethod() {
return GetEnumValue(dom::SVGGradientElement::SPREADMETHOD); return GetEnumValue(dom::SVGGradientElement::SPREADMETHOD);
} }
const SVGAnimatedTransformList* SVGGradientFrame::GetGradientTransformList( SVGGradientFrame* SVGGradientFrame::GetGradientTransformFrame(
nsIContent* aDefault) { SVGGradientFrame* aDefault) {
SVGAnimatedTransformList* thisTransformList = if (!StyleDisplay()->mTransform.IsNone()) {
static_cast<dom::SVGGradientElement*>(GetContent()) return this;
->GetAnimatedTransformList(); }
if (thisTransformList && thisTransformList->IsExplicitlySet())
return thisTransformList;
// Before we recurse, make sure we'll break reference loops and over long // Before we recurse, make sure we'll break reference loops and over long
// reference chains: // reference chains:
static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
@@ -124,21 +120,18 @@ const SVGAnimatedTransformList* SVGGradientFrame::GetGradientTransformList(
&sRefChainLengthCounter); &sRefChainLengthCounter);
if (MOZ_UNLIKELY(!refChainGuard.Reference())) { if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
// Break reference chain // Break reference chain
return static_cast<const dom::SVGGradientElement*>(aDefault) return aDefault;
->mGradientTransform.get();
} }
SVGGradientFrame* next = GetReferencedGradient(); if (SVGGradientFrame* next = GetReferencedGradient()) {
return next->GetGradientTransformFrame(aDefault);
return next ? next->GetGradientTransformList(aDefault) }
: static_cast<const dom::SVGGradientElement*>(aDefault) return aDefault;
->mGradientTransform.get();
} }
gfxMatrix SVGGradientFrame::GetGradientTransform( gfxMatrix SVGGradientFrame::GetGradientTransform(
nsIFrame* aSource, const gfxRect* aOverrideBounds) { nsIFrame* aSource, const gfxRect* aOverrideBounds) {
gfxMatrix bboxMatrix; gfxMatrix bboxMatrix;
uint16_t gradientUnits = GetGradientUnits(); uint16_t gradientUnits = GetGradientUnits();
if (gradientUnits != SVG_UNIT_TYPE_USERSPACEONUSE) { if (gradientUnits != SVG_UNIT_TYPE_USERSPACEONUSE) {
NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
@@ -154,16 +147,8 @@ gfxMatrix SVGGradientFrame::GetGradientTransform(
gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y()); gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y());
} }
const SVGAnimatedTransformList* animTransformList = return bboxMatrix.PreMultiply(
GetGradientTransformList(GetContent()); SVGUtils::GetTransformMatrixInUserSpace(GetGradientTransformFrame(this)));
if (!animTransformList) {
return bboxMatrix.PreMultiply(
SVGUtils::GetTransformMatrixInUserSpace(this));
}
gfxMatrix gradientTransform =
animTransformList->GetAnimValue().GetConsolidationMatrix();
return bboxMatrix.PreMultiply(gradientTransform);
} }
dom::SVGLinearGradientElement* SVGGradientFrame::GetLinearGradientWithLength( dom::SVGLinearGradientElement* SVGGradientFrame::GetLinearGradientWithLength(
@@ -294,7 +279,6 @@ already_AddRefed<gfxPattern> SVGGradientFrame::GetPaintServerPattern(
// above since this call can be expensive when "gradientUnits" is set to // above since this call can be expensive when "gradientUnits" is set to
// "objectBoundingBox" (since that requiring a GetBBox() call). // "objectBoundingBox" (since that requiring a GetBBox() call).
gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds); gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds);
if (patternMatrix.IsSingular()) { if (patternMatrix.IsSingular()) {
return nullptr; return nullptr;
} }

View File

@@ -75,8 +75,7 @@ class SVGGradientFrame : public SVGPaintServerFrame {
void GetStops(nsTArray<ColorStop>* aStops, float aGraphicOpacity); void GetStops(nsTArray<ColorStop>* aStops, float aGraphicOpacity);
const SVGAnimatedTransformList* GetGradientTransformList( SVGGradientFrame* GetGradientTransformFrame(SVGGradientFrame* aDefault);
nsIContent* aDefault);
// Will be singular for gradientUnits="objectBoundingBox" with an empty bbox. // Will be singular for gradientUnits="objectBoundingBox" with an empty bbox.
gfxMatrix GetGradientTransform(nsIFrame* aSource, gfxMatrix GetGradientTransform(nsIFrame* aSource,
const gfxRect* aOverrideBounds); const gfxRect* aOverrideBounds);

View File

@@ -439,13 +439,11 @@ uint16_t SVGPatternFrame::GetEnumValue(uint32_t aIndex, nsIContent* aDefault) {
.GetAnimValue(); .GetAnimValue();
} }
SVGAnimatedTransformList* SVGPatternFrame::GetPatternTransformList( SVGPatternFrame* SVGPatternFrame::GetPatternTransformFrame(
nsIContent* aDefault) { SVGPatternFrame* aDefault) {
SVGAnimatedTransformList* thisTransformList = if (!StyleDisplay()->mTransform.IsNone()) {
static_cast<SVGPatternElement*>(GetContent())->GetAnimatedTransformList(); return this;
}
if (thisTransformList && thisTransformList->IsExplicitlySet())
return thisTransformList;
// Before we recurse, make sure we'll break reference loops and over long // Before we recurse, make sure we'll break reference loops and over long
// reference chains: // reference chains:
@@ -454,23 +452,18 @@ SVGAnimatedTransformList* SVGPatternFrame::GetPatternTransformList(
&sRefChainLengthCounter); &sRefChainLengthCounter);
if (MOZ_UNLIKELY(!refChainGuard.Reference())) { if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
// Break reference chain // Break reference chain
return static_cast<SVGPatternElement*>(aDefault)->mPatternTransform.get(); return aDefault;
} }
SVGPatternFrame* next = GetReferencedPattern(); if (SVGPatternFrame* next = GetReferencedPattern()) {
return next ? next->GetPatternTransformList(aDefault) return next->GetPatternTransformFrame(aDefault);
: static_cast<SVGPatternElement*>(aDefault) }
->mPatternTransform.get(); return aDefault;
} }
gfxMatrix SVGPatternFrame::GetPatternTransform() { gfxMatrix SVGPatternFrame::GetPatternTransform() {
SVGAnimatedTransformList* animTransformList = return SVGUtils::GetTransformMatrixInUserSpace(
GetPatternTransformList(GetContent()); GetPatternTransformFrame(this));
if (!animTransformList) {
return SVGUtils::GetTransformMatrixInUserSpace(this);
}
return animTransformList->GetAnimValue().GetConsolidationMatrix();
} }
const SVGAnimatedViewBox& SVGPatternFrame::GetViewBox(nsIContent* aDefault) { const SVGAnimatedViewBox& SVGPatternFrame::GetViewBox(nsIContent* aDefault) {

View File

@@ -80,7 +80,7 @@ class SVGPatternFrame final : public SVGPaintServerFrame {
uint16_t GetEnumValue(uint32_t aIndex) { uint16_t GetEnumValue(uint32_t aIndex) {
return GetEnumValue(aIndex, mContent); return GetEnumValue(aIndex, mContent);
} }
SVGAnimatedTransformList* GetPatternTransformList(nsIContent* aDefault); SVGPatternFrame* GetPatternTransformFrame(SVGPatternFrame* aDefault);
gfxMatrix GetPatternTransform(); gfxMatrix GetPatternTransform();
const SVGAnimatedViewBox& GetViewBox(nsIContent* aDefault); const SVGAnimatedViewBox& GetViewBox(nsIContent* aDefault);
const SVGAnimatedViewBox& GetViewBox() { return GetViewBox(mContent); } const SVGAnimatedViewBox& GetViewBox() { return GetViewBox(mContent); }

View File

@@ -677,6 +677,11 @@ impl<E: TElement> StyleSharingCache<E> {
return; return;
} }
if element.smil_override().is_some() {
debug!("Failing to insert to the cache: SMIL");
return;
}
debug!( debug!(
"Inserting into cache: {:?} with parent {:?}", "Inserting into cache: {:?} with parent {:?}",
element, parent element, parent
@@ -814,6 +819,11 @@ impl<E: TElement> StyleSharingCache<E> {
return None; return None;
} }
if target.element.smil_override().is_some() {
trace!("Miss: SMIL");
return None;
}
if target.matches_user_and_content_rules() != if target.matches_user_and_content_rules() !=
candidate.element.matches_user_and_content_rules() candidate.element.matches_user_and_content_rules()
{ {

View File

@@ -10,16 +10,3 @@
[fill presentation attribute not supported on discard] [fill presentation attribute not supported on discard]
expected: FAIL expected: FAIL
[patternTransform presentation attribute supported on pattern]
expected: FAIL
[patternTransform presentation attribute supported on linearGradient]
expected: FAIL
[patternTransform presentation attribute supported on radialGradient]
expected: FAIL
[transform presentation attribute supported on g]
expected:
if (os == "mac") and not debug: [PASS, FAIL]

View File

@@ -121,7 +121,7 @@ if (CSS.supports("transform", "initial")) {
for (let e of ["linearGradient", "radialGradient"]) { for (let e of ["linearGradient", "radialGradient"]) {
test(function() { test(function() {
assertPresentationAttributeIsSupported(e, "gradientTransform", "scale(2)", "transform"); assertPresentationAttributeIsSupported(e, "gradientTransform", "scale(2)", "transform");
}, `patternTransform presentation attribute supported on ${e}`); }, `gradientTransform presentation attribute supported on ${e}`);
} }