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