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(eCSSProperty_d
, *pathSegList
);
254 if (const auto* transformList
= GetAnimatedTransformList()) {
255 *dest
->GetAnimatedTransformList(DO_ALLOCATE
) = *transformList
;
257 if (const auto* animateMotionTransform
= GetAnimateMotionTransform()) {
258 dest
->SetAnimateMotionTransform(animateMotionTransform
);
260 if (const auto* smilOverrideStyleDecoration
=
261 GetSMILOverrideStyleDeclaration()) {
262 RefPtr
<DeclarationBlock
> declClone
= smilOverrideStyleDecoration
->Clone();
263 declClone
->SetDirty();
264 dest
->SetSMILOverrideStyleDeclaration(*declClone
);
271 //----------------------------------------------------------------------
272 // SVGElement methods
274 void SVGElement::DidAnimateClass() {
275 // For Servo, snapshot the element before we change it.
276 PresShell
* presShell
= OwnerDoc()->GetPresShell();
278 if (nsPresContext
* presContext
= presShell
->GetPresContext()) {
279 presContext
->RestyleManager()->ClassAttributeWillBeChangedBySMIL(this);
284 mClassAttribute
.GetAnimValue(src
, this);
285 if (!mClassAnimAttr
) {
286 mClassAnimAttr
= MakeUnique
<nsAttrValue
>();
288 mClassAnimAttr
->ParseAtomArray(src
);
290 // FIXME(emilio): This re-selector-matches, but we do the snapshot stuff right
291 // above... Is this needed anymore?
293 presShell
->RestyleForAnimation(this, RestyleHint::RESTYLE_SELF
);
295 DidAnimateAttribute(kNameSpaceID_None
, nsGkAtoms::_class
);
298 nsresult
SVGElement::Init() {
299 // Set up length attributes - can't do this in the constructor
300 // because we can't do a virtual call at that point
302 GetLengthInfo().ResetAll();
303 GetNumberInfo().ResetAll();
304 GetNumberPairInfo().ResetAll();
305 GetIntegerInfo().ResetAll();
306 GetIntegerPairInfo().ResetAll();
307 GetBooleanInfo().ResetAll();
308 GetEnumInfo().ResetAll();
310 if (SVGAnimatedOrient
* orient
= GetAnimatedOrient()) {
314 if (SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox()) {
318 if (SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
319 GetAnimatedPreserveAspectRatio()) {
320 preserveAspectRatio
->Init();
323 GetLengthListInfo().ResetAll();
324 GetNumberListInfo().ResetAll();
326 // No need to reset SVGPointList since the default value is always the same
329 // No need to reset SVGPathData since the default value is always the same
332 GetStringInfo().ResetAll();
336 //----------------------------------------------------------------------
339 //----------------------------------------------------------------------
340 // nsIContent methods
342 nsresult
SVGElement::BindToTree(BindContext
& aContext
, nsINode
& aParent
) {
343 nsresult rv
= SVGElementBase::BindToTree(aContext
, aParent
);
344 NS_ENSURE_SUCCESS(rv
, rv
);
346 // Hide any nonce from the DOM, but keep the internal value of the
347 // nonce by copying and resetting the internal nonce value.
348 if (HasFlag(NODE_HAS_NONCE_AND_HEADER_CSP
) && IsInComposedDoc() &&
349 OwnerDoc()->GetBrowsingContext()) {
350 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
351 "SVGElement::ResetNonce::Runnable",
352 [self
= RefPtr
<SVGElement
>(this)]() {
354 self
->GetNonce(nonce
);
355 self
->SetAttr(kNameSpaceID_None
, nsGkAtoms::nonce
, u
""_ns
, true);
356 self
->SetNonce(nonce
);
363 void SVGElement::AfterSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
364 const nsAttrValue
* aValue
,
365 const nsAttrValue
* aOldValue
,
366 nsIPrincipal
* aSubjectPrincipal
, bool aNotify
) {
367 if (IsEventAttributeName(aName
) && aValue
) {
368 MOZ_ASSERT(aValue
->Type() == nsAttrValue::eString
,
369 "Expected string value for script body");
370 SetEventHandler(GetEventNameForAttr(aName
), aValue
->GetStringValue());
373 // The nonce will be copied over to an internal slot and cleared from the
374 // Element within BindToTree to avoid CSS Selector nonce exfiltration if
375 // the CSP list contains a header-delivered CSP.
376 if (nsGkAtoms::nonce
== aName
&& kNameSpaceID_None
== aNamespaceID
) {
378 SetNonce(aValue
->GetStringValue());
379 if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) {
380 SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP
);
387 return SVGElementBase::AfterSetAttr(aNamespaceID
, aName
, aValue
, aOldValue
,
388 aSubjectPrincipal
, aNotify
);
391 bool SVGElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
392 const nsAString
& aValue
,
393 nsIPrincipal
* aMaybeScriptedPrincipal
,
394 nsAttrValue
& aResult
) {
396 bool foundMatch
= false;
397 bool didSetResult
= false;
399 if (aNamespaceID
== kNameSpaceID_None
) {
400 // Check for SVGAnimatedLength attribute
401 LengthAttributesInfo lengthInfo
= GetLengthInfo();
404 for (i
= 0; i
< lengthInfo
.mCount
; i
++) {
405 if (aAttribute
== lengthInfo
.mInfos
[i
].mName
) {
406 rv
= lengthInfo
.mValues
[i
].SetBaseValueString(aValue
, this, false);
410 aResult
.SetTo(lengthInfo
.mValues
[i
], &aValue
);
419 // Check for SVGAnimatedLengthList attribute
420 LengthListAttributesInfo lengthListInfo
= GetLengthListInfo();
421 for (i
= 0; i
< lengthListInfo
.mCount
; i
++) {
422 if (aAttribute
== lengthListInfo
.mInfos
[i
].mName
) {
423 rv
= lengthListInfo
.mValues
[i
].SetBaseValueString(aValue
);
425 lengthListInfo
.Reset(i
);
427 aResult
.SetTo(lengthListInfo
.mValues
[i
].GetBaseValue(), &aValue
);
437 // Check for SVGAnimatedNumberList attribute
438 NumberListAttributesInfo numberListInfo
= GetNumberListInfo();
439 for (i
= 0; i
< numberListInfo
.mCount
; i
++) {
440 if (aAttribute
== numberListInfo
.mInfos
[i
].mName
) {
441 rv
= numberListInfo
.mValues
[i
].SetBaseValueString(aValue
);
443 numberListInfo
.Reset(i
);
445 aResult
.SetTo(numberListInfo
.mValues
[i
].GetBaseValue(), &aValue
);
455 // Check for SVGAnimatedPointList attribute
456 if (GetPointListAttrName() == aAttribute
) {
457 if (SVGAnimatedPointList
* pointList
= GetAnimatedPointList()) {
458 pointList
->SetBaseValueString(aValue
);
459 // The spec says we parse everything up to the failure, so we DON'T
460 // need to check the result of SetBaseValueString or call
461 // pointList->ClearBaseValue() if it fails
462 aResult
.SetTo(pointList
->GetBaseValue(), &aValue
);
470 // Check for SVGAnimatedPathSegList attribute
471 if (GetPathDataAttrName() == aAttribute
) {
472 if (SVGAnimatedPathSegList
* segList
= GetAnimPathSegList()) {
473 segList
->SetBaseValueString(aValue
);
474 // The spec says we parse everything up to the failure, so we DON'T
475 // need to check the result of SetBaseValueString or call
476 // segList->ClearBaseValue() if it fails
477 aResult
.SetTo(segList
->GetBaseValue(), &aValue
);
485 // Check for SVGAnimatedNumber attribute
486 NumberAttributesInfo numberInfo
= GetNumberInfo();
487 for (i
= 0; i
< numberInfo
.mCount
; i
++) {
488 if (aAttribute
== numberInfo
.mInfos
[i
].mName
) {
489 rv
= numberInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
493 aResult
.SetTo(numberInfo
.mValues
[i
].GetBaseValue(), &aValue
);
503 // Check for SVGAnimatedNumberPair attribute
504 NumberPairAttributesInfo numberPairInfo
= GetNumberPairInfo();
505 for (i
= 0; i
< numberPairInfo
.mCount
; i
++) {
506 if (aAttribute
== numberPairInfo
.mInfos
[i
].mName
) {
507 rv
= numberPairInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
509 numberPairInfo
.Reset(i
);
511 aResult
.SetTo(numberPairInfo
.mValues
[i
], &aValue
);
521 // Check for SVGAnimatedInteger attribute
522 IntegerAttributesInfo integerInfo
= GetIntegerInfo();
523 for (i
= 0; i
< integerInfo
.mCount
; i
++) {
524 if (aAttribute
== integerInfo
.mInfos
[i
].mName
) {
525 rv
= integerInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
527 integerInfo
.Reset(i
);
529 aResult
.SetTo(integerInfo
.mValues
[i
].GetBaseValue(), &aValue
);
539 // Check for SVGAnimatedIntegerPair attribute
540 IntegerPairAttributesInfo integerPairInfo
= GetIntegerPairInfo();
541 for (i
= 0; i
< integerPairInfo
.mCount
; i
++) {
542 if (aAttribute
== integerPairInfo
.mInfos
[i
].mName
) {
543 rv
= integerPairInfo
.mValues
[i
].SetBaseValueString(aValue
, this);
545 integerPairInfo
.Reset(i
);
547 aResult
.SetTo(integerPairInfo
.mValues
[i
], &aValue
);
557 // Check for SVGAnimatedBoolean attribute
558 BooleanAttributesInfo booleanInfo
= GetBooleanInfo();
559 for (i
= 0; i
< booleanInfo
.mCount
; i
++) {
560 if (aAttribute
== booleanInfo
.mInfos
[i
].mName
) {
561 nsAtom
* valAtom
= NS_GetStaticAtom(aValue
);
562 rv
= valAtom
? booleanInfo
.mValues
[i
].SetBaseValueAtom(valAtom
, this)
563 : NS_ERROR_DOM_SYNTAX_ERR
;
565 booleanInfo
.Reset(i
);
567 aResult
.SetTo(valAtom
);
577 // Check for SVGAnimatedEnumeration attribute
578 EnumAttributesInfo enumInfo
= GetEnumInfo();
579 for (i
= 0; i
< enumInfo
.mCount
; i
++) {
580 if (aAttribute
== enumInfo
.mInfos
[i
].mName
) {
581 RefPtr
<nsAtom
> valAtom
= NS_Atomize(aValue
);
582 if (!enumInfo
.mValues
[i
].SetBaseValueAtom(valAtom
, this)) {
583 // Exact error value does not matter; we just need to mark the
585 rv
= NS_ERROR_FAILURE
;
588 aResult
.SetTo(valAtom
);
598 // Check for conditional processing attributes
599 nsCOMPtr
<SVGTests
> tests
= do_QueryObject(this);
600 if (tests
&& tests
->ParseConditionalProcessingAttribute(
601 aAttribute
, aValue
, aResult
)) {
607 // Check for StringList attribute
608 StringListAttributesInfo stringListInfo
= GetStringListInfo();
609 for (i
= 0; i
< stringListInfo
.mCount
; i
++) {
610 if (aAttribute
== stringListInfo
.mInfos
[i
].mName
) {
611 rv
= stringListInfo
.mValues
[i
].SetValue(aValue
);
613 stringListInfo
.Reset(i
);
615 aResult
.SetTo(stringListInfo
.mValues
[i
], &aValue
);
625 // Check for orient attribute
626 if (aAttribute
== nsGkAtoms::orient
) {
627 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
629 rv
= orient
->SetBaseValueString(aValue
, this, false);
633 aResult
.SetTo(*orient
, &aValue
);
638 // Check for viewBox attribute
639 } else if (aAttribute
== nsGkAtoms::viewBox
) {
640 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
642 rv
= viewBox
->SetBaseValueString(aValue
, this, false);
646 aResult
.SetTo(*viewBox
, &aValue
);
651 // Check for preserveAspectRatio attribute
652 } else if (aAttribute
== nsGkAtoms::preserveAspectRatio
) {
653 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
654 GetAnimatedPreserveAspectRatio();
655 if (preserveAspectRatio
) {
656 rv
= preserveAspectRatio
->SetBaseValueString(aValue
, this, false);
658 preserveAspectRatio
->Init();
660 aResult
.SetTo(*preserveAspectRatio
, &aValue
);
665 // Check for SVGAnimatedTransformList attribute
666 } else if (GetTransformListAttrName() == aAttribute
) {
667 // The transform attribute is being set, so we must ensure that the
668 // SVGAnimatedTransformList is/has been allocated:
669 SVGAnimatedTransformList
* transformList
=
670 GetAnimatedTransformList(DO_ALLOCATE
);
671 rv
= transformList
->SetBaseValueString(aValue
, this);
673 transformList
->ClearBaseValue();
675 aResult
.SetTo(transformList
->GetBaseValue(), &aValue
);
679 } else if (aAttribute
== nsGkAtoms::tabindex
) {
680 didSetResult
= aResult
.ParseIntValue(aValue
);
685 if (aAttribute
== nsGkAtoms::_class
) {
686 mClassAttribute
.SetBaseValue(aValue
, this, false);
687 aResult
.ParseAtomArray(aValue
);
691 if (aAttribute
== nsGkAtoms::rel
) {
692 aResult
.ParseAtomArray(aValue
);
698 // Check for SVGAnimatedString attribute
699 StringAttributesInfo stringInfo
= GetStringInfo();
700 for (uint32_t i
= 0; i
< stringInfo
.mCount
; i
++) {
701 if (aNamespaceID
== stringInfo
.mInfos
[i
].mNamespaceID
&&
702 aAttribute
== stringInfo
.mInfos
[i
].mName
) {
703 stringInfo
.mValues
[i
].SetBaseValue(aValue
, this, false);
712 ReportAttributeParseFailure(OwnerDoc(), aAttribute
, aValue
);
716 aResult
.SetTo(aValue
);
721 return SVGElementBase::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
722 aMaybeScriptedPrincipal
, aResult
);
725 void SVGElement::UnsetAttrInternal(int32_t aNamespaceID
, nsAtom
* aName
,
727 // XXXbz there's a bunch of redundancy here with AfterSetAttr.
728 // Maybe consolidate?
730 if (aNamespaceID
== kNameSpaceID_None
) {
731 if (IsEventAttributeName(aName
)) {
732 EventListenerManager
* manager
= GetExistingListenerManager();
734 nsAtom
* eventName
= GetEventNameForAttr(aName
);
735 manager
->RemoveEventHandler(eventName
);
740 // Check if this is a length attribute going away
741 LengthAttributesInfo lenInfo
= GetLengthInfo();
743 for (uint32_t i
= 0; i
< lenInfo
.mCount
; i
++) {
744 if (aName
== lenInfo
.mInfos
[i
].mName
) {
745 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
751 // Check if this is a length list attribute going away
752 LengthListAttributesInfo lengthListInfo
= GetLengthListInfo();
754 for (uint32_t i
= 0; i
< lengthListInfo
.mCount
; i
++) {
755 if (aName
== lengthListInfo
.mInfos
[i
].mName
) {
756 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
757 lengthListInfo
.Reset(i
);
762 // Check if this is a number list attribute going away
763 NumberListAttributesInfo numberListInfo
= GetNumberListInfo();
765 for (uint32_t i
= 0; i
< numberListInfo
.mCount
; i
++) {
766 if (aName
== numberListInfo
.mInfos
[i
].mName
) {
767 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
768 numberListInfo
.Reset(i
);
773 // Check if this is a point list attribute going away
774 if (GetPointListAttrName() == aName
) {
775 SVGAnimatedPointList
* pointList
= GetAnimatedPointList();
777 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
778 pointList
->ClearBaseValue();
783 // Check if this is a path segment list attribute going away
784 if (GetPathDataAttrName() == aName
) {
785 SVGAnimatedPathSegList
* segList
= GetAnimPathSegList();
787 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
788 segList
->ClearBaseValue();
793 // Check if this is a number attribute going away
794 NumberAttributesInfo numInfo
= GetNumberInfo();
796 for (uint32_t i
= 0; i
< numInfo
.mCount
; i
++) {
797 if (aName
== numInfo
.mInfos
[i
].mName
) {
803 // Check if this is a number pair attribute going away
804 NumberPairAttributesInfo numPairInfo
= GetNumberPairInfo();
806 for (uint32_t i
= 0; i
< numPairInfo
.mCount
; i
++) {
807 if (aName
== numPairInfo
.mInfos
[i
].mName
) {
808 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
809 numPairInfo
.Reset(i
);
814 // Check if this is an integer attribute going away
815 IntegerAttributesInfo intInfo
= GetIntegerInfo();
817 for (uint32_t i
= 0; i
< intInfo
.mCount
; i
++) {
818 if (aName
== intInfo
.mInfos
[i
].mName
) {
824 // Check if this is an integer pair attribute going away
825 IntegerPairAttributesInfo intPairInfo
= GetIntegerPairInfo();
827 for (uint32_t i
= 0; i
< intPairInfo
.mCount
; i
++) {
828 if (aName
== intPairInfo
.mInfos
[i
].mName
) {
829 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
830 intPairInfo
.Reset(i
);
835 // Check if this is a boolean attribute going away
836 BooleanAttributesInfo boolInfo
= GetBooleanInfo();
838 for (uint32_t i
= 0; i
< boolInfo
.mCount
; i
++) {
839 if (aName
== boolInfo
.mInfos
[i
].mName
) {
845 // Check if this is an enum attribute going away
846 EnumAttributesInfo enumInfo
= GetEnumInfo();
848 for (uint32_t i
= 0; i
< enumInfo
.mCount
; i
++) {
849 if (aName
== enumInfo
.mInfos
[i
].mName
) {
855 // Check if this is an orient attribute going away
856 if (aName
== nsGkAtoms::orient
) {
857 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
859 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
865 // Check if this is a viewBox attribute going away
866 if (aName
== nsGkAtoms::viewBox
) {
867 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
869 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
875 // Check if this is a preserveAspectRatio attribute going away
876 if (aName
== nsGkAtoms::preserveAspectRatio
) {
877 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
878 GetAnimatedPreserveAspectRatio();
879 if (preserveAspectRatio
) {
880 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
881 preserveAspectRatio
->Init();
886 // Check if this is a transform list attribute going away
887 if (GetTransformListAttrName() == aName
) {
888 SVGAnimatedTransformList
* transformList
= GetAnimatedTransformList();
890 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
891 transformList
->ClearBaseValue();
896 // Check for conditional processing attributes
897 nsCOMPtr
<SVGTests
> tests
= do_QueryObject(this);
898 if (tests
&& tests
->IsConditionalProcessingAttribute(aName
)) {
899 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
900 tests
->UnsetAttr(aName
);
904 // Check if this is a string list attribute going away
905 StringListAttributesInfo stringListInfo
= GetStringListInfo();
907 for (uint32_t i
= 0; i
< stringListInfo
.mCount
; i
++) {
908 if (aName
== stringListInfo
.mInfos
[i
].mName
) {
909 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
910 stringListInfo
.Reset(i
);
915 if (aName
== nsGkAtoms::_class
) {
916 mClassAttribute
.Init();
921 // Check if this is a string attribute going away
922 StringAttributesInfo stringInfo
= GetStringInfo();
924 for (uint32_t i
= 0; i
< stringInfo
.mCount
; i
++) {
925 if (aNamespaceID
== stringInfo
.mInfos
[i
].mNamespaceID
&&
926 aName
== stringInfo
.mInfos
[i
].mName
) {
933 void SVGElement::BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
934 const nsAttrValue
* aValue
, bool aNotify
) {
936 UnsetAttrInternal(aNamespaceID
, aName
, aNotify
);
938 return SVGElementBase::BeforeSetAttr(aNamespaceID
, aName
, aValue
, aNotify
);
941 nsChangeHint
SVGElement::GetAttributeChangeHint(const nsAtom
* aAttribute
,
942 int32_t aModType
) const {
943 nsChangeHint retval
=
944 SVGElementBase::GetAttributeChangeHint(aAttribute
, aModType
);
946 nsCOMPtr
<SVGTests
> tests
= do_QueryObject(const_cast<SVGElement
*>(this));
947 if (tests
&& tests
->IsConditionalProcessingAttribute(aAttribute
)) {
948 // It would be nice to only reconstruct the frame if the value returned by
949 // SVGTests::PassesConditionalProcessingTests has changed, but we don't
951 retval
|= nsChangeHint_ReconstructFrame
;
956 void SVGElement::NodeInfoChanged(Document
* aOldDoc
) {
957 SVGElementBase::NodeInfoChanged(aOldDoc
);
961 SVGElement::IsAttributeMapped(const nsAtom
* name
) const {
962 if (name
== nsGkAtoms::lang
) {
966 if (IsSVGAnimationElement()) {
967 return SVGElementBase::IsAttributeMapped(name
);
970 static const MappedAttributeEntry attributes
[] = {
971 // Properties that we don't support are commented out.
972 // { nsGkAtoms::alignment_baseline },
973 // { nsGkAtoms::baseline_shift },
975 {nsGkAtoms::clip_path
},
976 {nsGkAtoms::clip_rule
},
978 {nsGkAtoms::colorInterpolation
},
979 {nsGkAtoms::colorInterpolationFilters
},
981 {nsGkAtoms::direction
},
982 {nsGkAtoms::display
},
983 {nsGkAtoms::dominant_baseline
},
985 {nsGkAtoms::fill_opacity
},
986 {nsGkAtoms::fill_rule
},
988 {nsGkAtoms::flood_color
},
989 {nsGkAtoms::flood_opacity
},
990 {nsGkAtoms::font_family
},
991 {nsGkAtoms::font_size
},
992 {nsGkAtoms::font_size_adjust
},
993 {nsGkAtoms::font_stretch
},
994 {nsGkAtoms::font_style
},
995 {nsGkAtoms::font_variant
},
996 {nsGkAtoms::fontWeight
},
997 {nsGkAtoms::image_rendering
},
998 {nsGkAtoms::letter_spacing
},
999 {nsGkAtoms::lighting_color
},
1000 {nsGkAtoms::marker_end
},
1001 {nsGkAtoms::marker_mid
},
1002 {nsGkAtoms::marker_start
},
1004 {nsGkAtoms::mask_type
},
1005 {nsGkAtoms::opacity
},
1006 {nsGkAtoms::overflow
},
1007 {nsGkAtoms::paint_order
},
1008 {nsGkAtoms::pointer_events
},
1009 {nsGkAtoms::shape_rendering
},
1010 {nsGkAtoms::stop_color
},
1011 {nsGkAtoms::stop_opacity
},
1012 {nsGkAtoms::stroke
},
1013 {nsGkAtoms::stroke_dasharray
},
1014 {nsGkAtoms::stroke_dashoffset
},
1015 {nsGkAtoms::stroke_linecap
},
1016 {nsGkAtoms::stroke_linejoin
},
1017 {nsGkAtoms::stroke_miterlimit
},
1018 {nsGkAtoms::stroke_opacity
},
1019 {nsGkAtoms::stroke_width
},
1020 {nsGkAtoms::text_anchor
},
1021 {nsGkAtoms::text_decoration
},
1022 {nsGkAtoms::text_rendering
},
1023 {nsGkAtoms::transform_origin
},
1024 {nsGkAtoms::unicode_bidi
},
1025 {nsGkAtoms::vector_effect
},
1026 {nsGkAtoms::visibility
},
1027 {nsGkAtoms::white_space
},
1028 {nsGkAtoms::word_spacing
},
1029 {nsGkAtoms::writing_mode
},
1032 static const MappedAttributeEntry
* const map
[] = {attributes
};
1034 return FindAttributeDependence(name
, map
) ||
1035 SVGElementBase::IsAttributeMapped(name
);
1038 //----------------------------------------------------------------------
1041 // forwarded to Element implementations
1043 //----------------------------------------------------------------------
1045 SVGSVGElement
* SVGElement::GetOwnerSVGElement() {
1046 nsIContent
* ancestor
= GetFlattenedTreeParent();
1048 while (ancestor
&& ancestor
->IsSVGElement()) {
1049 if (ancestor
->IsSVGElement(nsGkAtoms::foreignObject
)) {
1052 if (auto* svg
= SVGSVGElement::FromNode(ancestor
)) {
1055 ancestor
= ancestor
->GetFlattenedTreeParent();
1058 // we don't have an ancestor <svg> element...
1062 SVGElement
* SVGElement::GetViewportElement() {
1063 return SVGContentUtils::GetNearestViewportElement(this);
1066 already_AddRefed
<DOMSVGAnimatedString
> SVGElement::ClassName() {
1067 return mClassAttribute
.ToDOMAnimatedString(this);
1071 bool SVGElement::UpdateDeclarationBlockFromLength(
1072 StyleLockedDeclarationBlock
& aBlock
, nsCSSPropertyID aPropId
,
1073 const SVGAnimatedLength
& aLength
, ValToUse aValToUse
) {
1076 if (aValToUse
== ValToUse::Anim
) {
1077 value
= aLength
.GetAnimValInSpecifiedUnits();
1078 units
= aLength
.GetAnimUnitType();
1080 MOZ_ASSERT(aValToUse
== ValToUse::Base
);
1081 value
= aLength
.GetBaseValInSpecifiedUnits();
1082 units
= aLength
.GetBaseUnitType();
1085 // SVG parser doesn't check non-negativity of some parsed value, we should not
1086 // pass those to CSS side.
1088 SVGGeometryProperty::IsNonNegativeGeometryProperty(aPropId
)) {
1092 nsCSSUnit cssUnit
= SVGLength::SpecifiedUnitTypeToCSSUnit(units
);
1094 if (cssUnit
== eCSSUnit_Percent
) {
1095 Servo_DeclarationBlock_SetPercentValue(&aBlock
, aPropId
, value
/ 100.f
);
1097 Servo_DeclarationBlock_SetLengthValue(&aBlock
, aPropId
, value
, cssUnit
);
1104 bool SVGElement::UpdateDeclarationBlockFromPath(
1105 StyleLockedDeclarationBlock
& aBlock
, const SVGAnimatedPathSegList
& aPath
,
1106 ValToUse aValToUse
) {
1107 const SVGPathData
& pathData
=
1108 aValToUse
== ValToUse::Anim
? aPath
.GetAnimValue() : aPath
.GetBaseValue();
1110 // Based on the current discussion of https://github.com/w3c/svgwg/issues/321,
1111 // we may have to convert the relative commands into absolute commands.
1112 // The normalization should be fixed in Bug 1489392.
1113 Servo_DeclarationBlock_SetPathValue(&aBlock
, eCSSProperty_d
,
1114 &pathData
.RawData());
1118 template <typename Float
>
1119 static StyleTransformOperation
MatrixToTransformOperation(
1120 const gfx::BaseMatrix
<Float
>& aMatrix
) {
1121 return StyleTransformOperation::Matrix(StyleGenericMatrix
<float>{
1122 .a
= float(aMatrix
._11
),
1123 .b
= float(aMatrix
._12
),
1124 .c
= float(aMatrix
._21
),
1125 .d
= float(aMatrix
._22
),
1126 .e
= float(aMatrix
._31
),
1127 .f
= float(aMatrix
._32
),
1131 static void SVGTransformToCSS(const SVGTransform
& aTransform
,
1132 nsTArray
<StyleTransformOperation
>& aOut
) {
1133 switch (aTransform
.Type()) {
1134 case dom::SVGTransform_Binding::SVG_TRANSFORM_SCALE
: {
1135 const auto& m
= aTransform
.GetMatrix();
1136 aOut
.AppendElement(StyleTransformOperation::Scale(m
._11
, m
._22
));
1139 case dom::SVGTransform_Binding::SVG_TRANSFORM_TRANSLATE
: {
1140 auto p
= aTransform
.GetMatrix().GetTranslation();
1141 aOut
.AppendElement(StyleTransformOperation::Translate(
1142 LengthPercentage::FromPixels(CSSCoord(p
.x
)),
1143 LengthPercentage::FromPixels(CSSCoord(p
.y
))));
1146 case dom::SVGTransform_Binding::SVG_TRANSFORM_ROTATE
: {
1148 aTransform
.GetRotationOrigin(cx
, cy
);
1149 const StyleAngle angle
{aTransform
.Angle()};
1150 const bool hasOrigin
= cx
!= 0.0f
|| cy
!= 0.0f
;
1152 aOut
.AppendElement(StyleTransformOperation::Translate(
1153 LengthPercentage::FromPixels(cx
),
1154 LengthPercentage::FromPixels(cy
)));
1156 aOut
.AppendElement(StyleTransformOperation::Rotate(angle
));
1158 aOut
.AppendElement(StyleTransformOperation::Translate(
1159 LengthPercentage::FromPixels(-cx
),
1160 LengthPercentage::FromPixels(-cy
)));
1164 case dom::SVGTransform_Binding::SVG_TRANSFORM_SKEWX
:
1165 aOut
.AppendElement(StyleTransformOperation::SkewX({aTransform
.Angle()}));
1167 case dom::SVGTransform_Binding::SVG_TRANSFORM_SKEWY
:
1168 aOut
.AppendElement(StyleTransformOperation::SkewY({aTransform
.Angle()}));
1170 case dom::SVGTransform_Binding::SVG_TRANSFORM_MATRIX
: {
1171 aOut
.AppendElement(MatrixToTransformOperation(aTransform
.GetMatrix()));
1174 case dom::SVGTransform_Binding::SVG_TRANSFORM_UNKNOWN
:
1176 MOZ_CRASH("Bad SVGTransform?");
1181 bool SVGElement::UpdateDeclarationBlockFromTransform(
1182 StyleLockedDeclarationBlock
& aBlock
,
1183 const SVGAnimatedTransformList
* aTransform
,
1184 const gfx::Matrix
* aAnimateMotionTransform
, ValToUse aValToUse
) {
1185 MOZ_ASSERT(aTransform
|| aAnimateMotionTransform
);
1186 AutoTArray
<StyleTransformOperation
, 5> operations
;
1187 if (aAnimateMotionTransform
) {
1188 operations
.AppendElement(
1189 MatrixToTransformOperation(*aAnimateMotionTransform
));
1192 const SVGTransformList
& transforms
= aValToUse
== ValToUse::Anim
1193 ? aTransform
->GetAnimValue()
1194 : aTransform
->GetBaseValue();
1195 // TODO: Maybe make SVGTransform use StyleTransformOperation directly?
1196 for (size_t i
= 0, len
= transforms
.Length(); i
< len
; ++i
) {
1197 SVGTransformToCSS(transforms
[i
], operations
);
1200 Servo_DeclarationBlock_SetTransform(&aBlock
, eCSSProperty_transform
,
1205 //------------------------------------------------------------------------
1206 // Helper class: MappedAttrParser, for parsing values of mapped attributes
1210 class MOZ_STACK_CLASS MappedAttrParser
{
1212 explicit MappedAttrParser(SVGElement
& aElement
,
1213 StyleLockedDeclarationBlock
* aDecl
)
1214 : mElement(aElement
), mDecl(aDecl
) {
1216 Servo_DeclarationBlock_Clear(mDecl
);
1219 ~MappedAttrParser() {
1221 "If mDecl was initialized, it should have been returned via "
1222 "TakeDeclarationBlock (and have its pointer cleared)");
1225 // Parses a mapped attribute value.
1226 void ParseMappedAttrValue(nsAtom
* aMappedAttrName
,
1227 const nsAString
& aMappedAttrValue
);
1229 void TellStyleAlreadyParsedResult(nsAtom
const* aAtom
,
1230 SVGAnimatedLength
const& aLength
);
1231 void TellStyleAlreadyParsedResult(const SVGAnimatedPathSegList
&);
1232 void TellStyleAlreadyParsedResult(const SVGAnimatedTransformList
&);
1234 // If we've parsed any values for mapped attributes, this method returns the
1235 // already_AddRefed declaration block that incorporates the parsed values.
1236 // Otherwise, this method returns null.
1237 already_AddRefed
<StyleLockedDeclarationBlock
> TakeDeclarationBlock() {
1238 return mDecl
.forget();
1241 StyleLockedDeclarationBlock
& EnsureDeclarationBlock() {
1243 mDecl
= Servo_DeclarationBlock_CreateEmpty().Consume();
1248 URLExtraData
& EnsureExtraData() {
1250 mExtraData
= mElement
.GetURLDataForStyleAttr();
1256 // For reporting use counters
1257 SVGElement
& mElement
;
1259 // Declaration for storing parsed values (lazily initialized).
1260 RefPtr
<StyleLockedDeclarationBlock
> mDecl
;
1262 // URL data for parsing stuff. Also lazy.
1263 RefPtr
<URLExtraData
> mExtraData
;
1266 void MappedAttrParser::ParseMappedAttrValue(nsAtom
* aMappedAttrName
,
1267 const nsAString
& aMappedAttrValue
) {
1268 // Get the nsCSSPropertyID ID for our mapped attribute.
1269 nsCSSPropertyID propertyID
=
1270 nsCSSProps::LookupProperty(nsAutoAtomCString(aMappedAttrName
));
1271 if (propertyID
!= eCSSProperty_UNKNOWN
) {
1272 bool changed
= false; // outparam for ParseProperty.
1273 NS_ConvertUTF16toUTF8
value(aMappedAttrValue
);
1275 auto* doc
= mElement
.OwnerDoc();
1276 changed
= Servo_DeclarationBlock_SetPropertyById(
1277 &EnsureDeclarationBlock(), propertyID
, &value
, false,
1278 &EnsureExtraData(), StyleParsingMode::ALLOW_UNITLESS_LENGTH
,
1279 doc
->GetCompatibilityMode(), doc
->CSSLoader(), StyleCssRuleType::Style
,
1282 // TODO(emilio): If we want to record these from CSSOM more generally, we
1283 // can pass the document use counters down the FFI call. For now manually
1285 if (changed
&& StaticPrefs::layout_css_use_counters_enabled()) {
1286 UseCounter useCounter
= nsCSSProps::UseCounterFor(propertyID
);
1287 MOZ_ASSERT(useCounter
!= eUseCounter_UNKNOWN
);
1288 doc
->SetUseCounter(useCounter
);
1292 MOZ_ASSERT(aMappedAttrName
== nsGkAtoms::lang
,
1293 "Only 'lang' should be unrecognized!");
1294 // CSS parser doesn't know about 'lang', so we need to handle it specially.
1295 if (aMappedAttrName
== nsGkAtoms::lang
) {
1296 propertyID
= eCSSProperty__x_lang
;
1297 RefPtr
<nsAtom
> atom
= NS_Atomize(aMappedAttrValue
);
1298 Servo_DeclarationBlock_SetIdentStringValue(&EnsureDeclarationBlock(),
1303 void MappedAttrParser::TellStyleAlreadyParsedResult(
1304 nsAtom
const* aAtom
, SVGAnimatedLength
const& aLength
) {
1305 nsCSSPropertyID propertyID
=
1306 nsCSSProps::LookupProperty(nsAutoAtomCString(aAtom
));
1307 SVGElement::UpdateDeclarationBlockFromLength(EnsureDeclarationBlock(),
1308 propertyID
, aLength
,
1309 SVGElement::ValToUse::Base
);
1312 void MappedAttrParser::TellStyleAlreadyParsedResult(
1313 const SVGAnimatedPathSegList
& aPath
) {
1314 SVGElement::UpdateDeclarationBlockFromPath(EnsureDeclarationBlock(), aPath
,
1315 SVGElement::ValToUse::Base
);
1318 void MappedAttrParser::TellStyleAlreadyParsedResult(
1319 const SVGAnimatedTransformList
& aTransform
) {
1320 SVGElement::UpdateDeclarationBlockFromTransform(EnsureDeclarationBlock(),
1321 &aTransform
, nullptr,
1322 SVGElement::ValToUse::Base
);
1327 //----------------------------------------------------------------------
1328 // Implementation Helpers:
1330 void SVGElement::UpdateMappedDeclarationBlock() {
1331 MOZ_ASSERT(IsPendingMappedAttributeEvaluation());
1332 MappedAttrParser
mappedAttrParser(*this, mAttrs
.GetMappedDeclarationBlock());
1334 const bool lengthAffectsStyle
=
1335 SVGGeometryProperty::ElementMapsLengthsToStyle(this);
1336 bool sawTransform
= false;
1338 while (BorrowedAttrInfo info
= GetAttrInfoAt(i
++)) {
1339 const nsAttrName
* attrName
= info
.mName
;
1340 if (!attrName
->IsAtom()) {
1344 nsAtom
* nameAtom
= attrName
->Atom();
1345 if (!IsAttributeMapped(nameAtom
)) {
1349 if (nameAtom
== nsGkAtoms::lang
&&
1350 HasAttr(kNameSpaceID_XML
, nsGkAtoms::lang
)) {
1351 // xml:lang has precedence, and will get set via Gecko_GetXMLLangValue().
1355 if (lengthAffectsStyle
) {
1356 auto const* length
= GetAnimatedLength(nameAtom
);
1358 if (length
&& length
->HasBaseVal()) {
1359 // This is an element with geometry property set via SVG attribute,
1360 // and the attribute is already successfully parsed. We want to go
1361 // through the optimized path to tell the style system the result
1362 // directly, rather than let it parse the same thing again.
1363 mappedAttrParser
.TellStyleAlreadyParsedResult(nameAtom
, *length
);
1368 if (nameAtom
== nsGkAtoms::transform
||
1369 nameAtom
== nsGkAtoms::patternTransform
||
1370 nameAtom
== nsGkAtoms::gradientTransform
) {
1371 sawTransform
= true;
1372 const auto* transform
= GetAnimatedTransformList();
1373 MOZ_ASSERT(GetTransformListAttrName() == nameAtom
);
1374 MOZ_ASSERT(transform
);
1375 // We want to go through the optimized path to tell the style system the
1376 // result directly, rather than let it parse the same thing again.
1377 mappedAttrParser
.TellStyleAlreadyParsedResult(*transform
);
1381 if (nameAtom
== nsGkAtoms::d
) {
1382 const auto* path
= GetAnimPathSegList();
1383 // Note: Only SVGPathElement has d attribute.
1386 "SVGPathElement should have the non-null SVGAnimatedPathSegList");
1387 // The attribute should have been already successfully parsed.
1388 // We want to go through the optimized path to tell the style system
1389 // the result directly, rather than let it parse the same thing again.
1390 mappedAttrParser
.TellStyleAlreadyParsedResult(*path
);
1391 // Some other notes:
1392 // The syntax of CSS d property is different from SVG d attribute.
1393 // 1. CSS d proeprty accepts: none | path(<quoted string>);
1394 // 2. SVG d attribtue accepts: none | <string>
1395 // So we cannot use css parser to parse the SVG d attribute directly.
1396 // Besides, |mAttrs.AttrAt(i)| removes the quotes already, so the svg path
1397 // in |mAttrs.AttrAt(i)| would be something like `M0,0L1,1z` without the
1398 // quotes. So css tokenizer cannot recognize this as a quoted string, and
1399 // so svg_path::SVGPathData::parse() doesn't work for this. Fortunately,
1400 // we still can rely on the parsed result from
1401 // SVGElement::ParseAttribute() for d attribute.
1406 info
.mValue
->ToString(value
);
1407 mappedAttrParser
.ParseMappedAttrValue(nameAtom
, value
);
1410 // We need to map the SVG view's transform if we haven't mapped it already.
1411 if (NodeInfo()->NameAtom() == nsGkAtoms::svg
&& !sawTransform
) {
1412 if (const auto* transform
= GetAnimatedTransformList()) {
1413 mappedAttrParser
.TellStyleAlreadyParsedResult(*transform
);
1417 mAttrs
.SetMappedDeclarationBlock(mappedAttrParser
.TakeDeclarationBlock());
1421 * Helper methods for the type-specific WillChangeXXX methods.
1423 * This method sends out appropriate pre-change notifications so that selector
1424 * restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop
1425 * matching) work, and it returns an nsAttrValue that _may_ contain the
1426 * attribute's pre-change value.
1428 * The nsAttrValue returned by this method depends on whether there are
1429 * mutation event listeners listening for changes to this element's attributes.
1430 * If not, then the object returned is empty. If there are, then the
1431 * nsAttrValue returned contains a serialized copy of the attribute's value
1432 * prior to the change, and this object should be passed to the corresponding
1433 * DidChangeXXX method call (assuming a WillChangeXXX call is required for the
1434 * SVG type - see comment below). This is necessary so that the 'prevValue'
1435 * property of the mutation event that is dispatched will correctly contain the
1438 * The reason we need to serialize the old value if there are mutation
1439 * event listeners is because the underlying nsAttrValue for the attribute
1440 * points directly to a parsed representation of the attribute (e.g. an
1441 * SVGAnimatedLengthList*) that is a member of the SVG element. That object
1442 * will have changed by the time DidChangeXXX has been called, so without the
1443 * serialization of the old attribute value that we provide, DidChangeXXX
1444 * would have no way to get the old value to pass to SetAttrAndNotify.
1446 * We only return the old value when there are mutation event listeners because
1447 * it's not needed otherwise, and because it's expensive to serialize the old
1448 * value. This is especially true for list type attributes, which may be built
1449 * up via the SVG DOM resulting in a large number of Will/DidModifyXXX calls
1450 * before the script finally finishes setting the attribute.
1452 * Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check
1453 * and filter out redundant changes. Before calling WillChangeXXX, the caller
1454 * should check whether the new and old values are actually the same, and skip
1455 * calling Will/DidChangeXXX if they are.
1457 * Also note that not all SVG types use this scheme. For types that can be
1458 * represented by an nsAttrValue without pointing back to an SVG object (e.g.
1459 * enums, booleans, integers) we can simply use SetParsedAttr which will do all
1460 * of the above for us. For such types there is no matching WillChangeXXX
1461 * method, only DidChangeXXX which calls SetParsedAttr.
1463 nsAttrValue
SVGElement::WillChangeValue(
1464 nsAtom
* aName
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1465 // We need an empty attr value:
1466 // a) to pass to BeforeSetAttr when GetParsedAttr returns nullptr
1467 // b) to store the old value in the case we have mutation listeners
1469 // We can use the same value for both purposes, because if GetParsedAttr
1470 // returns non-null its return value is what will get passed to BeforeSetAttr,
1471 // not matter what our mutation listener situation is.
1473 // Also, we should be careful to always return this value to benefit from
1474 // return value optimization.
1475 nsAttrValue emptyOrOldAttrValue
;
1476 const nsAttrValue
* attrValue
= GetParsedAttr(aName
);
1478 // We only need to set the old value if we have listeners since otherwise it
1480 if (attrValue
&& nsContentUtils::WantMutationEvents(
1481 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this)) {
1482 emptyOrOldAttrValue
.SetToSerialized(*attrValue
);
1486 attrValue
? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION
)
1487 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION
);
1488 MutationObservers::NotifyAttributeWillChange(this, kNameSpaceID_None
, aName
,
1491 // This is not strictly correct--the attribute value parameter for
1492 // BeforeSetAttr should reflect the value that *will* be set but that implies
1493 // allocating, e.g. an extra SVGAnimatedLength, and isn't necessary at the
1494 // moment since no SVG elements overload BeforeSetAttr. For now we just pass
1495 // the current value.
1496 const nsAttrValue
* value
= attrValue
? attrValue
: &emptyOrOldAttrValue
;
1497 BeforeSetAttr(kNameSpaceID_None
, aName
, value
, kNotifyDocumentObservers
);
1498 return emptyOrOldAttrValue
;
1502 * Helper methods for the type-specific DidChangeXXX methods.
1504 * aEmptyOrOldValue will normally be the object returned from the corresponding
1505 * WillChangeXXX call. This is because:
1506 * a) WillChangeXXX will ensure the object is set when we have mutation
1508 * b) WillChangeXXX will ensure the object represents a serialized version of
1509 * the old attribute value so that the value doesn't change when the
1510 * underlying SVG type is updated.
1512 * aNewValue is replaced with the old value.
1514 void SVGElement::DidChangeValue(nsAtom
* aName
,
1515 const nsAttrValue
& aEmptyOrOldValue
,
1516 nsAttrValue
& aNewValue
,
1517 const mozAutoDocUpdate
& aProofOfUpdate
) {
1518 bool hasListeners
= nsContentUtils::WantMutationEvents(
1519 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this);
1521 HasAttr(aName
) ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION
)
1522 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION
);
1524 // XXX Really, the fourth argument to SetAttrAndNotify should be null if
1525 // aEmptyOrOldValue does not represent the actual previous value of the
1526 // attribute, but currently SVG elements do not even use the old attribute
1527 // value in |AfterSetAttr|, so this should be ok.
1528 SetAttrAndNotify(kNameSpaceID_None
, aName
, nullptr, &aEmptyOrOldValue
,
1529 aNewValue
, nullptr, modType
, hasListeners
,
1530 kNotifyDocumentObservers
, kCallAfterSetAttr
,
1531 GetComposedDoc(), aProofOfUpdate
);
1534 void SVGElement::MaybeSerializeAttrBeforeRemoval(nsAtom
* aName
, bool aNotify
) {
1535 if (!aNotify
|| !nsContentUtils::WantMutationEvents(
1536 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this)) {
1540 const nsAttrValue
* attrValue
= mAttrs
.GetAttr(aName
);
1541 if (!attrValue
) return;
1543 nsAutoString serializedValue
;
1544 attrValue
->ToString(serializedValue
);
1545 nsAttrValue
oldAttrValue(serializedValue
);
1547 mAttrs
.SetAndSwapAttr(aName
, oldAttrValue
, &oldValueSet
);
1550 nsAtom
* SVGElement::GetEventNameForAttr(nsAtom
* aAttr
) {
1551 if (IsSVGElement(nsGkAtoms::svg
)) {
1552 if (aAttr
== nsGkAtoms::onload
) return nsGkAtoms::onSVGLoad
;
1553 if (aAttr
== nsGkAtoms::onscroll
) return nsGkAtoms::onSVGScroll
;
1555 if (aAttr
== nsGkAtoms::onbegin
) return nsGkAtoms::onbeginEvent
;
1556 if (aAttr
== nsGkAtoms::onrepeat
) return nsGkAtoms::onrepeatEvent
;
1557 if (aAttr
== nsGkAtoms::onend
) return nsGkAtoms::onendEvent
;
1559 return SVGElementBase::GetEventNameForAttr(aAttr
);
1562 SVGViewportElement
* SVGElement::GetCtx() const {
1563 return SVGContentUtils::GetNearestViewportElement(this);
1567 gfxMatrix
SVGElement::ChildToUserSpaceTransform() const { return {}; }
1569 SVGElement::LengthAttributesInfo
SVGElement::GetLengthInfo() {
1570 return LengthAttributesInfo(nullptr, nullptr, 0);
1573 void SVGElement::SetLength(nsAtom
* aName
, const SVGAnimatedLength
& aLength
) {
1574 LengthAttributesInfo lengthInfo
= GetLengthInfo();
1576 for (uint32_t i
= 0; i
< lengthInfo
.mCount
; i
++) {
1577 if (aName
== lengthInfo
.mInfos
[i
].mName
) {
1578 lengthInfo
.mValues
[i
] = aLength
;
1579 DidAnimateLength(i
);
1583 MOZ_ASSERT(false, "no length found to set");
1586 nsAttrValue
SVGElement::WillChangeLength(
1587 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1588 return WillChangeValue(GetLengthInfo().mInfos
[aAttrEnum
].mName
,
1592 void SVGElement::DidChangeLength(uint8_t aAttrEnum
,
1593 const nsAttrValue
& aEmptyOrOldValue
,
1594 const mozAutoDocUpdate
& aProofOfUpdate
) {
1595 LengthAttributesInfo info
= GetLengthInfo();
1597 NS_ASSERTION(info
.mCount
> 0,
1598 "DidChangeLength on element with no length attribs");
1599 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1601 nsAttrValue newValue
;
1602 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
1604 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1608 void SVGElement::DidAnimateLength(uint8_t aAttrEnum
) {
1609 // We need to do this here. Normally the SMIL restyle would also cause us to
1610 // do this from DidSetComputedStyle, but we don't have that guarantee if our
1611 // frame gets reconstructed.
1612 ClearAnyCachedPath();
1614 if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) {
1615 nsCSSPropertyID propId
=
1616 SVGGeometryProperty::AttrEnumToCSSPropId(this, aAttrEnum
);
1618 // We don't map use element width/height currently. We can remove this
1620 if (propId
!= eCSSProperty_UNKNOWN
) {
1621 auto lengthInfo
= GetLengthInfo();
1622 if (lengthInfo
.mValues
[aAttrEnum
].IsAnimated()) {
1623 SMILOverrideStyle()->SetSMILValue(propId
,
1624 lengthInfo
.mValues
[aAttrEnum
]);
1626 SMILOverrideStyle()->ClearSMILValue(propId
);
1631 auto info
= GetLengthInfo();
1632 DidAnimateAttribute(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
);
1635 SVGAnimatedLength
* SVGElement::GetAnimatedLength(uint8_t aAttrEnum
) {
1636 LengthAttributesInfo info
= GetLengthInfo();
1637 if (aAttrEnum
< info
.mCount
) {
1638 return &info
.mValues
[aAttrEnum
];
1640 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1644 SVGAnimatedLength
* SVGElement::GetAnimatedLength(const nsAtom
* aAttrName
) {
1645 LengthAttributesInfo lengthInfo
= GetLengthInfo();
1647 for (uint32_t i
= 0; i
< lengthInfo
.mCount
; i
++) {
1648 if (aAttrName
== lengthInfo
.mInfos
[i
].mName
) {
1649 return &lengthInfo
.mValues
[i
];
1655 void SVGElement::GetAnimatedLengthValues(float* aFirst
, ...) {
1656 LengthAttributesInfo info
= GetLengthInfo();
1658 NS_ASSERTION(info
.mCount
> 0,
1659 "GetAnimatedLengthValues on element with no length attribs");
1661 SVGElementMetrics
metrics(this);
1667 va_start(args
, aFirst
);
1669 while (f
&& i
< info
.mCount
) {
1670 *f
= info
.mValues
[i
++].GetAnimValueWithZoom(metrics
);
1671 f
= va_arg(args
, float*);
1677 SVGElement::LengthListAttributesInfo
SVGElement::GetLengthListInfo() {
1678 return LengthListAttributesInfo(nullptr, nullptr, 0);
1681 nsAttrValue
SVGElement::WillChangeLengthList(
1682 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1683 return WillChangeValue(GetLengthListInfo().mInfos
[aAttrEnum
].mName
,
1687 void SVGElement::DidChangeLengthList(uint8_t aAttrEnum
,
1688 const nsAttrValue
& aEmptyOrOldValue
,
1689 const mozAutoDocUpdate
& aProofOfUpdate
) {
1690 LengthListAttributesInfo info
= GetLengthListInfo();
1692 NS_ASSERTION(info
.mCount
> 0,
1693 "DidChangeLengthList on element with no length list attribs");
1694 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1696 nsAttrValue newValue
;
1697 newValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1699 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1703 void SVGElement::GetAnimatedLengthListValues(SVGUserUnitList
* aFirst
, ...) {
1704 LengthListAttributesInfo info
= GetLengthListInfo();
1708 "GetAnimatedLengthListValues on element with no length list attribs");
1710 SVGUserUnitList
* list
= aFirst
;
1714 va_start(args
, aFirst
);
1716 while (list
&& i
< info
.mCount
) {
1717 list
->Init(&(info
.mValues
[i
].GetAnimValue()), this, info
.mInfos
[i
].mAxis
);
1719 list
= va_arg(args
, SVGUserUnitList
*);
1725 SVGAnimatedLengthList
* SVGElement::GetAnimatedLengthList(uint8_t aAttrEnum
) {
1726 LengthListAttributesInfo info
= GetLengthListInfo();
1727 if (aAttrEnum
< info
.mCount
) {
1728 return &(info
.mValues
[aAttrEnum
]);
1730 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1734 SVGElement::NumberListAttributesInfo
SVGElement::GetNumberListInfo() {
1735 return NumberListAttributesInfo(nullptr, nullptr, 0);
1738 nsAttrValue
SVGElement::WillChangeNumberList(
1739 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1740 return WillChangeValue(GetNumberListInfo().mInfos
[aAttrEnum
].mName
,
1744 void SVGElement::DidChangeNumberList(uint8_t aAttrEnum
,
1745 const nsAttrValue
& aEmptyOrOldValue
,
1746 const mozAutoDocUpdate
& aProofOfUpdate
) {
1747 NumberListAttributesInfo info
= GetNumberListInfo();
1749 MOZ_ASSERT(info
.mCount
> 0,
1750 "DidChangeNumberList on element with no number list attribs");
1751 MOZ_ASSERT(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1753 nsAttrValue newValue
;
1754 newValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1756 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1760 SVGAnimatedNumberList
* SVGElement::GetAnimatedNumberList(uint8_t aAttrEnum
) {
1761 NumberListAttributesInfo info
= GetNumberListInfo();
1762 if (aAttrEnum
< info
.mCount
) {
1763 return &(info
.mValues
[aAttrEnum
]);
1765 MOZ_ASSERT(false, "Bad attrEnum");
1769 SVGAnimatedNumberList
* SVGElement::GetAnimatedNumberList(nsAtom
* aAttrName
) {
1770 NumberListAttributesInfo info
= GetNumberListInfo();
1771 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
1772 if (aAttrName
== info
.mInfos
[i
].mName
) {
1773 return &info
.mValues
[i
];
1776 MOZ_ASSERT(false, "Bad caller");
1780 nsAttrValue
SVGElement::WillChangePointList(
1781 const mozAutoDocUpdate
& aProofOfUpdate
) {
1782 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1783 return WillChangeValue(GetPointListAttrName(), aProofOfUpdate
);
1786 void SVGElement::DidChangePointList(const nsAttrValue
& aEmptyOrOldValue
,
1787 const mozAutoDocUpdate
& aProofOfUpdate
) {
1788 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1790 nsAttrValue newValue
;
1791 newValue
.SetTo(GetAnimatedPointList()->GetBaseValue(), nullptr);
1793 DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue
, newValue
,
1797 void SVGElement::DidAnimatePointList() {
1798 MOZ_ASSERT(GetPointListAttrName(), "Animating non-existent path data?");
1800 ClearAnyCachedPath();
1802 DidAnimateAttribute(kNameSpaceID_None
, GetPointListAttrName());
1805 nsAttrValue
SVGElement::WillChangePathSegList(
1806 const mozAutoDocUpdate
& aProofOfUpdate
) {
1807 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1808 return WillChangeValue(GetPathDataAttrName(), aProofOfUpdate
);
1811 void SVGElement::DidChangePathSegList(const nsAttrValue
& aEmptyOrOldValue
,
1812 const mozAutoDocUpdate
& aProofOfUpdate
) {
1813 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1815 nsAttrValue newValue
;
1816 newValue
.SetTo(GetAnimPathSegList()->GetBaseValue(), nullptr);
1818 DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue
, newValue
,
1822 void SVGElement::DidAnimatePathSegList() {
1823 nsStaticAtom
* name
= GetPathDataAttrName();
1824 MOZ_ASSERT(name
, "Animating non-existent path data?");
1826 ClearAnyCachedPath();
1828 // Notify style we have to update the d property because of SMIL animation.
1829 if (name
== nsGkAtoms::d
) {
1830 auto* animPathSegList
= GetAnimPathSegList();
1831 if (animPathSegList
->IsAnimating()) {
1832 SMILOverrideStyle()->SetSMILValue(eCSSProperty_d
, *animPathSegList
);
1834 SMILOverrideStyle()->ClearSMILValue(eCSSProperty_d
);
1838 DidAnimateAttribute(kNameSpaceID_None
, name
);
1841 SVGElement::NumberAttributesInfo
SVGElement::GetNumberInfo() {
1842 return NumberAttributesInfo(nullptr, nullptr, 0);
1845 void SVGElement::DidChangeNumber(uint8_t aAttrEnum
) {
1846 NumberAttributesInfo info
= GetNumberInfo();
1848 NS_ASSERTION(info
.mCount
> 0,
1849 "DidChangeNumber on element with no number attribs");
1850 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1852 nsAttrValue attrValue
;
1853 attrValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1855 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
1859 void SVGElement::GetAnimatedNumberValues(float* aFirst
, ...) {
1860 NumberAttributesInfo info
= GetNumberInfo();
1862 NS_ASSERTION(info
.mCount
> 0,
1863 "GetAnimatedNumberValues on element with no number attribs");
1869 va_start(args
, aFirst
);
1871 while (f
&& i
< info
.mCount
) {
1872 *f
= info
.mValues
[i
++].GetAnimValue();
1873 f
= va_arg(args
, float*);
1878 SVGElement::NumberPairAttributesInfo
SVGElement::GetNumberPairInfo() {
1879 return NumberPairAttributesInfo(nullptr, nullptr, 0);
1882 nsAttrValue
SVGElement::WillChangeNumberPair(uint8_t aAttrEnum
) {
1883 mozAutoDocUpdate
updateBatch(GetComposedDoc(), kDontNotifyDocumentObservers
);
1884 return WillChangeValue(GetNumberPairInfo().mInfos
[aAttrEnum
].mName
,
1888 void SVGElement::DidChangeNumberPair(uint8_t aAttrEnum
,
1889 const nsAttrValue
& aEmptyOrOldValue
) {
1890 NumberPairAttributesInfo info
= GetNumberPairInfo();
1892 NS_ASSERTION(info
.mCount
> 0,
1893 "DidChangePairNumber on element with no number pair attribs");
1894 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1896 nsAttrValue newValue
;
1897 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
1899 mozAutoDocUpdate
updateBatch(GetComposedDoc(), kNotifyDocumentObservers
);
1900 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1904 SVGElement::IntegerAttributesInfo
SVGElement::GetIntegerInfo() {
1905 return IntegerAttributesInfo(nullptr, nullptr, 0);
1908 void SVGElement::DidChangeInteger(uint8_t aAttrEnum
) {
1909 IntegerAttributesInfo info
= GetIntegerInfo();
1910 NS_ASSERTION(info
.mCount
> 0,
1911 "DidChangeInteger on element with no integer attribs");
1912 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1914 nsAttrValue attrValue
;
1915 attrValue
.SetTo(info
.mValues
[aAttrEnum
].GetBaseValue(), nullptr);
1917 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
1921 void SVGElement::GetAnimatedIntegerValues(int32_t* aFirst
, ...) {
1922 IntegerAttributesInfo info
= GetIntegerInfo();
1924 NS_ASSERTION(info
.mCount
> 0,
1925 "GetAnimatedIntegerValues on element with no integer attribs");
1927 int32_t* n
= aFirst
;
1931 va_start(args
, aFirst
);
1933 while (n
&& i
< info
.mCount
) {
1934 *n
= info
.mValues
[i
++].GetAnimValue();
1935 n
= va_arg(args
, int32_t*);
1940 SVGElement::IntegerPairAttributesInfo
SVGElement::GetIntegerPairInfo() {
1941 return IntegerPairAttributesInfo(nullptr, nullptr, 0);
1944 nsAttrValue
SVGElement::WillChangeIntegerPair(
1945 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1946 return WillChangeValue(GetIntegerPairInfo().mInfos
[aAttrEnum
].mName
,
1950 void SVGElement::DidChangeIntegerPair(uint8_t aAttrEnum
,
1951 const nsAttrValue
& aEmptyOrOldValue
,
1952 const mozAutoDocUpdate
& aProofOfUpdate
) {
1953 IntegerPairAttributesInfo info
= GetIntegerPairInfo();
1955 NS_ASSERTION(info
.mCount
> 0,
1956 "DidChangeIntegerPair on element with no integer pair attribs");
1957 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1959 nsAttrValue newValue
;
1960 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
1962 DidChangeValue(info
.mInfos
[aAttrEnum
].mName
, aEmptyOrOldValue
, newValue
,
1966 SVGElement::BooleanAttributesInfo
SVGElement::GetBooleanInfo() {
1967 return BooleanAttributesInfo(nullptr, nullptr, 0);
1970 void SVGElement::DidChangeBoolean(uint8_t aAttrEnum
) {
1971 BooleanAttributesInfo info
= GetBooleanInfo();
1973 NS_ASSERTION(info
.mCount
> 0,
1974 "DidChangeBoolean on element with no boolean attribs");
1975 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1977 nsAttrValue
attrValue(info
.mValues
[aAttrEnum
].GetBaseValueAtom());
1978 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
1982 SVGElement::EnumAttributesInfo
SVGElement::GetEnumInfo() {
1983 return EnumAttributesInfo(nullptr, nullptr, 0);
1986 void SVGElement::DidChangeEnum(uint8_t aAttrEnum
) {
1987 EnumAttributesInfo info
= GetEnumInfo();
1989 NS_ASSERTION(info
.mCount
> 0,
1990 "DidChangeEnum on element with no enum attribs");
1991 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
1993 nsAttrValue
attrValue(info
.mValues
[aAttrEnum
].GetBaseValueAtom(this));
1994 SetParsedAttr(kNameSpaceID_None
, info
.mInfos
[aAttrEnum
].mName
, nullptr,
1998 SVGAnimatedOrient
* SVGElement::GetAnimatedOrient() { return nullptr; }
2000 nsAttrValue
SVGElement::WillChangeOrient(
2001 const mozAutoDocUpdate
& aProofOfUpdate
) {
2002 return WillChangeValue(nsGkAtoms::orient
, aProofOfUpdate
);
2005 void SVGElement::DidChangeOrient(const nsAttrValue
& aEmptyOrOldValue
,
2006 const mozAutoDocUpdate
& aProofOfUpdate
) {
2007 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
2009 NS_ASSERTION(orient
, "DidChangeOrient on element with no orient attrib");
2011 nsAttrValue newValue
;
2012 newValue
.SetTo(*orient
, nullptr);
2014 DidChangeValue(nsGkAtoms::orient
, aEmptyOrOldValue
, newValue
, aProofOfUpdate
);
2017 SVGAnimatedViewBox
* SVGElement::GetAnimatedViewBox() { return nullptr; }
2019 nsAttrValue
SVGElement::WillChangeViewBox(
2020 const mozAutoDocUpdate
& aProofOfUpdate
) {
2021 return WillChangeValue(nsGkAtoms::viewBox
, aProofOfUpdate
);
2024 void SVGElement::DidChangeViewBox(const nsAttrValue
& aEmptyOrOldValue
,
2025 const mozAutoDocUpdate
& aProofOfUpdate
) {
2026 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
2028 NS_ASSERTION(viewBox
, "DidChangeViewBox on element with no viewBox attrib");
2030 nsAttrValue newValue
;
2031 newValue
.SetTo(*viewBox
, nullptr);
2033 DidChangeValue(nsGkAtoms::viewBox
, aEmptyOrOldValue
, newValue
,
2037 SVGAnimatedPreserveAspectRatio
* SVGElement::GetAnimatedPreserveAspectRatio() {
2041 nsAttrValue
SVGElement::WillChangePreserveAspectRatio(
2042 const mozAutoDocUpdate
& aProofOfUpdate
) {
2043 return WillChangeValue(nsGkAtoms::preserveAspectRatio
, aProofOfUpdate
);
2046 void SVGElement::DidChangePreserveAspectRatio(
2047 const nsAttrValue
& aEmptyOrOldValue
,
2048 const mozAutoDocUpdate
& aProofOfUpdate
) {
2049 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
2050 GetAnimatedPreserveAspectRatio();
2052 NS_ASSERTION(preserveAspectRatio
,
2053 "DidChangePreserveAspectRatio on element with no "
2054 "preserveAspectRatio attrib");
2056 nsAttrValue newValue
;
2057 newValue
.SetTo(*preserveAspectRatio
, nullptr);
2059 DidChangeValue(nsGkAtoms::preserveAspectRatio
, aEmptyOrOldValue
, newValue
,
2063 nsAttrValue
SVGElement::WillChangeTransformList(
2064 const mozAutoDocUpdate
& aProofOfUpdate
) {
2065 return WillChangeValue(GetTransformListAttrName(), aProofOfUpdate
);
2068 void SVGElement::DidChangeTransformList(
2069 const nsAttrValue
& aEmptyOrOldValue
,
2070 const mozAutoDocUpdate
& aProofOfUpdate
) {
2071 MOZ_ASSERT(GetTransformListAttrName(),
2072 "Changing non-existent transform list?");
2074 // The transform attribute is being set, so we must ensure that the
2075 // SVGAnimatedTransformList is/has been allocated:
2076 nsAttrValue newValue
;
2077 newValue
.SetTo(GetAnimatedTransformList(DO_ALLOCATE
)->GetBaseValue(),
2080 DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue
, newValue
,
2084 void SVGElement::DidAnimateTransformList(int32_t aModType
) {
2085 MOZ_ASSERT(GetTransformListAttrName(),
2086 "Animating non-existent transform data?");
2087 const auto* animTransformList
= GetAnimatedTransformList();
2088 const auto* animateMotion
= GetAnimateMotionTransform();
2089 if (animateMotion
||
2090 (animTransformList
&& animTransformList
->IsAnimating())) {
2091 SMILOverrideStyle()->SetSMILValue(eCSSProperty_transform
, animTransformList
,
2094 SMILOverrideStyle()->ClearSMILValue(eCSSProperty_transform
);
2098 SVGElement::StringAttributesInfo
SVGElement::GetStringInfo() {
2099 return StringAttributesInfo(nullptr, nullptr, 0);
2102 void SVGElement::GetStringBaseValue(uint8_t aAttrEnum
,
2103 nsAString
& aResult
) const {
2104 SVGElement::StringAttributesInfo info
=
2105 const_cast<SVGElement
*>(this)->GetStringInfo();
2107 NS_ASSERTION(info
.mCount
> 0,
2108 "GetBaseValue on element with no string attribs");
2110 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
2112 GetAttr(info
.mInfos
[aAttrEnum
].mNamespaceID
, info
.mInfos
[aAttrEnum
].mName
,
2116 void SVGElement::SetStringBaseValue(uint8_t aAttrEnum
,
2117 const nsAString
& aValue
) {
2118 SVGElement::StringAttributesInfo info
= GetStringInfo();
2120 NS_ASSERTION(info
.mCount
> 0,
2121 "SetBaseValue on element with no string attribs");
2123 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
2125 SetAttr(info
.mInfos
[aAttrEnum
].mNamespaceID
, info
.mInfos
[aAttrEnum
].mName
,
2129 SVGElement::StringListAttributesInfo
SVGElement::GetStringListInfo() {
2130 return StringListAttributesInfo(nullptr, nullptr, 0);
2133 nsAttrValue
SVGElement::WillChangeStringList(
2134 bool aIsConditionalProcessingAttribute
, uint8_t aAttrEnum
,
2135 const mozAutoDocUpdate
& aProofOfUpdate
) {
2137 if (aIsConditionalProcessingAttribute
) {
2138 nsCOMPtr
<SVGTests
> tests(do_QueryInterface(this));
2139 name
= tests
->GetAttrName(aAttrEnum
);
2141 name
= GetStringListInfo().mInfos
[aAttrEnum
].mName
;
2143 return WillChangeValue(name
, aProofOfUpdate
);
2146 void SVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute
,
2148 const nsAttrValue
& aEmptyOrOldValue
,
2149 const mozAutoDocUpdate
& aProofOfUpdate
) {
2151 nsAttrValue newValue
;
2152 nsCOMPtr
<SVGTests
> tests
;
2154 if (aIsConditionalProcessingAttribute
) {
2155 tests
= do_QueryObject(this);
2156 name
= tests
->GetAttrName(aAttrEnum
);
2157 tests
->GetAttrValue(aAttrEnum
, newValue
);
2159 StringListAttributesInfo info
= GetStringListInfo();
2161 NS_ASSERTION(info
.mCount
> 0,
2162 "DidChangeStringList on element with no string list attribs");
2163 NS_ASSERTION(aAttrEnum
< info
.mCount
, "aAttrEnum out of range");
2165 name
= info
.mInfos
[aAttrEnum
].mName
;
2166 newValue
.SetTo(info
.mValues
[aAttrEnum
], nullptr);
2169 DidChangeValue(name
, aEmptyOrOldValue
, newValue
, aProofOfUpdate
);
2171 if (aIsConditionalProcessingAttribute
) {
2172 tests
->MaybeInvalidate();
2176 void SVGElement::DidAnimateAttribute(int32_t aNameSpaceID
, nsAtom
* aAttribute
) {
2177 if (auto* frame
= GetPrimaryFrame()) {
2178 frame
->AttributeChanged(aNameSpaceID
, aAttribute
,
2179 MutationEvent_Binding::MODIFICATION
);
2180 SVGObserverUtils::InvalidateRenderingObservers(frame
);
2183 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
2186 nsresult
SVGElement::ReportAttributeParseFailure(Document
* aDocument
,
2188 const nsAString
& aValue
) {
2189 AutoTArray
<nsString
, 2> strings
;
2190 strings
.AppendElement(nsDependentAtomString(aAttribute
));
2191 strings
.AppendElement(aValue
);
2192 return SVGContentUtils::ReportToConsole(aDocument
, "AttributeParseWarning",
2196 UniquePtr
<SMILAttr
> SVGElement::GetAnimatedAttr(int32_t aNamespaceID
,
2198 if (aNamespaceID
== kNameSpaceID_None
) {
2200 if (GetTransformListAttrName() == aName
) {
2201 // The transform attribute is being animated, so we must ensure that the
2202 // SVGAnimatedTransformList is/has been allocated:
2203 return GetAnimatedTransformList(DO_ALLOCATE
)->ToSMILAttr(this);
2206 // Motion (fake 'attribute' for animateMotion)
2207 if (aName
== nsGkAtoms::mozAnimateMotionDummyAttr
) {
2208 return MakeUnique
<SVGMotionSMILAttr
>(this);
2212 LengthAttributesInfo info
= GetLengthInfo();
2213 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2214 if (aName
== info
.mInfos
[i
].mName
) {
2215 return info
.mValues
[i
].ToSMILAttr(this);
2221 NumberAttributesInfo info
= GetNumberInfo();
2222 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2223 if (aName
== info
.mInfos
[i
].mName
) {
2224 return info
.mValues
[i
].ToSMILAttr(this);
2231 NumberPairAttributesInfo info
= GetNumberPairInfo();
2232 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2233 if (aName
== info
.mInfos
[i
].mName
) {
2234 return info
.mValues
[i
].ToSMILAttr(this);
2241 IntegerAttributesInfo info
= GetIntegerInfo();
2242 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2243 if (aName
== info
.mInfos
[i
].mName
) {
2244 return info
.mValues
[i
].ToSMILAttr(this);
2251 IntegerPairAttributesInfo info
= GetIntegerPairInfo();
2252 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2253 if (aName
== info
.mInfos
[i
].mName
) {
2254 return info
.mValues
[i
].ToSMILAttr(this);
2261 EnumAttributesInfo info
= GetEnumInfo();
2262 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2263 if (aName
== info
.mInfos
[i
].mName
) {
2264 return info
.mValues
[i
].ToSMILAttr(this);
2271 BooleanAttributesInfo info
= GetBooleanInfo();
2272 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2273 if (aName
== info
.mInfos
[i
].mName
) {
2274 return info
.mValues
[i
].ToSMILAttr(this);
2280 if (aName
== nsGkAtoms::orient
) {
2281 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
2282 return orient
? orient
->ToSMILAttr(this) : nullptr;
2286 if (aName
== nsGkAtoms::viewBox
) {
2287 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
2288 return viewBox
? viewBox
->ToSMILAttr(this) : nullptr;
2291 // preserveAspectRatio:
2292 if (aName
== nsGkAtoms::preserveAspectRatio
) {
2293 SVGAnimatedPreserveAspectRatio
* preserveAspectRatio
=
2294 GetAnimatedPreserveAspectRatio();
2295 return preserveAspectRatio
? preserveAspectRatio
->ToSMILAttr(this)
2301 NumberListAttributesInfo info
= GetNumberListInfo();
2302 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2303 if (aName
== info
.mInfos
[i
].mName
) {
2304 MOZ_ASSERT(i
<= UCHAR_MAX
, "Too many attributes");
2305 return info
.mValues
[i
].ToSMILAttr(this, uint8_t(i
));
2312 LengthListAttributesInfo info
= GetLengthListInfo();
2313 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2314 if (aName
== info
.mInfos
[i
].mName
) {
2315 MOZ_ASSERT(i
<= UCHAR_MAX
, "Too many attributes");
2316 return info
.mValues
[i
].ToSMILAttr(this, uint8_t(i
),
2317 info
.mInfos
[i
].mAxis
,
2318 info
.mInfos
[i
].mCouldZeroPadList
);
2325 if (GetPointListAttrName() == aName
) {
2326 SVGAnimatedPointList
* pointList
= GetAnimatedPointList();
2328 return pointList
->ToSMILAttr(this);
2335 if (GetPathDataAttrName() == aName
) {
2336 SVGAnimatedPathSegList
* segList
= GetAnimPathSegList();
2338 return segList
->ToSMILAttr(this);
2343 if (aName
== nsGkAtoms::_class
) {
2344 return mClassAttribute
.ToSMILAttr(this);
2350 StringAttributesInfo info
= GetStringInfo();
2351 for (uint32_t i
= 0; i
< info
.mCount
; i
++) {
2352 if (aNamespaceID
== info
.mInfos
[i
].mNamespaceID
&&
2353 aName
== info
.mInfos
[i
].mName
) {
2354 return info
.mValues
[i
].ToSMILAttr(this);
2362 void SVGElement::AnimationNeedsResample() {
2363 Document
* doc
= GetComposedDoc();
2364 if (doc
&& doc
->HasAnimationController()) {
2365 doc
->GetAnimationController()->SetResampleNeeded();
2369 void SVGElement::FlushAnimations() {
2370 Document
* doc
= GetComposedDoc();
2371 if (doc
&& doc
->HasAnimationController()) {
2372 doc
->GetAnimationController()->FlushResampleRequests();
2376 void SVGElement::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
2377 size_t* aNodeSize
) const {
2378 Element::AddSizeOfExcludingThis(aSizes
, aNodeSize
);
2381 } // namespace mozilla::dom