diff --git a/dom/smil/SMILCompositor.cpp b/dom/smil/SMILCompositor.cpp index 17363a5ddf92..bd1bae189852 100644 --- a/dom/smil/SMILCompositor.cpp +++ b/dom/smil/SMILCompositor.cpp @@ -6,6 +6,7 @@ #include "SMILCompositor.h" +#include "mozilla/dom/SVGSVGElement.h" #include "nsComputedDOMStyle.h" #include "nsCSSProps.h" #include "nsHashKeys.h" @@ -147,19 +148,21 @@ nsCSSPropertyID SMILCompositor::GetCSSPropertyToAnimate() const { // If we are animating the 'width' or 'height' of an outer SVG // element we should animate it as a CSS property, but for other elements - // (e.g. ) we should animate it as a length attribute. - // The easiest way to test for an outer SVG element, is to see if it is an - // SVG-namespace element mapping its width/height attribute to style. - // - // If we have animation of 'width' or 'height' on an SVG element that is - // NOT mapping that attributes to style then it must not be an outermost SVG - // element so we should return eCSSProperty_UNKNOWN to indicate that we - // should animate as an attribute instead. + // in SVG namespace (e.g. ) we should animate it as a length attribute. if ((mKey.mAttributeName == nsGkAtoms::width || mKey.mAttributeName == nsGkAtoms::height) && - mKey.mElement->GetNameSpaceID() == kNameSpaceID_SVG && - !mKey.mElement->IsAttributeMapped(mKey.mAttributeName)) { - return eCSSProperty_UNKNOWN; + mKey.mElement->GetNameSpaceID() == kNameSpaceID_SVG) { + // Not an element. + if (!mKey.mElement->IsSVGElement(nsGkAtoms::svg)) { + return eCSSProperty_UNKNOWN; + } + + // An inner element + if (static_cast(*mKey.mElement).IsInner()) { + return eCSSProperty_UNKNOWN; + } + + // Indeed an outer element, fall through. } return propID; diff --git a/dom/svg/SVGElement.cpp b/dom/svg/SVGElement.cpp index 9c023336f356..c0f4ba589757 100644 --- a/dom/svg/SVGElement.cpp +++ b/dom/svg/SVGElement.cpp @@ -30,6 +30,7 @@ #include "nsAttrValueOrString.h" #include "nsCSSProps.h" #include "nsContentUtils.h" +#include "nsDOMCSSAttrDeclaration.h" #include "nsICSSDeclaration.h" #include "nsIContentInlines.h" #include "mozilla/dom/Document.h" @@ -53,6 +54,7 @@ #include "SVGAnimatedOrient.h" #include "SVGAnimatedString.h" #include "SVGAnimatedViewBox.h" +#include "SVGGeometryProperty.h" #include "SVGMotionSMILAttr.h" #include @@ -1020,6 +1022,41 @@ already_AddRefed SVGElement::ClassName() { return mClassAttribute.ToDOMAnimatedString(this); } +/* static */ +bool SVGElement::UpdateDeclarationBlockFromLength( + DeclarationBlock& aBlock, nsCSSPropertyID aPropId, + const SVGAnimatedLength& aLength, ValToUse aValToUse) { + aBlock.AssertMutable(); + + float value; + if (aValToUse == ValToUse::Anim) { + value = aLength.GetAnimValInSpecifiedUnits(); + } else { + MOZ_ASSERT(aValToUse == ValToUse::Base); + value = aLength.GetBaseValInSpecifiedUnits(); + } + + // SVG parser doesn't check non-negativity of some parsed value, + // we should not pass those to CSS side. + if (value < 0 && + SVGGeometryProperty::IsNonNegativeGeometryProperty(aPropId)) { + return false; + } + + nsCSSUnit cssUnit = SVGGeometryProperty::SpecifiedUnitTypeToCSSUnit( + aLength.GetSpecifiedUnitType()); + + if (cssUnit == eCSSUnit_Percent) { + Servo_DeclarationBlock_SetPercentValue(aBlock.Raw(), aPropId, + value / 100.f); + } else { + Servo_DeclarationBlock_SetLengthValue(aBlock.Raw(), aPropId, value, + cssUnit); + } + + return true; +} + //------------------------------------------------------------------------ // Helper class: MappedAttrParser, for parsing values of mapped attributes @@ -1391,6 +1428,15 @@ void SVGElement::DidChangeLength(uint8_t aAttrEnum, } void SVGElement::DidAnimateLength(uint8_t aAttrEnum) { + if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) { + nsCSSPropertyID propId = + SVGGeometryProperty::AttrEnumToCSSPropId(this, aAttrEnum); + + SMILOverrideStyle()->SetSMILValue(propId, + GetLengthInfo().mLengths[aAttrEnum]); + return; + } + ClearAnyCachedPath(); nsIFrame* frame = GetPrimaryFrame(); diff --git a/dom/svg/SVGElement.h b/dom/svg/SVGElement.h index 0a745ecc1fdc..e43546f9af30 100644 --- a/dom/svg/SVGElement.h +++ b/dom/svg/SVGElement.h @@ -165,6 +165,12 @@ class SVGElement : public SVGElementBase // nsIContent virtual bool HasValidDimensions() const { return true; } void SetLength(nsAtom* aName, const SVGAnimatedLength& aLength); + enum class ValToUse { Base, Anim }; + static bool UpdateDeclarationBlockFromLength(DeclarationBlock& aBlock, + nsCSSPropertyID aPropId, + const SVGAnimatedLength& aLength, + ValToUse aValToUse); + nsAttrValue WillChangeLength(uint8_t aAttrEnum); nsAttrValue WillChangeNumberPair(uint8_t aAttrEnum); nsAttrValue WillChangeIntegerPair(uint8_t aAttrEnum); diff --git a/dom/svg/SVGGeometryProperty.cpp b/dom/svg/SVGGeometryProperty.cpp index 884cde708f86..62e1e87006b6 100644 --- a/dom/svg/SVGGeometryProperty.cpp +++ b/dom/svg/SVGGeometryProperty.cpp @@ -69,6 +69,19 @@ nsCSSPropertyID AttrEnumToCSSPropId(const SVGElement* aElement, return eCSSProperty_UNKNOWN; } +bool IsNonNegativeGeometryProperty(nsCSSPropertyID aProp) { + return aProp == eCSSProperty_r || aProp == eCSSProperty_rx || + aProp == eCSSProperty_ry || aProp == eCSSProperty_width || + aProp == eCSSProperty_height; +} + +bool ElementMapsLengthsToStyle(SVGElement const* aElement) { + return aElement->IsSVGElement(nsGkAtoms::rect) || + aElement->IsSVGElement(nsGkAtoms::circle) || + aElement->IsSVGElement(nsGkAtoms::ellipse) || + aElement->IsSVGElement(nsGkAtoms::foreignObject); +} + } // namespace SVGGeometryProperty } // namespace dom } // namespace mozilla diff --git a/dom/svg/SVGGeometryProperty.h b/dom/svg/SVGGeometryProperty.h index de08c2e43557..b28ec64335fe 100644 --- a/dom/svg/SVGGeometryProperty.h +++ b/dom/svg/SVGGeometryProperty.h @@ -152,6 +152,9 @@ nsCSSUnit SpecifiedUnitTypeToCSSUnit(uint8_t aSpecifiedUnit); nsCSSPropertyID AttrEnumToCSSPropId(const SVGElement* aElement, uint8_t aAttrEnum); +bool IsNonNegativeGeometryProperty(nsCSSPropertyID aProp); +bool ElementMapsLengthsToStyle(SVGElement const* aElement); + } // namespace SVGGeometryProperty } // namespace dom } // namespace mozilla diff --git a/dom/svg/SVGViewportElement.h b/dom/svg/SVGViewportElement.h index 60f34ee11fe5..2419ad577e13 100644 --- a/dom/svg/SVGViewportElement.h +++ b/dom/svg/SVGViewportElement.h @@ -124,6 +124,17 @@ class SVGViewportElement : public SVGGraphicsElement { mViewportHeight = aSize.height; } + /** + * Returns true if either this is an SVG element that is the child of + * another non-foreignObject SVG element, or this is a SVG element + * this is the root of a use-element shadow tree. + */ + bool IsInner() const { + const nsIContent* parent = GetFlattenedTreeParent(); + return parent && parent->IsSVGElement() && + !parent->IsSVGElement(nsGkAtoms::foreignObject); + } + // WebIDL already_AddRefed ViewBox(); already_AddRefed PreserveAspectRatio(); @@ -139,17 +150,6 @@ class SVGViewportElement : public SVGGraphicsElement { return IsInUncomposedDoc() && !GetParent(); } - /** - * Returns true if either this is an SVG element that is the child of - * another non-foreignObject SVG element, or this is a SVG element - * this is the root of a use-element shadow tree. - */ - bool IsInner() const { - const nsIContent* parent = GetFlattenedTreeParent(); - return parent && parent->IsSVGElement() && - !parent->IsSVGElement(nsGkAtoms::foreignObject); - } - /** * Returns the explicit or default preserveAspectRatio, unless we're * synthesizing a viewBox, in which case it returns the "none" value. diff --git a/layout/style/nsDOMCSSAttrDeclaration.cpp b/layout/style/nsDOMCSSAttrDeclaration.cpp index 8da15e47db47..3ed24764e1c3 100644 --- a/layout/style/nsDOMCSSAttrDeclaration.cpp +++ b/layout/style/nsDOMCSSAttrDeclaration.cpp @@ -10,10 +10,12 @@ #include "mozilla/dom/Document.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/SVGElement.h" #include "mozilla/dom/MutationEventBinding.h" #include "mozilla/DeclarationBlock.h" #include "mozilla/InternalMutationEvent.h" #include "mozilla/SMILCSSValueType.h" +#include "mozilla/SMILValue.h" #include "mozAutoDocUpdate.h" #include "nsIURI.h" #include "nsNodeUtils.h" @@ -133,9 +135,10 @@ nsDOMCSSAttributeDeclaration::GetParsingEnvironment( }; } -nsresult nsDOMCSSAttributeDeclaration::SetSMILValue( - const nsCSSPropertyID aPropID, const SMILValue& aValue) { +template +nsresult nsDOMCSSAttributeDeclaration::SetSMILValueHelper(SetterFunc aFunc) { MOZ_ASSERT(mIsSMILOverride); + // No need to do the ActiveLayerTracker / ScrollLinkedEffectDetector bits, // since we're in a SMIL animation anyway, no need to try to detect we're a // scripted animation. @@ -147,7 +150,9 @@ nsresult nsDOMCSSAttributeDeclaration::SetSMILValue( } mozAutoDocUpdate autoUpdate(DocToUpdate(), true); RefPtr decl = olddecl->EnsureMutable(); - bool changed = SMILCSSValueType::SetPropertyValues(aValue, *decl); + + bool changed = aFunc(*decl); + if (changed) { // We can pass nullptr as the latter param, since this is // mIsSMILOverride == true case. @@ -156,6 +161,23 @@ nsresult nsDOMCSSAttributeDeclaration::SetSMILValue( return NS_OK; } +nsresult nsDOMCSSAttributeDeclaration::SetSMILValue( + const nsCSSPropertyID /*aPropID*/, const SMILValue& aValue) { + MOZ_ASSERT(aValue.mType == &SMILCSSValueType::sSingleton, + "We should only try setting a CSS value type"); + return SetSMILValueHelper([&aValue](DeclarationBlock& aDecl) { + return SMILCSSValueType::SetPropertyValues(aValue, aDecl); + }); +} + +nsresult nsDOMCSSAttributeDeclaration::SetSMILValue( + const nsCSSPropertyID aPropID, const SVGAnimatedLength& aLength) { + return SetSMILValueHelper([aPropID, &aLength](DeclarationBlock& aDecl) { + return SVGElement::UpdateDeclarationBlockFromLength( + aDecl, aPropID, aLength, SVGElement::ValToUse::Anim); + }); +} + nsresult nsDOMCSSAttributeDeclaration::SetPropertyValue( const nsCSSPropertyID aPropID, const nsAString& aValue, nsIPrincipal* aSubjectPrincipal) { diff --git a/layout/style/nsDOMCSSAttrDeclaration.h b/layout/style/nsDOMCSSAttrDeclaration.h index 53d2451bce3e..3790df92e56b 100644 --- a/layout/style/nsDOMCSSAttrDeclaration.h +++ b/layout/style/nsDOMCSSAttrDeclaration.h @@ -18,6 +18,7 @@ struct RawServoUnlockedDeclarationBlock; namespace mozilla { class SMILValue; +class SVGAnimatedLength; namespace dom { class DomGroup; @@ -29,6 +30,7 @@ class nsDOMCSSAttributeDeclaration final : public nsDOMCSSDeclaration { public: typedef mozilla::dom::Element Element; typedef mozilla::SMILValue SMILValue; + typedef mozilla::SVGAnimatedLength SVGAnimatedLength; nsDOMCSSAttributeDeclaration(Element* aContent, bool aIsSMILOverride); NS_DECL_CYCLE_COLLECTING_ISUPPORTS @@ -45,7 +47,9 @@ class nsDOMCSSAttributeDeclaration final : public nsDOMCSSDeclaration { nsINode* GetParentObject() override { return mElement; } - nsresult SetSMILValue(const nsCSSPropertyID aPropID, const SMILValue&); + nsresult SetSMILValue(const nsCSSPropertyID aPropID, const SMILValue& aValue); + nsresult SetSMILValue(const nsCSSPropertyID aPropID, + const SVGAnimatedLength& aLength); nsresult SetPropertyValue(const nsCSSPropertyID aPropID, const nsAString& aValue, @@ -79,6 +83,10 @@ class nsDOMCSSAttributeDeclaration final : public nsDOMCSSDeclaration { * than the inline style rule). */ const bool mIsSMILOverride; + + private: + template + nsresult SetSMILValueHelper(SetterFunc aFunc); }; #endif /* nsDOMCSSAttributeDeclaration_h */