Bug 1635304 [wpt PR 23392] - Remove erroneous named properties object test, a=testonly
[gecko.git] / dom / svg / SVGElement.cpp
blobb6504776c59476d99e90a30b862881b35f27019c
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/SVGElementBinding.h"
12 #include "mozilla/dom/SVGGeometryElement.h"
13 #include "mozilla/dom/SVGLengthBinding.h"
14 #include "mozilla/dom/SVGSVGElement.h"
15 #include "mozilla/dom/SVGTests.h"
16 #include "mozilla/dom/SVGUnitTypesBinding.h"
18 #include "mozilla/ArrayUtils.h"
19 #include "mozilla/DebugOnly.h"
20 #include "mozilla/DeclarationBlock.h"
21 #include "mozilla/EventListenerManager.h"
22 #include "mozilla/InternalMutationEvent.h"
23 #include "mozilla/PresShell.h"
24 #include "mozilla/RestyleManager.h"
25 #include "mozilla/SMILAnimationController.h"
26 #include "mozilla/StaticPrefs_layout.h"
27 #include "mozilla/SVGContentUtils.h"
28 #include "mozilla/Unused.h"
30 #include "DOMSVGAnimatedEnumeration.h"
31 #include "mozAutoDocUpdate.h"
32 #include "nsAttrValueOrString.h"
33 #include "nsCSSProps.h"
34 #include "nsContentUtils.h"
35 #include "nsDOMCSSAttrDeclaration.h"
36 #include "nsICSSDeclaration.h"
37 #include "nsIContentInlines.h"
38 #include "mozilla/dom/Document.h"
39 #include "nsError.h"
40 #include "nsGkAtoms.h"
41 #include "nsIFrame.h"
42 #include "nsQueryObject.h"
43 #include "nsLayoutUtils.h"
44 #include "SVGAnimatedNumberList.h"
45 #include "SVGAnimatedLengthList.h"
46 #include "SVGAnimatedPointList.h"
47 #include "SVGAnimatedPathSegList.h"
48 #include "SVGAnimatedTransformList.h"
49 #include "SVGAnimatedBoolean.h"
50 #include "SVGAnimatedEnumeration.h"
51 #include "SVGAnimatedInteger.h"
52 #include "SVGAnimatedIntegerPair.h"
53 #include "SVGAnimatedLength.h"
54 #include "SVGAnimatedNumber.h"
55 #include "SVGAnimatedNumberPair.h"
56 #include "SVGAnimatedOrient.h"
57 #include "SVGAnimatedString.h"
58 #include "SVGAnimatedViewBox.h"
59 #include "SVGGeometryProperty.h"
60 #include "SVGMotionSMILAttr.h"
61 #include <stdarg.h>
63 // This is needed to ensure correct handling of calls to the
64 // vararg-list methods in this file:
65 // SVGElement::GetAnimated{Length,Number,Integer}Values
66 // See bug 547964 for details:
67 static_assert(sizeof(void*) == sizeof(nullptr),
68 "nullptr should be the correct size");
70 nsresult NS_NewSVGElement(
71 Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) {
72 RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
73 auto* nim = nodeInfo->NodeInfoManager();
74 RefPtr<mozilla::dom::SVGElement> it =
75 new (nim) mozilla::dom::SVGElement(nodeInfo.forget());
76 nsresult rv = it->Init();
78 if (NS_FAILED(rv)) {
79 return rv;
82 it.forget(aResult);
83 return rv;
86 namespace mozilla {
87 namespace dom {
88 using namespace SVGUnitTypes_Binding;
90 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGElement)
92 SVGEnumMapping SVGElement::sSVGUnitTypesMap[] = {
93 {nsGkAtoms::userSpaceOnUse, SVG_UNIT_TYPE_USERSPACEONUSE},
94 {nsGkAtoms::objectBoundingBox, SVG_UNIT_TYPE_OBJECTBOUNDINGBOX},
95 {nullptr, 0}};
97 SVGElement::SVGElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
98 : SVGElementBase(std::move(aNodeInfo)) {}
100 SVGElement::~SVGElement() {
101 OwnerDoc()->UnscheduleSVGForPresAttrEvaluation(this);
104 JSObject* SVGElement::WrapNode(JSContext* aCx,
105 JS::Handle<JSObject*> aGivenProto) {
106 return SVGElement_Binding::Wrap(aCx, this, aGivenProto);
109 nsresult SVGElement::CopyInnerTo(mozilla::dom::Element* aDest) {
110 nsresult rv = Element::CopyInnerTo(aDest);
111 NS_ENSURE_SUCCESS(rv, rv);
113 // cloning a node must retain its internal nonce slot
114 nsString* nonce = static_cast<nsString*>(GetProperty(nsGkAtoms::nonce));
115 if (nonce) {
116 static_cast<SVGElement*>(aDest)->SetNonce(*nonce);
118 return NS_OK;
121 //----------------------------------------------------------------------
122 // SVGElement methods
124 void SVGElement::DidAnimateClass() {
125 // For Servo, snapshot the element before we change it.
126 PresShell* presShell = OwnerDoc()->GetPresShell();
127 if (presShell) {
128 if (nsPresContext* presContext = presShell->GetPresContext()) {
129 presContext->RestyleManager()->ClassAttributeWillBeChangedBySMIL(this);
133 nsAutoString src;
134 mClassAttribute.GetAnimValue(src, this);
135 if (!mClassAnimAttr) {
136 mClassAnimAttr = MakeUnique<nsAttrValue>();
138 mClassAnimAttr->ParseAtomArray(src);
140 // FIXME(emilio): This re-selector-matches, but we do the snapshot stuff right
141 // above... Is this needed anymore?
142 if (presShell) {
143 presShell->RestyleForAnimation(this, RestyleHint::RESTYLE_SELF);
147 nsresult SVGElement::Init() {
148 // Set up length attributes - can't do this in the constructor
149 // because we can't do a virtual call at that point
151 LengthAttributesInfo lengthInfo = GetLengthInfo();
153 uint32_t i;
154 for (i = 0; i < lengthInfo.mLengthCount; i++) {
155 lengthInfo.Reset(i);
158 NumberAttributesInfo numberInfo = GetNumberInfo();
160 for (i = 0; i < numberInfo.mNumberCount; i++) {
161 numberInfo.Reset(i);
164 NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo();
166 for (i = 0; i < numberPairInfo.mNumberPairCount; i++) {
167 numberPairInfo.Reset(i);
170 IntegerAttributesInfo integerInfo = GetIntegerInfo();
172 for (i = 0; i < integerInfo.mIntegerCount; i++) {
173 integerInfo.Reset(i);
176 IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo();
178 for (i = 0; i < integerPairInfo.mIntegerPairCount; i++) {
179 integerPairInfo.Reset(i);
182 BooleanAttributesInfo booleanInfo = GetBooleanInfo();
184 for (i = 0; i < booleanInfo.mBooleanCount; i++) {
185 booleanInfo.Reset(i);
188 EnumAttributesInfo enumInfo = GetEnumInfo();
190 for (i = 0; i < enumInfo.mEnumCount; i++) {
191 enumInfo.Reset(i);
194 SVGAnimatedOrient* orient = GetAnimatedOrient();
196 if (orient) {
197 orient->Init();
200 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
202 if (viewBox) {
203 viewBox->Init();
206 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
207 GetAnimatedPreserveAspectRatio();
209 if (preserveAspectRatio) {
210 preserveAspectRatio->Init();
213 LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
215 for (i = 0; i < lengthListInfo.mLengthListCount; i++) {
216 lengthListInfo.Reset(i);
219 NumberListAttributesInfo numberListInfo = GetNumberListInfo();
221 for (i = 0; i < numberListInfo.mNumberListCount; i++) {
222 numberListInfo.Reset(i);
225 // No need to reset SVGPointList since the default value is always the same
226 // (an empty list).
228 // No need to reset SVGPathData since the default value is always the same
229 // (an empty list).
231 StringAttributesInfo stringInfo = GetStringInfo();
233 for (i = 0; i < stringInfo.mStringCount; i++) {
234 stringInfo.Reset(i);
237 return NS_OK;
240 //----------------------------------------------------------------------
241 // Implementation
243 //----------------------------------------------------------------------
244 // nsIContent methods
246 nsresult SVGElement::BindToTree(BindContext& aContext, nsINode& aParent) {
247 nsresult rv = SVGElementBase::BindToTree(aContext, aParent);
248 NS_ENSURE_SUCCESS(rv, rv);
250 // Hide any nonce from the DOM, but keep the internal value of the
251 // nonce by copying and resetting the internal nonce value.
252 if (HasFlag(NODE_HAS_NONCE_AND_HEADER_CSP) && IsInComposedDoc() &&
253 OwnerDoc()->GetBrowsingContext()) {
254 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
255 "SVGElement::ResetNonce::Runnable",
256 [self = RefPtr<SVGElement>(this)]() {
257 nsAutoString nonce;
258 self->GetNonce(nonce);
259 self->SetAttr(kNameSpaceID_None, nsGkAtoms::nonce, EmptyString(),
260 true);
261 self->SetNonce(nonce);
262 }));
265 if (!MayHaveStyle()) {
266 return NS_OK;
268 const nsAttrValue* oldVal = mAttrs.GetAttr(nsGkAtoms::style);
270 if (oldVal && oldVal->Type() == nsAttrValue::eCSSDeclaration) {
271 // we need to force a reparse because the baseURI of the document
272 // may have changed, and in particular because we may be clones of
273 // XBL anonymous content now being bound to the document we should
274 // render in and due to the hacky way in which we implement the
275 // interaction of XBL and SVG resources. Once we have a sane
276 // ownerDocument on XBL anonymous content, this can all go away.
277 nsAttrValue attrValue;
278 nsAutoString stringValue;
279 oldVal->ToString(stringValue);
280 // Force in data doc, since we already have a style rule
281 ParseStyleAttribute(stringValue, nullptr, attrValue, true);
282 // Don't bother going through SetInlineStyleDeclaration; we don't
283 // want to fire off mutation events or document notifications anyway
284 bool oldValueSet;
285 rv = mAttrs.SetAndSwapAttr(nsGkAtoms::style, attrValue, &oldValueSet);
286 NS_ENSURE_SUCCESS(rv, rv);
289 return NS_OK;
292 nsresult SVGElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
293 const nsAttrValue* aValue,
294 const nsAttrValue* aOldValue,
295 nsIPrincipal* aSubjectPrincipal,
296 bool aNotify) {
297 // We don't currently use nsMappedAttributes within SVG. If this changes, we
298 // need to be very careful because some nsAttrValues used by SVG point to
299 // member data of SVG elements and if an nsAttrValue outlives the SVG element
300 // whose data it points to (by virtue of being stored in
301 // mAttrs->mMappedAttributes, meaning it's shared between
302 // elements), the pointer will dangle. See bug 724680.
303 MOZ_ASSERT(!mAttrs.HasMappedAttrs(),
304 "Unexpected use of nsMappedAttributes within SVG");
306 // If this is an svg presentation attribute we need to map it into
307 // the content declaration block.
308 // XXX For some reason incremental mapping doesn't work, so for now
309 // just delete the style rule and lazily reconstruct it as needed).
310 if (aNamespaceID == kNameSpaceID_None && IsAttributeMapped(aName)) {
311 mContentDeclarationBlock = nullptr;
312 OwnerDoc()->ScheduleSVGForPresAttrEvaluation(this);
315 if (IsEventAttributeName(aName) && aValue) {
316 MOZ_ASSERT(aValue->Type() == nsAttrValue::eString,
317 "Expected string value for script body");
318 SetEventHandler(GetEventNameForAttr(aName), aValue->GetStringValue());
321 // The nonce will be copied over to an internal slot and cleared from the
322 // Element within BindToTree to avoid CSS Selector nonce exfiltration if
323 // the CSP list contains a header-delivered CSP.
324 if (nsGkAtoms::nonce == aName && kNameSpaceID_None == aNamespaceID) {
325 if (aValue) {
326 SetNonce(aValue->GetStringValue());
327 if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) {
328 SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP);
330 } else {
331 RemoveNonce();
335 return SVGElementBase::AfterSetAttr(aNamespaceID, aName, aValue, aOldValue,
336 aSubjectPrincipal, aNotify);
339 bool SVGElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
340 const nsAString& aValue,
341 nsIPrincipal* aMaybeScriptedPrincipal,
342 nsAttrValue& aResult) {
343 nsresult rv = NS_OK;
344 bool foundMatch = false;
345 bool didSetResult = false;
347 if (aNamespaceID == kNameSpaceID_None) {
348 // Check for SVGAnimatedLength attribute
349 LengthAttributesInfo lengthInfo = GetLengthInfo();
351 uint32_t i;
352 for (i = 0; i < lengthInfo.mLengthCount; i++) {
353 if (aAttribute == lengthInfo.mLengthInfo[i].mName) {
354 rv = lengthInfo.mLengths[i].SetBaseValueString(aValue, this, false);
355 if (NS_FAILED(rv)) {
356 lengthInfo.Reset(i);
357 } else {
358 aResult.SetTo(lengthInfo.mLengths[i], &aValue);
359 didSetResult = true;
361 foundMatch = true;
362 break;
366 if (!foundMatch) {
367 // Check for SVGAnimatedLengthList attribute
368 LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
369 for (i = 0; i < lengthListInfo.mLengthListCount; i++) {
370 if (aAttribute == lengthListInfo.mLengthListInfo[i].mName) {
371 rv = lengthListInfo.mLengthLists[i].SetBaseValueString(aValue);
372 if (NS_FAILED(rv)) {
373 lengthListInfo.Reset(i);
374 } else {
375 aResult.SetTo(lengthListInfo.mLengthLists[i].GetBaseValue(),
376 &aValue);
377 didSetResult = true;
379 foundMatch = true;
380 break;
385 if (!foundMatch) {
386 // Check for SVGAnimatedNumberList attribute
387 NumberListAttributesInfo numberListInfo = GetNumberListInfo();
388 for (i = 0; i < numberListInfo.mNumberListCount; i++) {
389 if (aAttribute == numberListInfo.mNumberListInfo[i].mName) {
390 rv = numberListInfo.mNumberLists[i].SetBaseValueString(aValue);
391 if (NS_FAILED(rv)) {
392 numberListInfo.Reset(i);
393 } else {
394 aResult.SetTo(numberListInfo.mNumberLists[i].GetBaseValue(),
395 &aValue);
396 didSetResult = true;
398 foundMatch = true;
399 break;
404 if (!foundMatch) {
405 // Check for SVGAnimatedPointList attribute
406 if (GetPointListAttrName() == aAttribute) {
407 SVGAnimatedPointList* pointList = GetAnimatedPointList();
408 if (pointList) {
409 pointList->SetBaseValueString(aValue);
410 // The spec says we parse everything up to the failure, so we DON'T
411 // need to check the result of SetBaseValueString or call
412 // pointList->ClearBaseValue() if it fails
413 aResult.SetTo(pointList->GetBaseValue(), &aValue);
414 didSetResult = true;
415 foundMatch = true;
420 if (!foundMatch) {
421 // Check for SVGAnimatedPathSegList attribute
422 if (GetPathDataAttrName() == aAttribute) {
423 SVGAnimatedPathSegList* segList = GetAnimPathSegList();
424 if (segList) {
425 segList->SetBaseValueString(aValue);
426 // The spec says we parse everything up to the failure, so we DON'T
427 // need to check the result of SetBaseValueString or call
428 // segList->ClearBaseValue() if it fails
429 aResult.SetTo(segList->GetBaseValue(), &aValue);
430 didSetResult = true;
431 foundMatch = true;
436 if (!foundMatch) {
437 // Check for SVGAnimatedNumber attribute
438 NumberAttributesInfo numberInfo = GetNumberInfo();
439 for (i = 0; i < numberInfo.mNumberCount; i++) {
440 if (aAttribute == numberInfo.mNumberInfo[i].mName) {
441 rv = numberInfo.mNumbers[i].SetBaseValueString(aValue, this);
442 if (NS_FAILED(rv)) {
443 numberInfo.Reset(i);
444 } else {
445 aResult.SetTo(numberInfo.mNumbers[i].GetBaseValue(), &aValue);
446 didSetResult = true;
448 foundMatch = true;
449 break;
454 if (!foundMatch) {
455 // Check for SVGAnimatedNumberPair attribute
456 NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo();
457 for (i = 0; i < numberPairInfo.mNumberPairCount; i++) {
458 if (aAttribute == numberPairInfo.mNumberPairInfo[i].mName) {
459 rv = numberPairInfo.mNumberPairs[i].SetBaseValueString(aValue, this);
460 if (NS_FAILED(rv)) {
461 numberPairInfo.Reset(i);
462 } else {
463 aResult.SetTo(numberPairInfo.mNumberPairs[i], &aValue);
464 didSetResult = true;
466 foundMatch = true;
467 break;
472 if (!foundMatch) {
473 // Check for SVGAnimatedInteger attribute
474 IntegerAttributesInfo integerInfo = GetIntegerInfo();
475 for (i = 0; i < integerInfo.mIntegerCount; i++) {
476 if (aAttribute == integerInfo.mIntegerInfo[i].mName) {
477 rv = integerInfo.mIntegers[i].SetBaseValueString(aValue, this);
478 if (NS_FAILED(rv)) {
479 integerInfo.Reset(i);
480 } else {
481 aResult.SetTo(integerInfo.mIntegers[i].GetBaseValue(), &aValue);
482 didSetResult = true;
484 foundMatch = true;
485 break;
490 if (!foundMatch) {
491 // Check for SVGAnimatedIntegerPair attribute
492 IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo();
493 for (i = 0; i < integerPairInfo.mIntegerPairCount; i++) {
494 if (aAttribute == integerPairInfo.mIntegerPairInfo[i].mName) {
495 rv =
496 integerPairInfo.mIntegerPairs[i].SetBaseValueString(aValue, this);
497 if (NS_FAILED(rv)) {
498 integerPairInfo.Reset(i);
499 } else {
500 aResult.SetTo(integerPairInfo.mIntegerPairs[i], &aValue);
501 didSetResult = true;
503 foundMatch = true;
504 break;
509 if (!foundMatch) {
510 // Check for SVGAnimatedBoolean attribute
511 BooleanAttributesInfo booleanInfo = GetBooleanInfo();
512 for (i = 0; i < booleanInfo.mBooleanCount; i++) {
513 if (aAttribute == booleanInfo.mBooleanInfo[i].mName) {
514 nsAtom* valAtom = NS_GetStaticAtom(aValue);
515 rv = valAtom
516 ? booleanInfo.mBooleans[i].SetBaseValueAtom(valAtom, this)
517 : NS_ERROR_DOM_SYNTAX_ERR;
518 if (NS_FAILED(rv)) {
519 booleanInfo.Reset(i);
520 } else {
521 aResult.SetTo(valAtom);
522 didSetResult = true;
524 foundMatch = true;
525 break;
530 if (!foundMatch) {
531 // Check for SVGAnimatedEnumeration attribute
532 EnumAttributesInfo enumInfo = GetEnumInfo();
533 for (i = 0; i < enumInfo.mEnumCount; i++) {
534 if (aAttribute == enumInfo.mEnumInfo[i].mName) {
535 RefPtr<nsAtom> valAtom = NS_Atomize(aValue);
536 if (!enumInfo.mEnums[i].SetBaseValueAtom(valAtom, this)) {
537 // Exact error value does not matter; we just need to mark the
538 // parse as failed.
539 rv = NS_ERROR_FAILURE;
540 enumInfo.SetUnknownValue(i);
541 } else {
542 aResult.SetTo(valAtom);
543 didSetResult = true;
545 foundMatch = true;
546 break;
551 if (!foundMatch) {
552 // Check for conditional processing attributes
553 nsCOMPtr<SVGTests> tests = do_QueryObject(this);
554 if (tests && tests->ParseConditionalProcessingAttribute(
555 aAttribute, aValue, aResult)) {
556 foundMatch = true;
560 if (!foundMatch) {
561 // Check for StringList attribute
562 StringListAttributesInfo stringListInfo = GetStringListInfo();
563 for (i = 0; i < stringListInfo.mStringListCount; i++) {
564 if (aAttribute == stringListInfo.mStringListInfo[i].mName) {
565 rv = stringListInfo.mStringLists[i].SetValue(aValue);
566 if (NS_FAILED(rv)) {
567 stringListInfo.Reset(i);
568 } else {
569 aResult.SetTo(stringListInfo.mStringLists[i], &aValue);
570 didSetResult = true;
572 foundMatch = true;
573 break;
578 if (!foundMatch) {
579 // Check for orient attribute
580 if (aAttribute == nsGkAtoms::orient) {
581 SVGAnimatedOrient* orient = GetAnimatedOrient();
582 if (orient) {
583 rv = orient->SetBaseValueString(aValue, this, false);
584 if (NS_FAILED(rv)) {
585 orient->Init();
586 } else {
587 aResult.SetTo(*orient, &aValue);
588 didSetResult = true;
590 foundMatch = true;
592 // Check for viewBox attribute
593 } else if (aAttribute == nsGkAtoms::viewBox) {
594 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
595 if (viewBox) {
596 rv = viewBox->SetBaseValueString(aValue, this, false);
597 if (NS_FAILED(rv)) {
598 viewBox->Init();
599 } else {
600 aResult.SetTo(*viewBox, &aValue);
601 didSetResult = true;
603 foundMatch = true;
605 // Check for preserveAspectRatio attribute
606 } else if (aAttribute == nsGkAtoms::preserveAspectRatio) {
607 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
608 GetAnimatedPreserveAspectRatio();
609 if (preserveAspectRatio) {
610 rv = preserveAspectRatio->SetBaseValueString(aValue, this, false);
611 if (NS_FAILED(rv)) {
612 preserveAspectRatio->Init();
613 } else {
614 aResult.SetTo(*preserveAspectRatio, &aValue);
615 didSetResult = true;
617 foundMatch = true;
619 // Check for SVGAnimatedTransformList attribute
620 } else if (GetTransformListAttrName() == aAttribute) {
621 // The transform attribute is being set, so we must ensure that the
622 // SVGAnimatedTransformList is/has been allocated:
623 SVGAnimatedTransformList* transformList =
624 GetAnimatedTransformList(DO_ALLOCATE);
625 rv = transformList->SetBaseValueString(aValue, this);
626 if (NS_FAILED(rv)) {
627 transformList->ClearBaseValue();
628 } else {
629 aResult.SetTo(transformList->GetBaseValue(), &aValue);
630 didSetResult = true;
632 foundMatch = true;
633 } else if (aAttribute == nsGkAtoms::tabindex) {
634 didSetResult = aResult.ParseIntValue(aValue);
635 foundMatch = true;
639 if (aAttribute == nsGkAtoms::_class) {
640 mClassAttribute.SetBaseValue(aValue, this, false);
641 aResult.ParseAtomArray(aValue);
642 return true;
645 if (aAttribute == nsGkAtoms::rel) {
646 aResult.ParseAtomArray(aValue);
647 return true;
651 if (!foundMatch) {
652 // Check for SVGAnimatedString attribute
653 StringAttributesInfo stringInfo = GetStringInfo();
654 for (uint32_t i = 0; i < stringInfo.mStringCount; i++) {
655 if (aNamespaceID == stringInfo.mStringInfo[i].mNamespaceID &&
656 aAttribute == stringInfo.mStringInfo[i].mName) {
657 stringInfo.mStrings[i].SetBaseValue(aValue, this, false);
658 foundMatch = true;
659 break;
664 if (foundMatch) {
665 if (NS_FAILED(rv)) {
666 ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue);
667 return false;
669 if (!didSetResult) {
670 aResult.SetTo(aValue);
672 return true;
675 return SVGElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
676 aMaybeScriptedPrincipal, aResult);
679 void SVGElement::UnsetAttrInternal(int32_t aNamespaceID, nsAtom* aName,
680 bool aNotify) {
681 // XXXbz there's a bunch of redundancy here with AfterSetAttr.
682 // Maybe consolidate?
684 if (aNamespaceID == kNameSpaceID_None) {
685 // If this is an svg presentation attribute, remove declaration block to
686 // force an update
687 if (IsAttributeMapped(aName)) {
688 mContentDeclarationBlock = nullptr;
691 if (IsEventAttributeName(aName)) {
692 EventListenerManager* manager = GetExistingListenerManager();
693 if (manager) {
694 nsAtom* eventName = GetEventNameForAttr(aName);
695 manager->RemoveEventHandler(eventName);
697 return;
700 // Check if this is a length attribute going away
701 LengthAttributesInfo lenInfo = GetLengthInfo();
703 for (uint32_t i = 0; i < lenInfo.mLengthCount; i++) {
704 if (aName == lenInfo.mLengthInfo[i].mName) {
705 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
706 lenInfo.Reset(i);
707 return;
711 // Check if this is a length list attribute going away
712 LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
714 for (uint32_t i = 0; i < lengthListInfo.mLengthListCount; i++) {
715 if (aName == lengthListInfo.mLengthListInfo[i].mName) {
716 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
717 lengthListInfo.Reset(i);
718 return;
722 // Check if this is a number list attribute going away
723 NumberListAttributesInfo numberListInfo = GetNumberListInfo();
725 for (uint32_t i = 0; i < numberListInfo.mNumberListCount; i++) {
726 if (aName == numberListInfo.mNumberListInfo[i].mName) {
727 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
728 numberListInfo.Reset(i);
729 return;
733 // Check if this is a point list attribute going away
734 if (GetPointListAttrName() == aName) {
735 SVGAnimatedPointList* pointList = GetAnimatedPointList();
736 if (pointList) {
737 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
738 pointList->ClearBaseValue();
739 return;
743 // Check if this is a path segment list attribute going away
744 if (GetPathDataAttrName() == aName) {
745 SVGAnimatedPathSegList* segList = GetAnimPathSegList();
746 if (segList) {
747 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
748 segList->ClearBaseValue();
749 return;
753 // Check if this is a number attribute going away
754 NumberAttributesInfo numInfo = GetNumberInfo();
756 for (uint32_t i = 0; i < numInfo.mNumberCount; i++) {
757 if (aName == numInfo.mNumberInfo[i].mName) {
758 numInfo.Reset(i);
759 return;
763 // Check if this is a number pair attribute going away
764 NumberPairAttributesInfo numPairInfo = GetNumberPairInfo();
766 for (uint32_t i = 0; i < numPairInfo.mNumberPairCount; i++) {
767 if (aName == numPairInfo.mNumberPairInfo[i].mName) {
768 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
769 numPairInfo.Reset(i);
770 return;
774 // Check if this is an integer attribute going away
775 IntegerAttributesInfo intInfo = GetIntegerInfo();
777 for (uint32_t i = 0; i < intInfo.mIntegerCount; i++) {
778 if (aName == intInfo.mIntegerInfo[i].mName) {
779 intInfo.Reset(i);
780 return;
784 // Check if this is an integer pair attribute going away
785 IntegerPairAttributesInfo intPairInfo = GetIntegerPairInfo();
787 for (uint32_t i = 0; i < intPairInfo.mIntegerPairCount; i++) {
788 if (aName == intPairInfo.mIntegerPairInfo[i].mName) {
789 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
790 intPairInfo.Reset(i);
791 return;
795 // Check if this is a boolean attribute going away
796 BooleanAttributesInfo boolInfo = GetBooleanInfo();
798 for (uint32_t i = 0; i < boolInfo.mBooleanCount; i++) {
799 if (aName == boolInfo.mBooleanInfo[i].mName) {
800 boolInfo.Reset(i);
801 return;
805 // Check if this is an enum attribute going away
806 EnumAttributesInfo enumInfo = GetEnumInfo();
808 for (uint32_t i = 0; i < enumInfo.mEnumCount; i++) {
809 if (aName == enumInfo.mEnumInfo[i].mName) {
810 enumInfo.Reset(i);
811 return;
815 // Check if this is an orient attribute going away
816 if (aName == nsGkAtoms::orient) {
817 SVGAnimatedOrient* orient = GetAnimatedOrient();
818 if (orient) {
819 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
820 orient->Init();
821 return;
825 // Check if this is a viewBox attribute going away
826 if (aName == nsGkAtoms::viewBox) {
827 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
828 if (viewBox) {
829 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
830 viewBox->Init();
831 return;
835 // Check if this is a preserveAspectRatio attribute going away
836 if (aName == nsGkAtoms::preserveAspectRatio) {
837 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
838 GetAnimatedPreserveAspectRatio();
839 if (preserveAspectRatio) {
840 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
841 preserveAspectRatio->Init();
842 return;
846 // Check if this is a transform list attribute going away
847 if (GetTransformListAttrName() == aName) {
848 SVGAnimatedTransformList* transformList = GetAnimatedTransformList();
849 if (transformList) {
850 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
851 transformList->ClearBaseValue();
852 return;
856 // Check for conditional processing attributes
857 nsCOMPtr<SVGTests> tests = do_QueryObject(this);
858 if (tests && tests->IsConditionalProcessingAttribute(aName)) {
859 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
860 tests->UnsetAttr(aName);
861 return;
864 // Check if this is a string list attribute going away
865 StringListAttributesInfo stringListInfo = GetStringListInfo();
867 for (uint32_t i = 0; i < stringListInfo.mStringListCount; i++) {
868 if (aName == stringListInfo.mStringListInfo[i].mName) {
869 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
870 stringListInfo.Reset(i);
871 return;
875 if (aName == nsGkAtoms::_class) {
876 mClassAttribute.Init();
877 return;
881 // Check if this is a string attribute going away
882 StringAttributesInfo stringInfo = GetStringInfo();
884 for (uint32_t i = 0; i < stringInfo.mStringCount; i++) {
885 if (aNamespaceID == stringInfo.mStringInfo[i].mNamespaceID &&
886 aName == stringInfo.mStringInfo[i].mName) {
887 stringInfo.Reset(i);
888 return;
893 nsresult SVGElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
894 const nsAttrValueOrString* aValue,
895 bool aNotify) {
896 if (!aValue) {
897 UnsetAttrInternal(aNamespaceID, aName, aNotify);
899 return SVGElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify);
902 nsChangeHint SVGElement::GetAttributeChangeHint(const nsAtom* aAttribute,
903 int32_t aModType) const {
904 nsChangeHint retval =
905 SVGElementBase::GetAttributeChangeHint(aAttribute, aModType);
907 nsCOMPtr<SVGTests> tests = do_QueryObject(const_cast<SVGElement*>(this));
908 if (tests && tests->IsConditionalProcessingAttribute(aAttribute)) {
909 // It would be nice to only reconstruct the frame if the value returned by
910 // SVGTests::PassesConditionalProcessingTests has changed, but we don't
911 // know that
912 retval |= nsChangeHint_ReconstructFrame;
914 return retval;
917 bool SVGElement::IsNodeOfType(uint32_t aFlags) const { return false; }
919 void SVGElement::NodeInfoChanged(Document* aOldDoc) {
920 SVGElementBase::NodeInfoChanged(aOldDoc);
921 aOldDoc->UnscheduleSVGForPresAttrEvaluation(this);
922 mContentDeclarationBlock = nullptr;
923 OwnerDoc()->ScheduleSVGForPresAttrEvaluation(this);
926 NS_IMETHODIMP_(bool)
927 SVGElement::IsAttributeMapped(const nsAtom* name) const {
928 if (name == nsGkAtoms::lang) {
929 return true;
931 return SVGElementBase::IsAttributeMapped(name);
934 // PresentationAttributes-FillStroke
935 /* static */
936 const Element::MappedAttributeEntry SVGElement::sFillStrokeMap[] = {
937 {nsGkAtoms::fill},
938 {nsGkAtoms::fill_opacity},
939 {nsGkAtoms::fill_rule},
940 {nsGkAtoms::paint_order},
941 {nsGkAtoms::stroke},
942 {nsGkAtoms::stroke_dasharray},
943 {nsGkAtoms::stroke_dashoffset},
944 {nsGkAtoms::stroke_linecap},
945 {nsGkAtoms::stroke_linejoin},
946 {nsGkAtoms::stroke_miterlimit},
947 {nsGkAtoms::stroke_opacity},
948 {nsGkAtoms::stroke_width},
949 {nsGkAtoms::vector_effect},
950 {nullptr}};
952 // PresentationAttributes-Graphics
953 /* static */
954 const Element::MappedAttributeEntry SVGElement::sGraphicsMap[] = {
955 {nsGkAtoms::clip_path},
956 {nsGkAtoms::clip_rule},
957 {nsGkAtoms::colorInterpolation},
958 {nsGkAtoms::cursor},
959 {nsGkAtoms::display},
960 {nsGkAtoms::filter},
961 {nsGkAtoms::image_rendering},
962 {nsGkAtoms::mask},
963 {nsGkAtoms::opacity},
964 {nsGkAtoms::pointer_events},
965 {nsGkAtoms::shape_rendering},
966 {nsGkAtoms::text_rendering},
967 {nsGkAtoms::transform_origin},
968 {nsGkAtoms::visibility},
969 {nullptr}};
971 // PresentationAttributes-TextContentElements
972 /* static */
973 const Element::MappedAttributeEntry SVGElement::sTextContentElementsMap[] = {
974 // Properties that we don't support are commented out.
975 // { nsGkAtoms::alignment_baseline },
976 // { nsGkAtoms::baseline_shift },
977 {nsGkAtoms::direction},
978 {nsGkAtoms::dominant_baseline},
979 {nsGkAtoms::letter_spacing},
980 {nsGkAtoms::text_anchor},
981 {nsGkAtoms::text_decoration},
982 {nsGkAtoms::unicode_bidi},
983 {nsGkAtoms::word_spacing},
984 {nsGkAtoms::writing_mode},
985 {nullptr}};
987 // PresentationAttributes-FontSpecification
988 /* static */
989 const Element::MappedAttributeEntry SVGElement::sFontSpecificationMap[] = {
990 {nsGkAtoms::font_family}, {nsGkAtoms::font_size},
991 {nsGkAtoms::font_size_adjust}, {nsGkAtoms::font_stretch},
992 {nsGkAtoms::font_style}, {nsGkAtoms::font_variant},
993 {nsGkAtoms::fontWeight}, {nullptr}};
995 // PresentationAttributes-GradientStop
996 /* static */
997 const Element::MappedAttributeEntry SVGElement::sGradientStopMap[] = {
998 {nsGkAtoms::stop_color}, {nsGkAtoms::stop_opacity}, {nullptr}};
1000 // PresentationAttributes-Viewports
1001 /* static */
1002 const Element::MappedAttributeEntry SVGElement::sViewportsMap[] = {
1003 {nsGkAtoms::overflow}, {nsGkAtoms::clip}, {nullptr}};
1005 // PresentationAttributes-Makers
1006 /* static */
1007 const Element::MappedAttributeEntry SVGElement::sMarkersMap[] = {
1008 {nsGkAtoms::marker_end},
1009 {nsGkAtoms::marker_mid},
1010 {nsGkAtoms::marker_start},
1011 {nullptr}};
1013 // PresentationAttributes-Color
1014 /* static */
1015 const Element::MappedAttributeEntry SVGElement::sColorMap[] = {
1016 {nsGkAtoms::color}, {nullptr}};
1018 // PresentationAttributes-Filters
1019 /* static */
1020 const Element::MappedAttributeEntry SVGElement::sFiltersMap[] = {
1021 {nsGkAtoms::colorInterpolationFilters}, {nullptr}};
1023 // PresentationAttributes-feFlood
1024 /* static */
1025 const Element::MappedAttributeEntry SVGElement::sFEFloodMap[] = {
1026 {nsGkAtoms::flood_color}, {nsGkAtoms::flood_opacity}, {nullptr}};
1028 // PresentationAttributes-LightingEffects
1029 /* static */
1030 const Element::MappedAttributeEntry SVGElement::sLightingEffectsMap[] = {
1031 {nsGkAtoms::lighting_color}, {nullptr}};
1033 // PresentationAttributes-mask
1034 /* static */
1035 const Element::MappedAttributeEntry SVGElement::sMaskMap[] = {
1036 {nsGkAtoms::mask_type}, {nullptr}};
1038 //----------------------------------------------------------------------
1039 // Element methods
1041 // forwarded to Element implementations
1043 //----------------------------------------------------------------------
1045 SVGSVGElement* SVGElement::GetOwnerSVGElement() {
1046 nsIContent* ancestor = GetFlattenedTreeParent();
1048 while (ancestor && ancestor->IsSVGElement()) {
1049 if (ancestor->IsSVGElement(nsGkAtoms::foreignObject)) {
1050 return nullptr;
1052 if (ancestor->IsSVGElement(nsGkAtoms::svg)) {
1053 return static_cast<SVGSVGElement*>(ancestor);
1055 ancestor = ancestor->GetFlattenedTreeParent();
1058 // we don't have an ancestor <svg> element...
1059 return nullptr;
1062 SVGElement* SVGElement::GetViewportElement() {
1063 return SVGContentUtils::GetNearestViewportElement(this);
1066 already_AddRefed<DOMSVGAnimatedString> SVGElement::ClassName() {
1067 return mClassAttribute.ToDOMAnimatedString(this);
1070 /* static */
1071 bool SVGElement::UpdateDeclarationBlockFromLength(
1072 DeclarationBlock& aBlock, nsCSSPropertyID aPropId,
1073 const SVGAnimatedLength& aLength, ValToUse aValToUse) {
1074 aBlock.AssertMutable();
1076 float value;
1077 if (aValToUse == ValToUse::Anim) {
1078 value = aLength.GetAnimValInSpecifiedUnits();
1079 } else {
1080 MOZ_ASSERT(aValToUse == ValToUse::Base);
1081 value = aLength.GetBaseValInSpecifiedUnits();
1084 // SVG parser doesn't check non-negativity of some parsed value,
1085 // we should not pass those to CSS side.
1086 if (value < 0 &&
1087 SVGGeometryProperty::IsNonNegativeGeometryProperty(aPropId)) {
1088 return false;
1091 nsCSSUnit cssUnit = SVGGeometryProperty::SpecifiedUnitTypeToCSSUnit(
1092 aLength.GetSpecifiedUnitType());
1094 if (cssUnit == eCSSUnit_Percent) {
1095 Servo_DeclarationBlock_SetPercentValue(aBlock.Raw(), aPropId,
1096 value / 100.f);
1097 } else {
1098 Servo_DeclarationBlock_SetLengthValue(aBlock.Raw(), aPropId, value,
1099 cssUnit);
1102 return true;
1105 //------------------------------------------------------------------------
1106 // Helper class: MappedAttrParser, for parsing values of mapped attributes
1108 namespace {
1110 class MOZ_STACK_CLASS MappedAttrParser {
1111 public:
1112 MappedAttrParser(css::Loader* aLoader, nsIURI* aBaseURI,
1113 SVGElement* aElement);
1114 ~MappedAttrParser();
1116 // Parses a mapped attribute value.
1117 void ParseMappedAttrValue(nsAtom* aMappedAttrName,
1118 const nsAString& aMappedAttrValue);
1120 void TellStyleAlreadyParsedResult(nsAtom const* aAtom,
1121 SVGAnimatedLength const& aLength);
1123 // If we've parsed any values for mapped attributes, this method returns the
1124 // already_AddRefed css::Declaration that incorporates the parsed
1125 // values. Otherwise, this method returns null.
1126 already_AddRefed<DeclarationBlock> GetDeclarationBlock();
1128 private:
1129 // MEMBER DATA
1130 // -----------
1131 css::Loader* mLoader;
1133 nsCOMPtr<nsIURI> mBaseURI;
1135 // Declaration for storing parsed values (lazily initialized)
1136 RefPtr<DeclarationBlock> mDecl;
1138 // For reporting use counters
1139 SVGElement* mElement;
1142 MappedAttrParser::MappedAttrParser(css::Loader* aLoader, nsIURI* aBaseURI,
1143 SVGElement* aElement)
1144 : mLoader(aLoader), mBaseURI(aBaseURI), mElement(aElement) {}
1146 MappedAttrParser::~MappedAttrParser() {
1147 MOZ_ASSERT(!mDecl,
1148 "If mDecl was initialized, it should have been returned via "
1149 "GetDeclarationBlock (and had its pointer cleared)");
1152 void MappedAttrParser::ParseMappedAttrValue(nsAtom* aMappedAttrName,
1153 const nsAString& aMappedAttrValue) {
1154 if (!mDecl) {
1155 mDecl = new DeclarationBlock();
1158 // Get the nsCSSPropertyID ID for our mapped attribute.
1159 nsCSSPropertyID propertyID =
1160 nsCSSProps::LookupProperty(nsAtomCString(aMappedAttrName));
1161 if (propertyID != eCSSProperty_UNKNOWN) {
1162 bool changed = false; // outparam for ParseProperty.
1163 NS_ConvertUTF16toUTF8 value(aMappedAttrValue);
1165 // FIXME (bug 1343964): Figure out a better solution for sending the base
1166 // uri to servo
1167 nsCOMPtr<nsIReferrerInfo> referrerInfo =
1168 ReferrerInfo::CreateForSVGResources(mElement->OwnerDoc());
1170 RefPtr<URLExtraData> data =
1171 new URLExtraData(mBaseURI, referrerInfo, mElement->NodePrincipal());
1172 changed = Servo_DeclarationBlock_SetPropertyById(
1173 mDecl->Raw(), propertyID, &value, false, data,
1174 ParsingMode::AllowUnitlessLength,
1175 mElement->OwnerDoc()->GetCompatibilityMode(), mLoader, {});
1177 // TODO(emilio): If we want to record these from CSSOM more generally, we
1178 // can pass the document use counters down the FFI call. For now manually
1179 // count them.
1180 if (changed && StaticPrefs::layout_css_use_counters_enabled()) {
1181 UseCounter useCounter = nsCSSProps::UseCounterFor(propertyID);
1182 MOZ_ASSERT(useCounter != eUseCounter_UNKNOWN);
1183 mElement->OwnerDoc()->SetUseCounter(useCounter);
1185 return;
1187 MOZ_ASSERT(aMappedAttrName == nsGkAtoms::lang,
1188 "Only 'lang' should be unrecognized!");
1189 // CSS parser doesn't know about 'lang', so we need to handle it specially.
1190 if (aMappedAttrName == nsGkAtoms::lang) {
1191 propertyID = eCSSProperty__x_lang;
1192 RefPtr<nsAtom> atom = NS_Atomize(aMappedAttrValue);
1193 Servo_DeclarationBlock_SetIdentStringValue(mDecl->Raw(), propertyID, atom);
1197 void MappedAttrParser::TellStyleAlreadyParsedResult(
1198 nsAtom const* aAtom, SVGAnimatedLength const& aLength) {
1199 if (!mDecl) {
1200 mDecl = new DeclarationBlock();
1202 nsCSSPropertyID propertyID = nsCSSProps::LookupProperty(nsAtomCString(aAtom));
1203 SVGElement::UpdateDeclarationBlockFromLength(*mDecl, propertyID, aLength,
1204 SVGElement::ValToUse::Base);
1207 already_AddRefed<DeclarationBlock> MappedAttrParser::GetDeclarationBlock() {
1208 return mDecl.forget();
1211 } // namespace
1213 //----------------------------------------------------------------------
1214 // Implementation Helpers:
1216 void SVGElement::UpdateContentDeclarationBlock() {
1217 NS_ASSERTION(!mContentDeclarationBlock,
1218 "we already have a content declaration block");
1220 uint32_t attrCount = mAttrs.AttrCount();
1221 if (!attrCount) {
1222 // nothing to do
1223 return;
1226 Document* doc = OwnerDoc();
1227 MappedAttrParser mappedAttrParser(doc->CSSLoader(), GetBaseURI(), this);
1229 bool lengthAffectsStyle =
1230 SVGGeometryProperty::ElementMapsLengthsToStyle(this);
1232 for (uint32_t i = 0; i < attrCount; ++i) {
1233 const nsAttrName* attrName = mAttrs.AttrNameAt(i);
1234 if (!attrName->IsAtom() || !IsAttributeMapped(attrName->Atom())) continue;
1236 if (attrName->NamespaceID() != kNameSpaceID_None &&
1237 !attrName->Equals(nsGkAtoms::lang, kNameSpaceID_XML)) {
1238 continue;
1241 if (attrName->Equals(nsGkAtoms::lang, kNameSpaceID_None) &&
1242 HasAttr(kNameSpaceID_XML, nsGkAtoms::lang)) {
1243 continue; // xml:lang has precedence
1246 if (lengthAffectsStyle) {
1247 auto const* length = GetAnimatedLength(attrName->Atom());
1249 if (length && length->HasBaseVal()) {
1250 // This is an element with geometry property set via SVG attribute,
1251 // and the attribute is already successfully parsed. We want to go
1252 // through the optimized path to tell the style system the result
1253 // directly, rather than let it parse the same thing again.
1254 mappedAttrParser.TellStyleAlreadyParsedResult(attrName->Atom(),
1255 *length);
1256 continue;
1260 nsAutoString value;
1261 mAttrs.AttrAt(i)->ToString(value);
1262 mappedAttrParser.ParseMappedAttrValue(attrName->Atom(), value);
1264 mContentDeclarationBlock = mappedAttrParser.GetDeclarationBlock();
1267 const DeclarationBlock* SVGElement::GetContentDeclarationBlock() const {
1268 return mContentDeclarationBlock;
1272 * Helper methods for the type-specific WillChangeXXX methods.
1274 * This method sends out appropriate pre-change notifications so that selector
1275 * restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop
1276 * matching) work, and it returns an nsAttrValue that _may_ contain the
1277 * attribute's pre-change value.
1279 * The nsAttrValue returned by this method depends on whether there are
1280 * mutation event listeners listening for changes to this element's attributes.
1281 * If not, then the object returned is empty. If there are, then the
1282 * nsAttrValue returned contains a serialized copy of the attribute's value
1283 * prior to the change, and this object should be passed to the corresponding
1284 * DidChangeXXX method call (assuming a WillChangeXXX call is required for the
1285 * SVG type - see comment below). This is necessary so that the 'prevValue'
1286 * property of the mutation event that is dispatched will correctly contain the
1287 * old value.
1289 * The reason we need to serialize the old value if there are mutation
1290 * event listeners is because the underlying nsAttrValue for the attribute
1291 * points directly to a parsed representation of the attribute (e.g. an
1292 * SVGAnimatedLengthList*) that is a member of the SVG element. That object
1293 * will have changed by the time DidChangeXXX has been called, so without the
1294 * serialization of the old attribute value that we provide, DidChangeXXX
1295 * would have no way to get the old value to pass to SetAttrAndNotify.
1297 * We only return the old value when there are mutation event listeners because
1298 * it's not needed otherwise, and because it's expensive to serialize the old
1299 * value. This is especially true for list type attributes, which may be built
1300 * up via the SVG DOM resulting in a large number of Will/DidModifyXXX calls
1301 * before the script finally finishes setting the attribute.
1303 * Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check
1304 * and filter out redundant changes. Before calling WillChangeXXX, the caller
1305 * should check whether the new and old values are actually the same, and skip
1306 * calling Will/DidChangeXXX if they are.
1308 * Also note that not all SVG types use this scheme. For types that can be
1309 * represented by an nsAttrValue without pointing back to an SVG object (e.g.
1310 * enums, booleans, integers) we can simply use SetParsedAttr which will do all
1311 * of the above for us. For such types there is no matching WillChangeXXX
1312 * method, only DidChangeXXX which calls SetParsedAttr.
1314 nsAttrValue SVGElement::WillChangeValue(
1315 nsAtom* aName, const mozAutoDocUpdate& aProofOfUpdate) {
1316 // We need an empty attr value:
1317 // a) to pass to BeforeSetAttr when GetParsedAttr returns nullptr
1318 // b) to store the old value in the case we have mutation listeners
1320 // We can use the same value for both purposes, because if GetParsedAttr
1321 // returns non-null its return value is what will get passed to BeforeSetAttr,
1322 // not matter what our mutation listener situation is.
1324 // Also, we should be careful to always return this value to benefit from
1325 // return value optimization.
1326 nsAttrValue emptyOrOldAttrValue;
1327 const nsAttrValue* attrValue = GetParsedAttr(aName);
1329 // We only need to set the old value if we have listeners since otherwise it
1330 // isn't used.
1331 if (attrValue && nsContentUtils::HasMutationListeners(
1332 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) {
1333 emptyOrOldAttrValue.SetToSerialized(*attrValue);
1336 uint8_t modType =
1337 attrValue ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
1338 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
1339 MutationObservers::NotifyAttributeWillChange(this, kNameSpaceID_None, aName,
1340 modType);
1342 // This is not strictly correct--the attribute value parameter for
1343 // BeforeSetAttr should reflect the value that *will* be set but that implies
1344 // allocating, e.g. an extra SVGAnimatedLength, and isn't necessary at the
1345 // moment since no SVG elements overload BeforeSetAttr. For now we just pass
1346 // the current value.
1347 nsAttrValueOrString attrStringOrValue(attrValue ? *attrValue
1348 : emptyOrOldAttrValue);
1349 DebugOnly<nsresult> rv = BeforeSetAttr(
1350 kNameSpaceID_None, aName, &attrStringOrValue, kNotifyDocumentObservers);
1351 // SVG elements aren't expected to overload BeforeSetAttr in such a way that
1352 // it may fail. So long as this is the case we don't need to check and pass on
1353 // the return value which simplifies the calling code significantly.
1354 MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected failure from BeforeSetAttr");
1356 return emptyOrOldAttrValue;
1360 * Helper methods for the type-specific DidChangeXXX methods.
1362 * aEmptyOrOldValue will normally be the object returned from the corresponding
1363 * WillChangeXXX call. This is because:
1364 * a) WillChangeXXX will ensure the object is set when we have mutation
1365 * listeners, and
1366 * b) WillChangeXXX will ensure the object represents a serialized version of
1367 * the old attribute value so that the value doesn't change when the
1368 * underlying SVG type is updated.
1370 * aNewValue is replaced with the old value.
1372 void SVGElement::DidChangeValue(nsAtom* aName,
1373 const nsAttrValue& aEmptyOrOldValue,
1374 nsAttrValue& aNewValue,
1375 const mozAutoDocUpdate& aProofOfUpdate) {
1376 bool hasListeners = nsContentUtils::HasMutationListeners(
1377 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this);
1378 uint8_t modType =
1379 HasAttr(kNameSpaceID_None, aName)
1380 ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
1381 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
1383 // XXX Really, the fourth argument to SetAttrAndNotify should be null if
1384 // aEmptyOrOldValue does not represent the actual previous value of the
1385 // attribute, but currently SVG elements do not even use the old attribute
1386 // value in |AfterSetAttr|, so this should be ok.
1387 SetAttrAndNotify(kNameSpaceID_None, aName, nullptr, &aEmptyOrOldValue,
1388 aNewValue, nullptr, modType, hasListeners,
1389 kNotifyDocumentObservers, kCallAfterSetAttr,
1390 GetComposedDoc(), aProofOfUpdate);
1393 void SVGElement::MaybeSerializeAttrBeforeRemoval(nsAtom* aName, bool aNotify) {
1394 if (!aNotify || !nsContentUtils::HasMutationListeners(
1395 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) {
1396 return;
1399 const nsAttrValue* attrValue = mAttrs.GetAttr(aName);
1400 if (!attrValue) return;
1402 nsAutoString serializedValue;
1403 attrValue->ToString(serializedValue);
1404 nsAttrValue oldAttrValue(serializedValue);
1405 bool oldValueSet;
1406 mAttrs.SetAndSwapAttr(aName, oldAttrValue, &oldValueSet);
1409 /* static */
1410 nsAtom* SVGElement::GetEventNameForAttr(nsAtom* aAttr) {
1411 if (aAttr == nsGkAtoms::onload) return nsGkAtoms::onSVGLoad;
1412 if (aAttr == nsGkAtoms::onunload) return nsGkAtoms::onSVGUnload;
1413 if (aAttr == nsGkAtoms::onresize) return nsGkAtoms::onSVGResize;
1414 if (aAttr == nsGkAtoms::onscroll) return nsGkAtoms::onSVGScroll;
1415 if (aAttr == nsGkAtoms::onzoom) return nsGkAtoms::onSVGZoom;
1416 if (aAttr == nsGkAtoms::onbegin) return nsGkAtoms::onbeginEvent;
1417 if (aAttr == nsGkAtoms::onrepeat) return nsGkAtoms::onrepeatEvent;
1418 if (aAttr == nsGkAtoms::onend) return nsGkAtoms::onendEvent;
1420 return SVGElementBase::GetEventNameForAttr(aAttr);
1423 SVGViewportElement* SVGElement::GetCtx() const {
1424 return SVGContentUtils::GetNearestViewportElement(this);
1427 /* virtual */
1428 gfxMatrix SVGElement::PrependLocalTransformsTo(const gfxMatrix& aMatrix,
1429 SVGTransformTypes aWhich) const {
1430 return aMatrix;
1433 SVGElement::LengthAttributesInfo SVGElement::GetLengthInfo() {
1434 return LengthAttributesInfo(nullptr, nullptr, 0);
1437 void SVGElement::LengthAttributesInfo::Reset(uint8_t aAttrEnum) {
1438 mLengths[aAttrEnum].Init(mLengthInfo[aAttrEnum].mCtxType, aAttrEnum,
1439 mLengthInfo[aAttrEnum].mDefaultValue,
1440 mLengthInfo[aAttrEnum].mDefaultUnitType);
1443 void SVGElement::SetLength(nsAtom* aName, const SVGAnimatedLength& aLength) {
1444 LengthAttributesInfo lengthInfo = GetLengthInfo();
1446 for (uint32_t i = 0; i < lengthInfo.mLengthCount; i++) {
1447 if (aName == lengthInfo.mLengthInfo[i].mName) {
1448 lengthInfo.mLengths[i] = aLength;
1449 DidAnimateLength(i);
1450 return;
1453 MOZ_ASSERT(false, "no length found to set");
1456 nsAttrValue SVGElement::WillChangeLength(
1457 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1458 return WillChangeValue(GetLengthInfo().mLengthInfo[aAttrEnum].mName,
1459 aProofOfUpdate);
1462 void SVGElement::DidChangeLength(uint8_t aAttrEnum,
1463 const nsAttrValue& aEmptyOrOldValue,
1464 const mozAutoDocUpdate& aProofOfUpdate) {
1465 LengthAttributesInfo info = GetLengthInfo();
1467 NS_ASSERTION(info.mLengthCount > 0,
1468 "DidChangeLength on element with no length attribs");
1469 NS_ASSERTION(aAttrEnum < info.mLengthCount, "aAttrEnum out of range");
1471 nsAttrValue newValue;
1472 newValue.SetTo(info.mLengths[aAttrEnum], nullptr);
1474 DidChangeValue(info.mLengthInfo[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1475 aProofOfUpdate);
1478 void SVGElement::DidAnimateLength(uint8_t aAttrEnum) {
1479 // We need to do this here. Normally the SMIL restyle would also cause us to
1480 // do this from DidSetComputedStyle, but we don't have that guarantee if our
1481 // frame gets reconstructed.
1482 ClearAnyCachedPath();
1484 if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) {
1485 nsCSSPropertyID propId =
1486 SVGGeometryProperty::AttrEnumToCSSPropId(this, aAttrEnum);
1488 SMILOverrideStyle()->SetSMILValue(propId,
1489 GetLengthInfo().mLengths[aAttrEnum]);
1490 return;
1493 nsIFrame* frame = GetPrimaryFrame();
1495 if (frame) {
1496 LengthAttributesInfo info = GetLengthInfo();
1497 frame->AttributeChanged(kNameSpaceID_None,
1498 info.mLengthInfo[aAttrEnum].mName,
1499 MutationEvent_Binding::SMIL);
1503 SVGAnimatedLength* SVGElement::GetAnimatedLength(const nsAtom* aAttrName) {
1504 LengthAttributesInfo lengthInfo = GetLengthInfo();
1506 for (uint32_t i = 0; i < lengthInfo.mLengthCount; i++) {
1507 if (aAttrName == lengthInfo.mLengthInfo[i].mName) {
1508 return &lengthInfo.mLengths[i];
1511 return nullptr;
1514 void SVGElement::GetAnimatedLengthValues(float* aFirst, ...) {
1515 LengthAttributesInfo info = GetLengthInfo();
1517 NS_ASSERTION(info.mLengthCount > 0,
1518 "GetAnimatedLengthValues on element with no length attribs");
1520 SVGViewportElement* ctx = nullptr;
1522 float* f = aFirst;
1523 uint32_t i = 0;
1525 va_list args;
1526 va_start(args, aFirst);
1528 while (f && i < info.mLengthCount) {
1529 uint8_t type = info.mLengths[i].GetSpecifiedUnitType();
1530 if (!ctx) {
1531 if (type != SVGLength_Binding::SVG_LENGTHTYPE_NUMBER &&
1532 type != SVGLength_Binding::SVG_LENGTHTYPE_PX)
1533 ctx = GetCtx();
1535 if (type == SVGLength_Binding::SVG_LENGTHTYPE_EMS ||
1536 type == SVGLength_Binding::SVG_LENGTHTYPE_EXS)
1537 *f = info.mLengths[i++].GetAnimValue(this);
1538 else
1539 *f = info.mLengths[i++].GetAnimValue(ctx);
1540 f = va_arg(args, float*);
1543 va_end(args);
1546 SVGElement::LengthListAttributesInfo SVGElement::GetLengthListInfo() {
1547 return LengthListAttributesInfo(nullptr, nullptr, 0);
1550 void SVGElement::LengthListAttributesInfo::Reset(uint8_t aAttrEnum) {
1551 mLengthLists[aAttrEnum].ClearBaseValue(aAttrEnum);
1552 // caller notifies
1555 nsAttrValue SVGElement::WillChangeLengthList(
1556 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1557 return WillChangeValue(GetLengthListInfo().mLengthListInfo[aAttrEnum].mName,
1558 aProofOfUpdate);
1561 void SVGElement::DidChangeLengthList(uint8_t aAttrEnum,
1562 const nsAttrValue& aEmptyOrOldValue,
1563 const mozAutoDocUpdate& aProofOfUpdate) {
1564 LengthListAttributesInfo info = GetLengthListInfo();
1566 NS_ASSERTION(info.mLengthListCount > 0,
1567 "DidChangeLengthList on element with no length list attribs");
1568 NS_ASSERTION(aAttrEnum < info.mLengthListCount, "aAttrEnum out of range");
1570 nsAttrValue newValue;
1571 newValue.SetTo(info.mLengthLists[aAttrEnum].GetBaseValue(), nullptr);
1573 DidChangeValue(info.mLengthListInfo[aAttrEnum].mName, aEmptyOrOldValue,
1574 newValue, aProofOfUpdate);
1577 void SVGElement::DidAnimateLengthList(uint8_t aAttrEnum) {
1578 nsIFrame* frame = GetPrimaryFrame();
1580 if (frame) {
1581 LengthListAttributesInfo info = GetLengthListInfo();
1582 frame->AttributeChanged(kNameSpaceID_None,
1583 info.mLengthListInfo[aAttrEnum].mName,
1584 MutationEvent_Binding::SMIL);
1588 void SVGElement::GetAnimatedLengthListValues(SVGUserUnitList* aFirst, ...) {
1589 LengthListAttributesInfo info = GetLengthListInfo();
1591 NS_ASSERTION(
1592 info.mLengthListCount > 0,
1593 "GetAnimatedLengthListValues on element with no length list attribs");
1595 SVGUserUnitList* list = aFirst;
1596 uint32_t i = 0;
1598 va_list args;
1599 va_start(args, aFirst);
1601 while (list && i < info.mLengthListCount) {
1602 list->Init(&(info.mLengthLists[i].GetAnimValue()), this,
1603 info.mLengthListInfo[i].mAxis);
1604 ++i;
1605 list = va_arg(args, SVGUserUnitList*);
1608 va_end(args);
1611 SVGAnimatedLengthList* SVGElement::GetAnimatedLengthList(uint8_t aAttrEnum) {
1612 LengthListAttributesInfo info = GetLengthListInfo();
1613 if (aAttrEnum < info.mLengthListCount) {
1614 return &(info.mLengthLists[aAttrEnum]);
1616 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1617 return nullptr;
1620 SVGElement::NumberListAttributesInfo SVGElement::GetNumberListInfo() {
1621 return NumberListAttributesInfo(nullptr, nullptr, 0);
1624 void SVGElement::NumberListAttributesInfo::Reset(uint8_t aAttrEnum) {
1625 MOZ_ASSERT(aAttrEnum < mNumberListCount, "Bad attr enum");
1626 mNumberLists[aAttrEnum].ClearBaseValue(aAttrEnum);
1627 // caller notifies
1630 nsAttrValue SVGElement::WillChangeNumberList(
1631 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1632 return WillChangeValue(GetNumberListInfo().mNumberListInfo[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.mNumberListCount > 0,
1642 "DidChangeNumberList on element with no number list attribs");
1643 MOZ_ASSERT(aAttrEnum < info.mNumberListCount, "aAttrEnum out of range");
1645 nsAttrValue newValue;
1646 newValue.SetTo(info.mNumberLists[aAttrEnum].GetBaseValue(), nullptr);
1648 DidChangeValue(info.mNumberListInfo[aAttrEnum].mName, aEmptyOrOldValue,
1649 newValue, aProofOfUpdate);
1652 void SVGElement::DidAnimateNumberList(uint8_t aAttrEnum) {
1653 nsIFrame* frame = GetPrimaryFrame();
1655 if (frame) {
1656 NumberListAttributesInfo info = GetNumberListInfo();
1657 MOZ_ASSERT(aAttrEnum < info.mNumberListCount, "aAttrEnum out of range");
1659 frame->AttributeChanged(kNameSpaceID_None,
1660 info.mNumberListInfo[aAttrEnum].mName,
1661 MutationEvent_Binding::SMIL);
1665 SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(uint8_t aAttrEnum) {
1666 NumberListAttributesInfo info = GetNumberListInfo();
1667 if (aAttrEnum < info.mNumberListCount) {
1668 return &(info.mNumberLists[aAttrEnum]);
1670 MOZ_ASSERT(false, "Bad attrEnum");
1671 return nullptr;
1674 SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(nsAtom* aAttrName) {
1675 NumberListAttributesInfo info = GetNumberListInfo();
1676 for (uint32_t i = 0; i < info.mNumberListCount; i++) {
1677 if (aAttrName == info.mNumberListInfo[i].mName) {
1678 return &info.mNumberLists[i];
1681 MOZ_ASSERT(false, "Bad caller");
1682 return nullptr;
1685 nsAttrValue SVGElement::WillChangePointList(
1686 const mozAutoDocUpdate& aProofOfUpdate) {
1687 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1688 return WillChangeValue(GetPointListAttrName(), aProofOfUpdate);
1691 void SVGElement::DidChangePointList(const nsAttrValue& aEmptyOrOldValue,
1692 const mozAutoDocUpdate& aProofOfUpdate) {
1693 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1695 nsAttrValue newValue;
1696 newValue.SetTo(GetAnimatedPointList()->GetBaseValue(), nullptr);
1698 DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue, newValue,
1699 aProofOfUpdate);
1702 void SVGElement::DidAnimatePointList() {
1703 MOZ_ASSERT(GetPointListAttrName(), "Animating non-existent path data?");
1705 ClearAnyCachedPath();
1707 nsIFrame* frame = GetPrimaryFrame();
1709 if (frame) {
1710 frame->AttributeChanged(kNameSpaceID_None, GetPointListAttrName(),
1711 MutationEvent_Binding::SMIL);
1715 nsAttrValue SVGElement::WillChangePathSegList(
1716 const mozAutoDocUpdate& aProofOfUpdate) {
1717 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1718 return WillChangeValue(GetPathDataAttrName(), aProofOfUpdate);
1721 void SVGElement::DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue,
1722 const mozAutoDocUpdate& aProofOfUpdate) {
1723 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1725 nsAttrValue newValue;
1726 newValue.SetTo(GetAnimPathSegList()->GetBaseValue(), nullptr);
1728 DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue, newValue,
1729 aProofOfUpdate);
1732 void SVGElement::DidAnimatePathSegList() {
1733 MOZ_ASSERT(GetPathDataAttrName(), "Animating non-existent path data?");
1735 ClearAnyCachedPath();
1737 nsIFrame* frame = GetPrimaryFrame();
1739 if (frame) {
1740 frame->AttributeChanged(kNameSpaceID_None, GetPathDataAttrName(),
1741 MutationEvent_Binding::SMIL);
1745 SVGElement::NumberAttributesInfo SVGElement::GetNumberInfo() {
1746 return NumberAttributesInfo(nullptr, nullptr, 0);
1749 void SVGElement::NumberAttributesInfo::Reset(uint8_t aAttrEnum) {
1750 mNumbers[aAttrEnum].Init(aAttrEnum, mNumberInfo[aAttrEnum].mDefaultValue);
1753 void SVGElement::DidChangeNumber(uint8_t aAttrEnum) {
1754 NumberAttributesInfo info = GetNumberInfo();
1756 NS_ASSERTION(info.mNumberCount > 0,
1757 "DidChangeNumber on element with no number attribs");
1758 NS_ASSERTION(aAttrEnum < info.mNumberCount, "aAttrEnum out of range");
1760 nsAttrValue attrValue;
1761 attrValue.SetTo(info.mNumbers[aAttrEnum].GetBaseValue(), nullptr);
1763 SetParsedAttr(kNameSpaceID_None, info.mNumberInfo[aAttrEnum].mName, nullptr,
1764 attrValue, true);
1767 void SVGElement::DidAnimateNumber(uint8_t aAttrEnum) {
1768 nsIFrame* frame = GetPrimaryFrame();
1770 if (frame) {
1771 NumberAttributesInfo info = GetNumberInfo();
1772 frame->AttributeChanged(kNameSpaceID_None,
1773 info.mNumberInfo[aAttrEnum].mName,
1774 MutationEvent_Binding::SMIL);
1778 void SVGElement::GetAnimatedNumberValues(float* aFirst, ...) {
1779 NumberAttributesInfo info = GetNumberInfo();
1781 NS_ASSERTION(info.mNumberCount > 0,
1782 "GetAnimatedNumberValues on element with no number attribs");
1784 float* f = aFirst;
1785 uint32_t i = 0;
1787 va_list args;
1788 va_start(args, aFirst);
1790 while (f && i < info.mNumberCount) {
1791 *f = info.mNumbers[i++].GetAnimValue();
1792 f = va_arg(args, float*);
1794 va_end(args);
1797 SVGElement::NumberPairAttributesInfo SVGElement::GetNumberPairInfo() {
1798 return NumberPairAttributesInfo(nullptr, nullptr, 0);
1801 void SVGElement::NumberPairAttributesInfo::Reset(uint8_t aAttrEnum) {
1802 mNumberPairs[aAttrEnum].Init(aAttrEnum,
1803 mNumberPairInfo[aAttrEnum].mDefaultValue1,
1804 mNumberPairInfo[aAttrEnum].mDefaultValue2);
1807 nsAttrValue SVGElement::WillChangeNumberPair(uint8_t aAttrEnum) {
1808 mozAutoDocUpdate updateBatch(GetComposedDoc(), kDontNotifyDocumentObservers);
1809 return WillChangeValue(GetNumberPairInfo().mNumberPairInfo[aAttrEnum].mName,
1810 updateBatch);
1813 void SVGElement::DidChangeNumberPair(uint8_t aAttrEnum,
1814 const nsAttrValue& aEmptyOrOldValue) {
1815 NumberPairAttributesInfo info = GetNumberPairInfo();
1817 NS_ASSERTION(info.mNumberPairCount > 0,
1818 "DidChangePairNumber on element with no number pair attribs");
1819 NS_ASSERTION(aAttrEnum < info.mNumberPairCount, "aAttrEnum out of range");
1821 nsAttrValue newValue;
1822 newValue.SetTo(info.mNumberPairs[aAttrEnum], nullptr);
1824 mozAutoDocUpdate updateBatch(GetComposedDoc(), kNotifyDocumentObservers);
1825 DidChangeValue(info.mNumberPairInfo[aAttrEnum].mName, aEmptyOrOldValue,
1826 newValue, updateBatch);
1829 void SVGElement::DidAnimateNumberPair(uint8_t aAttrEnum) {
1830 nsIFrame* frame = GetPrimaryFrame();
1832 if (frame) {
1833 NumberPairAttributesInfo info = GetNumberPairInfo();
1834 frame->AttributeChanged(kNameSpaceID_None,
1835 info.mNumberPairInfo[aAttrEnum].mName,
1836 MutationEvent_Binding::SMIL);
1840 SVGElement::IntegerAttributesInfo SVGElement::GetIntegerInfo() {
1841 return IntegerAttributesInfo(nullptr, nullptr, 0);
1844 void SVGElement::IntegerAttributesInfo::Reset(uint8_t aAttrEnum) {
1845 mIntegers[aAttrEnum].Init(aAttrEnum, mIntegerInfo[aAttrEnum].mDefaultValue);
1848 void SVGElement::DidChangeInteger(uint8_t aAttrEnum) {
1849 IntegerAttributesInfo info = GetIntegerInfo();
1851 NS_ASSERTION(info.mIntegerCount > 0,
1852 "DidChangeInteger on element with no integer attribs");
1853 NS_ASSERTION(aAttrEnum < info.mIntegerCount, "aAttrEnum out of range");
1855 nsAttrValue attrValue;
1856 attrValue.SetTo(info.mIntegers[aAttrEnum].GetBaseValue(), nullptr);
1858 SetParsedAttr(kNameSpaceID_None, info.mIntegerInfo[aAttrEnum].mName, nullptr,
1859 attrValue, true);
1862 void SVGElement::DidAnimateInteger(uint8_t aAttrEnum) {
1863 nsIFrame* frame = GetPrimaryFrame();
1865 if (frame) {
1866 IntegerAttributesInfo info = GetIntegerInfo();
1867 frame->AttributeChanged(kNameSpaceID_None,
1868 info.mIntegerInfo[aAttrEnum].mName,
1869 MutationEvent_Binding::SMIL);
1873 void SVGElement::GetAnimatedIntegerValues(int32_t* aFirst, ...) {
1874 IntegerAttributesInfo info = GetIntegerInfo();
1876 NS_ASSERTION(info.mIntegerCount > 0,
1877 "GetAnimatedIntegerValues on element with no integer attribs");
1879 int32_t* n = aFirst;
1880 uint32_t i = 0;
1882 va_list args;
1883 va_start(args, aFirst);
1885 while (n && i < info.mIntegerCount) {
1886 *n = info.mIntegers[i++].GetAnimValue();
1887 n = va_arg(args, int32_t*);
1889 va_end(args);
1892 SVGElement::IntegerPairAttributesInfo SVGElement::GetIntegerPairInfo() {
1893 return IntegerPairAttributesInfo(nullptr, nullptr, 0);
1896 void SVGElement::IntegerPairAttributesInfo::Reset(uint8_t aAttrEnum) {
1897 mIntegerPairs[aAttrEnum].Init(aAttrEnum,
1898 mIntegerPairInfo[aAttrEnum].mDefaultValue1,
1899 mIntegerPairInfo[aAttrEnum].mDefaultValue2);
1902 nsAttrValue SVGElement::WillChangeIntegerPair(
1903 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1904 return WillChangeValue(GetIntegerPairInfo().mIntegerPairInfo[aAttrEnum].mName,
1905 aProofOfUpdate);
1908 void SVGElement::DidChangeIntegerPair(uint8_t aAttrEnum,
1909 const nsAttrValue& aEmptyOrOldValue,
1910 const mozAutoDocUpdate& aProofOfUpdate) {
1911 IntegerPairAttributesInfo info = GetIntegerPairInfo();
1913 NS_ASSERTION(info.mIntegerPairCount > 0,
1914 "DidChangeIntegerPair on element with no integer pair attribs");
1915 NS_ASSERTION(aAttrEnum < info.mIntegerPairCount, "aAttrEnum out of range");
1917 nsAttrValue newValue;
1918 newValue.SetTo(info.mIntegerPairs[aAttrEnum], nullptr);
1920 DidChangeValue(info.mIntegerPairInfo[aAttrEnum].mName, aEmptyOrOldValue,
1921 newValue, aProofOfUpdate);
1924 void SVGElement::DidAnimateIntegerPair(uint8_t aAttrEnum) {
1925 nsIFrame* frame = GetPrimaryFrame();
1927 if (frame) {
1928 IntegerPairAttributesInfo info = GetIntegerPairInfo();
1929 frame->AttributeChanged(kNameSpaceID_None,
1930 info.mIntegerPairInfo[aAttrEnum].mName,
1931 MutationEvent_Binding::SMIL);
1935 SVGElement::BooleanAttributesInfo SVGElement::GetBooleanInfo() {
1936 return BooleanAttributesInfo(nullptr, nullptr, 0);
1939 void SVGElement::BooleanAttributesInfo::Reset(uint8_t aAttrEnum) {
1940 mBooleans[aAttrEnum].Init(aAttrEnum, mBooleanInfo[aAttrEnum].mDefaultValue);
1943 void SVGElement::DidChangeBoolean(uint8_t aAttrEnum) {
1944 BooleanAttributesInfo info = GetBooleanInfo();
1946 NS_ASSERTION(info.mBooleanCount > 0,
1947 "DidChangeBoolean on element with no boolean attribs");
1948 NS_ASSERTION(aAttrEnum < info.mBooleanCount, "aAttrEnum out of range");
1950 nsAttrValue attrValue(info.mBooleans[aAttrEnum].GetBaseValueAtom());
1951 SetParsedAttr(kNameSpaceID_None, info.mBooleanInfo[aAttrEnum].mName, nullptr,
1952 attrValue, true);
1955 void SVGElement::DidAnimateBoolean(uint8_t aAttrEnum) {
1956 nsIFrame* frame = GetPrimaryFrame();
1958 if (frame) {
1959 BooleanAttributesInfo info = GetBooleanInfo();
1960 frame->AttributeChanged(kNameSpaceID_None,
1961 info.mBooleanInfo[aAttrEnum].mName,
1962 MutationEvent_Binding::SMIL);
1966 SVGElement::EnumAttributesInfo SVGElement::GetEnumInfo() {
1967 return EnumAttributesInfo(nullptr, nullptr, 0);
1970 void SVGElement::EnumAttributesInfo::Reset(uint8_t aAttrEnum) {
1971 mEnums[aAttrEnum].Init(aAttrEnum, mEnumInfo[aAttrEnum].mDefaultValue);
1974 void SVGElement::EnumAttributesInfo::SetUnknownValue(uint8_t aAttrEnum) {
1975 // Fortunately in SVG every enum's unknown value is 0
1976 mEnums[aAttrEnum].Init(aAttrEnum, 0);
1979 void SVGElement::DidChangeEnum(uint8_t aAttrEnum) {
1980 EnumAttributesInfo info = GetEnumInfo();
1982 NS_ASSERTION(info.mEnumCount > 0,
1983 "DidChangeEnum on element with no enum attribs");
1984 NS_ASSERTION(aAttrEnum < info.mEnumCount, "aAttrEnum out of range");
1986 nsAttrValue attrValue(info.mEnums[aAttrEnum].GetBaseValueAtom(this));
1987 SetParsedAttr(kNameSpaceID_None, info.mEnumInfo[aAttrEnum].mName, nullptr,
1988 attrValue, true);
1991 void SVGElement::DidAnimateEnum(uint8_t aAttrEnum) {
1992 nsIFrame* frame = GetPrimaryFrame();
1994 if (frame) {
1995 EnumAttributesInfo info = GetEnumInfo();
1996 frame->AttributeChanged(kNameSpaceID_None, info.mEnumInfo[aAttrEnum].mName,
1997 MutationEvent_Binding::SMIL);
2001 SVGAnimatedOrient* SVGElement::GetAnimatedOrient() { return nullptr; }
2003 nsAttrValue SVGElement::WillChangeOrient(
2004 const mozAutoDocUpdate& aProofOfUpdate) {
2005 return WillChangeValue(nsGkAtoms::orient, aProofOfUpdate);
2008 void SVGElement::DidChangeOrient(const nsAttrValue& aEmptyOrOldValue,
2009 const mozAutoDocUpdate& aProofOfUpdate) {
2010 SVGAnimatedOrient* orient = GetAnimatedOrient();
2012 NS_ASSERTION(orient, "DidChangeOrient on element with no orient attrib");
2014 nsAttrValue newValue;
2015 newValue.SetTo(*orient, nullptr);
2017 DidChangeValue(nsGkAtoms::orient, aEmptyOrOldValue, newValue, aProofOfUpdate);
2020 void SVGElement::DidAnimateOrient() {
2021 nsIFrame* frame = GetPrimaryFrame();
2023 if (frame) {
2024 frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::orient,
2025 MutationEvent_Binding::SMIL);
2029 SVGAnimatedViewBox* SVGElement::GetAnimatedViewBox() { return nullptr; }
2031 nsAttrValue SVGElement::WillChangeViewBox(
2032 const mozAutoDocUpdate& aProofOfUpdate) {
2033 return WillChangeValue(nsGkAtoms::viewBox, aProofOfUpdate);
2036 void SVGElement::DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue,
2037 const mozAutoDocUpdate& aProofOfUpdate) {
2038 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
2040 NS_ASSERTION(viewBox, "DidChangeViewBox on element with no viewBox attrib");
2042 nsAttrValue newValue;
2043 newValue.SetTo(*viewBox, nullptr);
2045 DidChangeValue(nsGkAtoms::viewBox, aEmptyOrOldValue, newValue,
2046 aProofOfUpdate);
2049 void SVGElement::DidAnimateViewBox() {
2050 nsIFrame* frame = GetPrimaryFrame();
2052 if (frame) {
2053 frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::viewBox,
2054 MutationEvent_Binding::SMIL);
2058 SVGAnimatedPreserveAspectRatio* SVGElement::GetAnimatedPreserveAspectRatio() {
2059 return nullptr;
2062 nsAttrValue SVGElement::WillChangePreserveAspectRatio(
2063 const mozAutoDocUpdate& aProofOfUpdate) {
2064 return WillChangeValue(nsGkAtoms::preserveAspectRatio, aProofOfUpdate);
2067 void SVGElement::DidChangePreserveAspectRatio(
2068 const nsAttrValue& aEmptyOrOldValue,
2069 const mozAutoDocUpdate& aProofOfUpdate) {
2070 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
2071 GetAnimatedPreserveAspectRatio();
2073 NS_ASSERTION(preserveAspectRatio,
2074 "DidChangePreserveAspectRatio on element with no "
2075 "preserveAspectRatio attrib");
2077 nsAttrValue newValue;
2078 newValue.SetTo(*preserveAspectRatio, nullptr);
2080 DidChangeValue(nsGkAtoms::preserveAspectRatio, aEmptyOrOldValue, newValue,
2081 aProofOfUpdate);
2084 void SVGElement::DidAnimatePreserveAspectRatio() {
2085 nsIFrame* frame = GetPrimaryFrame();
2087 if (frame) {
2088 frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio,
2089 MutationEvent_Binding::SMIL);
2093 nsAttrValue SVGElement::WillChangeTransformList(
2094 const mozAutoDocUpdate& aProofOfUpdate) {
2095 return WillChangeValue(GetTransformListAttrName(), aProofOfUpdate);
2098 void SVGElement::DidChangeTransformList(
2099 const nsAttrValue& aEmptyOrOldValue,
2100 const mozAutoDocUpdate& aProofOfUpdate) {
2101 MOZ_ASSERT(GetTransformListAttrName(),
2102 "Changing non-existent transform list?");
2104 // The transform attribute is being set, so we must ensure that the
2105 // SVGAnimatedTransformList is/has been allocated:
2106 nsAttrValue newValue;
2107 newValue.SetTo(GetAnimatedTransformList(DO_ALLOCATE)->GetBaseValue(),
2108 nullptr);
2110 DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue, newValue,
2111 aProofOfUpdate);
2114 void SVGElement::DidAnimateTransformList(int32_t aModType) {
2115 MOZ_ASSERT(GetTransformListAttrName(),
2116 "Animating non-existent transform data?");
2118 nsIFrame* frame = GetPrimaryFrame();
2120 if (frame) {
2121 nsAtom* transformAttr = GetTransformListAttrName();
2122 frame->AttributeChanged(kNameSpaceID_None, transformAttr, aModType);
2123 // When script changes the 'transform' attribute, Element::SetAttrAndNotify
2124 // will call MutationObservers::NotifyAttributeChanged, under which
2125 // SVGTransformableElement::GetAttributeChangeHint will be called and an
2126 // appropriate change event posted to update our frame's overflow rects.
2127 // The SetAttrAndNotify doesn't happen for transform changes caused by
2128 // 'animateTransform' though (and sending out the mutation events that
2129 // MutationObservers::NotifyAttributeChanged dispatches would be
2130 // inappropriate anyway), so we need to post the change event ourself.
2131 nsChangeHint changeHint = GetAttributeChangeHint(transformAttr, aModType);
2132 if (changeHint) {
2133 nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0}, changeHint);
2138 SVGElement::StringAttributesInfo SVGElement::GetStringInfo() {
2139 return StringAttributesInfo(nullptr, nullptr, 0);
2142 void SVGElement::StringAttributesInfo::Reset(uint8_t aAttrEnum) {
2143 mStrings[aAttrEnum].Init(aAttrEnum);
2146 void SVGElement::GetStringBaseValue(uint8_t aAttrEnum,
2147 nsAString& aResult) const {
2148 SVGElement::StringAttributesInfo info =
2149 const_cast<SVGElement*>(this)->GetStringInfo();
2151 NS_ASSERTION(info.mStringCount > 0,
2152 "GetBaseValue on element with no string attribs");
2154 NS_ASSERTION(aAttrEnum < info.mStringCount, "aAttrEnum out of range");
2156 GetAttr(info.mStringInfo[aAttrEnum].mNamespaceID,
2157 info.mStringInfo[aAttrEnum].mName, aResult);
2160 void SVGElement::SetStringBaseValue(uint8_t aAttrEnum,
2161 const nsAString& aValue) {
2162 SVGElement::StringAttributesInfo info = GetStringInfo();
2164 NS_ASSERTION(info.mStringCount > 0,
2165 "SetBaseValue on element with no string attribs");
2167 NS_ASSERTION(aAttrEnum < info.mStringCount, "aAttrEnum out of range");
2169 SetAttr(info.mStringInfo[aAttrEnum].mNamespaceID,
2170 info.mStringInfo[aAttrEnum].mName, aValue, true);
2173 void SVGElement::DidAnimateString(uint8_t aAttrEnum) {
2174 nsIFrame* frame = GetPrimaryFrame();
2176 if (frame) {
2177 StringAttributesInfo info = GetStringInfo();
2178 frame->AttributeChanged(info.mStringInfo[aAttrEnum].mNamespaceID,
2179 info.mStringInfo[aAttrEnum].mName,
2180 MutationEvent_Binding::SMIL);
2184 SVGElement::StringListAttributesInfo SVGElement::GetStringListInfo() {
2185 return StringListAttributesInfo(nullptr, nullptr, 0);
2188 nsAttrValue SVGElement::WillChangeStringList(
2189 bool aIsConditionalProcessingAttribute, uint8_t aAttrEnum,
2190 const mozAutoDocUpdate& aProofOfUpdate) {
2191 nsStaticAtom* name;
2192 if (aIsConditionalProcessingAttribute) {
2193 nsCOMPtr<SVGTests> tests(do_QueryInterface(this));
2194 name = tests->GetAttrName(aAttrEnum);
2195 } else {
2196 name = GetStringListInfo().mStringListInfo[aAttrEnum].mName;
2198 return WillChangeValue(name, aProofOfUpdate);
2201 void SVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute,
2202 uint8_t aAttrEnum,
2203 const nsAttrValue& aEmptyOrOldValue,
2204 const mozAutoDocUpdate& aProofOfUpdate) {
2205 nsStaticAtom* name;
2206 nsAttrValue newValue;
2207 nsCOMPtr<SVGTests> tests;
2209 if (aIsConditionalProcessingAttribute) {
2210 tests = do_QueryObject(this);
2211 name = tests->GetAttrName(aAttrEnum);
2212 tests->GetAttrValue(aAttrEnum, newValue);
2213 } else {
2214 StringListAttributesInfo info = GetStringListInfo();
2216 NS_ASSERTION(info.mStringListCount > 0,
2217 "DidChangeStringList on element with no string list attribs");
2218 NS_ASSERTION(aAttrEnum < info.mStringListCount, "aAttrEnum out of range");
2220 name = info.mStringListInfo[aAttrEnum].mName;
2221 newValue.SetTo(info.mStringLists[aAttrEnum], nullptr);
2224 DidChangeValue(name, aEmptyOrOldValue, newValue, aProofOfUpdate);
2226 if (aIsConditionalProcessingAttribute) {
2227 tests->MaybeInvalidate();
2231 void SVGElement::StringListAttributesInfo::Reset(uint8_t aAttrEnum) {
2232 mStringLists[aAttrEnum].Clear();
2233 // caller notifies
2236 nsresult SVGElement::ReportAttributeParseFailure(Document* aDocument,
2237 nsAtom* aAttribute,
2238 const nsAString& aValue) {
2239 AutoTArray<nsString, 2> strings;
2240 strings.AppendElement(nsDependentAtomString(aAttribute));
2241 strings.AppendElement(aValue);
2242 return SVGContentUtils::ReportToConsole(aDocument, "AttributeParseWarning",
2243 strings);
2246 void SVGElement::RecompileScriptEventListeners() {
2247 int32_t i, count = mAttrs.AttrCount();
2248 for (i = 0; i < count; ++i) {
2249 const nsAttrName* name = mAttrs.AttrNameAt(i);
2251 // Eventlistenener-attributes are always in the null namespace
2252 if (!name->IsAtom()) {
2253 continue;
2256 nsAtom* attr = name->Atom();
2257 if (!IsEventAttributeName(attr)) {
2258 continue;
2261 nsAutoString value;
2262 GetAttr(attr, value);
2263 SetEventHandler(GetEventNameForAttr(attr), value, true);
2267 UniquePtr<SMILAttr> SVGElement::GetAnimatedAttr(int32_t aNamespaceID,
2268 nsAtom* aName) {
2269 if (aNamespaceID == kNameSpaceID_None) {
2270 // Transforms:
2271 if (GetTransformListAttrName() == aName) {
2272 // The transform attribute is being animated, so we must ensure that the
2273 // SVGAnimatedTransformList is/has been allocated:
2274 return GetAnimatedTransformList(DO_ALLOCATE)->ToSMILAttr(this);
2277 // Motion (fake 'attribute' for animateMotion)
2278 if (aName == nsGkAtoms::mozAnimateMotionDummyAttr) {
2279 return MakeUnique<SVGMotionSMILAttr>(this);
2282 // Lengths:
2283 LengthAttributesInfo info = GetLengthInfo();
2284 for (uint32_t i = 0; i < info.mLengthCount; i++) {
2285 if (aName == info.mLengthInfo[i].mName) {
2286 return info.mLengths[i].ToSMILAttr(this);
2290 // Numbers:
2292 NumberAttributesInfo info = GetNumberInfo();
2293 for (uint32_t i = 0; i < info.mNumberCount; i++) {
2294 if (aName == info.mNumberInfo[i].mName) {
2295 return info.mNumbers[i].ToSMILAttr(this);
2300 // Number Pairs:
2302 NumberPairAttributesInfo info = GetNumberPairInfo();
2303 for (uint32_t i = 0; i < info.mNumberPairCount; i++) {
2304 if (aName == info.mNumberPairInfo[i].mName) {
2305 return info.mNumberPairs[i].ToSMILAttr(this);
2310 // Integers:
2312 IntegerAttributesInfo info = GetIntegerInfo();
2313 for (uint32_t i = 0; i < info.mIntegerCount; i++) {
2314 if (aName == info.mIntegerInfo[i].mName) {
2315 return info.mIntegers[i].ToSMILAttr(this);
2320 // Integer Pairs:
2322 IntegerPairAttributesInfo info = GetIntegerPairInfo();
2323 for (uint32_t i = 0; i < info.mIntegerPairCount; i++) {
2324 if (aName == info.mIntegerPairInfo[i].mName) {
2325 return info.mIntegerPairs[i].ToSMILAttr(this);
2330 // Enumerations:
2332 EnumAttributesInfo info = GetEnumInfo();
2333 for (uint32_t i = 0; i < info.mEnumCount; i++) {
2334 if (aName == info.mEnumInfo[i].mName) {
2335 return info.mEnums[i].ToSMILAttr(this);
2340 // Booleans:
2342 BooleanAttributesInfo info = GetBooleanInfo();
2343 for (uint32_t i = 0; i < info.mBooleanCount; i++) {
2344 if (aName == info.mBooleanInfo[i].mName) {
2345 return info.mBooleans[i].ToSMILAttr(this);
2350 // orient:
2351 if (aName == nsGkAtoms::orient) {
2352 SVGAnimatedOrient* orient = GetAnimatedOrient();
2353 return orient ? orient->ToSMILAttr(this) : nullptr;
2356 // viewBox:
2357 if (aName == nsGkAtoms::viewBox) {
2358 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
2359 return viewBox ? viewBox->ToSMILAttr(this) : nullptr;
2362 // preserveAspectRatio:
2363 if (aName == nsGkAtoms::preserveAspectRatio) {
2364 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
2365 GetAnimatedPreserveAspectRatio();
2366 return preserveAspectRatio ? preserveAspectRatio->ToSMILAttr(this)
2367 : nullptr;
2370 // NumberLists:
2372 NumberListAttributesInfo info = GetNumberListInfo();
2373 for (uint32_t i = 0; i < info.mNumberListCount; i++) {
2374 if (aName == info.mNumberListInfo[i].mName) {
2375 MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes");
2376 return info.mNumberLists[i].ToSMILAttr(this, uint8_t(i));
2381 // LengthLists:
2383 LengthListAttributesInfo info = GetLengthListInfo();
2384 for (uint32_t i = 0; i < info.mLengthListCount; i++) {
2385 if (aName == info.mLengthListInfo[i].mName) {
2386 MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes");
2387 return info.mLengthLists[i].ToSMILAttr(
2388 this, uint8_t(i), info.mLengthListInfo[i].mAxis,
2389 info.mLengthListInfo[i].mCouldZeroPadList);
2394 // PointLists:
2396 if (GetPointListAttrName() == aName) {
2397 SVGAnimatedPointList* pointList = GetAnimatedPointList();
2398 if (pointList) {
2399 return pointList->ToSMILAttr(this);
2404 // PathSegLists:
2406 if (GetPathDataAttrName() == aName) {
2407 SVGAnimatedPathSegList* segList = GetAnimPathSegList();
2408 if (segList) {
2409 return segList->ToSMILAttr(this);
2414 if (aName == nsGkAtoms::_class) {
2415 return mClassAttribute.ToSMILAttr(this);
2419 // Strings
2421 StringAttributesInfo info = GetStringInfo();
2422 for (uint32_t i = 0; i < info.mStringCount; i++) {
2423 if (aNamespaceID == info.mStringInfo[i].mNamespaceID &&
2424 aName == info.mStringInfo[i].mName) {
2425 return info.mStrings[i].ToSMILAttr(this);
2430 return nullptr;
2433 void SVGElement::AnimationNeedsResample() {
2434 Document* doc = GetComposedDoc();
2435 if (doc && doc->HasAnimationController()) {
2436 doc->GetAnimationController()->SetResampleNeeded();
2440 void SVGElement::FlushAnimations() {
2441 Document* doc = GetComposedDoc();
2442 if (doc && doc->HasAnimationController()) {
2443 doc->GetAnimationController()->FlushResampleRequests();
2447 void SVGElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
2448 size_t* aNodeSize) const {
2449 Element::AddSizeOfExcludingThis(aSizes, aNodeSize);
2451 // These are owned by the element and not referenced from the stylesheets.
2452 // They're referenced from the rule tree, but the rule nodes don't measure
2453 // their style source (since they're non-owning), so unconditionally reporting
2454 // them even though it's a refcounted object is ok.
2455 if (mContentDeclarationBlock) {
2456 aSizes.mLayoutSvgMappedDeclarations +=
2457 mContentDeclarationBlock->SizeofIncludingThis(
2458 aSizes.mState.mMallocSizeOf);
2462 } // namespace dom
2463 } // namespace mozilla