1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/SVGElement.h"
9 #include "mozilla/AlreadyAddRefed.h"
10 #include "mozilla/dom/MutationEventBinding.h"
11 #include "mozilla/dom/MutationObservers.h"
12 #include "mozilla/dom/CSSRuleBinding.h"
13 #include "mozilla/dom/SVGElementBinding.h"
14 #include "mozilla/dom/SVGGeometryElement.h"
15 #include "mozilla/dom/SVGLengthBinding.h"
16 #include "mozilla/dom/SVGSVGElement.h"
17 #include "mozilla/dom/SVGTests.h"
18 #include "mozilla/dom/SVGUnitTypesBinding.h"
19 #include "mozilla/dom/Element.h"
21 #include "mozilla/ArrayUtils.h"
22 #include "mozilla/DebugOnly.h"
23 #include "mozilla/DeclarationBlock.h"
24 #include "mozilla/EventListenerManager.h"
25 #include "mozilla/InternalMutationEvent.h"
26 #include "mozilla/PresShell.h"
27 #include "mozilla/RestyleManager.h"
28 #include "mozilla/SMILAnimationController.h"
29 #include "mozilla/StaticPrefs_layout.h"
30 #include "mozilla/SVGContentUtils.h"
31 #include "mozilla/SVGObserverUtils.h"
32 #include "mozilla/Unused.h"
34 #include "mozAutoDocUpdate.h"
35 #include "nsAttrValueOrString.h"
36 #include "nsCSSProps.h"
37 #include "nsCSSValue.h"
38 #include "nsContentUtils.h"
39 #include "nsDOMCSSAttrDeclaration.h"
40 #include "nsICSSDeclaration.h"
41 #include "nsIContentInlines.h"
42 #include "mozilla/dom/Document.h"
44 #include "nsGkAtoms.h"
46 #include "nsQueryObject.h"
47 #include "nsLayoutUtils.h"
48 #include "SVGAnimatedNumberList.h"
49 #include "SVGAnimatedLengthList.h"
50 #include "SVGAnimatedPointList.h"
51 #include "SVGAnimatedPathSegList.h"
52 #include "SVGAnimatedTransformList.h"
53 #include "SVGAnimatedBoolean.h"
54 #include "SVGAnimatedEnumeration.h"
55 #include "SVGAnimatedInteger.h"
56 #include "SVGAnimatedIntegerPair.h"
57 #include "SVGAnimatedLength.h"
58 #include "SVGAnimatedNumber.h"
59 #include "SVGAnimatedNumberPair.h"
60 #include "SVGAnimatedOrient.h"
61 #include "SVGAnimatedString.h"
62 #include "SVGAnimatedViewBox.h"
63 #include "SVGGeometryProperty.h"
64 #include "SVGMotionSMILAttr.h"
67 // This is needed to ensure correct handling of calls to the
68 // vararg-list methods in this file:
69 // SVGElement::GetAnimated{Length,Number,Integer}Values
70 // See bug 547964 for details:
71 static_assert(sizeof(void*) == sizeof(nullptr),
72 "nullptr should be the correct size");
74 nsresult
NS_NewSVGElement(
75 mozilla::dom::Element
** aResult
,
76 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
) {
77 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo(aNodeInfo
);
78 auto* nim
= nodeInfo
->NodeInfoManager();
79 RefPtr
<mozilla::dom::SVGElement
> it
=
80 new (nim
) mozilla::dom::SVGElement(nodeInfo
.forget());
81 nsresult rv
= it
->Init();
91 namespace mozilla::dom
{
92 using namespace SVGUnitTypes_Binding
;
94 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGElement
)
96 // Use the CC variant of this, even though this class does not define
97 // a new CC participant, to make QIing to the CC interfaces faster.
98 NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(SVGElement
, SVGElementBase
,
101 SVGEnumMapping
SVGElement::sSVGUnitTypesMap
[] = {
102 {nsGkAtoms::userSpaceOnUse
, SVG_UNIT_TYPE_USERSPACEONUSE
},
103 {nsGkAtoms::objectBoundingBox
, SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
},
106 SVGElement::SVGElement(already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
107 : SVGElementBase(std::move(aNodeInfo
)) {}
109 SVGElement::~SVGElement() = default;
111 JSObject
* SVGElement::WrapNode(JSContext
* aCx
,
112 JS::Handle
<JSObject
*> aGivenProto
) {
113 return SVGElement_Binding::Wrap(aCx
, this, aGivenProto
);
116 template <typename Value
, typename Info
>
117 void SVGElement::AttributesInfo
<Value
, Info
>::ResetAll() {
118 for (uint32_t i
= 0; i
< mCount
; ++i
) {
123 template <typename Value
, typename Info
>
124 void SVGElement::AttributesInfo
<Value
, Info
>::CopyAllFrom(
125 const AttributesInfo
& aOther
) {
126 MOZ_DIAGNOSTIC_ASSERT(mCount
== aOther
.mCount
,
127 "Should only be called on clones");
128 for (uint32_t i
= 0; i
< mCount
; ++i
) {
129 mValues
[i
] = aOther
.mValues
[i
];
134 void SVGElement::LengthAttributesInfo::Reset(uint8_t aAttrEnum
) {
135 mValues
[aAttrEnum
].Init(mInfos
[aAttrEnum
].mCtxType
, aAttrEnum
,
136 mInfos
[aAttrEnum
].mDefaultValue
,
137 mInfos
[aAttrEnum
].mDefaultUnitType
);
141 void SVGElement::LengthListAttributesInfo::Reset(uint8_t aAttrEnum
) {
142 mValues
[aAttrEnum
].ClearBaseValue(aAttrEnum
);
147 void SVGElement::NumberListAttributesInfo::Reset(uint8_t aAttrEnum
) {
148 MOZ_ASSERT(aAttrEnum
< mCount
, "Bad attr enum");
149 mValues
[aAttrEnum
].ClearBaseValue(aAttrEnum
);
154 void SVGElement::NumberAttributesInfo::Reset(uint8_t aAttrEnum
) {
155 mValues
[aAttrEnum
].Init(aAttrEnum
, mInfos
[aAttrEnum
].mDefaultValue
);
159 void SVGElement::NumberPairAttributesInfo::Reset(uint8_t aAttrEnum
) {
160 mValues
[aAttrEnum
].Init(aAttrEnum
, mInfos
[aAttrEnum
].mDefaultValue1
,
161 mInfos
[aAttrEnum
].mDefaultValue2
);
165 void SVGElement::IntegerAttributesInfo::Reset(uint8_t aAttrEnum
) {
166 mValues
[aAttrEnum
].Init(aAttrEnum
, mInfos
[aAttrEnum
].mDefaultValue
);
170 void SVGElement::IntegerPairAttributesInfo::Reset(uint8_t aAttrEnum
) {
171 mValues
[aAttrEnum
].Init(aAttrEnum
, mInfos
[aAttrEnum
].mDefaultValue1
,
172 mInfos
[aAttrEnum
].mDefaultValue2
);
176 void SVGElement::BooleanAttributesInfo::Reset(uint8_t aAttrEnum
) {
177 mValues
[aAttrEnum
].Init(aAttrEnum
, mInfos
[aAttrEnum
].mDefaultValue
);
181 void SVGElement::StringListAttributesInfo::Reset(uint8_t aAttrEnum
) {
182 mValues
[aAttrEnum
].Clear();
187 void SVGElement::EnumAttributesInfo::Reset(uint8_t aAttrEnum
) {
188 mValues
[aAttrEnum
].Init(aAttrEnum
, mInfos
[aAttrEnum
].mDefaultValue
);
192 void SVGElement::StringAttributesInfo::Reset(uint8_t aAttrEnum
) {
193 mValues
[aAttrEnum
].Init(aAttrEnum
);
196 nsresult
SVGElement::CopyInnerTo(mozilla::dom::Element
* aDest
) {
197 nsresult rv
= Element::CopyInnerTo(aDest
);
198 NS_ENSURE_SUCCESS(rv
, rv
);
200 auto* dest
= static_cast<SVGElement
*>(aDest
);
202 // cloning a node must retain its internal nonce slot
203 if (auto* nonce
= static_cast<nsString
*>(GetProperty(nsGkAtoms::nonce
))) {
204 dest
->SetNonce(*nonce
);
207 // If our destination is a print document, copy all the relevant length values
208 // etc so that they match the state of the original node.
209 if (aDest
->OwnerDoc()->IsStaticDocument()) {
210 LengthAttributesInfo lengthInfo
= GetLengthInfo();
211 dest
->GetLengthInfo().CopyAllFrom(lengthInfo
);
212 if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) {
213 for (uint32_t i
= 0; i
< lengthInfo
.mCount
; i
++) {
214 nsCSSPropertyID propId
=
215 SVGGeometryProperty::AttrEnumToCSSPropId(this, i
);
217 // We don't map use element width/height currently. We can remove this
219 if (propId
!= eCSSProperty_UNKNOWN
) {
220 dest
->SMILOverrideStyle()->SetSMILValue(propId
,
221 lengthInfo
.mValues
[i
]);
225 dest
->GetNumberInfo().CopyAllFrom(GetNumberInfo());
226 dest
->GetNumberPairInfo().CopyAllFrom(GetNumberPairInfo());
227 dest
->GetIntegerInfo().CopyAllFrom(GetIntegerInfo());
228 dest
->GetIntegerPairInfo().CopyAllFrom(GetIntegerPairInfo());
229 dest
->GetBooleanInfo().CopyAllFrom(GetBooleanInfo());
230 if (const auto* orient
= GetAnimatedOrient()) {
231 *dest
->GetAnimatedOrient() = *orient
;
233 if (const auto* viewBox
= GetAnimatedViewBox()) {
234 *dest
->GetAnimatedViewBox() = *viewBox
;
236 if (const auto* preserveAspectRatio
= GetAnimatedPreserveAspectRatio()) {
237 *dest
->GetAnimatedPreserveAspectRatio() = *preserveAspectRatio
;
239 dest
->GetEnumInfo().CopyAllFrom(GetEnumInfo());
240 dest
->GetStringInfo().CopyAllFrom(GetStringInfo());
241 dest
->GetLengthListInfo().CopyAllFrom(GetLengthListInfo());
242 dest
->GetNumberListInfo().CopyAllFrom(GetNumberListInfo());
243 if (const auto* pointList
= GetAnimatedPointList()) {
244 *dest
->GetAnimatedPointList() = *pointList
;
246 if (const auto* pathSegList
= GetAnimPathSegList()) {
247 *dest
->GetAnimPathSegList() = *pathSegList
;
248 dest
->SMILOverrideStyle()->SetSMILValue(nsCSSPropertyID::eCSSProperty_d
,
251 if (const auto* transformList
= GetAnimatedTransformList()) {
252 *dest
->GetAnimatedTransformList(DO_ALLOCATE
) = *transformList
;
254 if (const auto* animateMotionTransform
= GetAnimateMotionTransform()) {
255 dest
->SetAnimateMotionTransform(animateMotionTransform
);
257 if (const auto* smilOverrideStyleDecoration
=
258 GetSMILOverrideStyleDeclaration()) {
259 RefPtr
<DeclarationBlock
> declClone
= smilOverrideStyleDecoration
->Clone();
260 declClone
->SetDirty();
261 dest
->SetSMILOverrideStyleDeclaration(*declClone
);
268 //----------------------------------------------------------------------
269 // SVGElement methods
271 void SVGElement::DidAnimateClass() {
272 // For Servo, snapshot the element before we change it.
273 PresShell
* presShell
= OwnerDoc()->GetPresShell();
275 if (nsPresContext
* presContext
= presShell
->GetPresContext()) {
276 presContext
->RestyleManager()->ClassAttributeWillBeChangedBySMIL(this);
281 mClassAttribute
.GetAnimValue(src
, this);
282 if (!mClassAnimAttr
) {
283 mClassAnimAttr
= MakeUnique
<nsAttrValue
>();
285 mClassAnimAttr
->ParseAtomArray(src
);
287 // FIXME(emilio): This re-selector-matches, but we do the snapshot stuff right
288 // above... Is this needed anymore?
290 presShell
->RestyleForAnimation(this, RestyleHint::RESTYLE_SELF
);
292 DidAnimateAttribute(kNameSpaceID_None
, nsGkAtoms::_class
);
295 nsresult
SVGElement::Init() {
296 // Set up length attributes - can't do this in the constructor
297 // because we can't do a virtual call at that point
299 GetLengthInfo().ResetAll();
300 GetNumberInfo().ResetAll();
301 GetNumberPairInfo().ResetAll();
302 GetIntegerInfo().ResetAll();
303 GetIntegerPairInfo().ResetAll();
304 GetBooleanInfo().ResetAll();
305 GetEnumInfo().ResetAll();
307 if (SVGAnimatedOrient
* orient
= GetAnimatedOrient()) {
311 if (SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox()) {
315 if (SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
316 GetAnimatedPreserveAspectRatio()) {
317 preserveAspectRatio
->Init();
320 GetLengthListInfo().ResetAll();
321 GetNumberListInfo().ResetAll();
323 // No need to reset SVGPointList since the default value is always the same
326 // No need to reset SVGPathData since the default value is always the same
329 GetStringInfo().ResetAll();
333 //----------------------------------------------------------------------
336 //----------------------------------------------------------------------
337 // nsIContent methods
339 nsresult
SVGElement::BindToTree(BindContext
& aContext
, nsINode
& aParent
) {
340 nsresult rv
= SVGElementBase::BindToTree(aContext
, aParent
);
341 NS_ENSURE_SUCCESS(rv
, rv
);
343 // Hide any nonce from the DOM, but keep the internal value of the
344 // nonce by copying and resetting the internal nonce value.
345 if (HasFlag(NODE_HAS_NONCE_AND_HEADER_CSP
) && IsInComposedDoc() &&
346 OwnerDoc()->GetBrowsingContext()) {
347 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
348 "SVGElement::ResetNonce::Runnable",
349 [self
= RefPtr
<SVGElement
>(this)]() {
351 self
->GetNonce(nonce
);
352 self
->SetAttr(kNameSpaceID_None
, nsGkAtoms::nonce
, u
""_ns
, true);
353 self
->SetNonce(nonce
);
360 void SVGElement::AfterSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
361 const nsAttrValue
* aValue
,
362 const nsAttrValue
* aOldValue
,
363 nsIPrincipal
* aSubjectPrincipal
, bool aNotify
) {
364 if (IsEventAttributeName(aName
) && aValue
) {
365 MOZ_ASSERT(aValue
->Type() == nsAttrValue::eString
,
366 "Expected string value for script body");
367 SetEventHandler(GetEventNameForAttr(aName
), aValue
->GetStringValue());
370 // The nonce will be copied over to an internal slot and cleared from the
371 // Element within BindToTree to avoid CSS Selector nonce exfiltration if
372 // the CSP list contains a header-delivered CSP.
373 if (nsGkAtoms::nonce
== aName
&& kNameSpaceID_None
== aNamespaceID
) {
375 SetNonce(aValue
->GetStringValue());
376 if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) {
377 SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP
);
384 return SVGElementBase::AfterSetAttr(aNamespaceID
, aName
, aValue
, aOldValue
,
385 aSubjectPrincipal
, aNotify
);
388 bool SVGElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
389 const nsAString
& aValue
,
390 nsIPrincipal
* aMaybeScriptedPrincipal
,
391 nsAttrValue
& aResult
) {
393 bool foundMatch
= false;
394 bool didSetResult
= false;
396 if (aNamespaceID
== kNameSpaceID_None
) {
397 // Check for SVGAnimatedLength attribute
398 LengthAttributesInfo lengthInfo
= GetLengthInfo();
401 for (i
= 0; i
< lengthInfo
.mCount
; i
++) {
402 if (aAttribute
== lengthInfo
.mInfos
[i
].mName
) {
403 rv
= lengthInfo
.mValues
[i
].SetBaseValueString(aValue
, this, false);
407 aResult
.SetTo(lengthInfo
.mValues
[i
], &aValue
);
416 // Check for SVGAnimatedLengthList attribute
417 LengthListAttributesInfo lengthListInfo
= GetLengthListInfo();
418 for (i
= 0; i
< lengthListInfo
.mCount
; i
++) {
419 if (aAttribute
== lengthListInfo
.mInfos
[i
].mName
) {
420 rv
= lengthListInfo
.mValues
[i
].SetBaseValueString(aValue
);
422 lengthListInfo
.Reset(i
);
424 aResult
.SetTo(lengthListInfo
.mValues
[i
].GetBaseValue(), &aValue
);
434 // Check for SVGAnimatedNumberList attribute
435 NumberListAttributesInfo numberListInfo
= GetNumberListInfo();
436 for (i
= 0; i
< numberListInfo
.mCount
; i
++) {
437 if (aAttribute
== numberListInfo
.mInfos
[i
].mName
) {
438 rv
= numberListInfo
.mValues
[i
].SetBaseValueString(aValue
);
440 numberListInfo
.Reset(i
);
442 aResult
.SetTo(numberListInfo
.mValues
[i
].GetBaseValue(), &aValue
);
452 // Check for SVGAnimatedPointList attribute
453 if (GetPointListAttrName() == aAttribute
) {
454 if (SVGAnimatedPointList
* pointList
= GetAnimatedPointList()) {
455 pointList
->SetBaseValueString(aValue
);
456 // The spec says we parse everything up to the failure, so we DON'T
457 // need to check the result of SetBaseValueString or call
458 // pointList->ClearBaseValue() if it fails
459 aResult
.SetTo(pointList
->GetBaseValue(), &aValue
);
467 // Check for SVGAnimatedPathSegList attribute
468 if (GetPathDataAttrName() == aAttribute
) {
469 if (SVGAnimatedPathSegList
* segList
= GetAnimPathSegList()) {
470 segList
->SetBaseValueString(aValue
);
471 // The spec says we parse everything up to the failure, so we DON'T
472 // need to check the result of SetBaseValueString or call
473 // segList->ClearBaseValue() if it fails
474 aResult
.SetTo(segList
->GetBaseValue(), &aValue
);
482 // Check for SVGAnimatedNumber attribute
483 NumberAttributesInfo numberInfo
= GetNumberInfo();
484 for (i
= 0; i
< numberInfo
.mCount
; i
++) {
485 if (aAttribute
== numberInfo
.mInfos
[i
].mName
) {
486 rv
= numberInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
490 aResult
.SetTo(numberInfo
.mValues
[i
].GetBaseValue(), &aValue
);
500 // Check for SVGAnimatedNumberPair attribute
501 NumberPairAttributesInfo numberPairInfo
= GetNumberPairInfo();
502 for (i
= 0; i
< numberPairInfo
.mCount
; i
++) {
503 if (aAttribute
== numberPairInfo
.mInfos
[i
].mName
) {
504 rv
= numberPairInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
506 numberPairInfo
.Reset(i
);
508 aResult
.SetTo(numberPairInfo
.mValues
[i
], &aValue
);
518 // Check for SVGAnimatedInteger attribute
519 IntegerAttributesInfo integerInfo
= GetIntegerInfo();
520 for (i
= 0; i
< integerInfo
.mCount
; i
++) {
521 if (aAttribute
== integerInfo
.mInfos
[i
].mName
) {
522 rv
= integerInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
524 integerInfo
.Reset(i
);
526 aResult
.SetTo(integerInfo
.mValues
[i
].GetBaseValue(), &aValue
);
536 // Check for SVGAnimatedIntegerPair attribute
537 IntegerPairAttributesInfo integerPairInfo
= GetIntegerPairInfo();
538 for (i
= 0; i
< integerPairInfo
.mCount
; i
++) {
539 if (aAttribute
== integerPairInfo
.mInfos
[i
].mName
) {
540 rv
= integerPairInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
542 integerPairInfo
.Reset(i
);
544 aResult
.SetTo(integerPairInfo
.mValues
[i
], &aValue
);
554 // Check for SVGAnimatedBoolean attribute
555 BooleanAttributesInfo booleanInfo
= GetBooleanInfo();
556 for (i
= 0; i
< booleanInfo
.mCount
; i
++) {
557 if (aAttribute
== booleanInfo
.mInfos
[i
].mName
) {
558 nsAtom
* valAtom
= NS_GetStaticAtom(aValue
);
559 rv
= valAtom
? booleanInfo
.mValues
[i
].SetBaseValueAtom(valAtom
, this)
560 : NS_ERROR_DOM_SYNTAX_ERR
;
562 booleanInfo
.Reset(i
);
564 aResult
.SetTo(valAtom
);
574 // Check for SVGAnimatedEnumeration attribute
575 EnumAttributesInfo enumInfo
= GetEnumInfo();
576 for (i
= 0; i
< enumInfo
.mCount
; i
++) {
577 if (aAttribute
== enumInfo
.mInfos
[i
].mName
) {
578 RefPtr
<nsAtom
> valAtom
= NS_Atomize(aValue
);
579 if (!enumInfo
.mValues
[i
].SetBaseValueAtom(valAtom
, this)) {
580 // Exact error value does not matter; we just need to mark the
582 rv
= NS_ERROR_FAILURE
;
585 aResult
.SetTo(valAtom
);
595 // Check for conditional processing attributes
596 nsCOMPtr
<SVGTests
> tests
= do_QueryObject(this);
597 if (tests
&& tests
->ParseConditionalProcessingAttribute(
598 aAttribute
, aValue
, aResult
)) {
604 // Check for StringList attribute
605 StringListAttributesInfo stringListInfo
= GetStringListInfo();
606 for (i
= 0; i
< stringListInfo
.mCount
; i
++) {
607 if (aAttribute
== stringListInfo
.mInfos
[i
].mName
) {
608 rv
= stringListInfo
.mValues
[i
].SetValue(aValue
);
610 stringListInfo
.Reset(i
);
612 aResult
.SetTo(stringListInfo
.mValues
[i
], &aValue
);
622 // Check for orient attribute
623 if (aAttribute
== nsGkAtoms::orient
) {
624 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
626 rv
= orient
->SetBaseValueString(aValue
, this, false);
630 aResult
.SetTo(*orient
, &aValue
);
635 // Check for viewBox attribute
636 } else if (aAttribute
== nsGkAtoms::viewBox
) {
637 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
639 rv
= viewBox
->SetBaseValueString(aValue
, this, false);
643 aResult
.SetTo(*viewBox
, &aValue
);
648 // Check for preserveAspectRatio attribute
649 } else if (aAttribute
== nsGkAtoms::preserveAspectRatio
) {
650 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
651 GetAnimatedPreserveAspectRatio();
652 if (preserveAspectRatio
) {
653 rv
= preserveAspectRatio
->SetBaseValueString(aValue
, this, false);
655 preserveAspectRatio
->Init();
657 aResult
.SetTo(*preserveAspectRatio
, &aValue
);
662 // Check for SVGAnimatedTransformList attribute
663 } else if (GetTransformListAttrName() == aAttribute
) {
664 // The transform attribute is being set, so we must ensure that the
665 // SVGAnimatedTransformList is/has been allocated:
666 SVGAnimatedTransformList
* transformList
=
667 GetAnimatedTransformList(DO_ALLOCATE
);
668 rv
= transformList
->SetBaseValueString(aValue
, this);
670 transformList
->ClearBaseValue();
672 aResult
.SetTo(transformList
->GetBaseValue(), &aValue
);
676 } else if (aAttribute
== nsGkAtoms::tabindex
) {
677 didSetResult
= aResult
.ParseIntValue(aValue
);
682 if (aAttribute
== nsGkAtoms::_class
) {
683 mClassAttribute
.SetBaseValue(aValue
, this, false);
684 aResult
.ParseAtomArray(aValue
);
688 if (aAttribute
== nsGkAtoms::rel
) {
689 aResult
.ParseAtomArray(aValue
);
695 // Check for SVGAnimatedString attribute
696 StringAttributesInfo stringInfo
= GetStringInfo();
697 for (uint32_t i
= 0; i
< stringInfo
.mCount
; i
++) {
698 if (aNamespaceID
== stringInfo
.mInfos
[i
].mNamespaceID
&&
699 aAttribute
== stringInfo
.mInfos
[i
].mName
) {
700 stringInfo
.mValues
[i
].SetBaseValue(aValue
, this, false);
709 ReportAttributeParseFailure(OwnerDoc(), aAttribute
, aValue
);
713 aResult
.SetTo(aValue
);
718 return SVGElementBase::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
719 aMaybeScriptedPrincipal
, aResult
);
722 void SVGElement::UnsetAttrInternal(int32_t aNamespaceID
, nsAtom
* aName
,
724 // XXXbz there's a bunch of redundancy here with AfterSetAttr.
725 // Maybe consolidate?
727 if (aNamespaceID
== kNameSpaceID_None
) {
728 if (IsEventAttributeName(aName
)) {
729 EventListenerManager
* manager
= GetExistingListenerManager();
731 nsAtom
* eventName
= GetEventNameForAttr(aName
);
732 manager
->RemoveEventHandler(eventName
);
737 // Check if this is a length attribute going away
738 LengthAttributesInfo lenInfo
= GetLengthInfo();
740 for (uint32_t i
= 0; i
< lenInfo
.mCount
; i
++) {
741 if (aName
== lenInfo
.mInfos
[i
].mName
) {
742 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
748 // Check if this is a length list attribute going away
749 LengthListAttributesInfo lengthListInfo
= GetLengthListInfo();
751 for (uint32_t i
= 0; i
< lengthListInfo
.mCount
; i
++) {
752 if (aName
== lengthListInfo
.mInfos
[i
].mName
) {
753 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
754 lengthListInfo
.Reset(i
);
759 // Check if this is a number list attribute going away
760 NumberListAttributesInfo numberListInfo
= GetNumberListInfo();
762 for (uint32_t i
= 0; i
< numberListInfo
.mCount
; i
++) {
763 if (aName
== numberListInfo
.mInfos
[i
].mName
) {
764 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
765 numberListInfo
.Reset(i
);
770 // Check if this is a point list attribute going away
771 if (GetPointListAttrName() == aName
) {
772 SVGAnimatedPointList
* pointList
= GetAnimatedPointList();
774 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
775 pointList
->ClearBaseValue();
780 // Check if this is a path segment list attribute going away
781 if (GetPathDataAttrName() == aName
) {
782 SVGAnimatedPathSegList
* segList
= GetAnimPathSegList();
784 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
785 segList
->ClearBaseValue();
790 // Check if this is a number attribute going away
791 NumberAttributesInfo numInfo
= GetNumberInfo();
793 for (uint32_t i
= 0; i
< numInfo
.mCount
; i
++) {
794 if (aName
== numInfo
.mInfos
[i
].mName
) {
800 // Check if this is a number pair attribute going away
801 NumberPairAttributesInfo numPairInfo
= GetNumberPairInfo();
803 for (uint32_t i
= 0; i
< numPairInfo
.mCount
; i
++) {
804 if (aName
== numPairInfo
.mInfos
[i
].mName
) {
805 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
806 numPairInfo
.Reset(i
);
811 // Check if this is an integer attribute going away
812 IntegerAttributesInfo intInfo
= GetIntegerInfo();
814 for (uint32_t i
= 0; i
< intInfo
.mCount
; i
++) {
815 if (aName
== intInfo
.mInfos
[i
].mName
) {
821 // Check if this is an integer pair attribute going away
822 IntegerPairAttributesInfo intPairInfo
= GetIntegerPairInfo();
824 for (uint32_t i
= 0; i
< intPairInfo
.mCount
; i
++) {
825 if (aName
== intPairInfo
.mInfos
[i
].mName
) {
826 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
827 intPairInfo
.Reset(i
);
832 // Check if this is a boolean attribute going away
833 BooleanAttributesInfo boolInfo
= GetBooleanInfo();
835 for (uint32_t i
= 0; i
< boolInfo
.mCount
; i
++) {
836 if (aName
== boolInfo
.mInfos
[i
].mName
) {
842 // Check if this is an enum attribute going away
843 EnumAttributesInfo enumInfo
= GetEnumInfo();
845 for (uint32_t i
= 0; i
< enumInfo
.mCount
; i
++) {
846 if (aName
== enumInfo
.mInfos
[i
].mName
) {
852 // Check if this is an orient attribute going away
853 if (aName
== nsGkAtoms::orient
) {
854 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
856 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
862 // Check if this is a viewBox attribute going away
863 if (aName
== nsGkAtoms::viewBox
) {
864 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
866 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
872 // Check if this is a preserveAspectRatio attribute going away
873 if (aName
== nsGkAtoms::preserveAspectRatio
) {
874 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
875 GetAnimatedPreserveAspectRatio();
876 if (preserveAspectRatio
) {
877 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
878 preserveAspectRatio
->Init();
883 // Check if this is a transform list attribute going away
884 if (GetTransformListAttrName() == aName
) {
885 SVGAnimatedTransformList
* transformList
= GetAnimatedTransformList();
887 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
888 transformList
->ClearBaseValue();
893 // Check for conditional processing attributes
894 nsCOMPtr
<SVGTests
> tests
= do_QueryObject(this);
895 if (tests
&& tests
->IsConditionalProcessingAttribute(aName
)) {
896 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
897 tests
->UnsetAttr(aName
);
901 // Check if this is a string list attribute going away
902 StringListAttributesInfo stringListInfo
= GetStringListInfo();
904 for (uint32_t i
= 0; i
< stringListInfo
.mCount
; i
++) {
905 if (aName
== stringListInfo
.mInfos
[i
].mName
) {
906 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
907 stringListInfo
.Reset(i
);
912 if (aName
== nsGkAtoms::_class
) {
913 mClassAttribute
.Init();
918 // Check if this is a string attribute going away
919 StringAttributesInfo stringInfo
= GetStringInfo();
921 for (uint32_t i
= 0; i
< stringInfo
.mCount
; i
++) {
922 if (aNamespaceID
== stringInfo
.mInfos
[i
].mNamespaceID
&&
923 aName
== stringInfo
.mInfos
[i
].mName
) {
930 void SVGElement::BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
931 const nsAttrValue
* aValue
, bool aNotify
) {
933 UnsetAttrInternal(aNamespaceID
, aName
, aNotify
);
935 return SVGElementBase::BeforeSetAttr(aNamespaceID
, aName
, aValue
, aNotify
);
938 nsChangeHint
SVGElement::GetAttributeChangeHint(const nsAtom
* aAttribute
,
939 int32_t aModType
) const {
940 nsChangeHint retval
=
941 SVGElementBase::GetAttributeChangeHint(aAttribute
, aModType
);
943 nsCOMPtr
<SVGTests
> tests
= do_QueryObject(const_cast<SVGElement
*>(this));
944 if (tests
&& tests
->IsConditionalProcessingAttribute(aAttribute
)) {
945 // It would be nice to only reconstruct the frame if the value returned by
946 // SVGTests::PassesConditionalProcessingTests has changed, but we don't
948 retval
|= nsChangeHint_ReconstructFrame
;
953 void SVGElement::NodeInfoChanged(Document
* aOldDoc
) {
954 SVGElementBase::NodeInfoChanged(aOldDoc
);
958 SVGElement::IsAttributeMapped(const nsAtom
* name
) const {
959 if (name
== nsGkAtoms::lang
) {
963 if (IsSVGAnimationElement()) {
964 return SVGElementBase::IsAttributeMapped(name
);
967 static const MappedAttributeEntry attributes
[] = {
968 // Properties that we don't support are commented out.
969 // { nsGkAtoms::alignment_baseline },
970 // { nsGkAtoms::baseline_shift },
972 {nsGkAtoms::clip_path
},
973 {nsGkAtoms::clip_rule
},
975 {nsGkAtoms::colorInterpolation
},
976 {nsGkAtoms::colorInterpolationFilters
},
978 {nsGkAtoms::direction
},
979 {nsGkAtoms::display
},
980 {nsGkAtoms::dominant_baseline
},
982 {nsGkAtoms::fill_opacity
},
983 {nsGkAtoms::fill_rule
},
985 {nsGkAtoms::flood_color
},
986 {nsGkAtoms::flood_opacity
},
987 {nsGkAtoms::font_family
},
988 {nsGkAtoms::font_size
},
989 {nsGkAtoms::font_size_adjust
},
990 {nsGkAtoms::font_stretch
},
991 {nsGkAtoms::font_style
},
992 {nsGkAtoms::font_variant
},
993 {nsGkAtoms::fontWeight
},
994 {nsGkAtoms::image_rendering
},
995 {nsGkAtoms::letter_spacing
},
996 {nsGkAtoms::lighting_color
},
997 {nsGkAtoms::marker_end
},
998 {nsGkAtoms::marker_mid
},
999 {nsGkAtoms::marker_start
},
1001 {nsGkAtoms::mask_type
},
1002 {nsGkAtoms::opacity
},
1003 {nsGkAtoms::overflow
},
1004 {nsGkAtoms::paint_order
},
1005 {nsGkAtoms::pointer_events
},
1006 {nsGkAtoms::shape_rendering
},
1007 {nsGkAtoms::stop_color
},
1008 {nsGkAtoms::stop_opacity
},
1009 {nsGkAtoms::stroke
},
1010 {nsGkAtoms::stroke_dasharray
},
1011 {nsGkAtoms::stroke_dashoffset
},
1012 {nsGkAtoms::stroke_linecap
},
1013 {nsGkAtoms::stroke_linejoin
},
1014 {nsGkAtoms::stroke_miterlimit
},
1015 {nsGkAtoms::stroke_opacity
},
1016 {nsGkAtoms::stroke_width
},
1017 {nsGkAtoms::text_anchor
},
1018 {nsGkAtoms::text_decoration
},
1019 {nsGkAtoms::text_rendering
},
1020 {nsGkAtoms::transform_origin
},
1021 {nsGkAtoms::unicode_bidi
},
1022 {nsGkAtoms::vector_effect
},
1023 {nsGkAtoms::visibility
},
1024 {nsGkAtoms::white_space
},
1025 {nsGkAtoms::word_spacing
},
1026 {nsGkAtoms::writing_mode
},
1029 static const MappedAttributeEntry
* const map
[] = {attributes
};
1031 return FindAttributeDependence(name
, map
) ||
1032 SVGElementBase::IsAttributeMapped(name
);
1035 //----------------------------------------------------------------------
1038 // forwarded to Element implementations
1040 //----------------------------------------------------------------------
1042 SVGSVGElement
* SVGElement::GetOwnerSVGElement() {
1043 nsIContent
* ancestor
= GetFlattenedTreeParent();
1045 while (ancestor
&& ancestor
->IsSVGElement()) {
1046 if (ancestor
->IsSVGElement(nsGkAtoms::foreignObject
)) {
1049 if (auto* svg
= SVGSVGElement::FromNode(ancestor
)) {
1052 ancestor
= ancestor
->GetFlattenedTreeParent();
1055 // we don't have an ancestor <svg> element...
1059 SVGElement
* SVGElement::GetViewportElement() {
1060 return SVGContentUtils::GetNearestViewportElement(this);
1063 already_AddRefed
<DOMSVGAnimatedString
> SVGElement::ClassName() {
1064 return mClassAttribute
.ToDOMAnimatedString(this);
1068 bool SVGElement::UpdateDeclarationBlockFromLength(
1069 StyleLockedDeclarationBlock
& aBlock
, nsCSSPropertyID aPropId
,
1070 const SVGAnimatedLength
& aLength
, ValToUse aValToUse
) {
1072 if (aValToUse
== ValToUse::Anim
) {
1073 value
= aLength
.GetAnimValInSpecifiedUnits();
1075 MOZ_ASSERT(aValToUse
== ValToUse::Base
);
1076 value
= aLength
.GetBaseValInSpecifiedUnits();
1079 // SVG parser doesn't check non-negativity of some parsed value, we should not
1080 // pass those to CSS side.
1082 SVGGeometryProperty::IsNonNegativeGeometryProperty(aPropId
)) {
1087 SVGLength::SpecifiedUnitTypeToCSSUnit(aLength
.GetSpecifiedUnitType());
1089 if (cssUnit
== eCSSUnit_Percent
) {
1090 Servo_DeclarationBlock_SetPercentValue(&aBlock
, aPropId
, value
/ 100.f
);
1092 Servo_DeclarationBlock_SetLengthValue(&aBlock
, aPropId
, value
, cssUnit
);
1099 bool SVGElement::UpdateDeclarationBlockFromPath(
1100 StyleLockedDeclarationBlock
& aBlock
, const SVGAnimatedPathSegList
& aPath
,
1101 ValToUse aValToUse
) {
1102 const SVGPathData
& pathData
=
1103 aValToUse
== ValToUse::Anim
? aPath
.GetAnimValue() : aPath
.GetBaseValue();
1105 // SVGPathData::mData is fallible but rust binding accepts nsTArray only, so
1106 // we need to point to one or the other. Fortunately, fallible and infallible
1107 // array types can be implicitly converted provided they are const.
1109 // FIXME: here we just convert the data structure from cpp verion into rust
1110 // version. We don't do any normalization for the path data from d attribute.
1111 // Based on the current discussion of https://github.com/w3c/svgwg/issues/321,
1112 // we may have to convert the relative commands into absolute commands.
1113 // The normalization should be fixed in Bug 1489392. Besides, Bug 1714238
1114 // will use the same data structure, so we may simplify this more.
1115 const nsTArray
<float>& asInFallibleArray
= pathData
.RawData();
1116 Servo_DeclarationBlock_SetPathValue(&aBlock
, eCSSProperty_d
,
1117 &asInFallibleArray
);
1121 //------------------------------------------------------------------------
1122 // Helper class: MappedAttrParser, for parsing values of mapped attributes
1126 class MOZ_STACK_CLASS MappedAttrParser
{
1128 explicit MappedAttrParser(SVGElement
& aElement
,
1129 StyleLockedDeclarationBlock
* aDecl
)
1130 : mElement(aElement
), mDecl(aDecl
) {
1132 Servo_DeclarationBlock_Clear(mDecl
);
1135 ~MappedAttrParser() {
1137 "If mDecl was initialized, it should have been returned via "
1138 "TakeDeclarationBlock (and have its pointer cleared)");
1141 // Parses a mapped attribute value.
1142 void ParseMappedAttrValue(nsAtom
* aMappedAttrName
,
1143 const nsAString
& aMappedAttrValue
);
1145 void TellStyleAlreadyParsedResult(nsAtom
const* aAtom
,
1146 SVGAnimatedLength
const& aLength
);
1147 void TellStyleAlreadyParsedResult(const SVGAnimatedPathSegList
& aPath
);
1149 // If we've parsed any values for mapped attributes, this method returns the
1150 // already_AddRefed declaration block that incorporates the parsed values.
1151 // Otherwise, this method returns null.
1152 already_AddRefed
<StyleLockedDeclarationBlock
> TakeDeclarationBlock() {
1153 return mDecl
.forget();
1156 StyleLockedDeclarationBlock
& EnsureDeclarationBlock() {
1158 mDecl
= Servo_DeclarationBlock_CreateEmpty().Consume();
1163 URLExtraData
& EnsureExtraData() {
1165 mExtraData
= mElement
.GetURLDataForStyleAttr();
1171 // For reporting use counters
1172 SVGElement
& mElement
;
1174 // Declaration for storing parsed values (lazily initialized).
1175 RefPtr
<StyleLockedDeclarationBlock
> mDecl
;
1177 // URL data for parsing stuff. Also lazy.
1178 RefPtr
<URLExtraData
> mExtraData
;
1181 void MappedAttrParser::ParseMappedAttrValue(nsAtom
* aMappedAttrName
,
1182 const nsAString
& aMappedAttrValue
) {
1183 // Get the nsCSSPropertyID ID for our mapped attribute.
1184 nsCSSPropertyID propertyID
=
1185 nsCSSProps::LookupProperty(nsAutoAtomCString(aMappedAttrName
));
1186 if (propertyID
!= eCSSProperty_UNKNOWN
) {
1187 bool changed
= false; // outparam for ParseProperty.
1188 NS_ConvertUTF16toUTF8
value(aMappedAttrValue
);
1190 auto* doc
= mElement
.OwnerDoc();
1191 changed
= Servo_DeclarationBlock_SetPropertyById(
1192 &EnsureDeclarationBlock(), propertyID
, &value
, false,
1193 &EnsureExtraData(), StyleParsingMode::ALLOW_UNITLESS_LENGTH
,
1194 doc
->GetCompatibilityMode(), doc
->CSSLoader(), StyleCssRuleType::Style
,
1197 // TODO(emilio): If we want to record these from CSSOM more generally, we
1198 // can pass the document use counters down the FFI call. For now manually
1200 if (changed
&& StaticPrefs::layout_css_use_counters_enabled()) {
1201 UseCounter useCounter
= nsCSSProps::UseCounterFor(propertyID
);
1202 MOZ_ASSERT(useCounter
!= eUseCounter_UNKNOWN
);
1203 doc
->SetUseCounter(useCounter
);
1207 MOZ_ASSERT(aMappedAttrName
== nsGkAtoms::lang
,
1208 "Only 'lang' should be unrecognized!");
1209 // CSS parser doesn't know about 'lang', so we need to handle it specially.
1210 if (aMappedAttrName
== nsGkAtoms::lang
) {
1211 propertyID
= eCSSProperty__x_lang
;
1212 RefPtr
<nsAtom
> atom
= NS_Atomize(aMappedAttrValue
);
1213 Servo_DeclarationBlock_SetIdentStringValue(&EnsureDeclarationBlock(),
1218 void MappedAttrParser::TellStyleAlreadyParsedResult(
1219 nsAtom
const* aAtom
, SVGAnimatedLength
const& aLength
) {
1220 nsCSSPropertyID propertyID
=
1221 nsCSSProps::LookupProperty(nsAutoAtomCString(aAtom
));
1222 SVGElement::UpdateDeclarationBlockFromLength(EnsureDeclarationBlock(),
1223 propertyID
, aLength
,
1224 SVGElement::ValToUse::Base
);
1227 void MappedAttrParser::TellStyleAlreadyParsedResult(
1228 const SVGAnimatedPathSegList
& aPath
) {
1229 SVGElement::UpdateDeclarationBlockFromPath(EnsureDeclarationBlock(), aPath
,
1230 SVGElement::ValToUse::Base
);
1235 //----------------------------------------------------------------------
1236 // Implementation Helpers:
1238 void SVGElement::UpdateMappedDeclarationBlock() {
1239 MOZ_ASSERT(IsPendingMappedAttributeEvaluation());
1240 MappedAttrParser
mappedAttrParser(*this, mAttrs
.GetMappedDeclarationBlock());
1242 const bool lengthAffectsStyle
=
1243 SVGGeometryProperty::ElementMapsLengthsToStyle(this);
1246 while (BorrowedAttrInfo info
= GetAttrInfoAt(i
++)) {
1247 const nsAttrName
* attrName
= info
.mName
;
1248 if (!attrName
->IsAtom() || !IsAttributeMapped(attrName
->Atom())) {
1252 if (attrName
->Atom() == nsGkAtoms::lang
&&
1253 HasAttr(kNameSpaceID_XML
, nsGkAtoms::lang
)) {
1254 // xml:lang has precedence, and will get set via Gecko_GetXMLLangValue().
1258 if (lengthAffectsStyle
) {
1259 auto const* length
= GetAnimatedLength(attrName
->Atom());
1261 if (length
&& length
->HasBaseVal()) {
1262 // This is an element with geometry property set via SVG attribute,
1263 // and the attribute is already successfully parsed. We want to go
1264 // through the optimized path to tell the style system the result
1265 // directly, rather than let it parse the same thing again.
1266 mappedAttrParser
.TellStyleAlreadyParsedResult(attrName
->Atom(),
1272 if (attrName
->Equals(nsGkAtoms::d
, kNameSpaceID_None
)) {
1273 const auto* path
= GetAnimPathSegList();
1274 // Note: Only SVGPathElement has d attribute.
1277 "SVGPathElement should have the non-null SVGAnimatedPathSegList");
1278 // The attribute should have been already successfully parsed.
1279 // We want to go through the optimized path to tell the style system
1280 // the result directly, rather than let it parse the same thing again.
1281 mappedAttrParser
.TellStyleAlreadyParsedResult(*path
);
1282 // Some other notes:
1283 // The syntax of CSS d property is different from SVG d attribute.
1284 // 1. CSS d proeprty accepts: none | path(<quoted string>);
1285 // 2. SVG d attribtue accepts: none | <string>
1286 // So we cannot use css parser to parse the SVG d attribute directly.
1287 // Besides, |mAttrs.AttrAt(i)| removes the quotes already, so the svg path
1288 // in |mAttrs.AttrAt(i)| would be something like `M0,0L1,1z` without the
1289 // quotes. So css tokenizer cannot recognize this as a quoted string, and
1290 // so svg_path::SVGPathData::parse() doesn't work for this. Fortunately,
1291 // we still can rely on the parsed result from
1292 // SVGElement::ParseAttribute() for d attribute.
1297 info
.mValue
->ToString(value
);
1298 mappedAttrParser
.ParseMappedAttrValue(attrName
->Atom(), value
);
1300 mAttrs
.SetMappedDeclarationBlock(mappedAttrParser
.TakeDeclarationBlock());
1304 * Helper methods for the type-specific WillChangeXXX methods.
1306 * This method sends out appropriate pre-change notifications so that selector
1307 * restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop
1308 * matching) work, and it returns an nsAttrValue that _may_ contain the
1309 * attribute's pre-change value.
1311 * The nsAttrValue returned by this method depends on whether there are
1312 * mutation event listeners listening for changes to this element's attributes.
1313 * If not, then the object returned is empty. If there are, then the
1314 * nsAttrValue returned contains a serialized copy of the attribute's value
1315 * prior to the change, and this object should be passed to the corresponding
1316 * DidChangeXXX method call (assuming a WillChangeXXX call is required for the
1317 * SVG type - see comment below). This is necessary so that the 'prevValue'
1318 * property of the mutation event that is dispatched will correctly contain the
1321 * The reason we need to serialize the old value if there are mutation
1322 * event listeners is because the underlying nsAttrValue for the attribute
1323 * points directly to a parsed representation of the attribute (e.g. an
1324 * SVGAnimatedLengthList*) that is a member of the SVG element. That object
1325 * will have changed by the time DidChangeXXX has been called, so without the
1326 * serialization of the old attribute value that we provide, DidChangeXXX
1327 * would have no way to get the old value to pass to SetAttrAndNotify.
1329 * We only return the old value when there are mutation event listeners because
1330 * it's not needed otherwise, and because it's expensive to serialize the old
1331 * value. This is especially true for list type attributes, which may be built
1332 * up via the SVG DOM resulting in a large number of Will/DidModifyXXX calls
1333 * before the script finally finishes setting the attribute.
1335 * Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check
1336 * and filter out redundant changes. Before calling WillChangeXXX, the caller
1337 * should check whether the new and old values are actually the same, and skip
1338 * calling Will/DidChangeXXX if they are.
1340 * Also note that not all SVG types use this scheme. For types that can be
1341 * represented by an nsAttrValue without pointing back to an SVG object (e.g.
1342 * enums, booleans, integers) we can simply use SetParsedAttr which will do all
1343 * of the above for us. For such types there is no matching WillChangeXXX
1344 * method, only DidChangeXXX which calls SetParsedAttr.
1346 nsAttrValue
SVGElement::WillChangeValue(
1347 nsAtom
* aName
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1348 // We need an empty attr value:
1349 // a) to pass to BeforeSetAttr when GetParsedAttr returns nullptr
1350 // b) to store the old value in the case we have mutation listeners
1352 // We can use the same value for both purposes, because if GetParsedAttr
1353 // returns non-null its return value is what will get passed to BeforeSetAttr,
1354 // not matter what our mutation listener situation is.
1356 // Also, we should be careful to always return this value to benefit from
1357 // return value optimization.
1358 nsAttrValue emptyOrOldAttrValue
;
1359 const nsAttrValue
* attrValue
= GetParsedAttr(aName
);
1361 // We only need to set the old value if we have listeners since otherwise it
1363 if (attrValue
&& nsContentUtils::HasMutationListeners(
1364 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this)) {
1365 emptyOrOldAttrValue
.SetToSerialized(*attrValue
);
1369 attrValue
? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION
)
1370 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION
);
1371 MutationObservers::NotifyAttributeWillChange(this, kNameSpaceID_None
, aName
,
1374 // This is not strictly correct--the attribute value parameter for
1375 // BeforeSetAttr should reflect the value that *will* be set but that implies
1376 // allocating, e.g. an extra SVGAnimatedLength, and isn't necessary at the
1377 // moment since no SVG elements overload BeforeSetAttr. For now we just pass
1378 // the current value.
1379 const nsAttrValue
* value
= attrValue
? attrValue
: &emptyOrOldAttrValue
;
1380 BeforeSetAttr(kNameSpaceID_None
, aName
, value
, kNotifyDocumentObservers
);
1381 return emptyOrOldAttrValue
;
1385 * Helper methods for the type-specific DidChangeXXX methods.
1387 * aEmptyOrOldValue will normally be the object returned from the corresponding
1388 * WillChangeXXX call. This is because:
1389 * a) WillChangeXXX will ensure the object is set when we have mutation
1391 * b) WillChangeXXX will ensure the object represents a serialized version of
1392 * the old attribute value so that the value doesn't change when the
1393 * underlying SVG type is updated.
1395 * aNewValue is replaced with the old value.
1397 void SVGElement::DidChangeValue(nsAtom
* aName
,
1398 const nsAttrValue
& aEmptyOrOldValue
,
1399 nsAttrValue
& aNewValue
,
1400 const mozAutoDocUpdate
& aProofOfUpdate
) {
1401 bool hasListeners
= nsContentUtils::HasMutationListeners(
1402 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this);
1404 HasAttr(aName
) ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION
)
1405 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION
);
1407 // XXX Really, the fourth argument to SetAttrAndNotify should be null if
1408 // aEmptyOrOldValue does not represent the actual previous value of the
1409 // attribute, but currently SVG elements do not even use the old attribute
1410 // value in |AfterSetAttr|, so this should be ok.
1411 SetAttrAndNotify(kNameSpaceID_None
, aName
, nullptr, &aEmptyOrOldValue
,
1412 aNewValue
, nullptr, modType
, hasListeners
,
1413 kNotifyDocumentObservers
, kCallAfterSetAttr
,
1414 GetComposedDoc(), aProofOfUpdate
);
1417 void SVGElement::MaybeSerializeAttrBeforeRemoval(nsAtom
* aName
, bool aNotify
) {
1418 if (!aNotify
|| !nsContentUtils::HasMutationListeners(
1419 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this)) {
1423 const nsAttrValue
* attrValue
= mAttrs
.GetAttr(aName
);
1424 if (!attrValue
) return;
1426 nsAutoString serializedValue
;
1427 attrValue
->ToString(serializedValue
);
1428 nsAttrValue
oldAttrValue(serializedValue
);
1430 mAttrs
.SetAndSwapAttr(aName
, oldAttrValue
, &oldValueSet
);
1433 nsAtom
* SVGElement::GetEventNameForAttr(nsAtom
* aAttr
) {
1434 if (IsSVGElement(nsGkAtoms::svg
)) {
1435 if (aAttr
== nsGkAtoms::onload
) return nsGkAtoms::onSVGLoad
;
1436 if (aAttr
== nsGkAtoms::onscroll
) return nsGkAtoms::onSVGScroll
;
1438 if (aAttr
== nsGkAtoms::onbegin
) return nsGkAtoms::onbeginEvent
;
1439 if (aAttr
== nsGkAtoms::onrepeat
) return nsGkAtoms::onrepeatEvent
;
1440 if (aAttr
== nsGkAtoms::onend
) return nsGkAtoms::onendEvent
;
1442 return SVGElementBase::GetEventNameForAttr(aAttr
);
1445 SVGViewportElement
* SVGElement::GetCtx() const {
1446 return SVGContentUtils::GetNearestViewportElement(this);
1450 gfxMatrix
SVGElement::PrependLocalTransformsTo(const gfxMatrix
& aMatrix
,
1451 SVGTransformTypes aWhich
) const {
1455 SVGElement::LengthAttributesInfo
SVGElement::GetLengthInfo() {
1456 return LengthAttributesInfo(nullptr, nullptr, 0);
1459 void SVGElement::SetLength(nsAtom
* aName
, const SVGAnimatedLength
& aLength
) {
1460 LengthAttributesInfo lengthInfo
= GetLengthInfo();
1462 for (uint32_t i
= 0; i
< lengthInfo
.mCount
; i
++) {
1463 if (aName
== lengthInfo
.mInfos
[i
].mName
) {
1464 lengthInfo
.mValues
[i
] = aLength
;
1465 DidAnimateLength(i
);
1469 MOZ_ASSERT(false, "no length found to set");
1472 nsAttrValue
SVGElement::WillChangeLength(
1473 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1474 return WillChangeValue(GetLengthInfo().mInfos
[aAttrEnum
].mName
,
1478 void SVGElement::DidChangeLength(uint8_t aAttrEnum
,
1479 const nsAttrValue
& aEmptyOrOldValue
,
1480 const mozAutoDocUpdate
& aProofOfUpdate
) {
1481 LengthAttributesInfo info
= GetLengthInfo();
1483 NS_ASSERTION(info
.mCount
> 0,
1484 "DidChangeLength on element with no length attribs");
1485 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1487 nsAttrValue newValue
;
1488 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
1490 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1494 void SVGElement::DidAnimateLength(uint8_t aAttrEnum
) {
1495 // We need to do this here. Normally the SMIL restyle would also cause us to
1496 // do this from DidSetComputedStyle, but we don't have that guarantee if our
1497 // frame gets reconstructed.
1498 ClearAnyCachedPath();
1500 if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) {
1501 nsCSSPropertyID propId
=
1502 SVGGeometryProperty::AttrEnumToCSSPropId(this, aAttrEnum
);
1504 // We don't map use element width/height currently. We can remove this
1506 if (propId
!= eCSSProperty_UNKNOWN
) {
1507 SMILOverrideStyle()->SetSMILValue(propId
,
1508 GetLengthInfo().mValues
[aAttrEnum
]);
1512 auto info
= GetLengthInfo();
1513 DidAnimateAttribute(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
);
1516 SVGAnimatedLength
* SVGElement::GetAnimatedLength(uint8_t aAttrEnum
) {
1517 LengthAttributesInfo info
= GetLengthInfo();
1518 if (aAttrEnum
< info
.mCount
) {
1519 return &info
.mValues
[aAttrEnum
];
1521 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1525 SVGAnimatedLength
* SVGElement::GetAnimatedLength(const nsAtom
* aAttrName
) {
1526 LengthAttributesInfo lengthInfo
= GetLengthInfo();
1528 for (uint32_t i
= 0; i
< lengthInfo
.mCount
; i
++) {
1529 if (aAttrName
== lengthInfo
.mInfos
[i
].mName
) {
1530 return &lengthInfo
.mValues
[i
];
1536 void SVGElement::GetAnimatedLengthValues(float* aFirst
, ...) {
1537 LengthAttributesInfo info
= GetLengthInfo();
1539 NS_ASSERTION(info
.mCount
> 0,
1540 "GetAnimatedLengthValues on element with no length attribs");
1542 SVGElementMetrics
metrics(this);
1548 va_start(args
, aFirst
);
1550 while (f
&& i
< info
.mCount
) {
1551 *f
= info
.mValues
[i
++].GetAnimValue(metrics
);
1552 f
= va_arg(args
, float*);
1558 SVGElement::LengthListAttributesInfo
SVGElement::GetLengthListInfo() {
1559 return LengthListAttributesInfo(nullptr, nullptr, 0);
1562 nsAttrValue
SVGElement::WillChangeLengthList(
1563 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1564 return WillChangeValue(GetLengthListInfo().mInfos
[aAttrEnum
].mName
,
1568 void SVGElement::DidChangeLengthList(uint8_t aAttrEnum
,
1569 const nsAttrValue
& aEmptyOrOldValue
,
1570 const mozAutoDocUpdate
& aProofOfUpdate
) {
1571 LengthListAttributesInfo info
= GetLengthListInfo();
1573 NS_ASSERTION(info
.mCount
> 0,
1574 "DidChangeLengthList on element with no length list attribs");
1575 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1577 nsAttrValue newValue
;
1578 newValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1580 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1584 void SVGElement::GetAnimatedLengthListValues(SVGUserUnitList
* aFirst
, ...) {
1585 LengthListAttributesInfo info
= GetLengthListInfo();
1589 "GetAnimatedLengthListValues on element with no length list attribs");
1591 SVGUserUnitList
* list
= aFirst
;
1595 va_start(args
, aFirst
);
1597 while (list
&& i
< info
.mCount
) {
1598 list
->Init(&(info
.mValues
[i
].GetAnimValue()), this, info
.mInfos
[i
].mAxis
);
1600 list
= va_arg(args
, SVGUserUnitList
*);
1606 SVGAnimatedLengthList
* SVGElement::GetAnimatedLengthList(uint8_t aAttrEnum
) {
1607 LengthListAttributesInfo info
= GetLengthListInfo();
1608 if (aAttrEnum
< info
.mCount
) {
1609 return &(info
.mValues
[aAttrEnum
]);
1611 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1615 SVGElement::NumberListAttributesInfo
SVGElement::GetNumberListInfo() {
1616 return NumberListAttributesInfo(nullptr, nullptr, 0);
1619 nsAttrValue
SVGElement::WillChangeNumberList(
1620 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1621 return WillChangeValue(GetNumberListInfo().mInfos
[aAttrEnum
].mName
,
1625 void SVGElement::DidChangeNumberList(uint8_t aAttrEnum
,
1626 const nsAttrValue
& aEmptyOrOldValue
,
1627 const mozAutoDocUpdate
& aProofOfUpdate
) {
1628 NumberListAttributesInfo info
= GetNumberListInfo();
1630 MOZ_ASSERT(info
.mCount
> 0,
1631 "DidChangeNumberList on element with no number list attribs");
1632 MOZ_ASSERT(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1634 nsAttrValue newValue
;
1635 newValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1637 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1641 SVGAnimatedNumberList
* SVGElement::GetAnimatedNumberList(uint8_t aAttrEnum
) {
1642 NumberListAttributesInfo info
= GetNumberListInfo();
1643 if (aAttrEnum
< info
.mCount
) {
1644 return &(info
.mValues
[aAttrEnum
]);
1646 MOZ_ASSERT(false, "Bad attrEnum");
1650 SVGAnimatedNumberList
* SVGElement::GetAnimatedNumberList(nsAtom
* aAttrName
) {
1651 NumberListAttributesInfo info
= GetNumberListInfo();
1652 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
1653 if (aAttrName
== info
.mInfos
[i
].mName
) {
1654 return &info
.mValues
[i
];
1657 MOZ_ASSERT(false, "Bad caller");
1661 nsAttrValue
SVGElement::WillChangePointList(
1662 const mozAutoDocUpdate
& aProofOfUpdate
) {
1663 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1664 return WillChangeValue(GetPointListAttrName(), aProofOfUpdate
);
1667 void SVGElement::DidChangePointList(const nsAttrValue
& aEmptyOrOldValue
,
1668 const mozAutoDocUpdate
& aProofOfUpdate
) {
1669 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1671 nsAttrValue newValue
;
1672 newValue
.SetTo(GetAnimatedPointList()->GetBaseValue(), nullptr);
1674 DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue
, newValue
,
1678 void SVGElement::DidAnimatePointList() {
1679 MOZ_ASSERT(GetPointListAttrName(), "Animating non-existent path data?");
1681 ClearAnyCachedPath();
1683 DidAnimateAttribute(kNameSpaceID_None
, GetPointListAttrName());
1686 nsAttrValue
SVGElement::WillChangePathSegList(
1687 const mozAutoDocUpdate
& aProofOfUpdate
) {
1688 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1689 return WillChangeValue(GetPathDataAttrName(), aProofOfUpdate
);
1692 void SVGElement::DidChangePathSegList(const nsAttrValue
& aEmptyOrOldValue
,
1693 const mozAutoDocUpdate
& aProofOfUpdate
) {
1694 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1696 nsAttrValue newValue
;
1697 newValue
.SetTo(GetAnimPathSegList()->GetBaseValue(), nullptr);
1699 DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue
, newValue
,
1703 void SVGElement::DidAnimatePathSegList() {
1704 nsStaticAtom
* name
= GetPathDataAttrName();
1705 MOZ_ASSERT(name
, "Animating non-existent path data?");
1707 ClearAnyCachedPath();
1709 // Notify style we have to update the d property because of SMIL animation.
1710 if (name
== nsGkAtoms::d
) {
1711 SMILOverrideStyle()->SetSMILValue(nsCSSPropertyID::eCSSProperty_d
,
1712 *GetAnimPathSegList());
1715 DidAnimateAttribute(kNameSpaceID_None
, name
);
1718 SVGElement::NumberAttributesInfo
SVGElement::GetNumberInfo() {
1719 return NumberAttributesInfo(nullptr, nullptr, 0);
1722 void SVGElement::DidChangeNumber(uint8_t aAttrEnum
) {
1723 NumberAttributesInfo info
= GetNumberInfo();
1725 NS_ASSERTION(info
.mCount
> 0,
1726 "DidChangeNumber on element with no number attribs");
1727 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1729 nsAttrValue attrValue
;
1730 attrValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1732 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
1736 void SVGElement::GetAnimatedNumberValues(float* aFirst
, ...) {
1737 NumberAttributesInfo info
= GetNumberInfo();
1739 NS_ASSERTION(info
.mCount
> 0,
1740 "GetAnimatedNumberValues on element with no number attribs");
1746 va_start(args
, aFirst
);
1748 while (f
&& i
< info
.mCount
) {
1749 *f
= info
.mValues
[i
++].GetAnimValue();
1750 f
= va_arg(args
, float*);
1755 SVGElement::NumberPairAttributesInfo
SVGElement::GetNumberPairInfo() {
1756 return NumberPairAttributesInfo(nullptr, nullptr, 0);
1759 nsAttrValue
SVGElement::WillChangeNumberPair(uint8_t aAttrEnum
) {
1760 mozAutoDocUpdate
updateBatch(GetComposedDoc(), kDontNotifyDocumentObservers
);
1761 return WillChangeValue(GetNumberPairInfo().mInfos
[aAttrEnum
].mName
,
1765 void SVGElement::DidChangeNumberPair(uint8_t aAttrEnum
,
1766 const nsAttrValue
& aEmptyOrOldValue
) {
1767 NumberPairAttributesInfo info
= GetNumberPairInfo();
1769 NS_ASSERTION(info
.mCount
> 0,
1770 "DidChangePairNumber on element with no number pair attribs");
1771 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1773 nsAttrValue newValue
;
1774 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
1776 mozAutoDocUpdate
updateBatch(GetComposedDoc(), kNotifyDocumentObservers
);
1777 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1781 SVGElement::IntegerAttributesInfo
SVGElement::GetIntegerInfo() {
1782 return IntegerAttributesInfo(nullptr, nullptr, 0);
1785 void SVGElement::DidChangeInteger(uint8_t aAttrEnum
) {
1786 IntegerAttributesInfo info
= GetIntegerInfo();
1787 NS_ASSERTION(info
.mCount
> 0,
1788 "DidChangeInteger on element with no integer attribs");
1789 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1791 nsAttrValue attrValue
;
1792 attrValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1794 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
1798 void SVGElement::GetAnimatedIntegerValues(int32_t* aFirst
, ...) {
1799 IntegerAttributesInfo info
= GetIntegerInfo();
1801 NS_ASSERTION(info
.mCount
> 0,
1802 "GetAnimatedIntegerValues on element with no integer attribs");
1804 int32_t* n
= aFirst
;
1808 va_start(args
, aFirst
);
1810 while (n
&& i
< info
.mCount
) {
1811 *n
= info
.mValues
[i
++].GetAnimValue();
1812 n
= va_arg(args
, int32_t*);
1817 SVGElement::IntegerPairAttributesInfo
SVGElement::GetIntegerPairInfo() {
1818 return IntegerPairAttributesInfo(nullptr, nullptr, 0);
1821 nsAttrValue
SVGElement::WillChangeIntegerPair(
1822 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1823 return WillChangeValue(GetIntegerPairInfo().mInfos
[aAttrEnum
].mName
,
1827 void SVGElement::DidChangeIntegerPair(uint8_t aAttrEnum
,
1828 const nsAttrValue
& aEmptyOrOldValue
,
1829 const mozAutoDocUpdate
& aProofOfUpdate
) {
1830 IntegerPairAttributesInfo info
= GetIntegerPairInfo();
1832 NS_ASSERTION(info
.mCount
> 0,
1833 "DidChangeIntegerPair on element with no integer pair attribs");
1834 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1836 nsAttrValue newValue
;
1837 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
1839 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1843 SVGElement::BooleanAttributesInfo
SVGElement::GetBooleanInfo() {
1844 return BooleanAttributesInfo(nullptr, nullptr, 0);
1847 void SVGElement::DidChangeBoolean(uint8_t aAttrEnum
) {
1848 BooleanAttributesInfo info
= GetBooleanInfo();
1850 NS_ASSERTION(info
.mCount
> 0,
1851 "DidChangeBoolean on element with no boolean attribs");
1852 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1854 nsAttrValue
attrValue(info
.mValues
[aAttrEnum
].GetBaseValueAtom());
1855 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
1859 SVGElement::EnumAttributesInfo
SVGElement::GetEnumInfo() {
1860 return EnumAttributesInfo(nullptr, nullptr, 0);
1863 void SVGElement::DidChangeEnum(uint8_t aAttrEnum
) {
1864 EnumAttributesInfo info
= GetEnumInfo();
1866 NS_ASSERTION(info
.mCount
> 0,
1867 "DidChangeEnum on element with no enum attribs");
1868 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1870 nsAttrValue
attrValue(info
.mValues
[aAttrEnum
].GetBaseValueAtom(this));
1871 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
1875 SVGAnimatedOrient
* SVGElement::GetAnimatedOrient() { return nullptr; }
1877 nsAttrValue
SVGElement::WillChangeOrient(
1878 const mozAutoDocUpdate
& aProofOfUpdate
) {
1879 return WillChangeValue(nsGkAtoms::orient
, aProofOfUpdate
);
1882 void SVGElement::DidChangeOrient(const nsAttrValue
& aEmptyOrOldValue
,
1883 const mozAutoDocUpdate
& aProofOfUpdate
) {
1884 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
1886 NS_ASSERTION(orient
, "DidChangeOrient on element with no orient attrib");
1888 nsAttrValue newValue
;
1889 newValue
.SetTo(*orient
, nullptr);
1891 DidChangeValue(nsGkAtoms::orient
, aEmptyOrOldValue
, newValue
, aProofOfUpdate
);
1894 SVGAnimatedViewBox
* SVGElement::GetAnimatedViewBox() { return nullptr; }
1896 nsAttrValue
SVGElement::WillChangeViewBox(
1897 const mozAutoDocUpdate
& aProofOfUpdate
) {
1898 return WillChangeValue(nsGkAtoms::viewBox
, aProofOfUpdate
);
1901 void SVGElement::DidChangeViewBox(const nsAttrValue
& aEmptyOrOldValue
,
1902 const mozAutoDocUpdate
& aProofOfUpdate
) {
1903 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
1905 NS_ASSERTION(viewBox
, "DidChangeViewBox on element with no viewBox attrib");
1907 nsAttrValue newValue
;
1908 newValue
.SetTo(*viewBox
, nullptr);
1910 DidChangeValue(nsGkAtoms::viewBox
, aEmptyOrOldValue
, newValue
,
1914 SVGAnimatedPreserveAspectRatio
* SVGElement::GetAnimatedPreserveAspectRatio() {
1918 nsAttrValue
SVGElement::WillChangePreserveAspectRatio(
1919 const mozAutoDocUpdate
& aProofOfUpdate
) {
1920 return WillChangeValue(nsGkAtoms::preserveAspectRatio
, aProofOfUpdate
);
1923 void SVGElement::DidChangePreserveAspectRatio(
1924 const nsAttrValue
& aEmptyOrOldValue
,
1925 const mozAutoDocUpdate
& aProofOfUpdate
) {
1926 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
1927 GetAnimatedPreserveAspectRatio();
1929 NS_ASSERTION(preserveAspectRatio
,
1930 "DidChangePreserveAspectRatio on element with no "
1931 "preserveAspectRatio attrib");
1933 nsAttrValue newValue
;
1934 newValue
.SetTo(*preserveAspectRatio
, nullptr);
1936 DidChangeValue(nsGkAtoms::preserveAspectRatio
, aEmptyOrOldValue
, newValue
,
1940 nsAttrValue
SVGElement::WillChangeTransformList(
1941 const mozAutoDocUpdate
& aProofOfUpdate
) {
1942 return WillChangeValue(GetTransformListAttrName(), aProofOfUpdate
);
1945 void SVGElement::DidChangeTransformList(
1946 const nsAttrValue
& aEmptyOrOldValue
,
1947 const mozAutoDocUpdate
& aProofOfUpdate
) {
1948 MOZ_ASSERT(GetTransformListAttrName(),
1949 "Changing non-existent transform list?");
1951 // The transform attribute is being set, so we must ensure that the
1952 // SVGAnimatedTransformList is/has been allocated:
1953 nsAttrValue newValue
;
1954 newValue
.SetTo(GetAnimatedTransformList(DO_ALLOCATE
)->GetBaseValue(),
1957 DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue
, newValue
,
1961 void SVGElement::DidAnimateTransformList(int32_t aModType
) {
1962 MOZ_ASSERT(GetTransformListAttrName(),
1963 "Animating non-existent transform data?");
1965 if (auto* frame
= GetPrimaryFrame()) {
1966 nsAtom
* transformAttr
= GetTransformListAttrName();
1967 frame
->AttributeChanged(kNameSpaceID_None
, transformAttr
, aModType
);
1968 // When script changes the 'transform' attribute, Element::SetAttrAndNotify
1969 // will call MutationObservers::NotifyAttributeChanged, under which
1970 // SVGTransformableElement::GetAttributeChangeHint will be called and an
1971 // appropriate change event posted to update our frame's overflow rects.
1972 // The SetAttrAndNotify doesn't happen for transform changes caused by
1973 // 'animateTransform' though (and sending out the mutation events that
1974 // MutationObservers::NotifyAttributeChanged dispatches would be
1975 // inappropriate anyway), so we need to post the change event ourself.
1976 nsChangeHint changeHint
= GetAttributeChangeHint(transformAttr
, aModType
);
1978 nsLayoutUtils::PostRestyleEvent(this, RestyleHint
{0}, changeHint
);
1980 SVGObserverUtils::InvalidateRenderingObservers(frame
);
1983 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
1986 SVGElement::StringAttributesInfo
SVGElement::GetStringInfo() {
1987 return StringAttributesInfo(nullptr, nullptr, 0);
1990 void SVGElement::GetStringBaseValue(uint8_t aAttrEnum
,
1991 nsAString
& aResult
) const {
1992 SVGElement::StringAttributesInfo info
=
1993 const_cast<SVGElement
*>(this)->GetStringInfo();
1995 NS_ASSERTION(info
.mCount
> 0,
1996 "GetBaseValue on element with no string attribs");
1998 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
2000 GetAttr(info
.mInfos
[aAttrEnum
].mNamespaceID
, info
.mInfos
[aAttrEnum
].mName
,
2004 void SVGElement::SetStringBaseValue(uint8_t aAttrEnum
,
2005 const nsAString
& aValue
) {
2006 SVGElement::StringAttributesInfo info
= GetStringInfo();
2008 NS_ASSERTION(info
.mCount
> 0,
2009 "SetBaseValue on element with no string attribs");
2011 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
2013 SetAttr(info
.mInfos
[aAttrEnum
].mNamespaceID
, info
.mInfos
[aAttrEnum
].mName
,
2017 SVGElement::StringListAttributesInfo
SVGElement::GetStringListInfo() {
2018 return StringListAttributesInfo(nullptr, nullptr, 0);
2021 nsAttrValue
SVGElement::WillChangeStringList(
2022 bool aIsConditionalProcessingAttribute
, uint8_t aAttrEnum
,
2023 const mozAutoDocUpdate
& aProofOfUpdate
) {
2025 if (aIsConditionalProcessingAttribute
) {
2026 nsCOMPtr
<SVGTests
> tests(do_QueryInterface(this));
2027 name
= tests
->GetAttrName(aAttrEnum
);
2029 name
= GetStringListInfo().mInfos
[aAttrEnum
].mName
;
2031 return WillChangeValue(name
, aProofOfUpdate
);
2034 void SVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute
,
2036 const nsAttrValue
& aEmptyOrOldValue
,
2037 const mozAutoDocUpdate
& aProofOfUpdate
) {
2039 nsAttrValue newValue
;
2040 nsCOMPtr
<SVGTests
> tests
;
2042 if (aIsConditionalProcessingAttribute
) {
2043 tests
= do_QueryObject(this);
2044 name
= tests
->GetAttrName(aAttrEnum
);
2045 tests
->GetAttrValue(aAttrEnum
, newValue
);
2047 StringListAttributesInfo info
= GetStringListInfo();
2049 NS_ASSERTION(info
.mCount
> 0,
2050 "DidChangeStringList on element with no string list attribs");
2051 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
2053 name
= info
.mInfos
[aAttrEnum
].mName
;
2054 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
2057 DidChangeValue(name
, aEmptyOrOldValue
, newValue
, aProofOfUpdate
);
2059 if (aIsConditionalProcessingAttribute
) {
2060 tests
->MaybeInvalidate();
2064 void SVGElement::DidAnimateAttribute(int32_t aNameSpaceID
, nsAtom
* aAttribute
) {
2065 if (auto* frame
= GetPrimaryFrame()) {
2066 frame
->AttributeChanged(aNameSpaceID
, aAttribute
,
2067 MutationEvent_Binding::SMIL
);
2068 SVGObserverUtils::InvalidateRenderingObservers(frame
);
2071 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
2074 nsresult
SVGElement::ReportAttributeParseFailure(Document
* aDocument
,
2076 const nsAString
& aValue
) {
2077 AutoTArray
<nsString
, 2> strings
;
2078 strings
.AppendElement(nsDependentAtomString(aAttribute
));
2079 strings
.AppendElement(aValue
);
2080 return SVGContentUtils::ReportToConsole(aDocument
, "AttributeParseWarning",
2084 UniquePtr
<SMILAttr
> SVGElement::GetAnimatedAttr(int32_t aNamespaceID
,
2086 if (aNamespaceID
== kNameSpaceID_None
) {
2088 if (GetTransformListAttrName() == aName
) {
2089 // The transform attribute is being animated, so we must ensure that the
2090 // SVGAnimatedTransformList is/has been allocated:
2091 return GetAnimatedTransformList(DO_ALLOCATE
)->ToSMILAttr(this);
2094 // Motion (fake 'attribute' for animateMotion)
2095 if (aName
== nsGkAtoms::mozAnimateMotionDummyAttr
) {
2096 return MakeUnique
<SVGMotionSMILAttr
>(this);
2100 LengthAttributesInfo info
= GetLengthInfo();
2101 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2102 if (aName
== info
.mInfos
[i
].mName
) {
2103 return info
.mValues
[i
].ToSMILAttr(this);
2109 NumberAttributesInfo info
= GetNumberInfo();
2110 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2111 if (aName
== info
.mInfos
[i
].mName
) {
2112 return info
.mValues
[i
].ToSMILAttr(this);
2119 NumberPairAttributesInfo info
= GetNumberPairInfo();
2120 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2121 if (aName
== info
.mInfos
[i
].mName
) {
2122 return info
.mValues
[i
].ToSMILAttr(this);
2129 IntegerAttributesInfo info
= GetIntegerInfo();
2130 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2131 if (aName
== info
.mInfos
[i
].mName
) {
2132 return info
.mValues
[i
].ToSMILAttr(this);
2139 IntegerPairAttributesInfo info
= GetIntegerPairInfo();
2140 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2141 if (aName
== info
.mInfos
[i
].mName
) {
2142 return info
.mValues
[i
].ToSMILAttr(this);
2149 EnumAttributesInfo info
= GetEnumInfo();
2150 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2151 if (aName
== info
.mInfos
[i
].mName
) {
2152 return info
.mValues
[i
].ToSMILAttr(this);
2159 BooleanAttributesInfo info
= GetBooleanInfo();
2160 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2161 if (aName
== info
.mInfos
[i
].mName
) {
2162 return info
.mValues
[i
].ToSMILAttr(this);
2168 if (aName
== nsGkAtoms::orient
) {
2169 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
2170 return orient
? orient
->ToSMILAttr(this) : nullptr;
2174 if (aName
== nsGkAtoms::viewBox
) {
2175 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
2176 return viewBox
? viewBox
->ToSMILAttr(this) : nullptr;
2179 // preserveAspectRatio:
2180 if (aName
== nsGkAtoms::preserveAspectRatio
) {
2181 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
2182 GetAnimatedPreserveAspectRatio();
2183 return preserveAspectRatio
? preserveAspectRatio
->ToSMILAttr(this)
2189 NumberListAttributesInfo info
= GetNumberListInfo();
2190 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2191 if (aName
== info
.mInfos
[i
].mName
) {
2192 MOZ_ASSERT(i
<= UCHAR_MAX
, "Too many attributes");
2193 return info
.mValues
[i
].ToSMILAttr(this, uint8_t(i
));
2200 LengthListAttributesInfo info
= GetLengthListInfo();
2201 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2202 if (aName
== info
.mInfos
[i
].mName
) {
2203 MOZ_ASSERT(i
<= UCHAR_MAX
, "Too many attributes");
2204 return info
.mValues
[i
].ToSMILAttr(this, uint8_t(i
),
2205 info
.mInfos
[i
].mAxis
,
2206 info
.mInfos
[i
].mCouldZeroPadList
);
2213 if (GetPointListAttrName() == aName
) {
2214 SVGAnimatedPointList
* pointList
= GetAnimatedPointList();
2216 return pointList
->ToSMILAttr(this);
2223 if (GetPathDataAttrName() == aName
) {
2224 SVGAnimatedPathSegList
* segList
= GetAnimPathSegList();
2226 return segList
->ToSMILAttr(this);
2231 if (aName
== nsGkAtoms::_class
) {
2232 return mClassAttribute
.ToSMILAttr(this);
2238 StringAttributesInfo info
= GetStringInfo();
2239 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2240 if (aNamespaceID
== info
.mInfos
[i
].mNamespaceID
&&
2241 aName
== info
.mInfos
[i
].mName
) {
2242 return info
.mValues
[i
].ToSMILAttr(this);
2250 void SVGElement::AnimationNeedsResample() {
2251 Document
* doc
= GetComposedDoc();
2252 if (doc
&& doc
->HasAnimationController()) {
2253 doc
->GetAnimationController()->SetResampleNeeded();
2257 void SVGElement::FlushAnimations() {
2258 Document
* doc
= GetComposedDoc();
2259 if (doc
&& doc
->HasAnimationController()) {
2260 doc
->GetAnimationController()->FlushResampleRequests();
2264 void SVGElement::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
2265 size_t* aNodeSize
) const {
2266 Element::AddSizeOfExcludingThis(aSizes
, aNodeSize
);
2269 } // namespace mozilla::dom