Bug 1805294 [wpt PR 37463] - WebKit export of https://bugs.webkit.org/show_bug.cgi...
[gecko.git] / dom / svg / SVGElement.cpp
blobec59d00926624e0aafac7a598dc65d3629abcc43
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/dom/MutationEventBinding.h"
10 #include "mozilla/dom/MutationObservers.h"
11 #include "mozilla/dom/CSSRuleBinding.h"
12 #include "mozilla/dom/SVGElementBinding.h"
13 #include "mozilla/dom/SVGGeometryElement.h"
14 #include "mozilla/dom/SVGLengthBinding.h"
15 #include "mozilla/dom/SVGSVGElement.h"
16 #include "mozilla/dom/SVGTests.h"
17 #include "mozilla/dom/SVGUnitTypesBinding.h"
18 #include "mozilla/dom/Element.h"
20 #include "mozilla/ArrayUtils.h"
21 #include "mozilla/DebugOnly.h"
22 #include "mozilla/DeclarationBlock.h"
23 #include "mozilla/EventListenerManager.h"
24 #include "mozilla/InternalMutationEvent.h"
25 #include "mozilla/PresShell.h"
26 #include "mozilla/RestyleManager.h"
27 #include "mozilla/SMILAnimationController.h"
28 #include "mozilla/StaticPrefs_layout.h"
29 #include "mozilla/SVGContentUtils.h"
30 #include "mozilla/Unused.h"
32 #include "mozAutoDocUpdate.h"
33 #include "nsAttrValueOrString.h"
34 #include "nsCSSProps.h"
35 #include "nsCSSValue.h"
36 #include "nsContentUtils.h"
37 #include "nsDOMCSSAttrDeclaration.h"
38 #include "nsICSSDeclaration.h"
39 #include "nsIContentInlines.h"
40 #include "mozilla/dom/Document.h"
41 #include "nsError.h"
42 #include "nsGkAtoms.h"
43 #include "nsIFrame.h"
44 #include "nsQueryObject.h"
45 #include "nsLayoutUtils.h"
46 #include "SVGAnimatedNumberList.h"
47 #include "SVGAnimatedLengthList.h"
48 #include "SVGAnimatedPointList.h"
49 #include "SVGAnimatedPathSegList.h"
50 #include "SVGAnimatedTransformList.h"
51 #include "SVGAnimatedBoolean.h"
52 #include "SVGAnimatedEnumeration.h"
53 #include "SVGAnimatedInteger.h"
54 #include "SVGAnimatedIntegerPair.h"
55 #include "SVGAnimatedLength.h"
56 #include "SVGAnimatedNumber.h"
57 #include "SVGAnimatedNumberPair.h"
58 #include "SVGAnimatedOrient.h"
59 #include "SVGAnimatedString.h"
60 #include "SVGAnimatedViewBox.h"
61 #include "SVGGeometryProperty.h"
62 #include "SVGMotionSMILAttr.h"
63 #include <stdarg.h>
65 // This is needed to ensure correct handling of calls to the
66 // vararg-list methods in this file:
67 // SVGElement::GetAnimated{Length,Number,Integer}Values
68 // See bug 547964 for details:
69 static_assert(sizeof(void*) == sizeof(nullptr),
70 "nullptr should be the correct size");
72 nsresult NS_NewSVGElement(
73 mozilla::dom::Element** aResult,
74 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) {
75 RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
76 auto* nim = nodeInfo->NodeInfoManager();
77 RefPtr<mozilla::dom::SVGElement> it =
78 new (nim) mozilla::dom::SVGElement(nodeInfo.forget());
79 nsresult rv = it->Init();
81 if (NS_FAILED(rv)) {
82 return rv;
85 it.forget(aResult);
86 return rv;
89 namespace mozilla::dom {
90 using namespace SVGUnitTypes_Binding;
92 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGElement)
94 // Use the CC variant of this, even though this class does not define
95 // a new CC participant, to make QIing to the CC interfaces faster.
96 NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(SVGElement, SVGElementBase,
97 SVGElement)
99 SVGEnumMapping SVGElement::sSVGUnitTypesMap[] = {
100 {nsGkAtoms::userSpaceOnUse, SVG_UNIT_TYPE_USERSPACEONUSE},
101 {nsGkAtoms::objectBoundingBox, SVG_UNIT_TYPE_OBJECTBOUNDINGBOX},
102 {nullptr, 0}};
104 SVGElement::SVGElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
105 : SVGElementBase(std::move(aNodeInfo)) {}
107 SVGElement::~SVGElement() {
108 OwnerDoc()->UnscheduleSVGForPresAttrEvaluation(this);
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 dest->GetLengthInfo().CopyAllFrom(GetLengthInfo());
211 dest->GetNumberInfo().CopyAllFrom(GetNumberInfo());
212 dest->GetNumberPairInfo().CopyAllFrom(GetNumberPairInfo());
213 dest->GetIntegerInfo().CopyAllFrom(GetIntegerInfo());
214 dest->GetIntegerPairInfo().CopyAllFrom(GetIntegerPairInfo());
215 dest->GetEnumInfo().CopyAllFrom(GetEnumInfo());
216 dest->GetStringInfo().CopyAllFrom(GetStringInfo());
217 dest->GetLengthListInfo().CopyAllFrom(GetLengthListInfo());
218 dest->GetNumberListInfo().CopyAllFrom(GetNumberListInfo());
221 return NS_OK;
224 //----------------------------------------------------------------------
225 // SVGElement methods
227 void SVGElement::DidAnimateClass() {
228 // For Servo, snapshot the element before we change it.
229 PresShell* presShell = OwnerDoc()->GetPresShell();
230 if (presShell) {
231 if (nsPresContext* presContext = presShell->GetPresContext()) {
232 presContext->RestyleManager()->ClassAttributeWillBeChangedBySMIL(this);
236 nsAutoString src;
237 mClassAttribute.GetAnimValue(src, this);
238 if (!mClassAnimAttr) {
239 mClassAnimAttr = MakeUnique<nsAttrValue>();
241 mClassAnimAttr->ParseAtomArray(src);
243 // FIXME(emilio): This re-selector-matches, but we do the snapshot stuff right
244 // above... Is this needed anymore?
245 if (presShell) {
246 presShell->RestyleForAnimation(this, RestyleHint::RESTYLE_SELF);
250 nsresult SVGElement::Init() {
251 // Set up length attributes - can't do this in the constructor
252 // because we can't do a virtual call at that point
254 GetLengthInfo().ResetAll();
255 GetNumberInfo().ResetAll();
256 GetNumberPairInfo().ResetAll();
257 GetIntegerInfo().ResetAll();
258 GetIntegerPairInfo().ResetAll();
259 GetBooleanInfo().ResetAll();
260 GetEnumInfo().ResetAll();
262 if (SVGAnimatedOrient* orient = GetAnimatedOrient()) {
263 orient->Init();
266 if (SVGAnimatedViewBox* viewBox = GetAnimatedViewBox()) {
267 viewBox->Init();
270 if (SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
271 GetAnimatedPreserveAspectRatio()) {
272 preserveAspectRatio->Init();
275 GetLengthListInfo().ResetAll();
276 GetNumberListInfo().ResetAll();
278 // No need to reset SVGPointList since the default value is always the same
279 // (an empty list).
281 // No need to reset SVGPathData since the default value is always the same
282 // (an empty list).
284 GetStringInfo().ResetAll();
285 return NS_OK;
288 //----------------------------------------------------------------------
289 // Implementation
291 //----------------------------------------------------------------------
292 // nsIContent methods
294 nsresult SVGElement::BindToTree(BindContext& aContext, nsINode& aParent) {
295 nsresult rv = SVGElementBase::BindToTree(aContext, aParent);
296 NS_ENSURE_SUCCESS(rv, rv);
298 // Hide any nonce from the DOM, but keep the internal value of the
299 // nonce by copying and resetting the internal nonce value.
300 if (HasFlag(NODE_HAS_NONCE_AND_HEADER_CSP) && IsInComposedDoc() &&
301 OwnerDoc()->GetBrowsingContext()) {
302 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
303 "SVGElement::ResetNonce::Runnable",
304 [self = RefPtr<SVGElement>(this)]() {
305 nsAutoString nonce;
306 self->GetNonce(nonce);
307 self->SetAttr(kNameSpaceID_None, nsGkAtoms::nonce, u""_ns, true);
308 self->SetNonce(nonce);
309 }));
312 return NS_OK;
315 nsresult SVGElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
316 const nsAttrValue* aValue,
317 const nsAttrValue* aOldValue,
318 nsIPrincipal* aSubjectPrincipal,
319 bool aNotify) {
320 // We don't currently use nsMappedAttributes within SVG. If this changes, we
321 // need to be very careful because some nsAttrValues used by SVG point to
322 // member data of SVG elements and if an nsAttrValue outlives the SVG element
323 // whose data it points to (by virtue of being stored in
324 // mAttrs->mMappedAttributes, meaning it's shared between
325 // elements), the pointer will dangle. See bug 724680.
326 MOZ_ASSERT(!mAttrs.HasMappedAttrs(),
327 "Unexpected use of nsMappedAttributes within SVG");
329 // If this is an svg presentation attribute we need to map it into
330 // the content declaration block.
331 // XXX For some reason incremental mapping doesn't work, so for now
332 // just delete the style rule and lazily reconstruct it as needed).
333 if (aNamespaceID == kNameSpaceID_None && IsAttributeMapped(aName)) {
334 mContentDeclarationBlock = nullptr;
335 OwnerDoc()->ScheduleSVGForPresAttrEvaluation(this);
338 if (IsEventAttributeName(aName) && aValue) {
339 MOZ_ASSERT(aValue->Type() == nsAttrValue::eString,
340 "Expected string value for script body");
341 SetEventHandler(GetEventNameForAttr(aName), aValue->GetStringValue());
344 // The nonce will be copied over to an internal slot and cleared from the
345 // Element within BindToTree to avoid CSS Selector nonce exfiltration if
346 // the CSP list contains a header-delivered CSP.
347 if (nsGkAtoms::nonce == aName && kNameSpaceID_None == aNamespaceID) {
348 if (aValue) {
349 SetNonce(aValue->GetStringValue());
350 if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) {
351 SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP);
353 } else {
354 RemoveNonce();
358 return SVGElementBase::AfterSetAttr(aNamespaceID, aName, aValue, aOldValue,
359 aSubjectPrincipal, aNotify);
362 bool SVGElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
363 const nsAString& aValue,
364 nsIPrincipal* aMaybeScriptedPrincipal,
365 nsAttrValue& aResult) {
366 nsresult rv = NS_OK;
367 bool foundMatch = false;
368 bool didSetResult = false;
370 if (aNamespaceID == kNameSpaceID_None) {
371 // Check for SVGAnimatedLength attribute
372 LengthAttributesInfo lengthInfo = GetLengthInfo();
374 uint32_t i;
375 for (i = 0; i < lengthInfo.mCount; i++) {
376 if (aAttribute == lengthInfo.mInfos[i].mName) {
377 rv = lengthInfo.mValues[i].SetBaseValueString(aValue, this, false);
378 if (NS_FAILED(rv)) {
379 lengthInfo.Reset(i);
380 } else {
381 aResult.SetTo(lengthInfo.mValues[i], &aValue);
382 didSetResult = true;
384 foundMatch = true;
385 break;
389 if (!foundMatch) {
390 // Check for SVGAnimatedLengthList attribute
391 LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
392 for (i = 0; i < lengthListInfo.mCount; i++) {
393 if (aAttribute == lengthListInfo.mInfos[i].mName) {
394 rv = lengthListInfo.mValues[i].SetBaseValueString(aValue);
395 if (NS_FAILED(rv)) {
396 lengthListInfo.Reset(i);
397 } else {
398 aResult.SetTo(lengthListInfo.mValues[i].GetBaseValue(), &aValue);
399 didSetResult = true;
401 foundMatch = true;
402 break;
407 if (!foundMatch) {
408 // Check for SVGAnimatedNumberList attribute
409 NumberListAttributesInfo numberListInfo = GetNumberListInfo();
410 for (i = 0; i < numberListInfo.mCount; i++) {
411 if (aAttribute == numberListInfo.mInfos[i].mName) {
412 rv = numberListInfo.mValues[i].SetBaseValueString(aValue);
413 if (NS_FAILED(rv)) {
414 numberListInfo.Reset(i);
415 } else {
416 aResult.SetTo(numberListInfo.mValues[i].GetBaseValue(), &aValue);
417 didSetResult = true;
419 foundMatch = true;
420 break;
425 if (!foundMatch) {
426 // Check for SVGAnimatedPointList attribute
427 if (GetPointListAttrName() == aAttribute) {
428 if (SVGAnimatedPointList* pointList = GetAnimatedPointList()) {
429 pointList->SetBaseValueString(aValue);
430 // The spec says we parse everything up to the failure, so we DON'T
431 // need to check the result of SetBaseValueString or call
432 // pointList->ClearBaseValue() if it fails
433 aResult.SetTo(pointList->GetBaseValue(), &aValue);
434 didSetResult = true;
435 foundMatch = true;
440 if (!foundMatch) {
441 // Check for SVGAnimatedPathSegList attribute
442 if (GetPathDataAttrName() == aAttribute) {
443 if (SVGAnimatedPathSegList* segList = GetAnimPathSegList()) {
444 segList->SetBaseValueString(aValue);
445 // The spec says we parse everything up to the failure, so we DON'T
446 // need to check the result of SetBaseValueString or call
447 // segList->ClearBaseValue() if it fails
448 aResult.SetTo(segList->GetBaseValue(), &aValue);
449 didSetResult = true;
450 foundMatch = true;
455 if (!foundMatch) {
456 // Check for SVGAnimatedNumber attribute
457 NumberAttributesInfo numberInfo = GetNumberInfo();
458 for (i = 0; i < numberInfo.mCount; i++) {
459 if (aAttribute == numberInfo.mInfos[i].mName) {
460 rv = numberInfo.mValues[i].SetBaseValueString(aValue, this);
461 if (NS_FAILED(rv)) {
462 numberInfo.Reset(i);
463 } else {
464 aResult.SetTo(numberInfo.mValues[i].GetBaseValue(), &aValue);
465 didSetResult = true;
467 foundMatch = true;
468 break;
473 if (!foundMatch) {
474 // Check for SVGAnimatedNumberPair attribute
475 NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo();
476 for (i = 0; i < numberPairInfo.mCount; i++) {
477 if (aAttribute == numberPairInfo.mInfos[i].mName) {
478 rv = numberPairInfo.mValues[i].SetBaseValueString(aValue, this);
479 if (NS_FAILED(rv)) {
480 numberPairInfo.Reset(i);
481 } else {
482 aResult.SetTo(numberPairInfo.mValues[i], &aValue);
483 didSetResult = true;
485 foundMatch = true;
486 break;
491 if (!foundMatch) {
492 // Check for SVGAnimatedInteger attribute
493 IntegerAttributesInfo integerInfo = GetIntegerInfo();
494 for (i = 0; i < integerInfo.mCount; i++) {
495 if (aAttribute == integerInfo.mInfos[i].mName) {
496 rv = integerInfo.mValues[i].SetBaseValueString(aValue, this);
497 if (NS_FAILED(rv)) {
498 integerInfo.Reset(i);
499 } else {
500 aResult.SetTo(integerInfo.mValues[i].GetBaseValue(), &aValue);
501 didSetResult = true;
503 foundMatch = true;
504 break;
509 if (!foundMatch) {
510 // Check for SVGAnimatedIntegerPair attribute
511 IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo();
512 for (i = 0; i < integerPairInfo.mCount; i++) {
513 if (aAttribute == integerPairInfo.mInfos[i].mName) {
514 rv = integerPairInfo.mValues[i].SetBaseValueString(aValue, this);
515 if (NS_FAILED(rv)) {
516 integerPairInfo.Reset(i);
517 } else {
518 aResult.SetTo(integerPairInfo.mValues[i], &aValue);
519 didSetResult = true;
521 foundMatch = true;
522 break;
527 if (!foundMatch) {
528 // Check for SVGAnimatedBoolean attribute
529 BooleanAttributesInfo booleanInfo = GetBooleanInfo();
530 for (i = 0; i < booleanInfo.mCount; i++) {
531 if (aAttribute == booleanInfo.mInfos[i].mName) {
532 nsAtom* valAtom = NS_GetStaticAtom(aValue);
533 rv = valAtom ? booleanInfo.mValues[i].SetBaseValueAtom(valAtom, this)
534 : NS_ERROR_DOM_SYNTAX_ERR;
535 if (NS_FAILED(rv)) {
536 booleanInfo.Reset(i);
537 } else {
538 aResult.SetTo(valAtom);
539 didSetResult = true;
541 foundMatch = true;
542 break;
547 if (!foundMatch) {
548 // Check for SVGAnimatedEnumeration attribute
549 EnumAttributesInfo enumInfo = GetEnumInfo();
550 for (i = 0; i < enumInfo.mCount; i++) {
551 if (aAttribute == enumInfo.mInfos[i].mName) {
552 RefPtr<nsAtom> valAtom = NS_Atomize(aValue);
553 if (!enumInfo.mValues[i].SetBaseValueAtom(valAtom, this)) {
554 // Exact error value does not matter; we just need to mark the
555 // parse as failed.
556 rv = NS_ERROR_FAILURE;
557 enumInfo.Reset(i);
558 } else {
559 aResult.SetTo(valAtom);
560 didSetResult = true;
562 foundMatch = true;
563 break;
568 if (!foundMatch) {
569 // Check for conditional processing attributes
570 nsCOMPtr<SVGTests> tests = do_QueryObject(this);
571 if (tests && tests->ParseConditionalProcessingAttribute(
572 aAttribute, aValue, aResult)) {
573 foundMatch = true;
577 if (!foundMatch) {
578 // Check for StringList attribute
579 StringListAttributesInfo stringListInfo = GetStringListInfo();
580 for (i = 0; i < stringListInfo.mCount; i++) {
581 if (aAttribute == stringListInfo.mInfos[i].mName) {
582 rv = stringListInfo.mValues[i].SetValue(aValue);
583 if (NS_FAILED(rv)) {
584 stringListInfo.Reset(i);
585 } else {
586 aResult.SetTo(stringListInfo.mValues[i], &aValue);
587 didSetResult = true;
589 foundMatch = true;
590 break;
595 if (!foundMatch) {
596 // Check for orient attribute
597 if (aAttribute == nsGkAtoms::orient) {
598 SVGAnimatedOrient* orient = GetAnimatedOrient();
599 if (orient) {
600 rv = orient->SetBaseValueString(aValue, this, false);
601 if (NS_FAILED(rv)) {
602 orient->Init();
603 } else {
604 aResult.SetTo(*orient, &aValue);
605 didSetResult = true;
607 foundMatch = true;
609 // Check for viewBox attribute
610 } else if (aAttribute == nsGkAtoms::viewBox) {
611 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
612 if (viewBox) {
613 rv = viewBox->SetBaseValueString(aValue, this, false);
614 if (NS_FAILED(rv)) {
615 viewBox->Init();
616 } else {
617 aResult.SetTo(*viewBox, &aValue);
618 didSetResult = true;
620 foundMatch = true;
622 // Check for preserveAspectRatio attribute
623 } else if (aAttribute == nsGkAtoms::preserveAspectRatio) {
624 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
625 GetAnimatedPreserveAspectRatio();
626 if (preserveAspectRatio) {
627 rv = preserveAspectRatio->SetBaseValueString(aValue, this, false);
628 if (NS_FAILED(rv)) {
629 preserveAspectRatio->Init();
630 } else {
631 aResult.SetTo(*preserveAspectRatio, &aValue);
632 didSetResult = true;
634 foundMatch = true;
636 // Check for SVGAnimatedTransformList attribute
637 } else if (GetTransformListAttrName() == aAttribute) {
638 // The transform attribute is being set, so we must ensure that the
639 // SVGAnimatedTransformList is/has been allocated:
640 SVGAnimatedTransformList* transformList =
641 GetAnimatedTransformList(DO_ALLOCATE);
642 rv = transformList->SetBaseValueString(aValue, this);
643 if (NS_FAILED(rv)) {
644 transformList->ClearBaseValue();
645 } else {
646 aResult.SetTo(transformList->GetBaseValue(), &aValue);
647 didSetResult = true;
649 foundMatch = true;
650 } else if (aAttribute == nsGkAtoms::tabindex) {
651 didSetResult = aResult.ParseIntValue(aValue);
652 foundMatch = true;
656 if (aAttribute == nsGkAtoms::_class) {
657 mClassAttribute.SetBaseValue(aValue, this, false);
658 aResult.ParseAtomArray(aValue);
659 return true;
662 if (aAttribute == nsGkAtoms::rel) {
663 aResult.ParseAtomArray(aValue);
664 return true;
668 if (!foundMatch) {
669 // Check for SVGAnimatedString attribute
670 StringAttributesInfo stringInfo = GetStringInfo();
671 for (uint32_t i = 0; i < stringInfo.mCount; i++) {
672 if (aNamespaceID == stringInfo.mInfos[i].mNamespaceID &&
673 aAttribute == stringInfo.mInfos[i].mName) {
674 stringInfo.mValues[i].SetBaseValue(aValue, this, false);
675 foundMatch = true;
676 break;
681 if (foundMatch) {
682 if (NS_FAILED(rv)) {
683 ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue);
684 return false;
686 if (!didSetResult) {
687 aResult.SetTo(aValue);
689 return true;
692 return SVGElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
693 aMaybeScriptedPrincipal, aResult);
696 void SVGElement::UnsetAttrInternal(int32_t aNamespaceID, nsAtom* aName,
697 bool aNotify) {
698 // XXXbz there's a bunch of redundancy here with AfterSetAttr.
699 // Maybe consolidate?
701 if (aNamespaceID == kNameSpaceID_None) {
702 // If this is an svg presentation attribute, remove declaration block to
703 // force an update
704 if (IsAttributeMapped(aName)) {
705 mContentDeclarationBlock = nullptr;
708 if (IsEventAttributeName(aName)) {
709 EventListenerManager* manager = GetExistingListenerManager();
710 if (manager) {
711 nsAtom* eventName = GetEventNameForAttr(aName);
712 manager->RemoveEventHandler(eventName);
714 return;
717 // Check if this is a length attribute going away
718 LengthAttributesInfo lenInfo = GetLengthInfo();
720 for (uint32_t i = 0; i < lenInfo.mCount; i++) {
721 if (aName == lenInfo.mInfos[i].mName) {
722 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
723 lenInfo.Reset(i);
724 return;
728 // Check if this is a length list attribute going away
729 LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
731 for (uint32_t i = 0; i < lengthListInfo.mCount; i++) {
732 if (aName == lengthListInfo.mInfos[i].mName) {
733 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
734 lengthListInfo.Reset(i);
735 return;
739 // Check if this is a number list attribute going away
740 NumberListAttributesInfo numberListInfo = GetNumberListInfo();
742 for (uint32_t i = 0; i < numberListInfo.mCount; i++) {
743 if (aName == numberListInfo.mInfos[i].mName) {
744 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
745 numberListInfo.Reset(i);
746 return;
750 // Check if this is a point list attribute going away
751 if (GetPointListAttrName() == aName) {
752 SVGAnimatedPointList* pointList = GetAnimatedPointList();
753 if (pointList) {
754 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
755 pointList->ClearBaseValue();
756 return;
760 // Check if this is a path segment list attribute going away
761 if (GetPathDataAttrName() == aName) {
762 SVGAnimatedPathSegList* segList = GetAnimPathSegList();
763 if (segList) {
764 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
765 segList->ClearBaseValue();
766 return;
770 // Check if this is a number attribute going away
771 NumberAttributesInfo numInfo = GetNumberInfo();
773 for (uint32_t i = 0; i < numInfo.mCount; i++) {
774 if (aName == numInfo.mInfos[i].mName) {
775 numInfo.Reset(i);
776 return;
780 // Check if this is a number pair attribute going away
781 NumberPairAttributesInfo numPairInfo = GetNumberPairInfo();
783 for (uint32_t i = 0; i < numPairInfo.mCount; i++) {
784 if (aName == numPairInfo.mInfos[i].mName) {
785 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
786 numPairInfo.Reset(i);
787 return;
791 // Check if this is an integer attribute going away
792 IntegerAttributesInfo intInfo = GetIntegerInfo();
794 for (uint32_t i = 0; i < intInfo.mCount; i++) {
795 if (aName == intInfo.mInfos[i].mName) {
796 intInfo.Reset(i);
797 return;
801 // Check if this is an integer pair attribute going away
802 IntegerPairAttributesInfo intPairInfo = GetIntegerPairInfo();
804 for (uint32_t i = 0; i < intPairInfo.mCount; i++) {
805 if (aName == intPairInfo.mInfos[i].mName) {
806 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
807 intPairInfo.Reset(i);
808 return;
812 // Check if this is a boolean attribute going away
813 BooleanAttributesInfo boolInfo = GetBooleanInfo();
815 for (uint32_t i = 0; i < boolInfo.mCount; i++) {
816 if (aName == boolInfo.mInfos[i].mName) {
817 boolInfo.Reset(i);
818 return;
822 // Check if this is an enum attribute going away
823 EnumAttributesInfo enumInfo = GetEnumInfo();
825 for (uint32_t i = 0; i < enumInfo.mCount; i++) {
826 if (aName == enumInfo.mInfos[i].mName) {
827 enumInfo.Reset(i);
828 return;
832 // Check if this is an orient attribute going away
833 if (aName == nsGkAtoms::orient) {
834 SVGAnimatedOrient* orient = GetAnimatedOrient();
835 if (orient) {
836 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
837 orient->Init();
838 return;
842 // Check if this is a viewBox attribute going away
843 if (aName == nsGkAtoms::viewBox) {
844 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
845 if (viewBox) {
846 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
847 viewBox->Init();
848 return;
852 // Check if this is a preserveAspectRatio attribute going away
853 if (aName == nsGkAtoms::preserveAspectRatio) {
854 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
855 GetAnimatedPreserveAspectRatio();
856 if (preserveAspectRatio) {
857 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
858 preserveAspectRatio->Init();
859 return;
863 // Check if this is a transform list attribute going away
864 if (GetTransformListAttrName() == aName) {
865 SVGAnimatedTransformList* transformList = GetAnimatedTransformList();
866 if (transformList) {
867 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
868 transformList->ClearBaseValue();
869 return;
873 // Check for conditional processing attributes
874 nsCOMPtr<SVGTests> tests = do_QueryObject(this);
875 if (tests && tests->IsConditionalProcessingAttribute(aName)) {
876 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
877 tests->UnsetAttr(aName);
878 return;
881 // Check if this is a string list attribute going away
882 StringListAttributesInfo stringListInfo = GetStringListInfo();
884 for (uint32_t i = 0; i < stringListInfo.mCount; i++) {
885 if (aName == stringListInfo.mInfos[i].mName) {
886 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
887 stringListInfo.Reset(i);
888 return;
892 if (aName == nsGkAtoms::_class) {
893 mClassAttribute.Init();
894 return;
898 // Check if this is a string attribute going away
899 StringAttributesInfo stringInfo = GetStringInfo();
901 for (uint32_t i = 0; i < stringInfo.mCount; i++) {
902 if (aNamespaceID == stringInfo.mInfos[i].mNamespaceID &&
903 aName == stringInfo.mInfos[i].mName) {
904 stringInfo.Reset(i);
905 return;
910 nsresult SVGElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
911 const nsAttrValueOrString* aValue,
912 bool aNotify) {
913 if (!aValue) {
914 UnsetAttrInternal(aNamespaceID, aName, aNotify);
916 return SVGElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify);
919 nsChangeHint SVGElement::GetAttributeChangeHint(const nsAtom* aAttribute,
920 int32_t aModType) const {
921 nsChangeHint retval =
922 SVGElementBase::GetAttributeChangeHint(aAttribute, aModType);
924 nsCOMPtr<SVGTests> tests = do_QueryObject(const_cast<SVGElement*>(this));
925 if (tests && tests->IsConditionalProcessingAttribute(aAttribute)) {
926 // It would be nice to only reconstruct the frame if the value returned by
927 // SVGTests::PassesConditionalProcessingTests has changed, but we don't
928 // know that
929 retval |= nsChangeHint_ReconstructFrame;
931 return retval;
934 bool SVGElement::IsNodeOfType(uint32_t aFlags) const { return false; }
936 void SVGElement::NodeInfoChanged(Document* aOldDoc) {
937 SVGElementBase::NodeInfoChanged(aOldDoc);
938 aOldDoc->UnscheduleSVGForPresAttrEvaluation(this);
939 mContentDeclarationBlock = nullptr;
940 OwnerDoc()->ScheduleSVGForPresAttrEvaluation(this);
943 NS_IMETHODIMP_(bool)
944 SVGElement::IsAttributeMapped(const nsAtom* name) const {
945 if (name == nsGkAtoms::lang) {
946 return true;
948 return SVGElementBase::IsAttributeMapped(name);
951 // PresentationAttributes-FillStroke
952 /* static */
953 const Element::MappedAttributeEntry SVGElement::sFillStrokeMap[] = {
954 {nsGkAtoms::fill},
955 {nsGkAtoms::fill_opacity},
956 {nsGkAtoms::fill_rule},
957 {nsGkAtoms::paint_order},
958 {nsGkAtoms::stroke},
959 {nsGkAtoms::stroke_dasharray},
960 {nsGkAtoms::stroke_dashoffset},
961 {nsGkAtoms::stroke_linecap},
962 {nsGkAtoms::stroke_linejoin},
963 {nsGkAtoms::stroke_miterlimit},
964 {nsGkAtoms::stroke_opacity},
965 {nsGkAtoms::stroke_width},
966 {nsGkAtoms::vector_effect},
967 {nullptr}};
969 // PresentationAttributes-Graphics
970 /* static */
971 const Element::MappedAttributeEntry SVGElement::sGraphicsMap[] = {
972 {nsGkAtoms::clip_path},
973 {nsGkAtoms::clip_rule},
974 {nsGkAtoms::colorInterpolation},
975 {nsGkAtoms::cursor},
976 {nsGkAtoms::display},
977 {nsGkAtoms::filter},
978 {nsGkAtoms::image_rendering},
979 {nsGkAtoms::mask},
980 {nsGkAtoms::opacity},
981 {nsGkAtoms::pointer_events},
982 {nsGkAtoms::shape_rendering},
983 {nsGkAtoms::text_rendering},
984 {nsGkAtoms::transform_origin},
985 {nsGkAtoms::visibility},
986 {nullptr}};
988 // PresentationAttributes-TextContentElements
989 /* static */
990 const Element::MappedAttributeEntry SVGElement::sTextContentElementsMap[] = {
991 // Properties that we don't support are commented out.
992 // { nsGkAtoms::alignment_baseline },
993 // { nsGkAtoms::baseline_shift },
994 {nsGkAtoms::direction}, {nsGkAtoms::dominant_baseline},
995 {nsGkAtoms::letter_spacing}, {nsGkAtoms::text_anchor},
996 {nsGkAtoms::text_decoration}, {nsGkAtoms::unicode_bidi},
997 {nsGkAtoms::white_space}, {nsGkAtoms::word_spacing},
998 {nsGkAtoms::writing_mode}, {nullptr}};
1000 // PresentationAttributes-FontSpecification
1001 /* static */
1002 const Element::MappedAttributeEntry SVGElement::sFontSpecificationMap[] = {
1003 {nsGkAtoms::font_family}, {nsGkAtoms::font_size},
1004 {nsGkAtoms::font_size_adjust}, {nsGkAtoms::font_stretch},
1005 {nsGkAtoms::font_style}, {nsGkAtoms::font_variant},
1006 {nsGkAtoms::fontWeight}, {nullptr}};
1008 // PresentationAttributes-GradientStop
1009 /* static */
1010 const Element::MappedAttributeEntry SVGElement::sGradientStopMap[] = {
1011 {nsGkAtoms::stop_color}, {nsGkAtoms::stop_opacity}, {nullptr}};
1013 // PresentationAttributes-Viewports
1014 /* static */
1015 const Element::MappedAttributeEntry SVGElement::sViewportsMap[] = {
1016 {nsGkAtoms::overflow}, {nsGkAtoms::clip}, {nullptr}};
1018 // PresentationAttributes-Makers
1019 /* static */
1020 const Element::MappedAttributeEntry SVGElement::sMarkersMap[] = {
1021 {nsGkAtoms::marker_end},
1022 {nsGkAtoms::marker_mid},
1023 {nsGkAtoms::marker_start},
1024 {nullptr}};
1026 // PresentationAttributes-Color
1027 /* static */
1028 const Element::MappedAttributeEntry SVGElement::sColorMap[] = {
1029 {nsGkAtoms::color}, {nullptr}};
1031 // PresentationAttributes-Filters
1032 /* static */
1033 const Element::MappedAttributeEntry SVGElement::sFiltersMap[] = {
1034 {nsGkAtoms::colorInterpolationFilters}, {nullptr}};
1036 // PresentationAttributes-feFlood
1037 /* static */
1038 const Element::MappedAttributeEntry SVGElement::sFEFloodMap[] = {
1039 {nsGkAtoms::flood_color}, {nsGkAtoms::flood_opacity}, {nullptr}};
1041 // PresentationAttributes-LightingEffects
1042 /* static */
1043 const Element::MappedAttributeEntry SVGElement::sLightingEffectsMap[] = {
1044 {nsGkAtoms::lighting_color}, {nullptr}};
1046 // PresentationAttributes-mask
1047 /* static */
1048 const Element::MappedAttributeEntry SVGElement::sMaskMap[] = {
1049 {nsGkAtoms::mask_type}, {nullptr}};
1051 //----------------------------------------------------------------------
1052 // Element methods
1054 // forwarded to Element implementations
1056 //----------------------------------------------------------------------
1058 SVGSVGElement* SVGElement::GetOwnerSVGElement() {
1059 nsIContent* ancestor = GetFlattenedTreeParent();
1061 while (ancestor && ancestor->IsSVGElement()) {
1062 if (ancestor->IsSVGElement(nsGkAtoms::foreignObject)) {
1063 return nullptr;
1065 if (ancestor->IsSVGElement(nsGkAtoms::svg)) {
1066 return static_cast<SVGSVGElement*>(ancestor);
1068 ancestor = ancestor->GetFlattenedTreeParent();
1071 // we don't have an ancestor <svg> element...
1072 return nullptr;
1075 SVGElement* SVGElement::GetViewportElement() {
1076 return SVGContentUtils::GetNearestViewportElement(this);
1079 already_AddRefed<DOMSVGAnimatedString> SVGElement::ClassName() {
1080 return mClassAttribute.ToDOMAnimatedString(this);
1083 /* static */
1084 bool SVGElement::UpdateDeclarationBlockFromLength(
1085 DeclarationBlock& aBlock, nsCSSPropertyID aPropId,
1086 const SVGAnimatedLength& aLength, ValToUse aValToUse) {
1087 aBlock.AssertMutable();
1089 float value;
1090 if (aValToUse == ValToUse::Anim) {
1091 value = aLength.GetAnimValInSpecifiedUnits();
1092 } else {
1093 MOZ_ASSERT(aValToUse == ValToUse::Base);
1094 value = aLength.GetBaseValInSpecifiedUnits();
1097 // SVG parser doesn't check non-negativity of some parsed value,
1098 // we should not pass those to CSS side.
1099 if (value < 0 &&
1100 SVGGeometryProperty::IsNonNegativeGeometryProperty(aPropId)) {
1101 return false;
1104 nsCSSUnit cssUnit = SVGGeometryProperty::SpecifiedUnitTypeToCSSUnit(
1105 aLength.GetSpecifiedUnitType());
1107 if (cssUnit == eCSSUnit_Percent) {
1108 Servo_DeclarationBlock_SetPercentValue(aBlock.Raw(), aPropId,
1109 value / 100.f);
1110 } else {
1111 Servo_DeclarationBlock_SetLengthValue(aBlock.Raw(), aPropId, value,
1112 cssUnit);
1115 return true;
1118 /* static */
1119 bool SVGElement::UpdateDeclarationBlockFromPath(
1120 DeclarationBlock& aBlock, const SVGAnimatedPathSegList& aPath,
1121 ValToUse aValToUse) {
1122 aBlock.AssertMutable();
1124 const SVGPathData& pathData =
1125 aValToUse == ValToUse::Anim ? aPath.GetAnimValue() : aPath.GetBaseValue();
1127 // SVGPathData::mData is fallible but rust binding accepts nsTArray only, so
1128 // we need to point to one or the other. Fortunately, fallible and infallible
1129 // array types can be implicitly converted provided they are const.
1131 // FIXME: here we just convert the data structure from cpp verion into rust
1132 // version. We don't do any normalization for the path data from d attribute.
1133 // Based on the current discussion of https://github.com/w3c/svgwg/issues/321,
1134 // we may have to convert the relative commands into absolute commands.
1135 // The normalization should be fixed in Bug 1489392. Besides, Bug 1714238
1136 // will use the same data structure, so we may simplify this more.
1137 const nsTArray<float>& asInFallibleArray = pathData.RawData();
1138 Servo_DeclarationBlock_SetPathValue(aBlock.Raw(), eCSSProperty_d,
1139 &asInFallibleArray);
1140 return true;
1143 //------------------------------------------------------------------------
1144 // Helper class: MappedAttrParser, for parsing values of mapped attributes
1146 namespace {
1148 class MOZ_STACK_CLASS MappedAttrParser {
1149 public:
1150 explicit MappedAttrParser(SVGElement& aElement) : mElement(aElement) {}
1151 ~MappedAttrParser() {
1152 MOZ_ASSERT(!mDecl,
1153 "If mDecl was initialized, it should have been returned via "
1154 "TakeDeclarationBlock (and have its pointer cleared)");
1157 // Parses a mapped attribute value.
1158 void ParseMappedAttrValue(nsAtom* aMappedAttrName,
1159 const nsAString& aMappedAttrValue);
1161 void TellStyleAlreadyParsedResult(nsAtom const* aAtom,
1162 SVGAnimatedLength const& aLength);
1163 void TellStyleAlreadyParsedResult(const SVGAnimatedPathSegList& aPath);
1165 // If we've parsed any values for mapped attributes, this method returns the
1166 // already_AddRefed css::Declaration that incorporates the parsed
1167 // values. Otherwise, this method returns null.
1168 already_AddRefed<DeclarationBlock> TakeDeclarationBlock() {
1169 return mDecl.forget();
1172 DeclarationBlock& EnsureDeclarationBlock() {
1173 if (!mDecl) {
1174 mDecl = new DeclarationBlock();
1176 return *mDecl;
1179 URLExtraData& EnsureExtraData() {
1180 if (!mExtraData) {
1181 nsCOMPtr<nsIReferrerInfo> referrerInfo =
1182 ReferrerInfo::CreateForSVGResources(mElement.OwnerDoc());
1183 mExtraData = MakeRefPtr<URLExtraData>(mElement.GetBaseURI(), referrerInfo,
1184 mElement.NodePrincipal());
1186 return *mExtraData;
1189 private:
1190 // Declaration for storing parsed values (lazily initialized).
1191 RefPtr<DeclarationBlock> mDecl;
1193 // URL data for parsing stuff. Also lazy.
1194 RefPtr<URLExtraData> mExtraData;
1196 // For reporting use counters
1197 SVGElement& mElement;
1200 void MappedAttrParser::ParseMappedAttrValue(nsAtom* aMappedAttrName,
1201 const nsAString& aMappedAttrValue) {
1202 // Get the nsCSSPropertyID ID for our mapped attribute.
1203 nsCSSPropertyID propertyID =
1204 nsCSSProps::LookupProperty(nsAtomCString(aMappedAttrName));
1205 if (propertyID != eCSSProperty_UNKNOWN) {
1206 bool changed = false; // outparam for ParseProperty.
1207 NS_ConvertUTF16toUTF8 value(aMappedAttrValue);
1209 auto* doc = mElement.OwnerDoc();
1210 changed = Servo_DeclarationBlock_SetPropertyById(
1211 EnsureDeclarationBlock().Raw(), propertyID, &value, false,
1212 &EnsureExtraData(), ParsingMode::AllowUnitlessLength,
1213 doc->GetCompatibilityMode(), doc->CSSLoader(), StyleCssRuleType::Style,
1214 {});
1216 // TODO(emilio): If we want to record these from CSSOM more generally, we
1217 // can pass the document use counters down the FFI call. For now manually
1218 // count them.
1219 if (changed && StaticPrefs::layout_css_use_counters_enabled()) {
1220 UseCounter useCounter = nsCSSProps::UseCounterFor(propertyID);
1221 MOZ_ASSERT(useCounter != eUseCounter_UNKNOWN);
1222 doc->SetUseCounter(useCounter);
1224 return;
1226 MOZ_ASSERT(aMappedAttrName == nsGkAtoms::lang,
1227 "Only 'lang' should be unrecognized!");
1228 // CSS parser doesn't know about 'lang', so we need to handle it specially.
1229 if (aMappedAttrName == nsGkAtoms::lang) {
1230 propertyID = eCSSProperty__x_lang;
1231 RefPtr<nsAtom> atom = NS_Atomize(aMappedAttrValue);
1232 Servo_DeclarationBlock_SetIdentStringValue(EnsureDeclarationBlock().Raw(),
1233 propertyID, atom);
1237 void MappedAttrParser::TellStyleAlreadyParsedResult(
1238 nsAtom const* aAtom, SVGAnimatedLength const& aLength) {
1239 nsCSSPropertyID propertyID = nsCSSProps::LookupProperty(nsAtomCString(aAtom));
1240 SVGElement::UpdateDeclarationBlockFromLength(EnsureDeclarationBlock(),
1241 propertyID, aLength,
1242 SVGElement::ValToUse::Base);
1245 void MappedAttrParser::TellStyleAlreadyParsedResult(
1246 const SVGAnimatedPathSegList& aPath) {
1247 SVGElement::UpdateDeclarationBlockFromPath(EnsureDeclarationBlock(), aPath,
1248 SVGElement::ValToUse::Base);
1251 } // namespace
1253 //----------------------------------------------------------------------
1254 // Implementation Helpers:
1256 void SVGElement::UpdateContentDeclarationBlock() {
1257 MOZ_ASSERT(!mContentDeclarationBlock,
1258 "we already have a content declaration block");
1260 MappedAttrParser mappedAttrParser(*this);
1262 bool lengthAffectsStyle =
1263 SVGGeometryProperty::ElementMapsLengthsToStyle(this);
1265 uint32_t i = 0;
1266 while (BorrowedAttrInfo info = GetAttrInfoAt(i++)) {
1267 const nsAttrName* attrName = info.mName;
1268 if (!attrName->IsAtom() || !IsAttributeMapped(attrName->Atom())) {
1269 continue;
1272 // FIXME(emilio): This check is dead, since IsAtom() implies that
1273 // NamespaceID() == None.
1274 if (attrName->NamespaceID() != kNameSpaceID_None &&
1275 !attrName->Equals(nsGkAtoms::lang, kNameSpaceID_XML)) {
1276 continue;
1279 if (attrName->Equals(nsGkAtoms::lang, kNameSpaceID_None) &&
1280 HasAttr(kNameSpaceID_XML, nsGkAtoms::lang)) {
1281 // xml:lang has precedence, and will get set via Gecko_GetXMLLangValue().
1282 continue;
1285 if (lengthAffectsStyle) {
1286 auto const* length = GetAnimatedLength(attrName->Atom());
1288 if (length && length->HasBaseVal()) {
1289 // This is an element with geometry property set via SVG attribute,
1290 // and the attribute is already successfully parsed. We want to go
1291 // through the optimized path to tell the style system the result
1292 // directly, rather than let it parse the same thing again.
1293 mappedAttrParser.TellStyleAlreadyParsedResult(attrName->Atom(),
1294 *length);
1295 continue;
1299 if (attrName->Equals(nsGkAtoms::d, kNameSpaceID_None)) {
1300 const auto* path = GetAnimPathSegList();
1301 // Note: Only SVGPathElement has d attribute.
1302 MOZ_ASSERT(
1303 path,
1304 "SVGPathElement should have the non-null SVGAnimatedPathSegList");
1305 // The attribute should have been already successfully parsed.
1306 // We want to go through the optimized path to tell the style system
1307 // the result directly, rather than let it parse the same thing again.
1308 mappedAttrParser.TellStyleAlreadyParsedResult(*path);
1309 // Some other notes:
1310 // The syntax of CSS d property is different from SVG d attribute.
1311 // 1. CSS d proeprty accepts: none | path(<quoted string>);
1312 // 2. SVG d attribtue accepts: none | <string>
1313 // So we cannot use css parser to parse the SVG d attribute directly.
1314 // Besides, |mAttrs.AttrAt(i)| removes the quotes already, so the svg path
1315 // in |mAttrs.AttrAt(i)| would be something like `M0,0L1,1z` without the
1316 // quotes. So css tokenizer cannot recognize this as a quoted string, and
1317 // so svg_path::SVGPathData::parse() doesn't work for this. Fortunately,
1318 // we still can rely on the parsed result from
1319 // SVGElement::ParseAttribute() for d attribute.
1320 continue;
1323 nsAutoString value;
1324 info.mValue->ToString(value);
1325 mappedAttrParser.ParseMappedAttrValue(attrName->Atom(), value);
1327 mContentDeclarationBlock = mappedAttrParser.TakeDeclarationBlock();
1330 const DeclarationBlock* SVGElement::GetContentDeclarationBlock() const {
1331 return mContentDeclarationBlock;
1335 * Helper methods for the type-specific WillChangeXXX methods.
1337 * This method sends out appropriate pre-change notifications so that selector
1338 * restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop
1339 * matching) work, and it returns an nsAttrValue that _may_ contain the
1340 * attribute's pre-change value.
1342 * The nsAttrValue returned by this method depends on whether there are
1343 * mutation event listeners listening for changes to this element's attributes.
1344 * If not, then the object returned is empty. If there are, then the
1345 * nsAttrValue returned contains a serialized copy of the attribute's value
1346 * prior to the change, and this object should be passed to the corresponding
1347 * DidChangeXXX method call (assuming a WillChangeXXX call is required for the
1348 * SVG type - see comment below). This is necessary so that the 'prevValue'
1349 * property of the mutation event that is dispatched will correctly contain the
1350 * old value.
1352 * The reason we need to serialize the old value if there are mutation
1353 * event listeners is because the underlying nsAttrValue for the attribute
1354 * points directly to a parsed representation of the attribute (e.g. an
1355 * SVGAnimatedLengthList*) that is a member of the SVG element. That object
1356 * will have changed by the time DidChangeXXX has been called, so without the
1357 * serialization of the old attribute value that we provide, DidChangeXXX
1358 * would have no way to get the old value to pass to SetAttrAndNotify.
1360 * We only return the old value when there are mutation event listeners because
1361 * it's not needed otherwise, and because it's expensive to serialize the old
1362 * value. This is especially true for list type attributes, which may be built
1363 * up via the SVG DOM resulting in a large number of Will/DidModifyXXX calls
1364 * before the script finally finishes setting the attribute.
1366 * Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check
1367 * and filter out redundant changes. Before calling WillChangeXXX, the caller
1368 * should check whether the new and old values are actually the same, and skip
1369 * calling Will/DidChangeXXX if they are.
1371 * Also note that not all SVG types use this scheme. For types that can be
1372 * represented by an nsAttrValue without pointing back to an SVG object (e.g.
1373 * enums, booleans, integers) we can simply use SetParsedAttr which will do all
1374 * of the above for us. For such types there is no matching WillChangeXXX
1375 * method, only DidChangeXXX which calls SetParsedAttr.
1377 nsAttrValue SVGElement::WillChangeValue(
1378 nsAtom* aName, const mozAutoDocUpdate& aProofOfUpdate) {
1379 // We need an empty attr value:
1380 // a) to pass to BeforeSetAttr when GetParsedAttr returns nullptr
1381 // b) to store the old value in the case we have mutation listeners
1383 // We can use the same value for both purposes, because if GetParsedAttr
1384 // returns non-null its return value is what will get passed to BeforeSetAttr,
1385 // not matter what our mutation listener situation is.
1387 // Also, we should be careful to always return this value to benefit from
1388 // return value optimization.
1389 nsAttrValue emptyOrOldAttrValue;
1390 const nsAttrValue* attrValue = GetParsedAttr(aName);
1392 // We only need to set the old value if we have listeners since otherwise it
1393 // isn't used.
1394 if (attrValue && nsContentUtils::HasMutationListeners(
1395 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) {
1396 emptyOrOldAttrValue.SetToSerialized(*attrValue);
1399 uint8_t modType =
1400 attrValue ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
1401 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
1402 MutationObservers::NotifyAttributeWillChange(this, kNameSpaceID_None, aName,
1403 modType);
1405 // This is not strictly correct--the attribute value parameter for
1406 // BeforeSetAttr should reflect the value that *will* be set but that implies
1407 // allocating, e.g. an extra SVGAnimatedLength, and isn't necessary at the
1408 // moment since no SVG elements overload BeforeSetAttr. For now we just pass
1409 // the current value.
1410 nsAttrValueOrString attrStringOrValue(attrValue ? *attrValue
1411 : emptyOrOldAttrValue);
1412 DebugOnly<nsresult> rv = BeforeSetAttr(
1413 kNameSpaceID_None, aName, &attrStringOrValue, kNotifyDocumentObservers);
1414 // SVG elements aren't expected to overload BeforeSetAttr in such a way that
1415 // it may fail. So long as this is the case we don't need to check and pass on
1416 // the return value which simplifies the calling code significantly.
1417 MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected failure from BeforeSetAttr");
1419 return emptyOrOldAttrValue;
1423 * Helper methods for the type-specific DidChangeXXX methods.
1425 * aEmptyOrOldValue will normally be the object returned from the corresponding
1426 * WillChangeXXX call. This is because:
1427 * a) WillChangeXXX will ensure the object is set when we have mutation
1428 * listeners, and
1429 * b) WillChangeXXX will ensure the object represents a serialized version of
1430 * the old attribute value so that the value doesn't change when the
1431 * underlying SVG type is updated.
1433 * aNewValue is replaced with the old value.
1435 void SVGElement::DidChangeValue(nsAtom* aName,
1436 const nsAttrValue& aEmptyOrOldValue,
1437 nsAttrValue& aNewValue,
1438 const mozAutoDocUpdate& aProofOfUpdate) {
1439 bool hasListeners = nsContentUtils::HasMutationListeners(
1440 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this);
1441 uint8_t modType =
1442 HasAttr(kNameSpaceID_None, aName)
1443 ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
1444 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
1446 // XXX Really, the fourth argument to SetAttrAndNotify should be null if
1447 // aEmptyOrOldValue does not represent the actual previous value of the
1448 // attribute, but currently SVG elements do not even use the old attribute
1449 // value in |AfterSetAttr|, so this should be ok.
1450 SetAttrAndNotify(kNameSpaceID_None, aName, nullptr, &aEmptyOrOldValue,
1451 aNewValue, nullptr, modType, hasListeners,
1452 kNotifyDocumentObservers, kCallAfterSetAttr,
1453 GetComposedDoc(), aProofOfUpdate);
1456 void SVGElement::MaybeSerializeAttrBeforeRemoval(nsAtom* aName, bool aNotify) {
1457 if (!aNotify || !nsContentUtils::HasMutationListeners(
1458 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) {
1459 return;
1462 const nsAttrValue* attrValue = mAttrs.GetAttr(aName);
1463 if (!attrValue) return;
1465 nsAutoString serializedValue;
1466 attrValue->ToString(serializedValue);
1467 nsAttrValue oldAttrValue(serializedValue);
1468 bool oldValueSet;
1469 mAttrs.SetAndSwapAttr(aName, oldAttrValue, &oldValueSet);
1472 nsAtom* SVGElement::GetEventNameForAttr(nsAtom* aAttr) {
1473 if (IsSVGElement(nsGkAtoms::svg)) {
1474 if (aAttr == nsGkAtoms::onload) return nsGkAtoms::onSVGLoad;
1475 if (aAttr == nsGkAtoms::onscroll) return nsGkAtoms::onSVGScroll;
1477 if (aAttr == nsGkAtoms::onbegin) return nsGkAtoms::onbeginEvent;
1478 if (aAttr == nsGkAtoms::onrepeat) return nsGkAtoms::onrepeatEvent;
1479 if (aAttr == nsGkAtoms::onend) return nsGkAtoms::onendEvent;
1481 return SVGElementBase::GetEventNameForAttr(aAttr);
1484 SVGViewportElement* SVGElement::GetCtx() const {
1485 return SVGContentUtils::GetNearestViewportElement(this);
1488 /* virtual */
1489 gfxMatrix SVGElement::PrependLocalTransformsTo(const gfxMatrix& aMatrix,
1490 SVGTransformTypes aWhich) const {
1491 return aMatrix;
1494 SVGElement::LengthAttributesInfo SVGElement::GetLengthInfo() {
1495 return LengthAttributesInfo(nullptr, nullptr, 0);
1498 void SVGElement::SetLength(nsAtom* aName, const SVGAnimatedLength& aLength) {
1499 LengthAttributesInfo lengthInfo = GetLengthInfo();
1501 for (uint32_t i = 0; i < lengthInfo.mCount; i++) {
1502 if (aName == lengthInfo.mInfos[i].mName) {
1503 lengthInfo.mValues[i] = aLength;
1504 DidAnimateLength(i);
1505 return;
1508 MOZ_ASSERT(false, "no length found to set");
1511 nsAttrValue SVGElement::WillChangeLength(
1512 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1513 return WillChangeValue(GetLengthInfo().mInfos[aAttrEnum].mName,
1514 aProofOfUpdate);
1517 void SVGElement::DidChangeLength(uint8_t aAttrEnum,
1518 const nsAttrValue& aEmptyOrOldValue,
1519 const mozAutoDocUpdate& aProofOfUpdate) {
1520 LengthAttributesInfo info = GetLengthInfo();
1522 NS_ASSERTION(info.mCount > 0,
1523 "DidChangeLength on element with no length attribs");
1524 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1526 nsAttrValue newValue;
1527 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
1529 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1530 aProofOfUpdate);
1533 void SVGElement::DidAnimateLength(uint8_t aAttrEnum) {
1534 // We need to do this here. Normally the SMIL restyle would also cause us to
1535 // do this from DidSetComputedStyle, but we don't have that guarantee if our
1536 // frame gets reconstructed.
1537 ClearAnyCachedPath();
1539 if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) {
1540 nsCSSPropertyID propId =
1541 SVGGeometryProperty::AttrEnumToCSSPropId(this, aAttrEnum);
1543 // We don't map use element width/height currently. We can remove this
1544 // test when we do.
1545 if (propId != eCSSProperty_UNKNOWN) {
1546 SMILOverrideStyle()->SetSMILValue(propId,
1547 GetLengthInfo().mValues[aAttrEnum]);
1548 return;
1552 nsIFrame* frame = GetPrimaryFrame();
1554 if (frame) {
1555 LengthAttributesInfo info = GetLengthInfo();
1556 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1557 MutationEvent_Binding::SMIL);
1561 SVGAnimatedLength* SVGElement::GetAnimatedLength(uint8_t aAttrEnum) {
1562 LengthAttributesInfo info = GetLengthInfo();
1563 if (aAttrEnum < info.mCount) {
1564 return &info.mValues[aAttrEnum];
1566 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1567 return nullptr;
1570 SVGAnimatedLength* SVGElement::GetAnimatedLength(const nsAtom* aAttrName) {
1571 LengthAttributesInfo lengthInfo = GetLengthInfo();
1573 for (uint32_t i = 0; i < lengthInfo.mCount; i++) {
1574 if (aAttrName == lengthInfo.mInfos[i].mName) {
1575 return &lengthInfo.mValues[i];
1578 return nullptr;
1581 void SVGElement::GetAnimatedLengthValues(float* aFirst, ...) {
1582 LengthAttributesInfo info = GetLengthInfo();
1584 NS_ASSERTION(info.mCount > 0,
1585 "GetAnimatedLengthValues on element with no length attribs");
1587 SVGViewportElement* ctx = nullptr;
1589 float* f = aFirst;
1590 uint32_t i = 0;
1592 va_list args;
1593 va_start(args, aFirst);
1595 while (f && i < info.mCount) {
1596 uint8_t type = info.mValues[i].GetSpecifiedUnitType();
1597 if (!ctx) {
1598 if (type != SVGLength_Binding::SVG_LENGTHTYPE_NUMBER &&
1599 type != SVGLength_Binding::SVG_LENGTHTYPE_PX)
1600 ctx = GetCtx();
1602 if (type == SVGLength_Binding::SVG_LENGTHTYPE_EMS ||
1603 type == SVGLength_Binding::SVG_LENGTHTYPE_EXS)
1604 *f = info.mValues[i++].GetAnimValue(this);
1605 else
1606 *f = info.mValues[i++].GetAnimValue(ctx);
1607 f = va_arg(args, float*);
1610 va_end(args);
1613 SVGElement::LengthListAttributesInfo SVGElement::GetLengthListInfo() {
1614 return LengthListAttributesInfo(nullptr, nullptr, 0);
1617 nsAttrValue SVGElement::WillChangeLengthList(
1618 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1619 return WillChangeValue(GetLengthListInfo().mInfos[aAttrEnum].mName,
1620 aProofOfUpdate);
1623 void SVGElement::DidChangeLengthList(uint8_t aAttrEnum,
1624 const nsAttrValue& aEmptyOrOldValue,
1625 const mozAutoDocUpdate& aProofOfUpdate) {
1626 LengthListAttributesInfo info = GetLengthListInfo();
1628 NS_ASSERTION(info.mCount > 0,
1629 "DidChangeLengthList on element with no length list attribs");
1630 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1632 nsAttrValue newValue;
1633 newValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1635 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1636 aProofOfUpdate);
1639 void SVGElement::DidAnimateLengthList(uint8_t aAttrEnum) {
1640 nsIFrame* frame = GetPrimaryFrame();
1642 if (frame) {
1643 LengthListAttributesInfo info = GetLengthListInfo();
1644 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1645 MutationEvent_Binding::SMIL);
1649 void SVGElement::GetAnimatedLengthListValues(SVGUserUnitList* aFirst, ...) {
1650 LengthListAttributesInfo info = GetLengthListInfo();
1652 NS_ASSERTION(
1653 info.mCount > 0,
1654 "GetAnimatedLengthListValues on element with no length list attribs");
1656 SVGUserUnitList* list = aFirst;
1657 uint32_t i = 0;
1659 va_list args;
1660 va_start(args, aFirst);
1662 while (list && i < info.mCount) {
1663 list->Init(&(info.mValues[i].GetAnimValue()), this, info.mInfos[i].mAxis);
1664 ++i;
1665 list = va_arg(args, SVGUserUnitList*);
1668 va_end(args);
1671 SVGAnimatedLengthList* SVGElement::GetAnimatedLengthList(uint8_t aAttrEnum) {
1672 LengthListAttributesInfo info = GetLengthListInfo();
1673 if (aAttrEnum < info.mCount) {
1674 return &(info.mValues[aAttrEnum]);
1676 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1677 return nullptr;
1680 SVGElement::NumberListAttributesInfo SVGElement::GetNumberListInfo() {
1681 return NumberListAttributesInfo(nullptr, nullptr, 0);
1684 nsAttrValue SVGElement::WillChangeNumberList(
1685 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1686 return WillChangeValue(GetNumberListInfo().mInfos[aAttrEnum].mName,
1687 aProofOfUpdate);
1690 void SVGElement::DidChangeNumberList(uint8_t aAttrEnum,
1691 const nsAttrValue& aEmptyOrOldValue,
1692 const mozAutoDocUpdate& aProofOfUpdate) {
1693 NumberListAttributesInfo info = GetNumberListInfo();
1695 MOZ_ASSERT(info.mCount > 0,
1696 "DidChangeNumberList on element with no number list attribs");
1697 MOZ_ASSERT(aAttrEnum < info.mCount, "aAttrEnum out of range");
1699 nsAttrValue newValue;
1700 newValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1702 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1703 aProofOfUpdate);
1706 void SVGElement::DidAnimateNumberList(uint8_t aAttrEnum) {
1707 nsIFrame* frame = GetPrimaryFrame();
1709 if (frame) {
1710 NumberListAttributesInfo info = GetNumberListInfo();
1711 MOZ_ASSERT(aAttrEnum < info.mCount, "aAttrEnum out of range");
1713 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1714 MutationEvent_Binding::SMIL);
1718 SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(uint8_t aAttrEnum) {
1719 NumberListAttributesInfo info = GetNumberListInfo();
1720 if (aAttrEnum < info.mCount) {
1721 return &(info.mValues[aAttrEnum]);
1723 MOZ_ASSERT(false, "Bad attrEnum");
1724 return nullptr;
1727 SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(nsAtom* aAttrName) {
1728 NumberListAttributesInfo info = GetNumberListInfo();
1729 for (uint32_t i = 0; i < info.mCount; i++) {
1730 if (aAttrName == info.mInfos[i].mName) {
1731 return &info.mValues[i];
1734 MOZ_ASSERT(false, "Bad caller");
1735 return nullptr;
1738 nsAttrValue SVGElement::WillChangePointList(
1739 const mozAutoDocUpdate& aProofOfUpdate) {
1740 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1741 return WillChangeValue(GetPointListAttrName(), aProofOfUpdate);
1744 void SVGElement::DidChangePointList(const nsAttrValue& aEmptyOrOldValue,
1745 const mozAutoDocUpdate& aProofOfUpdate) {
1746 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1748 nsAttrValue newValue;
1749 newValue.SetTo(GetAnimatedPointList()->GetBaseValue(), nullptr);
1751 DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue, newValue,
1752 aProofOfUpdate);
1755 void SVGElement::DidAnimatePointList() {
1756 MOZ_ASSERT(GetPointListAttrName(), "Animating non-existent path data?");
1758 ClearAnyCachedPath();
1760 nsIFrame* frame = GetPrimaryFrame();
1762 if (frame) {
1763 frame->AttributeChanged(kNameSpaceID_None, GetPointListAttrName(),
1764 MutationEvent_Binding::SMIL);
1768 nsAttrValue SVGElement::WillChangePathSegList(
1769 const mozAutoDocUpdate& aProofOfUpdate) {
1770 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1771 return WillChangeValue(GetPathDataAttrName(), aProofOfUpdate);
1774 void SVGElement::DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue,
1775 const mozAutoDocUpdate& aProofOfUpdate) {
1776 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1778 nsAttrValue newValue;
1779 newValue.SetTo(GetAnimPathSegList()->GetBaseValue(), nullptr);
1781 DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue, newValue,
1782 aProofOfUpdate);
1785 void SVGElement::DidAnimatePathSegList() {
1786 nsStaticAtom* name = GetPathDataAttrName();
1787 MOZ_ASSERT(name, "Animating non-existent path data?");
1789 ClearAnyCachedPath();
1791 // Notify style we have to update the d property because of SMIL animation.
1792 if (StaticPrefs::layout_css_d_property_enabled() && name == nsGkAtoms::d) {
1793 SMILOverrideStyle()->SetSMILValue(nsCSSPropertyID::eCSSProperty_d,
1794 *GetAnimPathSegList());
1795 return;
1798 if (nsIFrame* frame = GetPrimaryFrame()) {
1799 frame->AttributeChanged(kNameSpaceID_None, name,
1800 MutationEvent_Binding::SMIL);
1804 SVGElement::NumberAttributesInfo SVGElement::GetNumberInfo() {
1805 return NumberAttributesInfo(nullptr, nullptr, 0);
1808 void SVGElement::DidChangeNumber(uint8_t aAttrEnum) {
1809 NumberAttributesInfo info = GetNumberInfo();
1811 NS_ASSERTION(info.mCount > 0,
1812 "DidChangeNumber on element with no number attribs");
1813 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1815 nsAttrValue attrValue;
1816 attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1818 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1819 attrValue, true);
1822 void SVGElement::DidAnimateNumber(uint8_t aAttrEnum) {
1823 nsIFrame* frame = GetPrimaryFrame();
1825 if (frame) {
1826 NumberAttributesInfo info = GetNumberInfo();
1827 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1828 MutationEvent_Binding::SMIL);
1832 void SVGElement::GetAnimatedNumberValues(float* aFirst, ...) {
1833 NumberAttributesInfo info = GetNumberInfo();
1835 NS_ASSERTION(info.mCount > 0,
1836 "GetAnimatedNumberValues on element with no number attribs");
1838 float* f = aFirst;
1839 uint32_t i = 0;
1841 va_list args;
1842 va_start(args, aFirst);
1844 while (f && i < info.mCount) {
1845 *f = info.mValues[i++].GetAnimValue();
1846 f = va_arg(args, float*);
1848 va_end(args);
1851 SVGElement::NumberPairAttributesInfo SVGElement::GetNumberPairInfo() {
1852 return NumberPairAttributesInfo(nullptr, nullptr, 0);
1855 nsAttrValue SVGElement::WillChangeNumberPair(uint8_t aAttrEnum) {
1856 mozAutoDocUpdate updateBatch(GetComposedDoc(), kDontNotifyDocumentObservers);
1857 return WillChangeValue(GetNumberPairInfo().mInfos[aAttrEnum].mName,
1858 updateBatch);
1861 void SVGElement::DidChangeNumberPair(uint8_t aAttrEnum,
1862 const nsAttrValue& aEmptyOrOldValue) {
1863 NumberPairAttributesInfo info = GetNumberPairInfo();
1865 NS_ASSERTION(info.mCount > 0,
1866 "DidChangePairNumber on element with no number pair attribs");
1867 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1869 nsAttrValue newValue;
1870 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
1872 mozAutoDocUpdate updateBatch(GetComposedDoc(), kNotifyDocumentObservers);
1873 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1874 updateBatch);
1877 void SVGElement::DidAnimateNumberPair(uint8_t aAttrEnum) {
1878 nsIFrame* frame = GetPrimaryFrame();
1880 if (frame) {
1881 NumberPairAttributesInfo info = GetNumberPairInfo();
1882 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1883 MutationEvent_Binding::SMIL);
1887 SVGElement::IntegerAttributesInfo SVGElement::GetIntegerInfo() {
1888 return IntegerAttributesInfo(nullptr, nullptr, 0);
1891 void SVGElement::DidChangeInteger(uint8_t aAttrEnum) {
1892 IntegerAttributesInfo info = GetIntegerInfo();
1893 NS_ASSERTION(info.mCount > 0,
1894 "DidChangeInteger on element with no integer attribs");
1895 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1897 nsAttrValue attrValue;
1898 attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1900 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1901 attrValue, true);
1904 void SVGElement::DidAnimateInteger(uint8_t aAttrEnum) {
1905 nsIFrame* frame = GetPrimaryFrame();
1907 if (frame) {
1908 IntegerAttributesInfo info = GetIntegerInfo();
1909 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1910 MutationEvent_Binding::SMIL);
1914 void SVGElement::GetAnimatedIntegerValues(int32_t* aFirst, ...) {
1915 IntegerAttributesInfo info = GetIntegerInfo();
1917 NS_ASSERTION(info.mCount > 0,
1918 "GetAnimatedIntegerValues on element with no integer attribs");
1920 int32_t* n = aFirst;
1921 uint32_t i = 0;
1923 va_list args;
1924 va_start(args, aFirst);
1926 while (n && i < info.mCount) {
1927 *n = info.mValues[i++].GetAnimValue();
1928 n = va_arg(args, int32_t*);
1930 va_end(args);
1933 SVGElement::IntegerPairAttributesInfo SVGElement::GetIntegerPairInfo() {
1934 return IntegerPairAttributesInfo(nullptr, nullptr, 0);
1937 nsAttrValue SVGElement::WillChangeIntegerPair(
1938 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1939 return WillChangeValue(GetIntegerPairInfo().mInfos[aAttrEnum].mName,
1940 aProofOfUpdate);
1943 void SVGElement::DidChangeIntegerPair(uint8_t aAttrEnum,
1944 const nsAttrValue& aEmptyOrOldValue,
1945 const mozAutoDocUpdate& aProofOfUpdate) {
1946 IntegerPairAttributesInfo info = GetIntegerPairInfo();
1948 NS_ASSERTION(info.mCount > 0,
1949 "DidChangeIntegerPair on element with no integer pair attribs");
1950 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1952 nsAttrValue newValue;
1953 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
1955 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1956 aProofOfUpdate);
1959 void SVGElement::DidAnimateIntegerPair(uint8_t aAttrEnum) {
1960 nsIFrame* frame = GetPrimaryFrame();
1962 if (frame) {
1963 IntegerPairAttributesInfo info = GetIntegerPairInfo();
1964 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1965 MutationEvent_Binding::SMIL);
1969 SVGElement::BooleanAttributesInfo SVGElement::GetBooleanInfo() {
1970 return BooleanAttributesInfo(nullptr, nullptr, 0);
1973 void SVGElement::DidChangeBoolean(uint8_t aAttrEnum) {
1974 BooleanAttributesInfo info = GetBooleanInfo();
1976 NS_ASSERTION(info.mCount > 0,
1977 "DidChangeBoolean on element with no boolean attribs");
1978 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1980 nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom());
1981 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1982 attrValue, true);
1985 void SVGElement::DidAnimateBoolean(uint8_t aAttrEnum) {
1986 nsIFrame* frame = GetPrimaryFrame();
1988 if (frame) {
1989 BooleanAttributesInfo info = GetBooleanInfo();
1990 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1991 MutationEvent_Binding::SMIL);
1995 SVGElement::EnumAttributesInfo SVGElement::GetEnumInfo() {
1996 return EnumAttributesInfo(nullptr, nullptr, 0);
1999 void SVGElement::DidChangeEnum(uint8_t aAttrEnum) {
2000 EnumAttributesInfo info = GetEnumInfo();
2002 NS_ASSERTION(info.mCount > 0,
2003 "DidChangeEnum on element with no enum attribs");
2004 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2006 nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom(this));
2007 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
2008 attrValue, true);
2011 void SVGElement::DidAnimateEnum(uint8_t aAttrEnum) {
2012 nsIFrame* frame = GetPrimaryFrame();
2014 if (frame) {
2015 EnumAttributesInfo info = GetEnumInfo();
2016 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
2017 MutationEvent_Binding::SMIL);
2021 SVGAnimatedOrient* SVGElement::GetAnimatedOrient() { return nullptr; }
2023 nsAttrValue SVGElement::WillChangeOrient(
2024 const mozAutoDocUpdate& aProofOfUpdate) {
2025 return WillChangeValue(nsGkAtoms::orient, aProofOfUpdate);
2028 void SVGElement::DidChangeOrient(const nsAttrValue& aEmptyOrOldValue,
2029 const mozAutoDocUpdate& aProofOfUpdate) {
2030 SVGAnimatedOrient* orient = GetAnimatedOrient();
2032 NS_ASSERTION(orient, "DidChangeOrient on element with no orient attrib");
2034 nsAttrValue newValue;
2035 newValue.SetTo(*orient, nullptr);
2037 DidChangeValue(nsGkAtoms::orient, aEmptyOrOldValue, newValue, aProofOfUpdate);
2040 void SVGElement::DidAnimateOrient() {
2041 nsIFrame* frame = GetPrimaryFrame();
2043 if (frame) {
2044 frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::orient,
2045 MutationEvent_Binding::SMIL);
2049 SVGAnimatedViewBox* SVGElement::GetAnimatedViewBox() { return nullptr; }
2051 nsAttrValue SVGElement::WillChangeViewBox(
2052 const mozAutoDocUpdate& aProofOfUpdate) {
2053 return WillChangeValue(nsGkAtoms::viewBox, aProofOfUpdate);
2056 void SVGElement::DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue,
2057 const mozAutoDocUpdate& aProofOfUpdate) {
2058 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
2060 NS_ASSERTION(viewBox, "DidChangeViewBox on element with no viewBox attrib");
2062 nsAttrValue newValue;
2063 newValue.SetTo(*viewBox, nullptr);
2065 DidChangeValue(nsGkAtoms::viewBox, aEmptyOrOldValue, newValue,
2066 aProofOfUpdate);
2069 void SVGElement::DidAnimateViewBox() {
2070 nsIFrame* frame = GetPrimaryFrame();
2072 if (frame) {
2073 frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::viewBox,
2074 MutationEvent_Binding::SMIL);
2078 SVGAnimatedPreserveAspectRatio* SVGElement::GetAnimatedPreserveAspectRatio() {
2079 return nullptr;
2082 nsAttrValue SVGElement::WillChangePreserveAspectRatio(
2083 const mozAutoDocUpdate& aProofOfUpdate) {
2084 return WillChangeValue(nsGkAtoms::preserveAspectRatio, aProofOfUpdate);
2087 void SVGElement::DidChangePreserveAspectRatio(
2088 const nsAttrValue& aEmptyOrOldValue,
2089 const mozAutoDocUpdate& aProofOfUpdate) {
2090 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
2091 GetAnimatedPreserveAspectRatio();
2093 NS_ASSERTION(preserveAspectRatio,
2094 "DidChangePreserveAspectRatio on element with no "
2095 "preserveAspectRatio attrib");
2097 nsAttrValue newValue;
2098 newValue.SetTo(*preserveAspectRatio, nullptr);
2100 DidChangeValue(nsGkAtoms::preserveAspectRatio, aEmptyOrOldValue, newValue,
2101 aProofOfUpdate);
2104 void SVGElement::DidAnimatePreserveAspectRatio() {
2105 nsIFrame* frame = GetPrimaryFrame();
2107 if (frame) {
2108 frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio,
2109 MutationEvent_Binding::SMIL);
2113 nsAttrValue SVGElement::WillChangeTransformList(
2114 const mozAutoDocUpdate& aProofOfUpdate) {
2115 return WillChangeValue(GetTransformListAttrName(), aProofOfUpdate);
2118 void SVGElement::DidChangeTransformList(
2119 const nsAttrValue& aEmptyOrOldValue,
2120 const mozAutoDocUpdate& aProofOfUpdate) {
2121 MOZ_ASSERT(GetTransformListAttrName(),
2122 "Changing non-existent transform list?");
2124 // The transform attribute is being set, so we must ensure that the
2125 // SVGAnimatedTransformList is/has been allocated:
2126 nsAttrValue newValue;
2127 newValue.SetTo(GetAnimatedTransformList(DO_ALLOCATE)->GetBaseValue(),
2128 nullptr);
2130 DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue, newValue,
2131 aProofOfUpdate);
2134 void SVGElement::DidAnimateTransformList(int32_t aModType) {
2135 MOZ_ASSERT(GetTransformListAttrName(),
2136 "Animating non-existent transform data?");
2138 nsIFrame* frame = GetPrimaryFrame();
2140 if (frame) {
2141 nsAtom* transformAttr = GetTransformListAttrName();
2142 frame->AttributeChanged(kNameSpaceID_None, transformAttr, aModType);
2143 // When script changes the 'transform' attribute, Element::SetAttrAndNotify
2144 // will call MutationObservers::NotifyAttributeChanged, under which
2145 // SVGTransformableElement::GetAttributeChangeHint will be called and an
2146 // appropriate change event posted to update our frame's overflow rects.
2147 // The SetAttrAndNotify doesn't happen for transform changes caused by
2148 // 'animateTransform' though (and sending out the mutation events that
2149 // MutationObservers::NotifyAttributeChanged dispatches would be
2150 // inappropriate anyway), so we need to post the change event ourself.
2151 nsChangeHint changeHint = GetAttributeChangeHint(transformAttr, aModType);
2152 if (changeHint) {
2153 nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0}, changeHint);
2158 SVGElement::StringAttributesInfo SVGElement::GetStringInfo() {
2159 return StringAttributesInfo(nullptr, nullptr, 0);
2162 void SVGElement::GetStringBaseValue(uint8_t aAttrEnum,
2163 nsAString& aResult) const {
2164 SVGElement::StringAttributesInfo info =
2165 const_cast<SVGElement*>(this)->GetStringInfo();
2167 NS_ASSERTION(info.mCount > 0,
2168 "GetBaseValue on element with no string attribs");
2170 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2172 GetAttr(info.mInfos[aAttrEnum].mNamespaceID, info.mInfos[aAttrEnum].mName,
2173 aResult);
2176 void SVGElement::SetStringBaseValue(uint8_t aAttrEnum,
2177 const nsAString& aValue) {
2178 SVGElement::StringAttributesInfo info = GetStringInfo();
2180 NS_ASSERTION(info.mCount > 0,
2181 "SetBaseValue on element with no string attribs");
2183 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2185 SetAttr(info.mInfos[aAttrEnum].mNamespaceID, info.mInfos[aAttrEnum].mName,
2186 aValue, true);
2189 void SVGElement::DidAnimateString(uint8_t aAttrEnum) {
2190 nsIFrame* frame = GetPrimaryFrame();
2192 if (frame) {
2193 StringAttributesInfo info = GetStringInfo();
2194 frame->AttributeChanged(info.mInfos[aAttrEnum].mNamespaceID,
2195 info.mInfos[aAttrEnum].mName,
2196 MutationEvent_Binding::SMIL);
2200 SVGElement::StringListAttributesInfo SVGElement::GetStringListInfo() {
2201 return StringListAttributesInfo(nullptr, nullptr, 0);
2204 nsAttrValue SVGElement::WillChangeStringList(
2205 bool aIsConditionalProcessingAttribute, uint8_t aAttrEnum,
2206 const mozAutoDocUpdate& aProofOfUpdate) {
2207 nsStaticAtom* name;
2208 if (aIsConditionalProcessingAttribute) {
2209 nsCOMPtr<SVGTests> tests(do_QueryInterface(this));
2210 name = tests->GetAttrName(aAttrEnum);
2211 } else {
2212 name = GetStringListInfo().mInfos[aAttrEnum].mName;
2214 return WillChangeValue(name, aProofOfUpdate);
2217 void SVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute,
2218 uint8_t aAttrEnum,
2219 const nsAttrValue& aEmptyOrOldValue,
2220 const mozAutoDocUpdate& aProofOfUpdate) {
2221 nsStaticAtom* name;
2222 nsAttrValue newValue;
2223 nsCOMPtr<SVGTests> tests;
2225 if (aIsConditionalProcessingAttribute) {
2226 tests = do_QueryObject(this);
2227 name = tests->GetAttrName(aAttrEnum);
2228 tests->GetAttrValue(aAttrEnum, newValue);
2229 } else {
2230 StringListAttributesInfo info = GetStringListInfo();
2232 NS_ASSERTION(info.mCount > 0,
2233 "DidChangeStringList on element with no string list attribs");
2234 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2236 name = info.mInfos[aAttrEnum].mName;
2237 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
2240 DidChangeValue(name, aEmptyOrOldValue, newValue, aProofOfUpdate);
2242 if (aIsConditionalProcessingAttribute) {
2243 tests->MaybeInvalidate();
2247 nsresult SVGElement::ReportAttributeParseFailure(Document* aDocument,
2248 nsAtom* aAttribute,
2249 const nsAString& aValue) {
2250 AutoTArray<nsString, 2> strings;
2251 strings.AppendElement(nsDependentAtomString(aAttribute));
2252 strings.AppendElement(aValue);
2253 return SVGContentUtils::ReportToConsole(aDocument, "AttributeParseWarning",
2254 strings);
2257 UniquePtr<SMILAttr> SVGElement::GetAnimatedAttr(int32_t aNamespaceID,
2258 nsAtom* aName) {
2259 if (aNamespaceID == kNameSpaceID_None) {
2260 // Transforms:
2261 if (GetTransformListAttrName() == aName) {
2262 // The transform attribute is being animated, so we must ensure that the
2263 // SVGAnimatedTransformList is/has been allocated:
2264 return GetAnimatedTransformList(DO_ALLOCATE)->ToSMILAttr(this);
2267 // Motion (fake 'attribute' for animateMotion)
2268 if (aName == nsGkAtoms::mozAnimateMotionDummyAttr) {
2269 return MakeUnique<SVGMotionSMILAttr>(this);
2272 // Lengths:
2273 LengthAttributesInfo info = GetLengthInfo();
2274 for (uint32_t i = 0; i < info.mCount; i++) {
2275 if (aName == info.mInfos[i].mName) {
2276 return info.mValues[i].ToSMILAttr(this);
2280 // Numbers:
2282 NumberAttributesInfo info = GetNumberInfo();
2283 for (uint32_t i = 0; i < info.mCount; i++) {
2284 if (aName == info.mInfos[i].mName) {
2285 return info.mValues[i].ToSMILAttr(this);
2290 // Number Pairs:
2292 NumberPairAttributesInfo info = GetNumberPairInfo();
2293 for (uint32_t i = 0; i < info.mCount; i++) {
2294 if (aName == info.mInfos[i].mName) {
2295 return info.mValues[i].ToSMILAttr(this);
2300 // Integers:
2302 IntegerAttributesInfo info = GetIntegerInfo();
2303 for (uint32_t i = 0; i < info.mCount; i++) {
2304 if (aName == info.mInfos[i].mName) {
2305 return info.mValues[i].ToSMILAttr(this);
2310 // Integer Pairs:
2312 IntegerPairAttributesInfo info = GetIntegerPairInfo();
2313 for (uint32_t i = 0; i < info.mCount; i++) {
2314 if (aName == info.mInfos[i].mName) {
2315 return info.mValues[i].ToSMILAttr(this);
2320 // Enumerations:
2322 EnumAttributesInfo info = GetEnumInfo();
2323 for (uint32_t i = 0; i < info.mCount; i++) {
2324 if (aName == info.mInfos[i].mName) {
2325 return info.mValues[i].ToSMILAttr(this);
2330 // Booleans:
2332 BooleanAttributesInfo info = GetBooleanInfo();
2333 for (uint32_t i = 0; i < info.mCount; i++) {
2334 if (aName == info.mInfos[i].mName) {
2335 return info.mValues[i].ToSMILAttr(this);
2340 // orient:
2341 if (aName == nsGkAtoms::orient) {
2342 SVGAnimatedOrient* orient = GetAnimatedOrient();
2343 return orient ? orient->ToSMILAttr(this) : nullptr;
2346 // viewBox:
2347 if (aName == nsGkAtoms::viewBox) {
2348 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
2349 return viewBox ? viewBox->ToSMILAttr(this) : nullptr;
2352 // preserveAspectRatio:
2353 if (aName == nsGkAtoms::preserveAspectRatio) {
2354 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
2355 GetAnimatedPreserveAspectRatio();
2356 return preserveAspectRatio ? preserveAspectRatio->ToSMILAttr(this)
2357 : nullptr;
2360 // NumberLists:
2362 NumberListAttributesInfo info = GetNumberListInfo();
2363 for (uint32_t i = 0; i < info.mCount; i++) {
2364 if (aName == info.mInfos[i].mName) {
2365 MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes");
2366 return info.mValues[i].ToSMILAttr(this, uint8_t(i));
2371 // LengthLists:
2373 LengthListAttributesInfo info = GetLengthListInfo();
2374 for (uint32_t i = 0; i < info.mCount; i++) {
2375 if (aName == info.mInfos[i].mName) {
2376 MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes");
2377 return info.mValues[i].ToSMILAttr(this, uint8_t(i),
2378 info.mInfos[i].mAxis,
2379 info.mInfos[i].mCouldZeroPadList);
2384 // PointLists:
2386 if (GetPointListAttrName() == aName) {
2387 SVGAnimatedPointList* pointList = GetAnimatedPointList();
2388 if (pointList) {
2389 return pointList->ToSMILAttr(this);
2394 // PathSegLists:
2396 if (GetPathDataAttrName() == aName) {
2397 SVGAnimatedPathSegList* segList = GetAnimPathSegList();
2398 if (segList) {
2399 return segList->ToSMILAttr(this);
2404 if (aName == nsGkAtoms::_class) {
2405 return mClassAttribute.ToSMILAttr(this);
2409 // Strings
2411 StringAttributesInfo info = GetStringInfo();
2412 for (uint32_t i = 0; i < info.mCount; i++) {
2413 if (aNamespaceID == info.mInfos[i].mNamespaceID &&
2414 aName == info.mInfos[i].mName) {
2415 return info.mValues[i].ToSMILAttr(this);
2420 return nullptr;
2423 void SVGElement::AnimationNeedsResample() {
2424 Document* doc = GetComposedDoc();
2425 if (doc && doc->HasAnimationController()) {
2426 doc->GetAnimationController()->SetResampleNeeded();
2430 void SVGElement::FlushAnimations() {
2431 Document* doc = GetComposedDoc();
2432 if (doc && doc->HasAnimationController()) {
2433 doc->GetAnimationController()->FlushResampleRequests();
2437 void SVGElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
2438 size_t* aNodeSize) const {
2439 Element::AddSizeOfExcludingThis(aSizes, aNodeSize);
2441 // These are owned by the element and not referenced from the stylesheets.
2442 // They're referenced from the rule tree, but the rule nodes don't measure
2443 // their style source (since they're non-owning), so unconditionally reporting
2444 // them even though it's a refcounted object is ok.
2445 if (mContentDeclarationBlock) {
2446 aSizes.mLayoutSvgMappedDeclarations +=
2447 mContentDeclarationBlock->SizeofIncludingThis(
2448 aSizes.mState.mMallocSizeOf);
2452 } // namespace mozilla::dom