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/. */
6 
7 #include "mozilla/dom/SVGElement.h"
8 
9 #include "mozilla/dom/MutationEventBinding.h"
10 #include "mozilla/dom/MutationObservers.h"
11 #include "mozilla/dom/CSSRuleBinding.h"
12 #include "mozilla/dom/SVGElementBinding.h"
13 #include "mozilla/dom/SVGGeometryElement.h"
14 #include "mozilla/dom/SVGLengthBinding.h"
15 #include "mozilla/dom/SVGSVGElement.h"
16 #include "mozilla/dom/SVGTests.h"
17 #include "mozilla/dom/SVGUnitTypesBinding.h"
18 #include "mozilla/dom/Element.h"
19 
20 #include "mozilla/ArrayUtils.h"
21 #include "mozilla/DebugOnly.h"
22 #include "mozilla/DeclarationBlock.h"
23 #include "mozilla/EventListenerManager.h"
24 #include "mozilla/InternalMutationEvent.h"
25 #include "mozilla/PresShell.h"
26 #include "mozilla/RestyleManager.h"
27 #include "mozilla/SMILAnimationController.h"
28 #include "mozilla/StaticPrefs_layout.h"
29 #include "mozilla/SVGContentUtils.h"
30 #include "mozilla/Unused.h"
31 
32 #include "mozAutoDocUpdate.h"
33 #include "nsAttrValueOrString.h"
34 #include "nsCSSProps.h"
35 #include "nsCSSValue.h"
36 #include "nsContentUtils.h"
37 #include "nsDOMCSSAttrDeclaration.h"
38 #include "nsICSSDeclaration.h"
39 #include "nsIContentInlines.h"
40 #include "mozilla/dom/Document.h"
41 #include "nsError.h"
42 #include "nsGkAtoms.h"
43 #include "nsIFrame.h"
44 #include "nsQueryObject.h"
45 #include "nsLayoutUtils.h"
46 #include "SVGAnimatedNumberList.h"
47 #include "SVGAnimatedLengthList.h"
48 #include "SVGAnimatedPointList.h"
49 #include "SVGAnimatedPathSegList.h"
50 #include "SVGAnimatedTransformList.h"
51 #include "SVGAnimatedBoolean.h"
52 #include "SVGAnimatedEnumeration.h"
53 #include "SVGAnimatedInteger.h"
54 #include "SVGAnimatedIntegerPair.h"
55 #include "SVGAnimatedLength.h"
56 #include "SVGAnimatedNumber.h"
57 #include "SVGAnimatedNumberPair.h"
58 #include "SVGAnimatedOrient.h"
59 #include "SVGAnimatedString.h"
60 #include "SVGAnimatedViewBox.h"
61 #include "SVGGeometryProperty.h"
62 #include "SVGMotionSMILAttr.h"
63 #include <stdarg.h>
64 
65 // This is needed to ensure correct handling of calls to the
66 // vararg-list methods in this file:
67 //   SVGElement::GetAnimated{Length,Number,Integer}Values
68 // See bug 547964 for details:
69 static_assert(sizeof(void*) == sizeof(nullptr),
70               "nullptr should be the correct size");
71 
NS_NewSVGElement(mozilla::dom::Element ** aResult,already_AddRefed<mozilla::dom::NodeInfo> && aNodeInfo)72 nsresult NS_NewSVGElement(
73     mozilla::dom::Element** aResult,
74     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) {
75   RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
76   auto* nim = nodeInfo->NodeInfoManager();
77   RefPtr<mozilla::dom::SVGElement> it =
78       new (nim) mozilla::dom::SVGElement(nodeInfo.forget());
79   nsresult rv = it->Init();
80 
81   if (NS_FAILED(rv)) {
82     return rv;
83   }
84 
85   it.forget(aResult);
86   return rv;
87 }
88 
89 namespace mozilla {
90 namespace dom {
91 using namespace SVGUnitTypes_Binding;
92 
93 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGElement)
94 
95 // Use the CC variant of this, even though this class does not define
96 // a new CC participant, to make QIing to the CC interfaces faster.
97 NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(SVGElement, SVGElementBase,
98                                                    SVGElement)
99 
100 SVGEnumMapping SVGElement::sSVGUnitTypesMap[] = {
101     {nsGkAtoms::userSpaceOnUse, SVG_UNIT_TYPE_USERSPACEONUSE},
102     {nsGkAtoms::objectBoundingBox, SVG_UNIT_TYPE_OBJECTBOUNDINGBOX},
103     {nullptr, 0}};
104 
SVGElement(already_AddRefed<mozilla::dom::NodeInfo> && aNodeInfo)105 SVGElement::SVGElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
106     : SVGElementBase(std::move(aNodeInfo)) {}
107 
~SVGElement()108 SVGElement::~SVGElement() {
109   OwnerDoc()->UnscheduleSVGForPresAttrEvaluation(this);
110 }
111 
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)112 JSObject* SVGElement::WrapNode(JSContext* aCx,
113                                JS::Handle<JSObject*> aGivenProto) {
114   return SVGElement_Binding::Wrap(aCx, this, aGivenProto);
115 }
116 
117 template <typename Value, typename Info>
ResetAll()118 void SVGElement::AttributesInfo<Value, Info>::ResetAll() {
119   for (uint32_t i = 0; i < mCount; ++i) {
120     Reset(i);
121   }
122 }
123 
124 template <typename Value, typename Info>
CopyAllFrom(const AttributesInfo & aOther)125 void SVGElement::AttributesInfo<Value, Info>::CopyAllFrom(
126     const AttributesInfo& aOther) {
127   MOZ_DIAGNOSTIC_ASSERT(mCount == aOther.mCount,
128                         "Should only be called on clones");
129   for (uint32_t i = 0; i < mCount; ++i) {
130     mValues[i] = aOther.mValues[i];
131   }
132 }
133 
134 template <>
Reset(uint8_t aAttrEnum)135 void SVGElement::LengthAttributesInfo::Reset(uint8_t aAttrEnum) {
136   mValues[aAttrEnum].Init(mInfos[aAttrEnum].mCtxType, aAttrEnum,
137                           mInfos[aAttrEnum].mDefaultValue,
138                           mInfos[aAttrEnum].mDefaultUnitType);
139 }
140 
141 template <>
Reset(uint8_t aAttrEnum)142 void SVGElement::LengthListAttributesInfo::Reset(uint8_t aAttrEnum) {
143   mValues[aAttrEnum].ClearBaseValue(aAttrEnum);
144   // caller notifies
145 }
146 
147 template <>
Reset(uint8_t aAttrEnum)148 void SVGElement::NumberListAttributesInfo::Reset(uint8_t aAttrEnum) {
149   MOZ_ASSERT(aAttrEnum < mCount, "Bad attr enum");
150   mValues[aAttrEnum].ClearBaseValue(aAttrEnum);
151   // caller notifies
152 }
153 
154 template <>
Reset(uint8_t aAttrEnum)155 void SVGElement::NumberAttributesInfo::Reset(uint8_t aAttrEnum) {
156   mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
157 }
158 
159 template <>
Reset(uint8_t aAttrEnum)160 void SVGElement::NumberPairAttributesInfo::Reset(uint8_t aAttrEnum) {
161   mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue1,
162                           mInfos[aAttrEnum].mDefaultValue2);
163 }
164 
165 template <>
Reset(uint8_t aAttrEnum)166 void SVGElement::IntegerAttributesInfo::Reset(uint8_t aAttrEnum) {
167   mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
168 }
169 
170 template <>
Reset(uint8_t aAttrEnum)171 void SVGElement::IntegerPairAttributesInfo::Reset(uint8_t aAttrEnum) {
172   mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue1,
173                           mInfos[aAttrEnum].mDefaultValue2);
174 }
175 
176 template <>
Reset(uint8_t aAttrEnum)177 void SVGElement::BooleanAttributesInfo::Reset(uint8_t aAttrEnum) {
178   mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
179 }
180 
181 template <>
Reset(uint8_t aAttrEnum)182 void SVGElement::StringListAttributesInfo::Reset(uint8_t aAttrEnum) {
183   mValues[aAttrEnum].Clear();
184   // caller notifies
185 }
186 
187 template <>
Reset(uint8_t aAttrEnum)188 void SVGElement::EnumAttributesInfo::Reset(uint8_t aAttrEnum) {
189   mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
190 }
191 
192 template <>
Reset(uint8_t aAttrEnum)193 void SVGElement::StringAttributesInfo::Reset(uint8_t aAttrEnum) {
194   mValues[aAttrEnum].Init(aAttrEnum);
195 }
196 
CopyInnerTo(mozilla::dom::Element * aDest)197 nsresult SVGElement::CopyInnerTo(mozilla::dom::Element* aDest) {
198   nsresult rv = Element::CopyInnerTo(aDest);
199   NS_ENSURE_SUCCESS(rv, rv);
200 
201   auto* dest = static_cast<SVGElement*>(aDest);
202 
203   // cloning a node must retain its internal nonce slot
204   if (auto* nonce = static_cast<nsString*>(GetProperty(nsGkAtoms::nonce))) {
205     dest->SetNonce(*nonce);
206   }
207 
208   // If our destination is a print document, copy all the relevant length values
209   // etc so that they match the state of the original node.
210   if (aDest->OwnerDoc()->IsStaticDocument()) {
211     dest->GetLengthInfo().CopyAllFrom(GetLengthInfo());
212     dest->GetNumberInfo().CopyAllFrom(GetNumberInfo());
213     dest->GetNumberPairInfo().CopyAllFrom(GetNumberPairInfo());
214     dest->GetIntegerInfo().CopyAllFrom(GetIntegerInfo());
215     dest->GetIntegerPairInfo().CopyAllFrom(GetIntegerPairInfo());
216     dest->GetEnumInfo().CopyAllFrom(GetEnumInfo());
217     dest->GetStringInfo().CopyAllFrom(GetStringInfo());
218     dest->GetLengthListInfo().CopyAllFrom(GetLengthListInfo());
219     dest->GetNumberListInfo().CopyAllFrom(GetNumberListInfo());
220   }
221 
222   return NS_OK;
223 }
224 
225 //----------------------------------------------------------------------
226 // SVGElement methods
227 
DidAnimateClass()228 void SVGElement::DidAnimateClass() {
229   // For Servo, snapshot the element before we change it.
230   PresShell* presShell = OwnerDoc()->GetPresShell();
231   if (presShell) {
232     if (nsPresContext* presContext = presShell->GetPresContext()) {
233       presContext->RestyleManager()->ClassAttributeWillBeChangedBySMIL(this);
234     }
235   }
236 
237   nsAutoString src;
238   mClassAttribute.GetAnimValue(src, this);
239   if (!mClassAnimAttr) {
240     mClassAnimAttr = MakeUnique<nsAttrValue>();
241   }
242   mClassAnimAttr->ParseAtomArray(src);
243 
244   // FIXME(emilio): This re-selector-matches, but we do the snapshot stuff right
245   // above... Is this needed anymore?
246   if (presShell) {
247     presShell->RestyleForAnimation(this, RestyleHint::RESTYLE_SELF);
248   }
249 }
250 
Init()251 nsresult SVGElement::Init() {
252   // Set up length attributes - can't do this in the constructor
253   // because we can't do a virtual call at that point
254 
255   GetLengthInfo().ResetAll();
256   GetNumberInfo().ResetAll();
257   GetNumberPairInfo().ResetAll();
258   GetIntegerInfo().ResetAll();
259   GetIntegerPairInfo().ResetAll();
260   GetBooleanInfo().ResetAll();
261   GetEnumInfo().ResetAll();
262 
263   if (SVGAnimatedOrient* orient = GetAnimatedOrient()) {
264     orient->Init();
265   }
266 
267   if (SVGAnimatedViewBox* viewBox = GetAnimatedViewBox()) {
268     viewBox->Init();
269   }
270 
271   if (SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
272           GetAnimatedPreserveAspectRatio()) {
273     preserveAspectRatio->Init();
274   }
275 
276   GetLengthListInfo().ResetAll();
277   GetNumberListInfo().ResetAll();
278 
279   // No need to reset SVGPointList since the default value is always the same
280   // (an empty list).
281 
282   // No need to reset SVGPathData since the default value is always the same
283   // (an empty list).
284 
285   GetStringInfo().ResetAll();
286   return NS_OK;
287 }
288 
289 //----------------------------------------------------------------------
290 // Implementation
291 
292 //----------------------------------------------------------------------
293 // nsIContent methods
294 
BindToTree(BindContext & aContext,nsINode & aParent)295 nsresult SVGElement::BindToTree(BindContext& aContext, nsINode& aParent) {
296   nsresult rv = SVGElementBase::BindToTree(aContext, aParent);
297   NS_ENSURE_SUCCESS(rv, rv);
298 
299   // Hide any nonce from the DOM, but keep the internal value of the
300   // nonce by copying and resetting the internal nonce value.
301   if (HasFlag(NODE_HAS_NONCE_AND_HEADER_CSP) && IsInComposedDoc() &&
302       OwnerDoc()->GetBrowsingContext()) {
303     nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
304         "SVGElement::ResetNonce::Runnable",
305         [self = RefPtr<SVGElement>(this)]() {
306           nsAutoString nonce;
307           self->GetNonce(nonce);
308           self->SetAttr(kNameSpaceID_None, nsGkAtoms::nonce, u""_ns, true);
309           self->SetNonce(nonce);
310         }));
311   }
312 
313   return NS_OK;
314 }
315 
AfterSetAttr(int32_t aNamespaceID,nsAtom * aName,const nsAttrValue * aValue,const nsAttrValue * aOldValue,nsIPrincipal * aSubjectPrincipal,bool aNotify)316 nsresult SVGElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
317                                   const nsAttrValue* aValue,
318                                   const nsAttrValue* aOldValue,
319                                   nsIPrincipal* aSubjectPrincipal,
320                                   bool aNotify) {
321   // We don't currently use nsMappedAttributes within SVG. If this changes, we
322   // need to be very careful because some nsAttrValues used by SVG point to
323   // member data of SVG elements and if an nsAttrValue outlives the SVG element
324   // whose data it points to (by virtue of being stored in
325   // mAttrs->mMappedAttributes, meaning it's shared between
326   // elements), the pointer will dangle. See bug 724680.
327   MOZ_ASSERT(!mAttrs.HasMappedAttrs(),
328              "Unexpected use of nsMappedAttributes within SVG");
329 
330   // If this is an svg presentation attribute we need to map it into
331   // the content declaration block.
332   // XXX For some reason incremental mapping doesn't work, so for now
333   // just delete the style rule and lazily reconstruct it as needed).
334   if (aNamespaceID == kNameSpaceID_None && IsAttributeMapped(aName)) {
335     mContentDeclarationBlock = nullptr;
336     OwnerDoc()->ScheduleSVGForPresAttrEvaluation(this);
337   }
338 
339   if (IsEventAttributeName(aName) && aValue) {
340     MOZ_ASSERT(aValue->Type() == nsAttrValue::eString,
341                "Expected string value for script body");
342     SetEventHandler(GetEventNameForAttr(aName), aValue->GetStringValue());
343   }
344 
345   // The nonce will be copied over to an internal slot and cleared from the
346   // Element within BindToTree to avoid CSS Selector nonce exfiltration if
347   // the CSP list contains a header-delivered CSP.
348   if (nsGkAtoms::nonce == aName && kNameSpaceID_None == aNamespaceID) {
349     if (aValue) {
350       SetNonce(aValue->GetStringValue());
351       if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) {
352         SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP);
353       }
354     } else {
355       RemoveNonce();
356     }
357   }
358 
359   return SVGElementBase::AfterSetAttr(aNamespaceID, aName, aValue, aOldValue,
360                                       aSubjectPrincipal, aNotify);
361 }
362 
ParseAttribute(int32_t aNamespaceID,nsAtom * aAttribute,const nsAString & aValue,nsIPrincipal * aMaybeScriptedPrincipal,nsAttrValue & aResult)363 bool SVGElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
364                                 const nsAString& aValue,
365                                 nsIPrincipal* aMaybeScriptedPrincipal,
366                                 nsAttrValue& aResult) {
367   nsresult rv = NS_OK;
368   bool foundMatch = false;
369   bool didSetResult = false;
370 
371   if (aNamespaceID == kNameSpaceID_None) {
372     // Check for SVGAnimatedLength attribute
373     LengthAttributesInfo lengthInfo = GetLengthInfo();
374 
375     uint32_t i;
376     for (i = 0; i < lengthInfo.mCount; i++) {
377       if (aAttribute == lengthInfo.mInfos[i].mName) {
378         rv = lengthInfo.mValues[i].SetBaseValueString(aValue, this, false);
379         if (NS_FAILED(rv)) {
380           lengthInfo.Reset(i);
381         } else {
382           aResult.SetTo(lengthInfo.mValues[i], &aValue);
383           didSetResult = true;
384         }
385         foundMatch = true;
386         break;
387       }
388     }
389 
390     if (!foundMatch) {
391       // Check for SVGAnimatedLengthList attribute
392       LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
393       for (i = 0; i < lengthListInfo.mCount; i++) {
394         if (aAttribute == lengthListInfo.mInfos[i].mName) {
395           rv = lengthListInfo.mValues[i].SetBaseValueString(aValue);
396           if (NS_FAILED(rv)) {
397             lengthListInfo.Reset(i);
398           } else {
399             aResult.SetTo(lengthListInfo.mValues[i].GetBaseValue(), &aValue);
400             didSetResult = true;
401           }
402           foundMatch = true;
403           break;
404         }
405       }
406     }
407 
408     if (!foundMatch) {
409       // Check for SVGAnimatedNumberList attribute
410       NumberListAttributesInfo numberListInfo = GetNumberListInfo();
411       for (i = 0; i < numberListInfo.mCount; i++) {
412         if (aAttribute == numberListInfo.mInfos[i].mName) {
413           rv = numberListInfo.mValues[i].SetBaseValueString(aValue);
414           if (NS_FAILED(rv)) {
415             numberListInfo.Reset(i);
416           } else {
417             aResult.SetTo(numberListInfo.mValues[i].GetBaseValue(), &aValue);
418             didSetResult = true;
419           }
420           foundMatch = true;
421           break;
422         }
423       }
424     }
425 
426     if (!foundMatch) {
427       // Check for SVGAnimatedPointList attribute
428       if (GetPointListAttrName() == aAttribute) {
429         if (SVGAnimatedPointList* pointList = GetAnimatedPointList()) {
430           pointList->SetBaseValueString(aValue);
431           // The spec says we parse everything up to the failure, so we DON'T
432           // need to check the result of SetBaseValueString or call
433           // pointList->ClearBaseValue() if it fails
434           aResult.SetTo(pointList->GetBaseValue(), &aValue);
435           didSetResult = true;
436           foundMatch = true;
437         }
438       }
439     }
440 
441     if (!foundMatch) {
442       // Check for SVGAnimatedPathSegList attribute
443       if (GetPathDataAttrName() == aAttribute) {
444         if (SVGAnimatedPathSegList* segList = GetAnimPathSegList()) {
445           segList->SetBaseValueString(aValue);
446           // The spec says we parse everything up to the failure, so we DON'T
447           // need to check the result of SetBaseValueString or call
448           // segList->ClearBaseValue() if it fails
449           aResult.SetTo(segList->GetBaseValue(), &aValue);
450           didSetResult = true;
451           foundMatch = true;
452         }
453       }
454     }
455 
456     if (!foundMatch) {
457       // Check for SVGAnimatedNumber attribute
458       NumberAttributesInfo numberInfo = GetNumberInfo();
459       for (i = 0; i < numberInfo.mCount; i++) {
460         if (aAttribute == numberInfo.mInfos[i].mName) {
461           rv = numberInfo.mValues[i].SetBaseValueString(aValue, this);
462           if (NS_FAILED(rv)) {
463             numberInfo.Reset(i);
464           } else {
465             aResult.SetTo(numberInfo.mValues[i].GetBaseValue(), &aValue);
466             didSetResult = true;
467           }
468           foundMatch = true;
469           break;
470         }
471       }
472     }
473 
474     if (!foundMatch) {
475       // Check for SVGAnimatedNumberPair attribute
476       NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo();
477       for (i = 0; i < numberPairInfo.mCount; i++) {
478         if (aAttribute == numberPairInfo.mInfos[i].mName) {
479           rv = numberPairInfo.mValues[i].SetBaseValueString(aValue, this);
480           if (NS_FAILED(rv)) {
481             numberPairInfo.Reset(i);
482           } else {
483             aResult.SetTo(numberPairInfo.mValues[i], &aValue);
484             didSetResult = true;
485           }
486           foundMatch = true;
487           break;
488         }
489       }
490     }
491 
492     if (!foundMatch) {
493       // Check for SVGAnimatedInteger attribute
494       IntegerAttributesInfo integerInfo = GetIntegerInfo();
495       for (i = 0; i < integerInfo.mCount; i++) {
496         if (aAttribute == integerInfo.mInfos[i].mName) {
497           rv = integerInfo.mValues[i].SetBaseValueString(aValue, this);
498           if (NS_FAILED(rv)) {
499             integerInfo.Reset(i);
500           } else {
501             aResult.SetTo(integerInfo.mValues[i].GetBaseValue(), &aValue);
502             didSetResult = true;
503           }
504           foundMatch = true;
505           break;
506         }
507       }
508     }
509 
510     if (!foundMatch) {
511       // Check for SVGAnimatedIntegerPair attribute
512       IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo();
513       for (i = 0; i < integerPairInfo.mCount; i++) {
514         if (aAttribute == integerPairInfo.mInfos[i].mName) {
515           rv = integerPairInfo.mValues[i].SetBaseValueString(aValue, this);
516           if (NS_FAILED(rv)) {
517             integerPairInfo.Reset(i);
518           } else {
519             aResult.SetTo(integerPairInfo.mValues[i], &aValue);
520             didSetResult = true;
521           }
522           foundMatch = true;
523           break;
524         }
525       }
526     }
527 
528     if (!foundMatch) {
529       // Check for SVGAnimatedBoolean attribute
530       BooleanAttributesInfo booleanInfo = GetBooleanInfo();
531       for (i = 0; i < booleanInfo.mCount; i++) {
532         if (aAttribute == booleanInfo.mInfos[i].mName) {
533           nsAtom* valAtom = NS_GetStaticAtom(aValue);
534           rv = valAtom ? booleanInfo.mValues[i].SetBaseValueAtom(valAtom, this)
535                        : NS_ERROR_DOM_SYNTAX_ERR;
536           if (NS_FAILED(rv)) {
537             booleanInfo.Reset(i);
538           } else {
539             aResult.SetTo(valAtom);
540             didSetResult = true;
541           }
542           foundMatch = true;
543           break;
544         }
545       }
546     }
547 
548     if (!foundMatch) {
549       // Check for SVGAnimatedEnumeration attribute
550       EnumAttributesInfo enumInfo = GetEnumInfo();
551       for (i = 0; i < enumInfo.mCount; i++) {
552         if (aAttribute == enumInfo.mInfos[i].mName) {
553           RefPtr<nsAtom> valAtom = NS_Atomize(aValue);
554           if (!enumInfo.mValues[i].SetBaseValueAtom(valAtom, this)) {
555             // Exact error value does not matter; we just need to mark the
556             // parse as failed.
557             rv = NS_ERROR_FAILURE;
558             enumInfo.Reset(i);
559           } else {
560             aResult.SetTo(valAtom);
561             didSetResult = true;
562           }
563           foundMatch = true;
564           break;
565         }
566       }
567     }
568 
569     if (!foundMatch) {
570       // Check for conditional processing attributes
571       nsCOMPtr<SVGTests> tests = do_QueryObject(this);
572       if (tests && tests->ParseConditionalProcessingAttribute(
573                        aAttribute, aValue, aResult)) {
574         foundMatch = true;
575       }
576     }
577 
578     if (!foundMatch) {
579       // Check for StringList attribute
580       StringListAttributesInfo stringListInfo = GetStringListInfo();
581       for (i = 0; i < stringListInfo.mCount; i++) {
582         if (aAttribute == stringListInfo.mInfos[i].mName) {
583           rv = stringListInfo.mValues[i].SetValue(aValue);
584           if (NS_FAILED(rv)) {
585             stringListInfo.Reset(i);
586           } else {
587             aResult.SetTo(stringListInfo.mValues[i], &aValue);
588             didSetResult = true;
589           }
590           foundMatch = true;
591           break;
592         }
593       }
594     }
595 
596     if (!foundMatch) {
597       // Check for orient attribute
598       if (aAttribute == nsGkAtoms::orient) {
599         SVGAnimatedOrient* orient = GetAnimatedOrient();
600         if (orient) {
601           rv = orient->SetBaseValueString(aValue, this, false);
602           if (NS_FAILED(rv)) {
603             orient->Init();
604           } else {
605             aResult.SetTo(*orient, &aValue);
606             didSetResult = true;
607           }
608           foundMatch = true;
609         }
610         // Check for viewBox attribute
611       } else if (aAttribute == nsGkAtoms::viewBox) {
612         SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
613         if (viewBox) {
614           rv = viewBox->SetBaseValueString(aValue, this, false);
615           if (NS_FAILED(rv)) {
616             viewBox->Init();
617           } else {
618             aResult.SetTo(*viewBox, &aValue);
619             didSetResult = true;
620           }
621           foundMatch = true;
622         }
623         // Check for preserveAspectRatio attribute
624       } else if (aAttribute == nsGkAtoms::preserveAspectRatio) {
625         SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
626             GetAnimatedPreserveAspectRatio();
627         if (preserveAspectRatio) {
628           rv = preserveAspectRatio->SetBaseValueString(aValue, this, false);
629           if (NS_FAILED(rv)) {
630             preserveAspectRatio->Init();
631           } else {
632             aResult.SetTo(*preserveAspectRatio, &aValue);
633             didSetResult = true;
634           }
635           foundMatch = true;
636         }
637         // Check for SVGAnimatedTransformList attribute
638       } else if (GetTransformListAttrName() == aAttribute) {
639         // The transform attribute is being set, so we must ensure that the
640         // SVGAnimatedTransformList is/has been allocated:
641         SVGAnimatedTransformList* transformList =
642             GetAnimatedTransformList(DO_ALLOCATE);
643         rv = transformList->SetBaseValueString(aValue, this);
644         if (NS_FAILED(rv)) {
645           transformList->ClearBaseValue();
646         } else {
647           aResult.SetTo(transformList->GetBaseValue(), &aValue);
648           didSetResult = true;
649         }
650         foundMatch = true;
651       } else if (aAttribute == nsGkAtoms::tabindex) {
652         didSetResult = aResult.ParseIntValue(aValue);
653         foundMatch = true;
654       }
655     }
656 
657     if (aAttribute == nsGkAtoms::_class) {
658       mClassAttribute.SetBaseValue(aValue, this, false);
659       aResult.ParseAtomArray(aValue);
660       return true;
661     }
662 
663     if (aAttribute == nsGkAtoms::rel) {
664       aResult.ParseAtomArray(aValue);
665       return true;
666     }
667   }
668 
669   if (!foundMatch) {
670     // Check for SVGAnimatedString attribute
671     StringAttributesInfo stringInfo = GetStringInfo();
672     for (uint32_t i = 0; i < stringInfo.mCount; i++) {
673       if (aNamespaceID == stringInfo.mInfos[i].mNamespaceID &&
674           aAttribute == stringInfo.mInfos[i].mName) {
675         stringInfo.mValues[i].SetBaseValue(aValue, this, false);
676         foundMatch = true;
677         break;
678       }
679     }
680   }
681 
682   if (foundMatch) {
683     if (NS_FAILED(rv)) {
684       ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue);
685       return false;
686     }
687     if (!didSetResult) {
688       aResult.SetTo(aValue);
689     }
690     return true;
691   }
692 
693   return SVGElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
694                                         aMaybeScriptedPrincipal, aResult);
695 }
696 
UnsetAttrInternal(int32_t aNamespaceID,nsAtom * aName,bool aNotify)697 void SVGElement::UnsetAttrInternal(int32_t aNamespaceID, nsAtom* aName,
698                                    bool aNotify) {
699   // XXXbz there's a bunch of redundancy here with AfterSetAttr.
700   // Maybe consolidate?
701 
702   if (aNamespaceID == kNameSpaceID_None) {
703     // If this is an svg presentation attribute, remove declaration block to
704     // force an update
705     if (IsAttributeMapped(aName)) {
706       mContentDeclarationBlock = nullptr;
707     }
708 
709     if (IsEventAttributeName(aName)) {
710       EventListenerManager* manager = GetExistingListenerManager();
711       if (manager) {
712         nsAtom* eventName = GetEventNameForAttr(aName);
713         manager->RemoveEventHandler(eventName);
714       }
715       return;
716     }
717 
718     // Check if this is a length attribute going away
719     LengthAttributesInfo lenInfo = GetLengthInfo();
720 
721     for (uint32_t i = 0; i < lenInfo.mCount; i++) {
722       if (aName == lenInfo.mInfos[i].mName) {
723         MaybeSerializeAttrBeforeRemoval(aName, aNotify);
724         lenInfo.Reset(i);
725         return;
726       }
727     }
728 
729     // Check if this is a length list attribute going away
730     LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
731 
732     for (uint32_t i = 0; i < lengthListInfo.mCount; i++) {
733       if (aName == lengthListInfo.mInfos[i].mName) {
734         MaybeSerializeAttrBeforeRemoval(aName, aNotify);
735         lengthListInfo.Reset(i);
736         return;
737       }
738     }
739 
740     // Check if this is a number list attribute going away
741     NumberListAttributesInfo numberListInfo = GetNumberListInfo();
742 
743     for (uint32_t i = 0; i < numberListInfo.mCount; i++) {
744       if (aName == numberListInfo.mInfos[i].mName) {
745         MaybeSerializeAttrBeforeRemoval(aName, aNotify);
746         numberListInfo.Reset(i);
747         return;
748       }
749     }
750 
751     // Check if this is a point list attribute going away
752     if (GetPointListAttrName() == aName) {
753       SVGAnimatedPointList* pointList = GetAnimatedPointList();
754       if (pointList) {
755         MaybeSerializeAttrBeforeRemoval(aName, aNotify);
756         pointList->ClearBaseValue();
757         return;
758       }
759     }
760 
761     // Check if this is a path segment list attribute going away
762     if (GetPathDataAttrName() == aName) {
763       SVGAnimatedPathSegList* segList = GetAnimPathSegList();
764       if (segList) {
765         MaybeSerializeAttrBeforeRemoval(aName, aNotify);
766         segList->ClearBaseValue();
767         return;
768       }
769     }
770 
771     // Check if this is a number attribute going away
772     NumberAttributesInfo numInfo = GetNumberInfo();
773 
774     for (uint32_t i = 0; i < numInfo.mCount; i++) {
775       if (aName == numInfo.mInfos[i].mName) {
776         numInfo.Reset(i);
777         return;
778       }
779     }
780 
781     // Check if this is a number pair attribute going away
782     NumberPairAttributesInfo numPairInfo = GetNumberPairInfo();
783 
784     for (uint32_t i = 0; i < numPairInfo.mCount; i++) {
785       if (aName == numPairInfo.mInfos[i].mName) {
786         MaybeSerializeAttrBeforeRemoval(aName, aNotify);
787         numPairInfo.Reset(i);
788         return;
789       }
790     }
791 
792     // Check if this is an integer attribute going away
793     IntegerAttributesInfo intInfo = GetIntegerInfo();
794 
795     for (uint32_t i = 0; i < intInfo.mCount; i++) {
796       if (aName == intInfo.mInfos[i].mName) {
797         intInfo.Reset(i);
798         return;
799       }
800     }
801 
802     // Check if this is an integer pair attribute going away
803     IntegerPairAttributesInfo intPairInfo = GetIntegerPairInfo();
804 
805     for (uint32_t i = 0; i < intPairInfo.mCount; i++) {
806       if (aName == intPairInfo.mInfos[i].mName) {
807         MaybeSerializeAttrBeforeRemoval(aName, aNotify);
808         intPairInfo.Reset(i);
809         return;
810       }
811     }
812 
813     // Check if this is a boolean attribute going away
814     BooleanAttributesInfo boolInfo = GetBooleanInfo();
815 
816     for (uint32_t i = 0; i < boolInfo.mCount; i++) {
817       if (aName == boolInfo.mInfos[i].mName) {
818         boolInfo.Reset(i);
819         return;
820       }
821     }
822 
823     // Check if this is an enum attribute going away
824     EnumAttributesInfo enumInfo = GetEnumInfo();
825 
826     for (uint32_t i = 0; i < enumInfo.mCount; i++) {
827       if (aName == enumInfo.mInfos[i].mName) {
828         enumInfo.Reset(i);
829         return;
830       }
831     }
832 
833     // Check if this is an orient attribute going away
834     if (aName == nsGkAtoms::orient) {
835       SVGAnimatedOrient* orient = GetAnimatedOrient();
836       if (orient) {
837         MaybeSerializeAttrBeforeRemoval(aName, aNotify);
838         orient->Init();
839         return;
840       }
841     }
842 
843     // Check if this is a viewBox attribute going away
844     if (aName == nsGkAtoms::viewBox) {
845       SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
846       if (viewBox) {
847         MaybeSerializeAttrBeforeRemoval(aName, aNotify);
848         viewBox->Init();
849         return;
850       }
851     }
852 
853     // Check if this is a preserveAspectRatio attribute going away
854     if (aName == nsGkAtoms::preserveAspectRatio) {
855       SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
856           GetAnimatedPreserveAspectRatio();
857       if (preserveAspectRatio) {
858         MaybeSerializeAttrBeforeRemoval(aName, aNotify);
859         preserveAspectRatio->Init();
860         return;
861       }
862     }
863 
864     // Check if this is a transform list attribute going away
865     if (GetTransformListAttrName() == aName) {
866       SVGAnimatedTransformList* transformList = GetAnimatedTransformList();
867       if (transformList) {
868         MaybeSerializeAttrBeforeRemoval(aName, aNotify);
869         transformList->ClearBaseValue();
870         return;
871       }
872     }
873 
874     // Check for conditional processing attributes
875     nsCOMPtr<SVGTests> tests = do_QueryObject(this);
876     if (tests && tests->IsConditionalProcessingAttribute(aName)) {
877       MaybeSerializeAttrBeforeRemoval(aName, aNotify);
878       tests->UnsetAttr(aName);
879       return;
880     }
881 
882     // Check if this is a string list attribute going away
883     StringListAttributesInfo stringListInfo = GetStringListInfo();
884 
885     for (uint32_t i = 0; i < stringListInfo.mCount; i++) {
886       if (aName == stringListInfo.mInfos[i].mName) {
887         MaybeSerializeAttrBeforeRemoval(aName, aNotify);
888         stringListInfo.Reset(i);
889         return;
890       }
891     }
892 
893     if (aName == nsGkAtoms::_class) {
894       mClassAttribute.Init();
895       return;
896     }
897   }
898 
899   // Check if this is a string attribute going away
900   StringAttributesInfo stringInfo = GetStringInfo();
901 
902   for (uint32_t i = 0; i < stringInfo.mCount; i++) {
903     if (aNamespaceID == stringInfo.mInfos[i].mNamespaceID &&
904         aName == stringInfo.mInfos[i].mName) {
905       stringInfo.Reset(i);
906       return;
907     }
908   }
909 }
910 
BeforeSetAttr(int32_t aNamespaceID,nsAtom * aName,const nsAttrValueOrString * aValue,bool aNotify)911 nsresult SVGElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
912                                    const nsAttrValueOrString* aValue,
913                                    bool aNotify) {
914   if (!aValue) {
915     UnsetAttrInternal(aNamespaceID, aName, aNotify);
916   }
917   return SVGElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify);
918 }
919 
GetAttributeChangeHint(const nsAtom * aAttribute,int32_t aModType) const920 nsChangeHint SVGElement::GetAttributeChangeHint(const nsAtom* aAttribute,
921                                                 int32_t aModType) const {
922   nsChangeHint retval =
923       SVGElementBase::GetAttributeChangeHint(aAttribute, aModType);
924 
925   nsCOMPtr<SVGTests> tests = do_QueryObject(const_cast<SVGElement*>(this));
926   if (tests && tests->IsConditionalProcessingAttribute(aAttribute)) {
927     // It would be nice to only reconstruct the frame if the value returned by
928     // SVGTests::PassesConditionalProcessingTests has changed, but we don't
929     // know that
930     retval |= nsChangeHint_ReconstructFrame;
931   }
932   return retval;
933 }
934 
IsNodeOfType(uint32_t aFlags) const935 bool SVGElement::IsNodeOfType(uint32_t aFlags) const { return false; }
936 
NodeInfoChanged(Document * aOldDoc)937 void SVGElement::NodeInfoChanged(Document* aOldDoc) {
938   SVGElementBase::NodeInfoChanged(aOldDoc);
939   aOldDoc->UnscheduleSVGForPresAttrEvaluation(this);
940   mContentDeclarationBlock = nullptr;
941   OwnerDoc()->ScheduleSVGForPresAttrEvaluation(this);
942 }
943 
NS_IMETHODIMP_(bool)944 NS_IMETHODIMP_(bool)
945 SVGElement::IsAttributeMapped(const nsAtom* name) const {
946   if (name == nsGkAtoms::lang) {
947     return true;
948   }
949   return SVGElementBase::IsAttributeMapped(name);
950 }
951 
952 // PresentationAttributes-FillStroke
953 /* static */
954 const Element::MappedAttributeEntry SVGElement::sFillStrokeMap[] = {
955     {nsGkAtoms::fill},
956     {nsGkAtoms::fill_opacity},
957     {nsGkAtoms::fill_rule},
958     {nsGkAtoms::paint_order},
959     {nsGkAtoms::stroke},
960     {nsGkAtoms::stroke_dasharray},
961     {nsGkAtoms::stroke_dashoffset},
962     {nsGkAtoms::stroke_linecap},
963     {nsGkAtoms::stroke_linejoin},
964     {nsGkAtoms::stroke_miterlimit},
965     {nsGkAtoms::stroke_opacity},
966     {nsGkAtoms::stroke_width},
967     {nsGkAtoms::vector_effect},
968     {nullptr}};
969 
970 // PresentationAttributes-Graphics
971 /* static */
972 const Element::MappedAttributeEntry SVGElement::sGraphicsMap[] = {
973     {nsGkAtoms::clip_path},
974     {nsGkAtoms::clip_rule},
975     {nsGkAtoms::colorInterpolation},
976     {nsGkAtoms::cursor},
977     {nsGkAtoms::display},
978     {nsGkAtoms::filter},
979     {nsGkAtoms::image_rendering},
980     {nsGkAtoms::mask},
981     {nsGkAtoms::opacity},
982     {nsGkAtoms::pointer_events},
983     {nsGkAtoms::shape_rendering},
984     {nsGkAtoms::text_rendering},
985     {nsGkAtoms::transform_origin},
986     {nsGkAtoms::visibility},
987     {nullptr}};
988 
989 // PresentationAttributes-TextContentElements
990 /* static */
991 const Element::MappedAttributeEntry SVGElement::sTextContentElementsMap[] = {
992     // Properties that we don't support are commented out.
993     // { nsGkAtoms::alignment_baseline },
994     // { nsGkAtoms::baseline_shift },
995     {nsGkAtoms::direction},
996     {nsGkAtoms::dominant_baseline},
997     {nsGkAtoms::letter_spacing},
998     {nsGkAtoms::text_anchor},
999     {nsGkAtoms::text_decoration},
1000     {nsGkAtoms::unicode_bidi},
1001     {nsGkAtoms::word_spacing},
1002     {nsGkAtoms::writing_mode},
1003     {nullptr}};
1004 
1005 // PresentationAttributes-FontSpecification
1006 /* static */
1007 const Element::MappedAttributeEntry SVGElement::sFontSpecificationMap[] = {
1008     {nsGkAtoms::font_family},      {nsGkAtoms::font_size},
1009     {nsGkAtoms::font_size_adjust}, {nsGkAtoms::font_stretch},
1010     {nsGkAtoms::font_style},       {nsGkAtoms::font_variant},
1011     {nsGkAtoms::fontWeight},       {nullptr}};
1012 
1013 // PresentationAttributes-GradientStop
1014 /* static */
1015 const Element::MappedAttributeEntry SVGElement::sGradientStopMap[] = {
1016     {nsGkAtoms::stop_color}, {nsGkAtoms::stop_opacity}, {nullptr}};
1017 
1018 // PresentationAttributes-Viewports
1019 /* static */
1020 const Element::MappedAttributeEntry SVGElement::sViewportsMap[] = {
1021     {nsGkAtoms::overflow}, {nsGkAtoms::clip}, {nullptr}};
1022 
1023 // PresentationAttributes-Makers
1024 /* static */
1025 const Element::MappedAttributeEntry SVGElement::sMarkersMap[] = {
1026     {nsGkAtoms::marker_end},
1027     {nsGkAtoms::marker_mid},
1028     {nsGkAtoms::marker_start},
1029     {nullptr}};
1030 
1031 // PresentationAttributes-Color
1032 /* static */
1033 const Element::MappedAttributeEntry SVGElement::sColorMap[] = {
1034     {nsGkAtoms::color}, {nullptr}};
1035 
1036 // PresentationAttributes-Filters
1037 /* static */
1038 const Element::MappedAttributeEntry SVGElement::sFiltersMap[] = {
1039     {nsGkAtoms::colorInterpolationFilters}, {nullptr}};
1040 
1041 // PresentationAttributes-feFlood
1042 /* static */
1043 const Element::MappedAttributeEntry SVGElement::sFEFloodMap[] = {
1044     {nsGkAtoms::flood_color}, {nsGkAtoms::flood_opacity}, {nullptr}};
1045 
1046 // PresentationAttributes-LightingEffects
1047 /* static */
1048 const Element::MappedAttributeEntry SVGElement::sLightingEffectsMap[] = {
1049     {nsGkAtoms::lighting_color}, {nullptr}};
1050 
1051 // PresentationAttributes-mask
1052 /* static */
1053 const Element::MappedAttributeEntry SVGElement::sMaskMap[] = {
1054     {nsGkAtoms::mask_type}, {nullptr}};
1055 
1056 //----------------------------------------------------------------------
1057 // Element methods
1058 
1059 // forwarded to Element implementations
1060 
1061 //----------------------------------------------------------------------
1062 
GetOwnerSVGElement()1063 SVGSVGElement* SVGElement::GetOwnerSVGElement() {
1064   nsIContent* ancestor = GetFlattenedTreeParent();
1065 
1066   while (ancestor && ancestor->IsSVGElement()) {
1067     if (ancestor->IsSVGElement(nsGkAtoms::foreignObject)) {
1068       return nullptr;
1069     }
1070     if (ancestor->IsSVGElement(nsGkAtoms::svg)) {
1071       return static_cast<SVGSVGElement*>(ancestor);
1072     }
1073     ancestor = ancestor->GetFlattenedTreeParent();
1074   }
1075 
1076   // we don't have an ancestor <svg> element...
1077   return nullptr;
1078 }
1079 
GetViewportElement()1080 SVGElement* SVGElement::GetViewportElement() {
1081   return SVGContentUtils::GetNearestViewportElement(this);
1082 }
1083 
ClassName()1084 already_AddRefed<DOMSVGAnimatedString> SVGElement::ClassName() {
1085   return mClassAttribute.ToDOMAnimatedString(this);
1086 }
1087 
1088 /* static */
UpdateDeclarationBlockFromLength(DeclarationBlock & aBlock,nsCSSPropertyID aPropId,const SVGAnimatedLength & aLength,ValToUse aValToUse)1089 bool SVGElement::UpdateDeclarationBlockFromLength(
1090     DeclarationBlock& aBlock, nsCSSPropertyID aPropId,
1091     const SVGAnimatedLength& aLength, ValToUse aValToUse) {
1092   aBlock.AssertMutable();
1093 
1094   float value;
1095   if (aValToUse == ValToUse::Anim) {
1096     value = aLength.GetAnimValInSpecifiedUnits();
1097   } else {
1098     MOZ_ASSERT(aValToUse == ValToUse::Base);
1099     value = aLength.GetBaseValInSpecifiedUnits();
1100   }
1101 
1102   // SVG parser doesn't check non-negativity of some parsed value,
1103   // we should not pass those to CSS side.
1104   if (value < 0 &&
1105       SVGGeometryProperty::IsNonNegativeGeometryProperty(aPropId)) {
1106     return false;
1107   }
1108 
1109   nsCSSUnit cssUnit = SVGGeometryProperty::SpecifiedUnitTypeToCSSUnit(
1110       aLength.GetSpecifiedUnitType());
1111 
1112   if (cssUnit == eCSSUnit_Percent) {
1113     Servo_DeclarationBlock_SetPercentValue(aBlock.Raw(), aPropId,
1114                                            value / 100.f);
1115   } else {
1116     Servo_DeclarationBlock_SetLengthValue(aBlock.Raw(), aPropId, value,
1117                                           cssUnit);
1118   }
1119 
1120   return true;
1121 }
1122 
1123 /* static */
UpdateDeclarationBlockFromPath(DeclarationBlock & aBlock,const SVGAnimatedPathSegList & aPath,ValToUse aValToUse)1124 bool SVGElement::UpdateDeclarationBlockFromPath(
1125     DeclarationBlock& aBlock, const SVGAnimatedPathSegList& aPath,
1126     ValToUse aValToUse) {
1127   aBlock.AssertMutable();
1128 
1129   const SVGPathData& pathData =
1130       aValToUse == ValToUse::Anim ? aPath.GetAnimValue() : aPath.GetBaseValue();
1131 
1132   // SVGPathData::mData is fallible but rust binding accepts nsTArray only, so
1133   // we need to point to one or the other. Fortunately, fallible and infallible
1134   // array types can be implicitly converted provided they are const.
1135   //
1136   // FIXME: here we just convert the data structure from cpp verion into rust
1137   // version. We don't do any normalization for the path data from d attribute.
1138   // Based on the current discussion of https://github.com/w3c/svgwg/issues/321,
1139   // we may have to convert the relative commands into absolute commands.
1140   // The normalization should be fixed in Bug 1489392. Besides, Bug 1714238
1141   // will use the same data structure, so we may simplify this more.
1142   const nsTArray<float>& asInFallibleArray = pathData.RawData();
1143   Servo_DeclarationBlock_SetPathValue(aBlock.Raw(), eCSSProperty_d,
1144                                       &asInFallibleArray);
1145   return true;
1146 }
1147 
1148 //------------------------------------------------------------------------
1149 // Helper class: MappedAttrParser, for parsing values of mapped attributes
1150 
1151 namespace {
1152 
1153 class MOZ_STACK_CLASS MappedAttrParser {
1154  public:
1155   MappedAttrParser(css::Loader* aLoader, nsIURI* aBaseURI,
1156                    SVGElement* aElement);
1157   ~MappedAttrParser();
1158 
1159   // Parses a mapped attribute value.
1160   void ParseMappedAttrValue(nsAtom* aMappedAttrName,
1161                             const nsAString& aMappedAttrValue);
1162 
1163   void TellStyleAlreadyParsedResult(nsAtom const* aAtom,
1164                                     SVGAnimatedLength const& aLength);
1165   void TellStyleAlreadyParsedResult(const SVGAnimatedPathSegList& aPath);
1166 
1167   // If we've parsed any values for mapped attributes, this method returns the
1168   // already_AddRefed css::Declaration that incorporates the parsed
1169   // values. Otherwise, this method returns null.
1170   already_AddRefed<DeclarationBlock> GetDeclarationBlock();
1171 
1172  private:
1173   // MEMBER DATA
1174   // -----------
1175   css::Loader* mLoader;
1176 
1177   nsCOMPtr<nsIURI> mBaseURI;
1178 
1179   // Declaration for storing parsed values (lazily initialized)
1180   RefPtr<DeclarationBlock> mDecl;
1181 
1182   // For reporting use counters
1183   SVGElement* mElement;
1184 };
1185 
MappedAttrParser(css::Loader * aLoader,nsIURI * aBaseURI,SVGElement * aElement)1186 MappedAttrParser::MappedAttrParser(css::Loader* aLoader, nsIURI* aBaseURI,
1187                                    SVGElement* aElement)
1188     : mLoader(aLoader), mBaseURI(aBaseURI), mElement(aElement) {}
1189 
~MappedAttrParser()1190 MappedAttrParser::~MappedAttrParser() {
1191   MOZ_ASSERT(!mDecl,
1192              "If mDecl was initialized, it should have been returned via "
1193              "GetDeclarationBlock (and had its pointer cleared)");
1194 }
1195 
ParseMappedAttrValue(nsAtom * aMappedAttrName,const nsAString & aMappedAttrValue)1196 void MappedAttrParser::ParseMappedAttrValue(nsAtom* aMappedAttrName,
1197                                             const nsAString& aMappedAttrValue) {
1198   if (!mDecl) {
1199     mDecl = new DeclarationBlock();
1200   }
1201 
1202   // Get the nsCSSPropertyID ID for our mapped attribute.
1203   nsCSSPropertyID propertyID =
1204       nsCSSProps::LookupProperty(nsAtomCString(aMappedAttrName));
1205   if (propertyID != eCSSProperty_UNKNOWN) {
1206     bool changed = false;  // outparam for ParseProperty.
1207     NS_ConvertUTF16toUTF8 value(aMappedAttrValue);
1208 
1209     // FIXME (bug 1343964): Figure out a better solution for sending the base
1210     // uri to servo
1211     nsCOMPtr<nsIReferrerInfo> referrerInfo =
1212         ReferrerInfo::CreateForSVGResources(mElement->OwnerDoc());
1213 
1214     auto data = MakeRefPtr<URLExtraData>(mBaseURI, referrerInfo,
1215                                          mElement->NodePrincipal());
1216     changed = Servo_DeclarationBlock_SetPropertyById(
1217         mDecl->Raw(), propertyID, &value, false, data,
1218         ParsingMode::AllowUnitlessLength,
1219         mElement->OwnerDoc()->GetCompatibilityMode(), mLoader,
1220         CSSRule_Binding::STYLE_RULE, {});
1221 
1222     // TODO(emilio): If we want to record these from CSSOM more generally, we
1223     // can pass the document use counters down the FFI call. For now manually
1224     // count them.
1225     if (changed && StaticPrefs::layout_css_use_counters_enabled()) {
1226       UseCounter useCounter = nsCSSProps::UseCounterFor(propertyID);
1227       MOZ_ASSERT(useCounter != eUseCounter_UNKNOWN);
1228       mElement->OwnerDoc()->SetUseCounter(useCounter);
1229     }
1230     return;
1231   }
1232   MOZ_ASSERT(aMappedAttrName == nsGkAtoms::lang,
1233              "Only 'lang' should be unrecognized!");
1234   // CSS parser doesn't know about 'lang', so we need to handle it specially.
1235   if (aMappedAttrName == nsGkAtoms::lang) {
1236     propertyID = eCSSProperty__x_lang;
1237     RefPtr<nsAtom> atom = NS_Atomize(aMappedAttrValue);
1238     Servo_DeclarationBlock_SetIdentStringValue(mDecl->Raw(), propertyID, atom);
1239   }
1240 }
1241 
TellStyleAlreadyParsedResult(nsAtom const * aAtom,SVGAnimatedLength const & aLength)1242 void MappedAttrParser::TellStyleAlreadyParsedResult(
1243     nsAtom const* aAtom, SVGAnimatedLength const& aLength) {
1244   if (!mDecl) {
1245     mDecl = new DeclarationBlock();
1246   }
1247   nsCSSPropertyID propertyID = nsCSSProps::LookupProperty(nsAtomCString(aAtom));
1248   SVGElement::UpdateDeclarationBlockFromLength(*mDecl, propertyID, aLength,
1249                                                SVGElement::ValToUse::Base);
1250 }
1251 
TellStyleAlreadyParsedResult(const SVGAnimatedPathSegList & aPath)1252 void MappedAttrParser::TellStyleAlreadyParsedResult(
1253     const SVGAnimatedPathSegList& aPath) {
1254   if (!mDecl) {
1255     mDecl = new DeclarationBlock();
1256   }
1257   SVGElement::UpdateDeclarationBlockFromPath(*mDecl, aPath,
1258                                              SVGElement::ValToUse::Base);
1259 }
1260 
GetDeclarationBlock()1261 already_AddRefed<DeclarationBlock> MappedAttrParser::GetDeclarationBlock() {
1262   return mDecl.forget();
1263 }
1264 
1265 }  // namespace
1266 
1267 //----------------------------------------------------------------------
1268 // Implementation Helpers:
1269 
UpdateContentDeclarationBlock()1270 void SVGElement::UpdateContentDeclarationBlock() {
1271   NS_ASSERTION(!mContentDeclarationBlock,
1272                "we already have a content declaration block");
1273 
1274   uint32_t attrCount = mAttrs.AttrCount();
1275   if (!attrCount) {
1276     // nothing to do
1277     return;
1278   }
1279 
1280   Document* doc = OwnerDoc();
1281   MappedAttrParser mappedAttrParser(doc->CSSLoader(), GetBaseURI(), this);
1282 
1283   bool lengthAffectsStyle =
1284       SVGGeometryProperty::ElementMapsLengthsToStyle(this);
1285 
1286   for (uint32_t i = 0; i < attrCount; ++i) {
1287     const nsAttrName* attrName = mAttrs.AttrNameAt(i);
1288     if (!attrName->IsAtom() || !IsAttributeMapped(attrName->Atom())) continue;
1289 
1290     if (attrName->NamespaceID() != kNameSpaceID_None &&
1291         !attrName->Equals(nsGkAtoms::lang, kNameSpaceID_XML)) {
1292       continue;
1293     }
1294 
1295     if (attrName->Equals(nsGkAtoms::lang, kNameSpaceID_None) &&
1296         HasAttr(kNameSpaceID_XML, nsGkAtoms::lang)) {
1297       continue;  // xml:lang has precedence
1298     }
1299 
1300     if (lengthAffectsStyle) {
1301       auto const* length = GetAnimatedLength(attrName->Atom());
1302 
1303       if (length && length->HasBaseVal()) {
1304         // This is an element with geometry property set via SVG attribute,
1305         // and the attribute is already successfully parsed. We want to go
1306         // through the optimized path to tell the style system the result
1307         // directly, rather than let it parse the same thing again.
1308         mappedAttrParser.TellStyleAlreadyParsedResult(attrName->Atom(),
1309                                                       *length);
1310         continue;
1311       }
1312     }
1313 
1314     if (attrName->Equals(nsGkAtoms::d, kNameSpaceID_None)) {
1315       const auto* path = GetAnimPathSegList();
1316       // Note: Only SVGPathElement has d attribute.
1317       MOZ_ASSERT(
1318           path,
1319           "SVGPathElement should have the non-null SVGAnimatedPathSegList");
1320       // The attribute should have been already successfully parsed.
1321       // We want to go through the optimized path to tell the style system
1322       // the result directly, rather than let it parse the same thing again.
1323       mappedAttrParser.TellStyleAlreadyParsedResult(*path);
1324       // Some other notes:
1325       // The syntax of CSS d property is different from SVG d attribute.
1326       // 1. CSS d proeprty accepts:  none | path(<quoted string>);
1327       // 2. SVG d attribtue accepts: none | <string>
1328       // So we cannot use css parser to parse the SVG d attribute directly.
1329       // Besides, |mAttrs.AttrAt(i)| removes the quotes already, so the svg path
1330       // in |mAttrs.AttrAt(i)| would be something like `M0,0L1,1z` without the
1331       // quotes. So css tokenizer cannot recognize this as a quoted string, and
1332       // so svg_path::SVGPathData::parse() doesn't work for this. Fortunately,
1333       // we still can rely on the parsed result from
1334       // SVGElement::ParseAttribute() for d attribute.
1335       continue;
1336     }
1337 
1338     nsAutoString value;
1339     mAttrs.AttrAt(i)->ToString(value);
1340     mappedAttrParser.ParseMappedAttrValue(attrName->Atom(), value);
1341   }
1342   mContentDeclarationBlock = mappedAttrParser.GetDeclarationBlock();
1343 }
1344 
GetContentDeclarationBlock() const1345 const DeclarationBlock* SVGElement::GetContentDeclarationBlock() const {
1346   return mContentDeclarationBlock;
1347 }
1348 
1349 /**
1350  * Helper methods for the type-specific WillChangeXXX methods.
1351  *
1352  * This method sends out appropriate pre-change notifications so that selector
1353  * restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop
1354  * matching) work, and it returns an nsAttrValue that _may_ contain the
1355  * attribute's pre-change value.
1356  *
1357  * The nsAttrValue returned by this method depends on whether there are
1358  * mutation event listeners listening for changes to this element's attributes.
1359  * If not, then the object returned is empty. If there are, then the
1360  * nsAttrValue returned contains a serialized copy of the attribute's value
1361  * prior to the change, and this object should be passed to the corresponding
1362  * DidChangeXXX method call (assuming a WillChangeXXX call is required for the
1363  * SVG type - see comment below). This is necessary so that the 'prevValue'
1364  * property of the mutation event that is dispatched will correctly contain the
1365  * old value.
1366  *
1367  * The reason we need to serialize the old value if there are mutation
1368  * event listeners is because the underlying nsAttrValue for the attribute
1369  * points directly to a parsed representation of the attribute (e.g. an
1370  * SVGAnimatedLengthList*) that is a member of the SVG element. That object
1371  * will have changed by the time DidChangeXXX has been called, so without the
1372  * serialization of the old attribute value that we provide, DidChangeXXX
1373  * would have no way to get the old value to pass to SetAttrAndNotify.
1374  *
1375  * We only return the old value when there are mutation event listeners because
1376  * it's not needed otherwise, and because it's expensive to serialize the old
1377  * value. This is especially true for list type attributes, which may be built
1378  * up via the SVG DOM resulting in a large number of Will/DidModifyXXX calls
1379  * before the script finally finishes setting the attribute.
1380  *
1381  * Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check
1382  * and filter out redundant changes. Before calling WillChangeXXX, the caller
1383  * should check whether the new and old values are actually the same, and skip
1384  * calling Will/DidChangeXXX if they are.
1385  *
1386  * Also note that not all SVG types use this scheme. For types that can be
1387  * represented by an nsAttrValue without pointing back to an SVG object (e.g.
1388  * enums, booleans, integers) we can simply use SetParsedAttr which will do all
1389  * of the above for us. For such types there is no matching WillChangeXXX
1390  * method, only DidChangeXXX which calls SetParsedAttr.
1391  */
WillChangeValue(nsAtom * aName,const mozAutoDocUpdate & aProofOfUpdate)1392 nsAttrValue SVGElement::WillChangeValue(
1393     nsAtom* aName, const mozAutoDocUpdate& aProofOfUpdate) {
1394   // We need an empty attr value:
1395   //   a) to pass to BeforeSetAttr when GetParsedAttr returns nullptr
1396   //   b) to store the old value in the case we have mutation listeners
1397   //
1398   // We can use the same value for both purposes, because if GetParsedAttr
1399   // returns non-null its return value is what will get passed to BeforeSetAttr,
1400   // not matter what our mutation listener situation is.
1401   //
1402   // Also, we should be careful to always return this value to benefit from
1403   // return value optimization.
1404   nsAttrValue emptyOrOldAttrValue;
1405   const nsAttrValue* attrValue = GetParsedAttr(aName);
1406 
1407   // We only need to set the old value if we have listeners since otherwise it
1408   // isn't used.
1409   if (attrValue && nsContentUtils::HasMutationListeners(
1410                        this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) {
1411     emptyOrOldAttrValue.SetToSerialized(*attrValue);
1412   }
1413 
1414   uint8_t modType =
1415       attrValue ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
1416                 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
1417   MutationObservers::NotifyAttributeWillChange(this, kNameSpaceID_None, aName,
1418                                                modType);
1419 
1420   // This is not strictly correct--the attribute value parameter for
1421   // BeforeSetAttr should reflect the value that *will* be set but that implies
1422   // allocating, e.g. an extra SVGAnimatedLength, and isn't necessary at the
1423   // moment since no SVG elements overload BeforeSetAttr. For now we just pass
1424   // the current value.
1425   nsAttrValueOrString attrStringOrValue(attrValue ? *attrValue
1426                                                   : emptyOrOldAttrValue);
1427   DebugOnly<nsresult> rv = BeforeSetAttr(
1428       kNameSpaceID_None, aName, &attrStringOrValue, kNotifyDocumentObservers);
1429   // SVG elements aren't expected to overload BeforeSetAttr in such a way that
1430   // it may fail. So long as this is the case we don't need to check and pass on
1431   // the return value which simplifies the calling code significantly.
1432   MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected failure from BeforeSetAttr");
1433 
1434   return emptyOrOldAttrValue;
1435 }
1436 
1437 /**
1438  * Helper methods for the type-specific DidChangeXXX methods.
1439  *
1440  * aEmptyOrOldValue will normally be the object returned from the corresponding
1441  * WillChangeXXX call. This is because:
1442  * a) WillChangeXXX will ensure the object is set when we have mutation
1443  *    listeners, and
1444  * b) WillChangeXXX will ensure the object represents a serialized version of
1445  *    the old attribute value so that the value doesn't change when the
1446  *    underlying SVG type is updated.
1447  *
1448  * aNewValue is replaced with the old value.
1449  */
DidChangeValue(nsAtom * aName,const nsAttrValue & aEmptyOrOldValue,nsAttrValue & aNewValue,const mozAutoDocUpdate & aProofOfUpdate)1450 void SVGElement::DidChangeValue(nsAtom* aName,
1451                                 const nsAttrValue& aEmptyOrOldValue,
1452                                 nsAttrValue& aNewValue,
1453                                 const mozAutoDocUpdate& aProofOfUpdate) {
1454   bool hasListeners = nsContentUtils::HasMutationListeners(
1455       this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this);
1456   uint8_t modType =
1457       HasAttr(kNameSpaceID_None, aName)
1458           ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
1459           : static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
1460 
1461   // XXX Really, the fourth argument to SetAttrAndNotify should be null if
1462   // aEmptyOrOldValue does not represent the actual previous value of the
1463   // attribute, but currently SVG elements do not even use the old attribute
1464   // value in |AfterSetAttr|, so this should be ok.
1465   SetAttrAndNotify(kNameSpaceID_None, aName, nullptr, &aEmptyOrOldValue,
1466                    aNewValue, nullptr, modType, hasListeners,
1467                    kNotifyDocumentObservers, kCallAfterSetAttr,
1468                    GetComposedDoc(), aProofOfUpdate);
1469 }
1470 
MaybeSerializeAttrBeforeRemoval(nsAtom * aName,bool aNotify)1471 void SVGElement::MaybeSerializeAttrBeforeRemoval(nsAtom* aName, bool aNotify) {
1472   if (!aNotify || !nsContentUtils::HasMutationListeners(
1473                       this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) {
1474     return;
1475   }
1476 
1477   const nsAttrValue* attrValue = mAttrs.GetAttr(aName);
1478   if (!attrValue) return;
1479 
1480   nsAutoString serializedValue;
1481   attrValue->ToString(serializedValue);
1482   nsAttrValue oldAttrValue(serializedValue);
1483   bool oldValueSet;
1484   mAttrs.SetAndSwapAttr(aName, oldAttrValue, &oldValueSet);
1485 }
1486 
GetEventNameForAttr(nsAtom * aAttr)1487 nsAtom* SVGElement::GetEventNameForAttr(nsAtom* aAttr) {
1488   if (IsSVGElement(nsGkAtoms::svg)) {
1489     if (aAttr == nsGkAtoms::onload) return nsGkAtoms::onSVGLoad;
1490     if (aAttr == nsGkAtoms::onscroll) return nsGkAtoms::onSVGScroll;
1491   }
1492   if (aAttr == nsGkAtoms::onbegin) return nsGkAtoms::onbeginEvent;
1493   if (aAttr == nsGkAtoms::onrepeat) return nsGkAtoms::onrepeatEvent;
1494   if (aAttr == nsGkAtoms::onend) return nsGkAtoms::onendEvent;
1495 
1496   return SVGElementBase::GetEventNameForAttr(aAttr);
1497 }
1498 
GetCtx() const1499 SVGViewportElement* SVGElement::GetCtx() const {
1500   return SVGContentUtils::GetNearestViewportElement(this);
1501 }
1502 
1503 /* virtual */
PrependLocalTransformsTo(const gfxMatrix & aMatrix,SVGTransformTypes aWhich) const1504 gfxMatrix SVGElement::PrependLocalTransformsTo(const gfxMatrix& aMatrix,
1505                                                SVGTransformTypes aWhich) const {
1506   return aMatrix;
1507 }
1508 
GetLengthInfo()1509 SVGElement::LengthAttributesInfo SVGElement::GetLengthInfo() {
1510   return LengthAttributesInfo(nullptr, nullptr, 0);
1511 }
1512 
SetLength(nsAtom * aName,const SVGAnimatedLength & aLength)1513 void SVGElement::SetLength(nsAtom* aName, const SVGAnimatedLength& aLength) {
1514   LengthAttributesInfo lengthInfo = GetLengthInfo();
1515 
1516   for (uint32_t i = 0; i < lengthInfo.mCount; i++) {
1517     if (aName == lengthInfo.mInfos[i].mName) {
1518       lengthInfo.mValues[i] = aLength;
1519       DidAnimateLength(i);
1520       return;
1521     }
1522   }
1523   MOZ_ASSERT(false, "no length found to set");
1524 }
1525 
WillChangeLength(uint8_t aAttrEnum,const mozAutoDocUpdate & aProofOfUpdate)1526 nsAttrValue SVGElement::WillChangeLength(
1527     uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1528   return WillChangeValue(GetLengthInfo().mInfos[aAttrEnum].mName,
1529                          aProofOfUpdate);
1530 }
1531 
DidChangeLength(uint8_t aAttrEnum,const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)1532 void SVGElement::DidChangeLength(uint8_t aAttrEnum,
1533                                  const nsAttrValue& aEmptyOrOldValue,
1534                                  const mozAutoDocUpdate& aProofOfUpdate) {
1535   LengthAttributesInfo info = GetLengthInfo();
1536 
1537   NS_ASSERTION(info.mCount > 0,
1538                "DidChangeLength on element with no length attribs");
1539   NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1540 
1541   nsAttrValue newValue;
1542   newValue.SetTo(info.mValues[aAttrEnum], nullptr);
1543 
1544   DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1545                  aProofOfUpdate);
1546 }
1547 
DidAnimateLength(uint8_t aAttrEnum)1548 void SVGElement::DidAnimateLength(uint8_t aAttrEnum) {
1549   // We need to do this here. Normally the SMIL restyle would also cause us to
1550   // do this from DidSetComputedStyle, but we don't have that guarantee if our
1551   // frame gets reconstructed.
1552   ClearAnyCachedPath();
1553 
1554   if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) {
1555     nsCSSPropertyID propId =
1556         SVGGeometryProperty::AttrEnumToCSSPropId(this, aAttrEnum);
1557 
1558     // We don't map use element width/height currently. We can remove this
1559     // test when we do.
1560     if (propId != eCSSProperty_UNKNOWN) {
1561       SMILOverrideStyle()->SetSMILValue(propId,
1562                                         GetLengthInfo().mValues[aAttrEnum]);
1563       return;
1564     }
1565   }
1566 
1567   nsIFrame* frame = GetPrimaryFrame();
1568 
1569   if (frame) {
1570     LengthAttributesInfo info = GetLengthInfo();
1571     frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1572                             MutationEvent_Binding::SMIL);
1573   }
1574 }
1575 
GetAnimatedLength(uint8_t aAttrEnum)1576 SVGAnimatedLength* SVGElement::GetAnimatedLength(uint8_t aAttrEnum) {
1577   LengthAttributesInfo info = GetLengthInfo();
1578   if (aAttrEnum < info.mCount) {
1579     return &info.mValues[aAttrEnum];
1580   }
1581   MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1582   return nullptr;
1583 }
1584 
GetAnimatedLength(const nsAtom * aAttrName)1585 SVGAnimatedLength* SVGElement::GetAnimatedLength(const nsAtom* aAttrName) {
1586   LengthAttributesInfo lengthInfo = GetLengthInfo();
1587 
1588   for (uint32_t i = 0; i < lengthInfo.mCount; i++) {
1589     if (aAttrName == lengthInfo.mInfos[i].mName) {
1590       return &lengthInfo.mValues[i];
1591     }
1592   }
1593   return nullptr;
1594 }
1595 
GetAnimatedLengthValues(float * aFirst,...)1596 void SVGElement::GetAnimatedLengthValues(float* aFirst, ...) {
1597   LengthAttributesInfo info = GetLengthInfo();
1598 
1599   NS_ASSERTION(info.mCount > 0,
1600                "GetAnimatedLengthValues on element with no length attribs");
1601 
1602   SVGViewportElement* ctx = nullptr;
1603 
1604   float* f = aFirst;
1605   uint32_t i = 0;
1606 
1607   va_list args;
1608   va_start(args, aFirst);
1609 
1610   while (f && i < info.mCount) {
1611     uint8_t type = info.mValues[i].GetSpecifiedUnitType();
1612     if (!ctx) {
1613       if (type != SVGLength_Binding::SVG_LENGTHTYPE_NUMBER &&
1614           type != SVGLength_Binding::SVG_LENGTHTYPE_PX)
1615         ctx = GetCtx();
1616     }
1617     if (type == SVGLength_Binding::SVG_LENGTHTYPE_EMS ||
1618         type == SVGLength_Binding::SVG_LENGTHTYPE_EXS)
1619       *f = info.mValues[i++].GetAnimValue(this);
1620     else
1621       *f = info.mValues[i++].GetAnimValue(ctx);
1622     f = va_arg(args, float*);
1623   }
1624 
1625   va_end(args);
1626 }
1627 
GetLengthListInfo()1628 SVGElement::LengthListAttributesInfo SVGElement::GetLengthListInfo() {
1629   return LengthListAttributesInfo(nullptr, nullptr, 0);
1630 }
1631 
WillChangeLengthList(uint8_t aAttrEnum,const mozAutoDocUpdate & aProofOfUpdate)1632 nsAttrValue SVGElement::WillChangeLengthList(
1633     uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1634   return WillChangeValue(GetLengthListInfo().mInfos[aAttrEnum].mName,
1635                          aProofOfUpdate);
1636 }
1637 
DidChangeLengthList(uint8_t aAttrEnum,const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)1638 void SVGElement::DidChangeLengthList(uint8_t aAttrEnum,
1639                                      const nsAttrValue& aEmptyOrOldValue,
1640                                      const mozAutoDocUpdate& aProofOfUpdate) {
1641   LengthListAttributesInfo info = GetLengthListInfo();
1642 
1643   NS_ASSERTION(info.mCount > 0,
1644                "DidChangeLengthList on element with no length list attribs");
1645   NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1646 
1647   nsAttrValue newValue;
1648   newValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1649 
1650   DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1651                  aProofOfUpdate);
1652 }
1653 
DidAnimateLengthList(uint8_t aAttrEnum)1654 void SVGElement::DidAnimateLengthList(uint8_t aAttrEnum) {
1655   nsIFrame* frame = GetPrimaryFrame();
1656 
1657   if (frame) {
1658     LengthListAttributesInfo info = GetLengthListInfo();
1659     frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1660                             MutationEvent_Binding::SMIL);
1661   }
1662 }
1663 
GetAnimatedLengthListValues(SVGUserUnitList * aFirst,...)1664 void SVGElement::GetAnimatedLengthListValues(SVGUserUnitList* aFirst, ...) {
1665   LengthListAttributesInfo info = GetLengthListInfo();
1666 
1667   NS_ASSERTION(
1668       info.mCount > 0,
1669       "GetAnimatedLengthListValues on element with no length list attribs");
1670 
1671   SVGUserUnitList* list = aFirst;
1672   uint32_t i = 0;
1673 
1674   va_list args;
1675   va_start(args, aFirst);
1676 
1677   while (list && i < info.mCount) {
1678     list->Init(&(info.mValues[i].GetAnimValue()), this, info.mInfos[i].mAxis);
1679     ++i;
1680     list = va_arg(args, SVGUserUnitList*);
1681   }
1682 
1683   va_end(args);
1684 }
1685 
GetAnimatedLengthList(uint8_t aAttrEnum)1686 SVGAnimatedLengthList* SVGElement::GetAnimatedLengthList(uint8_t aAttrEnum) {
1687   LengthListAttributesInfo info = GetLengthListInfo();
1688   if (aAttrEnum < info.mCount) {
1689     return &(info.mValues[aAttrEnum]);
1690   }
1691   MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1692   return nullptr;
1693 }
1694 
GetNumberListInfo()1695 SVGElement::NumberListAttributesInfo SVGElement::GetNumberListInfo() {
1696   return NumberListAttributesInfo(nullptr, nullptr, 0);
1697 }
1698 
WillChangeNumberList(uint8_t aAttrEnum,const mozAutoDocUpdate & aProofOfUpdate)1699 nsAttrValue SVGElement::WillChangeNumberList(
1700     uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1701   return WillChangeValue(GetNumberListInfo().mInfos[aAttrEnum].mName,
1702                          aProofOfUpdate);
1703 }
1704 
DidChangeNumberList(uint8_t aAttrEnum,const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)1705 void SVGElement::DidChangeNumberList(uint8_t aAttrEnum,
1706                                      const nsAttrValue& aEmptyOrOldValue,
1707                                      const mozAutoDocUpdate& aProofOfUpdate) {
1708   NumberListAttributesInfo info = GetNumberListInfo();
1709 
1710   MOZ_ASSERT(info.mCount > 0,
1711              "DidChangeNumberList on element with no number list attribs");
1712   MOZ_ASSERT(aAttrEnum < info.mCount, "aAttrEnum out of range");
1713 
1714   nsAttrValue newValue;
1715   newValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1716 
1717   DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1718                  aProofOfUpdate);
1719 }
1720 
DidAnimateNumberList(uint8_t aAttrEnum)1721 void SVGElement::DidAnimateNumberList(uint8_t aAttrEnum) {
1722   nsIFrame* frame = GetPrimaryFrame();
1723 
1724   if (frame) {
1725     NumberListAttributesInfo info = GetNumberListInfo();
1726     MOZ_ASSERT(aAttrEnum < info.mCount, "aAttrEnum out of range");
1727 
1728     frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1729                             MutationEvent_Binding::SMIL);
1730   }
1731 }
1732 
GetAnimatedNumberList(uint8_t aAttrEnum)1733 SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(uint8_t aAttrEnum) {
1734   NumberListAttributesInfo info = GetNumberListInfo();
1735   if (aAttrEnum < info.mCount) {
1736     return &(info.mValues[aAttrEnum]);
1737   }
1738   MOZ_ASSERT(false, "Bad attrEnum");
1739   return nullptr;
1740 }
1741 
GetAnimatedNumberList(nsAtom * aAttrName)1742 SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(nsAtom* aAttrName) {
1743   NumberListAttributesInfo info = GetNumberListInfo();
1744   for (uint32_t i = 0; i < info.mCount; i++) {
1745     if (aAttrName == info.mInfos[i].mName) {
1746       return &info.mValues[i];
1747     }
1748   }
1749   MOZ_ASSERT(false, "Bad caller");
1750   return nullptr;
1751 }
1752 
WillChangePointList(const mozAutoDocUpdate & aProofOfUpdate)1753 nsAttrValue SVGElement::WillChangePointList(
1754     const mozAutoDocUpdate& aProofOfUpdate) {
1755   MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1756   return WillChangeValue(GetPointListAttrName(), aProofOfUpdate);
1757 }
1758 
DidChangePointList(const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)1759 void SVGElement::DidChangePointList(const nsAttrValue& aEmptyOrOldValue,
1760                                     const mozAutoDocUpdate& aProofOfUpdate) {
1761   MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1762 
1763   nsAttrValue newValue;
1764   newValue.SetTo(GetAnimatedPointList()->GetBaseValue(), nullptr);
1765 
1766   DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue, newValue,
1767                  aProofOfUpdate);
1768 }
1769 
DidAnimatePointList()1770 void SVGElement::DidAnimatePointList() {
1771   MOZ_ASSERT(GetPointListAttrName(), "Animating non-existent path data?");
1772 
1773   ClearAnyCachedPath();
1774 
1775   nsIFrame* frame = GetPrimaryFrame();
1776 
1777   if (frame) {
1778     frame->AttributeChanged(kNameSpaceID_None, GetPointListAttrName(),
1779                             MutationEvent_Binding::SMIL);
1780   }
1781 }
1782 
WillChangePathSegList(const mozAutoDocUpdate & aProofOfUpdate)1783 nsAttrValue SVGElement::WillChangePathSegList(
1784     const mozAutoDocUpdate& aProofOfUpdate) {
1785   MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1786   return WillChangeValue(GetPathDataAttrName(), aProofOfUpdate);
1787 }
1788 
DidChangePathSegList(const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)1789 void SVGElement::DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue,
1790                                       const mozAutoDocUpdate& aProofOfUpdate) {
1791   MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1792 
1793   nsAttrValue newValue;
1794   newValue.SetTo(GetAnimPathSegList()->GetBaseValue(), nullptr);
1795 
1796   DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue, newValue,
1797                  aProofOfUpdate);
1798 }
1799 
DidAnimatePathSegList()1800 void SVGElement::DidAnimatePathSegList() {
1801   nsStaticAtom* name = GetPathDataAttrName();
1802   MOZ_ASSERT(name, "Animating non-existent path data?");
1803 
1804   ClearAnyCachedPath();
1805 
1806   // Notify style we have to update the d property because of SMIL animation.
1807   if (StaticPrefs::layout_css_d_property_enabled() && name == nsGkAtoms::d) {
1808     SMILOverrideStyle()->SetSMILValue(nsCSSPropertyID::eCSSProperty_d,
1809                                       *GetAnimPathSegList());
1810     return;
1811   }
1812 
1813   if (nsIFrame* frame = GetPrimaryFrame()) {
1814     frame->AttributeChanged(kNameSpaceID_None, name,
1815                             MutationEvent_Binding::SMIL);
1816   }
1817 }
1818 
GetNumberInfo()1819 SVGElement::NumberAttributesInfo SVGElement::GetNumberInfo() {
1820   return NumberAttributesInfo(nullptr, nullptr, 0);
1821 }
1822 
DidChangeNumber(uint8_t aAttrEnum)1823 void SVGElement::DidChangeNumber(uint8_t aAttrEnum) {
1824   NumberAttributesInfo info = GetNumberInfo();
1825 
1826   NS_ASSERTION(info.mCount > 0,
1827                "DidChangeNumber on element with no number attribs");
1828   NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1829 
1830   nsAttrValue attrValue;
1831   attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1832 
1833   SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1834                 attrValue, true);
1835 }
1836 
DidAnimateNumber(uint8_t aAttrEnum)1837 void SVGElement::DidAnimateNumber(uint8_t aAttrEnum) {
1838   nsIFrame* frame = GetPrimaryFrame();
1839 
1840   if (frame) {
1841     NumberAttributesInfo info = GetNumberInfo();
1842     frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1843                             MutationEvent_Binding::SMIL);
1844   }
1845 }
1846 
GetAnimatedNumberValues(float * aFirst,...)1847 void SVGElement::GetAnimatedNumberValues(float* aFirst, ...) {
1848   NumberAttributesInfo info = GetNumberInfo();
1849 
1850   NS_ASSERTION(info.mCount > 0,
1851                "GetAnimatedNumberValues on element with no number attribs");
1852 
1853   float* f = aFirst;
1854   uint32_t i = 0;
1855 
1856   va_list args;
1857   va_start(args, aFirst);
1858 
1859   while (f && i < info.mCount) {
1860     *f = info.mValues[i++].GetAnimValue();
1861     f = va_arg(args, float*);
1862   }
1863   va_end(args);
1864 }
1865 
GetNumberPairInfo()1866 SVGElement::NumberPairAttributesInfo SVGElement::GetNumberPairInfo() {
1867   return NumberPairAttributesInfo(nullptr, nullptr, 0);
1868 }
1869 
WillChangeNumberPair(uint8_t aAttrEnum)1870 nsAttrValue SVGElement::WillChangeNumberPair(uint8_t aAttrEnum) {
1871   mozAutoDocUpdate updateBatch(GetComposedDoc(), kDontNotifyDocumentObservers);
1872   return WillChangeValue(GetNumberPairInfo().mInfos[aAttrEnum].mName,
1873                          updateBatch);
1874 }
1875 
DidChangeNumberPair(uint8_t aAttrEnum,const nsAttrValue & aEmptyOrOldValue)1876 void SVGElement::DidChangeNumberPair(uint8_t aAttrEnum,
1877                                      const nsAttrValue& aEmptyOrOldValue) {
1878   NumberPairAttributesInfo info = GetNumberPairInfo();
1879 
1880   NS_ASSERTION(info.mCount > 0,
1881                "DidChangePairNumber on element with no number pair attribs");
1882   NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1883 
1884   nsAttrValue newValue;
1885   newValue.SetTo(info.mValues[aAttrEnum], nullptr);
1886 
1887   mozAutoDocUpdate updateBatch(GetComposedDoc(), kNotifyDocumentObservers);
1888   DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1889                  updateBatch);
1890 }
1891 
DidAnimateNumberPair(uint8_t aAttrEnum)1892 void SVGElement::DidAnimateNumberPair(uint8_t aAttrEnum) {
1893   nsIFrame* frame = GetPrimaryFrame();
1894 
1895   if (frame) {
1896     NumberPairAttributesInfo info = GetNumberPairInfo();
1897     frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1898                             MutationEvent_Binding::SMIL);
1899   }
1900 }
1901 
GetIntegerInfo()1902 SVGElement::IntegerAttributesInfo SVGElement::GetIntegerInfo() {
1903   return IntegerAttributesInfo(nullptr, nullptr, 0);
1904 }
1905 
DidChangeInteger(uint8_t aAttrEnum)1906 void SVGElement::DidChangeInteger(uint8_t aAttrEnum) {
1907   IntegerAttributesInfo info = GetIntegerInfo();
1908   NS_ASSERTION(info.mCount > 0,
1909                "DidChangeInteger on element with no integer attribs");
1910   NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1911 
1912   nsAttrValue attrValue;
1913   attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1914 
1915   SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1916                 attrValue, true);
1917 }
1918 
DidAnimateInteger(uint8_t aAttrEnum)1919 void SVGElement::DidAnimateInteger(uint8_t aAttrEnum) {
1920   nsIFrame* frame = GetPrimaryFrame();
1921 
1922   if (frame) {
1923     IntegerAttributesInfo info = GetIntegerInfo();
1924     frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1925                             MutationEvent_Binding::SMIL);
1926   }
1927 }
1928 
GetAnimatedIntegerValues(int32_t * aFirst,...)1929 void SVGElement::GetAnimatedIntegerValues(int32_t* aFirst, ...) {
1930   IntegerAttributesInfo info = GetIntegerInfo();
1931 
1932   NS_ASSERTION(info.mCount > 0,
1933                "GetAnimatedIntegerValues on element with no integer attribs");
1934 
1935   int32_t* n = aFirst;
1936   uint32_t i = 0;
1937 
1938   va_list args;
1939   va_start(args, aFirst);
1940 
1941   while (n && i < info.mCount) {
1942     *n = info.mValues[i++].GetAnimValue();
1943     n = va_arg(args, int32_t*);
1944   }
1945   va_end(args);
1946 }
1947 
GetIntegerPairInfo()1948 SVGElement::IntegerPairAttributesInfo SVGElement::GetIntegerPairInfo() {
1949   return IntegerPairAttributesInfo(nullptr, nullptr, 0);
1950 }
1951 
WillChangeIntegerPair(uint8_t aAttrEnum,const mozAutoDocUpdate & aProofOfUpdate)1952 nsAttrValue SVGElement::WillChangeIntegerPair(
1953     uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1954   return WillChangeValue(GetIntegerPairInfo().mInfos[aAttrEnum].mName,
1955                          aProofOfUpdate);
1956 }
1957 
DidChangeIntegerPair(uint8_t aAttrEnum,const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)1958 void SVGElement::DidChangeIntegerPair(uint8_t aAttrEnum,
1959                                       const nsAttrValue& aEmptyOrOldValue,
1960                                       const mozAutoDocUpdate& aProofOfUpdate) {
1961   IntegerPairAttributesInfo info = GetIntegerPairInfo();
1962 
1963   NS_ASSERTION(info.mCount > 0,
1964                "DidChangeIntegerPair on element with no integer pair attribs");
1965   NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1966 
1967   nsAttrValue newValue;
1968   newValue.SetTo(info.mValues[aAttrEnum], nullptr);
1969 
1970   DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1971                  aProofOfUpdate);
1972 }
1973 
DidAnimateIntegerPair(uint8_t aAttrEnum)1974 void SVGElement::DidAnimateIntegerPair(uint8_t aAttrEnum) {
1975   nsIFrame* frame = GetPrimaryFrame();
1976 
1977   if (frame) {
1978     IntegerPairAttributesInfo info = GetIntegerPairInfo();
1979     frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1980                             MutationEvent_Binding::SMIL);
1981   }
1982 }
1983 
GetBooleanInfo()1984 SVGElement::BooleanAttributesInfo SVGElement::GetBooleanInfo() {
1985   return BooleanAttributesInfo(nullptr, nullptr, 0);
1986 }
1987 
DidChangeBoolean(uint8_t aAttrEnum)1988 void SVGElement::DidChangeBoolean(uint8_t aAttrEnum) {
1989   BooleanAttributesInfo info = GetBooleanInfo();
1990 
1991   NS_ASSERTION(info.mCount > 0,
1992                "DidChangeBoolean on element with no boolean attribs");
1993   NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1994 
1995   nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom());
1996   SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1997                 attrValue, true);
1998 }
1999 
DidAnimateBoolean(uint8_t aAttrEnum)2000 void SVGElement::DidAnimateBoolean(uint8_t aAttrEnum) {
2001   nsIFrame* frame = GetPrimaryFrame();
2002 
2003   if (frame) {
2004     BooleanAttributesInfo info = GetBooleanInfo();
2005     frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
2006                             MutationEvent_Binding::SMIL);
2007   }
2008 }
2009 
GetEnumInfo()2010 SVGElement::EnumAttributesInfo SVGElement::GetEnumInfo() {
2011   return EnumAttributesInfo(nullptr, nullptr, 0);
2012 }
2013 
DidChangeEnum(uint8_t aAttrEnum)2014 void SVGElement::DidChangeEnum(uint8_t aAttrEnum) {
2015   EnumAttributesInfo info = GetEnumInfo();
2016 
2017   NS_ASSERTION(info.mCount > 0,
2018                "DidChangeEnum on element with no enum attribs");
2019   NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2020 
2021   nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom(this));
2022   SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
2023                 attrValue, true);
2024 }
2025 
DidAnimateEnum(uint8_t aAttrEnum)2026 void SVGElement::DidAnimateEnum(uint8_t aAttrEnum) {
2027   nsIFrame* frame = GetPrimaryFrame();
2028 
2029   if (frame) {
2030     EnumAttributesInfo info = GetEnumInfo();
2031     frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
2032                             MutationEvent_Binding::SMIL);
2033   }
2034 }
2035 
GetAnimatedOrient()2036 SVGAnimatedOrient* SVGElement::GetAnimatedOrient() { return nullptr; }
2037 
WillChangeOrient(const mozAutoDocUpdate & aProofOfUpdate)2038 nsAttrValue SVGElement::WillChangeOrient(
2039     const mozAutoDocUpdate& aProofOfUpdate) {
2040   return WillChangeValue(nsGkAtoms::orient, aProofOfUpdate);
2041 }
2042 
DidChangeOrient(const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)2043 void SVGElement::DidChangeOrient(const nsAttrValue& aEmptyOrOldValue,
2044                                  const mozAutoDocUpdate& aProofOfUpdate) {
2045   SVGAnimatedOrient* orient = GetAnimatedOrient();
2046 
2047   NS_ASSERTION(orient, "DidChangeOrient on element with no orient attrib");
2048 
2049   nsAttrValue newValue;
2050   newValue.SetTo(*orient, nullptr);
2051 
2052   DidChangeValue(nsGkAtoms::orient, aEmptyOrOldValue, newValue, aProofOfUpdate);
2053 }
2054 
DidAnimateOrient()2055 void SVGElement::DidAnimateOrient() {
2056   nsIFrame* frame = GetPrimaryFrame();
2057 
2058   if (frame) {
2059     frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::orient,
2060                             MutationEvent_Binding::SMIL);
2061   }
2062 }
2063 
GetAnimatedViewBox()2064 SVGAnimatedViewBox* SVGElement::GetAnimatedViewBox() { return nullptr; }
2065 
WillChangeViewBox(const mozAutoDocUpdate & aProofOfUpdate)2066 nsAttrValue SVGElement::WillChangeViewBox(
2067     const mozAutoDocUpdate& aProofOfUpdate) {
2068   return WillChangeValue(nsGkAtoms::viewBox, aProofOfUpdate);
2069 }
2070 
DidChangeViewBox(const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)2071 void SVGElement::DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue,
2072                                   const mozAutoDocUpdate& aProofOfUpdate) {
2073   SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
2074 
2075   NS_ASSERTION(viewBox, "DidChangeViewBox on element with no viewBox attrib");
2076 
2077   nsAttrValue newValue;
2078   newValue.SetTo(*viewBox, nullptr);
2079 
2080   DidChangeValue(nsGkAtoms::viewBox, aEmptyOrOldValue, newValue,
2081                  aProofOfUpdate);
2082 }
2083 
DidAnimateViewBox()2084 void SVGElement::DidAnimateViewBox() {
2085   nsIFrame* frame = GetPrimaryFrame();
2086 
2087   if (frame) {
2088     frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::viewBox,
2089                             MutationEvent_Binding::SMIL);
2090   }
2091 }
2092 
GetAnimatedPreserveAspectRatio()2093 SVGAnimatedPreserveAspectRatio* SVGElement::GetAnimatedPreserveAspectRatio() {
2094   return nullptr;
2095 }
2096 
WillChangePreserveAspectRatio(const mozAutoDocUpdate & aProofOfUpdate)2097 nsAttrValue SVGElement::WillChangePreserveAspectRatio(
2098     const mozAutoDocUpdate& aProofOfUpdate) {
2099   return WillChangeValue(nsGkAtoms::preserveAspectRatio, aProofOfUpdate);
2100 }
2101 
DidChangePreserveAspectRatio(const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)2102 void SVGElement::DidChangePreserveAspectRatio(
2103     const nsAttrValue& aEmptyOrOldValue,
2104     const mozAutoDocUpdate& aProofOfUpdate) {
2105   SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
2106       GetAnimatedPreserveAspectRatio();
2107 
2108   NS_ASSERTION(preserveAspectRatio,
2109                "DidChangePreserveAspectRatio on element with no "
2110                "preserveAspectRatio attrib");
2111 
2112   nsAttrValue newValue;
2113   newValue.SetTo(*preserveAspectRatio, nullptr);
2114 
2115   DidChangeValue(nsGkAtoms::preserveAspectRatio, aEmptyOrOldValue, newValue,
2116                  aProofOfUpdate);
2117 }
2118 
DidAnimatePreserveAspectRatio()2119 void SVGElement::DidAnimatePreserveAspectRatio() {
2120   nsIFrame* frame = GetPrimaryFrame();
2121 
2122   if (frame) {
2123     frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio,
2124                             MutationEvent_Binding::SMIL);
2125   }
2126 }
2127 
WillChangeTransformList(const mozAutoDocUpdate & aProofOfUpdate)2128 nsAttrValue SVGElement::WillChangeTransformList(
2129     const mozAutoDocUpdate& aProofOfUpdate) {
2130   return WillChangeValue(GetTransformListAttrName(), aProofOfUpdate);
2131 }
2132 
DidChangeTransformList(const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)2133 void SVGElement::DidChangeTransformList(
2134     const nsAttrValue& aEmptyOrOldValue,
2135     const mozAutoDocUpdate& aProofOfUpdate) {
2136   MOZ_ASSERT(GetTransformListAttrName(),
2137              "Changing non-existent transform list?");
2138 
2139   // The transform attribute is being set, so we must ensure that the
2140   // SVGAnimatedTransformList is/has been allocated:
2141   nsAttrValue newValue;
2142   newValue.SetTo(GetAnimatedTransformList(DO_ALLOCATE)->GetBaseValue(),
2143                  nullptr);
2144 
2145   DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue, newValue,
2146                  aProofOfUpdate);
2147 }
2148 
DidAnimateTransformList(int32_t aModType)2149 void SVGElement::DidAnimateTransformList(int32_t aModType) {
2150   MOZ_ASSERT(GetTransformListAttrName(),
2151              "Animating non-existent transform data?");
2152 
2153   nsIFrame* frame = GetPrimaryFrame();
2154 
2155   if (frame) {
2156     nsAtom* transformAttr = GetTransformListAttrName();
2157     frame->AttributeChanged(kNameSpaceID_None, transformAttr, aModType);
2158     // When script changes the 'transform' attribute, Element::SetAttrAndNotify
2159     // will call MutationObservers::NotifyAttributeChanged, under which
2160     // SVGTransformableElement::GetAttributeChangeHint will be called and an
2161     // appropriate change event posted to update our frame's overflow rects.
2162     // The SetAttrAndNotify doesn't happen for transform changes caused by
2163     // 'animateTransform' though (and sending out the mutation events that
2164     // MutationObservers::NotifyAttributeChanged dispatches would be
2165     // inappropriate anyway), so we need to post the change event ourself.
2166     nsChangeHint changeHint = GetAttributeChangeHint(transformAttr, aModType);
2167     if (changeHint) {
2168       nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0}, changeHint);
2169     }
2170   }
2171 }
2172 
GetStringInfo()2173 SVGElement::StringAttributesInfo SVGElement::GetStringInfo() {
2174   return StringAttributesInfo(nullptr, nullptr, 0);
2175 }
2176 
GetStringBaseValue(uint8_t aAttrEnum,nsAString & aResult) const2177 void SVGElement::GetStringBaseValue(uint8_t aAttrEnum,
2178                                     nsAString& aResult) const {
2179   SVGElement::StringAttributesInfo info =
2180       const_cast<SVGElement*>(this)->GetStringInfo();
2181 
2182   NS_ASSERTION(info.mCount > 0,
2183                "GetBaseValue on element with no string attribs");
2184 
2185   NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2186 
2187   GetAttr(info.mInfos[aAttrEnum].mNamespaceID, info.mInfos[aAttrEnum].mName,
2188           aResult);
2189 }
2190 
SetStringBaseValue(uint8_t aAttrEnum,const nsAString & aValue)2191 void SVGElement::SetStringBaseValue(uint8_t aAttrEnum,
2192                                     const nsAString& aValue) {
2193   SVGElement::StringAttributesInfo info = GetStringInfo();
2194 
2195   NS_ASSERTION(info.mCount > 0,
2196                "SetBaseValue on element with no string attribs");
2197 
2198   NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2199 
2200   SetAttr(info.mInfos[aAttrEnum].mNamespaceID, info.mInfos[aAttrEnum].mName,
2201           aValue, true);
2202 }
2203 
DidAnimateString(uint8_t aAttrEnum)2204 void SVGElement::DidAnimateString(uint8_t aAttrEnum) {
2205   nsIFrame* frame = GetPrimaryFrame();
2206 
2207   if (frame) {
2208     StringAttributesInfo info = GetStringInfo();
2209     frame->AttributeChanged(info.mInfos[aAttrEnum].mNamespaceID,
2210                             info.mInfos[aAttrEnum].mName,
2211                             MutationEvent_Binding::SMIL);
2212   }
2213 }
2214 
GetStringListInfo()2215 SVGElement::StringListAttributesInfo SVGElement::GetStringListInfo() {
2216   return StringListAttributesInfo(nullptr, nullptr, 0);
2217 }
2218 
WillChangeStringList(bool aIsConditionalProcessingAttribute,uint8_t aAttrEnum,const mozAutoDocUpdate & aProofOfUpdate)2219 nsAttrValue SVGElement::WillChangeStringList(
2220     bool aIsConditionalProcessingAttribute, uint8_t aAttrEnum,
2221     const mozAutoDocUpdate& aProofOfUpdate) {
2222   nsStaticAtom* name;
2223   if (aIsConditionalProcessingAttribute) {
2224     nsCOMPtr<SVGTests> tests(do_QueryInterface(this));
2225     name = tests->GetAttrName(aAttrEnum);
2226   } else {
2227     name = GetStringListInfo().mInfos[aAttrEnum].mName;
2228   }
2229   return WillChangeValue(name, aProofOfUpdate);
2230 }
2231 
DidChangeStringList(bool aIsConditionalProcessingAttribute,uint8_t aAttrEnum,const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)2232 void SVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute,
2233                                      uint8_t aAttrEnum,
2234                                      const nsAttrValue& aEmptyOrOldValue,
2235                                      const mozAutoDocUpdate& aProofOfUpdate) {
2236   nsStaticAtom* name;
2237   nsAttrValue newValue;
2238   nsCOMPtr<SVGTests> tests;
2239 
2240   if (aIsConditionalProcessingAttribute) {
2241     tests = do_QueryObject(this);
2242     name = tests->GetAttrName(aAttrEnum);
2243     tests->GetAttrValue(aAttrEnum, newValue);
2244   } else {
2245     StringListAttributesInfo info = GetStringListInfo();
2246 
2247     NS_ASSERTION(info.mCount > 0,
2248                  "DidChangeStringList on element with no string list attribs");
2249     NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2250 
2251     name = info.mInfos[aAttrEnum].mName;
2252     newValue.SetTo(info.mValues[aAttrEnum], nullptr);
2253   }
2254 
2255   DidChangeValue(name, aEmptyOrOldValue, newValue, aProofOfUpdate);
2256 
2257   if (aIsConditionalProcessingAttribute) {
2258     tests->MaybeInvalidate();
2259   }
2260 }
2261 
ReportAttributeParseFailure(Document * aDocument,nsAtom * aAttribute,const nsAString & aValue)2262 nsresult SVGElement::ReportAttributeParseFailure(Document* aDocument,
2263                                                  nsAtom* aAttribute,
2264                                                  const nsAString& aValue) {
2265   AutoTArray<nsString, 2> strings;
2266   strings.AppendElement(nsDependentAtomString(aAttribute));
2267   strings.AppendElement(aValue);
2268   return SVGContentUtils::ReportToConsole(aDocument, "AttributeParseWarning",
2269                                           strings);
2270 }
2271 
RecompileScriptEventListeners()2272 void SVGElement::RecompileScriptEventListeners() {
2273   int32_t i, count = mAttrs.AttrCount();
2274   for (i = 0; i < count; ++i) {
2275     const nsAttrName* name = mAttrs.AttrNameAt(i);
2276 
2277     // Eventlistenener-attributes are always in the null namespace
2278     if (!name->IsAtom()) {
2279       continue;
2280     }
2281 
2282     nsAtom* attr = name->Atom();
2283     if (!IsEventAttributeName(attr)) {
2284       continue;
2285     }
2286 
2287     nsAutoString value;
2288     GetAttr(attr, value);
2289     SetEventHandler(GetEventNameForAttr(attr), value, true);
2290   }
2291 }
2292 
GetAnimatedAttr(int32_t aNamespaceID,nsAtom * aName)2293 UniquePtr<SMILAttr> SVGElement::GetAnimatedAttr(int32_t aNamespaceID,
2294                                                 nsAtom* aName) {
2295   if (aNamespaceID == kNameSpaceID_None) {
2296     // Transforms:
2297     if (GetTransformListAttrName() == aName) {
2298       // The transform attribute is being animated, so we must ensure that the
2299       // SVGAnimatedTransformList is/has been allocated:
2300       return GetAnimatedTransformList(DO_ALLOCATE)->ToSMILAttr(this);
2301     }
2302 
2303     // Motion (fake 'attribute' for animateMotion)
2304     if (aName == nsGkAtoms::mozAnimateMotionDummyAttr) {
2305       return MakeUnique<SVGMotionSMILAttr>(this);
2306     }
2307 
2308     // Lengths:
2309     LengthAttributesInfo info = GetLengthInfo();
2310     for (uint32_t i = 0; i < info.mCount; i++) {
2311       if (aName == info.mInfos[i].mName) {
2312         return info.mValues[i].ToSMILAttr(this);
2313       }
2314     }
2315 
2316     // Numbers:
2317     {
2318       NumberAttributesInfo info = GetNumberInfo();
2319       for (uint32_t i = 0; i < info.mCount; i++) {
2320         if (aName == info.mInfos[i].mName) {
2321           return info.mValues[i].ToSMILAttr(this);
2322         }
2323       }
2324     }
2325 
2326     // Number Pairs:
2327     {
2328       NumberPairAttributesInfo info = GetNumberPairInfo();
2329       for (uint32_t i = 0; i < info.mCount; i++) {
2330         if (aName == info.mInfos[i].mName) {
2331           return info.mValues[i].ToSMILAttr(this);
2332         }
2333       }
2334     }
2335 
2336     // Integers:
2337     {
2338       IntegerAttributesInfo info = GetIntegerInfo();
2339       for (uint32_t i = 0; i < info.mCount; i++) {
2340         if (aName == info.mInfos[i].mName) {
2341           return info.mValues[i].ToSMILAttr(this);
2342         }
2343       }
2344     }
2345 
2346     // Integer Pairs:
2347     {
2348       IntegerPairAttributesInfo info = GetIntegerPairInfo();
2349       for (uint32_t i = 0; i < info.mCount; i++) {
2350         if (aName == info.mInfos[i].mName) {
2351           return info.mValues[i].ToSMILAttr(this);
2352         }
2353       }
2354     }
2355 
2356     // Enumerations:
2357     {
2358       EnumAttributesInfo info = GetEnumInfo();
2359       for (uint32_t i = 0; i < info.mCount; i++) {
2360         if (aName == info.mInfos[i].mName) {
2361           return info.mValues[i].ToSMILAttr(this);
2362         }
2363       }
2364     }
2365 
2366     // Booleans:
2367     {
2368       BooleanAttributesInfo info = GetBooleanInfo();
2369       for (uint32_t i = 0; i < info.mCount; i++) {
2370         if (aName == info.mInfos[i].mName) {
2371           return info.mValues[i].ToSMILAttr(this);
2372         }
2373       }
2374     }
2375 
2376     // orient:
2377     if (aName == nsGkAtoms::orient) {
2378       SVGAnimatedOrient* orient = GetAnimatedOrient();
2379       return orient ? orient->ToSMILAttr(this) : nullptr;
2380     }
2381 
2382     // viewBox:
2383     if (aName == nsGkAtoms::viewBox) {
2384       SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
2385       return viewBox ? viewBox->ToSMILAttr(this) : nullptr;
2386     }
2387 
2388     // preserveAspectRatio:
2389     if (aName == nsGkAtoms::preserveAspectRatio) {
2390       SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
2391           GetAnimatedPreserveAspectRatio();
2392       return preserveAspectRatio ? preserveAspectRatio->ToSMILAttr(this)
2393                                  : nullptr;
2394     }
2395 
2396     // NumberLists:
2397     {
2398       NumberListAttributesInfo info = GetNumberListInfo();
2399       for (uint32_t i = 0; i < info.mCount; i++) {
2400         if (aName == info.mInfos[i].mName) {
2401           MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes");
2402           return info.mValues[i].ToSMILAttr(this, uint8_t(i));
2403         }
2404       }
2405     }
2406 
2407     // LengthLists:
2408     {
2409       LengthListAttributesInfo info = GetLengthListInfo();
2410       for (uint32_t i = 0; i < info.mCount; i++) {
2411         if (aName == info.mInfos[i].mName) {
2412           MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes");
2413           return info.mValues[i].ToSMILAttr(this, uint8_t(i),
2414                                             info.mInfos[i].mAxis,
2415                                             info.mInfos[i].mCouldZeroPadList);
2416         }
2417       }
2418     }
2419 
2420     // PointLists:
2421     {
2422       if (GetPointListAttrName() == aName) {
2423         SVGAnimatedPointList* pointList = GetAnimatedPointList();
2424         if (pointList) {
2425           return pointList->ToSMILAttr(this);
2426         }
2427       }
2428     }
2429 
2430     // PathSegLists:
2431     {
2432       if (GetPathDataAttrName() == aName) {
2433         SVGAnimatedPathSegList* segList = GetAnimPathSegList();
2434         if (segList) {
2435           return segList->ToSMILAttr(this);
2436         }
2437       }
2438     }
2439 
2440     if (aName == nsGkAtoms::_class) {
2441       return mClassAttribute.ToSMILAttr(this);
2442     }
2443   }
2444 
2445   // Strings
2446   {
2447     StringAttributesInfo info = GetStringInfo();
2448     for (uint32_t i = 0; i < info.mCount; i++) {
2449       if (aNamespaceID == info.mInfos[i].mNamespaceID &&
2450           aName == info.mInfos[i].mName) {
2451         return info.mValues[i].ToSMILAttr(this);
2452       }
2453     }
2454   }
2455 
2456   return nullptr;
2457 }
2458 
AnimationNeedsResample()2459 void SVGElement::AnimationNeedsResample() {
2460   Document* doc = GetComposedDoc();
2461   if (doc && doc->HasAnimationController()) {
2462     doc->GetAnimationController()->SetResampleNeeded();
2463   }
2464 }
2465 
FlushAnimations()2466 void SVGElement::FlushAnimations() {
2467   Document* doc = GetComposedDoc();
2468   if (doc && doc->HasAnimationController()) {
2469     doc->GetAnimationController()->FlushResampleRequests();
2470   }
2471 }
2472 
AddSizeOfExcludingThis(nsWindowSizes & aSizes,size_t * aNodeSize) const2473 void SVGElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
2474                                         size_t* aNodeSize) const {
2475   Element::AddSizeOfExcludingThis(aSizes, aNodeSize);
2476 
2477   // These are owned by the element and not referenced from the stylesheets.
2478   // They're referenced from the rule tree, but the rule nodes don't measure
2479   // their style source (since they're non-owning), so unconditionally reporting
2480   // them even though it's a refcounted object is ok.
2481   if (mContentDeclarationBlock) {
2482     aSizes.mLayoutSvgMappedDeclarations +=
2483         mContentDeclarationBlock->SizeofIncludingThis(
2484             aSizes.mState.mMallocSizeOf);
2485   }
2486 }
2487 
2488 }  // namespace dom
2489 }  // namespace mozilla
2490