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/dom/MutationEventBinding.h"
10 #include "mozilla/dom/MutationObservers.h"
11 #include "mozilla/dom/CSSRuleBinding.h"
12 #include "mozilla/dom/SVGElementBinding.h"
13 #include "mozilla/dom/SVGGeometryElement.h"
14 #include "mozilla/dom/SVGLengthBinding.h"
15 #include "mozilla/dom/SVGSVGElement.h"
16 #include "mozilla/dom/SVGTests.h"
17 #include "mozilla/dom/SVGUnitTypesBinding.h"
18 #include "mozilla/dom/Element.h"
20 #include "mozilla/ArrayUtils.h"
21 #include "mozilla/DebugOnly.h"
22 #include "mozilla/DeclarationBlock.h"
23 #include "mozilla/EventListenerManager.h"
24 #include "mozilla/InternalMutationEvent.h"
25 #include "mozilla/PresShell.h"
26 #include "mozilla/RestyleManager.h"
27 #include "mozilla/SMILAnimationController.h"
28 #include "mozilla/StaticPrefs_layout.h"
29 #include "mozilla/SVGContentUtils.h"
30 #include "mozilla/Unused.h"
32 #include "mozAutoDocUpdate.h"
33 #include "nsAttrValueOrString.h"
34 #include "nsCSSProps.h"
35 #include "nsCSSValue.h"
36 #include "nsContentUtils.h"
37 #include "nsDOMCSSAttrDeclaration.h"
38 #include "nsICSSDeclaration.h"
39 #include "nsIContentInlines.h"
40 #include "mozilla/dom/Document.h"
42 #include "nsGkAtoms.h"
44 #include "nsQueryObject.h"
45 #include "nsLayoutUtils.h"
46 #include "SVGAnimatedNumberList.h"
47 #include "SVGAnimatedLengthList.h"
48 #include "SVGAnimatedPointList.h"
49 #include "SVGAnimatedPathSegList.h"
50 #include "SVGAnimatedTransformList.h"
51 #include "SVGAnimatedBoolean.h"
52 #include "SVGAnimatedEnumeration.h"
53 #include "SVGAnimatedInteger.h"
54 #include "SVGAnimatedIntegerPair.h"
55 #include "SVGAnimatedLength.h"
56 #include "SVGAnimatedNumber.h"
57 #include "SVGAnimatedNumberPair.h"
58 #include "SVGAnimatedOrient.h"
59 #include "SVGAnimatedString.h"
60 #include "SVGAnimatedViewBox.h"
61 #include "SVGGeometryProperty.h"
62 #include "SVGMotionSMILAttr.h"
65 // This is needed to ensure correct handling of calls to the
66 // vararg-list methods in this file:
67 // SVGElement::GetAnimated{Length,Number,Integer}Values
68 // See bug 547964 for details:
69 static_assert(sizeof(void*) == sizeof(nullptr),
70 "nullptr should be the correct size");
72 nsresult
NS_NewSVGElement(
73 mozilla::dom::Element
** aResult
,
74 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
) {
75 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo(aNodeInfo
);
76 auto* nim
= nodeInfo
->NodeInfoManager();
77 RefPtr
<mozilla::dom::SVGElement
> it
=
78 new (nim
) mozilla::dom::SVGElement(nodeInfo
.forget());
79 nsresult rv
= it
->Init();
89 namespace mozilla::dom
{
90 using namespace SVGUnitTypes_Binding
;
92 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGElement
)
94 // Use the CC variant of this, even though this class does not define
95 // a new CC participant, to make QIing to the CC interfaces faster.
96 NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(SVGElement
, SVGElementBase
,
99 SVGEnumMapping
SVGElement::sSVGUnitTypesMap
[] = {
100 {nsGkAtoms::userSpaceOnUse
, SVG_UNIT_TYPE_USERSPACEONUSE
},
101 {nsGkAtoms::objectBoundingBox
, SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
},
104 SVGElement::SVGElement(already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
105 : SVGElementBase(std::move(aNodeInfo
)) {}
107 SVGElement::~SVGElement() {
108 OwnerDoc()->UnscheduleSVGForPresAttrEvaluation(this);
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 dest
->GetLengthInfo().CopyAllFrom(GetLengthInfo());
211 dest
->GetNumberInfo().CopyAllFrom(GetNumberInfo());
212 dest
->GetNumberPairInfo().CopyAllFrom(GetNumberPairInfo());
213 dest
->GetIntegerInfo().CopyAllFrom(GetIntegerInfo());
214 dest
->GetIntegerPairInfo().CopyAllFrom(GetIntegerPairInfo());
215 dest
->GetEnumInfo().CopyAllFrom(GetEnumInfo());
216 dest
->GetStringInfo().CopyAllFrom(GetStringInfo());
217 dest
->GetLengthListInfo().CopyAllFrom(GetLengthListInfo());
218 dest
->GetNumberListInfo().CopyAllFrom(GetNumberListInfo());
224 //----------------------------------------------------------------------
225 // SVGElement methods
227 void SVGElement::DidAnimateClass() {
228 // For Servo, snapshot the element before we change it.
229 PresShell
* presShell
= OwnerDoc()->GetPresShell();
231 if (nsPresContext
* presContext
= presShell
->GetPresContext()) {
232 presContext
->RestyleManager()->ClassAttributeWillBeChangedBySMIL(this);
237 mClassAttribute
.GetAnimValue(src
, this);
238 if (!mClassAnimAttr
) {
239 mClassAnimAttr
= MakeUnique
<nsAttrValue
>();
241 mClassAnimAttr
->ParseAtomArray(src
);
243 // FIXME(emilio): This re-selector-matches, but we do the snapshot stuff right
244 // above... Is this needed anymore?
246 presShell
->RestyleForAnimation(this, RestyleHint::RESTYLE_SELF
);
250 nsresult
SVGElement::Init() {
251 // Set up length attributes - can't do this in the constructor
252 // because we can't do a virtual call at that point
254 GetLengthInfo().ResetAll();
255 GetNumberInfo().ResetAll();
256 GetNumberPairInfo().ResetAll();
257 GetIntegerInfo().ResetAll();
258 GetIntegerPairInfo().ResetAll();
259 GetBooleanInfo().ResetAll();
260 GetEnumInfo().ResetAll();
262 if (SVGAnimatedOrient
* orient
= GetAnimatedOrient()) {
266 if (SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox()) {
270 if (SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
271 GetAnimatedPreserveAspectRatio()) {
272 preserveAspectRatio
->Init();
275 GetLengthListInfo().ResetAll();
276 GetNumberListInfo().ResetAll();
278 // No need to reset SVGPointList since the default value is always the same
281 // No need to reset SVGPathData since the default value is always the same
284 GetStringInfo().ResetAll();
288 //----------------------------------------------------------------------
291 //----------------------------------------------------------------------
292 // nsIContent methods
294 nsresult
SVGElement::BindToTree(BindContext
& aContext
, nsINode
& aParent
) {
295 nsresult rv
= SVGElementBase::BindToTree(aContext
, aParent
);
296 NS_ENSURE_SUCCESS(rv
, rv
);
298 // Hide any nonce from the DOM, but keep the internal value of the
299 // nonce by copying and resetting the internal nonce value.
300 if (HasFlag(NODE_HAS_NONCE_AND_HEADER_CSP
) && IsInComposedDoc() &&
301 OwnerDoc()->GetBrowsingContext()) {
302 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
303 "SVGElement::ResetNonce::Runnable",
304 [self
= RefPtr
<SVGElement
>(this)]() {
306 self
->GetNonce(nonce
);
307 self
->SetAttr(kNameSpaceID_None
, nsGkAtoms::nonce
, u
""_ns
, true);
308 self
->SetNonce(nonce
);
315 nsresult
SVGElement::AfterSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
316 const nsAttrValue
* aValue
,
317 const nsAttrValue
* aOldValue
,
318 nsIPrincipal
* aSubjectPrincipal
,
320 // We don't currently use nsMappedAttributes within SVG. If this changes, we
321 // need to be very careful because some nsAttrValues used by SVG point to
322 // member data of SVG elements and if an nsAttrValue outlives the SVG element
323 // whose data it points to (by virtue of being stored in
324 // mAttrs->mMappedAttributes, meaning it's shared between
325 // elements), the pointer will dangle. See bug 724680.
326 MOZ_ASSERT(!mAttrs
.HasMappedAttrs(),
327 "Unexpected use of nsMappedAttributes within SVG");
329 // If this is an svg presentation attribute we need to map it into
330 // the content declaration block.
331 // XXX For some reason incremental mapping doesn't work, so for now
332 // just delete the style rule and lazily reconstruct it as needed).
333 if (aNamespaceID
== kNameSpaceID_None
&& IsAttributeMapped(aName
)) {
334 mContentDeclarationBlock
= nullptr;
335 OwnerDoc()->ScheduleSVGForPresAttrEvaluation(this);
338 if (IsEventAttributeName(aName
) && aValue
) {
339 MOZ_ASSERT(aValue
->Type() == nsAttrValue::eString
,
340 "Expected string value for script body");
341 SetEventHandler(GetEventNameForAttr(aName
), aValue
->GetStringValue());
344 // The nonce will be copied over to an internal slot and cleared from the
345 // Element within BindToTree to avoid CSS Selector nonce exfiltration if
346 // the CSP list contains a header-delivered CSP.
347 if (nsGkAtoms::nonce
== aName
&& kNameSpaceID_None
== aNamespaceID
) {
349 SetNonce(aValue
->GetStringValue());
350 if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) {
351 SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP
);
358 return SVGElementBase::AfterSetAttr(aNamespaceID
, aName
, aValue
, aOldValue
,
359 aSubjectPrincipal
, aNotify
);
362 bool SVGElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
363 const nsAString
& aValue
,
364 nsIPrincipal
* aMaybeScriptedPrincipal
,
365 nsAttrValue
& aResult
) {
367 bool foundMatch
= false;
368 bool didSetResult
= false;
370 if (aNamespaceID
== kNameSpaceID_None
) {
371 // Check for SVGAnimatedLength attribute
372 LengthAttributesInfo lengthInfo
= GetLengthInfo();
375 for (i
= 0; i
< lengthInfo
.mCount
; i
++) {
376 if (aAttribute
== lengthInfo
.mInfos
[i
].mName
) {
377 rv
= lengthInfo
.mValues
[i
].SetBaseValueString(aValue
, this, false);
381 aResult
.SetTo(lengthInfo
.mValues
[i
], &aValue
);
390 // Check for SVGAnimatedLengthList attribute
391 LengthListAttributesInfo lengthListInfo
= GetLengthListInfo();
392 for (i
= 0; i
< lengthListInfo
.mCount
; i
++) {
393 if (aAttribute
== lengthListInfo
.mInfos
[i
].mName
) {
394 rv
= lengthListInfo
.mValues
[i
].SetBaseValueString(aValue
);
396 lengthListInfo
.Reset(i
);
398 aResult
.SetTo(lengthListInfo
.mValues
[i
].GetBaseValue(), &aValue
);
408 // Check for SVGAnimatedNumberList attribute
409 NumberListAttributesInfo numberListInfo
= GetNumberListInfo();
410 for (i
= 0; i
< numberListInfo
.mCount
; i
++) {
411 if (aAttribute
== numberListInfo
.mInfos
[i
].mName
) {
412 rv
= numberListInfo
.mValues
[i
].SetBaseValueString(aValue
);
414 numberListInfo
.Reset(i
);
416 aResult
.SetTo(numberListInfo
.mValues
[i
].GetBaseValue(), &aValue
);
426 // Check for SVGAnimatedPointList attribute
427 if (GetPointListAttrName() == aAttribute
) {
428 if (SVGAnimatedPointList
* pointList
= GetAnimatedPointList()) {
429 pointList
->SetBaseValueString(aValue
);
430 // The spec says we parse everything up to the failure, so we DON'T
431 // need to check the result of SetBaseValueString or call
432 // pointList->ClearBaseValue() if it fails
433 aResult
.SetTo(pointList
->GetBaseValue(), &aValue
);
441 // Check for SVGAnimatedPathSegList attribute
442 if (GetPathDataAttrName() == aAttribute
) {
443 if (SVGAnimatedPathSegList
* segList
= GetAnimPathSegList()) {
444 segList
->SetBaseValueString(aValue
);
445 // The spec says we parse everything up to the failure, so we DON'T
446 // need to check the result of SetBaseValueString or call
447 // segList->ClearBaseValue() if it fails
448 aResult
.SetTo(segList
->GetBaseValue(), &aValue
);
456 // Check for SVGAnimatedNumber attribute
457 NumberAttributesInfo numberInfo
= GetNumberInfo();
458 for (i
= 0; i
< numberInfo
.mCount
; i
++) {
459 if (aAttribute
== numberInfo
.mInfos
[i
].mName
) {
460 rv
= numberInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
464 aResult
.SetTo(numberInfo
.mValues
[i
].GetBaseValue(), &aValue
);
474 // Check for SVGAnimatedNumberPair attribute
475 NumberPairAttributesInfo numberPairInfo
= GetNumberPairInfo();
476 for (i
= 0; i
< numberPairInfo
.mCount
; i
++) {
477 if (aAttribute
== numberPairInfo
.mInfos
[i
].mName
) {
478 rv
= numberPairInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
480 numberPairInfo
.Reset(i
);
482 aResult
.SetTo(numberPairInfo
.mValues
[i
], &aValue
);
492 // Check for SVGAnimatedInteger attribute
493 IntegerAttributesInfo integerInfo
= GetIntegerInfo();
494 for (i
= 0; i
< integerInfo
.mCount
; i
++) {
495 if (aAttribute
== integerInfo
.mInfos
[i
].mName
) {
496 rv
= integerInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
498 integerInfo
.Reset(i
);
500 aResult
.SetTo(integerInfo
.mValues
[i
].GetBaseValue(), &aValue
);
510 // Check for SVGAnimatedIntegerPair attribute
511 IntegerPairAttributesInfo integerPairInfo
= GetIntegerPairInfo();
512 for (i
= 0; i
< integerPairInfo
.mCount
; i
++) {
513 if (aAttribute
== integerPairInfo
.mInfos
[i
].mName
) {
514 rv
= integerPairInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
516 integerPairInfo
.Reset(i
);
518 aResult
.SetTo(integerPairInfo
.mValues
[i
], &aValue
);
528 // Check for SVGAnimatedBoolean attribute
529 BooleanAttributesInfo booleanInfo
= GetBooleanInfo();
530 for (i
= 0; i
< booleanInfo
.mCount
; i
++) {
531 if (aAttribute
== booleanInfo
.mInfos
[i
].mName
) {
532 nsAtom
* valAtom
= NS_GetStaticAtom(aValue
);
533 rv
= valAtom
? booleanInfo
.mValues
[i
].SetBaseValueAtom(valAtom
, this)
534 : NS_ERROR_DOM_SYNTAX_ERR
;
536 booleanInfo
.Reset(i
);
538 aResult
.SetTo(valAtom
);
548 // Check for SVGAnimatedEnumeration attribute
549 EnumAttributesInfo enumInfo
= GetEnumInfo();
550 for (i
= 0; i
< enumInfo
.mCount
; i
++) {
551 if (aAttribute
== enumInfo
.mInfos
[i
].mName
) {
552 RefPtr
<nsAtom
> valAtom
= NS_Atomize(aValue
);
553 if (!enumInfo
.mValues
[i
].SetBaseValueAtom(valAtom
, this)) {
554 // Exact error value does not matter; we just need to mark the
556 rv
= NS_ERROR_FAILURE
;
559 aResult
.SetTo(valAtom
);
569 // Check for conditional processing attributes
570 nsCOMPtr
<SVGTests
> tests
= do_QueryObject(this);
571 if (tests
&& tests
->ParseConditionalProcessingAttribute(
572 aAttribute
, aValue
, aResult
)) {
578 // Check for StringList attribute
579 StringListAttributesInfo stringListInfo
= GetStringListInfo();
580 for (i
= 0; i
< stringListInfo
.mCount
; i
++) {
581 if (aAttribute
== stringListInfo
.mInfos
[i
].mName
) {
582 rv
= stringListInfo
.mValues
[i
].SetValue(aValue
);
584 stringListInfo
.Reset(i
);
586 aResult
.SetTo(stringListInfo
.mValues
[i
], &aValue
);
596 // Check for orient attribute
597 if (aAttribute
== nsGkAtoms::orient
) {
598 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
600 rv
= orient
->SetBaseValueString(aValue
, this, false);
604 aResult
.SetTo(*orient
, &aValue
);
609 // Check for viewBox attribute
610 } else if (aAttribute
== nsGkAtoms::viewBox
) {
611 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
613 rv
= viewBox
->SetBaseValueString(aValue
, this, false);
617 aResult
.SetTo(*viewBox
, &aValue
);
622 // Check for preserveAspectRatio attribute
623 } else if (aAttribute
== nsGkAtoms::preserveAspectRatio
) {
624 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
625 GetAnimatedPreserveAspectRatio();
626 if (preserveAspectRatio
) {
627 rv
= preserveAspectRatio
->SetBaseValueString(aValue
, this, false);
629 preserveAspectRatio
->Init();
631 aResult
.SetTo(*preserveAspectRatio
, &aValue
);
636 // Check for SVGAnimatedTransformList attribute
637 } else if (GetTransformListAttrName() == aAttribute
) {
638 // The transform attribute is being set, so we must ensure that the
639 // SVGAnimatedTransformList is/has been allocated:
640 SVGAnimatedTransformList
* transformList
=
641 GetAnimatedTransformList(DO_ALLOCATE
);
642 rv
= transformList
->SetBaseValueString(aValue
, this);
644 transformList
->ClearBaseValue();
646 aResult
.SetTo(transformList
->GetBaseValue(), &aValue
);
650 } else if (aAttribute
== nsGkAtoms::tabindex
) {
651 didSetResult
= aResult
.ParseIntValue(aValue
);
656 if (aAttribute
== nsGkAtoms::_class
) {
657 mClassAttribute
.SetBaseValue(aValue
, this, false);
658 aResult
.ParseAtomArray(aValue
);
662 if (aAttribute
== nsGkAtoms::rel
) {
663 aResult
.ParseAtomArray(aValue
);
669 // Check for SVGAnimatedString attribute
670 StringAttributesInfo stringInfo
= GetStringInfo();
671 for (uint32_t i
= 0; i
< stringInfo
.mCount
; i
++) {
672 if (aNamespaceID
== stringInfo
.mInfos
[i
].mNamespaceID
&&
673 aAttribute
== stringInfo
.mInfos
[i
].mName
) {
674 stringInfo
.mValues
[i
].SetBaseValue(aValue
, this, false);
683 ReportAttributeParseFailure(OwnerDoc(), aAttribute
, aValue
);
687 aResult
.SetTo(aValue
);
692 return SVGElementBase::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
693 aMaybeScriptedPrincipal
, aResult
);
696 void SVGElement::UnsetAttrInternal(int32_t aNamespaceID
, nsAtom
* aName
,
698 // XXXbz there's a bunch of redundancy here with AfterSetAttr.
699 // Maybe consolidate?
701 if (aNamespaceID
== kNameSpaceID_None
) {
702 // If this is an svg presentation attribute, remove declaration block to
704 if (IsAttributeMapped(aName
)) {
705 mContentDeclarationBlock
= nullptr;
708 if (IsEventAttributeName(aName
)) {
709 EventListenerManager
* manager
= GetExistingListenerManager();
711 nsAtom
* eventName
= GetEventNameForAttr(aName
);
712 manager
->RemoveEventHandler(eventName
);
717 // Check if this is a length attribute going away
718 LengthAttributesInfo lenInfo
= GetLengthInfo();
720 for (uint32_t i
= 0; i
< lenInfo
.mCount
; i
++) {
721 if (aName
== lenInfo
.mInfos
[i
].mName
) {
722 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
728 // Check if this is a length list attribute going away
729 LengthListAttributesInfo lengthListInfo
= GetLengthListInfo();
731 for (uint32_t i
= 0; i
< lengthListInfo
.mCount
; i
++) {
732 if (aName
== lengthListInfo
.mInfos
[i
].mName
) {
733 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
734 lengthListInfo
.Reset(i
);
739 // Check if this is a number list attribute going away
740 NumberListAttributesInfo numberListInfo
= GetNumberListInfo();
742 for (uint32_t i
= 0; i
< numberListInfo
.mCount
; i
++) {
743 if (aName
== numberListInfo
.mInfos
[i
].mName
) {
744 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
745 numberListInfo
.Reset(i
);
750 // Check if this is a point list attribute going away
751 if (GetPointListAttrName() == aName
) {
752 SVGAnimatedPointList
* pointList
= GetAnimatedPointList();
754 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
755 pointList
->ClearBaseValue();
760 // Check if this is a path segment list attribute going away
761 if (GetPathDataAttrName() == aName
) {
762 SVGAnimatedPathSegList
* segList
= GetAnimPathSegList();
764 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
765 segList
->ClearBaseValue();
770 // Check if this is a number attribute going away
771 NumberAttributesInfo numInfo
= GetNumberInfo();
773 for (uint32_t i
= 0; i
< numInfo
.mCount
; i
++) {
774 if (aName
== numInfo
.mInfos
[i
].mName
) {
780 // Check if this is a number pair attribute going away
781 NumberPairAttributesInfo numPairInfo
= GetNumberPairInfo();
783 for (uint32_t i
= 0; i
< numPairInfo
.mCount
; i
++) {
784 if (aName
== numPairInfo
.mInfos
[i
].mName
) {
785 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
786 numPairInfo
.Reset(i
);
791 // Check if this is an integer attribute going away
792 IntegerAttributesInfo intInfo
= GetIntegerInfo();
794 for (uint32_t i
= 0; i
< intInfo
.mCount
; i
++) {
795 if (aName
== intInfo
.mInfos
[i
].mName
) {
801 // Check if this is an integer pair attribute going away
802 IntegerPairAttributesInfo intPairInfo
= GetIntegerPairInfo();
804 for (uint32_t i
= 0; i
< intPairInfo
.mCount
; i
++) {
805 if (aName
== intPairInfo
.mInfos
[i
].mName
) {
806 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
807 intPairInfo
.Reset(i
);
812 // Check if this is a boolean attribute going away
813 BooleanAttributesInfo boolInfo
= GetBooleanInfo();
815 for (uint32_t i
= 0; i
< boolInfo
.mCount
; i
++) {
816 if (aName
== boolInfo
.mInfos
[i
].mName
) {
822 // Check if this is an enum attribute going away
823 EnumAttributesInfo enumInfo
= GetEnumInfo();
825 for (uint32_t i
= 0; i
< enumInfo
.mCount
; i
++) {
826 if (aName
== enumInfo
.mInfos
[i
].mName
) {
832 // Check if this is an orient attribute going away
833 if (aName
== nsGkAtoms::orient
) {
834 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
836 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
842 // Check if this is a viewBox attribute going away
843 if (aName
== nsGkAtoms::viewBox
) {
844 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
846 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
852 // Check if this is a preserveAspectRatio attribute going away
853 if (aName
== nsGkAtoms::preserveAspectRatio
) {
854 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
855 GetAnimatedPreserveAspectRatio();
856 if (preserveAspectRatio
) {
857 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
858 preserveAspectRatio
->Init();
863 // Check if this is a transform list attribute going away
864 if (GetTransformListAttrName() == aName
) {
865 SVGAnimatedTransformList
* transformList
= GetAnimatedTransformList();
867 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
868 transformList
->ClearBaseValue();
873 // Check for conditional processing attributes
874 nsCOMPtr
<SVGTests
> tests
= do_QueryObject(this);
875 if (tests
&& tests
->IsConditionalProcessingAttribute(aName
)) {
876 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
877 tests
->UnsetAttr(aName
);
881 // Check if this is a string list attribute going away
882 StringListAttributesInfo stringListInfo
= GetStringListInfo();
884 for (uint32_t i
= 0; i
< stringListInfo
.mCount
; i
++) {
885 if (aName
== stringListInfo
.mInfos
[i
].mName
) {
886 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
887 stringListInfo
.Reset(i
);
892 if (aName
== nsGkAtoms::_class
) {
893 mClassAttribute
.Init();
898 // Check if this is a string attribute going away
899 StringAttributesInfo stringInfo
= GetStringInfo();
901 for (uint32_t i
= 0; i
< stringInfo
.mCount
; i
++) {
902 if (aNamespaceID
== stringInfo
.mInfos
[i
].mNamespaceID
&&
903 aName
== stringInfo
.mInfos
[i
].mName
) {
910 nsresult
SVGElement::BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
911 const nsAttrValueOrString
* aValue
,
914 UnsetAttrInternal(aNamespaceID
, aName
, aNotify
);
916 return SVGElementBase::BeforeSetAttr(aNamespaceID
, aName
, aValue
, aNotify
);
919 nsChangeHint
SVGElement::GetAttributeChangeHint(const nsAtom
* aAttribute
,
920 int32_t aModType
) const {
921 nsChangeHint retval
=
922 SVGElementBase::GetAttributeChangeHint(aAttribute
, aModType
);
924 nsCOMPtr
<SVGTests
> tests
= do_QueryObject(const_cast<SVGElement
*>(this));
925 if (tests
&& tests
->IsConditionalProcessingAttribute(aAttribute
)) {
926 // It would be nice to only reconstruct the frame if the value returned by
927 // SVGTests::PassesConditionalProcessingTests has changed, but we don't
929 retval
|= nsChangeHint_ReconstructFrame
;
934 bool SVGElement::IsNodeOfType(uint32_t aFlags
) const { return false; }
936 void SVGElement::NodeInfoChanged(Document
* aOldDoc
) {
937 SVGElementBase::NodeInfoChanged(aOldDoc
);
938 aOldDoc
->UnscheduleSVGForPresAttrEvaluation(this);
939 mContentDeclarationBlock
= nullptr;
940 OwnerDoc()->ScheduleSVGForPresAttrEvaluation(this);
944 SVGElement::IsAttributeMapped(const nsAtom
* name
) const {
945 if (name
== nsGkAtoms::lang
) {
948 return SVGElementBase::IsAttributeMapped(name
);
951 // PresentationAttributes-FillStroke
953 const Element::MappedAttributeEntry
SVGElement::sFillStrokeMap
[] = {
955 {nsGkAtoms::fill_opacity
},
956 {nsGkAtoms::fill_rule
},
957 {nsGkAtoms::paint_order
},
959 {nsGkAtoms::stroke_dasharray
},
960 {nsGkAtoms::stroke_dashoffset
},
961 {nsGkAtoms::stroke_linecap
},
962 {nsGkAtoms::stroke_linejoin
},
963 {nsGkAtoms::stroke_miterlimit
},
964 {nsGkAtoms::stroke_opacity
},
965 {nsGkAtoms::stroke_width
},
966 {nsGkAtoms::vector_effect
},
969 // PresentationAttributes-Graphics
971 const Element::MappedAttributeEntry
SVGElement::sGraphicsMap
[] = {
972 {nsGkAtoms::clip_path
},
973 {nsGkAtoms::clip_rule
},
974 {nsGkAtoms::colorInterpolation
},
976 {nsGkAtoms::display
},
978 {nsGkAtoms::image_rendering
},
980 {nsGkAtoms::opacity
},
981 {nsGkAtoms::pointer_events
},
982 {nsGkAtoms::shape_rendering
},
983 {nsGkAtoms::text_rendering
},
984 {nsGkAtoms::transform_origin
},
985 {nsGkAtoms::visibility
},
988 // PresentationAttributes-TextContentElements
990 const Element::MappedAttributeEntry
SVGElement::sTextContentElementsMap
[] = {
991 // Properties that we don't support are commented out.
992 // { nsGkAtoms::alignment_baseline },
993 // { nsGkAtoms::baseline_shift },
994 {nsGkAtoms::direction
}, {nsGkAtoms::dominant_baseline
},
995 {nsGkAtoms::letter_spacing
}, {nsGkAtoms::text_anchor
},
996 {nsGkAtoms::text_decoration
}, {nsGkAtoms::unicode_bidi
},
997 {nsGkAtoms::white_space
}, {nsGkAtoms::word_spacing
},
998 {nsGkAtoms::writing_mode
}, {nullptr}};
1000 // PresentationAttributes-FontSpecification
1002 const Element::MappedAttributeEntry
SVGElement::sFontSpecificationMap
[] = {
1003 {nsGkAtoms::font_family
}, {nsGkAtoms::font_size
},
1004 {nsGkAtoms::font_size_adjust
}, {nsGkAtoms::font_stretch
},
1005 {nsGkAtoms::font_style
}, {nsGkAtoms::font_variant
},
1006 {nsGkAtoms::fontWeight
}, {nullptr}};
1008 // PresentationAttributes-GradientStop
1010 const Element::MappedAttributeEntry
SVGElement::sGradientStopMap
[] = {
1011 {nsGkAtoms::stop_color
}, {nsGkAtoms::stop_opacity
}, {nullptr}};
1013 // PresentationAttributes-Viewports
1015 const Element::MappedAttributeEntry
SVGElement::sViewportsMap
[] = {
1016 {nsGkAtoms::overflow
}, {nsGkAtoms::clip
}, {nullptr}};
1018 // PresentationAttributes-Makers
1020 const Element::MappedAttributeEntry
SVGElement::sMarkersMap
[] = {
1021 {nsGkAtoms::marker_end
},
1022 {nsGkAtoms::marker_mid
},
1023 {nsGkAtoms::marker_start
},
1026 // PresentationAttributes-Color
1028 const Element::MappedAttributeEntry
SVGElement::sColorMap
[] = {
1029 {nsGkAtoms::color
}, {nullptr}};
1031 // PresentationAttributes-Filters
1033 const Element::MappedAttributeEntry
SVGElement::sFiltersMap
[] = {
1034 {nsGkAtoms::colorInterpolationFilters
}, {nullptr}};
1036 // PresentationAttributes-feFlood
1038 const Element::MappedAttributeEntry
SVGElement::sFEFloodMap
[] = {
1039 {nsGkAtoms::flood_color
}, {nsGkAtoms::flood_opacity
}, {nullptr}};
1041 // PresentationAttributes-LightingEffects
1043 const Element::MappedAttributeEntry
SVGElement::sLightingEffectsMap
[] = {
1044 {nsGkAtoms::lighting_color
}, {nullptr}};
1046 // PresentationAttributes-mask
1048 const Element::MappedAttributeEntry
SVGElement::sMaskMap
[] = {
1049 {nsGkAtoms::mask_type
}, {nullptr}};
1051 //----------------------------------------------------------------------
1054 // forwarded to Element implementations
1056 //----------------------------------------------------------------------
1058 SVGSVGElement
* SVGElement::GetOwnerSVGElement() {
1059 nsIContent
* ancestor
= GetFlattenedTreeParent();
1061 while (ancestor
&& ancestor
->IsSVGElement()) {
1062 if (ancestor
->IsSVGElement(nsGkAtoms::foreignObject
)) {
1065 if (ancestor
->IsSVGElement(nsGkAtoms::svg
)) {
1066 return static_cast<SVGSVGElement
*>(ancestor
);
1068 ancestor
= ancestor
->GetFlattenedTreeParent();
1071 // we don't have an ancestor <svg> element...
1075 SVGElement
* SVGElement::GetViewportElement() {
1076 return SVGContentUtils::GetNearestViewportElement(this);
1079 already_AddRefed
<DOMSVGAnimatedString
> SVGElement::ClassName() {
1080 return mClassAttribute
.ToDOMAnimatedString(this);
1084 bool SVGElement::UpdateDeclarationBlockFromLength(
1085 DeclarationBlock
& aBlock
, nsCSSPropertyID aPropId
,
1086 const SVGAnimatedLength
& aLength
, ValToUse aValToUse
) {
1087 aBlock
.AssertMutable();
1090 if (aValToUse
== ValToUse::Anim
) {
1091 value
= aLength
.GetAnimValInSpecifiedUnits();
1093 MOZ_ASSERT(aValToUse
== ValToUse::Base
);
1094 value
= aLength
.GetBaseValInSpecifiedUnits();
1097 // SVG parser doesn't check non-negativity of some parsed value,
1098 // we should not pass those to CSS side.
1100 SVGGeometryProperty::IsNonNegativeGeometryProperty(aPropId
)) {
1104 nsCSSUnit cssUnit
= SVGGeometryProperty::SpecifiedUnitTypeToCSSUnit(
1105 aLength
.GetSpecifiedUnitType());
1107 if (cssUnit
== eCSSUnit_Percent
) {
1108 Servo_DeclarationBlock_SetPercentValue(aBlock
.Raw(), aPropId
,
1111 Servo_DeclarationBlock_SetLengthValue(aBlock
.Raw(), aPropId
, value
,
1119 bool SVGElement::UpdateDeclarationBlockFromPath(
1120 DeclarationBlock
& aBlock
, const SVGAnimatedPathSegList
& aPath
,
1121 ValToUse aValToUse
) {
1122 aBlock
.AssertMutable();
1124 const SVGPathData
& pathData
=
1125 aValToUse
== ValToUse::Anim
? aPath
.GetAnimValue() : aPath
.GetBaseValue();
1127 // SVGPathData::mData is fallible but rust binding accepts nsTArray only, so
1128 // we need to point to one or the other. Fortunately, fallible and infallible
1129 // array types can be implicitly converted provided they are const.
1131 // FIXME: here we just convert the data structure from cpp verion into rust
1132 // version. We don't do any normalization for the path data from d attribute.
1133 // Based on the current discussion of https://github.com/w3c/svgwg/issues/321,
1134 // we may have to convert the relative commands into absolute commands.
1135 // The normalization should be fixed in Bug 1489392. Besides, Bug 1714238
1136 // will use the same data structure, so we may simplify this more.
1137 const nsTArray
<float>& asInFallibleArray
= pathData
.RawData();
1138 Servo_DeclarationBlock_SetPathValue(aBlock
.Raw(), eCSSProperty_d
,
1139 &asInFallibleArray
);
1143 //------------------------------------------------------------------------
1144 // Helper class: MappedAttrParser, for parsing values of mapped attributes
1148 class MOZ_STACK_CLASS MappedAttrParser
{
1150 explicit MappedAttrParser(SVGElement
& aElement
) : mElement(aElement
) {}
1151 ~MappedAttrParser() {
1153 "If mDecl was initialized, it should have been returned via "
1154 "TakeDeclarationBlock (and have its pointer cleared)");
1157 // Parses a mapped attribute value.
1158 void ParseMappedAttrValue(nsAtom
* aMappedAttrName
,
1159 const nsAString
& aMappedAttrValue
);
1161 void TellStyleAlreadyParsedResult(nsAtom
const* aAtom
,
1162 SVGAnimatedLength
const& aLength
);
1163 void TellStyleAlreadyParsedResult(const SVGAnimatedPathSegList
& aPath
);
1165 // If we've parsed any values for mapped attributes, this method returns the
1166 // already_AddRefed css::Declaration that incorporates the parsed
1167 // values. Otherwise, this method returns null.
1168 already_AddRefed
<DeclarationBlock
> TakeDeclarationBlock() {
1169 return mDecl
.forget();
1172 DeclarationBlock
& EnsureDeclarationBlock() {
1174 mDecl
= new DeclarationBlock();
1179 URLExtraData
& EnsureExtraData() {
1181 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
1182 ReferrerInfo::CreateForSVGResources(mElement
.OwnerDoc());
1183 mExtraData
= MakeRefPtr
<URLExtraData
>(mElement
.GetBaseURI(), referrerInfo
,
1184 mElement
.NodePrincipal());
1190 // Declaration for storing parsed values (lazily initialized).
1191 RefPtr
<DeclarationBlock
> mDecl
;
1193 // URL data for parsing stuff. Also lazy.
1194 RefPtr
<URLExtraData
> mExtraData
;
1196 // For reporting use counters
1197 SVGElement
& mElement
;
1200 void MappedAttrParser::ParseMappedAttrValue(nsAtom
* aMappedAttrName
,
1201 const nsAString
& aMappedAttrValue
) {
1202 // Get the nsCSSPropertyID ID for our mapped attribute.
1203 nsCSSPropertyID propertyID
=
1204 nsCSSProps::LookupProperty(nsAtomCString(aMappedAttrName
));
1205 if (propertyID
!= eCSSProperty_UNKNOWN
) {
1206 bool changed
= false; // outparam for ParseProperty.
1207 NS_ConvertUTF16toUTF8
value(aMappedAttrValue
);
1209 auto* doc
= mElement
.OwnerDoc();
1210 changed
= Servo_DeclarationBlock_SetPropertyById(
1211 EnsureDeclarationBlock().Raw(), propertyID
, &value
, false,
1212 &EnsureExtraData(), ParsingMode::AllowUnitlessLength
,
1213 doc
->GetCompatibilityMode(), doc
->CSSLoader(), StyleCssRuleType::Style
,
1216 // TODO(emilio): If we want to record these from CSSOM more generally, we
1217 // can pass the document use counters down the FFI call. For now manually
1219 if (changed
&& StaticPrefs::layout_css_use_counters_enabled()) {
1220 UseCounter useCounter
= nsCSSProps::UseCounterFor(propertyID
);
1221 MOZ_ASSERT(useCounter
!= eUseCounter_UNKNOWN
);
1222 doc
->SetUseCounter(useCounter
);
1226 MOZ_ASSERT(aMappedAttrName
== nsGkAtoms::lang
,
1227 "Only 'lang' should be unrecognized!");
1228 // CSS parser doesn't know about 'lang', so we need to handle it specially.
1229 if (aMappedAttrName
== nsGkAtoms::lang
) {
1230 propertyID
= eCSSProperty__x_lang
;
1231 RefPtr
<nsAtom
> atom
= NS_Atomize(aMappedAttrValue
);
1232 Servo_DeclarationBlock_SetIdentStringValue(EnsureDeclarationBlock().Raw(),
1237 void MappedAttrParser::TellStyleAlreadyParsedResult(
1238 nsAtom
const* aAtom
, SVGAnimatedLength
const& aLength
) {
1239 nsCSSPropertyID propertyID
= nsCSSProps::LookupProperty(nsAtomCString(aAtom
));
1240 SVGElement::UpdateDeclarationBlockFromLength(EnsureDeclarationBlock(),
1241 propertyID
, aLength
,
1242 SVGElement::ValToUse::Base
);
1245 void MappedAttrParser::TellStyleAlreadyParsedResult(
1246 const SVGAnimatedPathSegList
& aPath
) {
1247 SVGElement::UpdateDeclarationBlockFromPath(EnsureDeclarationBlock(), aPath
,
1248 SVGElement::ValToUse::Base
);
1253 //----------------------------------------------------------------------
1254 // Implementation Helpers:
1256 void SVGElement::UpdateContentDeclarationBlock() {
1257 MOZ_ASSERT(!mContentDeclarationBlock
,
1258 "we already have a content declaration block");
1260 MappedAttrParser
mappedAttrParser(*this);
1262 bool lengthAffectsStyle
=
1263 SVGGeometryProperty::ElementMapsLengthsToStyle(this);
1266 while (BorrowedAttrInfo info
= GetAttrInfoAt(i
++)) {
1267 const nsAttrName
* attrName
= info
.mName
;
1268 if (!attrName
->IsAtom() || !IsAttributeMapped(attrName
->Atom())) {
1272 // FIXME(emilio): This check is dead, since IsAtom() implies that
1273 // NamespaceID() == None.
1274 if (attrName
->NamespaceID() != kNameSpaceID_None
&&
1275 !attrName
->Equals(nsGkAtoms::lang
, kNameSpaceID_XML
)) {
1279 if (attrName
->Equals(nsGkAtoms::lang
, kNameSpaceID_None
) &&
1280 HasAttr(kNameSpaceID_XML
, nsGkAtoms::lang
)) {
1281 // xml:lang has precedence, and will get set via Gecko_GetXMLLangValue().
1285 if (lengthAffectsStyle
) {
1286 auto const* length
= GetAnimatedLength(attrName
->Atom());
1288 if (length
&& length
->HasBaseVal()) {
1289 // This is an element with geometry property set via SVG attribute,
1290 // and the attribute is already successfully parsed. We want to go
1291 // through the optimized path to tell the style system the result
1292 // directly, rather than let it parse the same thing again.
1293 mappedAttrParser
.TellStyleAlreadyParsedResult(attrName
->Atom(),
1299 if (attrName
->Equals(nsGkAtoms::d
, kNameSpaceID_None
)) {
1300 const auto* path
= GetAnimPathSegList();
1301 // Note: Only SVGPathElement has d attribute.
1304 "SVGPathElement should have the non-null SVGAnimatedPathSegList");
1305 // The attribute should have been already successfully parsed.
1306 // We want to go through the optimized path to tell the style system
1307 // the result directly, rather than let it parse the same thing again.
1308 mappedAttrParser
.TellStyleAlreadyParsedResult(*path
);
1309 // Some other notes:
1310 // The syntax of CSS d property is different from SVG d attribute.
1311 // 1. CSS d proeprty accepts: none | path(<quoted string>);
1312 // 2. SVG d attribtue accepts: none | <string>
1313 // So we cannot use css parser to parse the SVG d attribute directly.
1314 // Besides, |mAttrs.AttrAt(i)| removes the quotes already, so the svg path
1315 // in |mAttrs.AttrAt(i)| would be something like `M0,0L1,1z` without the
1316 // quotes. So css tokenizer cannot recognize this as a quoted string, and
1317 // so svg_path::SVGPathData::parse() doesn't work for this. Fortunately,
1318 // we still can rely on the parsed result from
1319 // SVGElement::ParseAttribute() for d attribute.
1324 info
.mValue
->ToString(value
);
1325 mappedAttrParser
.ParseMappedAttrValue(attrName
->Atom(), value
);
1327 mContentDeclarationBlock
= mappedAttrParser
.TakeDeclarationBlock();
1330 const DeclarationBlock
* SVGElement::GetContentDeclarationBlock() const {
1331 return mContentDeclarationBlock
;
1335 * Helper methods for the type-specific WillChangeXXX methods.
1337 * This method sends out appropriate pre-change notifications so that selector
1338 * restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop
1339 * matching) work, and it returns an nsAttrValue that _may_ contain the
1340 * attribute's pre-change value.
1342 * The nsAttrValue returned by this method depends on whether there are
1343 * mutation event listeners listening for changes to this element's attributes.
1344 * If not, then the object returned is empty. If there are, then the
1345 * nsAttrValue returned contains a serialized copy of the attribute's value
1346 * prior to the change, and this object should be passed to the corresponding
1347 * DidChangeXXX method call (assuming a WillChangeXXX call is required for the
1348 * SVG type - see comment below). This is necessary so that the 'prevValue'
1349 * property of the mutation event that is dispatched will correctly contain the
1352 * The reason we need to serialize the old value if there are mutation
1353 * event listeners is because the underlying nsAttrValue for the attribute
1354 * points directly to a parsed representation of the attribute (e.g. an
1355 * SVGAnimatedLengthList*) that is a member of the SVG element. That object
1356 * will have changed by the time DidChangeXXX has been called, so without the
1357 * serialization of the old attribute value that we provide, DidChangeXXX
1358 * would have no way to get the old value to pass to SetAttrAndNotify.
1360 * We only return the old value when there are mutation event listeners because
1361 * it's not needed otherwise, and because it's expensive to serialize the old
1362 * value. This is especially true for list type attributes, which may be built
1363 * up via the SVG DOM resulting in a large number of Will/DidModifyXXX calls
1364 * before the script finally finishes setting the attribute.
1366 * Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check
1367 * and filter out redundant changes. Before calling WillChangeXXX, the caller
1368 * should check whether the new and old values are actually the same, and skip
1369 * calling Will/DidChangeXXX if they are.
1371 * Also note that not all SVG types use this scheme. For types that can be
1372 * represented by an nsAttrValue without pointing back to an SVG object (e.g.
1373 * enums, booleans, integers) we can simply use SetParsedAttr which will do all
1374 * of the above for us. For such types there is no matching WillChangeXXX
1375 * method, only DidChangeXXX which calls SetParsedAttr.
1377 nsAttrValue
SVGElement::WillChangeValue(
1378 nsAtom
* aName
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1379 // We need an empty attr value:
1380 // a) to pass to BeforeSetAttr when GetParsedAttr returns nullptr
1381 // b) to store the old value in the case we have mutation listeners
1383 // We can use the same value for both purposes, because if GetParsedAttr
1384 // returns non-null its return value is what will get passed to BeforeSetAttr,
1385 // not matter what our mutation listener situation is.
1387 // Also, we should be careful to always return this value to benefit from
1388 // return value optimization.
1389 nsAttrValue emptyOrOldAttrValue
;
1390 const nsAttrValue
* attrValue
= GetParsedAttr(aName
);
1392 // We only need to set the old value if we have listeners since otherwise it
1394 if (attrValue
&& nsContentUtils::HasMutationListeners(
1395 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this)) {
1396 emptyOrOldAttrValue
.SetToSerialized(*attrValue
);
1400 attrValue
? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION
)
1401 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION
);
1402 MutationObservers::NotifyAttributeWillChange(this, kNameSpaceID_None
, aName
,
1405 // This is not strictly correct--the attribute value parameter for
1406 // BeforeSetAttr should reflect the value that *will* be set but that implies
1407 // allocating, e.g. an extra SVGAnimatedLength, and isn't necessary at the
1408 // moment since no SVG elements overload BeforeSetAttr. For now we just pass
1409 // the current value.
1410 nsAttrValueOrString
attrStringOrValue(attrValue
? *attrValue
1411 : emptyOrOldAttrValue
);
1412 DebugOnly
<nsresult
> rv
= BeforeSetAttr(
1413 kNameSpaceID_None
, aName
, &attrStringOrValue
, kNotifyDocumentObservers
);
1414 // SVG elements aren't expected to overload BeforeSetAttr in such a way that
1415 // it may fail. So long as this is the case we don't need to check and pass on
1416 // the return value which simplifies the calling code significantly.
1417 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Unexpected failure from BeforeSetAttr");
1419 return emptyOrOldAttrValue
;
1423 * Helper methods for the type-specific DidChangeXXX methods.
1425 * aEmptyOrOldValue will normally be the object returned from the corresponding
1426 * WillChangeXXX call. This is because:
1427 * a) WillChangeXXX will ensure the object is set when we have mutation
1429 * b) WillChangeXXX will ensure the object represents a serialized version of
1430 * the old attribute value so that the value doesn't change when the
1431 * underlying SVG type is updated.
1433 * aNewValue is replaced with the old value.
1435 void SVGElement::DidChangeValue(nsAtom
* aName
,
1436 const nsAttrValue
& aEmptyOrOldValue
,
1437 nsAttrValue
& aNewValue
,
1438 const mozAutoDocUpdate
& aProofOfUpdate
) {
1439 bool hasListeners
= nsContentUtils::HasMutationListeners(
1440 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this);
1442 HasAttr(kNameSpaceID_None
, aName
)
1443 ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION
)
1444 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION
);
1446 // XXX Really, the fourth argument to SetAttrAndNotify should be null if
1447 // aEmptyOrOldValue does not represent the actual previous value of the
1448 // attribute, but currently SVG elements do not even use the old attribute
1449 // value in |AfterSetAttr|, so this should be ok.
1450 SetAttrAndNotify(kNameSpaceID_None
, aName
, nullptr, &aEmptyOrOldValue
,
1451 aNewValue
, nullptr, modType
, hasListeners
,
1452 kNotifyDocumentObservers
, kCallAfterSetAttr
,
1453 GetComposedDoc(), aProofOfUpdate
);
1456 void SVGElement::MaybeSerializeAttrBeforeRemoval(nsAtom
* aName
, bool aNotify
) {
1457 if (!aNotify
|| !nsContentUtils::HasMutationListeners(
1458 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this)) {
1462 const nsAttrValue
* attrValue
= mAttrs
.GetAttr(aName
);
1463 if (!attrValue
) return;
1465 nsAutoString serializedValue
;
1466 attrValue
->ToString(serializedValue
);
1467 nsAttrValue
oldAttrValue(serializedValue
);
1469 mAttrs
.SetAndSwapAttr(aName
, oldAttrValue
, &oldValueSet
);
1472 nsAtom
* SVGElement::GetEventNameForAttr(nsAtom
* aAttr
) {
1473 if (IsSVGElement(nsGkAtoms::svg
)) {
1474 if (aAttr
== nsGkAtoms::onload
) return nsGkAtoms::onSVGLoad
;
1475 if (aAttr
== nsGkAtoms::onscroll
) return nsGkAtoms::onSVGScroll
;
1477 if (aAttr
== nsGkAtoms::onbegin
) return nsGkAtoms::onbeginEvent
;
1478 if (aAttr
== nsGkAtoms::onrepeat
) return nsGkAtoms::onrepeatEvent
;
1479 if (aAttr
== nsGkAtoms::onend
) return nsGkAtoms::onendEvent
;
1481 return SVGElementBase::GetEventNameForAttr(aAttr
);
1484 SVGViewportElement
* SVGElement::GetCtx() const {
1485 return SVGContentUtils::GetNearestViewportElement(this);
1489 gfxMatrix
SVGElement::PrependLocalTransformsTo(const gfxMatrix
& aMatrix
,
1490 SVGTransformTypes aWhich
) const {
1494 SVGElement::LengthAttributesInfo
SVGElement::GetLengthInfo() {
1495 return LengthAttributesInfo(nullptr, nullptr, 0);
1498 void SVGElement::SetLength(nsAtom
* aName
, const SVGAnimatedLength
& aLength
) {
1499 LengthAttributesInfo lengthInfo
= GetLengthInfo();
1501 for (uint32_t i
= 0; i
< lengthInfo
.mCount
; i
++) {
1502 if (aName
== lengthInfo
.mInfos
[i
].mName
) {
1503 lengthInfo
.mValues
[i
] = aLength
;
1504 DidAnimateLength(i
);
1508 MOZ_ASSERT(false, "no length found to set");
1511 nsAttrValue
SVGElement::WillChangeLength(
1512 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1513 return WillChangeValue(GetLengthInfo().mInfos
[aAttrEnum
].mName
,
1517 void SVGElement::DidChangeLength(uint8_t aAttrEnum
,
1518 const nsAttrValue
& aEmptyOrOldValue
,
1519 const mozAutoDocUpdate
& aProofOfUpdate
) {
1520 LengthAttributesInfo info
= GetLengthInfo();
1522 NS_ASSERTION(info
.mCount
> 0,
1523 "DidChangeLength on element with no length attribs");
1524 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1526 nsAttrValue newValue
;
1527 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
1529 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1533 void SVGElement::DidAnimateLength(uint8_t aAttrEnum
) {
1534 // We need to do this here. Normally the SMIL restyle would also cause us to
1535 // do this from DidSetComputedStyle, but we don't have that guarantee if our
1536 // frame gets reconstructed.
1537 ClearAnyCachedPath();
1539 if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) {
1540 nsCSSPropertyID propId
=
1541 SVGGeometryProperty::AttrEnumToCSSPropId(this, aAttrEnum
);
1543 // We don't map use element width/height currently. We can remove this
1545 if (propId
!= eCSSProperty_UNKNOWN
) {
1546 SMILOverrideStyle()->SetSMILValue(propId
,
1547 GetLengthInfo().mValues
[aAttrEnum
]);
1552 nsIFrame
* frame
= GetPrimaryFrame();
1555 LengthAttributesInfo info
= GetLengthInfo();
1556 frame
->AttributeChanged(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
,
1557 MutationEvent_Binding::SMIL
);
1561 SVGAnimatedLength
* SVGElement::GetAnimatedLength(uint8_t aAttrEnum
) {
1562 LengthAttributesInfo info
= GetLengthInfo();
1563 if (aAttrEnum
< info
.mCount
) {
1564 return &info
.mValues
[aAttrEnum
];
1566 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1570 SVGAnimatedLength
* SVGElement::GetAnimatedLength(const nsAtom
* aAttrName
) {
1571 LengthAttributesInfo lengthInfo
= GetLengthInfo();
1573 for (uint32_t i
= 0; i
< lengthInfo
.mCount
; i
++) {
1574 if (aAttrName
== lengthInfo
.mInfos
[i
].mName
) {
1575 return &lengthInfo
.mValues
[i
];
1581 void SVGElement::GetAnimatedLengthValues(float* aFirst
, ...) {
1582 LengthAttributesInfo info
= GetLengthInfo();
1584 NS_ASSERTION(info
.mCount
> 0,
1585 "GetAnimatedLengthValues on element with no length attribs");
1587 SVGViewportElement
* ctx
= nullptr;
1593 va_start(args
, aFirst
);
1595 while (f
&& i
< info
.mCount
) {
1596 uint8_t type
= info
.mValues
[i
].GetSpecifiedUnitType();
1598 if (type
!= SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
&&
1599 type
!= SVGLength_Binding::SVG_LENGTHTYPE_PX
)
1602 if (type
== SVGLength_Binding::SVG_LENGTHTYPE_EMS
||
1603 type
== SVGLength_Binding::SVG_LENGTHTYPE_EXS
)
1604 *f
= info
.mValues
[i
++].GetAnimValue(this);
1606 *f
= info
.mValues
[i
++].GetAnimValue(ctx
);
1607 f
= va_arg(args
, float*);
1613 SVGElement::LengthListAttributesInfo
SVGElement::GetLengthListInfo() {
1614 return LengthListAttributesInfo(nullptr, nullptr, 0);
1617 nsAttrValue
SVGElement::WillChangeLengthList(
1618 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1619 return WillChangeValue(GetLengthListInfo().mInfos
[aAttrEnum
].mName
,
1623 void SVGElement::DidChangeLengthList(uint8_t aAttrEnum
,
1624 const nsAttrValue
& aEmptyOrOldValue
,
1625 const mozAutoDocUpdate
& aProofOfUpdate
) {
1626 LengthListAttributesInfo info
= GetLengthListInfo();
1628 NS_ASSERTION(info
.mCount
> 0,
1629 "DidChangeLengthList on element with no length list attribs");
1630 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1632 nsAttrValue newValue
;
1633 newValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1635 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1639 void SVGElement::DidAnimateLengthList(uint8_t aAttrEnum
) {
1640 nsIFrame
* frame
= GetPrimaryFrame();
1643 LengthListAttributesInfo info
= GetLengthListInfo();
1644 frame
->AttributeChanged(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
,
1645 MutationEvent_Binding::SMIL
);
1649 void SVGElement::GetAnimatedLengthListValues(SVGUserUnitList
* aFirst
, ...) {
1650 LengthListAttributesInfo info
= GetLengthListInfo();
1654 "GetAnimatedLengthListValues on element with no length list attribs");
1656 SVGUserUnitList
* list
= aFirst
;
1660 va_start(args
, aFirst
);
1662 while (list
&& i
< info
.mCount
) {
1663 list
->Init(&(info
.mValues
[i
].GetAnimValue()), this, info
.mInfos
[i
].mAxis
);
1665 list
= va_arg(args
, SVGUserUnitList
*);
1671 SVGAnimatedLengthList
* SVGElement::GetAnimatedLengthList(uint8_t aAttrEnum
) {
1672 LengthListAttributesInfo info
= GetLengthListInfo();
1673 if (aAttrEnum
< info
.mCount
) {
1674 return &(info
.mValues
[aAttrEnum
]);
1676 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1680 SVGElement::NumberListAttributesInfo
SVGElement::GetNumberListInfo() {
1681 return NumberListAttributesInfo(nullptr, nullptr, 0);
1684 nsAttrValue
SVGElement::WillChangeNumberList(
1685 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1686 return WillChangeValue(GetNumberListInfo().mInfos
[aAttrEnum
].mName
,
1690 void SVGElement::DidChangeNumberList(uint8_t aAttrEnum
,
1691 const nsAttrValue
& aEmptyOrOldValue
,
1692 const mozAutoDocUpdate
& aProofOfUpdate
) {
1693 NumberListAttributesInfo info
= GetNumberListInfo();
1695 MOZ_ASSERT(info
.mCount
> 0,
1696 "DidChangeNumberList on element with no number list attribs");
1697 MOZ_ASSERT(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1699 nsAttrValue newValue
;
1700 newValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1702 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1706 void SVGElement::DidAnimateNumberList(uint8_t aAttrEnum
) {
1707 nsIFrame
* frame
= GetPrimaryFrame();
1710 NumberListAttributesInfo info
= GetNumberListInfo();
1711 MOZ_ASSERT(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1713 frame
->AttributeChanged(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
,
1714 MutationEvent_Binding::SMIL
);
1718 SVGAnimatedNumberList
* SVGElement::GetAnimatedNumberList(uint8_t aAttrEnum
) {
1719 NumberListAttributesInfo info
= GetNumberListInfo();
1720 if (aAttrEnum
< info
.mCount
) {
1721 return &(info
.mValues
[aAttrEnum
]);
1723 MOZ_ASSERT(false, "Bad attrEnum");
1727 SVGAnimatedNumberList
* SVGElement::GetAnimatedNumberList(nsAtom
* aAttrName
) {
1728 NumberListAttributesInfo info
= GetNumberListInfo();
1729 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
1730 if (aAttrName
== info
.mInfos
[i
].mName
) {
1731 return &info
.mValues
[i
];
1734 MOZ_ASSERT(false, "Bad caller");
1738 nsAttrValue
SVGElement::WillChangePointList(
1739 const mozAutoDocUpdate
& aProofOfUpdate
) {
1740 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1741 return WillChangeValue(GetPointListAttrName(), aProofOfUpdate
);
1744 void SVGElement::DidChangePointList(const nsAttrValue
& aEmptyOrOldValue
,
1745 const mozAutoDocUpdate
& aProofOfUpdate
) {
1746 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1748 nsAttrValue newValue
;
1749 newValue
.SetTo(GetAnimatedPointList()->GetBaseValue(), nullptr);
1751 DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue
, newValue
,
1755 void SVGElement::DidAnimatePointList() {
1756 MOZ_ASSERT(GetPointListAttrName(), "Animating non-existent path data?");
1758 ClearAnyCachedPath();
1760 nsIFrame
* frame
= GetPrimaryFrame();
1763 frame
->AttributeChanged(kNameSpaceID_None
, GetPointListAttrName(),
1764 MutationEvent_Binding::SMIL
);
1768 nsAttrValue
SVGElement::WillChangePathSegList(
1769 const mozAutoDocUpdate
& aProofOfUpdate
) {
1770 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1771 return WillChangeValue(GetPathDataAttrName(), aProofOfUpdate
);
1774 void SVGElement::DidChangePathSegList(const nsAttrValue
& aEmptyOrOldValue
,
1775 const mozAutoDocUpdate
& aProofOfUpdate
) {
1776 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1778 nsAttrValue newValue
;
1779 newValue
.SetTo(GetAnimPathSegList()->GetBaseValue(), nullptr);
1781 DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue
, newValue
,
1785 void SVGElement::DidAnimatePathSegList() {
1786 nsStaticAtom
* name
= GetPathDataAttrName();
1787 MOZ_ASSERT(name
, "Animating non-existent path data?");
1789 ClearAnyCachedPath();
1791 // Notify style we have to update the d property because of SMIL animation.
1792 if (StaticPrefs::layout_css_d_property_enabled() && name
== nsGkAtoms::d
) {
1793 SMILOverrideStyle()->SetSMILValue(nsCSSPropertyID::eCSSProperty_d
,
1794 *GetAnimPathSegList());
1798 if (nsIFrame
* frame
= GetPrimaryFrame()) {
1799 frame
->AttributeChanged(kNameSpaceID_None
, name
,
1800 MutationEvent_Binding::SMIL
);
1804 SVGElement::NumberAttributesInfo
SVGElement::GetNumberInfo() {
1805 return NumberAttributesInfo(nullptr, nullptr, 0);
1808 void SVGElement::DidChangeNumber(uint8_t aAttrEnum
) {
1809 NumberAttributesInfo info
= GetNumberInfo();
1811 NS_ASSERTION(info
.mCount
> 0,
1812 "DidChangeNumber on element with no number attribs");
1813 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1815 nsAttrValue attrValue
;
1816 attrValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1818 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
1822 void SVGElement::DidAnimateNumber(uint8_t aAttrEnum
) {
1823 nsIFrame
* frame
= GetPrimaryFrame();
1826 NumberAttributesInfo info
= GetNumberInfo();
1827 frame
->AttributeChanged(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
,
1828 MutationEvent_Binding::SMIL
);
1832 void SVGElement::GetAnimatedNumberValues(float* aFirst
, ...) {
1833 NumberAttributesInfo info
= GetNumberInfo();
1835 NS_ASSERTION(info
.mCount
> 0,
1836 "GetAnimatedNumberValues on element with no number attribs");
1842 va_start(args
, aFirst
);
1844 while (f
&& i
< info
.mCount
) {
1845 *f
= info
.mValues
[i
++].GetAnimValue();
1846 f
= va_arg(args
, float*);
1851 SVGElement::NumberPairAttributesInfo
SVGElement::GetNumberPairInfo() {
1852 return NumberPairAttributesInfo(nullptr, nullptr, 0);
1855 nsAttrValue
SVGElement::WillChangeNumberPair(uint8_t aAttrEnum
) {
1856 mozAutoDocUpdate
updateBatch(GetComposedDoc(), kDontNotifyDocumentObservers
);
1857 return WillChangeValue(GetNumberPairInfo().mInfos
[aAttrEnum
].mName
,
1861 void SVGElement::DidChangeNumberPair(uint8_t aAttrEnum
,
1862 const nsAttrValue
& aEmptyOrOldValue
) {
1863 NumberPairAttributesInfo info
= GetNumberPairInfo();
1865 NS_ASSERTION(info
.mCount
> 0,
1866 "DidChangePairNumber on element with no number pair attribs");
1867 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1869 nsAttrValue newValue
;
1870 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
1872 mozAutoDocUpdate
updateBatch(GetComposedDoc(), kNotifyDocumentObservers
);
1873 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1877 void SVGElement::DidAnimateNumberPair(uint8_t aAttrEnum
) {
1878 nsIFrame
* frame
= GetPrimaryFrame();
1881 NumberPairAttributesInfo info
= GetNumberPairInfo();
1882 frame
->AttributeChanged(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
,
1883 MutationEvent_Binding::SMIL
);
1887 SVGElement::IntegerAttributesInfo
SVGElement::GetIntegerInfo() {
1888 return IntegerAttributesInfo(nullptr, nullptr, 0);
1891 void SVGElement::DidChangeInteger(uint8_t aAttrEnum
) {
1892 IntegerAttributesInfo info
= GetIntegerInfo();
1893 NS_ASSERTION(info
.mCount
> 0,
1894 "DidChangeInteger on element with no integer attribs");
1895 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1897 nsAttrValue attrValue
;
1898 attrValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1900 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
1904 void SVGElement::DidAnimateInteger(uint8_t aAttrEnum
) {
1905 nsIFrame
* frame
= GetPrimaryFrame();
1908 IntegerAttributesInfo info
= GetIntegerInfo();
1909 frame
->AttributeChanged(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
,
1910 MutationEvent_Binding::SMIL
);
1914 void SVGElement::GetAnimatedIntegerValues(int32_t* aFirst
, ...) {
1915 IntegerAttributesInfo info
= GetIntegerInfo();
1917 NS_ASSERTION(info
.mCount
> 0,
1918 "GetAnimatedIntegerValues on element with no integer attribs");
1920 int32_t* n
= aFirst
;
1924 va_start(args
, aFirst
);
1926 while (n
&& i
< info
.mCount
) {
1927 *n
= info
.mValues
[i
++].GetAnimValue();
1928 n
= va_arg(args
, int32_t*);
1933 SVGElement::IntegerPairAttributesInfo
SVGElement::GetIntegerPairInfo() {
1934 return IntegerPairAttributesInfo(nullptr, nullptr, 0);
1937 nsAttrValue
SVGElement::WillChangeIntegerPair(
1938 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1939 return WillChangeValue(GetIntegerPairInfo().mInfos
[aAttrEnum
].mName
,
1943 void SVGElement::DidChangeIntegerPair(uint8_t aAttrEnum
,
1944 const nsAttrValue
& aEmptyOrOldValue
,
1945 const mozAutoDocUpdate
& aProofOfUpdate
) {
1946 IntegerPairAttributesInfo info
= GetIntegerPairInfo();
1948 NS_ASSERTION(info
.mCount
> 0,
1949 "DidChangeIntegerPair on element with no integer pair attribs");
1950 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1952 nsAttrValue newValue
;
1953 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
1955 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1959 void SVGElement::DidAnimateIntegerPair(uint8_t aAttrEnum
) {
1960 nsIFrame
* frame
= GetPrimaryFrame();
1963 IntegerPairAttributesInfo info
= GetIntegerPairInfo();
1964 frame
->AttributeChanged(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
,
1965 MutationEvent_Binding::SMIL
);
1969 SVGElement::BooleanAttributesInfo
SVGElement::GetBooleanInfo() {
1970 return BooleanAttributesInfo(nullptr, nullptr, 0);
1973 void SVGElement::DidChangeBoolean(uint8_t aAttrEnum
) {
1974 BooleanAttributesInfo info
= GetBooleanInfo();
1976 NS_ASSERTION(info
.mCount
> 0,
1977 "DidChangeBoolean on element with no boolean attribs");
1978 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1980 nsAttrValue
attrValue(info
.mValues
[aAttrEnum
].GetBaseValueAtom());
1981 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
1985 void SVGElement::DidAnimateBoolean(uint8_t aAttrEnum
) {
1986 nsIFrame
* frame
= GetPrimaryFrame();
1989 BooleanAttributesInfo info
= GetBooleanInfo();
1990 frame
->AttributeChanged(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
,
1991 MutationEvent_Binding::SMIL
);
1995 SVGElement::EnumAttributesInfo
SVGElement::GetEnumInfo() {
1996 return EnumAttributesInfo(nullptr, nullptr, 0);
1999 void SVGElement::DidChangeEnum(uint8_t aAttrEnum
) {
2000 EnumAttributesInfo info
= GetEnumInfo();
2002 NS_ASSERTION(info
.mCount
> 0,
2003 "DidChangeEnum on element with no enum attribs");
2004 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
2006 nsAttrValue
attrValue(info
.mValues
[aAttrEnum
].GetBaseValueAtom(this));
2007 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
2011 void SVGElement::DidAnimateEnum(uint8_t aAttrEnum
) {
2012 nsIFrame
* frame
= GetPrimaryFrame();
2015 EnumAttributesInfo info
= GetEnumInfo();
2016 frame
->AttributeChanged(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
,
2017 MutationEvent_Binding::SMIL
);
2021 SVGAnimatedOrient
* SVGElement::GetAnimatedOrient() { return nullptr; }
2023 nsAttrValue
SVGElement::WillChangeOrient(
2024 const mozAutoDocUpdate
& aProofOfUpdate
) {
2025 return WillChangeValue(nsGkAtoms::orient
, aProofOfUpdate
);
2028 void SVGElement::DidChangeOrient(const nsAttrValue
& aEmptyOrOldValue
,
2029 const mozAutoDocUpdate
& aProofOfUpdate
) {
2030 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
2032 NS_ASSERTION(orient
, "DidChangeOrient on element with no orient attrib");
2034 nsAttrValue newValue
;
2035 newValue
.SetTo(*orient
, nullptr);
2037 DidChangeValue(nsGkAtoms::orient
, aEmptyOrOldValue
, newValue
, aProofOfUpdate
);
2040 void SVGElement::DidAnimateOrient() {
2041 nsIFrame
* frame
= GetPrimaryFrame();
2044 frame
->AttributeChanged(kNameSpaceID_None
, nsGkAtoms::orient
,
2045 MutationEvent_Binding::SMIL
);
2049 SVGAnimatedViewBox
* SVGElement::GetAnimatedViewBox() { return nullptr; }
2051 nsAttrValue
SVGElement::WillChangeViewBox(
2052 const mozAutoDocUpdate
& aProofOfUpdate
) {
2053 return WillChangeValue(nsGkAtoms::viewBox
, aProofOfUpdate
);
2056 void SVGElement::DidChangeViewBox(const nsAttrValue
& aEmptyOrOldValue
,
2057 const mozAutoDocUpdate
& aProofOfUpdate
) {
2058 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
2060 NS_ASSERTION(viewBox
, "DidChangeViewBox on element with no viewBox attrib");
2062 nsAttrValue newValue
;
2063 newValue
.SetTo(*viewBox
, nullptr);
2065 DidChangeValue(nsGkAtoms::viewBox
, aEmptyOrOldValue
, newValue
,
2069 void SVGElement::DidAnimateViewBox() {
2070 nsIFrame
* frame
= GetPrimaryFrame();
2073 frame
->AttributeChanged(kNameSpaceID_None
, nsGkAtoms::viewBox
,
2074 MutationEvent_Binding::SMIL
);
2078 SVGAnimatedPreserveAspectRatio
* SVGElement::GetAnimatedPreserveAspectRatio() {
2082 nsAttrValue
SVGElement::WillChangePreserveAspectRatio(
2083 const mozAutoDocUpdate
& aProofOfUpdate
) {
2084 return WillChangeValue(nsGkAtoms::preserveAspectRatio
, aProofOfUpdate
);
2087 void SVGElement::DidChangePreserveAspectRatio(
2088 const nsAttrValue
& aEmptyOrOldValue
,
2089 const mozAutoDocUpdate
& aProofOfUpdate
) {
2090 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
2091 GetAnimatedPreserveAspectRatio();
2093 NS_ASSERTION(preserveAspectRatio
,
2094 "DidChangePreserveAspectRatio on element with no "
2095 "preserveAspectRatio attrib");
2097 nsAttrValue newValue
;
2098 newValue
.SetTo(*preserveAspectRatio
, nullptr);
2100 DidChangeValue(nsGkAtoms::preserveAspectRatio
, aEmptyOrOldValue
, newValue
,
2104 void SVGElement::DidAnimatePreserveAspectRatio() {
2105 nsIFrame
* frame
= GetPrimaryFrame();
2108 frame
->AttributeChanged(kNameSpaceID_None
, nsGkAtoms::preserveAspectRatio
,
2109 MutationEvent_Binding::SMIL
);
2113 nsAttrValue
SVGElement::WillChangeTransformList(
2114 const mozAutoDocUpdate
& aProofOfUpdate
) {
2115 return WillChangeValue(GetTransformListAttrName(), aProofOfUpdate
);
2118 void SVGElement::DidChangeTransformList(
2119 const nsAttrValue
& aEmptyOrOldValue
,
2120 const mozAutoDocUpdate
& aProofOfUpdate
) {
2121 MOZ_ASSERT(GetTransformListAttrName(),
2122 "Changing non-existent transform list?");
2124 // The transform attribute is being set, so we must ensure that the
2125 // SVGAnimatedTransformList is/has been allocated:
2126 nsAttrValue newValue
;
2127 newValue
.SetTo(GetAnimatedTransformList(DO_ALLOCATE
)->GetBaseValue(),
2130 DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue
, newValue
,
2134 void SVGElement::DidAnimateTransformList(int32_t aModType
) {
2135 MOZ_ASSERT(GetTransformListAttrName(),
2136 "Animating non-existent transform data?");
2138 nsIFrame
* frame
= GetPrimaryFrame();
2141 nsAtom
* transformAttr
= GetTransformListAttrName();
2142 frame
->AttributeChanged(kNameSpaceID_None
, transformAttr
, aModType
);
2143 // When script changes the 'transform' attribute, Element::SetAttrAndNotify
2144 // will call MutationObservers::NotifyAttributeChanged, under which
2145 // SVGTransformableElement::GetAttributeChangeHint will be called and an
2146 // appropriate change event posted to update our frame's overflow rects.
2147 // The SetAttrAndNotify doesn't happen for transform changes caused by
2148 // 'animateTransform' though (and sending out the mutation events that
2149 // MutationObservers::NotifyAttributeChanged dispatches would be
2150 // inappropriate anyway), so we need to post the change event ourself.
2151 nsChangeHint changeHint
= GetAttributeChangeHint(transformAttr
, aModType
);
2153 nsLayoutUtils::PostRestyleEvent(this, RestyleHint
{0}, changeHint
);
2158 SVGElement::StringAttributesInfo
SVGElement::GetStringInfo() {
2159 return StringAttributesInfo(nullptr, nullptr, 0);
2162 void SVGElement::GetStringBaseValue(uint8_t aAttrEnum
,
2163 nsAString
& aResult
) const {
2164 SVGElement::StringAttributesInfo info
=
2165 const_cast<SVGElement
*>(this)->GetStringInfo();
2167 NS_ASSERTION(info
.mCount
> 0,
2168 "GetBaseValue on element with no string attribs");
2170 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
2172 GetAttr(info
.mInfos
[aAttrEnum
].mNamespaceID
, info
.mInfos
[aAttrEnum
].mName
,
2176 void SVGElement::SetStringBaseValue(uint8_t aAttrEnum
,
2177 const nsAString
& aValue
) {
2178 SVGElement::StringAttributesInfo info
= GetStringInfo();
2180 NS_ASSERTION(info
.mCount
> 0,
2181 "SetBaseValue on element with no string attribs");
2183 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
2185 SetAttr(info
.mInfos
[aAttrEnum
].mNamespaceID
, info
.mInfos
[aAttrEnum
].mName
,
2189 void SVGElement::DidAnimateString(uint8_t aAttrEnum
) {
2190 nsIFrame
* frame
= GetPrimaryFrame();
2193 StringAttributesInfo info
= GetStringInfo();
2194 frame
->AttributeChanged(info
.mInfos
[aAttrEnum
].mNamespaceID
,
2195 info
.mInfos
[aAttrEnum
].mName
,
2196 MutationEvent_Binding::SMIL
);
2200 SVGElement::StringListAttributesInfo
SVGElement::GetStringListInfo() {
2201 return StringListAttributesInfo(nullptr, nullptr, 0);
2204 nsAttrValue
SVGElement::WillChangeStringList(
2205 bool aIsConditionalProcessingAttribute
, uint8_t aAttrEnum
,
2206 const mozAutoDocUpdate
& aProofOfUpdate
) {
2208 if (aIsConditionalProcessingAttribute
) {
2209 nsCOMPtr
<SVGTests
> tests(do_QueryInterface(this));
2210 name
= tests
->GetAttrName(aAttrEnum
);
2212 name
= GetStringListInfo().mInfos
[aAttrEnum
].mName
;
2214 return WillChangeValue(name
, aProofOfUpdate
);
2217 void SVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute
,
2219 const nsAttrValue
& aEmptyOrOldValue
,
2220 const mozAutoDocUpdate
& aProofOfUpdate
) {
2222 nsAttrValue newValue
;
2223 nsCOMPtr
<SVGTests
> tests
;
2225 if (aIsConditionalProcessingAttribute
) {
2226 tests
= do_QueryObject(this);
2227 name
= tests
->GetAttrName(aAttrEnum
);
2228 tests
->GetAttrValue(aAttrEnum
, newValue
);
2230 StringListAttributesInfo info
= GetStringListInfo();
2232 NS_ASSERTION(info
.mCount
> 0,
2233 "DidChangeStringList on element with no string list attribs");
2234 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
2236 name
= info
.mInfos
[aAttrEnum
].mName
;
2237 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
2240 DidChangeValue(name
, aEmptyOrOldValue
, newValue
, aProofOfUpdate
);
2242 if (aIsConditionalProcessingAttribute
) {
2243 tests
->MaybeInvalidate();
2247 nsresult
SVGElement::ReportAttributeParseFailure(Document
* aDocument
,
2249 const nsAString
& aValue
) {
2250 AutoTArray
<nsString
, 2> strings
;
2251 strings
.AppendElement(nsDependentAtomString(aAttribute
));
2252 strings
.AppendElement(aValue
);
2253 return SVGContentUtils::ReportToConsole(aDocument
, "AttributeParseWarning",
2257 UniquePtr
<SMILAttr
> SVGElement::GetAnimatedAttr(int32_t aNamespaceID
,
2259 if (aNamespaceID
== kNameSpaceID_None
) {
2261 if (GetTransformListAttrName() == aName
) {
2262 // The transform attribute is being animated, so we must ensure that the
2263 // SVGAnimatedTransformList is/has been allocated:
2264 return GetAnimatedTransformList(DO_ALLOCATE
)->ToSMILAttr(this);
2267 // Motion (fake 'attribute' for animateMotion)
2268 if (aName
== nsGkAtoms::mozAnimateMotionDummyAttr
) {
2269 return MakeUnique
<SVGMotionSMILAttr
>(this);
2273 LengthAttributesInfo info
= GetLengthInfo();
2274 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2275 if (aName
== info
.mInfos
[i
].mName
) {
2276 return info
.mValues
[i
].ToSMILAttr(this);
2282 NumberAttributesInfo info
= GetNumberInfo();
2283 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2284 if (aName
== info
.mInfos
[i
].mName
) {
2285 return info
.mValues
[i
].ToSMILAttr(this);
2292 NumberPairAttributesInfo info
= GetNumberPairInfo();
2293 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2294 if (aName
== info
.mInfos
[i
].mName
) {
2295 return info
.mValues
[i
].ToSMILAttr(this);
2302 IntegerAttributesInfo info
= GetIntegerInfo();
2303 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2304 if (aName
== info
.mInfos
[i
].mName
) {
2305 return info
.mValues
[i
].ToSMILAttr(this);
2312 IntegerPairAttributesInfo info
= GetIntegerPairInfo();
2313 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2314 if (aName
== info
.mInfos
[i
].mName
) {
2315 return info
.mValues
[i
].ToSMILAttr(this);
2322 EnumAttributesInfo info
= GetEnumInfo();
2323 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2324 if (aName
== info
.mInfos
[i
].mName
) {
2325 return info
.mValues
[i
].ToSMILAttr(this);
2332 BooleanAttributesInfo info
= GetBooleanInfo();
2333 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2334 if (aName
== info
.mInfos
[i
].mName
) {
2335 return info
.mValues
[i
].ToSMILAttr(this);
2341 if (aName
== nsGkAtoms::orient
) {
2342 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
2343 return orient
? orient
->ToSMILAttr(this) : nullptr;
2347 if (aName
== nsGkAtoms::viewBox
) {
2348 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
2349 return viewBox
? viewBox
->ToSMILAttr(this) : nullptr;
2352 // preserveAspectRatio:
2353 if (aName
== nsGkAtoms::preserveAspectRatio
) {
2354 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
2355 GetAnimatedPreserveAspectRatio();
2356 return preserveAspectRatio
? preserveAspectRatio
->ToSMILAttr(this)
2362 NumberListAttributesInfo info
= GetNumberListInfo();
2363 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2364 if (aName
== info
.mInfos
[i
].mName
) {
2365 MOZ_ASSERT(i
<= UCHAR_MAX
, "Too many attributes");
2366 return info
.mValues
[i
].ToSMILAttr(this, uint8_t(i
));
2373 LengthListAttributesInfo info
= GetLengthListInfo();
2374 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2375 if (aName
== info
.mInfos
[i
].mName
) {
2376 MOZ_ASSERT(i
<= UCHAR_MAX
, "Too many attributes");
2377 return info
.mValues
[i
].ToSMILAttr(this, uint8_t(i
),
2378 info
.mInfos
[i
].mAxis
,
2379 info
.mInfos
[i
].mCouldZeroPadList
);
2386 if (GetPointListAttrName() == aName
) {
2387 SVGAnimatedPointList
* pointList
= GetAnimatedPointList();
2389 return pointList
->ToSMILAttr(this);
2396 if (GetPathDataAttrName() == aName
) {
2397 SVGAnimatedPathSegList
* segList
= GetAnimPathSegList();
2399 return segList
->ToSMILAttr(this);
2404 if (aName
== nsGkAtoms::_class
) {
2405 return mClassAttribute
.ToSMILAttr(this);
2411 StringAttributesInfo info
= GetStringInfo();
2412 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2413 if (aNamespaceID
== info
.mInfos
[i
].mNamespaceID
&&
2414 aName
== info
.mInfos
[i
].mName
) {
2415 return info
.mValues
[i
].ToSMILAttr(this);
2423 void SVGElement::AnimationNeedsResample() {
2424 Document
* doc
= GetComposedDoc();
2425 if (doc
&& doc
->HasAnimationController()) {
2426 doc
->GetAnimationController()->SetResampleNeeded();
2430 void SVGElement::FlushAnimations() {
2431 Document
* doc
= GetComposedDoc();
2432 if (doc
&& doc
->HasAnimationController()) {
2433 doc
->GetAnimationController()->FlushResampleRequests();
2437 void SVGElement::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
2438 size_t* aNodeSize
) const {
2439 Element::AddSizeOfExcludingThis(aSizes
, aNodeSize
);
2441 // These are owned by the element and not referenced from the stylesheets.
2442 // They're referenced from the rule tree, but the rule nodes don't measure
2443 // their style source (since they're non-owning), so unconditionally reporting
2444 // them even though it's a refcounted object is ok.
2445 if (mContentDeclarationBlock
) {
2446 aSizes
.mLayoutSvgMappedDeclarations
+=
2447 mContentDeclarationBlock
->SizeofIncludingThis(
2448 aSizes
.mState
.mMallocSizeOf
);
2452 } // namespace mozilla::dom