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"
40 #include "nsGkAtoms.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"
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();
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
},
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
));
116 static_cast<SVGElement
*>(aDest
)->SetNonce(*nonce
);
121 //----------------------------------------------------------------------
122 // SVGElement methods
124 void SVGElement::DidAnimateClass() {
125 // For Servo, snapshot the element before we change it.
126 PresShell
* presShell
= OwnerDoc()->GetPresShell();
128 if (nsPresContext
* presContext
= presShell
->GetPresContext()) {
129 presContext
->RestyleManager()->ClassAttributeWillBeChangedBySMIL(this);
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?
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();
154 for (i
= 0; i
< lengthInfo
.mLengthCount
; i
++) {
158 NumberAttributesInfo numberInfo
= GetNumberInfo();
160 for (i
= 0; i
< numberInfo
.mNumberCount
; 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
++) {
194 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
200 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
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
228 // No need to reset SVGPathData since the default value is always the same
231 StringAttributesInfo stringInfo
= GetStringInfo();
233 for (i
= 0; i
< stringInfo
.mStringCount
; i
++) {
240 //----------------------------------------------------------------------
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)]() {
258 self
->GetNonce(nonce
);
259 self
->SetAttr(kNameSpaceID_None
, nsGkAtoms::nonce
, EmptyString(),
261 self
->SetNonce(nonce
);
265 if (!MayHaveStyle()) {
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
285 rv
= mAttrs
.SetAndSwapAttr(nsGkAtoms::style
, attrValue
, &oldValueSet
);
286 NS_ENSURE_SUCCESS(rv
, rv
);
292 nsresult
SVGElement::AfterSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
293 const nsAttrValue
* aValue
,
294 const nsAttrValue
* aOldValue
,
295 nsIPrincipal
* aSubjectPrincipal
,
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
) {
326 SetNonce(aValue
->GetStringValue());
327 if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) {
328 SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP
);
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
) {
344 bool foundMatch
= false;
345 bool didSetResult
= false;
347 if (aNamespaceID
== kNameSpaceID_None
) {
348 // Check for SVGAnimatedLength attribute
349 LengthAttributesInfo lengthInfo
= GetLengthInfo();
352 for (i
= 0; i
< lengthInfo
.mLengthCount
; i
++) {
353 if (aAttribute
== lengthInfo
.mLengthInfo
[i
].mName
) {
354 rv
= lengthInfo
.mLengths
[i
].SetBaseValueString(aValue
, this, false);
358 aResult
.SetTo(lengthInfo
.mLengths
[i
], &aValue
);
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
);
373 lengthListInfo
.Reset(i
);
375 aResult
.SetTo(lengthListInfo
.mLengthLists
[i
].GetBaseValue(),
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
);
392 numberListInfo
.Reset(i
);
394 aResult
.SetTo(numberListInfo
.mNumberLists
[i
].GetBaseValue(),
405 // Check for SVGAnimatedPointList attribute
406 if (GetPointListAttrName() == aAttribute
) {
407 SVGAnimatedPointList
* pointList
= GetAnimatedPointList();
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
);
421 // Check for SVGAnimatedPathSegList attribute
422 if (GetPathDataAttrName() == aAttribute
) {
423 SVGAnimatedPathSegList
* segList
= GetAnimPathSegList();
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
);
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);
445 aResult
.SetTo(numberInfo
.mNumbers
[i
].GetBaseValue(), &aValue
);
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);
461 numberPairInfo
.Reset(i
);
463 aResult
.SetTo(numberPairInfo
.mNumberPairs
[i
], &aValue
);
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);
479 integerInfo
.Reset(i
);
481 aResult
.SetTo(integerInfo
.mIntegers
[i
].GetBaseValue(), &aValue
);
491 // Check for SVGAnimatedIntegerPair attribute
492 IntegerPairAttributesInfo integerPairInfo
= GetIntegerPairInfo();
493 for (i
= 0; i
< integerPairInfo
.mIntegerPairCount
; i
++) {
494 if (aAttribute
== integerPairInfo
.mIntegerPairInfo
[i
].mName
) {
496 integerPairInfo
.mIntegerPairs
[i
].SetBaseValueString(aValue
, this);
498 integerPairInfo
.Reset(i
);
500 aResult
.SetTo(integerPairInfo
.mIntegerPairs
[i
], &aValue
);
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
);
516 ? booleanInfo
.mBooleans
[i
].SetBaseValueAtom(valAtom
, this)
517 : NS_ERROR_DOM_SYNTAX_ERR
;
519 booleanInfo
.Reset(i
);
521 aResult
.SetTo(valAtom
);
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
539 rv
= NS_ERROR_FAILURE
;
540 enumInfo
.SetUnknownValue(i
);
542 aResult
.SetTo(valAtom
);
552 // Check for conditional processing attributes
553 nsCOMPtr
<SVGTests
> tests
= do_QueryObject(this);
554 if (tests
&& tests
->ParseConditionalProcessingAttribute(
555 aAttribute
, aValue
, aResult
)) {
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
);
567 stringListInfo
.Reset(i
);
569 aResult
.SetTo(stringListInfo
.mStringLists
[i
], &aValue
);
579 // Check for orient attribute
580 if (aAttribute
== nsGkAtoms::orient
) {
581 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
583 rv
= orient
->SetBaseValueString(aValue
, this, false);
587 aResult
.SetTo(*orient
, &aValue
);
592 // Check for viewBox attribute
593 } else if (aAttribute
== nsGkAtoms::viewBox
) {
594 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
596 rv
= viewBox
->SetBaseValueString(aValue
, this, false);
600 aResult
.SetTo(*viewBox
, &aValue
);
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);
612 preserveAspectRatio
->Init();
614 aResult
.SetTo(*preserveAspectRatio
, &aValue
);
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);
627 transformList
->ClearBaseValue();
629 aResult
.SetTo(transformList
->GetBaseValue(), &aValue
);
633 } else if (aAttribute
== nsGkAtoms::tabindex
) {
634 didSetResult
= aResult
.ParseIntValue(aValue
);
639 if (aAttribute
== nsGkAtoms::_class
) {
640 mClassAttribute
.SetBaseValue(aValue
, this, false);
641 aResult
.ParseAtomArray(aValue
);
645 if (aAttribute
== nsGkAtoms::rel
) {
646 aResult
.ParseAtomArray(aValue
);
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);
666 ReportAttributeParseFailure(OwnerDoc(), aAttribute
, aValue
);
670 aResult
.SetTo(aValue
);
675 return SVGElementBase::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
676 aMaybeScriptedPrincipal
, aResult
);
679 void SVGElement::UnsetAttrInternal(int32_t aNamespaceID
, nsAtom
* aName
,
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
687 if (IsAttributeMapped(aName
)) {
688 mContentDeclarationBlock
= nullptr;
691 if (IsEventAttributeName(aName
)) {
692 EventListenerManager
* manager
= GetExistingListenerManager();
694 nsAtom
* eventName
= GetEventNameForAttr(aName
);
695 manager
->RemoveEventHandler(eventName
);
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
);
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
);
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
);
733 // Check if this is a point list attribute going away
734 if (GetPointListAttrName() == aName
) {
735 SVGAnimatedPointList
* pointList
= GetAnimatedPointList();
737 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
738 pointList
->ClearBaseValue();
743 // Check if this is a path segment list attribute going away
744 if (GetPathDataAttrName() == aName
) {
745 SVGAnimatedPathSegList
* segList
= GetAnimPathSegList();
747 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
748 segList
->ClearBaseValue();
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
) {
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
);
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
) {
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
);
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
) {
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
) {
815 // Check if this is an orient attribute going away
816 if (aName
== nsGkAtoms::orient
) {
817 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
819 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
825 // Check if this is a viewBox attribute going away
826 if (aName
== nsGkAtoms::viewBox
) {
827 SVGAnimatedViewBox
* viewBox
= GetAnimatedViewBox();
829 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
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();
846 // Check if this is a transform list attribute going away
847 if (GetTransformListAttrName() == aName
) {
848 SVGAnimatedTransformList
* transformList
= GetAnimatedTransformList();
850 MaybeSerializeAttrBeforeRemoval(aName
, aNotify
);
851 transformList
->ClearBaseValue();
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
);
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
);
875 if (aName
== nsGkAtoms::_class
) {
876 mClassAttribute
.Init();
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
) {
893 nsresult
SVGElement::BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
894 const nsAttrValueOrString
* 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
912 retval
|= nsChangeHint_ReconstructFrame
;
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);
927 SVGElement::IsAttributeMapped(const nsAtom
* name
) const {
928 if (name
== nsGkAtoms::lang
) {
931 return SVGElementBase::IsAttributeMapped(name
);
934 // PresentationAttributes-FillStroke
936 const Element::MappedAttributeEntry
SVGElement::sFillStrokeMap
[] = {
938 {nsGkAtoms::fill_opacity
},
939 {nsGkAtoms::fill_rule
},
940 {nsGkAtoms::paint_order
},
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
},
952 // PresentationAttributes-Graphics
954 const Element::MappedAttributeEntry
SVGElement::sGraphicsMap
[] = {
955 {nsGkAtoms::clip_path
},
956 {nsGkAtoms::clip_rule
},
957 {nsGkAtoms::colorInterpolation
},
959 {nsGkAtoms::display
},
961 {nsGkAtoms::image_rendering
},
963 {nsGkAtoms::opacity
},
964 {nsGkAtoms::pointer_events
},
965 {nsGkAtoms::shape_rendering
},
966 {nsGkAtoms::text_rendering
},
967 {nsGkAtoms::transform_origin
},
968 {nsGkAtoms::visibility
},
971 // PresentationAttributes-TextContentElements
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
},
987 // PresentationAttributes-FontSpecification
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
997 const Element::MappedAttributeEntry
SVGElement::sGradientStopMap
[] = {
998 {nsGkAtoms::stop_color
}, {nsGkAtoms::stop_opacity
}, {nullptr}};
1000 // PresentationAttributes-Viewports
1002 const Element::MappedAttributeEntry
SVGElement::sViewportsMap
[] = {
1003 {nsGkAtoms::overflow
}, {nsGkAtoms::clip
}, {nullptr}};
1005 // PresentationAttributes-Makers
1007 const Element::MappedAttributeEntry
SVGElement::sMarkersMap
[] = {
1008 {nsGkAtoms::marker_end
},
1009 {nsGkAtoms::marker_mid
},
1010 {nsGkAtoms::marker_start
},
1013 // PresentationAttributes-Color
1015 const Element::MappedAttributeEntry
SVGElement::sColorMap
[] = {
1016 {nsGkAtoms::color
}, {nullptr}};
1018 // PresentationAttributes-Filters
1020 const Element::MappedAttributeEntry
SVGElement::sFiltersMap
[] = {
1021 {nsGkAtoms::colorInterpolationFilters
}, {nullptr}};
1023 // PresentationAttributes-feFlood
1025 const Element::MappedAttributeEntry
SVGElement::sFEFloodMap
[] = {
1026 {nsGkAtoms::flood_color
}, {nsGkAtoms::flood_opacity
}, {nullptr}};
1028 // PresentationAttributes-LightingEffects
1030 const Element::MappedAttributeEntry
SVGElement::sLightingEffectsMap
[] = {
1031 {nsGkAtoms::lighting_color
}, {nullptr}};
1033 // PresentationAttributes-mask
1035 const Element::MappedAttributeEntry
SVGElement::sMaskMap
[] = {
1036 {nsGkAtoms::mask_type
}, {nullptr}};
1038 //----------------------------------------------------------------------
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
)) {
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...
1062 SVGElement
* SVGElement::GetViewportElement() {
1063 return SVGContentUtils::GetNearestViewportElement(this);
1066 already_AddRefed
<DOMSVGAnimatedString
> SVGElement::ClassName() {
1067 return mClassAttribute
.ToDOMAnimatedString(this);
1071 bool SVGElement::UpdateDeclarationBlockFromLength(
1072 DeclarationBlock
& aBlock
, nsCSSPropertyID aPropId
,
1073 const SVGAnimatedLength
& aLength
, ValToUse aValToUse
) {
1074 aBlock
.AssertMutable();
1077 if (aValToUse
== ValToUse::Anim
) {
1078 value
= aLength
.GetAnimValInSpecifiedUnits();
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.
1087 SVGGeometryProperty::IsNonNegativeGeometryProperty(aPropId
)) {
1091 nsCSSUnit cssUnit
= SVGGeometryProperty::SpecifiedUnitTypeToCSSUnit(
1092 aLength
.GetSpecifiedUnitType());
1094 if (cssUnit
== eCSSUnit_Percent
) {
1095 Servo_DeclarationBlock_SetPercentValue(aBlock
.Raw(), aPropId
,
1098 Servo_DeclarationBlock_SetLengthValue(aBlock
.Raw(), aPropId
, value
,
1105 //------------------------------------------------------------------------
1106 // Helper class: MappedAttrParser, for parsing values of mapped attributes
1110 class MOZ_STACK_CLASS MappedAttrParser
{
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();
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() {
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
) {
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
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
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
);
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
) {
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();
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();
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
)) {
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(),
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
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
1331 if (attrValue
&& nsContentUtils::HasMutationListeners(
1332 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED
, this)) {
1333 emptyOrOldAttrValue
.SetToSerialized(*attrValue
);
1337 attrValue
? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION
)
1338 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION
);
1339 MutationObservers::NotifyAttributeWillChange(this, kNameSpaceID_None
, aName
,
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
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);
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)) {
1399 const nsAttrValue
* attrValue
= mAttrs
.GetAttr(aName
);
1400 if (!attrValue
) return;
1402 nsAutoString serializedValue
;
1403 attrValue
->ToString(serializedValue
);
1404 nsAttrValue
oldAttrValue(serializedValue
);
1406 mAttrs
.SetAndSwapAttr(aName
, oldAttrValue
, &oldValueSet
);
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);
1428 gfxMatrix
SVGElement::PrependLocalTransformsTo(const gfxMatrix
& aMatrix
,
1429 SVGTransformTypes aWhich
) const {
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
);
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
,
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
,
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
]);
1493 nsIFrame
* frame
= GetPrimaryFrame();
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
];
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;
1526 va_start(args
, aFirst
);
1528 while (f
&& i
< info
.mLengthCount
) {
1529 uint8_t type
= info
.mLengths
[i
].GetSpecifiedUnitType();
1531 if (type
!= SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
&&
1532 type
!= SVGLength_Binding::SVG_LENGTHTYPE_PX
)
1535 if (type
== SVGLength_Binding::SVG_LENGTHTYPE_EMS
||
1536 type
== SVGLength_Binding::SVG_LENGTHTYPE_EXS
)
1537 *f
= info
.mLengths
[i
++].GetAnimValue(this);
1539 *f
= info
.mLengths
[i
++].GetAnimValue(ctx
);
1540 f
= va_arg(args
, float*);
1546 SVGElement::LengthListAttributesInfo
SVGElement::GetLengthListInfo() {
1547 return LengthListAttributesInfo(nullptr, nullptr, 0);
1550 void SVGElement::LengthListAttributesInfo::Reset(uint8_t aAttrEnum
) {
1551 mLengthLists
[aAttrEnum
].ClearBaseValue(aAttrEnum
);
1555 nsAttrValue
SVGElement::WillChangeLengthList(
1556 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1557 return WillChangeValue(GetLengthListInfo().mLengthListInfo
[aAttrEnum
].mName
,
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();
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();
1592 info
.mLengthListCount
> 0,
1593 "GetAnimatedLengthListValues on element with no length list attribs");
1595 SVGUserUnitList
* list
= aFirst
;
1599 va_start(args
, aFirst
);
1601 while (list
&& i
< info
.mLengthListCount
) {
1602 list
->Init(&(info
.mLengthLists
[i
].GetAnimValue()), this,
1603 info
.mLengthListInfo
[i
].mAxis
);
1605 list
= va_arg(args
, SVGUserUnitList
*);
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");
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
);
1630 nsAttrValue
SVGElement::WillChangeNumberList(
1631 uint8_t aAttrEnum
, const mozAutoDocUpdate
& aProofOfUpdate
) {
1632 return WillChangeValue(GetNumberListInfo().mNumberListInfo
[aAttrEnum
].mName
,
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();
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");
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");
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
,
1702 void SVGElement::DidAnimatePointList() {
1703 MOZ_ASSERT(GetPointListAttrName(), "Animating non-existent path data?");
1705 ClearAnyCachedPath();
1707 nsIFrame
* frame
= GetPrimaryFrame();
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
,
1732 void SVGElement::DidAnimatePathSegList() {
1733 MOZ_ASSERT(GetPathDataAttrName(), "Animating non-existent path data?");
1735 ClearAnyCachedPath();
1737 nsIFrame
* frame
= GetPrimaryFrame();
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,
1767 void SVGElement::DidAnimateNumber(uint8_t aAttrEnum
) {
1768 nsIFrame
* frame
= GetPrimaryFrame();
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");
1788 va_start(args
, aFirst
);
1790 while (f
&& i
< info
.mNumberCount
) {
1791 *f
= info
.mNumbers
[i
++].GetAnimValue();
1792 f
= va_arg(args
, float*);
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
,
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();
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,
1862 void SVGElement::DidAnimateInteger(uint8_t aAttrEnum
) {
1863 nsIFrame
* frame
= GetPrimaryFrame();
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
;
1883 va_start(args
, aFirst
);
1885 while (n
&& i
< info
.mIntegerCount
) {
1886 *n
= info
.mIntegers
[i
++].GetAnimValue();
1887 n
= va_arg(args
, int32_t*);
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
,
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();
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,
1955 void SVGElement::DidAnimateBoolean(uint8_t aAttrEnum
) {
1956 nsIFrame
* frame
= GetPrimaryFrame();
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,
1991 void SVGElement::DidAnimateEnum(uint8_t aAttrEnum
) {
1992 nsIFrame
* frame
= GetPrimaryFrame();
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();
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
,
2049 void SVGElement::DidAnimateViewBox() {
2050 nsIFrame
* frame
= GetPrimaryFrame();
2053 frame
->AttributeChanged(kNameSpaceID_None
, nsGkAtoms::viewBox
,
2054 MutationEvent_Binding::SMIL
);
2058 SVGAnimatedPreserveAspectRatio
* SVGElement::GetAnimatedPreserveAspectRatio() {
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
,
2084 void SVGElement::DidAnimatePreserveAspectRatio() {
2085 nsIFrame
* frame
= GetPrimaryFrame();
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(),
2110 DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue
, newValue
,
2114 void SVGElement::DidAnimateTransformList(int32_t aModType
) {
2115 MOZ_ASSERT(GetTransformListAttrName(),
2116 "Animating non-existent transform data?");
2118 nsIFrame
* frame
= GetPrimaryFrame();
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
);
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();
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
) {
2192 if (aIsConditionalProcessingAttribute
) {
2193 nsCOMPtr
<SVGTests
> tests(do_QueryInterface(this));
2194 name
= tests
->GetAttrName(aAttrEnum
);
2196 name
= GetStringListInfo().mStringListInfo
[aAttrEnum
].mName
;
2198 return WillChangeValue(name
, aProofOfUpdate
);
2201 void SVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute
,
2203 const nsAttrValue
& aEmptyOrOldValue
,
2204 const mozAutoDocUpdate
& aProofOfUpdate
) {
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
);
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();
2236 nsresult
SVGElement::ReportAttributeParseFailure(Document
* aDocument
,
2238 const nsAString
& aValue
) {
2239 AutoTArray
<nsString
, 2> strings
;
2240 strings
.AppendElement(nsDependentAtomString(aAttribute
));
2241 strings
.AppendElement(aValue
);
2242 return SVGContentUtils::ReportToConsole(aDocument
, "AttributeParseWarning",
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()) {
2256 nsAtom
* attr
= name
->Atom();
2257 if (!IsEventAttributeName(attr
)) {
2262 GetAttr(attr
, value
);
2263 SetEventHandler(GetEventNameForAttr(attr
), value
, true);
2267 UniquePtr
<SMILAttr
> SVGElement::GetAnimatedAttr(int32_t aNamespaceID
,
2269 if (aNamespaceID
== kNameSpaceID_None
) {
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);
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);
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);
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);
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);
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);
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);
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);
2351 if (aName
== nsGkAtoms::orient
) {
2352 SVGAnimatedOrient
* orient
= GetAnimatedOrient();
2353 return orient
? orient
->ToSMILAttr(this) : nullptr;
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)
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
));
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
);
2396 if (GetPointListAttrName() == aName
) {
2397 SVGAnimatedPointList
* pointList
= GetAnimatedPointList();
2399 return pointList
->ToSMILAttr(this);
2406 if (GetPathDataAttrName() == aName
) {
2407 SVGAnimatedPathSegList
* segList
= GetAnimPathSegList();
2409 return segList
->ToSMILAttr(this);
2414 if (aName
== nsGkAtoms::_class
) {
2415 return mClassAttribute
.ToSMILAttr(this);
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);
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
);
2463 } // namespace mozilla