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 aDest
->OwnerDoc()->CloningForSVGUse()) {
211 LengthAttributesInfo lengthInfo
= GetLengthInfo();
212 dest
->GetLengthInfo().CopyAllFrom(lengthInfo
);
213 if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) {
214 for (uint32_t i
= 0; i
< lengthInfo
.mCount
; i
++) {
215 nsCSSPropertyID propId
=
216 SVGGeometryProperty::AttrEnumToCSSPropId(this, i
);
218 // We don't map use element width/height currently. We can remove this
220 if (propId
!= eCSSProperty_UNKNOWN
&&
221 lengthInfo
.mValues
[i
].IsAnimated()) {
222 dest
->SMILOverrideStyle()->SetSMILValue(propId
,
223 lengthInfo
.mValues
[i
]);
227 dest
->GetNumberInfo().CopyAllFrom(GetNumberInfo());
228 dest
->GetNumberPairInfo().CopyAllFrom(GetNumberPairInfo());
229 dest
->GetIntegerInfo().CopyAllFrom(GetIntegerInfo());
230 dest
->GetIntegerPairInfo().CopyAllFrom(GetIntegerPairInfo());
231 dest
->GetBooleanInfo().CopyAllFrom(GetBooleanInfo());
232 if (const auto* orient
= GetAnimatedOrient()) {
233 *dest
->GetAnimatedOrient() = *orient
;
235 if (const auto* viewBox
= GetAnimatedViewBox()) {
236 *dest
->GetAnimatedViewBox() = *viewBox
;
238 if (const auto* preserveAspectRatio
= GetAnimatedPreserveAspectRatio()) {
239 *dest
->GetAnimatedPreserveAspectRatio() = *preserveAspectRatio
;
241 dest
->GetEnumInfo().CopyAllFrom(GetEnumInfo());
242 dest
->GetStringInfo().CopyAllFrom(GetStringInfo());
243 dest
->GetLengthListInfo().CopyAllFrom(GetLengthListInfo());
244 dest
->GetNumberListInfo().CopyAllFrom(GetNumberListInfo());
245 if (const auto* pointList
= GetAnimatedPointList()) {
246 *dest
->GetAnimatedPointList() = *pointList
;
248 if (const auto* pathSegList
= GetAnimPathSegList()) {
249 *dest
->GetAnimPathSegList() = *pathSegList
;
250 if (pathSegList
->IsAnimating()) {
251 dest
->SMILOverrideStyle()->SetSMILValue(nsCSSPropertyID::eCSSProperty_d
,
255 if (const auto* transformList
= GetAnimatedTransformList()) {
256 *dest
->GetAnimatedTransformList(DO_ALLOCATE
) = *transformList
;
258 if (const auto* animateMotionTransform
= GetAnimateMotionTransform()) {
259 dest
->SetAnimateMotionTransform(animateMotionTransform
);
261 if (const auto* smilOverrideStyleDecoration
=
262 GetSMILOverrideStyleDeclaration()) {
263 RefPtr
<DeclarationBlock
> declClone
= smilOverrideStyleDecoration
->Clone();
264 declClone
->SetDirty();
265 dest
->SetSMILOverrideStyleDeclaration(*declClone
);
272 //----------------------------------------------------------------------
273 // SVGElement methods
275 void SVGElement::DidAnimateClass() {
276 // For Servo, snapshot the element before we change it.
277 PresShell
* presShell
= OwnerDoc()->GetPresShell();
279 if (nsPresContext
* presContext
= presShell
->GetPresContext()) {
280 presContext
->RestyleManager()->ClassAttributeWillBeChangedBySMIL(this);
285 mClassAttribute
.GetAnimValue(src
, this);
286 if (!mClassAnimAttr
) {
287 mClassAnimAttr
= MakeUnique
<nsAttrValue
>();
289 mClassAnimAttr
->ParseAtomArray(src
);
291 // FIXME(emilio): This re-selector-matches, but we do the snapshot stuff right
292 // above... Is this needed anymore?
294 presShell
->RestyleForAnimation(this, RestyleHint::RESTYLE_SELF
);
296 DidAnimateAttribute(kNameSpaceID_None
, nsGkAtoms::_class
);
299 nsresult
SVGElement::Init() {
300 // Set up length attributes - can't do this in the constructor
301 // because we can't do a virtual call at that point
303 GetLengthInfo().ResetAll();
304 GetNumberInfo().ResetAll();
305 GetNumberPairInfo().ResetAll();
306 GetIntegerInfo().ResetAll();
307 GetIntegerPairInfo().ResetAll();
308 GetBooleanInfo().ResetAll();
309 GetEnumInfo().ResetAll();
311 if (SVGAnimatedOrient
* orient
= GetAnimatedOrient()) {
315 if (SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox()) {
319 if (SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
320 GetAnimatedPreserveAspectRatio()) {
321 preserveAspectRatio
->Init();
324 GetLengthListInfo().ResetAll();
325 GetNumberListInfo().ResetAll();
327 // No need to reset SVGPointList since the default value is always the same
330 // No need to reset SVGPathData since the default value is always the same
333 GetStringInfo().ResetAll();
337 //----------------------------------------------------------------------
340 //----------------------------------------------------------------------
341 // nsIContent methods
343 nsresult
SVGElement::BindToTree(BindContext
& aContext
, nsINode
& aParent
) {
344 nsresult rv
= SVGElementBase::BindToTree(aContext
, aParent
);
345 NS_ENSURE_SUCCESS(rv
, rv
);
347 // Hide any nonce from the DOM, but keep the internal value of the
348 // nonce by copying and resetting the internal nonce value.
349 if (HasFlag(NODE_HAS_NONCE_AND_HEADER_CSP
) && IsInComposedDoc() &&
350 OwnerDoc()->GetBrowsingContext()) {
351 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
352 "SVGElement::ResetNonce::Runnable",
353 [self
= RefPtr
<SVGElement
>(this)]() {
355 self
->GetNonce(nonce
);
356 self
->SetAttr(kNameSpaceID_None
, nsGkAtoms::nonce
, u
""_ns
, true);
357 self
->SetNonce(nonce
);
364 void SVGElement::AfterSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
365 const nsAttrValue
* aValue
,
366 const nsAttrValue
* aOldValue
,
367 nsIPrincipal
* aSubjectPrincipal
, bool aNotify
) {
368 if (IsEventAttributeName(aName
) && aValue
) {
369 MOZ_ASSERT(aValue
->Type() == nsAttrValue::eString
,
370 "Expected string value for script body");
371 SetEventHandler(GetEventNameForAttr(aName
), aValue
->GetStringValue());
374 // The nonce will be copied over to an internal slot and cleared from the
375 // Element within BindToTree to avoid CSS Selector nonce exfiltration if
376 // the CSP list contains a header-delivered CSP.
377 if (nsGkAtoms::nonce
== aName
&& kNameSpaceID_None
== aNamespaceID
) {
379 SetNonce(aValue
->GetStringValue());
380 if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) {
381 SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP
);
388 return SVGElementBase::AfterSetAttr(aNamespaceID
, aName
, aValue
, aOldValue
,
389 aSubjectPrincipal
, aNotify
);
392 bool SVGElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
393 const nsAString
& aValue
,
394 nsIPrincipal
* aMaybeScriptedPrincipal
,
395 nsAttrValue
& aResult
) {
397 bool foundMatch
= false;
398 bool didSetResult
= false;
400 if (aNamespaceID
== kNameSpaceID_None
) {
401 // Check for SVGAnimatedLength attribute
402 LengthAttributesInfo lengthInfo
= GetLengthInfo();
405 for (i
= 0; i
< lengthInfo
.mCount
; i
++) {
406 if (aAttribute
== lengthInfo
.mInfos
[i
].mName
) {
407 rv
= lengthInfo
.mValues
[i
].SetBaseValueString(aValue
, this, false);
411 aResult
.SetTo(lengthInfo
.mValues
[i
], &aValue
);
420 // Check for SVGAnimatedLengthList attribute
421 LengthListAttributesInfo lengthListInfo
= GetLengthListInfo();
422 for (i
= 0; i
< lengthListInfo
.mCount
; i
++) {
423 if (aAttribute
== lengthListInfo
.mInfos
[i
].mName
) {
424 rv
= lengthListInfo
.mValues
[i
].SetBaseValueString(aValue
);
426 lengthListInfo
.Reset(i
);
428 aResult
.SetTo(lengthListInfo
.mValues
[i
].GetBaseValue(), &aValue
);
438 // Check for SVGAnimatedNumberList attribute
439 NumberListAttributesInfo numberListInfo
= GetNumberListInfo();
440 for (i
= 0; i
< numberListInfo
.mCount
; i
++) {
441 if (aAttribute
== numberListInfo
.mInfos
[i
].mName
) {
442 rv
= numberListInfo
.mValues
[i
].SetBaseValueString(aValue
);
444 numberListInfo
.Reset(i
);
446 aResult
.SetTo(numberListInfo
.mValues
[i
].GetBaseValue(), &aValue
);
456 // Check for SVGAnimatedPointList attribute
457 if (GetPointListAttrName() == aAttribute
) {
458 if (SVGAnimatedPointList
* pointList
= GetAnimatedPointList()) {
459 pointList
->SetBaseValueString(aValue
);
460 // The spec says we parse everything up to the failure, so we DON'T
461 // need to check the result of SetBaseValueString or call
462 // pointList->ClearBaseValue() if it fails
463 aResult
.SetTo(pointList
->GetBaseValue(), &aValue
);
471 // Check for SVGAnimatedPathSegList attribute
472 if (GetPathDataAttrName() == aAttribute
) {
473 if (SVGAnimatedPathSegList
* segList
= GetAnimPathSegList()) {
474 segList
->SetBaseValueString(aValue
);
475 // The spec says we parse everything up to the failure, so we DON'T
476 // need to check the result of SetBaseValueString or call
477 // segList->ClearBaseValue() if it fails
478 aResult
.SetTo(segList
->GetBaseValue(), &aValue
);
486 // Check for SVGAnimatedNumber attribute
487 NumberAttributesInfo numberInfo
= GetNumberInfo();
488 for (i
= 0; i
< numberInfo
.mCount
; i
++) {
489 if (aAttribute
== numberInfo
.mInfos
[i
].mName
) {
490 rv
= numberInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
494 aResult
.SetTo(numberInfo
.mValues
[i
].GetBaseValue(), &aValue
);
504 // Check for SVGAnimatedNumberPair attribute
505 NumberPairAttributesInfo numberPairInfo
= GetNumberPairInfo();
506 for (i
= 0; i
< numberPairInfo
.mCount
; i
++) {
507 if (aAttribute
== numberPairInfo
.mInfos
[i
].mName
) {
508 rv
= numberPairInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
510 numberPairInfo
.Reset(i
);
512 aResult
.SetTo(numberPairInfo
.mValues
[i
], &aValue
);
522 // Check for SVGAnimatedInteger attribute
523 IntegerAttributesInfo integerInfo
= GetIntegerInfo();
524 for (i
= 0; i
< integerInfo
.mCount
; i
++) {
525 if (aAttribute
== integerInfo
.mInfos
[i
].mName
) {
526 rv
= integerInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
528 integerInfo
.Reset(i
);
530 aResult
.SetTo(integerInfo
.mValues
[i
].GetBaseValue(), &aValue
);
540 // Check for SVGAnimatedIntegerPair attribute
541 IntegerPairAttributesInfo integerPairInfo
= GetIntegerPairInfo();
542 for (i
= 0; i
< integerPairInfo
.mCount
; i
++) {
543 if (aAttribute
== integerPairInfo
.mInfos
[i
].mName
) {
544 rv
= integerPairInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
546 integerPairInfo
.Reset(i
);
548 aResult
.SetTo(integerPairInfo
.mValues
[i
], &aValue
);
558 // Check for SVGAnimatedBoolean attribute
559 BooleanAttributesInfo booleanInfo
= GetBooleanInfo();
560 for (i
= 0; i
< booleanInfo
.mCount
; i
++) {
561 if (aAttribute
== booleanInfo
.mInfos
[i
].mName
) {
562 nsAtom
* valAtom
= NS_GetStaticAtom(aValue
);
563 rv
= valAtom
? booleanInfo
.mValues
[i
].SetBaseValueAtom(valAtom
, this)
564 : NS_ERROR_DOM_SYNTAX_ERR
;
566 booleanInfo
.Reset(i
);
568 aResult
.SetTo(valAtom
);
578 // Check for SVGAnimatedEnumeration attribute
579 EnumAttributesInfo enumInfo
= GetEnumInfo();
580 for (i
= 0; i
< enumInfo
.mCount
; i
++) {
581 if (aAttribute
== enumInfo
.mInfos
[i
].mName
) {
582 RefPtr
<nsAtom
> valAtom
= NS_Atomize(aValue
);
583 if (!enumInfo
.mValues
[i
].SetBaseValueAtom(valAtom
, this)) {
584 // Exact error value does not matter; we just need to mark the
586 rv
= NS_ERROR_FAILURE
;
589 aResult
.SetTo(valAtom
);
599 // Check for conditional processing attributes
600 nsCOMPtr
<SVGTests
> tests
= do_QueryObject(this);
601 if (tests
&& tests
->ParseConditionalProcessingAttribute(
602 aAttribute
, aValue
, aResult
)) {
608 // Check for StringList attribute
609 StringListAttributesInfo stringListInfo
= GetStringListInfo();
610 for (i
= 0; i
< stringListInfo
.mCount
; i
++) {
611 if (aAttribute
== stringListInfo
.mInfos
[i
].mName
) {
612 rv
= stringListInfo
.mValues
[i
].SetValue(aValue
);
614 stringListInfo
.Reset(i
);
616 aResult
.SetTo(stringListInfo
.mValues
[i
], &aValue
);
626 // Check for orient attribute
627 if (aAttribute
== nsGkAtoms::orient
) {
628 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
630 rv
= orient
->SetBaseValueString(aValue
, this, false);
634 aResult
.SetTo(*orient
, &aValue
);
639 // Check for viewBox attribute
640 } else if (aAttribute
== nsGkAtoms::viewBox
) {
641 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
643 rv
= viewBox
->SetBaseValueString(aValue
, this, false);
647 aResult
.SetTo(*viewBox
, &aValue
);
652 // Check for preserveAspectRatio attribute
653 } else if (aAttribute
== nsGkAtoms::preserveAspectRatio
) {
654 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
655 GetAnimatedPreserveAspectRatio();
656 if (preserveAspectRatio
) {
657 rv
= preserveAspectRatio
->SetBaseValueString(aValue
, this, false);
659 preserveAspectRatio
->Init();
661 aResult
.SetTo(*preserveAspectRatio
, &aValue
);
666 // Check for SVGAnimatedTransformList attribute
667 } else if (GetTransformListAttrName() == aAttribute
) {
668 // The transform attribute is being set, so we must ensure that the
669 // SVGAnimatedTransformList is/has been allocated:
670 SVGAnimatedTransformList
* transformList
=
671 GetAnimatedTransformList(DO_ALLOCATE
);
672 rv
= transformList
->SetBaseValueString(aValue
, this);
674 transformList
->ClearBaseValue();
676 aResult
.SetTo(transformList
->GetBaseValue(), &aValue
);
680 } else if (aAttribute
== nsGkAtoms::tabindex
) {
681 didSetResult
= aResult
.ParseIntValue(aValue
);
686 if (aAttribute
== nsGkAtoms::_class
) {
687 mClassAttribute
.SetBaseValue(aValue
, this, false);
688 aResult
.ParseAtomArray(aValue
);
692 if (aAttribute
== nsGkAtoms::rel
) {
693 aResult
.ParseAtomArray(aValue
);
699 // Check for SVGAnimatedString attribute
700 StringAttributesInfo stringInfo
= GetStringInfo();
701 for (uint32_t i
= 0; i
< stringInfo
.mCount
; i
++) {
702 if (aNamespaceID
== stringInfo
.mInfos
[i
].mNamespaceID
&&
703 aAttribute
== stringInfo
.mInfos
[i
].mName
) {
704 stringInfo
.mValues
[i
].SetBaseValue(aValue
, this, false);
713 ReportAttributeParseFailure(OwnerDoc(), aAttribute
, aValue
);
717 aResult
.SetTo(aValue
);
722 return SVGElementBase::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
723 aMaybeScriptedPrincipal
, aResult
);
726 void SVGElement::UnsetAttrInternal(int32_t aNamespaceID
, nsAtom
* aName
,
728 // XXXbz there's a bunch of redundancy here with AfterSetAttr.
729 // Maybe consolidate?
731 if (aNamespaceID
== kNameSpaceID_None
) {
732 if (IsEventAttributeName(aName
)) {
733 EventListenerManager
* manager
= GetExistingListenerManager();
735 nsAtom
* eventName
= GetEventNameForAttr(aName
);
736 manager
->RemoveEventHandler(eventName
);
741 // Check if this is a length attribute going away
742 LengthAttributesInfo lenInfo
= GetLengthInfo();
744 for (uint32_t i
= 0; i
< lenInfo
.mCount
; i
++) {
745 if (aName
== lenInfo
.mInfos
[i
].mName
) {
746 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
752 // Check if this is a length list attribute going away
753 LengthListAttributesInfo lengthListInfo
= GetLengthListInfo();
755 for (uint32_t i
= 0; i
< lengthListInfo
.mCount
; i
++) {
756 if (aName
== lengthListInfo
.mInfos
[i
].mName
) {
757 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
758 lengthListInfo
.Reset(i
);
763 // Check if this is a number list attribute going away
764 NumberListAttributesInfo numberListInfo
= GetNumberListInfo();
766 for (uint32_t i
= 0; i
< numberListInfo
.mCount
; i
++) {
767 if (aName
== numberListInfo
.mInfos
[i
].mName
) {
768 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
769 numberListInfo
.Reset(i
);
774 // Check if this is a point list attribute going away
775 if (GetPointListAttrName() == aName
) {
776 SVGAnimatedPointList
* pointList
= GetAnimatedPointList();
778 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
779 pointList
->ClearBaseValue();
784 // Check if this is a path segment list attribute going away
785 if (GetPathDataAttrName() == aName
) {
786 SVGAnimatedPathSegList
* segList
= GetAnimPathSegList();
788 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
789 segList
->ClearBaseValue();
794 // Check if this is a number attribute going away
795 NumberAttributesInfo numInfo
= GetNumberInfo();
797 for (uint32_t i
= 0; i
< numInfo
.mCount
; i
++) {
798 if (aName
== numInfo
.mInfos
[i
].mName
) {
804 // Check if this is a number pair attribute going away
805 NumberPairAttributesInfo numPairInfo
= GetNumberPairInfo();
807 for (uint32_t i
= 0; i
< numPairInfo
.mCount
; i
++) {
808 if (aName
== numPairInfo
.mInfos
[i
].mName
) {
809 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
810 numPairInfo
.Reset(i
);
815 // Check if this is an integer attribute going away
816 IntegerAttributesInfo intInfo
= GetIntegerInfo();
818 for (uint32_t i
= 0; i
< intInfo
.mCount
; i
++) {
819 if (aName
== intInfo
.mInfos
[i
].mName
) {
825 // Check if this is an integer pair attribute going away
826 IntegerPairAttributesInfo intPairInfo
= GetIntegerPairInfo();
828 for (uint32_t i
= 0; i
< intPairInfo
.mCount
; i
++) {
829 if (aName
== intPairInfo
.mInfos
[i
].mName
) {
830 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
831 intPairInfo
.Reset(i
);
836 // Check if this is a boolean attribute going away
837 BooleanAttributesInfo boolInfo
= GetBooleanInfo();
839 for (uint32_t i
= 0; i
< boolInfo
.mCount
; i
++) {
840 if (aName
== boolInfo
.mInfos
[i
].mName
) {
846 // Check if this is an enum attribute going away
847 EnumAttributesInfo enumInfo
= GetEnumInfo();
849 for (uint32_t i
= 0; i
< enumInfo
.mCount
; i
++) {
850 if (aName
== enumInfo
.mInfos
[i
].mName
) {
856 // Check if this is an orient attribute going away
857 if (aName
== nsGkAtoms::orient
) {
858 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
860 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
866 // Check if this is a viewBox attribute going away
867 if (aName
== nsGkAtoms::viewBox
) {
868 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
870 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
876 // Check if this is a preserveAspectRatio attribute going away
877 if (aName
== nsGkAtoms::preserveAspectRatio
) {
878 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
879 GetAnimatedPreserveAspectRatio();
880 if (preserveAspectRatio
) {
881 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
882 preserveAspectRatio
->Init();
887 // Check if this is a transform list attribute going away
888 if (GetTransformListAttrName() == aName
) {
889 SVGAnimatedTransformList
* transformList
= GetAnimatedTransformList();
891 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
892 transformList
->ClearBaseValue();
897 // Check for conditional processing attributes
898 nsCOMPtr
<SVGTests
> tests
= do_QueryObject(this);
899 if (tests
&& tests
->IsConditionalProcessingAttribute(aName
)) {
900 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
901 tests
->UnsetAttr(aName
);
905 // Check if this is a string list attribute going away
906 StringListAttributesInfo stringListInfo
= GetStringListInfo();
908 for (uint32_t i
= 0; i
< stringListInfo
.mCount
; i
++) {
909 if (aName
== stringListInfo
.mInfos
[i
].mName
) {
910 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
911 stringListInfo
.Reset(i
);
916 if (aName
== nsGkAtoms::_class
) {
917 mClassAttribute
.Init();
922 // Check if this is a string attribute going away
923 StringAttributesInfo stringInfo
= GetStringInfo();
925 for (uint32_t i
= 0; i
< stringInfo
.mCount
; i
++) {
926 if (aNamespaceID
== stringInfo
.mInfos
[i
].mNamespaceID
&&
927 aName
== stringInfo
.mInfos
[i
].mName
) {
934 void SVGElement::BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
935 const nsAttrValue
* aValue
, bool aNotify
) {
937 UnsetAttrInternal(aNamespaceID
, aName
, aNotify
);
939 return SVGElementBase::BeforeSetAttr(aNamespaceID
, aName
, aValue
, aNotify
);
942 nsChangeHint
SVGElement::GetAttributeChangeHint(const nsAtom
* aAttribute
,
943 int32_t aModType
) const {
944 nsChangeHint retval
=
945 SVGElementBase::GetAttributeChangeHint(aAttribute
, aModType
);
947 nsCOMPtr
<SVGTests
> tests
= do_QueryObject(const_cast<SVGElement
*>(this));
948 if (tests
&& tests
->IsConditionalProcessingAttribute(aAttribute
)) {
949 // It would be nice to only reconstruct the frame if the value returned by
950 // SVGTests::PassesConditionalProcessingTests has changed, but we don't
952 retval
|= nsChangeHint_ReconstructFrame
;
957 void SVGElement::NodeInfoChanged(Document
* aOldDoc
) {
958 SVGElementBase::NodeInfoChanged(aOldDoc
);
962 SVGElement::IsAttributeMapped(const nsAtom
* name
) const {
963 if (name
== nsGkAtoms::lang
) {
967 if (IsSVGAnimationElement()) {
968 return SVGElementBase::IsAttributeMapped(name
);
971 static const MappedAttributeEntry attributes
[] = {
972 // Properties that we don't support are commented out.
973 // { nsGkAtoms::alignment_baseline },
974 // { nsGkAtoms::baseline_shift },
976 {nsGkAtoms::clip_path
},
977 {nsGkAtoms::clip_rule
},
979 {nsGkAtoms::colorInterpolation
},
980 {nsGkAtoms::colorInterpolationFilters
},
982 {nsGkAtoms::direction
},
983 {nsGkAtoms::display
},
984 {nsGkAtoms::dominant_baseline
},
986 {nsGkAtoms::fill_opacity
},
987 {nsGkAtoms::fill_rule
},
989 {nsGkAtoms::flood_color
},
990 {nsGkAtoms::flood_opacity
},
991 {nsGkAtoms::font_family
},
992 {nsGkAtoms::font_size
},
993 {nsGkAtoms::font_size_adjust
},
994 {nsGkAtoms::font_stretch
},
995 {nsGkAtoms::font_style
},
996 {nsGkAtoms::font_variant
},
997 {nsGkAtoms::fontWeight
},
998 {nsGkAtoms::image_rendering
},
999 {nsGkAtoms::letter_spacing
},
1000 {nsGkAtoms::lighting_color
},
1001 {nsGkAtoms::marker_end
},
1002 {nsGkAtoms::marker_mid
},
1003 {nsGkAtoms::marker_start
},
1005 {nsGkAtoms::mask_type
},
1006 {nsGkAtoms::opacity
},
1007 {nsGkAtoms::overflow
},
1008 {nsGkAtoms::paint_order
},
1009 {nsGkAtoms::pointer_events
},
1010 {nsGkAtoms::shape_rendering
},
1011 {nsGkAtoms::stop_color
},
1012 {nsGkAtoms::stop_opacity
},
1013 {nsGkAtoms::stroke
},
1014 {nsGkAtoms::stroke_dasharray
},
1015 {nsGkAtoms::stroke_dashoffset
},
1016 {nsGkAtoms::stroke_linecap
},
1017 {nsGkAtoms::stroke_linejoin
},
1018 {nsGkAtoms::stroke_miterlimit
},
1019 {nsGkAtoms::stroke_opacity
},
1020 {nsGkAtoms::stroke_width
},
1021 {nsGkAtoms::text_anchor
},
1022 {nsGkAtoms::text_decoration
},
1023 {nsGkAtoms::text_rendering
},
1024 {nsGkAtoms::transform_origin
},
1025 {nsGkAtoms::unicode_bidi
},
1026 {nsGkAtoms::vector_effect
},
1027 {nsGkAtoms::visibility
},
1028 {nsGkAtoms::white_space
},
1029 {nsGkAtoms::word_spacing
},
1030 {nsGkAtoms::writing_mode
},
1033 static const MappedAttributeEntry
* const map
[] = {attributes
};
1035 return FindAttributeDependence(name
, map
) ||
1036 SVGElementBase::IsAttributeMapped(name
);
1039 //----------------------------------------------------------------------
1042 // forwarded to Element implementations
1044 //----------------------------------------------------------------------
1046 SVGSVGElement
* SVGElement::GetOwnerSVGElement() {
1047 nsIContent
* ancestor
= GetFlattenedTreeParent();
1049 while (ancestor
&& ancestor
->IsSVGElement()) {
1050 if (ancestor
->IsSVGElement(nsGkAtoms::foreignObject
)) {
1053 if (auto* svg
= SVGSVGElement::FromNode(ancestor
)) {
1056 ancestor
= ancestor
->GetFlattenedTreeParent();
1059 // we don't have an ancestor <svg> element...
1063 SVGElement
* SVGElement::GetViewportElement() {
1064 return SVGContentUtils::GetNearestViewportElement(this);
1067 already_AddRefed
<DOMSVGAnimatedString
> SVGElement::ClassName() {
1068 return mClassAttribute
.ToDOMAnimatedString(this);
1072 bool SVGElement::UpdateDeclarationBlockFromLength(
1073 StyleLockedDeclarationBlock
& aBlock
, nsCSSPropertyID aPropId
,
1074 const SVGAnimatedLength
& aLength
, ValToUse aValToUse
) {
1077 if (aValToUse
== ValToUse::Anim
) {
1078 value
= aLength
.GetAnimValInSpecifiedUnits();
1079 units
= aLength
.GetAnimUnitType();
1081 MOZ_ASSERT(aValToUse
== ValToUse::Base
);
1082 value
= aLength
.GetBaseValInSpecifiedUnits();
1083 units
= aLength
.GetBaseUnitType();
1086 // SVG parser doesn't check non-negativity of some parsed value, we should not
1087 // pass those to CSS side.
1089 SVGGeometryProperty::IsNonNegativeGeometryProperty(aPropId
)) {
1093 nsCSSUnit cssUnit
= SVGLength::SpecifiedUnitTypeToCSSUnit(units
);
1095 if (cssUnit
== eCSSUnit_Percent
) {
1096 Servo_DeclarationBlock_SetPercentValue(&aBlock
, aPropId
, value
/ 100.f
);
1098 Servo_DeclarationBlock_SetLengthValue(&aBlock
, aPropId
, value
, cssUnit
);
1105 bool SVGElement::UpdateDeclarationBlockFromPath(
1106 StyleLockedDeclarationBlock
& aBlock
, const SVGAnimatedPathSegList
& aPath
,
1107 ValToUse aValToUse
) {
1108 const SVGPathData
& pathData
=
1109 aValToUse
== ValToUse::Anim
? aPath
.GetAnimValue() : aPath
.GetBaseValue();
1111 // SVGPathData::mData is fallible but rust binding accepts nsTArray only, so
1112 // we need to point to one or the other. Fortunately, fallible and infallible
1113 // array types can be implicitly converted provided they are const.
1115 // FIXME: here we just convert the data structure from cpp verion into rust
1116 // version. We don't do any normalization for the path data from d attribute.
1117 // Based on the current discussion of https://github.com/w3c/svgwg/issues/321,
1118 // we may have to convert the relative commands into absolute commands.
1119 // The normalization should be fixed in Bug 1489392. Besides, Bug 1714238
1120 // will use the same data structure, so we may simplify this more.
1121 const nsTArray
<float>& asInFallibleArray
= pathData
.RawData();
1122 Servo_DeclarationBlock_SetPathValue(&aBlock
, eCSSProperty_d
,
1123 &asInFallibleArray
);
1127 //------------------------------------------------------------------------
1128 // Helper class: MappedAttrParser, for parsing values of mapped attributes
1132 class MOZ_STACK_CLASS MappedAttrParser
{
1134 explicit MappedAttrParser(SVGElement
& aElement
,
1135 StyleLockedDeclarationBlock
* aDecl
)
1136 : mElement(aElement
), mDecl(aDecl
) {
1138 Servo_DeclarationBlock_Clear(mDecl
);
1141 ~MappedAttrParser() {
1143 "If mDecl was initialized, it should have been returned via "
1144 "TakeDeclarationBlock (and have its pointer cleared)");
1147 // Parses a mapped attribute value.
1148 void ParseMappedAttrValue(nsAtom
* aMappedAttrName
,
1149 const nsAString
& aMappedAttrValue
);
1151 void TellStyleAlreadyParsedResult(nsAtom
const* aAtom
,
1152 SVGAnimatedLength
const& aLength
);
1153 void TellStyleAlreadyParsedResult(const SVGAnimatedPathSegList
& aPath
);
1155 // If we've parsed any values for mapped attributes, this method returns the
1156 // already_AddRefed declaration block that incorporates the parsed values.
1157 // Otherwise, this method returns null.
1158 already_AddRefed
<StyleLockedDeclarationBlock
> TakeDeclarationBlock() {
1159 return mDecl
.forget();
1162 StyleLockedDeclarationBlock
& EnsureDeclarationBlock() {
1164 mDecl
= Servo_DeclarationBlock_CreateEmpty().Consume();
1169 URLExtraData
& EnsureExtraData() {
1171 mExtraData
= mElement
.GetURLDataForStyleAttr();
1177 // For reporting use counters
1178 SVGElement
& mElement
;
1180 // Declaration for storing parsed values (lazily initialized).
1181 RefPtr
<StyleLockedDeclarationBlock
> mDecl
;
1183 // URL data for parsing stuff. Also lazy.
1184 RefPtr
<URLExtraData
> mExtraData
;
1187 void MappedAttrParser::ParseMappedAttrValue(nsAtom
* aMappedAttrName
,
1188 const nsAString
& aMappedAttrValue
) {
1189 // Get the nsCSSPropertyID ID for our mapped attribute.
1190 nsCSSPropertyID propertyID
=
1191 nsCSSProps::LookupProperty(nsAutoAtomCString(aMappedAttrName
));
1192 if (propertyID
!= eCSSProperty_UNKNOWN
) {
1193 bool changed
= false; // outparam for ParseProperty.
1194 NS_ConvertUTF16toUTF8
value(aMappedAttrValue
);
1196 auto* doc
= mElement
.OwnerDoc();
1197 changed
= Servo_DeclarationBlock_SetPropertyById(
1198 &EnsureDeclarationBlock(), propertyID
, &value
, false,
1199 &EnsureExtraData(), StyleParsingMode::ALLOW_UNITLESS_LENGTH
,
1200 doc
->GetCompatibilityMode(), doc
->CSSLoader(), StyleCssRuleType::Style
,
1203 // TODO(emilio): If we want to record these from CSSOM more generally, we
1204 // can pass the document use counters down the FFI call. For now manually
1206 if (changed
&& StaticPrefs::layout_css_use_counters_enabled()) {
1207 UseCounter useCounter
= nsCSSProps::UseCounterFor(propertyID
);
1208 MOZ_ASSERT(useCounter
!= eUseCounter_UNKNOWN
);
1209 doc
->SetUseCounter(useCounter
);
1213 MOZ_ASSERT(aMappedAttrName
== nsGkAtoms::lang
,
1214 "Only 'lang' should be unrecognized!");
1215 // CSS parser doesn't know about 'lang', so we need to handle it specially.
1216 if (aMappedAttrName
== nsGkAtoms::lang
) {
1217 propertyID
= eCSSProperty__x_lang
;
1218 RefPtr
<nsAtom
> atom
= NS_Atomize(aMappedAttrValue
);
1219 Servo_DeclarationBlock_SetIdentStringValue(&EnsureDeclarationBlock(),
1224 void MappedAttrParser::TellStyleAlreadyParsedResult(
1225 nsAtom
const* aAtom
, SVGAnimatedLength
const& aLength
) {
1226 nsCSSPropertyID propertyID
=
1227 nsCSSProps::LookupProperty(nsAutoAtomCString(aAtom
));
1228 SVGElement::UpdateDeclarationBlockFromLength(EnsureDeclarationBlock(),
1229 propertyID
, aLength
,
1230 SVGElement::ValToUse::Base
);
1233 void MappedAttrParser::TellStyleAlreadyParsedResult(
1234 const SVGAnimatedPathSegList
& aPath
) {
1235 SVGElement::UpdateDeclarationBlockFromPath(EnsureDeclarationBlock(), aPath
,
1236 SVGElement::ValToUse::Base
);
1241 //----------------------------------------------------------------------
1242 // Implementation Helpers:
1244 void SVGElement::UpdateMappedDeclarationBlock() {
1245 MOZ_ASSERT(IsPendingMappedAttributeEvaluation());
1246 MappedAttrParser
mappedAttrParser(*this, mAttrs
.GetMappedDeclarationBlock());
1248 const bool lengthAffectsStyle
=
1249 SVGGeometryProperty::ElementMapsLengthsToStyle(this);
1252 while (BorrowedAttrInfo info
= GetAttrInfoAt(i
++)) {
1253 const nsAttrName
* attrName
= info
.mName
;
1254 if (!attrName
->IsAtom() || !IsAttributeMapped(attrName
->Atom())) {
1258 if (attrName
->Atom() == nsGkAtoms::lang
&&
1259 HasAttr(kNameSpaceID_XML
, nsGkAtoms::lang
)) {
1260 // xml:lang has precedence, and will get set via Gecko_GetXMLLangValue().
1264 if (lengthAffectsStyle
) {
1265 auto const* length
= GetAnimatedLength(attrName
->Atom());
1267 if (length
&& length
->HasBaseVal()) {
1268 // This is an element with geometry property set via SVG attribute,
1269 // and the attribute is already successfully parsed. We want to go
1270 // through the optimized path to tell the style system the result
1271 // directly, rather than let it parse the same thing again.
1272 mappedAttrParser
.TellStyleAlreadyParsedResult(attrName
->Atom(),
1278 if (attrName
->Equals(nsGkAtoms::d
, kNameSpaceID_None
)) {
1279 const auto* path
= GetAnimPathSegList();
1280 // Note: Only SVGPathElement has d attribute.
1283 "SVGPathElement should have the non-null SVGAnimatedPathSegList");
1284 // The attribute should have been already successfully parsed.
1285 // We want to go through the optimized path to tell the style system
1286 // the result directly, rather than let it parse the same thing again.
1287 mappedAttrParser
.TellStyleAlreadyParsedResult(*path
);
1288 // Some other notes:
1289 // The syntax of CSS d property is different from SVG d attribute.
1290 // 1. CSS d proeprty accepts: none | path(<quoted string>);
1291 // 2. SVG d attribtue accepts: none | <string>
1292 // So we cannot use css parser to parse the SVG d attribute directly.
1293 // Besides, |mAttrs.AttrAt(i)| removes the quotes already, so the svg path
1294 // in |mAttrs.AttrAt(i)| would be something like `M0,0L1,1z` without the
1295 // quotes. So css tokenizer cannot recognize this as a quoted string, and
1296 // so svg_path::SVGPathData::parse() doesn't work for this. Fortunately,
1297 // we still can rely on the parsed result from
1298 // SVGElement::ParseAttribute() for d attribute.
1303 info
.mValue
->ToString(value
);
1304 mappedAttrParser
.ParseMappedAttrValue(attrName
->Atom(), value
);
1306 mAttrs
.SetMappedDeclarationBlock(mappedAttrParser
.TakeDeclarationBlock());
1310 * Helper methods for the type-specific WillChangeXXX methods.
1312 * This method sends out appropriate pre-change notifications so that selector
1313 * restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop
1314 * matching) work, and it returns an nsAttrValue that _may_ contain the
1315 * attribute's pre-change value.
1317 * The nsAttrValue returned by this method depends on whether there are
1318 * mutation event listeners listening for changes to this element's attributes.
1319 * If not, then the object returned is empty. If there are, then the
1320 * nsAttrValue returned contains a serialized copy of the attribute's value
1321 * prior to the change, and this object should be passed to the corresponding
1322 * DidChangeXXX method call (assuming a WillChangeXXX call is required for the
1323 * SVG type - see comment below). This is necessary so that the 'prevValue'
1324 * property of the mutation event that is dispatched will correctly contain the
1327 * The reason we need to serialize the old value if there are mutation
1328 * event listeners is because the underlying nsAttrValue for the attribute
1329 * points directly to a parsed representation of the attribute (e.g. an
1330 * SVGAnimatedLengthList*) that is a member of the SVG element. That object
1331 * will have changed by the time DidChangeXXX has been called, so without the
1332 * serialization of the old attribute value that we provide, DidChangeXXX
1333 * would have no way to get the old value to pass to SetAttrAndNotify.
1335 * We only return the old value when there are mutation event listeners because
1336 * it's not needed otherwise, and because it's expensive to serialize the old
1337 * value. This is especially true for list type attributes, which may be built
1338 * up via the SVG DOM resulting in a large number of Will/DidModifyXXX calls
1339 * before the script finally finishes setting the attribute.
1341 * Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check
1342 * and filter out redundant changes. Before calling WillChangeXXX, the caller
1343 * should check whether the new and old values are actually the same, and skip
1344 * calling Will/DidChangeXXX if they are.
1346 * Also note that not all SVG types use this scheme. For types that can be
1347 * represented by an nsAttrValue without pointing back to an SVG object (e.g.
1348 * enums, booleans, integers) we can simply use SetParsedAttr which will do all
1349 * of the above for us. For such types there is no matching WillChangeXXX
1350 * method, only DidChangeXXX which calls SetParsedAttr.
1352 nsAttrValue
SVGElement::WillChangeValue(
1353 nsAtom
* aName
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1354 // We need an empty attr value:
1355 // a) to pass to BeforeSetAttr when GetParsedAttr returns nullptr
1356 // b) to store the old value in the case we have mutation listeners
1358 // We can use the same value for both purposes, because if GetParsedAttr
1359 // returns non-null its return value is what will get passed to BeforeSetAttr,
1360 // not matter what our mutation listener situation is.
1362 // Also, we should be careful to always return this value to benefit from
1363 // return value optimization.
1364 nsAttrValue emptyOrOldAttrValue
;
1365 const nsAttrValue
* attrValue
= GetParsedAttr(aName
);
1367 // We only need to set the old value if we have listeners since otherwise it
1369 if (attrValue
&& nsContentUtils::HasMutationListeners(
1370 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this)) {
1371 emptyOrOldAttrValue
.SetToSerialized(*attrValue
);
1375 attrValue
? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION
)
1376 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION
);
1377 MutationObservers::NotifyAttributeWillChange(this, kNameSpaceID_None
, aName
,
1380 // This is not strictly correct--the attribute value parameter for
1381 // BeforeSetAttr should reflect the value that *will* be set but that implies
1382 // allocating, e.g. an extra SVGAnimatedLength, and isn't necessary at the
1383 // moment since no SVG elements overload BeforeSetAttr. For now we just pass
1384 // the current value.
1385 const nsAttrValue
* value
= attrValue
? attrValue
: &emptyOrOldAttrValue
;
1386 BeforeSetAttr(kNameSpaceID_None
, aName
, value
, kNotifyDocumentObservers
);
1387 return emptyOrOldAttrValue
;
1391 * Helper methods for the type-specific DidChangeXXX methods.
1393 * aEmptyOrOldValue will normally be the object returned from the corresponding
1394 * WillChangeXXX call. This is because:
1395 * a) WillChangeXXX will ensure the object is set when we have mutation
1397 * b) WillChangeXXX will ensure the object represents a serialized version of
1398 * the old attribute value so that the value doesn't change when the
1399 * underlying SVG type is updated.
1401 * aNewValue is replaced with the old value.
1403 void SVGElement::DidChangeValue(nsAtom
* aName
,
1404 const nsAttrValue
& aEmptyOrOldValue
,
1405 nsAttrValue
& aNewValue
,
1406 const mozAutoDocUpdate
& aProofOfUpdate
) {
1407 bool hasListeners
= nsContentUtils::HasMutationListeners(
1408 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this);
1410 HasAttr(aName
) ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION
)
1411 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION
);
1413 // XXX Really, the fourth argument to SetAttrAndNotify should be null if
1414 // aEmptyOrOldValue does not represent the actual previous value of the
1415 // attribute, but currently SVG elements do not even use the old attribute
1416 // value in |AfterSetAttr|, so this should be ok.
1417 SetAttrAndNotify(kNameSpaceID_None
, aName
, nullptr, &aEmptyOrOldValue
,
1418 aNewValue
, nullptr, modType
, hasListeners
,
1419 kNotifyDocumentObservers
, kCallAfterSetAttr
,
1420 GetComposedDoc(), aProofOfUpdate
);
1423 void SVGElement::MaybeSerializeAttrBeforeRemoval(nsAtom
* aName
, bool aNotify
) {
1424 if (!aNotify
|| !nsContentUtils::HasMutationListeners(
1425 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this)) {
1429 const nsAttrValue
* attrValue
= mAttrs
.GetAttr(aName
);
1430 if (!attrValue
) return;
1432 nsAutoString serializedValue
;
1433 attrValue
->ToString(serializedValue
);
1434 nsAttrValue
oldAttrValue(serializedValue
);
1436 mAttrs
.SetAndSwapAttr(aName
, oldAttrValue
, &oldValueSet
);
1439 nsAtom
* SVGElement::GetEventNameForAttr(nsAtom
* aAttr
) {
1440 if (IsSVGElement(nsGkAtoms::svg
)) {
1441 if (aAttr
== nsGkAtoms::onload
) return nsGkAtoms::onSVGLoad
;
1442 if (aAttr
== nsGkAtoms::onscroll
) return nsGkAtoms::onSVGScroll
;
1444 if (aAttr
== nsGkAtoms::onbegin
) return nsGkAtoms::onbeginEvent
;
1445 if (aAttr
== nsGkAtoms::onrepeat
) return nsGkAtoms::onrepeatEvent
;
1446 if (aAttr
== nsGkAtoms::onend
) return nsGkAtoms::onendEvent
;
1448 return SVGElementBase::GetEventNameForAttr(aAttr
);
1451 SVGViewportElement
* SVGElement::GetCtx() const {
1452 return SVGContentUtils::GetNearestViewportElement(this);
1456 gfxMatrix
SVGElement::PrependLocalTransformsTo(const gfxMatrix
& aMatrix
,
1457 SVGTransformTypes aWhich
) const {
1461 SVGElement::LengthAttributesInfo
SVGElement::GetLengthInfo() {
1462 return LengthAttributesInfo(nullptr, nullptr, 0);
1465 void SVGElement::SetLength(nsAtom
* aName
, const SVGAnimatedLength
& aLength
) {
1466 LengthAttributesInfo lengthInfo
= GetLengthInfo();
1468 for (uint32_t i
= 0; i
< lengthInfo
.mCount
; i
++) {
1469 if (aName
== lengthInfo
.mInfos
[i
].mName
) {
1470 lengthInfo
.mValues
[i
] = aLength
;
1471 DidAnimateLength(i
);
1475 MOZ_ASSERT(false, "no length found to set");
1478 nsAttrValue
SVGElement::WillChangeLength(
1479 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1480 return WillChangeValue(GetLengthInfo().mInfos
[aAttrEnum
].mName
,
1484 void SVGElement::DidChangeLength(uint8_t aAttrEnum
,
1485 const nsAttrValue
& aEmptyOrOldValue
,
1486 const mozAutoDocUpdate
& aProofOfUpdate
) {
1487 LengthAttributesInfo info
= GetLengthInfo();
1489 NS_ASSERTION(info
.mCount
> 0,
1490 "DidChangeLength on element with no length attribs");
1491 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1493 nsAttrValue newValue
;
1494 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
1496 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1500 void SVGElement::DidAnimateLength(uint8_t aAttrEnum
) {
1501 // We need to do this here. Normally the SMIL restyle would also cause us to
1502 // do this from DidSetComputedStyle, but we don't have that guarantee if our
1503 // frame gets reconstructed.
1504 ClearAnyCachedPath();
1506 if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) {
1507 nsCSSPropertyID propId
=
1508 SVGGeometryProperty::AttrEnumToCSSPropId(this, aAttrEnum
);
1510 // We don't map use element width/height currently. We can remove this
1512 if (propId
!= eCSSProperty_UNKNOWN
) {
1513 auto lengthInfo
= GetLengthInfo();
1514 if (lengthInfo
.mValues
[aAttrEnum
].IsAnimated()) {
1515 SMILOverrideStyle()->SetSMILValue(propId
,
1516 lengthInfo
.mValues
[aAttrEnum
]);
1518 SMILOverrideStyle()->ClearSMILValue(propId
);
1523 auto info
= GetLengthInfo();
1524 DidAnimateAttribute(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
);
1527 SVGAnimatedLength
* SVGElement::GetAnimatedLength(uint8_t aAttrEnum
) {
1528 LengthAttributesInfo info
= GetLengthInfo();
1529 if (aAttrEnum
< info
.mCount
) {
1530 return &info
.mValues
[aAttrEnum
];
1532 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1536 SVGAnimatedLength
* SVGElement::GetAnimatedLength(const nsAtom
* aAttrName
) {
1537 LengthAttributesInfo lengthInfo
= GetLengthInfo();
1539 for (uint32_t i
= 0; i
< lengthInfo
.mCount
; i
++) {
1540 if (aAttrName
== lengthInfo
.mInfos
[i
].mName
) {
1541 return &lengthInfo
.mValues
[i
];
1547 void SVGElement::GetAnimatedLengthValues(float* aFirst
, ...) {
1548 LengthAttributesInfo info
= GetLengthInfo();
1550 NS_ASSERTION(info
.mCount
> 0,
1551 "GetAnimatedLengthValues on element with no length attribs");
1553 SVGElementMetrics
metrics(this);
1559 va_start(args
, aFirst
);
1561 while (f
&& i
< info
.mCount
) {
1562 *f
= info
.mValues
[i
++].GetAnimValue(metrics
);
1563 f
= va_arg(args
, float*);
1569 SVGElement::LengthListAttributesInfo
SVGElement::GetLengthListInfo() {
1570 return LengthListAttributesInfo(nullptr, nullptr, 0);
1573 nsAttrValue
SVGElement::WillChangeLengthList(
1574 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1575 return WillChangeValue(GetLengthListInfo().mInfos
[aAttrEnum
].mName
,
1579 void SVGElement::DidChangeLengthList(uint8_t aAttrEnum
,
1580 const nsAttrValue
& aEmptyOrOldValue
,
1581 const mozAutoDocUpdate
& aProofOfUpdate
) {
1582 LengthListAttributesInfo info
= GetLengthListInfo();
1584 NS_ASSERTION(info
.mCount
> 0,
1585 "DidChangeLengthList on element with no length list attribs");
1586 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1588 nsAttrValue newValue
;
1589 newValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1591 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1595 void SVGElement::GetAnimatedLengthListValues(SVGUserUnitList
* aFirst
, ...) {
1596 LengthListAttributesInfo info
= GetLengthListInfo();
1600 "GetAnimatedLengthListValues on element with no length list attribs");
1602 SVGUserUnitList
* list
= aFirst
;
1606 va_start(args
, aFirst
);
1608 while (list
&& i
< info
.mCount
) {
1609 list
->Init(&(info
.mValues
[i
].GetAnimValue()), this, info
.mInfos
[i
].mAxis
);
1611 list
= va_arg(args
, SVGUserUnitList
*);
1617 SVGAnimatedLengthList
* SVGElement::GetAnimatedLengthList(uint8_t aAttrEnum
) {
1618 LengthListAttributesInfo info
= GetLengthListInfo();
1619 if (aAttrEnum
< info
.mCount
) {
1620 return &(info
.mValues
[aAttrEnum
]);
1622 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1626 SVGElement::NumberListAttributesInfo
SVGElement::GetNumberListInfo() {
1627 return NumberListAttributesInfo(nullptr, nullptr, 0);
1630 nsAttrValue
SVGElement::WillChangeNumberList(
1631 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1632 return WillChangeValue(GetNumberListInfo().mInfos
[aAttrEnum
].mName
,
1636 void SVGElement::DidChangeNumberList(uint8_t aAttrEnum
,
1637 const nsAttrValue
& aEmptyOrOldValue
,
1638 const mozAutoDocUpdate
& aProofOfUpdate
) {
1639 NumberListAttributesInfo info
= GetNumberListInfo();
1641 MOZ_ASSERT(info
.mCount
> 0,
1642 "DidChangeNumberList on element with no number list attribs");
1643 MOZ_ASSERT(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1645 nsAttrValue newValue
;
1646 newValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1648 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1652 SVGAnimatedNumberList
* SVGElement::GetAnimatedNumberList(uint8_t aAttrEnum
) {
1653 NumberListAttributesInfo info
= GetNumberListInfo();
1654 if (aAttrEnum
< info
.mCount
) {
1655 return &(info
.mValues
[aAttrEnum
]);
1657 MOZ_ASSERT(false, "Bad attrEnum");
1661 SVGAnimatedNumberList
* SVGElement::GetAnimatedNumberList(nsAtom
* aAttrName
) {
1662 NumberListAttributesInfo info
= GetNumberListInfo();
1663 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
1664 if (aAttrName
== info
.mInfos
[i
].mName
) {
1665 return &info
.mValues
[i
];
1668 MOZ_ASSERT(false, "Bad caller");
1672 nsAttrValue
SVGElement::WillChangePointList(
1673 const mozAutoDocUpdate
& aProofOfUpdate
) {
1674 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1675 return WillChangeValue(GetPointListAttrName(), aProofOfUpdate
);
1678 void SVGElement::DidChangePointList(const nsAttrValue
& aEmptyOrOldValue
,
1679 const mozAutoDocUpdate
& aProofOfUpdate
) {
1680 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1682 nsAttrValue newValue
;
1683 newValue
.SetTo(GetAnimatedPointList()->GetBaseValue(), nullptr);
1685 DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue
, newValue
,
1689 void SVGElement::DidAnimatePointList() {
1690 MOZ_ASSERT(GetPointListAttrName(), "Animating non-existent path data?");
1692 ClearAnyCachedPath();
1694 DidAnimateAttribute(kNameSpaceID_None
, GetPointListAttrName());
1697 nsAttrValue
SVGElement::WillChangePathSegList(
1698 const mozAutoDocUpdate
& aProofOfUpdate
) {
1699 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1700 return WillChangeValue(GetPathDataAttrName(), aProofOfUpdate
);
1703 void SVGElement::DidChangePathSegList(const nsAttrValue
& aEmptyOrOldValue
,
1704 const mozAutoDocUpdate
& aProofOfUpdate
) {
1705 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1707 nsAttrValue newValue
;
1708 newValue
.SetTo(GetAnimPathSegList()->GetBaseValue(), nullptr);
1710 DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue
, newValue
,
1714 void SVGElement::DidAnimatePathSegList() {
1715 nsStaticAtom
* name
= GetPathDataAttrName();
1716 MOZ_ASSERT(name
, "Animating non-existent path data?");
1718 ClearAnyCachedPath();
1720 // Notify style we have to update the d property because of SMIL animation.
1721 if (name
== nsGkAtoms::d
) {
1722 auto* animPathSegList
= GetAnimPathSegList();
1723 if (animPathSegList
->IsAnimating()) {
1724 SMILOverrideStyle()->SetSMILValue(nsCSSPropertyID::eCSSProperty_d
,
1727 SMILOverrideStyle()->ClearSMILValue(nsCSSPropertyID::eCSSProperty_d
);
1731 DidAnimateAttribute(kNameSpaceID_None
, name
);
1734 SVGElement::NumberAttributesInfo
SVGElement::GetNumberInfo() {
1735 return NumberAttributesInfo(nullptr, nullptr, 0);
1738 void SVGElement::DidChangeNumber(uint8_t aAttrEnum
) {
1739 NumberAttributesInfo info
= GetNumberInfo();
1741 NS_ASSERTION(info
.mCount
> 0,
1742 "DidChangeNumber on element with no number attribs");
1743 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1745 nsAttrValue attrValue
;
1746 attrValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1748 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
1752 void SVGElement::GetAnimatedNumberValues(float* aFirst
, ...) {
1753 NumberAttributesInfo info
= GetNumberInfo();
1755 NS_ASSERTION(info
.mCount
> 0,
1756 "GetAnimatedNumberValues on element with no number attribs");
1762 va_start(args
, aFirst
);
1764 while (f
&& i
< info
.mCount
) {
1765 *f
= info
.mValues
[i
++].GetAnimValue();
1766 f
= va_arg(args
, float*);
1771 SVGElement::NumberPairAttributesInfo
SVGElement::GetNumberPairInfo() {
1772 return NumberPairAttributesInfo(nullptr, nullptr, 0);
1775 nsAttrValue
SVGElement::WillChangeNumberPair(uint8_t aAttrEnum
) {
1776 mozAutoDocUpdate
updateBatch(GetComposedDoc(), kDontNotifyDocumentObservers
);
1777 return WillChangeValue(GetNumberPairInfo().mInfos
[aAttrEnum
].mName
,
1781 void SVGElement::DidChangeNumberPair(uint8_t aAttrEnum
,
1782 const nsAttrValue
& aEmptyOrOldValue
) {
1783 NumberPairAttributesInfo info
= GetNumberPairInfo();
1785 NS_ASSERTION(info
.mCount
> 0,
1786 "DidChangePairNumber on element with no number pair attribs");
1787 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1789 nsAttrValue newValue
;
1790 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
1792 mozAutoDocUpdate
updateBatch(GetComposedDoc(), kNotifyDocumentObservers
);
1793 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1797 SVGElement::IntegerAttributesInfo
SVGElement::GetIntegerInfo() {
1798 return IntegerAttributesInfo(nullptr, nullptr, 0);
1801 void SVGElement::DidChangeInteger(uint8_t aAttrEnum
) {
1802 IntegerAttributesInfo info
= GetIntegerInfo();
1803 NS_ASSERTION(info
.mCount
> 0,
1804 "DidChangeInteger on element with no integer attribs");
1805 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1807 nsAttrValue attrValue
;
1808 attrValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1810 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
1814 void SVGElement::GetAnimatedIntegerValues(int32_t* aFirst
, ...) {
1815 IntegerAttributesInfo info
= GetIntegerInfo();
1817 NS_ASSERTION(info
.mCount
> 0,
1818 "GetAnimatedIntegerValues on element with no integer attribs");
1820 int32_t* n
= aFirst
;
1824 va_start(args
, aFirst
);
1826 while (n
&& i
< info
.mCount
) {
1827 *n
= info
.mValues
[i
++].GetAnimValue();
1828 n
= va_arg(args
, int32_t*);
1833 SVGElement::IntegerPairAttributesInfo
SVGElement::GetIntegerPairInfo() {
1834 return IntegerPairAttributesInfo(nullptr, nullptr, 0);
1837 nsAttrValue
SVGElement::WillChangeIntegerPair(
1838 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1839 return WillChangeValue(GetIntegerPairInfo().mInfos
[aAttrEnum
].mName
,
1843 void SVGElement::DidChangeIntegerPair(uint8_t aAttrEnum
,
1844 const nsAttrValue
& aEmptyOrOldValue
,
1845 const mozAutoDocUpdate
& aProofOfUpdate
) {
1846 IntegerPairAttributesInfo info
= GetIntegerPairInfo();
1848 NS_ASSERTION(info
.mCount
> 0,
1849 "DidChangeIntegerPair on element with no integer pair attribs");
1850 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1852 nsAttrValue newValue
;
1853 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
1855 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1859 SVGElement::BooleanAttributesInfo
SVGElement::GetBooleanInfo() {
1860 return BooleanAttributesInfo(nullptr, nullptr, 0);
1863 void SVGElement::DidChangeBoolean(uint8_t aAttrEnum
) {
1864 BooleanAttributesInfo info
= GetBooleanInfo();
1866 NS_ASSERTION(info
.mCount
> 0,
1867 "DidChangeBoolean on element with no boolean attribs");
1868 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1870 nsAttrValue
attrValue(info
.mValues
[aAttrEnum
].GetBaseValueAtom());
1871 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
1875 SVGElement::EnumAttributesInfo
SVGElement::GetEnumInfo() {
1876 return EnumAttributesInfo(nullptr, nullptr, 0);
1879 void SVGElement::DidChangeEnum(uint8_t aAttrEnum
) {
1880 EnumAttributesInfo info
= GetEnumInfo();
1882 NS_ASSERTION(info
.mCount
> 0,
1883 "DidChangeEnum on element with no enum attribs");
1884 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1886 nsAttrValue
attrValue(info
.mValues
[aAttrEnum
].GetBaseValueAtom(this));
1887 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
1891 SVGAnimatedOrient
* SVGElement::GetAnimatedOrient() { return nullptr; }
1893 nsAttrValue
SVGElement::WillChangeOrient(
1894 const mozAutoDocUpdate
& aProofOfUpdate
) {
1895 return WillChangeValue(nsGkAtoms::orient
, aProofOfUpdate
);
1898 void SVGElement::DidChangeOrient(const nsAttrValue
& aEmptyOrOldValue
,
1899 const mozAutoDocUpdate
& aProofOfUpdate
) {
1900 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
1902 NS_ASSERTION(orient
, "DidChangeOrient on element with no orient attrib");
1904 nsAttrValue newValue
;
1905 newValue
.SetTo(*orient
, nullptr);
1907 DidChangeValue(nsGkAtoms::orient
, aEmptyOrOldValue
, newValue
, aProofOfUpdate
);
1910 SVGAnimatedViewBox
* SVGElement::GetAnimatedViewBox() { return nullptr; }
1912 nsAttrValue
SVGElement::WillChangeViewBox(
1913 const mozAutoDocUpdate
& aProofOfUpdate
) {
1914 return WillChangeValue(nsGkAtoms::viewBox
, aProofOfUpdate
);
1917 void SVGElement::DidChangeViewBox(const nsAttrValue
& aEmptyOrOldValue
,
1918 const mozAutoDocUpdate
& aProofOfUpdate
) {
1919 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
1921 NS_ASSERTION(viewBox
, "DidChangeViewBox on element with no viewBox attrib");
1923 nsAttrValue newValue
;
1924 newValue
.SetTo(*viewBox
, nullptr);
1926 DidChangeValue(nsGkAtoms::viewBox
, aEmptyOrOldValue
, newValue
,
1930 SVGAnimatedPreserveAspectRatio
* SVGElement::GetAnimatedPreserveAspectRatio() {
1934 nsAttrValue
SVGElement::WillChangePreserveAspectRatio(
1935 const mozAutoDocUpdate
& aProofOfUpdate
) {
1936 return WillChangeValue(nsGkAtoms::preserveAspectRatio
, aProofOfUpdate
);
1939 void SVGElement::DidChangePreserveAspectRatio(
1940 const nsAttrValue
& aEmptyOrOldValue
,
1941 const mozAutoDocUpdate
& aProofOfUpdate
) {
1942 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
1943 GetAnimatedPreserveAspectRatio();
1945 NS_ASSERTION(preserveAspectRatio
,
1946 "DidChangePreserveAspectRatio on element with no "
1947 "preserveAspectRatio attrib");
1949 nsAttrValue newValue
;
1950 newValue
.SetTo(*preserveAspectRatio
, nullptr);
1952 DidChangeValue(nsGkAtoms::preserveAspectRatio
, aEmptyOrOldValue
, newValue
,
1956 nsAttrValue
SVGElement::WillChangeTransformList(
1957 const mozAutoDocUpdate
& aProofOfUpdate
) {
1958 return WillChangeValue(GetTransformListAttrName(), aProofOfUpdate
);
1961 void SVGElement::DidChangeTransformList(
1962 const nsAttrValue
& aEmptyOrOldValue
,
1963 const mozAutoDocUpdate
& aProofOfUpdate
) {
1964 MOZ_ASSERT(GetTransformListAttrName(),
1965 "Changing non-existent transform list?");
1967 // The transform attribute is being set, so we must ensure that the
1968 // SVGAnimatedTransformList is/has been allocated:
1969 nsAttrValue newValue
;
1970 newValue
.SetTo(GetAnimatedTransformList(DO_ALLOCATE
)->GetBaseValue(),
1973 DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue
, newValue
,
1977 void SVGElement::DidAnimateTransformList(int32_t aModType
) {
1978 MOZ_ASSERT(GetTransformListAttrName(),
1979 "Animating non-existent transform data?");
1981 if (auto* frame
= GetPrimaryFrame()) {
1982 nsAtom
* transformAttr
= GetTransformListAttrName();
1983 frame
->AttributeChanged(kNameSpaceID_None
, transformAttr
, aModType
);
1984 // When script changes the 'transform' attribute, Element::SetAttrAndNotify
1985 // will call MutationObservers::NotifyAttributeChanged, under which
1986 // SVGTransformableElement::GetAttributeChangeHint will be called and an
1987 // appropriate change event posted to update our frame's overflow rects.
1988 // The SetAttrAndNotify doesn't happen for transform changes caused by
1989 // 'animateTransform' though (and sending out the mutation events that
1990 // MutationObservers::NotifyAttributeChanged dispatches would be
1991 // inappropriate anyway), so we need to post the change event ourself.
1992 nsChangeHint changeHint
= GetAttributeChangeHint(transformAttr
, aModType
);
1994 nsLayoutUtils::PostRestyleEvent(this, RestyleHint
{0}, changeHint
);
1996 SVGObserverUtils::InvalidateRenderingObservers(frame
);
1999 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
2002 SVGElement::StringAttributesInfo
SVGElement::GetStringInfo() {
2003 return StringAttributesInfo(nullptr, nullptr, 0);
2006 void SVGElement::GetStringBaseValue(uint8_t aAttrEnum
,
2007 nsAString
& aResult
) const {
2008 SVGElement::StringAttributesInfo info
=
2009 const_cast<SVGElement
*>(this)->GetStringInfo();
2011 NS_ASSERTION(info
.mCount
> 0,
2012 "GetBaseValue on element with no string attribs");
2014 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
2016 GetAttr(info
.mInfos
[aAttrEnum
].mNamespaceID
, info
.mInfos
[aAttrEnum
].mName
,
2020 void SVGElement::SetStringBaseValue(uint8_t aAttrEnum
,
2021 const nsAString
& aValue
) {
2022 SVGElement::StringAttributesInfo info
= GetStringInfo();
2024 NS_ASSERTION(info
.mCount
> 0,
2025 "SetBaseValue on element with no string attribs");
2027 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
2029 SetAttr(info
.mInfos
[aAttrEnum
].mNamespaceID
, info
.mInfos
[aAttrEnum
].mName
,
2033 SVGElement::StringListAttributesInfo
SVGElement::GetStringListInfo() {
2034 return StringListAttributesInfo(nullptr, nullptr, 0);
2037 nsAttrValue
SVGElement::WillChangeStringList(
2038 bool aIsConditionalProcessingAttribute
, uint8_t aAttrEnum
,
2039 const mozAutoDocUpdate
& aProofOfUpdate
) {
2041 if (aIsConditionalProcessingAttribute
) {
2042 nsCOMPtr
<SVGTests
> tests(do_QueryInterface(this));
2043 name
= tests
->GetAttrName(aAttrEnum
);
2045 name
= GetStringListInfo().mInfos
[aAttrEnum
].mName
;
2047 return WillChangeValue(name
, aProofOfUpdate
);
2050 void SVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute
,
2052 const nsAttrValue
& aEmptyOrOldValue
,
2053 const mozAutoDocUpdate
& aProofOfUpdate
) {
2055 nsAttrValue newValue
;
2056 nsCOMPtr
<SVGTests
> tests
;
2058 if (aIsConditionalProcessingAttribute
) {
2059 tests
= do_QueryObject(this);
2060 name
= tests
->GetAttrName(aAttrEnum
);
2061 tests
->GetAttrValue(aAttrEnum
, newValue
);
2063 StringListAttributesInfo info
= GetStringListInfo();
2065 NS_ASSERTION(info
.mCount
> 0,
2066 "DidChangeStringList on element with no string list attribs");
2067 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
2069 name
= info
.mInfos
[aAttrEnum
].mName
;
2070 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
2073 DidChangeValue(name
, aEmptyOrOldValue
, newValue
, aProofOfUpdate
);
2075 if (aIsConditionalProcessingAttribute
) {
2076 tests
->MaybeInvalidate();
2080 void SVGElement::DidAnimateAttribute(int32_t aNameSpaceID
, nsAtom
* aAttribute
) {
2081 if (auto* frame
= GetPrimaryFrame()) {
2082 frame
->AttributeChanged(aNameSpaceID
, aAttribute
,
2083 MutationEvent_Binding::SMIL
);
2084 SVGObserverUtils::InvalidateRenderingObservers(frame
);
2087 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
2090 nsresult
SVGElement::ReportAttributeParseFailure(Document
* aDocument
,
2092 const nsAString
& aValue
) {
2093 AutoTArray
<nsString
, 2> strings
;
2094 strings
.AppendElement(nsDependentAtomString(aAttribute
));
2095 strings
.AppendElement(aValue
);
2096 return SVGContentUtils::ReportToConsole(aDocument
, "AttributeParseWarning",
2100 UniquePtr
<SMILAttr
> SVGElement::GetAnimatedAttr(int32_t aNamespaceID
,
2102 if (aNamespaceID
== kNameSpaceID_None
) {
2104 if (GetTransformListAttrName() == aName
) {
2105 // The transform attribute is being animated, so we must ensure that the
2106 // SVGAnimatedTransformList is/has been allocated:
2107 return GetAnimatedTransformList(DO_ALLOCATE
)->ToSMILAttr(this);
2110 // Motion (fake 'attribute' for animateMotion)
2111 if (aName
== nsGkAtoms::mozAnimateMotionDummyAttr
) {
2112 return MakeUnique
<SVGMotionSMILAttr
>(this);
2116 LengthAttributesInfo info
= GetLengthInfo();
2117 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2118 if (aName
== info
.mInfos
[i
].mName
) {
2119 return info
.mValues
[i
].ToSMILAttr(this);
2125 NumberAttributesInfo info
= GetNumberInfo();
2126 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2127 if (aName
== info
.mInfos
[i
].mName
) {
2128 return info
.mValues
[i
].ToSMILAttr(this);
2135 NumberPairAttributesInfo info
= GetNumberPairInfo();
2136 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2137 if (aName
== info
.mInfos
[i
].mName
) {
2138 return info
.mValues
[i
].ToSMILAttr(this);
2145 IntegerAttributesInfo info
= GetIntegerInfo();
2146 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2147 if (aName
== info
.mInfos
[i
].mName
) {
2148 return info
.mValues
[i
].ToSMILAttr(this);
2155 IntegerPairAttributesInfo info
= GetIntegerPairInfo();
2156 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2157 if (aName
== info
.mInfos
[i
].mName
) {
2158 return info
.mValues
[i
].ToSMILAttr(this);
2165 EnumAttributesInfo info
= GetEnumInfo();
2166 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2167 if (aName
== info
.mInfos
[i
].mName
) {
2168 return info
.mValues
[i
].ToSMILAttr(this);
2175 BooleanAttributesInfo info
= GetBooleanInfo();
2176 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2177 if (aName
== info
.mInfos
[i
].mName
) {
2178 return info
.mValues
[i
].ToSMILAttr(this);
2184 if (aName
== nsGkAtoms::orient
) {
2185 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
2186 return orient
? orient
->ToSMILAttr(this) : nullptr;
2190 if (aName
== nsGkAtoms::viewBox
) {
2191 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
2192 return viewBox
? viewBox
->ToSMILAttr(this) : nullptr;
2195 // preserveAspectRatio:
2196 if (aName
== nsGkAtoms::preserveAspectRatio
) {
2197 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
2198 GetAnimatedPreserveAspectRatio();
2199 return preserveAspectRatio
? preserveAspectRatio
->ToSMILAttr(this)
2205 NumberListAttributesInfo info
= GetNumberListInfo();
2206 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2207 if (aName
== info
.mInfos
[i
].mName
) {
2208 MOZ_ASSERT(i
<= UCHAR_MAX
, "Too many attributes");
2209 return info
.mValues
[i
].ToSMILAttr(this, uint8_t(i
));
2216 LengthListAttributesInfo info
= GetLengthListInfo();
2217 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2218 if (aName
== info
.mInfos
[i
].mName
) {
2219 MOZ_ASSERT(i
<= UCHAR_MAX
, "Too many attributes");
2220 return info
.mValues
[i
].ToSMILAttr(this, uint8_t(i
),
2221 info
.mInfos
[i
].mAxis
,
2222 info
.mInfos
[i
].mCouldZeroPadList
);
2229 if (GetPointListAttrName() == aName
) {
2230 SVGAnimatedPointList
* pointList
= GetAnimatedPointList();
2232 return pointList
->ToSMILAttr(this);
2239 if (GetPathDataAttrName() == aName
) {
2240 SVGAnimatedPathSegList
* segList
= GetAnimPathSegList();
2242 return segList
->ToSMILAttr(this);
2247 if (aName
== nsGkAtoms::_class
) {
2248 return mClassAttribute
.ToSMILAttr(this);
2254 StringAttributesInfo info
= GetStringInfo();
2255 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2256 if (aNamespaceID
== info
.mInfos
[i
].mNamespaceID
&&
2257 aName
== info
.mInfos
[i
].mName
) {
2258 return info
.mValues
[i
].ToSMILAttr(this);
2266 void SVGElement::AnimationNeedsResample() {
2267 Document
* doc
= GetComposedDoc();
2268 if (doc
&& doc
->HasAnimationController()) {
2269 doc
->GetAnimationController()->SetResampleNeeded();
2273 void SVGElement::FlushAnimations() {
2274 Document
* doc
= GetComposedDoc();
2275 if (doc
&& doc
->HasAnimationController()) {
2276 doc
->GetAnimationController()->FlushResampleRequests();
2280 void SVGElement::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
2281 size_t* aNodeSize
) const {
2282 Element::AddSizeOfExcludingThis(aSizes
, aNodeSize
);
2285 } // namespace mozilla::dom