Bug 1850713: remove duplicated setting of early hint preloader id in `ScriptLoader...
[gecko.git] / dom / svg / SVGElement.cpp
blob19a4aaed195a253717e2249894f8c9a93d92278a
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/SVGElement.h"
9 #include "mozilla/AlreadyAddRefed.h"
10 #include "mozilla/dom/MutationEventBinding.h"
11 #include "mozilla/dom/MutationObservers.h"
12 #include "mozilla/dom/CSSRuleBinding.h"
13 #include "mozilla/dom/SVGElementBinding.h"
14 #include "mozilla/dom/SVGGeometryElement.h"
15 #include "mozilla/dom/SVGLengthBinding.h"
16 #include "mozilla/dom/SVGSVGElement.h"
17 #include "mozilla/dom/SVGTests.h"
18 #include "mozilla/dom/SVGUnitTypesBinding.h"
19 #include "mozilla/dom/Element.h"
21 #include "mozilla/ArrayUtils.h"
22 #include "mozilla/DebugOnly.h"
23 #include "mozilla/DeclarationBlock.h"
24 #include "mozilla/EventListenerManager.h"
25 #include "mozilla/InternalMutationEvent.h"
26 #include "mozilla/PresShell.h"
27 #include "mozilla/RestyleManager.h"
28 #include "mozilla/SMILAnimationController.h"
29 #include "mozilla/StaticPrefs_layout.h"
30 #include "mozilla/SVGContentUtils.h"
31 #include "mozilla/SVGObserverUtils.h"
32 #include "mozilla/Unused.h"
34 #include "mozAutoDocUpdate.h"
35 #include "nsAttrValueOrString.h"
36 #include "nsCSSProps.h"
37 #include "nsCSSValue.h"
38 #include "nsContentUtils.h"
39 #include "nsDOMCSSAttrDeclaration.h"
40 #include "nsICSSDeclaration.h"
41 #include "nsIContentInlines.h"
42 #include "mozilla/dom/Document.h"
43 #include "nsError.h"
44 #include "nsGkAtoms.h"
45 #include "nsIFrame.h"
46 #include "nsQueryObject.h"
47 #include "nsLayoutUtils.h"
48 #include "SVGAnimatedNumberList.h"
49 #include "SVGAnimatedLengthList.h"
50 #include "SVGAnimatedPointList.h"
51 #include "SVGAnimatedPathSegList.h"
52 #include "SVGAnimatedTransformList.h"
53 #include "SVGAnimatedBoolean.h"
54 #include "SVGAnimatedEnumeration.h"
55 #include "SVGAnimatedInteger.h"
56 #include "SVGAnimatedIntegerPair.h"
57 #include "SVGAnimatedLength.h"
58 #include "SVGAnimatedNumber.h"
59 #include "SVGAnimatedNumberPair.h"
60 #include "SVGAnimatedOrient.h"
61 #include "SVGAnimatedString.h"
62 #include "SVGAnimatedViewBox.h"
63 #include "SVGGeometryProperty.h"
64 #include "SVGMotionSMILAttr.h"
65 #include <stdarg.h>
67 // This is needed to ensure correct handling of calls to the
68 // vararg-list methods in this file:
69 // SVGElement::GetAnimated{Length,Number,Integer}Values
70 // See bug 547964 for details:
71 static_assert(sizeof(void*) == sizeof(nullptr),
72 "nullptr should be the correct size");
74 nsresult NS_NewSVGElement(
75 mozilla::dom::Element** aResult,
76 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) {
77 RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
78 auto* nim = nodeInfo->NodeInfoManager();
79 RefPtr<mozilla::dom::SVGElement> it =
80 new (nim) mozilla::dom::SVGElement(nodeInfo.forget());
81 nsresult rv = it->Init();
83 if (NS_FAILED(rv)) {
84 return rv;
87 it.forget(aResult);
88 return rv;
91 namespace mozilla::dom {
92 using namespace SVGUnitTypes_Binding;
94 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGElement)
96 // Use the CC variant of this, even though this class does not define
97 // a new CC participant, to make QIing to the CC interfaces faster.
98 NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(SVGElement, SVGElementBase,
99 SVGElement)
101 SVGEnumMapping SVGElement::sSVGUnitTypesMap[] = {
102 {nsGkAtoms::userSpaceOnUse, SVG_UNIT_TYPE_USERSPACEONUSE},
103 {nsGkAtoms::objectBoundingBox, SVG_UNIT_TYPE_OBJECTBOUNDINGBOX},
104 {nullptr, 0}};
106 SVGElement::SVGElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
107 : SVGElementBase(std::move(aNodeInfo)) {}
109 SVGElement::~SVGElement() = default;
111 JSObject* SVGElement::WrapNode(JSContext* aCx,
112 JS::Handle<JSObject*> aGivenProto) {
113 return SVGElement_Binding::Wrap(aCx, this, aGivenProto);
116 template <typename Value, typename Info>
117 void SVGElement::AttributesInfo<Value, Info>::ResetAll() {
118 for (uint32_t i = 0; i < mCount; ++i) {
119 Reset(i);
123 template <typename Value, typename Info>
124 void SVGElement::AttributesInfo<Value, Info>::CopyAllFrom(
125 const AttributesInfo& aOther) {
126 MOZ_DIAGNOSTIC_ASSERT(mCount == aOther.mCount,
127 "Should only be called on clones");
128 for (uint32_t i = 0; i < mCount; ++i) {
129 mValues[i] = aOther.mValues[i];
133 template <>
134 void SVGElement::LengthAttributesInfo::Reset(uint8_t aAttrEnum) {
135 mValues[aAttrEnum].Init(mInfos[aAttrEnum].mCtxType, aAttrEnum,
136 mInfos[aAttrEnum].mDefaultValue,
137 mInfos[aAttrEnum].mDefaultUnitType);
140 template <>
141 void SVGElement::LengthListAttributesInfo::Reset(uint8_t aAttrEnum) {
142 mValues[aAttrEnum].ClearBaseValue(aAttrEnum);
143 // caller notifies
146 template <>
147 void SVGElement::NumberListAttributesInfo::Reset(uint8_t aAttrEnum) {
148 MOZ_ASSERT(aAttrEnum < mCount, "Bad attr enum");
149 mValues[aAttrEnum].ClearBaseValue(aAttrEnum);
150 // caller notifies
153 template <>
154 void SVGElement::NumberAttributesInfo::Reset(uint8_t aAttrEnum) {
155 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
158 template <>
159 void SVGElement::NumberPairAttributesInfo::Reset(uint8_t aAttrEnum) {
160 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue1,
161 mInfos[aAttrEnum].mDefaultValue2);
164 template <>
165 void SVGElement::IntegerAttributesInfo::Reset(uint8_t aAttrEnum) {
166 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
169 template <>
170 void SVGElement::IntegerPairAttributesInfo::Reset(uint8_t aAttrEnum) {
171 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue1,
172 mInfos[aAttrEnum].mDefaultValue2);
175 template <>
176 void SVGElement::BooleanAttributesInfo::Reset(uint8_t aAttrEnum) {
177 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
180 template <>
181 void SVGElement::StringListAttributesInfo::Reset(uint8_t aAttrEnum) {
182 mValues[aAttrEnum].Clear();
183 // caller notifies
186 template <>
187 void SVGElement::EnumAttributesInfo::Reset(uint8_t aAttrEnum) {
188 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
191 template <>
192 void SVGElement::StringAttributesInfo::Reset(uint8_t aAttrEnum) {
193 mValues[aAttrEnum].Init(aAttrEnum);
196 nsresult SVGElement::CopyInnerTo(mozilla::dom::Element* aDest) {
197 nsresult rv = Element::CopyInnerTo(aDest);
198 NS_ENSURE_SUCCESS(rv, rv);
200 auto* dest = static_cast<SVGElement*>(aDest);
202 // cloning a node must retain its internal nonce slot
203 if (auto* nonce = static_cast<nsString*>(GetProperty(nsGkAtoms::nonce))) {
204 dest->SetNonce(*nonce);
207 // If our destination is a print document, copy all the relevant length values
208 // etc so that they match the state of the original node.
209 if (aDest->OwnerDoc()->IsStaticDocument()) {
210 LengthAttributesInfo lengthInfo = GetLengthInfo();
211 dest->GetLengthInfo().CopyAllFrom(lengthInfo);
212 if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) {
213 for (uint32_t i = 0; i < lengthInfo.mCount; i++) {
214 nsCSSPropertyID propId =
215 SVGGeometryProperty::AttrEnumToCSSPropId(this, i);
217 // We don't map use element width/height currently. We can remove this
218 // test when we do.
219 if (propId != eCSSProperty_UNKNOWN) {
220 dest->SMILOverrideStyle()->SetSMILValue(propId,
221 lengthInfo.mValues[i]);
225 dest->GetNumberInfo().CopyAllFrom(GetNumberInfo());
226 dest->GetNumberPairInfo().CopyAllFrom(GetNumberPairInfo());
227 dest->GetIntegerInfo().CopyAllFrom(GetIntegerInfo());
228 dest->GetIntegerPairInfo().CopyAllFrom(GetIntegerPairInfo());
229 dest->GetBooleanInfo().CopyAllFrom(GetBooleanInfo());
230 if (const auto* orient = GetAnimatedOrient()) {
231 *dest->GetAnimatedOrient() = *orient;
233 if (const auto* viewBox = GetAnimatedViewBox()) {
234 *dest->GetAnimatedViewBox() = *viewBox;
236 if (const auto* preserveAspectRatio = GetAnimatedPreserveAspectRatio()) {
237 *dest->GetAnimatedPreserveAspectRatio() = *preserveAspectRatio;
239 dest->GetEnumInfo().CopyAllFrom(GetEnumInfo());
240 dest->GetStringInfo().CopyAllFrom(GetStringInfo());
241 dest->GetLengthListInfo().CopyAllFrom(GetLengthListInfo());
242 dest->GetNumberListInfo().CopyAllFrom(GetNumberListInfo());
243 if (const auto* pointList = GetAnimatedPointList()) {
244 *dest->GetAnimatedPointList() = *pointList;
246 if (const auto* pathSegList = GetAnimPathSegList()) {
247 *dest->GetAnimPathSegList() = *pathSegList;
248 dest->SMILOverrideStyle()->SetSMILValue(nsCSSPropertyID::eCSSProperty_d,
249 *pathSegList);
251 if (const auto* transformList = GetAnimatedTransformList()) {
252 *dest->GetAnimatedTransformList(DO_ALLOCATE) = *transformList;
254 if (const auto* animateMotionTransform = GetAnimateMotionTransform()) {
255 dest->SetAnimateMotionTransform(animateMotionTransform);
257 if (const auto* smilOverrideStyleDecoration =
258 GetSMILOverrideStyleDeclaration()) {
259 RefPtr<DeclarationBlock> declClone = smilOverrideStyleDecoration->Clone();
260 declClone->SetDirty();
261 dest->SetSMILOverrideStyleDeclaration(*declClone);
265 return NS_OK;
268 //----------------------------------------------------------------------
269 // SVGElement methods
271 void SVGElement::DidAnimateClass() {
272 // For Servo, snapshot the element before we change it.
273 PresShell* presShell = OwnerDoc()->GetPresShell();
274 if (presShell) {
275 if (nsPresContext* presContext = presShell->GetPresContext()) {
276 presContext->RestyleManager()->ClassAttributeWillBeChangedBySMIL(this);
280 nsAutoString src;
281 mClassAttribute.GetAnimValue(src, this);
282 if (!mClassAnimAttr) {
283 mClassAnimAttr = MakeUnique<nsAttrValue>();
285 mClassAnimAttr->ParseAtomArray(src);
287 // FIXME(emilio): This re-selector-matches, but we do the snapshot stuff right
288 // above... Is this needed anymore?
289 if (presShell) {
290 presShell->RestyleForAnimation(this, RestyleHint::RESTYLE_SELF);
292 DidAnimateAttribute(kNameSpaceID_None, nsGkAtoms::_class);
295 nsresult SVGElement::Init() {
296 // Set up length attributes - can't do this in the constructor
297 // because we can't do a virtual call at that point
299 GetLengthInfo().ResetAll();
300 GetNumberInfo().ResetAll();
301 GetNumberPairInfo().ResetAll();
302 GetIntegerInfo().ResetAll();
303 GetIntegerPairInfo().ResetAll();
304 GetBooleanInfo().ResetAll();
305 GetEnumInfo().ResetAll();
307 if (SVGAnimatedOrient* orient = GetAnimatedOrient()) {
308 orient->Init();
311 if (SVGAnimatedViewBox* viewBox = GetAnimatedViewBox()) {
312 viewBox->Init();
315 if (SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
316 GetAnimatedPreserveAspectRatio()) {
317 preserveAspectRatio->Init();
320 GetLengthListInfo().ResetAll();
321 GetNumberListInfo().ResetAll();
323 // No need to reset SVGPointList since the default value is always the same
324 // (an empty list).
326 // No need to reset SVGPathData since the default value is always the same
327 // (an empty list).
329 GetStringInfo().ResetAll();
330 return NS_OK;
333 //----------------------------------------------------------------------
334 // Implementation
336 //----------------------------------------------------------------------
337 // nsIContent methods
339 nsresult SVGElement::BindToTree(BindContext& aContext, nsINode& aParent) {
340 nsresult rv = SVGElementBase::BindToTree(aContext, aParent);
341 NS_ENSURE_SUCCESS(rv, rv);
343 // Hide any nonce from the DOM, but keep the internal value of the
344 // nonce by copying and resetting the internal nonce value.
345 if (HasFlag(NODE_HAS_NONCE_AND_HEADER_CSP) && IsInComposedDoc() &&
346 OwnerDoc()->GetBrowsingContext()) {
347 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
348 "SVGElement::ResetNonce::Runnable",
349 [self = RefPtr<SVGElement>(this)]() {
350 nsAutoString nonce;
351 self->GetNonce(nonce);
352 self->SetAttr(kNameSpaceID_None, nsGkAtoms::nonce, u""_ns, true);
353 self->SetNonce(nonce);
354 }));
357 return NS_OK;
360 void SVGElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
361 const nsAttrValue* aValue,
362 const nsAttrValue* aOldValue,
363 nsIPrincipal* aSubjectPrincipal, bool aNotify) {
364 if (IsEventAttributeName(aName) && aValue) {
365 MOZ_ASSERT(aValue->Type() == nsAttrValue::eString,
366 "Expected string value for script body");
367 SetEventHandler(GetEventNameForAttr(aName), aValue->GetStringValue());
370 // The nonce will be copied over to an internal slot and cleared from the
371 // Element within BindToTree to avoid CSS Selector nonce exfiltration if
372 // the CSP list contains a header-delivered CSP.
373 if (nsGkAtoms::nonce == aName && kNameSpaceID_None == aNamespaceID) {
374 if (aValue) {
375 SetNonce(aValue->GetStringValue());
376 if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) {
377 SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP);
379 } else {
380 RemoveNonce();
384 return SVGElementBase::AfterSetAttr(aNamespaceID, aName, aValue, aOldValue,
385 aSubjectPrincipal, aNotify);
388 bool SVGElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
389 const nsAString& aValue,
390 nsIPrincipal* aMaybeScriptedPrincipal,
391 nsAttrValue& aResult) {
392 nsresult rv = NS_OK;
393 bool foundMatch = false;
394 bool didSetResult = false;
396 if (aNamespaceID == kNameSpaceID_None) {
397 // Check for SVGAnimatedLength attribute
398 LengthAttributesInfo lengthInfo = GetLengthInfo();
400 uint32_t i;
401 for (i = 0; i < lengthInfo.mCount; i++) {
402 if (aAttribute == lengthInfo.mInfos[i].mName) {
403 rv = lengthInfo.mValues[i].SetBaseValueString(aValue, this, false);
404 if (NS_FAILED(rv)) {
405 lengthInfo.Reset(i);
406 } else {
407 aResult.SetTo(lengthInfo.mValues[i], &aValue);
408 didSetResult = true;
410 foundMatch = true;
411 break;
415 if (!foundMatch) {
416 // Check for SVGAnimatedLengthList attribute
417 LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
418 for (i = 0; i < lengthListInfo.mCount; i++) {
419 if (aAttribute == lengthListInfo.mInfos[i].mName) {
420 rv = lengthListInfo.mValues[i].SetBaseValueString(aValue);
421 if (NS_FAILED(rv)) {
422 lengthListInfo.Reset(i);
423 } else {
424 aResult.SetTo(lengthListInfo.mValues[i].GetBaseValue(), &aValue);
425 didSetResult = true;
427 foundMatch = true;
428 break;
433 if (!foundMatch) {
434 // Check for SVGAnimatedNumberList attribute
435 NumberListAttributesInfo numberListInfo = GetNumberListInfo();
436 for (i = 0; i < numberListInfo.mCount; i++) {
437 if (aAttribute == numberListInfo.mInfos[i].mName) {
438 rv = numberListInfo.mValues[i].SetBaseValueString(aValue);
439 if (NS_FAILED(rv)) {
440 numberListInfo.Reset(i);
441 } else {
442 aResult.SetTo(numberListInfo.mValues[i].GetBaseValue(), &aValue);
443 didSetResult = true;
445 foundMatch = true;
446 break;
451 if (!foundMatch) {
452 // Check for SVGAnimatedPointList attribute
453 if (GetPointListAttrName() == aAttribute) {
454 if (SVGAnimatedPointList* pointList = GetAnimatedPointList()) {
455 pointList->SetBaseValueString(aValue);
456 // The spec says we parse everything up to the failure, so we DON'T
457 // need to check the result of SetBaseValueString or call
458 // pointList->ClearBaseValue() if it fails
459 aResult.SetTo(pointList->GetBaseValue(), &aValue);
460 didSetResult = true;
461 foundMatch = true;
466 if (!foundMatch) {
467 // Check for SVGAnimatedPathSegList attribute
468 if (GetPathDataAttrName() == aAttribute) {
469 if (SVGAnimatedPathSegList* segList = GetAnimPathSegList()) {
470 segList->SetBaseValueString(aValue);
471 // The spec says we parse everything up to the failure, so we DON'T
472 // need to check the result of SetBaseValueString or call
473 // segList->ClearBaseValue() if it fails
474 aResult.SetTo(segList->GetBaseValue(), &aValue);
475 didSetResult = true;
476 foundMatch = true;
481 if (!foundMatch) {
482 // Check for SVGAnimatedNumber attribute
483 NumberAttributesInfo numberInfo = GetNumberInfo();
484 for (i = 0; i < numberInfo.mCount; i++) {
485 if (aAttribute == numberInfo.mInfos[i].mName) {
486 rv = numberInfo.mValues[i].SetBaseValueString(aValue, this);
487 if (NS_FAILED(rv)) {
488 numberInfo.Reset(i);
489 } else {
490 aResult.SetTo(numberInfo.mValues[i].GetBaseValue(), &aValue);
491 didSetResult = true;
493 foundMatch = true;
494 break;
499 if (!foundMatch) {
500 // Check for SVGAnimatedNumberPair attribute
501 NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo();
502 for (i = 0; i < numberPairInfo.mCount; i++) {
503 if (aAttribute == numberPairInfo.mInfos[i].mName) {
504 rv = numberPairInfo.mValues[i].SetBaseValueString(aValue, this);
505 if (NS_FAILED(rv)) {
506 numberPairInfo.Reset(i);
507 } else {
508 aResult.SetTo(numberPairInfo.mValues[i], &aValue);
509 didSetResult = true;
511 foundMatch = true;
512 break;
517 if (!foundMatch) {
518 // Check for SVGAnimatedInteger attribute
519 IntegerAttributesInfo integerInfo = GetIntegerInfo();
520 for (i = 0; i < integerInfo.mCount; i++) {
521 if (aAttribute == integerInfo.mInfos[i].mName) {
522 rv = integerInfo.mValues[i].SetBaseValueString(aValue, this);
523 if (NS_FAILED(rv)) {
524 integerInfo.Reset(i);
525 } else {
526 aResult.SetTo(integerInfo.mValues[i].GetBaseValue(), &aValue);
527 didSetResult = true;
529 foundMatch = true;
530 break;
535 if (!foundMatch) {
536 // Check for SVGAnimatedIntegerPair attribute
537 IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo();
538 for (i = 0; i < integerPairInfo.mCount; i++) {
539 if (aAttribute == integerPairInfo.mInfos[i].mName) {
540 rv = integerPairInfo.mValues[i].SetBaseValueString(aValue, this);
541 if (NS_FAILED(rv)) {
542 integerPairInfo.Reset(i);
543 } else {
544 aResult.SetTo(integerPairInfo.mValues[i], &aValue);
545 didSetResult = true;
547 foundMatch = true;
548 break;
553 if (!foundMatch) {
554 // Check for SVGAnimatedBoolean attribute
555 BooleanAttributesInfo booleanInfo = GetBooleanInfo();
556 for (i = 0; i < booleanInfo.mCount; i++) {
557 if (aAttribute == booleanInfo.mInfos[i].mName) {
558 nsAtom* valAtom = NS_GetStaticAtom(aValue);
559 rv = valAtom ? booleanInfo.mValues[i].SetBaseValueAtom(valAtom, this)
560 : NS_ERROR_DOM_SYNTAX_ERR;
561 if (NS_FAILED(rv)) {
562 booleanInfo.Reset(i);
563 } else {
564 aResult.SetTo(valAtom);
565 didSetResult = true;
567 foundMatch = true;
568 break;
573 if (!foundMatch) {
574 // Check for SVGAnimatedEnumeration attribute
575 EnumAttributesInfo enumInfo = GetEnumInfo();
576 for (i = 0; i < enumInfo.mCount; i++) {
577 if (aAttribute == enumInfo.mInfos[i].mName) {
578 RefPtr<nsAtom> valAtom = NS_Atomize(aValue);
579 if (!enumInfo.mValues[i].SetBaseValueAtom(valAtom, this)) {
580 // Exact error value does not matter; we just need to mark the
581 // parse as failed.
582 rv = NS_ERROR_FAILURE;
583 enumInfo.Reset(i);
584 } else {
585 aResult.SetTo(valAtom);
586 didSetResult = true;
588 foundMatch = true;
589 break;
594 if (!foundMatch) {
595 // Check for conditional processing attributes
596 nsCOMPtr<SVGTests> tests = do_QueryObject(this);
597 if (tests && tests->ParseConditionalProcessingAttribute(
598 aAttribute, aValue, aResult)) {
599 foundMatch = true;
603 if (!foundMatch) {
604 // Check for StringList attribute
605 StringListAttributesInfo stringListInfo = GetStringListInfo();
606 for (i = 0; i < stringListInfo.mCount; i++) {
607 if (aAttribute == stringListInfo.mInfos[i].mName) {
608 rv = stringListInfo.mValues[i].SetValue(aValue);
609 if (NS_FAILED(rv)) {
610 stringListInfo.Reset(i);
611 } else {
612 aResult.SetTo(stringListInfo.mValues[i], &aValue);
613 didSetResult = true;
615 foundMatch = true;
616 break;
621 if (!foundMatch) {
622 // Check for orient attribute
623 if (aAttribute == nsGkAtoms::orient) {
624 SVGAnimatedOrient* orient = GetAnimatedOrient();
625 if (orient) {
626 rv = orient->SetBaseValueString(aValue, this, false);
627 if (NS_FAILED(rv)) {
628 orient->Init();
629 } else {
630 aResult.SetTo(*orient, &aValue);
631 didSetResult = true;
633 foundMatch = true;
635 // Check for viewBox attribute
636 } else if (aAttribute == nsGkAtoms::viewBox) {
637 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
638 if (viewBox) {
639 rv = viewBox->SetBaseValueString(aValue, this, false);
640 if (NS_FAILED(rv)) {
641 viewBox->Init();
642 } else {
643 aResult.SetTo(*viewBox, &aValue);
644 didSetResult = true;
646 foundMatch = true;
648 // Check for preserveAspectRatio attribute
649 } else if (aAttribute == nsGkAtoms::preserveAspectRatio) {
650 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
651 GetAnimatedPreserveAspectRatio();
652 if (preserveAspectRatio) {
653 rv = preserveAspectRatio->SetBaseValueString(aValue, this, false);
654 if (NS_FAILED(rv)) {
655 preserveAspectRatio->Init();
656 } else {
657 aResult.SetTo(*preserveAspectRatio, &aValue);
658 didSetResult = true;
660 foundMatch = true;
662 // Check for SVGAnimatedTransformList attribute
663 } else if (GetTransformListAttrName() == aAttribute) {
664 // The transform attribute is being set, so we must ensure that the
665 // SVGAnimatedTransformList is/has been allocated:
666 SVGAnimatedTransformList* transformList =
667 GetAnimatedTransformList(DO_ALLOCATE);
668 rv = transformList->SetBaseValueString(aValue, this);
669 if (NS_FAILED(rv)) {
670 transformList->ClearBaseValue();
671 } else {
672 aResult.SetTo(transformList->GetBaseValue(), &aValue);
673 didSetResult = true;
675 foundMatch = true;
676 } else if (aAttribute == nsGkAtoms::tabindex) {
677 didSetResult = aResult.ParseIntValue(aValue);
678 foundMatch = true;
682 if (aAttribute == nsGkAtoms::_class) {
683 mClassAttribute.SetBaseValue(aValue, this, false);
684 aResult.ParseAtomArray(aValue);
685 return true;
688 if (aAttribute == nsGkAtoms::rel) {
689 aResult.ParseAtomArray(aValue);
690 return true;
694 if (!foundMatch) {
695 // Check for SVGAnimatedString attribute
696 StringAttributesInfo stringInfo = GetStringInfo();
697 for (uint32_t i = 0; i < stringInfo.mCount; i++) {
698 if (aNamespaceID == stringInfo.mInfos[i].mNamespaceID &&
699 aAttribute == stringInfo.mInfos[i].mName) {
700 stringInfo.mValues[i].SetBaseValue(aValue, this, false);
701 foundMatch = true;
702 break;
707 if (foundMatch) {
708 if (NS_FAILED(rv)) {
709 ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue);
710 return false;
712 if (!didSetResult) {
713 aResult.SetTo(aValue);
715 return true;
718 return SVGElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
719 aMaybeScriptedPrincipal, aResult);
722 void SVGElement::UnsetAttrInternal(int32_t aNamespaceID, nsAtom* aName,
723 bool aNotify) {
724 // XXXbz there's a bunch of redundancy here with AfterSetAttr.
725 // Maybe consolidate?
727 if (aNamespaceID == kNameSpaceID_None) {
728 if (IsEventAttributeName(aName)) {
729 EventListenerManager* manager = GetExistingListenerManager();
730 if (manager) {
731 nsAtom* eventName = GetEventNameForAttr(aName);
732 manager->RemoveEventHandler(eventName);
734 return;
737 // Check if this is a length attribute going away
738 LengthAttributesInfo lenInfo = GetLengthInfo();
740 for (uint32_t i = 0; i < lenInfo.mCount; i++) {
741 if (aName == lenInfo.mInfos[i].mName) {
742 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
743 lenInfo.Reset(i);
744 return;
748 // Check if this is a length list attribute going away
749 LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
751 for (uint32_t i = 0; i < lengthListInfo.mCount; i++) {
752 if (aName == lengthListInfo.mInfos[i].mName) {
753 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
754 lengthListInfo.Reset(i);
755 return;
759 // Check if this is a number list attribute going away
760 NumberListAttributesInfo numberListInfo = GetNumberListInfo();
762 for (uint32_t i = 0; i < numberListInfo.mCount; i++) {
763 if (aName == numberListInfo.mInfos[i].mName) {
764 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
765 numberListInfo.Reset(i);
766 return;
770 // Check if this is a point list attribute going away
771 if (GetPointListAttrName() == aName) {
772 SVGAnimatedPointList* pointList = GetAnimatedPointList();
773 if (pointList) {
774 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
775 pointList->ClearBaseValue();
776 return;
780 // Check if this is a path segment list attribute going away
781 if (GetPathDataAttrName() == aName) {
782 SVGAnimatedPathSegList* segList = GetAnimPathSegList();
783 if (segList) {
784 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
785 segList->ClearBaseValue();
786 return;
790 // Check if this is a number attribute going away
791 NumberAttributesInfo numInfo = GetNumberInfo();
793 for (uint32_t i = 0; i < numInfo.mCount; i++) {
794 if (aName == numInfo.mInfos[i].mName) {
795 numInfo.Reset(i);
796 return;
800 // Check if this is a number pair attribute going away
801 NumberPairAttributesInfo numPairInfo = GetNumberPairInfo();
803 for (uint32_t i = 0; i < numPairInfo.mCount; i++) {
804 if (aName == numPairInfo.mInfos[i].mName) {
805 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
806 numPairInfo.Reset(i);
807 return;
811 // Check if this is an integer attribute going away
812 IntegerAttributesInfo intInfo = GetIntegerInfo();
814 for (uint32_t i = 0; i < intInfo.mCount; i++) {
815 if (aName == intInfo.mInfos[i].mName) {
816 intInfo.Reset(i);
817 return;
821 // Check if this is an integer pair attribute going away
822 IntegerPairAttributesInfo intPairInfo = GetIntegerPairInfo();
824 for (uint32_t i = 0; i < intPairInfo.mCount; i++) {
825 if (aName == intPairInfo.mInfos[i].mName) {
826 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
827 intPairInfo.Reset(i);
828 return;
832 // Check if this is a boolean attribute going away
833 BooleanAttributesInfo boolInfo = GetBooleanInfo();
835 for (uint32_t i = 0; i < boolInfo.mCount; i++) {
836 if (aName == boolInfo.mInfos[i].mName) {
837 boolInfo.Reset(i);
838 return;
842 // Check if this is an enum attribute going away
843 EnumAttributesInfo enumInfo = GetEnumInfo();
845 for (uint32_t i = 0; i < enumInfo.mCount; i++) {
846 if (aName == enumInfo.mInfos[i].mName) {
847 enumInfo.Reset(i);
848 return;
852 // Check if this is an orient attribute going away
853 if (aName == nsGkAtoms::orient) {
854 SVGAnimatedOrient* orient = GetAnimatedOrient();
855 if (orient) {
856 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
857 orient->Init();
858 return;
862 // Check if this is a viewBox attribute going away
863 if (aName == nsGkAtoms::viewBox) {
864 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
865 if (viewBox) {
866 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
867 viewBox->Init();
868 return;
872 // Check if this is a preserveAspectRatio attribute going away
873 if (aName == nsGkAtoms::preserveAspectRatio) {
874 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
875 GetAnimatedPreserveAspectRatio();
876 if (preserveAspectRatio) {
877 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
878 preserveAspectRatio->Init();
879 return;
883 // Check if this is a transform list attribute going away
884 if (GetTransformListAttrName() == aName) {
885 SVGAnimatedTransformList* transformList = GetAnimatedTransformList();
886 if (transformList) {
887 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
888 transformList->ClearBaseValue();
889 return;
893 // Check for conditional processing attributes
894 nsCOMPtr<SVGTests> tests = do_QueryObject(this);
895 if (tests && tests->IsConditionalProcessingAttribute(aName)) {
896 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
897 tests->UnsetAttr(aName);
898 return;
901 // Check if this is a string list attribute going away
902 StringListAttributesInfo stringListInfo = GetStringListInfo();
904 for (uint32_t i = 0; i < stringListInfo.mCount; i++) {
905 if (aName == stringListInfo.mInfos[i].mName) {
906 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
907 stringListInfo.Reset(i);
908 return;
912 if (aName == nsGkAtoms::_class) {
913 mClassAttribute.Init();
914 return;
918 // Check if this is a string attribute going away
919 StringAttributesInfo stringInfo = GetStringInfo();
921 for (uint32_t i = 0; i < stringInfo.mCount; i++) {
922 if (aNamespaceID == stringInfo.mInfos[i].mNamespaceID &&
923 aName == stringInfo.mInfos[i].mName) {
924 stringInfo.Reset(i);
925 return;
930 void SVGElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
931 const nsAttrValue* aValue, bool aNotify) {
932 if (!aValue) {
933 UnsetAttrInternal(aNamespaceID, aName, aNotify);
935 return SVGElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify);
938 nsChangeHint SVGElement::GetAttributeChangeHint(const nsAtom* aAttribute,
939 int32_t aModType) const {
940 nsChangeHint retval =
941 SVGElementBase::GetAttributeChangeHint(aAttribute, aModType);
943 nsCOMPtr<SVGTests> tests = do_QueryObject(const_cast<SVGElement*>(this));
944 if (tests && tests->IsConditionalProcessingAttribute(aAttribute)) {
945 // It would be nice to only reconstruct the frame if the value returned by
946 // SVGTests::PassesConditionalProcessingTests has changed, but we don't
947 // know that
948 retval |= nsChangeHint_ReconstructFrame;
950 return retval;
953 void SVGElement::NodeInfoChanged(Document* aOldDoc) {
954 SVGElementBase::NodeInfoChanged(aOldDoc);
957 NS_IMETHODIMP_(bool)
958 SVGElement::IsAttributeMapped(const nsAtom* name) const {
959 if (name == nsGkAtoms::lang) {
960 return true;
963 if (IsSVGAnimationElement()) {
964 return SVGElementBase::IsAttributeMapped(name);
967 static const MappedAttributeEntry attributes[] = {
968 // Properties that we don't support are commented out.
969 // { nsGkAtoms::alignment_baseline },
970 // { nsGkAtoms::baseline_shift },
971 {nsGkAtoms::clip},
972 {nsGkAtoms::clip_path},
973 {nsGkAtoms::clip_rule},
974 {nsGkAtoms::color},
975 {nsGkAtoms::colorInterpolation},
976 {nsGkAtoms::colorInterpolationFilters},
977 {nsGkAtoms::cursor},
978 {nsGkAtoms::direction},
979 {nsGkAtoms::display},
980 {nsGkAtoms::dominant_baseline},
981 {nsGkAtoms::fill},
982 {nsGkAtoms::fill_opacity},
983 {nsGkAtoms::fill_rule},
984 {nsGkAtoms::filter},
985 {nsGkAtoms::flood_color},
986 {nsGkAtoms::flood_opacity},
987 {nsGkAtoms::font_family},
988 {nsGkAtoms::font_size},
989 {nsGkAtoms::font_size_adjust},
990 {nsGkAtoms::font_stretch},
991 {nsGkAtoms::font_style},
992 {nsGkAtoms::font_variant},
993 {nsGkAtoms::fontWeight},
994 {nsGkAtoms::image_rendering},
995 {nsGkAtoms::letter_spacing},
996 {nsGkAtoms::lighting_color},
997 {nsGkAtoms::marker_end},
998 {nsGkAtoms::marker_mid},
999 {nsGkAtoms::marker_start},
1000 {nsGkAtoms::mask},
1001 {nsGkAtoms::mask_type},
1002 {nsGkAtoms::opacity},
1003 {nsGkAtoms::overflow},
1004 {nsGkAtoms::paint_order},
1005 {nsGkAtoms::pointer_events},
1006 {nsGkAtoms::shape_rendering},
1007 {nsGkAtoms::stop_color},
1008 {nsGkAtoms::stop_opacity},
1009 {nsGkAtoms::stroke},
1010 {nsGkAtoms::stroke_dasharray},
1011 {nsGkAtoms::stroke_dashoffset},
1012 {nsGkAtoms::stroke_linecap},
1013 {nsGkAtoms::stroke_linejoin},
1014 {nsGkAtoms::stroke_miterlimit},
1015 {nsGkAtoms::stroke_opacity},
1016 {nsGkAtoms::stroke_width},
1017 {nsGkAtoms::text_anchor},
1018 {nsGkAtoms::text_decoration},
1019 {nsGkAtoms::text_rendering},
1020 {nsGkAtoms::transform_origin},
1021 {nsGkAtoms::unicode_bidi},
1022 {nsGkAtoms::vector_effect},
1023 {nsGkAtoms::visibility},
1024 {nsGkAtoms::white_space},
1025 {nsGkAtoms::word_spacing},
1026 {nsGkAtoms::writing_mode},
1027 {nullptr}};
1029 static const MappedAttributeEntry* const map[] = {attributes};
1031 return FindAttributeDependence(name, map) ||
1032 SVGElementBase::IsAttributeMapped(name);
1035 //----------------------------------------------------------------------
1036 // Element methods
1038 // forwarded to Element implementations
1040 //----------------------------------------------------------------------
1042 SVGSVGElement* SVGElement::GetOwnerSVGElement() {
1043 nsIContent* ancestor = GetFlattenedTreeParent();
1045 while (ancestor && ancestor->IsSVGElement()) {
1046 if (ancestor->IsSVGElement(nsGkAtoms::foreignObject)) {
1047 return nullptr;
1049 if (auto* svg = SVGSVGElement::FromNode(ancestor)) {
1050 return svg;
1052 ancestor = ancestor->GetFlattenedTreeParent();
1055 // we don't have an ancestor <svg> element...
1056 return nullptr;
1059 SVGElement* SVGElement::GetViewportElement() {
1060 return SVGContentUtils::GetNearestViewportElement(this);
1063 already_AddRefed<DOMSVGAnimatedString> SVGElement::ClassName() {
1064 return mClassAttribute.ToDOMAnimatedString(this);
1067 /* static */
1068 bool SVGElement::UpdateDeclarationBlockFromLength(
1069 StyleLockedDeclarationBlock& aBlock, nsCSSPropertyID aPropId,
1070 const SVGAnimatedLength& aLength, ValToUse aValToUse) {
1071 float value;
1072 if (aValToUse == ValToUse::Anim) {
1073 value = aLength.GetAnimValInSpecifiedUnits();
1074 } else {
1075 MOZ_ASSERT(aValToUse == ValToUse::Base);
1076 value = aLength.GetBaseValInSpecifiedUnits();
1079 // SVG parser doesn't check non-negativity of some parsed value, we should not
1080 // pass those to CSS side.
1081 if (value < 0 &&
1082 SVGGeometryProperty::IsNonNegativeGeometryProperty(aPropId)) {
1083 return false;
1086 nsCSSUnit cssUnit =
1087 SVGLength::SpecifiedUnitTypeToCSSUnit(aLength.GetSpecifiedUnitType());
1089 if (cssUnit == eCSSUnit_Percent) {
1090 Servo_DeclarationBlock_SetPercentValue(&aBlock, aPropId, value / 100.f);
1091 } else {
1092 Servo_DeclarationBlock_SetLengthValue(&aBlock, aPropId, value, cssUnit);
1095 return true;
1098 /* static */
1099 bool SVGElement::UpdateDeclarationBlockFromPath(
1100 StyleLockedDeclarationBlock& aBlock, const SVGAnimatedPathSegList& aPath,
1101 ValToUse aValToUse) {
1102 const SVGPathData& pathData =
1103 aValToUse == ValToUse::Anim ? aPath.GetAnimValue() : aPath.GetBaseValue();
1105 // SVGPathData::mData is fallible but rust binding accepts nsTArray only, so
1106 // we need to point to one or the other. Fortunately, fallible and infallible
1107 // array types can be implicitly converted provided they are const.
1109 // FIXME: here we just convert the data structure from cpp verion into rust
1110 // version. We don't do any normalization for the path data from d attribute.
1111 // Based on the current discussion of https://github.com/w3c/svgwg/issues/321,
1112 // we may have to convert the relative commands into absolute commands.
1113 // The normalization should be fixed in Bug 1489392. Besides, Bug 1714238
1114 // will use the same data structure, so we may simplify this more.
1115 const nsTArray<float>& asInFallibleArray = pathData.RawData();
1116 Servo_DeclarationBlock_SetPathValue(&aBlock, eCSSProperty_d,
1117 &asInFallibleArray);
1118 return true;
1121 //------------------------------------------------------------------------
1122 // Helper class: MappedAttrParser, for parsing values of mapped attributes
1124 namespace {
1126 class MOZ_STACK_CLASS MappedAttrParser {
1127 public:
1128 explicit MappedAttrParser(SVGElement& aElement,
1129 StyleLockedDeclarationBlock* aDecl)
1130 : mElement(aElement), mDecl(aDecl) {
1131 if (mDecl) {
1132 Servo_DeclarationBlock_Clear(mDecl);
1135 ~MappedAttrParser() {
1136 MOZ_ASSERT(!mDecl,
1137 "If mDecl was initialized, it should have been returned via "
1138 "TakeDeclarationBlock (and have its pointer cleared)");
1141 // Parses a mapped attribute value.
1142 void ParseMappedAttrValue(nsAtom* aMappedAttrName,
1143 const nsAString& aMappedAttrValue);
1145 void TellStyleAlreadyParsedResult(nsAtom const* aAtom,
1146 SVGAnimatedLength const& aLength);
1147 void TellStyleAlreadyParsedResult(const SVGAnimatedPathSegList& aPath);
1149 // If we've parsed any values for mapped attributes, this method returns the
1150 // already_AddRefed declaration block that incorporates the parsed values.
1151 // Otherwise, this method returns null.
1152 already_AddRefed<StyleLockedDeclarationBlock> TakeDeclarationBlock() {
1153 return mDecl.forget();
1156 StyleLockedDeclarationBlock& EnsureDeclarationBlock() {
1157 if (!mDecl) {
1158 mDecl = Servo_DeclarationBlock_CreateEmpty().Consume();
1160 return *mDecl;
1163 URLExtraData& EnsureExtraData() {
1164 if (!mExtraData) {
1165 mExtraData = mElement.GetURLDataForStyleAttr();
1167 return *mExtraData;
1170 private:
1171 // For reporting use counters
1172 SVGElement& mElement;
1174 // Declaration for storing parsed values (lazily initialized).
1175 RefPtr<StyleLockedDeclarationBlock> mDecl;
1177 // URL data for parsing stuff. Also lazy.
1178 RefPtr<URLExtraData> mExtraData;
1181 void MappedAttrParser::ParseMappedAttrValue(nsAtom* aMappedAttrName,
1182 const nsAString& aMappedAttrValue) {
1183 // Get the nsCSSPropertyID ID for our mapped attribute.
1184 nsCSSPropertyID propertyID =
1185 nsCSSProps::LookupProperty(nsAutoAtomCString(aMappedAttrName));
1186 if (propertyID != eCSSProperty_UNKNOWN) {
1187 bool changed = false; // outparam for ParseProperty.
1188 NS_ConvertUTF16toUTF8 value(aMappedAttrValue);
1190 auto* doc = mElement.OwnerDoc();
1191 changed = Servo_DeclarationBlock_SetPropertyById(
1192 &EnsureDeclarationBlock(), propertyID, &value, false,
1193 &EnsureExtraData(), StyleParsingMode::ALLOW_UNITLESS_LENGTH,
1194 doc->GetCompatibilityMode(), doc->CSSLoader(), StyleCssRuleType::Style,
1195 {});
1197 // TODO(emilio): If we want to record these from CSSOM more generally, we
1198 // can pass the document use counters down the FFI call. For now manually
1199 // count them.
1200 if (changed && StaticPrefs::layout_css_use_counters_enabled()) {
1201 UseCounter useCounter = nsCSSProps::UseCounterFor(propertyID);
1202 MOZ_ASSERT(useCounter != eUseCounter_UNKNOWN);
1203 doc->SetUseCounter(useCounter);
1205 return;
1207 MOZ_ASSERT(aMappedAttrName == nsGkAtoms::lang,
1208 "Only 'lang' should be unrecognized!");
1209 // CSS parser doesn't know about 'lang', so we need to handle it specially.
1210 if (aMappedAttrName == nsGkAtoms::lang) {
1211 propertyID = eCSSProperty__x_lang;
1212 RefPtr<nsAtom> atom = NS_Atomize(aMappedAttrValue);
1213 Servo_DeclarationBlock_SetIdentStringValue(&EnsureDeclarationBlock(),
1214 propertyID, atom);
1218 void MappedAttrParser::TellStyleAlreadyParsedResult(
1219 nsAtom const* aAtom, SVGAnimatedLength const& aLength) {
1220 nsCSSPropertyID propertyID =
1221 nsCSSProps::LookupProperty(nsAutoAtomCString(aAtom));
1222 SVGElement::UpdateDeclarationBlockFromLength(EnsureDeclarationBlock(),
1223 propertyID, aLength,
1224 SVGElement::ValToUse::Base);
1227 void MappedAttrParser::TellStyleAlreadyParsedResult(
1228 const SVGAnimatedPathSegList& aPath) {
1229 SVGElement::UpdateDeclarationBlockFromPath(EnsureDeclarationBlock(), aPath,
1230 SVGElement::ValToUse::Base);
1233 } // namespace
1235 //----------------------------------------------------------------------
1236 // Implementation Helpers:
1238 void SVGElement::UpdateMappedDeclarationBlock() {
1239 MOZ_ASSERT(IsPendingMappedAttributeEvaluation());
1240 MappedAttrParser mappedAttrParser(*this, mAttrs.GetMappedDeclarationBlock());
1242 const bool lengthAffectsStyle =
1243 SVGGeometryProperty::ElementMapsLengthsToStyle(this);
1245 uint32_t i = 0;
1246 while (BorrowedAttrInfo info = GetAttrInfoAt(i++)) {
1247 const nsAttrName* attrName = info.mName;
1248 if (!attrName->IsAtom() || !IsAttributeMapped(attrName->Atom())) {
1249 continue;
1252 if (attrName->Atom() == nsGkAtoms::lang &&
1253 HasAttr(kNameSpaceID_XML, nsGkAtoms::lang)) {
1254 // xml:lang has precedence, and will get set via Gecko_GetXMLLangValue().
1255 continue;
1258 if (lengthAffectsStyle) {
1259 auto const* length = GetAnimatedLength(attrName->Atom());
1261 if (length && length->HasBaseVal()) {
1262 // This is an element with geometry property set via SVG attribute,
1263 // and the attribute is already successfully parsed. We want to go
1264 // through the optimized path to tell the style system the result
1265 // directly, rather than let it parse the same thing again.
1266 mappedAttrParser.TellStyleAlreadyParsedResult(attrName->Atom(),
1267 *length);
1268 continue;
1272 if (attrName->Equals(nsGkAtoms::d, kNameSpaceID_None)) {
1273 const auto* path = GetAnimPathSegList();
1274 // Note: Only SVGPathElement has d attribute.
1275 MOZ_ASSERT(
1276 path,
1277 "SVGPathElement should have the non-null SVGAnimatedPathSegList");
1278 // The attribute should have been already successfully parsed.
1279 // We want to go through the optimized path to tell the style system
1280 // the result directly, rather than let it parse the same thing again.
1281 mappedAttrParser.TellStyleAlreadyParsedResult(*path);
1282 // Some other notes:
1283 // The syntax of CSS d property is different from SVG d attribute.
1284 // 1. CSS d proeprty accepts: none | path(<quoted string>);
1285 // 2. SVG d attribtue accepts: none | <string>
1286 // So we cannot use css parser to parse the SVG d attribute directly.
1287 // Besides, |mAttrs.AttrAt(i)| removes the quotes already, so the svg path
1288 // in |mAttrs.AttrAt(i)| would be something like `M0,0L1,1z` without the
1289 // quotes. So css tokenizer cannot recognize this as a quoted string, and
1290 // so svg_path::SVGPathData::parse() doesn't work for this. Fortunately,
1291 // we still can rely on the parsed result from
1292 // SVGElement::ParseAttribute() for d attribute.
1293 continue;
1296 nsAutoString value;
1297 info.mValue->ToString(value);
1298 mappedAttrParser.ParseMappedAttrValue(attrName->Atom(), value);
1300 mAttrs.SetMappedDeclarationBlock(mappedAttrParser.TakeDeclarationBlock());
1304 * Helper methods for the type-specific WillChangeXXX methods.
1306 * This method sends out appropriate pre-change notifications so that selector
1307 * restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop
1308 * matching) work, and it returns an nsAttrValue that _may_ contain the
1309 * attribute's pre-change value.
1311 * The nsAttrValue returned by this method depends on whether there are
1312 * mutation event listeners listening for changes to this element's attributes.
1313 * If not, then the object returned is empty. If there are, then the
1314 * nsAttrValue returned contains a serialized copy of the attribute's value
1315 * prior to the change, and this object should be passed to the corresponding
1316 * DidChangeXXX method call (assuming a WillChangeXXX call is required for the
1317 * SVG type - see comment below). This is necessary so that the 'prevValue'
1318 * property of the mutation event that is dispatched will correctly contain the
1319 * old value.
1321 * The reason we need to serialize the old value if there are mutation
1322 * event listeners is because the underlying nsAttrValue for the attribute
1323 * points directly to a parsed representation of the attribute (e.g. an
1324 * SVGAnimatedLengthList*) that is a member of the SVG element. That object
1325 * will have changed by the time DidChangeXXX has been called, so without the
1326 * serialization of the old attribute value that we provide, DidChangeXXX
1327 * would have no way to get the old value to pass to SetAttrAndNotify.
1329 * We only return the old value when there are mutation event listeners because
1330 * it's not needed otherwise, and because it's expensive to serialize the old
1331 * value. This is especially true for list type attributes, which may be built
1332 * up via the SVG DOM resulting in a large number of Will/DidModifyXXX calls
1333 * before the script finally finishes setting the attribute.
1335 * Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check
1336 * and filter out redundant changes. Before calling WillChangeXXX, the caller
1337 * should check whether the new and old values are actually the same, and skip
1338 * calling Will/DidChangeXXX if they are.
1340 * Also note that not all SVG types use this scheme. For types that can be
1341 * represented by an nsAttrValue without pointing back to an SVG object (e.g.
1342 * enums, booleans, integers) we can simply use SetParsedAttr which will do all
1343 * of the above for us. For such types there is no matching WillChangeXXX
1344 * method, only DidChangeXXX which calls SetParsedAttr.
1346 nsAttrValue SVGElement::WillChangeValue(
1347 nsAtom* aName, const mozAutoDocUpdate& aProofOfUpdate) {
1348 // We need an empty attr value:
1349 // a) to pass to BeforeSetAttr when GetParsedAttr returns nullptr
1350 // b) to store the old value in the case we have mutation listeners
1352 // We can use the same value for both purposes, because if GetParsedAttr
1353 // returns non-null its return value is what will get passed to BeforeSetAttr,
1354 // not matter what our mutation listener situation is.
1356 // Also, we should be careful to always return this value to benefit from
1357 // return value optimization.
1358 nsAttrValue emptyOrOldAttrValue;
1359 const nsAttrValue* attrValue = GetParsedAttr(aName);
1361 // We only need to set the old value if we have listeners since otherwise it
1362 // isn't used.
1363 if (attrValue && nsContentUtils::HasMutationListeners(
1364 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) {
1365 emptyOrOldAttrValue.SetToSerialized(*attrValue);
1368 uint8_t modType =
1369 attrValue ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
1370 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
1371 MutationObservers::NotifyAttributeWillChange(this, kNameSpaceID_None, aName,
1372 modType);
1374 // This is not strictly correct--the attribute value parameter for
1375 // BeforeSetAttr should reflect the value that *will* be set but that implies
1376 // allocating, e.g. an extra SVGAnimatedLength, and isn't necessary at the
1377 // moment since no SVG elements overload BeforeSetAttr. For now we just pass
1378 // the current value.
1379 const nsAttrValue* value = attrValue ? attrValue : &emptyOrOldAttrValue;
1380 BeforeSetAttr(kNameSpaceID_None, aName, value, kNotifyDocumentObservers);
1381 return emptyOrOldAttrValue;
1385 * Helper methods for the type-specific DidChangeXXX methods.
1387 * aEmptyOrOldValue will normally be the object returned from the corresponding
1388 * WillChangeXXX call. This is because:
1389 * a) WillChangeXXX will ensure the object is set when we have mutation
1390 * listeners, and
1391 * b) WillChangeXXX will ensure the object represents a serialized version of
1392 * the old attribute value so that the value doesn't change when the
1393 * underlying SVG type is updated.
1395 * aNewValue is replaced with the old value.
1397 void SVGElement::DidChangeValue(nsAtom* aName,
1398 const nsAttrValue& aEmptyOrOldValue,
1399 nsAttrValue& aNewValue,
1400 const mozAutoDocUpdate& aProofOfUpdate) {
1401 bool hasListeners = nsContentUtils::HasMutationListeners(
1402 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this);
1403 uint8_t modType =
1404 HasAttr(aName) ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
1405 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
1407 // XXX Really, the fourth argument to SetAttrAndNotify should be null if
1408 // aEmptyOrOldValue does not represent the actual previous value of the
1409 // attribute, but currently SVG elements do not even use the old attribute
1410 // value in |AfterSetAttr|, so this should be ok.
1411 SetAttrAndNotify(kNameSpaceID_None, aName, nullptr, &aEmptyOrOldValue,
1412 aNewValue, nullptr, modType, hasListeners,
1413 kNotifyDocumentObservers, kCallAfterSetAttr,
1414 GetComposedDoc(), aProofOfUpdate);
1417 void SVGElement::MaybeSerializeAttrBeforeRemoval(nsAtom* aName, bool aNotify) {
1418 if (!aNotify || !nsContentUtils::HasMutationListeners(
1419 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) {
1420 return;
1423 const nsAttrValue* attrValue = mAttrs.GetAttr(aName);
1424 if (!attrValue) return;
1426 nsAutoString serializedValue;
1427 attrValue->ToString(serializedValue);
1428 nsAttrValue oldAttrValue(serializedValue);
1429 bool oldValueSet;
1430 mAttrs.SetAndSwapAttr(aName, oldAttrValue, &oldValueSet);
1433 nsAtom* SVGElement::GetEventNameForAttr(nsAtom* aAttr) {
1434 if (IsSVGElement(nsGkAtoms::svg)) {
1435 if (aAttr == nsGkAtoms::onload) return nsGkAtoms::onSVGLoad;
1436 if (aAttr == nsGkAtoms::onscroll) return nsGkAtoms::onSVGScroll;
1438 if (aAttr == nsGkAtoms::onbegin) return nsGkAtoms::onbeginEvent;
1439 if (aAttr == nsGkAtoms::onrepeat) return nsGkAtoms::onrepeatEvent;
1440 if (aAttr == nsGkAtoms::onend) return nsGkAtoms::onendEvent;
1442 return SVGElementBase::GetEventNameForAttr(aAttr);
1445 SVGViewportElement* SVGElement::GetCtx() const {
1446 return SVGContentUtils::GetNearestViewportElement(this);
1449 /* virtual */
1450 gfxMatrix SVGElement::PrependLocalTransformsTo(const gfxMatrix& aMatrix,
1451 SVGTransformTypes aWhich) const {
1452 return aMatrix;
1455 SVGElement::LengthAttributesInfo SVGElement::GetLengthInfo() {
1456 return LengthAttributesInfo(nullptr, nullptr, 0);
1459 void SVGElement::SetLength(nsAtom* aName, const SVGAnimatedLength& aLength) {
1460 LengthAttributesInfo lengthInfo = GetLengthInfo();
1462 for (uint32_t i = 0; i < lengthInfo.mCount; i++) {
1463 if (aName == lengthInfo.mInfos[i].mName) {
1464 lengthInfo.mValues[i] = aLength;
1465 DidAnimateLength(i);
1466 return;
1469 MOZ_ASSERT(false, "no length found to set");
1472 nsAttrValue SVGElement::WillChangeLength(
1473 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1474 return WillChangeValue(GetLengthInfo().mInfos[aAttrEnum].mName,
1475 aProofOfUpdate);
1478 void SVGElement::DidChangeLength(uint8_t aAttrEnum,
1479 const nsAttrValue& aEmptyOrOldValue,
1480 const mozAutoDocUpdate& aProofOfUpdate) {
1481 LengthAttributesInfo info = GetLengthInfo();
1483 NS_ASSERTION(info.mCount > 0,
1484 "DidChangeLength on element with no length attribs");
1485 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1487 nsAttrValue newValue;
1488 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
1490 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1491 aProofOfUpdate);
1494 void SVGElement::DidAnimateLength(uint8_t aAttrEnum) {
1495 // We need to do this here. Normally the SMIL restyle would also cause us to
1496 // do this from DidSetComputedStyle, but we don't have that guarantee if our
1497 // frame gets reconstructed.
1498 ClearAnyCachedPath();
1500 if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) {
1501 nsCSSPropertyID propId =
1502 SVGGeometryProperty::AttrEnumToCSSPropId(this, aAttrEnum);
1504 // We don't map use element width/height currently. We can remove this
1505 // test when we do.
1506 if (propId != eCSSProperty_UNKNOWN) {
1507 SMILOverrideStyle()->SetSMILValue(propId,
1508 GetLengthInfo().mValues[aAttrEnum]);
1512 auto info = GetLengthInfo();
1513 DidAnimateAttribute(kNameSpaceID_None, info.mInfos[aAttrEnum].mName);
1516 SVGAnimatedLength* SVGElement::GetAnimatedLength(uint8_t aAttrEnum) {
1517 LengthAttributesInfo info = GetLengthInfo();
1518 if (aAttrEnum < info.mCount) {
1519 return &info.mValues[aAttrEnum];
1521 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1522 return nullptr;
1525 SVGAnimatedLength* SVGElement::GetAnimatedLength(const nsAtom* aAttrName) {
1526 LengthAttributesInfo lengthInfo = GetLengthInfo();
1528 for (uint32_t i = 0; i < lengthInfo.mCount; i++) {
1529 if (aAttrName == lengthInfo.mInfos[i].mName) {
1530 return &lengthInfo.mValues[i];
1533 return nullptr;
1536 void SVGElement::GetAnimatedLengthValues(float* aFirst, ...) {
1537 LengthAttributesInfo info = GetLengthInfo();
1539 NS_ASSERTION(info.mCount > 0,
1540 "GetAnimatedLengthValues on element with no length attribs");
1542 SVGElementMetrics metrics(this);
1544 float* f = aFirst;
1545 uint32_t i = 0;
1547 va_list args;
1548 va_start(args, aFirst);
1550 while (f && i < info.mCount) {
1551 *f = info.mValues[i++].GetAnimValue(metrics);
1552 f = va_arg(args, float*);
1555 va_end(args);
1558 SVGElement::LengthListAttributesInfo SVGElement::GetLengthListInfo() {
1559 return LengthListAttributesInfo(nullptr, nullptr, 0);
1562 nsAttrValue SVGElement::WillChangeLengthList(
1563 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1564 return WillChangeValue(GetLengthListInfo().mInfos[aAttrEnum].mName,
1565 aProofOfUpdate);
1568 void SVGElement::DidChangeLengthList(uint8_t aAttrEnum,
1569 const nsAttrValue& aEmptyOrOldValue,
1570 const mozAutoDocUpdate& aProofOfUpdate) {
1571 LengthListAttributesInfo info = GetLengthListInfo();
1573 NS_ASSERTION(info.mCount > 0,
1574 "DidChangeLengthList on element with no length list attribs");
1575 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1577 nsAttrValue newValue;
1578 newValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1580 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1581 aProofOfUpdate);
1584 void SVGElement::GetAnimatedLengthListValues(SVGUserUnitList* aFirst, ...) {
1585 LengthListAttributesInfo info = GetLengthListInfo();
1587 NS_ASSERTION(
1588 info.mCount > 0,
1589 "GetAnimatedLengthListValues on element with no length list attribs");
1591 SVGUserUnitList* list = aFirst;
1592 uint32_t i = 0;
1594 va_list args;
1595 va_start(args, aFirst);
1597 while (list && i < info.mCount) {
1598 list->Init(&(info.mValues[i].GetAnimValue()), this, info.mInfos[i].mAxis);
1599 ++i;
1600 list = va_arg(args, SVGUserUnitList*);
1603 va_end(args);
1606 SVGAnimatedLengthList* SVGElement::GetAnimatedLengthList(uint8_t aAttrEnum) {
1607 LengthListAttributesInfo info = GetLengthListInfo();
1608 if (aAttrEnum < info.mCount) {
1609 return &(info.mValues[aAttrEnum]);
1611 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1612 return nullptr;
1615 SVGElement::NumberListAttributesInfo SVGElement::GetNumberListInfo() {
1616 return NumberListAttributesInfo(nullptr, nullptr, 0);
1619 nsAttrValue SVGElement::WillChangeNumberList(
1620 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1621 return WillChangeValue(GetNumberListInfo().mInfos[aAttrEnum].mName,
1622 aProofOfUpdate);
1625 void SVGElement::DidChangeNumberList(uint8_t aAttrEnum,
1626 const nsAttrValue& aEmptyOrOldValue,
1627 const mozAutoDocUpdate& aProofOfUpdate) {
1628 NumberListAttributesInfo info = GetNumberListInfo();
1630 MOZ_ASSERT(info.mCount > 0,
1631 "DidChangeNumberList on element with no number list attribs");
1632 MOZ_ASSERT(aAttrEnum < info.mCount, "aAttrEnum out of range");
1634 nsAttrValue newValue;
1635 newValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1637 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1638 aProofOfUpdate);
1641 SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(uint8_t aAttrEnum) {
1642 NumberListAttributesInfo info = GetNumberListInfo();
1643 if (aAttrEnum < info.mCount) {
1644 return &(info.mValues[aAttrEnum]);
1646 MOZ_ASSERT(false, "Bad attrEnum");
1647 return nullptr;
1650 SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(nsAtom* aAttrName) {
1651 NumberListAttributesInfo info = GetNumberListInfo();
1652 for (uint32_t i = 0; i < info.mCount; i++) {
1653 if (aAttrName == info.mInfos[i].mName) {
1654 return &info.mValues[i];
1657 MOZ_ASSERT(false, "Bad caller");
1658 return nullptr;
1661 nsAttrValue SVGElement::WillChangePointList(
1662 const mozAutoDocUpdate& aProofOfUpdate) {
1663 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1664 return WillChangeValue(GetPointListAttrName(), aProofOfUpdate);
1667 void SVGElement::DidChangePointList(const nsAttrValue& aEmptyOrOldValue,
1668 const mozAutoDocUpdate& aProofOfUpdate) {
1669 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1671 nsAttrValue newValue;
1672 newValue.SetTo(GetAnimatedPointList()->GetBaseValue(), nullptr);
1674 DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue, newValue,
1675 aProofOfUpdate);
1678 void SVGElement::DidAnimatePointList() {
1679 MOZ_ASSERT(GetPointListAttrName(), "Animating non-existent path data?");
1681 ClearAnyCachedPath();
1683 DidAnimateAttribute(kNameSpaceID_None, GetPointListAttrName());
1686 nsAttrValue SVGElement::WillChangePathSegList(
1687 const mozAutoDocUpdate& aProofOfUpdate) {
1688 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1689 return WillChangeValue(GetPathDataAttrName(), aProofOfUpdate);
1692 void SVGElement::DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue,
1693 const mozAutoDocUpdate& aProofOfUpdate) {
1694 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1696 nsAttrValue newValue;
1697 newValue.SetTo(GetAnimPathSegList()->GetBaseValue(), nullptr);
1699 DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue, newValue,
1700 aProofOfUpdate);
1703 void SVGElement::DidAnimatePathSegList() {
1704 nsStaticAtom* name = GetPathDataAttrName();
1705 MOZ_ASSERT(name, "Animating non-existent path data?");
1707 ClearAnyCachedPath();
1709 // Notify style we have to update the d property because of SMIL animation.
1710 if (name == nsGkAtoms::d) {
1711 SMILOverrideStyle()->SetSMILValue(nsCSSPropertyID::eCSSProperty_d,
1712 *GetAnimPathSegList());
1715 DidAnimateAttribute(kNameSpaceID_None, name);
1718 SVGElement::NumberAttributesInfo SVGElement::GetNumberInfo() {
1719 return NumberAttributesInfo(nullptr, nullptr, 0);
1722 void SVGElement::DidChangeNumber(uint8_t aAttrEnum) {
1723 NumberAttributesInfo info = GetNumberInfo();
1725 NS_ASSERTION(info.mCount > 0,
1726 "DidChangeNumber on element with no number attribs");
1727 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1729 nsAttrValue attrValue;
1730 attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1732 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1733 attrValue, true);
1736 void SVGElement::GetAnimatedNumberValues(float* aFirst, ...) {
1737 NumberAttributesInfo info = GetNumberInfo();
1739 NS_ASSERTION(info.mCount > 0,
1740 "GetAnimatedNumberValues on element with no number attribs");
1742 float* f = aFirst;
1743 uint32_t i = 0;
1745 va_list args;
1746 va_start(args, aFirst);
1748 while (f && i < info.mCount) {
1749 *f = info.mValues[i++].GetAnimValue();
1750 f = va_arg(args, float*);
1752 va_end(args);
1755 SVGElement::NumberPairAttributesInfo SVGElement::GetNumberPairInfo() {
1756 return NumberPairAttributesInfo(nullptr, nullptr, 0);
1759 nsAttrValue SVGElement::WillChangeNumberPair(uint8_t aAttrEnum) {
1760 mozAutoDocUpdate updateBatch(GetComposedDoc(), kDontNotifyDocumentObservers);
1761 return WillChangeValue(GetNumberPairInfo().mInfos[aAttrEnum].mName,
1762 updateBatch);
1765 void SVGElement::DidChangeNumberPair(uint8_t aAttrEnum,
1766 const nsAttrValue& aEmptyOrOldValue) {
1767 NumberPairAttributesInfo info = GetNumberPairInfo();
1769 NS_ASSERTION(info.mCount > 0,
1770 "DidChangePairNumber on element with no number pair attribs");
1771 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1773 nsAttrValue newValue;
1774 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
1776 mozAutoDocUpdate updateBatch(GetComposedDoc(), kNotifyDocumentObservers);
1777 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1778 updateBatch);
1781 SVGElement::IntegerAttributesInfo SVGElement::GetIntegerInfo() {
1782 return IntegerAttributesInfo(nullptr, nullptr, 0);
1785 void SVGElement::DidChangeInteger(uint8_t aAttrEnum) {
1786 IntegerAttributesInfo info = GetIntegerInfo();
1787 NS_ASSERTION(info.mCount > 0,
1788 "DidChangeInteger on element with no integer attribs");
1789 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1791 nsAttrValue attrValue;
1792 attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1794 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1795 attrValue, true);
1798 void SVGElement::GetAnimatedIntegerValues(int32_t* aFirst, ...) {
1799 IntegerAttributesInfo info = GetIntegerInfo();
1801 NS_ASSERTION(info.mCount > 0,
1802 "GetAnimatedIntegerValues on element with no integer attribs");
1804 int32_t* n = aFirst;
1805 uint32_t i = 0;
1807 va_list args;
1808 va_start(args, aFirst);
1810 while (n && i < info.mCount) {
1811 *n = info.mValues[i++].GetAnimValue();
1812 n = va_arg(args, int32_t*);
1814 va_end(args);
1817 SVGElement::IntegerPairAttributesInfo SVGElement::GetIntegerPairInfo() {
1818 return IntegerPairAttributesInfo(nullptr, nullptr, 0);
1821 nsAttrValue SVGElement::WillChangeIntegerPair(
1822 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1823 return WillChangeValue(GetIntegerPairInfo().mInfos[aAttrEnum].mName,
1824 aProofOfUpdate);
1827 void SVGElement::DidChangeIntegerPair(uint8_t aAttrEnum,
1828 const nsAttrValue& aEmptyOrOldValue,
1829 const mozAutoDocUpdate& aProofOfUpdate) {
1830 IntegerPairAttributesInfo info = GetIntegerPairInfo();
1832 NS_ASSERTION(info.mCount > 0,
1833 "DidChangeIntegerPair on element with no integer pair attribs");
1834 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1836 nsAttrValue newValue;
1837 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
1839 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1840 aProofOfUpdate);
1843 SVGElement::BooleanAttributesInfo SVGElement::GetBooleanInfo() {
1844 return BooleanAttributesInfo(nullptr, nullptr, 0);
1847 void SVGElement::DidChangeBoolean(uint8_t aAttrEnum) {
1848 BooleanAttributesInfo info = GetBooleanInfo();
1850 NS_ASSERTION(info.mCount > 0,
1851 "DidChangeBoolean on element with no boolean attribs");
1852 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1854 nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom());
1855 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1856 attrValue, true);
1859 SVGElement::EnumAttributesInfo SVGElement::GetEnumInfo() {
1860 return EnumAttributesInfo(nullptr, nullptr, 0);
1863 void SVGElement::DidChangeEnum(uint8_t aAttrEnum) {
1864 EnumAttributesInfo info = GetEnumInfo();
1866 NS_ASSERTION(info.mCount > 0,
1867 "DidChangeEnum on element with no enum attribs");
1868 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1870 nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom(this));
1871 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1872 attrValue, true);
1875 SVGAnimatedOrient* SVGElement::GetAnimatedOrient() { return nullptr; }
1877 nsAttrValue SVGElement::WillChangeOrient(
1878 const mozAutoDocUpdate& aProofOfUpdate) {
1879 return WillChangeValue(nsGkAtoms::orient, aProofOfUpdate);
1882 void SVGElement::DidChangeOrient(const nsAttrValue& aEmptyOrOldValue,
1883 const mozAutoDocUpdate& aProofOfUpdate) {
1884 SVGAnimatedOrient* orient = GetAnimatedOrient();
1886 NS_ASSERTION(orient, "DidChangeOrient on element with no orient attrib");
1888 nsAttrValue newValue;
1889 newValue.SetTo(*orient, nullptr);
1891 DidChangeValue(nsGkAtoms::orient, aEmptyOrOldValue, newValue, aProofOfUpdate);
1894 SVGAnimatedViewBox* SVGElement::GetAnimatedViewBox() { return nullptr; }
1896 nsAttrValue SVGElement::WillChangeViewBox(
1897 const mozAutoDocUpdate& aProofOfUpdate) {
1898 return WillChangeValue(nsGkAtoms::viewBox, aProofOfUpdate);
1901 void SVGElement::DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue,
1902 const mozAutoDocUpdate& aProofOfUpdate) {
1903 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
1905 NS_ASSERTION(viewBox, "DidChangeViewBox on element with no viewBox attrib");
1907 nsAttrValue newValue;
1908 newValue.SetTo(*viewBox, nullptr);
1910 DidChangeValue(nsGkAtoms::viewBox, aEmptyOrOldValue, newValue,
1911 aProofOfUpdate);
1914 SVGAnimatedPreserveAspectRatio* SVGElement::GetAnimatedPreserveAspectRatio() {
1915 return nullptr;
1918 nsAttrValue SVGElement::WillChangePreserveAspectRatio(
1919 const mozAutoDocUpdate& aProofOfUpdate) {
1920 return WillChangeValue(nsGkAtoms::preserveAspectRatio, aProofOfUpdate);
1923 void SVGElement::DidChangePreserveAspectRatio(
1924 const nsAttrValue& aEmptyOrOldValue,
1925 const mozAutoDocUpdate& aProofOfUpdate) {
1926 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
1927 GetAnimatedPreserveAspectRatio();
1929 NS_ASSERTION(preserveAspectRatio,
1930 "DidChangePreserveAspectRatio on element with no "
1931 "preserveAspectRatio attrib");
1933 nsAttrValue newValue;
1934 newValue.SetTo(*preserveAspectRatio, nullptr);
1936 DidChangeValue(nsGkAtoms::preserveAspectRatio, aEmptyOrOldValue, newValue,
1937 aProofOfUpdate);
1940 nsAttrValue SVGElement::WillChangeTransformList(
1941 const mozAutoDocUpdate& aProofOfUpdate) {
1942 return WillChangeValue(GetTransformListAttrName(), aProofOfUpdate);
1945 void SVGElement::DidChangeTransformList(
1946 const nsAttrValue& aEmptyOrOldValue,
1947 const mozAutoDocUpdate& aProofOfUpdate) {
1948 MOZ_ASSERT(GetTransformListAttrName(),
1949 "Changing non-existent transform list?");
1951 // The transform attribute is being set, so we must ensure that the
1952 // SVGAnimatedTransformList is/has been allocated:
1953 nsAttrValue newValue;
1954 newValue.SetTo(GetAnimatedTransformList(DO_ALLOCATE)->GetBaseValue(),
1955 nullptr);
1957 DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue, newValue,
1958 aProofOfUpdate);
1961 void SVGElement::DidAnimateTransformList(int32_t aModType) {
1962 MOZ_ASSERT(GetTransformListAttrName(),
1963 "Animating non-existent transform data?");
1965 if (auto* frame = GetPrimaryFrame()) {
1966 nsAtom* transformAttr = GetTransformListAttrName();
1967 frame->AttributeChanged(kNameSpaceID_None, transformAttr, aModType);
1968 // When script changes the 'transform' attribute, Element::SetAttrAndNotify
1969 // will call MutationObservers::NotifyAttributeChanged, under which
1970 // SVGTransformableElement::GetAttributeChangeHint will be called and an
1971 // appropriate change event posted to update our frame's overflow rects.
1972 // The SetAttrAndNotify doesn't happen for transform changes caused by
1973 // 'animateTransform' though (and sending out the mutation events that
1974 // MutationObservers::NotifyAttributeChanged dispatches would be
1975 // inappropriate anyway), so we need to post the change event ourself.
1976 nsChangeHint changeHint = GetAttributeChangeHint(transformAttr, aModType);
1977 if (changeHint) {
1978 nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0}, changeHint);
1980 SVGObserverUtils::InvalidateRenderingObservers(frame);
1981 return;
1983 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
1986 SVGElement::StringAttributesInfo SVGElement::GetStringInfo() {
1987 return StringAttributesInfo(nullptr, nullptr, 0);
1990 void SVGElement::GetStringBaseValue(uint8_t aAttrEnum,
1991 nsAString& aResult) const {
1992 SVGElement::StringAttributesInfo info =
1993 const_cast<SVGElement*>(this)->GetStringInfo();
1995 NS_ASSERTION(info.mCount > 0,
1996 "GetBaseValue on element with no string attribs");
1998 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2000 GetAttr(info.mInfos[aAttrEnum].mNamespaceID, info.mInfos[aAttrEnum].mName,
2001 aResult);
2004 void SVGElement::SetStringBaseValue(uint8_t aAttrEnum,
2005 const nsAString& aValue) {
2006 SVGElement::StringAttributesInfo info = GetStringInfo();
2008 NS_ASSERTION(info.mCount > 0,
2009 "SetBaseValue on element with no string attribs");
2011 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2013 SetAttr(info.mInfos[aAttrEnum].mNamespaceID, info.mInfos[aAttrEnum].mName,
2014 aValue, true);
2017 SVGElement::StringListAttributesInfo SVGElement::GetStringListInfo() {
2018 return StringListAttributesInfo(nullptr, nullptr, 0);
2021 nsAttrValue SVGElement::WillChangeStringList(
2022 bool aIsConditionalProcessingAttribute, uint8_t aAttrEnum,
2023 const mozAutoDocUpdate& aProofOfUpdate) {
2024 nsStaticAtom* name;
2025 if (aIsConditionalProcessingAttribute) {
2026 nsCOMPtr<SVGTests> tests(do_QueryInterface(this));
2027 name = tests->GetAttrName(aAttrEnum);
2028 } else {
2029 name = GetStringListInfo().mInfos[aAttrEnum].mName;
2031 return WillChangeValue(name, aProofOfUpdate);
2034 void SVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute,
2035 uint8_t aAttrEnum,
2036 const nsAttrValue& aEmptyOrOldValue,
2037 const mozAutoDocUpdate& aProofOfUpdate) {
2038 nsStaticAtom* name;
2039 nsAttrValue newValue;
2040 nsCOMPtr<SVGTests> tests;
2042 if (aIsConditionalProcessingAttribute) {
2043 tests = do_QueryObject(this);
2044 name = tests->GetAttrName(aAttrEnum);
2045 tests->GetAttrValue(aAttrEnum, newValue);
2046 } else {
2047 StringListAttributesInfo info = GetStringListInfo();
2049 NS_ASSERTION(info.mCount > 0,
2050 "DidChangeStringList on element with no string list attribs");
2051 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2053 name = info.mInfos[aAttrEnum].mName;
2054 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
2057 DidChangeValue(name, aEmptyOrOldValue, newValue, aProofOfUpdate);
2059 if (aIsConditionalProcessingAttribute) {
2060 tests->MaybeInvalidate();
2064 void SVGElement::DidAnimateAttribute(int32_t aNameSpaceID, nsAtom* aAttribute) {
2065 if (auto* frame = GetPrimaryFrame()) {
2066 frame->AttributeChanged(aNameSpaceID, aAttribute,
2067 MutationEvent_Binding::SMIL);
2068 SVGObserverUtils::InvalidateRenderingObservers(frame);
2069 return;
2071 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
2074 nsresult SVGElement::ReportAttributeParseFailure(Document* aDocument,
2075 nsAtom* aAttribute,
2076 const nsAString& aValue) {
2077 AutoTArray<nsString, 2> strings;
2078 strings.AppendElement(nsDependentAtomString(aAttribute));
2079 strings.AppendElement(aValue);
2080 return SVGContentUtils::ReportToConsole(aDocument, "AttributeParseWarning",
2081 strings);
2084 UniquePtr<SMILAttr> SVGElement::GetAnimatedAttr(int32_t aNamespaceID,
2085 nsAtom* aName) {
2086 if (aNamespaceID == kNameSpaceID_None) {
2087 // Transforms:
2088 if (GetTransformListAttrName() == aName) {
2089 // The transform attribute is being animated, so we must ensure that the
2090 // SVGAnimatedTransformList is/has been allocated:
2091 return GetAnimatedTransformList(DO_ALLOCATE)->ToSMILAttr(this);
2094 // Motion (fake 'attribute' for animateMotion)
2095 if (aName == nsGkAtoms::mozAnimateMotionDummyAttr) {
2096 return MakeUnique<SVGMotionSMILAttr>(this);
2099 // Lengths:
2100 LengthAttributesInfo info = GetLengthInfo();
2101 for (uint32_t i = 0; i < info.mCount; i++) {
2102 if (aName == info.mInfos[i].mName) {
2103 return info.mValues[i].ToSMILAttr(this);
2107 // Numbers:
2109 NumberAttributesInfo info = GetNumberInfo();
2110 for (uint32_t i = 0; i < info.mCount; i++) {
2111 if (aName == info.mInfos[i].mName) {
2112 return info.mValues[i].ToSMILAttr(this);
2117 // Number Pairs:
2119 NumberPairAttributesInfo info = GetNumberPairInfo();
2120 for (uint32_t i = 0; i < info.mCount; i++) {
2121 if (aName == info.mInfos[i].mName) {
2122 return info.mValues[i].ToSMILAttr(this);
2127 // Integers:
2129 IntegerAttributesInfo info = GetIntegerInfo();
2130 for (uint32_t i = 0; i < info.mCount; i++) {
2131 if (aName == info.mInfos[i].mName) {
2132 return info.mValues[i].ToSMILAttr(this);
2137 // Integer Pairs:
2139 IntegerPairAttributesInfo info = GetIntegerPairInfo();
2140 for (uint32_t i = 0; i < info.mCount; i++) {
2141 if (aName == info.mInfos[i].mName) {
2142 return info.mValues[i].ToSMILAttr(this);
2147 // Enumerations:
2149 EnumAttributesInfo info = GetEnumInfo();
2150 for (uint32_t i = 0; i < info.mCount; i++) {
2151 if (aName == info.mInfos[i].mName) {
2152 return info.mValues[i].ToSMILAttr(this);
2157 // Booleans:
2159 BooleanAttributesInfo info = GetBooleanInfo();
2160 for (uint32_t i = 0; i < info.mCount; i++) {
2161 if (aName == info.mInfos[i].mName) {
2162 return info.mValues[i].ToSMILAttr(this);
2167 // orient:
2168 if (aName == nsGkAtoms::orient) {
2169 SVGAnimatedOrient* orient = GetAnimatedOrient();
2170 return orient ? orient->ToSMILAttr(this) : nullptr;
2173 // viewBox:
2174 if (aName == nsGkAtoms::viewBox) {
2175 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
2176 return viewBox ? viewBox->ToSMILAttr(this) : nullptr;
2179 // preserveAspectRatio:
2180 if (aName == nsGkAtoms::preserveAspectRatio) {
2181 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
2182 GetAnimatedPreserveAspectRatio();
2183 return preserveAspectRatio ? preserveAspectRatio->ToSMILAttr(this)
2184 : nullptr;
2187 // NumberLists:
2189 NumberListAttributesInfo info = GetNumberListInfo();
2190 for (uint32_t i = 0; i < info.mCount; i++) {
2191 if (aName == info.mInfos[i].mName) {
2192 MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes");
2193 return info.mValues[i].ToSMILAttr(this, uint8_t(i));
2198 // LengthLists:
2200 LengthListAttributesInfo info = GetLengthListInfo();
2201 for (uint32_t i = 0; i < info.mCount; i++) {
2202 if (aName == info.mInfos[i].mName) {
2203 MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes");
2204 return info.mValues[i].ToSMILAttr(this, uint8_t(i),
2205 info.mInfos[i].mAxis,
2206 info.mInfos[i].mCouldZeroPadList);
2211 // PointLists:
2213 if (GetPointListAttrName() == aName) {
2214 SVGAnimatedPointList* pointList = GetAnimatedPointList();
2215 if (pointList) {
2216 return pointList->ToSMILAttr(this);
2221 // PathSegLists:
2223 if (GetPathDataAttrName() == aName) {
2224 SVGAnimatedPathSegList* segList = GetAnimPathSegList();
2225 if (segList) {
2226 return segList->ToSMILAttr(this);
2231 if (aName == nsGkAtoms::_class) {
2232 return mClassAttribute.ToSMILAttr(this);
2236 // Strings
2238 StringAttributesInfo info = GetStringInfo();
2239 for (uint32_t i = 0; i < info.mCount; i++) {
2240 if (aNamespaceID == info.mInfos[i].mNamespaceID &&
2241 aName == info.mInfos[i].mName) {
2242 return info.mValues[i].ToSMILAttr(this);
2247 return nullptr;
2250 void SVGElement::AnimationNeedsResample() {
2251 Document* doc = GetComposedDoc();
2252 if (doc && doc->HasAnimationController()) {
2253 doc->GetAnimationController()->SetResampleNeeded();
2257 void SVGElement::FlushAnimations() {
2258 Document* doc = GetComposedDoc();
2259 if (doc && doc->HasAnimationController()) {
2260 doc->GetAnimationController()->FlushResampleRequests();
2264 void SVGElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
2265 size_t* aNodeSize) const {
2266 Element::AddSizeOfExcludingThis(aSizes, aNodeSize);
2269 } // namespace mozilla::dom