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