Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / svg / SVGElement.cpp
blobb4ec81a81ebcca57a21286db0546b51732203023
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/SVGElement.h"
9 #include "mozilla/AlreadyAddRefed.h"
10 #include "mozilla/dom/MutationEventBinding.h"
11 #include "mozilla/dom/MutationObservers.h"
12 #include "mozilla/dom/CSSRuleBinding.h"
13 #include "mozilla/dom/SVGElementBinding.h"
14 #include "mozilla/dom/SVGGeometryElement.h"
15 #include "mozilla/dom/SVGLengthBinding.h"
16 #include "mozilla/dom/SVGSVGElement.h"
17 #include "mozilla/dom/SVGTests.h"
18 #include "mozilla/dom/SVGUnitTypesBinding.h"
19 #include "mozilla/dom/Element.h"
21 #include "mozilla/ArrayUtils.h"
22 #include "mozilla/DebugOnly.h"
23 #include "mozilla/DeclarationBlock.h"
24 #include "mozilla/EventListenerManager.h"
25 #include "mozilla/InternalMutationEvent.h"
26 #include "mozilla/PresShell.h"
27 #include "mozilla/RestyleManager.h"
28 #include "mozilla/SMILAnimationController.h"
29 #include "mozilla/StaticPrefs_layout.h"
30 #include "mozilla/SVGContentUtils.h"
31 #include "mozilla/SVGObserverUtils.h"
32 #include "mozilla/Unused.h"
34 #include "mozAutoDocUpdate.h"
35 #include "nsAttrValueOrString.h"
36 #include "nsCSSProps.h"
37 #include "nsCSSValue.h"
38 #include "nsContentUtils.h"
39 #include "nsDOMCSSAttrDeclaration.h"
40 #include "nsICSSDeclaration.h"
41 #include "nsIContentInlines.h"
42 #include "mozilla/dom/Document.h"
43 #include "nsError.h"
44 #include "nsGkAtoms.h"
45 #include "nsIFrame.h"
46 #include "nsQueryObject.h"
47 #include "nsLayoutUtils.h"
48 #include "SVGAnimatedNumberList.h"
49 #include "SVGAnimatedLengthList.h"
50 #include "SVGAnimatedPointList.h"
51 #include "SVGAnimatedPathSegList.h"
52 #include "SVGAnimatedTransformList.h"
53 #include "SVGAnimatedBoolean.h"
54 #include "SVGAnimatedEnumeration.h"
55 #include "SVGAnimatedInteger.h"
56 #include "SVGAnimatedIntegerPair.h"
57 #include "SVGAnimatedLength.h"
58 #include "SVGAnimatedNumber.h"
59 #include "SVGAnimatedNumberPair.h"
60 #include "SVGAnimatedOrient.h"
61 #include "SVGAnimatedString.h"
62 #include "SVGAnimatedViewBox.h"
63 #include "SVGGeometryProperty.h"
64 #include "SVGMotionSMILAttr.h"
65 #include <stdarg.h>
67 // This is needed to ensure correct handling of calls to the
68 // vararg-list methods in this file:
69 // SVGElement::GetAnimated{Length,Number,Integer}Values
70 // See bug 547964 for details:
71 static_assert(sizeof(void*) == sizeof(nullptr),
72 "nullptr should be the correct size");
74 nsresult NS_NewSVGElement(
75 mozilla::dom::Element** aResult,
76 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) {
77 RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
78 auto* nim = nodeInfo->NodeInfoManager();
79 RefPtr<mozilla::dom::SVGElement> it =
80 new (nim) mozilla::dom::SVGElement(nodeInfo.forget());
81 nsresult rv = it->Init();
83 if (NS_FAILED(rv)) {
84 return rv;
87 it.forget(aResult);
88 return rv;
91 namespace mozilla::dom {
92 using namespace SVGUnitTypes_Binding;
94 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGElement)
96 // Use the CC variant of this, even though this class does not define
97 // a new CC participant, to make QIing to the CC interfaces faster.
98 NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(SVGElement, SVGElementBase,
99 SVGElement)
101 SVGEnumMapping SVGElement::sSVGUnitTypesMap[] = {
102 {nsGkAtoms::userSpaceOnUse, SVG_UNIT_TYPE_USERSPACEONUSE},
103 {nsGkAtoms::objectBoundingBox, SVG_UNIT_TYPE_OBJECTBOUNDINGBOX},
104 {nullptr, 0}};
106 SVGElement::SVGElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
107 : SVGElementBase(std::move(aNodeInfo)) {}
109 SVGElement::~SVGElement() = default;
111 JSObject* SVGElement::WrapNode(JSContext* aCx,
112 JS::Handle<JSObject*> aGivenProto) {
113 return SVGElement_Binding::Wrap(aCx, this, aGivenProto);
116 template <typename Value, typename Info>
117 void SVGElement::AttributesInfo<Value, Info>::ResetAll() {
118 for (uint32_t i = 0; i < mCount; ++i) {
119 Reset(i);
123 template <typename Value, typename Info>
124 void SVGElement::AttributesInfo<Value, Info>::CopyAllFrom(
125 const AttributesInfo& aOther) {
126 MOZ_DIAGNOSTIC_ASSERT(mCount == aOther.mCount,
127 "Should only be called on clones");
128 for (uint32_t i = 0; i < mCount; ++i) {
129 mValues[i] = aOther.mValues[i];
133 template <>
134 void SVGElement::LengthAttributesInfo::Reset(uint8_t aAttrEnum) {
135 mValues[aAttrEnum].Init(mInfos[aAttrEnum].mCtxType, aAttrEnum,
136 mInfos[aAttrEnum].mDefaultValue,
137 mInfos[aAttrEnum].mDefaultUnitType);
140 template <>
141 void SVGElement::LengthListAttributesInfo::Reset(uint8_t aAttrEnum) {
142 mValues[aAttrEnum].ClearBaseValue(aAttrEnum);
143 // caller notifies
146 template <>
147 void SVGElement::NumberListAttributesInfo::Reset(uint8_t aAttrEnum) {
148 MOZ_ASSERT(aAttrEnum < mCount, "Bad attr enum");
149 mValues[aAttrEnum].ClearBaseValue(aAttrEnum);
150 // caller notifies
153 template <>
154 void SVGElement::NumberAttributesInfo::Reset(uint8_t aAttrEnum) {
155 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
158 template <>
159 void SVGElement::NumberPairAttributesInfo::Reset(uint8_t aAttrEnum) {
160 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue1,
161 mInfos[aAttrEnum].mDefaultValue2);
164 template <>
165 void SVGElement::IntegerAttributesInfo::Reset(uint8_t aAttrEnum) {
166 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
169 template <>
170 void SVGElement::IntegerPairAttributesInfo::Reset(uint8_t aAttrEnum) {
171 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue1,
172 mInfos[aAttrEnum].mDefaultValue2);
175 template <>
176 void SVGElement::BooleanAttributesInfo::Reset(uint8_t aAttrEnum) {
177 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
180 template <>
181 void SVGElement::StringListAttributesInfo::Reset(uint8_t aAttrEnum) {
182 mValues[aAttrEnum].Clear();
183 // caller notifies
186 template <>
187 void SVGElement::EnumAttributesInfo::Reset(uint8_t aAttrEnum) {
188 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
191 template <>
192 void SVGElement::StringAttributesInfo::Reset(uint8_t aAttrEnum) {
193 mValues[aAttrEnum].Init(aAttrEnum);
196 nsresult SVGElement::CopyInnerTo(mozilla::dom::Element* aDest) {
197 nsresult rv = Element::CopyInnerTo(aDest);
198 NS_ENSURE_SUCCESS(rv, rv);
200 auto* dest = static_cast<SVGElement*>(aDest);
202 // cloning a node must retain its internal nonce slot
203 if (auto* nonce = static_cast<nsString*>(GetProperty(nsGkAtoms::nonce))) {
204 dest->SetNonce(*nonce);
207 // If our destination is a print document, copy all the relevant length values
208 // etc so that they match the state of the original node.
209 if (aDest->OwnerDoc()->IsStaticDocument() ||
210 aDest->OwnerDoc()->CloningForSVGUse()) {
211 LengthAttributesInfo lengthInfo = GetLengthInfo();
212 dest->GetLengthInfo().CopyAllFrom(lengthInfo);
213 if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) {
214 for (uint32_t i = 0; i < lengthInfo.mCount; i++) {
215 nsCSSPropertyID propId =
216 SVGGeometryProperty::AttrEnumToCSSPropId(this, i);
218 // We don't map use element width/height currently. We can remove this
219 // test when we do.
220 if (propId != eCSSProperty_UNKNOWN &&
221 lengthInfo.mValues[i].IsAnimated()) {
222 dest->SMILOverrideStyle()->SetSMILValue(propId,
223 lengthInfo.mValues[i]);
227 dest->GetNumberInfo().CopyAllFrom(GetNumberInfo());
228 dest->GetNumberPairInfo().CopyAllFrom(GetNumberPairInfo());
229 dest->GetIntegerInfo().CopyAllFrom(GetIntegerInfo());
230 dest->GetIntegerPairInfo().CopyAllFrom(GetIntegerPairInfo());
231 dest->GetBooleanInfo().CopyAllFrom(GetBooleanInfo());
232 if (const auto* orient = GetAnimatedOrient()) {
233 *dest->GetAnimatedOrient() = *orient;
235 if (const auto* viewBox = GetAnimatedViewBox()) {
236 *dest->GetAnimatedViewBox() = *viewBox;
238 if (const auto* preserveAspectRatio = GetAnimatedPreserveAspectRatio()) {
239 *dest->GetAnimatedPreserveAspectRatio() = *preserveAspectRatio;
241 dest->GetEnumInfo().CopyAllFrom(GetEnumInfo());
242 dest->GetStringInfo().CopyAllFrom(GetStringInfo());
243 dest->GetLengthListInfo().CopyAllFrom(GetLengthListInfo());
244 dest->GetNumberListInfo().CopyAllFrom(GetNumberListInfo());
245 if (const auto* pointList = GetAnimatedPointList()) {
246 *dest->GetAnimatedPointList() = *pointList;
248 if (const auto* pathSegList = GetAnimPathSegList()) {
249 *dest->GetAnimPathSegList() = *pathSegList;
250 if (pathSegList->IsAnimating()) {
251 dest->SMILOverrideStyle()->SetSMILValue(eCSSProperty_d, *pathSegList);
254 if (const auto* transformList = GetAnimatedTransformList()) {
255 *dest->GetAnimatedTransformList(DO_ALLOCATE) = *transformList;
257 if (const auto* animateMotionTransform = GetAnimateMotionTransform()) {
258 dest->SetAnimateMotionTransform(animateMotionTransform);
260 if (const auto* smilOverrideStyleDecoration =
261 GetSMILOverrideStyleDeclaration()) {
262 RefPtr<DeclarationBlock> declClone = smilOverrideStyleDecoration->Clone();
263 declClone->SetDirty();
264 dest->SetSMILOverrideStyleDeclaration(*declClone);
268 return NS_OK;
271 //----------------------------------------------------------------------
272 // SVGElement methods
274 void SVGElement::DidAnimateClass() {
275 // For Servo, snapshot the element before we change it.
276 PresShell* presShell = OwnerDoc()->GetPresShell();
277 if (presShell) {
278 if (nsPresContext* presContext = presShell->GetPresContext()) {
279 presContext->RestyleManager()->ClassAttributeWillBeChangedBySMIL(this);
283 nsAutoString src;
284 mClassAttribute.GetAnimValue(src, this);
285 if (!mClassAnimAttr) {
286 mClassAnimAttr = MakeUnique<nsAttrValue>();
288 mClassAnimAttr->ParseAtomArray(src);
290 // FIXME(emilio): This re-selector-matches, but we do the snapshot stuff right
291 // above... Is this needed anymore?
292 if (presShell) {
293 presShell->RestyleForAnimation(this, RestyleHint::RESTYLE_SELF);
295 DidAnimateAttribute(kNameSpaceID_None, nsGkAtoms::_class);
298 nsresult SVGElement::Init() {
299 // Set up length attributes - can't do this in the constructor
300 // because we can't do a virtual call at that point
302 GetLengthInfo().ResetAll();
303 GetNumberInfo().ResetAll();
304 GetNumberPairInfo().ResetAll();
305 GetIntegerInfo().ResetAll();
306 GetIntegerPairInfo().ResetAll();
307 GetBooleanInfo().ResetAll();
308 GetEnumInfo().ResetAll();
310 if (SVGAnimatedOrient* orient = GetAnimatedOrient()) {
311 orient->Init();
314 if (SVGAnimatedViewBox* viewBox = GetAnimatedViewBox()) {
315 viewBox->Init();
318 if (SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
319 GetAnimatedPreserveAspectRatio()) {
320 preserveAspectRatio->Init();
323 GetLengthListInfo().ResetAll();
324 GetNumberListInfo().ResetAll();
326 // No need to reset SVGPointList since the default value is always the same
327 // (an empty list).
329 // No need to reset SVGPathData since the default value is always the same
330 // (an empty list).
332 GetStringInfo().ResetAll();
333 return NS_OK;
336 //----------------------------------------------------------------------
337 // Implementation
339 //----------------------------------------------------------------------
340 // nsIContent methods
342 nsresult SVGElement::BindToTree(BindContext& aContext, nsINode& aParent) {
343 nsresult rv = SVGElementBase::BindToTree(aContext, aParent);
344 NS_ENSURE_SUCCESS(rv, rv);
346 // Hide any nonce from the DOM, but keep the internal value of the
347 // nonce by copying and resetting the internal nonce value.
348 if (HasFlag(NODE_HAS_NONCE_AND_HEADER_CSP) && IsInComposedDoc() &&
349 OwnerDoc()->GetBrowsingContext()) {
350 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
351 "SVGElement::ResetNonce::Runnable",
352 [self = RefPtr<SVGElement>(this)]() {
353 nsAutoString nonce;
354 self->GetNonce(nonce);
355 self->SetAttr(kNameSpaceID_None, nsGkAtoms::nonce, u""_ns, true);
356 self->SetNonce(nonce);
357 }));
360 return NS_OK;
363 void SVGElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
364 const nsAttrValue* aValue,
365 const nsAttrValue* aOldValue,
366 nsIPrincipal* aSubjectPrincipal, bool aNotify) {
367 if (IsEventAttributeName(aName) && aValue) {
368 MOZ_ASSERT(aValue->Type() == nsAttrValue::eString,
369 "Expected string value for script body");
370 SetEventHandler(GetEventNameForAttr(aName), aValue->GetStringValue());
373 // The nonce will be copied over to an internal slot and cleared from the
374 // Element within BindToTree to avoid CSS Selector nonce exfiltration if
375 // the CSP list contains a header-delivered CSP.
376 if (nsGkAtoms::nonce == aName && kNameSpaceID_None == aNamespaceID) {
377 if (aValue) {
378 SetNonce(aValue->GetStringValue());
379 if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) {
380 SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP);
382 } else {
383 RemoveNonce();
387 return SVGElementBase::AfterSetAttr(aNamespaceID, aName, aValue, aOldValue,
388 aSubjectPrincipal, aNotify);
391 bool SVGElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
392 const nsAString& aValue,
393 nsIPrincipal* aMaybeScriptedPrincipal,
394 nsAttrValue& aResult) {
395 nsresult rv = NS_OK;
396 bool foundMatch = false;
397 bool didSetResult = false;
399 if (aNamespaceID == kNameSpaceID_None) {
400 // Check for SVGAnimatedLength attribute
401 LengthAttributesInfo lengthInfo = GetLengthInfo();
403 uint32_t i;
404 for (i = 0; i < lengthInfo.mCount; i++) {
405 if (aAttribute == lengthInfo.mInfos[i].mName) {
406 rv = lengthInfo.mValues[i].SetBaseValueString(aValue, this, false);
407 if (NS_FAILED(rv)) {
408 lengthInfo.Reset(i);
409 } else {
410 aResult.SetTo(lengthInfo.mValues[i], &aValue);
411 didSetResult = true;
413 foundMatch = true;
414 break;
418 if (!foundMatch) {
419 // Check for SVGAnimatedLengthList attribute
420 LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
421 for (i = 0; i < lengthListInfo.mCount; i++) {
422 if (aAttribute == lengthListInfo.mInfos[i].mName) {
423 rv = lengthListInfo.mValues[i].SetBaseValueString(aValue);
424 if (NS_FAILED(rv)) {
425 lengthListInfo.Reset(i);
426 } else {
427 aResult.SetTo(lengthListInfo.mValues[i].GetBaseValue(), &aValue);
428 didSetResult = true;
430 foundMatch = true;
431 break;
436 if (!foundMatch) {
437 // Check for SVGAnimatedNumberList attribute
438 NumberListAttributesInfo numberListInfo = GetNumberListInfo();
439 for (i = 0; i < numberListInfo.mCount; i++) {
440 if (aAttribute == numberListInfo.mInfos[i].mName) {
441 rv = numberListInfo.mValues[i].SetBaseValueString(aValue);
442 if (NS_FAILED(rv)) {
443 numberListInfo.Reset(i);
444 } else {
445 aResult.SetTo(numberListInfo.mValues[i].GetBaseValue(), &aValue);
446 didSetResult = true;
448 foundMatch = true;
449 break;
454 if (!foundMatch) {
455 // Check for SVGAnimatedPointList attribute
456 if (GetPointListAttrName() == aAttribute) {
457 if (SVGAnimatedPointList* pointList = GetAnimatedPointList()) {
458 pointList->SetBaseValueString(aValue);
459 // The spec says we parse everything up to the failure, so we DON'T
460 // need to check the result of SetBaseValueString or call
461 // pointList->ClearBaseValue() if it fails
462 aResult.SetTo(pointList->GetBaseValue(), &aValue);
463 didSetResult = true;
464 foundMatch = true;
469 if (!foundMatch) {
470 // Check for SVGAnimatedPathSegList attribute
471 if (GetPathDataAttrName() == aAttribute) {
472 if (SVGAnimatedPathSegList* segList = GetAnimPathSegList()) {
473 segList->SetBaseValueString(aValue);
474 // The spec says we parse everything up to the failure, so we DON'T
475 // need to check the result of SetBaseValueString or call
476 // segList->ClearBaseValue() if it fails
477 aResult.SetTo(segList->GetBaseValue(), &aValue);
478 didSetResult = true;
479 foundMatch = true;
484 if (!foundMatch) {
485 // Check for SVGAnimatedNumber attribute
486 NumberAttributesInfo numberInfo = GetNumberInfo();
487 for (i = 0; i < numberInfo.mCount; i++) {
488 if (aAttribute == numberInfo.mInfos[i].mName) {
489 rv = numberInfo.mValues[i].SetBaseValueString(aValue, this);
490 if (NS_FAILED(rv)) {
491 numberInfo.Reset(i);
492 } else {
493 aResult.SetTo(numberInfo.mValues[i].GetBaseValue(), &aValue);
494 didSetResult = true;
496 foundMatch = true;
497 break;
502 if (!foundMatch) {
503 // Check for SVGAnimatedNumberPair attribute
504 NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo();
505 for (i = 0; i < numberPairInfo.mCount; i++) {
506 if (aAttribute == numberPairInfo.mInfos[i].mName) {
507 rv = numberPairInfo.mValues[i].SetBaseValueString(aValue, this);
508 if (NS_FAILED(rv)) {
509 numberPairInfo.Reset(i);
510 } else {
511 aResult.SetTo(numberPairInfo.mValues[i], &aValue);
512 didSetResult = true;
514 foundMatch = true;
515 break;
520 if (!foundMatch) {
521 // Check for SVGAnimatedInteger attribute
522 IntegerAttributesInfo integerInfo = GetIntegerInfo();
523 for (i = 0; i < integerInfo.mCount; i++) {
524 if (aAttribute == integerInfo.mInfos[i].mName) {
525 rv = integerInfo.mValues[i].SetBaseValueString(aValue, this);
526 if (NS_FAILED(rv)) {
527 integerInfo.Reset(i);
528 } else {
529 aResult.SetTo(integerInfo.mValues[i].GetBaseValue(), &aValue);
530 didSetResult = true;
532 foundMatch = true;
533 break;
538 if (!foundMatch) {
539 // Check for SVGAnimatedIntegerPair attribute
540 IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo();
541 for (i = 0; i < integerPairInfo.mCount; i++) {
542 if (aAttribute == integerPairInfo.mInfos[i].mName) {
543 rv = integerPairInfo.mValues[i].SetBaseValueString(aValue, this);
544 if (NS_FAILED(rv)) {
545 integerPairInfo.Reset(i);
546 } else {
547 aResult.SetTo(integerPairInfo.mValues[i], &aValue);
548 didSetResult = true;
550 foundMatch = true;
551 break;
556 if (!foundMatch) {
557 // Check for SVGAnimatedBoolean attribute
558 BooleanAttributesInfo booleanInfo = GetBooleanInfo();
559 for (i = 0; i < booleanInfo.mCount; i++) {
560 if (aAttribute == booleanInfo.mInfos[i].mName) {
561 nsAtom* valAtom = NS_GetStaticAtom(aValue);
562 rv = valAtom ? booleanInfo.mValues[i].SetBaseValueAtom(valAtom, this)
563 : NS_ERROR_DOM_SYNTAX_ERR;
564 if (NS_FAILED(rv)) {
565 booleanInfo.Reset(i);
566 } else {
567 aResult.SetTo(valAtom);
568 didSetResult = true;
570 foundMatch = true;
571 break;
576 if (!foundMatch) {
577 // Check for SVGAnimatedEnumeration attribute
578 EnumAttributesInfo enumInfo = GetEnumInfo();
579 for (i = 0; i < enumInfo.mCount; i++) {
580 if (aAttribute == enumInfo.mInfos[i].mName) {
581 RefPtr<nsAtom> valAtom = NS_Atomize(aValue);
582 if (!enumInfo.mValues[i].SetBaseValueAtom(valAtom, this)) {
583 // Exact error value does not matter; we just need to mark the
584 // parse as failed.
585 rv = NS_ERROR_FAILURE;
586 enumInfo.Reset(i);
587 } else {
588 aResult.SetTo(valAtom);
589 didSetResult = true;
591 foundMatch = true;
592 break;
597 if (!foundMatch) {
598 // Check for conditional processing attributes
599 nsCOMPtr<SVGTests> tests = do_QueryObject(this);
600 if (tests && tests->ParseConditionalProcessingAttribute(
601 aAttribute, aValue, aResult)) {
602 foundMatch = true;
606 if (!foundMatch) {
607 // Check for StringList attribute
608 StringListAttributesInfo stringListInfo = GetStringListInfo();
609 for (i = 0; i < stringListInfo.mCount; i++) {
610 if (aAttribute == stringListInfo.mInfos[i].mName) {
611 rv = stringListInfo.mValues[i].SetValue(aValue);
612 if (NS_FAILED(rv)) {
613 stringListInfo.Reset(i);
614 } else {
615 aResult.SetTo(stringListInfo.mValues[i], &aValue);
616 didSetResult = true;
618 foundMatch = true;
619 break;
624 if (!foundMatch) {
625 // Check for orient attribute
626 if (aAttribute == nsGkAtoms::orient) {
627 SVGAnimatedOrient* orient = GetAnimatedOrient();
628 if (orient) {
629 rv = orient->SetBaseValueString(aValue, this, false);
630 if (NS_FAILED(rv)) {
631 orient->Init();
632 } else {
633 aResult.SetTo(*orient, &aValue);
634 didSetResult = true;
636 foundMatch = true;
638 // Check for viewBox attribute
639 } else if (aAttribute == nsGkAtoms::viewBox) {
640 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
641 if (viewBox) {
642 rv = viewBox->SetBaseValueString(aValue, this, false);
643 if (NS_FAILED(rv)) {
644 viewBox->Init();
645 } else {
646 aResult.SetTo(*viewBox, &aValue);
647 didSetResult = true;
649 foundMatch = true;
651 // Check for preserveAspectRatio attribute
652 } else if (aAttribute == nsGkAtoms::preserveAspectRatio) {
653 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
654 GetAnimatedPreserveAspectRatio();
655 if (preserveAspectRatio) {
656 rv = preserveAspectRatio->SetBaseValueString(aValue, this, false);
657 if (NS_FAILED(rv)) {
658 preserveAspectRatio->Init();
659 } else {
660 aResult.SetTo(*preserveAspectRatio, &aValue);
661 didSetResult = true;
663 foundMatch = true;
665 // Check for SVGAnimatedTransformList attribute
666 } else if (GetTransformListAttrName() == aAttribute) {
667 // The transform attribute is being set, so we must ensure that the
668 // SVGAnimatedTransformList is/has been allocated:
669 SVGAnimatedTransformList* transformList =
670 GetAnimatedTransformList(DO_ALLOCATE);
671 rv = transformList->SetBaseValueString(aValue, this);
672 if (NS_FAILED(rv)) {
673 transformList->ClearBaseValue();
674 } else {
675 aResult.SetTo(transformList->GetBaseValue(), &aValue);
676 didSetResult = true;
678 foundMatch = true;
679 } else if (aAttribute == nsGkAtoms::tabindex) {
680 didSetResult = aResult.ParseIntValue(aValue);
681 foundMatch = true;
685 if (aAttribute == nsGkAtoms::_class) {
686 mClassAttribute.SetBaseValue(aValue, this, false);
687 aResult.ParseAtomArray(aValue);
688 return true;
691 if (aAttribute == nsGkAtoms::rel) {
692 aResult.ParseAtomArray(aValue);
693 return true;
697 if (!foundMatch) {
698 // Check for SVGAnimatedString attribute
699 StringAttributesInfo stringInfo = GetStringInfo();
700 for (uint32_t i = 0; i < stringInfo.mCount; i++) {
701 if (aNamespaceID == stringInfo.mInfos[i].mNamespaceID &&
702 aAttribute == stringInfo.mInfos[i].mName) {
703 stringInfo.mValues[i].SetBaseValue(aValue, this, false);
704 foundMatch = true;
705 break;
710 if (foundMatch) {
711 if (NS_FAILED(rv)) {
712 ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue);
713 return false;
715 if (!didSetResult) {
716 aResult.SetTo(aValue);
718 return true;
721 return SVGElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
722 aMaybeScriptedPrincipal, aResult);
725 void SVGElement::UnsetAttrInternal(int32_t aNamespaceID, nsAtom* aName,
726 bool aNotify) {
727 // XXXbz there's a bunch of redundancy here with AfterSetAttr.
728 // Maybe consolidate?
730 if (aNamespaceID == kNameSpaceID_None) {
731 if (IsEventAttributeName(aName)) {
732 EventListenerManager* manager = GetExistingListenerManager();
733 if (manager) {
734 nsAtom* eventName = GetEventNameForAttr(aName);
735 manager->RemoveEventHandler(eventName);
737 return;
740 // Check if this is a length attribute going away
741 LengthAttributesInfo lenInfo = GetLengthInfo();
743 for (uint32_t i = 0; i < lenInfo.mCount; i++) {
744 if (aName == lenInfo.mInfos[i].mName) {
745 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
746 lenInfo.Reset(i);
747 return;
751 // Check if this is a length list attribute going away
752 LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
754 for (uint32_t i = 0; i < lengthListInfo.mCount; i++) {
755 if (aName == lengthListInfo.mInfos[i].mName) {
756 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
757 lengthListInfo.Reset(i);
758 return;
762 // Check if this is a number list attribute going away
763 NumberListAttributesInfo numberListInfo = GetNumberListInfo();
765 for (uint32_t i = 0; i < numberListInfo.mCount; i++) {
766 if (aName == numberListInfo.mInfos[i].mName) {
767 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
768 numberListInfo.Reset(i);
769 return;
773 // Check if this is a point list attribute going away
774 if (GetPointListAttrName() == aName) {
775 SVGAnimatedPointList* pointList = GetAnimatedPointList();
776 if (pointList) {
777 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
778 pointList->ClearBaseValue();
779 return;
783 // Check if this is a path segment list attribute going away
784 if (GetPathDataAttrName() == aName) {
785 SVGAnimatedPathSegList* segList = GetAnimPathSegList();
786 if (segList) {
787 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
788 segList->ClearBaseValue();
789 return;
793 // Check if this is a number attribute going away
794 NumberAttributesInfo numInfo = GetNumberInfo();
796 for (uint32_t i = 0; i < numInfo.mCount; i++) {
797 if (aName == numInfo.mInfos[i].mName) {
798 numInfo.Reset(i);
799 return;
803 // Check if this is a number pair attribute going away
804 NumberPairAttributesInfo numPairInfo = GetNumberPairInfo();
806 for (uint32_t i = 0; i < numPairInfo.mCount; i++) {
807 if (aName == numPairInfo.mInfos[i].mName) {
808 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
809 numPairInfo.Reset(i);
810 return;
814 // Check if this is an integer attribute going away
815 IntegerAttributesInfo intInfo = GetIntegerInfo();
817 for (uint32_t i = 0; i < intInfo.mCount; i++) {
818 if (aName == intInfo.mInfos[i].mName) {
819 intInfo.Reset(i);
820 return;
824 // Check if this is an integer pair attribute going away
825 IntegerPairAttributesInfo intPairInfo = GetIntegerPairInfo();
827 for (uint32_t i = 0; i < intPairInfo.mCount; i++) {
828 if (aName == intPairInfo.mInfos[i].mName) {
829 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
830 intPairInfo.Reset(i);
831 return;
835 // Check if this is a boolean attribute going away
836 BooleanAttributesInfo boolInfo = GetBooleanInfo();
838 for (uint32_t i = 0; i < boolInfo.mCount; i++) {
839 if (aName == boolInfo.mInfos[i].mName) {
840 boolInfo.Reset(i);
841 return;
845 // Check if this is an enum attribute going away
846 EnumAttributesInfo enumInfo = GetEnumInfo();
848 for (uint32_t i = 0; i < enumInfo.mCount; i++) {
849 if (aName == enumInfo.mInfos[i].mName) {
850 enumInfo.Reset(i);
851 return;
855 // Check if this is an orient attribute going away
856 if (aName == nsGkAtoms::orient) {
857 SVGAnimatedOrient* orient = GetAnimatedOrient();
858 if (orient) {
859 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
860 orient->Init();
861 return;
865 // Check if this is a viewBox attribute going away
866 if (aName == nsGkAtoms::viewBox) {
867 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
868 if (viewBox) {
869 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
870 viewBox->Init();
871 return;
875 // Check if this is a preserveAspectRatio attribute going away
876 if (aName == nsGkAtoms::preserveAspectRatio) {
877 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
878 GetAnimatedPreserveAspectRatio();
879 if (preserveAspectRatio) {
880 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
881 preserveAspectRatio->Init();
882 return;
886 // Check if this is a transform list attribute going away
887 if (GetTransformListAttrName() == aName) {
888 SVGAnimatedTransformList* transformList = GetAnimatedTransformList();
889 if (transformList) {
890 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
891 transformList->ClearBaseValue();
892 return;
896 // Check for conditional processing attributes
897 nsCOMPtr<SVGTests> tests = do_QueryObject(this);
898 if (tests && tests->IsConditionalProcessingAttribute(aName)) {
899 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
900 tests->UnsetAttr(aName);
901 return;
904 // Check if this is a string list attribute going away
905 StringListAttributesInfo stringListInfo = GetStringListInfo();
907 for (uint32_t i = 0; i < stringListInfo.mCount; i++) {
908 if (aName == stringListInfo.mInfos[i].mName) {
909 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
910 stringListInfo.Reset(i);
911 return;
915 if (aName == nsGkAtoms::_class) {
916 mClassAttribute.Init();
917 return;
921 // Check if this is a string attribute going away
922 StringAttributesInfo stringInfo = GetStringInfo();
924 for (uint32_t i = 0; i < stringInfo.mCount; i++) {
925 if (aNamespaceID == stringInfo.mInfos[i].mNamespaceID &&
926 aName == stringInfo.mInfos[i].mName) {
927 stringInfo.Reset(i);
928 return;
933 void SVGElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
934 const nsAttrValue* aValue, bool aNotify) {
935 if (!aValue) {
936 UnsetAttrInternal(aNamespaceID, aName, aNotify);
938 return SVGElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify);
941 nsChangeHint SVGElement::GetAttributeChangeHint(const nsAtom* aAttribute,
942 int32_t aModType) const {
943 nsChangeHint retval =
944 SVGElementBase::GetAttributeChangeHint(aAttribute, aModType);
946 nsCOMPtr<SVGTests> tests = do_QueryObject(const_cast<SVGElement*>(this));
947 if (tests && tests->IsConditionalProcessingAttribute(aAttribute)) {
948 // It would be nice to only reconstruct the frame if the value returned by
949 // SVGTests::PassesConditionalProcessingTests has changed, but we don't
950 // know that
951 retval |= nsChangeHint_ReconstructFrame;
953 return retval;
956 void SVGElement::NodeInfoChanged(Document* aOldDoc) {
957 SVGElementBase::NodeInfoChanged(aOldDoc);
960 NS_IMETHODIMP_(bool)
961 SVGElement::IsAttributeMapped(const nsAtom* name) const {
962 if (name == nsGkAtoms::lang) {
963 return true;
966 if (IsSVGAnimationElement()) {
967 return SVGElementBase::IsAttributeMapped(name);
970 static const MappedAttributeEntry attributes[] = {
971 // Properties that we don't support are commented out.
972 // { nsGkAtoms::alignment_baseline },
973 // { nsGkAtoms::baseline_shift },
974 {nsGkAtoms::clip},
975 {nsGkAtoms::clip_path},
976 {nsGkAtoms::clip_rule},
977 {nsGkAtoms::color},
978 {nsGkAtoms::colorInterpolation},
979 {nsGkAtoms::colorInterpolationFilters},
980 {nsGkAtoms::cursor},
981 {nsGkAtoms::direction},
982 {nsGkAtoms::display},
983 {nsGkAtoms::dominant_baseline},
984 {nsGkAtoms::fill},
985 {nsGkAtoms::fill_opacity},
986 {nsGkAtoms::fill_rule},
987 {nsGkAtoms::filter},
988 {nsGkAtoms::flood_color},
989 {nsGkAtoms::flood_opacity},
990 {nsGkAtoms::font_family},
991 {nsGkAtoms::font_size},
992 {nsGkAtoms::font_size_adjust},
993 {nsGkAtoms::font_stretch},
994 {nsGkAtoms::font_style},
995 {nsGkAtoms::font_variant},
996 {nsGkAtoms::fontWeight},
997 {nsGkAtoms::image_rendering},
998 {nsGkAtoms::letter_spacing},
999 {nsGkAtoms::lighting_color},
1000 {nsGkAtoms::marker_end},
1001 {nsGkAtoms::marker_mid},
1002 {nsGkAtoms::marker_start},
1003 {nsGkAtoms::mask},
1004 {nsGkAtoms::mask_type},
1005 {nsGkAtoms::opacity},
1006 {nsGkAtoms::overflow},
1007 {nsGkAtoms::paint_order},
1008 {nsGkAtoms::pointer_events},
1009 {nsGkAtoms::shape_rendering},
1010 {nsGkAtoms::stop_color},
1011 {nsGkAtoms::stop_opacity},
1012 {nsGkAtoms::stroke},
1013 {nsGkAtoms::stroke_dasharray},
1014 {nsGkAtoms::stroke_dashoffset},
1015 {nsGkAtoms::stroke_linecap},
1016 {nsGkAtoms::stroke_linejoin},
1017 {nsGkAtoms::stroke_miterlimit},
1018 {nsGkAtoms::stroke_opacity},
1019 {nsGkAtoms::stroke_width},
1020 {nsGkAtoms::text_anchor},
1021 {nsGkAtoms::text_decoration},
1022 {nsGkAtoms::text_rendering},
1023 {nsGkAtoms::transform_origin},
1024 {nsGkAtoms::unicode_bidi},
1025 {nsGkAtoms::vector_effect},
1026 {nsGkAtoms::visibility},
1027 {nsGkAtoms::white_space},
1028 {nsGkAtoms::word_spacing},
1029 {nsGkAtoms::writing_mode},
1030 {nullptr}};
1032 static const MappedAttributeEntry* const map[] = {attributes};
1034 return FindAttributeDependence(name, map) ||
1035 SVGElementBase::IsAttributeMapped(name);
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 (auto* svg = SVGSVGElement::FromNode(ancestor)) {
1053 return svg;
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 StyleLockedDeclarationBlock& aBlock, nsCSSPropertyID aPropId,
1073 const SVGAnimatedLength& aLength, ValToUse aValToUse) {
1074 float value;
1075 uint8_t units;
1076 if (aValToUse == ValToUse::Anim) {
1077 value = aLength.GetAnimValInSpecifiedUnits();
1078 units = aLength.GetAnimUnitType();
1079 } else {
1080 MOZ_ASSERT(aValToUse == ValToUse::Base);
1081 value = aLength.GetBaseValInSpecifiedUnits();
1082 units = aLength.GetBaseUnitType();
1085 // SVG parser doesn't check non-negativity of some parsed value, we should not
1086 // pass those to CSS side.
1087 if (value < 0 &&
1088 SVGGeometryProperty::IsNonNegativeGeometryProperty(aPropId)) {
1089 return false;
1092 nsCSSUnit cssUnit = SVGLength::SpecifiedUnitTypeToCSSUnit(units);
1094 if (cssUnit == eCSSUnit_Percent) {
1095 Servo_DeclarationBlock_SetPercentValue(&aBlock, aPropId, value / 100.f);
1096 } else {
1097 Servo_DeclarationBlock_SetLengthValue(&aBlock, aPropId, value, cssUnit);
1100 return true;
1103 /* static */
1104 bool SVGElement::UpdateDeclarationBlockFromPath(
1105 StyleLockedDeclarationBlock& aBlock, const SVGAnimatedPathSegList& aPath,
1106 ValToUse aValToUse) {
1107 const SVGPathData& pathData =
1108 aValToUse == ValToUse::Anim ? aPath.GetAnimValue() : aPath.GetBaseValue();
1110 // Based on the current discussion of https://github.com/w3c/svgwg/issues/321,
1111 // we may have to convert the relative commands into absolute commands.
1112 // The normalization should be fixed in Bug 1489392.
1113 Servo_DeclarationBlock_SetPathValue(&aBlock, eCSSProperty_d,
1114 &pathData.RawData());
1115 return true;
1118 template <typename Float>
1119 static StyleTransformOperation MatrixToTransformOperation(
1120 const gfx::BaseMatrix<Float>& aMatrix) {
1121 return StyleTransformOperation::Matrix(StyleGenericMatrix<float>{
1122 .a = float(aMatrix._11),
1123 .b = float(aMatrix._12),
1124 .c = float(aMatrix._21),
1125 .d = float(aMatrix._22),
1126 .e = float(aMatrix._31),
1127 .f = float(aMatrix._32),
1131 static void SVGTransformToCSS(const SVGTransform& aTransform,
1132 nsTArray<StyleTransformOperation>& aOut) {
1133 switch (aTransform.Type()) {
1134 case dom::SVGTransform_Binding::SVG_TRANSFORM_SCALE: {
1135 const auto& m = aTransform.GetMatrix();
1136 aOut.AppendElement(StyleTransformOperation::Scale(m._11, m._22));
1137 return;
1139 case dom::SVGTransform_Binding::SVG_TRANSFORM_TRANSLATE: {
1140 auto p = aTransform.GetMatrix().GetTranslation();
1141 aOut.AppendElement(StyleTransformOperation::Translate(
1142 LengthPercentage::FromPixels(CSSCoord(p.x)),
1143 LengthPercentage::FromPixels(CSSCoord(p.y))));
1144 return;
1146 case dom::SVGTransform_Binding::SVG_TRANSFORM_ROTATE: {
1147 float cx, cy;
1148 aTransform.GetRotationOrigin(cx, cy);
1149 const StyleAngle angle{aTransform.Angle()};
1150 const bool hasOrigin = cx != 0.0f || cy != 0.0f;
1151 if (hasOrigin) {
1152 aOut.AppendElement(StyleTransformOperation::Translate(
1153 LengthPercentage::FromPixels(cx),
1154 LengthPercentage::FromPixels(cy)));
1156 aOut.AppendElement(StyleTransformOperation::Rotate(angle));
1157 if (hasOrigin) {
1158 aOut.AppendElement(StyleTransformOperation::Translate(
1159 LengthPercentage::FromPixels(-cx),
1160 LengthPercentage::FromPixels(-cy)));
1162 return;
1164 case dom::SVGTransform_Binding::SVG_TRANSFORM_SKEWX:
1165 aOut.AppendElement(StyleTransformOperation::SkewX({aTransform.Angle()}));
1166 return;
1167 case dom::SVGTransform_Binding::SVG_TRANSFORM_SKEWY:
1168 aOut.AppendElement(StyleTransformOperation::SkewY({aTransform.Angle()}));
1169 return;
1170 case dom::SVGTransform_Binding::SVG_TRANSFORM_MATRIX: {
1171 aOut.AppendElement(MatrixToTransformOperation(aTransform.GetMatrix()));
1172 return;
1174 case dom::SVGTransform_Binding::SVG_TRANSFORM_UNKNOWN:
1175 default:
1176 MOZ_CRASH("Bad SVGTransform?");
1180 /* static */
1181 bool SVGElement::UpdateDeclarationBlockFromTransform(
1182 StyleLockedDeclarationBlock& aBlock,
1183 const SVGAnimatedTransformList* aTransform,
1184 const gfx::Matrix* aAnimateMotionTransform, ValToUse aValToUse) {
1185 MOZ_ASSERT(aTransform || aAnimateMotionTransform);
1186 AutoTArray<StyleTransformOperation, 5> operations;
1187 if (aAnimateMotionTransform) {
1188 operations.AppendElement(
1189 MatrixToTransformOperation(*aAnimateMotionTransform));
1191 if (aTransform) {
1192 const SVGTransformList& transforms = aValToUse == ValToUse::Anim
1193 ? aTransform->GetAnimValue()
1194 : aTransform->GetBaseValue();
1195 // TODO: Maybe make SVGTransform use StyleTransformOperation directly?
1196 for (size_t i = 0, len = transforms.Length(); i < len; ++i) {
1197 SVGTransformToCSS(transforms[i], operations);
1200 Servo_DeclarationBlock_SetTransform(&aBlock, eCSSProperty_transform,
1201 &operations);
1202 return true;
1205 //------------------------------------------------------------------------
1206 // Helper class: MappedAttrParser, for parsing values of mapped attributes
1208 namespace {
1210 class MOZ_STACK_CLASS MappedAttrParser {
1211 public:
1212 explicit MappedAttrParser(SVGElement& aElement,
1213 StyleLockedDeclarationBlock* aDecl)
1214 : mElement(aElement), mDecl(aDecl) {
1215 if (mDecl) {
1216 Servo_DeclarationBlock_Clear(mDecl);
1219 ~MappedAttrParser() {
1220 MOZ_ASSERT(!mDecl,
1221 "If mDecl was initialized, it should have been returned via "
1222 "TakeDeclarationBlock (and have its pointer cleared)");
1225 // Parses a mapped attribute value.
1226 void ParseMappedAttrValue(nsAtom* aMappedAttrName,
1227 const nsAString& aMappedAttrValue);
1229 void TellStyleAlreadyParsedResult(nsAtom const* aAtom,
1230 SVGAnimatedLength const& aLength);
1231 void TellStyleAlreadyParsedResult(const SVGAnimatedPathSegList&);
1232 void TellStyleAlreadyParsedResult(const SVGAnimatedTransformList&);
1234 // If we've parsed any values for mapped attributes, this method returns the
1235 // already_AddRefed declaration block that incorporates the parsed values.
1236 // Otherwise, this method returns null.
1237 already_AddRefed<StyleLockedDeclarationBlock> TakeDeclarationBlock() {
1238 return mDecl.forget();
1241 StyleLockedDeclarationBlock& EnsureDeclarationBlock() {
1242 if (!mDecl) {
1243 mDecl = Servo_DeclarationBlock_CreateEmpty().Consume();
1245 return *mDecl;
1248 URLExtraData& EnsureExtraData() {
1249 if (!mExtraData) {
1250 mExtraData = mElement.GetURLDataForStyleAttr();
1252 return *mExtraData;
1255 private:
1256 // For reporting use counters
1257 SVGElement& mElement;
1259 // Declaration for storing parsed values (lazily initialized).
1260 RefPtr<StyleLockedDeclarationBlock> mDecl;
1262 // URL data for parsing stuff. Also lazy.
1263 RefPtr<URLExtraData> mExtraData;
1266 void MappedAttrParser::ParseMappedAttrValue(nsAtom* aMappedAttrName,
1267 const nsAString& aMappedAttrValue) {
1268 // Get the nsCSSPropertyID ID for our mapped attribute.
1269 nsCSSPropertyID propertyID =
1270 nsCSSProps::LookupProperty(nsAutoAtomCString(aMappedAttrName));
1271 if (propertyID != eCSSProperty_UNKNOWN) {
1272 bool changed = false; // outparam for ParseProperty.
1273 NS_ConvertUTF16toUTF8 value(aMappedAttrValue);
1275 auto* doc = mElement.OwnerDoc();
1276 changed = Servo_DeclarationBlock_SetPropertyById(
1277 &EnsureDeclarationBlock(), propertyID, &value, false,
1278 &EnsureExtraData(), StyleParsingMode::ALLOW_UNITLESS_LENGTH,
1279 doc->GetCompatibilityMode(), doc->CSSLoader(), StyleCssRuleType::Style,
1280 {});
1282 // TODO(emilio): If we want to record these from CSSOM more generally, we
1283 // can pass the document use counters down the FFI call. For now manually
1284 // count them.
1285 if (changed && StaticPrefs::layout_css_use_counters_enabled()) {
1286 UseCounter useCounter = nsCSSProps::UseCounterFor(propertyID);
1287 MOZ_ASSERT(useCounter != eUseCounter_UNKNOWN);
1288 doc->SetUseCounter(useCounter);
1290 return;
1292 MOZ_ASSERT(aMappedAttrName == nsGkAtoms::lang,
1293 "Only 'lang' should be unrecognized!");
1294 // CSS parser doesn't know about 'lang', so we need to handle it specially.
1295 if (aMappedAttrName == nsGkAtoms::lang) {
1296 propertyID = eCSSProperty__x_lang;
1297 RefPtr<nsAtom> atom = NS_Atomize(aMappedAttrValue);
1298 Servo_DeclarationBlock_SetIdentStringValue(&EnsureDeclarationBlock(),
1299 propertyID, atom);
1303 void MappedAttrParser::TellStyleAlreadyParsedResult(
1304 nsAtom const* aAtom, SVGAnimatedLength const& aLength) {
1305 nsCSSPropertyID propertyID =
1306 nsCSSProps::LookupProperty(nsAutoAtomCString(aAtom));
1307 SVGElement::UpdateDeclarationBlockFromLength(EnsureDeclarationBlock(),
1308 propertyID, aLength,
1309 SVGElement::ValToUse::Base);
1312 void MappedAttrParser::TellStyleAlreadyParsedResult(
1313 const SVGAnimatedPathSegList& aPath) {
1314 SVGElement::UpdateDeclarationBlockFromPath(EnsureDeclarationBlock(), aPath,
1315 SVGElement::ValToUse::Base);
1318 void MappedAttrParser::TellStyleAlreadyParsedResult(
1319 const SVGAnimatedTransformList& aTransform) {
1320 SVGElement::UpdateDeclarationBlockFromTransform(EnsureDeclarationBlock(),
1321 &aTransform, nullptr,
1322 SVGElement::ValToUse::Base);
1325 } // namespace
1327 //----------------------------------------------------------------------
1328 // Implementation Helpers:
1330 void SVGElement::UpdateMappedDeclarationBlock() {
1331 MOZ_ASSERT(IsPendingMappedAttributeEvaluation());
1332 MappedAttrParser mappedAttrParser(*this, mAttrs.GetMappedDeclarationBlock());
1334 const bool lengthAffectsStyle =
1335 SVGGeometryProperty::ElementMapsLengthsToStyle(this);
1336 bool sawTransform = false;
1337 uint32_t i = 0;
1338 while (BorrowedAttrInfo info = GetAttrInfoAt(i++)) {
1339 const nsAttrName* attrName = info.mName;
1340 if (!attrName->IsAtom()) {
1341 continue;
1344 nsAtom* nameAtom = attrName->Atom();
1345 if (!IsAttributeMapped(nameAtom)) {
1346 continue;
1349 if (nameAtom == nsGkAtoms::lang &&
1350 HasAttr(kNameSpaceID_XML, nsGkAtoms::lang)) {
1351 // xml:lang has precedence, and will get set via Gecko_GetXMLLangValue().
1352 continue;
1355 if (lengthAffectsStyle) {
1356 auto const* length = GetAnimatedLength(nameAtom);
1358 if (length && length->HasBaseVal()) {
1359 // This is an element with geometry property set via SVG attribute,
1360 // and the attribute is already successfully parsed. We want to go
1361 // through the optimized path to tell the style system the result
1362 // directly, rather than let it parse the same thing again.
1363 mappedAttrParser.TellStyleAlreadyParsedResult(nameAtom, *length);
1364 continue;
1368 if (nameAtom == nsGkAtoms::transform ||
1369 nameAtom == nsGkAtoms::patternTransform ||
1370 nameAtom == nsGkAtoms::gradientTransform) {
1371 sawTransform = true;
1372 const auto* transform = GetAnimatedTransformList();
1373 MOZ_ASSERT(GetTransformListAttrName() == nameAtom);
1374 MOZ_ASSERT(transform);
1375 // We want to go through the optimized path to tell the style system the
1376 // result directly, rather than let it parse the same thing again.
1377 mappedAttrParser.TellStyleAlreadyParsedResult(*transform);
1378 continue;
1381 if (nameAtom == nsGkAtoms::d) {
1382 const auto* path = GetAnimPathSegList();
1383 // Note: Only SVGPathElement has d attribute.
1384 MOZ_ASSERT(
1385 path,
1386 "SVGPathElement should have the non-null SVGAnimatedPathSegList");
1387 // The attribute should have been already successfully parsed.
1388 // We want to go through the optimized path to tell the style system
1389 // the result directly, rather than let it parse the same thing again.
1390 mappedAttrParser.TellStyleAlreadyParsedResult(*path);
1391 // Some other notes:
1392 // The syntax of CSS d property is different from SVG d attribute.
1393 // 1. CSS d proeprty accepts: none | path(<quoted string>);
1394 // 2. SVG d attribtue accepts: none | <string>
1395 // So we cannot use css parser to parse the SVG d attribute directly.
1396 // Besides, |mAttrs.AttrAt(i)| removes the quotes already, so the svg path
1397 // in |mAttrs.AttrAt(i)| would be something like `M0,0L1,1z` without the
1398 // quotes. So css tokenizer cannot recognize this as a quoted string, and
1399 // so svg_path::SVGPathData::parse() doesn't work for this. Fortunately,
1400 // we still can rely on the parsed result from
1401 // SVGElement::ParseAttribute() for d attribute.
1402 continue;
1405 nsAutoString value;
1406 info.mValue->ToString(value);
1407 mappedAttrParser.ParseMappedAttrValue(nameAtom, value);
1410 // We need to map the SVG view's transform if we haven't mapped it already.
1411 if (NodeInfo()->NameAtom() == nsGkAtoms::svg && !sawTransform) {
1412 if (const auto* transform = GetAnimatedTransformList()) {
1413 mappedAttrParser.TellStyleAlreadyParsedResult(*transform);
1417 mAttrs.SetMappedDeclarationBlock(mappedAttrParser.TakeDeclarationBlock());
1421 * Helper methods for the type-specific WillChangeXXX methods.
1423 * This method sends out appropriate pre-change notifications so that selector
1424 * restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop
1425 * matching) work, and it returns an nsAttrValue that _may_ contain the
1426 * attribute's pre-change value.
1428 * The nsAttrValue returned by this method depends on whether there are
1429 * mutation event listeners listening for changes to this element's attributes.
1430 * If not, then the object returned is empty. If there are, then the
1431 * nsAttrValue returned contains a serialized copy of the attribute's value
1432 * prior to the change, and this object should be passed to the corresponding
1433 * DidChangeXXX method call (assuming a WillChangeXXX call is required for the
1434 * SVG type - see comment below). This is necessary so that the 'prevValue'
1435 * property of the mutation event that is dispatched will correctly contain the
1436 * old value.
1438 * The reason we need to serialize the old value if there are mutation
1439 * event listeners is because the underlying nsAttrValue for the attribute
1440 * points directly to a parsed representation of the attribute (e.g. an
1441 * SVGAnimatedLengthList*) that is a member of the SVG element. That object
1442 * will have changed by the time DidChangeXXX has been called, so without the
1443 * serialization of the old attribute value that we provide, DidChangeXXX
1444 * would have no way to get the old value to pass to SetAttrAndNotify.
1446 * We only return the old value when there are mutation event listeners because
1447 * it's not needed otherwise, and because it's expensive to serialize the old
1448 * value. This is especially true for list type attributes, which may be built
1449 * up via the SVG DOM resulting in a large number of Will/DidModifyXXX calls
1450 * before the script finally finishes setting the attribute.
1452 * Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check
1453 * and filter out redundant changes. Before calling WillChangeXXX, the caller
1454 * should check whether the new and old values are actually the same, and skip
1455 * calling Will/DidChangeXXX if they are.
1457 * Also note that not all SVG types use this scheme. For types that can be
1458 * represented by an nsAttrValue without pointing back to an SVG object (e.g.
1459 * enums, booleans, integers) we can simply use SetParsedAttr which will do all
1460 * of the above for us. For such types there is no matching WillChangeXXX
1461 * method, only DidChangeXXX which calls SetParsedAttr.
1463 nsAttrValue SVGElement::WillChangeValue(
1464 nsAtom* aName, const mozAutoDocUpdate& aProofOfUpdate) {
1465 // We need an empty attr value:
1466 // a) to pass to BeforeSetAttr when GetParsedAttr returns nullptr
1467 // b) to store the old value in the case we have mutation listeners
1469 // We can use the same value for both purposes, because if GetParsedAttr
1470 // returns non-null its return value is what will get passed to BeforeSetAttr,
1471 // not matter what our mutation listener situation is.
1473 // Also, we should be careful to always return this value to benefit from
1474 // return value optimization.
1475 nsAttrValue emptyOrOldAttrValue;
1476 const nsAttrValue* attrValue = GetParsedAttr(aName);
1478 // We only need to set the old value if we have listeners since otherwise it
1479 // isn't used.
1480 if (attrValue && nsContentUtils::WantMutationEvents(
1481 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) {
1482 emptyOrOldAttrValue.SetToSerialized(*attrValue);
1485 uint8_t modType =
1486 attrValue ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
1487 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
1488 MutationObservers::NotifyAttributeWillChange(this, kNameSpaceID_None, aName,
1489 modType);
1491 // This is not strictly correct--the attribute value parameter for
1492 // BeforeSetAttr should reflect the value that *will* be set but that implies
1493 // allocating, e.g. an extra SVGAnimatedLength, and isn't necessary at the
1494 // moment since no SVG elements overload BeforeSetAttr. For now we just pass
1495 // the current value.
1496 const nsAttrValue* value = attrValue ? attrValue : &emptyOrOldAttrValue;
1497 BeforeSetAttr(kNameSpaceID_None, aName, value, kNotifyDocumentObservers);
1498 return emptyOrOldAttrValue;
1502 * Helper methods for the type-specific DidChangeXXX methods.
1504 * aEmptyOrOldValue will normally be the object returned from the corresponding
1505 * WillChangeXXX call. This is because:
1506 * a) WillChangeXXX will ensure the object is set when we have mutation
1507 * listeners, and
1508 * b) WillChangeXXX will ensure the object represents a serialized version of
1509 * the old attribute value so that the value doesn't change when the
1510 * underlying SVG type is updated.
1512 * aNewValue is replaced with the old value.
1514 void SVGElement::DidChangeValue(nsAtom* aName,
1515 const nsAttrValue& aEmptyOrOldValue,
1516 nsAttrValue& aNewValue,
1517 const mozAutoDocUpdate& aProofOfUpdate) {
1518 bool hasListeners = nsContentUtils::WantMutationEvents(
1519 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this);
1520 uint8_t modType =
1521 HasAttr(aName) ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
1522 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
1524 // XXX Really, the fourth argument to SetAttrAndNotify should be null if
1525 // aEmptyOrOldValue does not represent the actual previous value of the
1526 // attribute, but currently SVG elements do not even use the old attribute
1527 // value in |AfterSetAttr|, so this should be ok.
1528 SetAttrAndNotify(kNameSpaceID_None, aName, nullptr, &aEmptyOrOldValue,
1529 aNewValue, nullptr, modType, hasListeners,
1530 kNotifyDocumentObservers, kCallAfterSetAttr,
1531 GetComposedDoc(), aProofOfUpdate);
1534 void SVGElement::MaybeSerializeAttrBeforeRemoval(nsAtom* aName, bool aNotify) {
1535 if (!aNotify || !nsContentUtils::WantMutationEvents(
1536 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) {
1537 return;
1540 const nsAttrValue* attrValue = mAttrs.GetAttr(aName);
1541 if (!attrValue) return;
1543 nsAutoString serializedValue;
1544 attrValue->ToString(serializedValue);
1545 nsAttrValue oldAttrValue(serializedValue);
1546 bool oldValueSet;
1547 mAttrs.SetAndSwapAttr(aName, oldAttrValue, &oldValueSet);
1550 nsAtom* SVGElement::GetEventNameForAttr(nsAtom* aAttr) {
1551 if (IsSVGElement(nsGkAtoms::svg)) {
1552 if (aAttr == nsGkAtoms::onload) return nsGkAtoms::onSVGLoad;
1553 if (aAttr == nsGkAtoms::onscroll) return nsGkAtoms::onSVGScroll;
1555 if (aAttr == nsGkAtoms::onbegin) return nsGkAtoms::onbeginEvent;
1556 if (aAttr == nsGkAtoms::onrepeat) return nsGkAtoms::onrepeatEvent;
1557 if (aAttr == nsGkAtoms::onend) return nsGkAtoms::onendEvent;
1559 return SVGElementBase::GetEventNameForAttr(aAttr);
1562 SVGViewportElement* SVGElement::GetCtx() const {
1563 return SVGContentUtils::GetNearestViewportElement(this);
1566 /* virtual */
1567 gfxMatrix SVGElement::ChildToUserSpaceTransform() const { return {}; }
1569 SVGElement::LengthAttributesInfo SVGElement::GetLengthInfo() {
1570 return LengthAttributesInfo(nullptr, nullptr, 0);
1573 void SVGElement::SetLength(nsAtom* aName, const SVGAnimatedLength& aLength) {
1574 LengthAttributesInfo lengthInfo = GetLengthInfo();
1576 for (uint32_t i = 0; i < lengthInfo.mCount; i++) {
1577 if (aName == lengthInfo.mInfos[i].mName) {
1578 lengthInfo.mValues[i] = aLength;
1579 DidAnimateLength(i);
1580 return;
1583 MOZ_ASSERT(false, "no length found to set");
1586 nsAttrValue SVGElement::WillChangeLength(
1587 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1588 return WillChangeValue(GetLengthInfo().mInfos[aAttrEnum].mName,
1589 aProofOfUpdate);
1592 void SVGElement::DidChangeLength(uint8_t aAttrEnum,
1593 const nsAttrValue& aEmptyOrOldValue,
1594 const mozAutoDocUpdate& aProofOfUpdate) {
1595 LengthAttributesInfo info = GetLengthInfo();
1597 NS_ASSERTION(info.mCount > 0,
1598 "DidChangeLength on element with no length attribs");
1599 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1601 nsAttrValue newValue;
1602 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
1604 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1605 aProofOfUpdate);
1608 void SVGElement::DidAnimateLength(uint8_t aAttrEnum) {
1609 // We need to do this here. Normally the SMIL restyle would also cause us to
1610 // do this from DidSetComputedStyle, but we don't have that guarantee if our
1611 // frame gets reconstructed.
1612 ClearAnyCachedPath();
1614 if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) {
1615 nsCSSPropertyID propId =
1616 SVGGeometryProperty::AttrEnumToCSSPropId(this, aAttrEnum);
1618 // We don't map use element width/height currently. We can remove this
1619 // test when we do.
1620 if (propId != eCSSProperty_UNKNOWN) {
1621 auto lengthInfo = GetLengthInfo();
1622 if (lengthInfo.mValues[aAttrEnum].IsAnimated()) {
1623 SMILOverrideStyle()->SetSMILValue(propId,
1624 lengthInfo.mValues[aAttrEnum]);
1625 } else {
1626 SMILOverrideStyle()->ClearSMILValue(propId);
1631 auto info = GetLengthInfo();
1632 DidAnimateAttribute(kNameSpaceID_None, info.mInfos[aAttrEnum].mName);
1635 SVGAnimatedLength* SVGElement::GetAnimatedLength(uint8_t aAttrEnum) {
1636 LengthAttributesInfo info = GetLengthInfo();
1637 if (aAttrEnum < info.mCount) {
1638 return &info.mValues[aAttrEnum];
1640 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1641 return nullptr;
1644 SVGAnimatedLength* SVGElement::GetAnimatedLength(const nsAtom* aAttrName) {
1645 LengthAttributesInfo lengthInfo = GetLengthInfo();
1647 for (uint32_t i = 0; i < lengthInfo.mCount; i++) {
1648 if (aAttrName == lengthInfo.mInfos[i].mName) {
1649 return &lengthInfo.mValues[i];
1652 return nullptr;
1655 void SVGElement::GetAnimatedLengthValues(float* aFirst, ...) {
1656 LengthAttributesInfo info = GetLengthInfo();
1658 NS_ASSERTION(info.mCount > 0,
1659 "GetAnimatedLengthValues on element with no length attribs");
1661 SVGElementMetrics metrics(this);
1663 float* f = aFirst;
1664 uint32_t i = 0;
1666 va_list args;
1667 va_start(args, aFirst);
1669 while (f && i < info.mCount) {
1670 *f = info.mValues[i++].GetAnimValueWithZoom(metrics);
1671 f = va_arg(args, float*);
1674 va_end(args);
1677 SVGElement::LengthListAttributesInfo SVGElement::GetLengthListInfo() {
1678 return LengthListAttributesInfo(nullptr, nullptr, 0);
1681 nsAttrValue SVGElement::WillChangeLengthList(
1682 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1683 return WillChangeValue(GetLengthListInfo().mInfos[aAttrEnum].mName,
1684 aProofOfUpdate);
1687 void SVGElement::DidChangeLengthList(uint8_t aAttrEnum,
1688 const nsAttrValue& aEmptyOrOldValue,
1689 const mozAutoDocUpdate& aProofOfUpdate) {
1690 LengthListAttributesInfo info = GetLengthListInfo();
1692 NS_ASSERTION(info.mCount > 0,
1693 "DidChangeLengthList on element with no length list attribs");
1694 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1696 nsAttrValue newValue;
1697 newValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1699 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1700 aProofOfUpdate);
1703 void SVGElement::GetAnimatedLengthListValues(SVGUserUnitList* aFirst, ...) {
1704 LengthListAttributesInfo info = GetLengthListInfo();
1706 NS_ASSERTION(
1707 info.mCount > 0,
1708 "GetAnimatedLengthListValues on element with no length list attribs");
1710 SVGUserUnitList* list = aFirst;
1711 uint32_t i = 0;
1713 va_list args;
1714 va_start(args, aFirst);
1716 while (list && i < info.mCount) {
1717 list->Init(&(info.mValues[i].GetAnimValue()), this, info.mInfos[i].mAxis);
1718 ++i;
1719 list = va_arg(args, SVGUserUnitList*);
1722 va_end(args);
1725 SVGAnimatedLengthList* SVGElement::GetAnimatedLengthList(uint8_t aAttrEnum) {
1726 LengthListAttributesInfo info = GetLengthListInfo();
1727 if (aAttrEnum < info.mCount) {
1728 return &(info.mValues[aAttrEnum]);
1730 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1731 return nullptr;
1734 SVGElement::NumberListAttributesInfo SVGElement::GetNumberListInfo() {
1735 return NumberListAttributesInfo(nullptr, nullptr, 0);
1738 nsAttrValue SVGElement::WillChangeNumberList(
1739 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1740 return WillChangeValue(GetNumberListInfo().mInfos[aAttrEnum].mName,
1741 aProofOfUpdate);
1744 void SVGElement::DidChangeNumberList(uint8_t aAttrEnum,
1745 const nsAttrValue& aEmptyOrOldValue,
1746 const mozAutoDocUpdate& aProofOfUpdate) {
1747 NumberListAttributesInfo info = GetNumberListInfo();
1749 MOZ_ASSERT(info.mCount > 0,
1750 "DidChangeNumberList on element with no number list attribs");
1751 MOZ_ASSERT(aAttrEnum < info.mCount, "aAttrEnum out of range");
1753 nsAttrValue newValue;
1754 newValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1756 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1757 aProofOfUpdate);
1760 SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(uint8_t aAttrEnum) {
1761 NumberListAttributesInfo info = GetNumberListInfo();
1762 if (aAttrEnum < info.mCount) {
1763 return &(info.mValues[aAttrEnum]);
1765 MOZ_ASSERT(false, "Bad attrEnum");
1766 return nullptr;
1769 SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(nsAtom* aAttrName) {
1770 NumberListAttributesInfo info = GetNumberListInfo();
1771 for (uint32_t i = 0; i < info.mCount; i++) {
1772 if (aAttrName == info.mInfos[i].mName) {
1773 return &info.mValues[i];
1776 MOZ_ASSERT(false, "Bad caller");
1777 return nullptr;
1780 nsAttrValue SVGElement::WillChangePointList(
1781 const mozAutoDocUpdate& aProofOfUpdate) {
1782 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1783 return WillChangeValue(GetPointListAttrName(), aProofOfUpdate);
1786 void SVGElement::DidChangePointList(const nsAttrValue& aEmptyOrOldValue,
1787 const mozAutoDocUpdate& aProofOfUpdate) {
1788 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1790 nsAttrValue newValue;
1791 newValue.SetTo(GetAnimatedPointList()->GetBaseValue(), nullptr);
1793 DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue, newValue,
1794 aProofOfUpdate);
1797 void SVGElement::DidAnimatePointList() {
1798 MOZ_ASSERT(GetPointListAttrName(), "Animating non-existent path data?");
1800 ClearAnyCachedPath();
1802 DidAnimateAttribute(kNameSpaceID_None, GetPointListAttrName());
1805 nsAttrValue SVGElement::WillChangePathSegList(
1806 const mozAutoDocUpdate& aProofOfUpdate) {
1807 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1808 return WillChangeValue(GetPathDataAttrName(), aProofOfUpdate);
1811 void SVGElement::DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue,
1812 const mozAutoDocUpdate& aProofOfUpdate) {
1813 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1815 nsAttrValue newValue;
1816 newValue.SetTo(GetAnimPathSegList()->GetBaseValue(), nullptr);
1818 DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue, newValue,
1819 aProofOfUpdate);
1822 void SVGElement::DidAnimatePathSegList() {
1823 nsStaticAtom* name = GetPathDataAttrName();
1824 MOZ_ASSERT(name, "Animating non-existent path data?");
1826 ClearAnyCachedPath();
1828 // Notify style we have to update the d property because of SMIL animation.
1829 if (name == nsGkAtoms::d) {
1830 auto* animPathSegList = GetAnimPathSegList();
1831 if (animPathSegList->IsAnimating()) {
1832 SMILOverrideStyle()->SetSMILValue(eCSSProperty_d, *animPathSegList);
1833 } else {
1834 SMILOverrideStyle()->ClearSMILValue(eCSSProperty_d);
1838 DidAnimateAttribute(kNameSpaceID_None, name);
1841 SVGElement::NumberAttributesInfo SVGElement::GetNumberInfo() {
1842 return NumberAttributesInfo(nullptr, nullptr, 0);
1845 void SVGElement::DidChangeNumber(uint8_t aAttrEnum) {
1846 NumberAttributesInfo info = GetNumberInfo();
1848 NS_ASSERTION(info.mCount > 0,
1849 "DidChangeNumber on element with no number attribs");
1850 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1852 nsAttrValue attrValue;
1853 attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1855 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1856 attrValue, true);
1859 void SVGElement::GetAnimatedNumberValues(float* aFirst, ...) {
1860 NumberAttributesInfo info = GetNumberInfo();
1862 NS_ASSERTION(info.mCount > 0,
1863 "GetAnimatedNumberValues on element with no number attribs");
1865 float* f = aFirst;
1866 uint32_t i = 0;
1868 va_list args;
1869 va_start(args, aFirst);
1871 while (f && i < info.mCount) {
1872 *f = info.mValues[i++].GetAnimValue();
1873 f = va_arg(args, float*);
1875 va_end(args);
1878 SVGElement::NumberPairAttributesInfo SVGElement::GetNumberPairInfo() {
1879 return NumberPairAttributesInfo(nullptr, nullptr, 0);
1882 nsAttrValue SVGElement::WillChangeNumberPair(uint8_t aAttrEnum) {
1883 mozAutoDocUpdate updateBatch(GetComposedDoc(), kDontNotifyDocumentObservers);
1884 return WillChangeValue(GetNumberPairInfo().mInfos[aAttrEnum].mName,
1885 updateBatch);
1888 void SVGElement::DidChangeNumberPair(uint8_t aAttrEnum,
1889 const nsAttrValue& aEmptyOrOldValue) {
1890 NumberPairAttributesInfo info = GetNumberPairInfo();
1892 NS_ASSERTION(info.mCount > 0,
1893 "DidChangePairNumber on element with no number pair attribs");
1894 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1896 nsAttrValue newValue;
1897 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
1899 mozAutoDocUpdate updateBatch(GetComposedDoc(), kNotifyDocumentObservers);
1900 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1901 updateBatch);
1904 SVGElement::IntegerAttributesInfo SVGElement::GetIntegerInfo() {
1905 return IntegerAttributesInfo(nullptr, nullptr, 0);
1908 void SVGElement::DidChangeInteger(uint8_t aAttrEnum) {
1909 IntegerAttributesInfo info = GetIntegerInfo();
1910 NS_ASSERTION(info.mCount > 0,
1911 "DidChangeInteger on element with no integer attribs");
1912 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1914 nsAttrValue attrValue;
1915 attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1917 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1918 attrValue, true);
1921 void SVGElement::GetAnimatedIntegerValues(int32_t* aFirst, ...) {
1922 IntegerAttributesInfo info = GetIntegerInfo();
1924 NS_ASSERTION(info.mCount > 0,
1925 "GetAnimatedIntegerValues on element with no integer attribs");
1927 int32_t* n = aFirst;
1928 uint32_t i = 0;
1930 va_list args;
1931 va_start(args, aFirst);
1933 while (n && i < info.mCount) {
1934 *n = info.mValues[i++].GetAnimValue();
1935 n = va_arg(args, int32_t*);
1937 va_end(args);
1940 SVGElement::IntegerPairAttributesInfo SVGElement::GetIntegerPairInfo() {
1941 return IntegerPairAttributesInfo(nullptr, nullptr, 0);
1944 nsAttrValue SVGElement::WillChangeIntegerPair(
1945 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1946 return WillChangeValue(GetIntegerPairInfo().mInfos[aAttrEnum].mName,
1947 aProofOfUpdate);
1950 void SVGElement::DidChangeIntegerPair(uint8_t aAttrEnum,
1951 const nsAttrValue& aEmptyOrOldValue,
1952 const mozAutoDocUpdate& aProofOfUpdate) {
1953 IntegerPairAttributesInfo info = GetIntegerPairInfo();
1955 NS_ASSERTION(info.mCount > 0,
1956 "DidChangeIntegerPair on element with no integer pair attribs");
1957 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1959 nsAttrValue newValue;
1960 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
1962 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1963 aProofOfUpdate);
1966 SVGElement::BooleanAttributesInfo SVGElement::GetBooleanInfo() {
1967 return BooleanAttributesInfo(nullptr, nullptr, 0);
1970 void SVGElement::DidChangeBoolean(uint8_t aAttrEnum) {
1971 BooleanAttributesInfo info = GetBooleanInfo();
1973 NS_ASSERTION(info.mCount > 0,
1974 "DidChangeBoolean on element with no boolean attribs");
1975 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1977 nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom());
1978 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1979 attrValue, true);
1982 SVGElement::EnumAttributesInfo SVGElement::GetEnumInfo() {
1983 return EnumAttributesInfo(nullptr, nullptr, 0);
1986 void SVGElement::DidChangeEnum(uint8_t aAttrEnum) {
1987 EnumAttributesInfo info = GetEnumInfo();
1989 NS_ASSERTION(info.mCount > 0,
1990 "DidChangeEnum on element with no enum attribs");
1991 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1993 nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom(this));
1994 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1995 attrValue, true);
1998 SVGAnimatedOrient* SVGElement::GetAnimatedOrient() { return nullptr; }
2000 nsAttrValue SVGElement::WillChangeOrient(
2001 const mozAutoDocUpdate& aProofOfUpdate) {
2002 return WillChangeValue(nsGkAtoms::orient, aProofOfUpdate);
2005 void SVGElement::DidChangeOrient(const nsAttrValue& aEmptyOrOldValue,
2006 const mozAutoDocUpdate& aProofOfUpdate) {
2007 SVGAnimatedOrient* orient = GetAnimatedOrient();
2009 NS_ASSERTION(orient, "DidChangeOrient on element with no orient attrib");
2011 nsAttrValue newValue;
2012 newValue.SetTo(*orient, nullptr);
2014 DidChangeValue(nsGkAtoms::orient, aEmptyOrOldValue, newValue, aProofOfUpdate);
2017 SVGAnimatedViewBox* SVGElement::GetAnimatedViewBox() { return nullptr; }
2019 nsAttrValue SVGElement::WillChangeViewBox(
2020 const mozAutoDocUpdate& aProofOfUpdate) {
2021 return WillChangeValue(nsGkAtoms::viewBox, aProofOfUpdate);
2024 void SVGElement::DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue,
2025 const mozAutoDocUpdate& aProofOfUpdate) {
2026 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
2028 NS_ASSERTION(viewBox, "DidChangeViewBox on element with no viewBox attrib");
2030 nsAttrValue newValue;
2031 newValue.SetTo(*viewBox, nullptr);
2033 DidChangeValue(nsGkAtoms::viewBox, aEmptyOrOldValue, newValue,
2034 aProofOfUpdate);
2037 SVGAnimatedPreserveAspectRatio* SVGElement::GetAnimatedPreserveAspectRatio() {
2038 return nullptr;
2041 nsAttrValue SVGElement::WillChangePreserveAspectRatio(
2042 const mozAutoDocUpdate& aProofOfUpdate) {
2043 return WillChangeValue(nsGkAtoms::preserveAspectRatio, aProofOfUpdate);
2046 void SVGElement::DidChangePreserveAspectRatio(
2047 const nsAttrValue& aEmptyOrOldValue,
2048 const mozAutoDocUpdate& aProofOfUpdate) {
2049 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
2050 GetAnimatedPreserveAspectRatio();
2052 NS_ASSERTION(preserveAspectRatio,
2053 "DidChangePreserveAspectRatio on element with no "
2054 "preserveAspectRatio attrib");
2056 nsAttrValue newValue;
2057 newValue.SetTo(*preserveAspectRatio, nullptr);
2059 DidChangeValue(nsGkAtoms::preserveAspectRatio, aEmptyOrOldValue, newValue,
2060 aProofOfUpdate);
2063 nsAttrValue SVGElement::WillChangeTransformList(
2064 const mozAutoDocUpdate& aProofOfUpdate) {
2065 return WillChangeValue(GetTransformListAttrName(), aProofOfUpdate);
2068 void SVGElement::DidChangeTransformList(
2069 const nsAttrValue& aEmptyOrOldValue,
2070 const mozAutoDocUpdate& aProofOfUpdate) {
2071 MOZ_ASSERT(GetTransformListAttrName(),
2072 "Changing non-existent transform list?");
2074 // The transform attribute is being set, so we must ensure that the
2075 // SVGAnimatedTransformList is/has been allocated:
2076 nsAttrValue newValue;
2077 newValue.SetTo(GetAnimatedTransformList(DO_ALLOCATE)->GetBaseValue(),
2078 nullptr);
2080 DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue, newValue,
2081 aProofOfUpdate);
2084 void SVGElement::DidAnimateTransformList(int32_t aModType) {
2085 MOZ_ASSERT(GetTransformListAttrName(),
2086 "Animating non-existent transform data?");
2087 const auto* animTransformList = GetAnimatedTransformList();
2088 const auto* animateMotion = GetAnimateMotionTransform();
2089 if (animateMotion ||
2090 (animTransformList && animTransformList->IsAnimating())) {
2091 SMILOverrideStyle()->SetSMILValue(eCSSProperty_transform, animTransformList,
2092 animateMotion);
2093 } else {
2094 SMILOverrideStyle()->ClearSMILValue(eCSSProperty_transform);
2098 SVGElement::StringAttributesInfo SVGElement::GetStringInfo() {
2099 return StringAttributesInfo(nullptr, nullptr, 0);
2102 void SVGElement::GetStringBaseValue(uint8_t aAttrEnum,
2103 nsAString& aResult) const {
2104 SVGElement::StringAttributesInfo info =
2105 const_cast<SVGElement*>(this)->GetStringInfo();
2107 NS_ASSERTION(info.mCount > 0,
2108 "GetBaseValue on element with no string attribs");
2110 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2112 GetAttr(info.mInfos[aAttrEnum].mNamespaceID, info.mInfos[aAttrEnum].mName,
2113 aResult);
2116 void SVGElement::SetStringBaseValue(uint8_t aAttrEnum,
2117 const nsAString& aValue) {
2118 SVGElement::StringAttributesInfo info = GetStringInfo();
2120 NS_ASSERTION(info.mCount > 0,
2121 "SetBaseValue on element with no string attribs");
2123 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2125 SetAttr(info.mInfos[aAttrEnum].mNamespaceID, info.mInfos[aAttrEnum].mName,
2126 aValue, true);
2129 SVGElement::StringListAttributesInfo SVGElement::GetStringListInfo() {
2130 return StringListAttributesInfo(nullptr, nullptr, 0);
2133 nsAttrValue SVGElement::WillChangeStringList(
2134 bool aIsConditionalProcessingAttribute, uint8_t aAttrEnum,
2135 const mozAutoDocUpdate& aProofOfUpdate) {
2136 nsStaticAtom* name;
2137 if (aIsConditionalProcessingAttribute) {
2138 nsCOMPtr<SVGTests> tests(do_QueryInterface(this));
2139 name = tests->GetAttrName(aAttrEnum);
2140 } else {
2141 name = GetStringListInfo().mInfos[aAttrEnum].mName;
2143 return WillChangeValue(name, aProofOfUpdate);
2146 void SVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute,
2147 uint8_t aAttrEnum,
2148 const nsAttrValue& aEmptyOrOldValue,
2149 const mozAutoDocUpdate& aProofOfUpdate) {
2150 nsStaticAtom* name;
2151 nsAttrValue newValue;
2152 nsCOMPtr<SVGTests> tests;
2154 if (aIsConditionalProcessingAttribute) {
2155 tests = do_QueryObject(this);
2156 name = tests->GetAttrName(aAttrEnum);
2157 tests->GetAttrValue(aAttrEnum, newValue);
2158 } else {
2159 StringListAttributesInfo info = GetStringListInfo();
2161 NS_ASSERTION(info.mCount > 0,
2162 "DidChangeStringList on element with no string list attribs");
2163 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2165 name = info.mInfos[aAttrEnum].mName;
2166 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
2169 DidChangeValue(name, aEmptyOrOldValue, newValue, aProofOfUpdate);
2171 if (aIsConditionalProcessingAttribute) {
2172 tests->MaybeInvalidate();
2176 void SVGElement::DidAnimateAttribute(int32_t aNameSpaceID, nsAtom* aAttribute) {
2177 if (auto* frame = GetPrimaryFrame()) {
2178 frame->AttributeChanged(aNameSpaceID, aAttribute,
2179 MutationEvent_Binding::MODIFICATION);
2180 SVGObserverUtils::InvalidateRenderingObservers(frame);
2181 return;
2183 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
2186 nsresult SVGElement::ReportAttributeParseFailure(Document* aDocument,
2187 nsAtom* aAttribute,
2188 const nsAString& aValue) {
2189 AutoTArray<nsString, 2> strings;
2190 strings.AppendElement(nsDependentAtomString(aAttribute));
2191 strings.AppendElement(aValue);
2192 return SVGContentUtils::ReportToConsole(aDocument, "AttributeParseWarning",
2193 strings);
2196 UniquePtr<SMILAttr> SVGElement::GetAnimatedAttr(int32_t aNamespaceID,
2197 nsAtom* aName) {
2198 if (aNamespaceID == kNameSpaceID_None) {
2199 // Transforms:
2200 if (GetTransformListAttrName() == aName) {
2201 // The transform attribute is being animated, so we must ensure that the
2202 // SVGAnimatedTransformList is/has been allocated:
2203 return GetAnimatedTransformList(DO_ALLOCATE)->ToSMILAttr(this);
2206 // Motion (fake 'attribute' for animateMotion)
2207 if (aName == nsGkAtoms::mozAnimateMotionDummyAttr) {
2208 return MakeUnique<SVGMotionSMILAttr>(this);
2211 // Lengths:
2212 LengthAttributesInfo info = GetLengthInfo();
2213 for (uint32_t i = 0; i < info.mCount; i++) {
2214 if (aName == info.mInfos[i].mName) {
2215 return info.mValues[i].ToSMILAttr(this);
2219 // Numbers:
2221 NumberAttributesInfo info = GetNumberInfo();
2222 for (uint32_t i = 0; i < info.mCount; i++) {
2223 if (aName == info.mInfos[i].mName) {
2224 return info.mValues[i].ToSMILAttr(this);
2229 // Number Pairs:
2231 NumberPairAttributesInfo info = GetNumberPairInfo();
2232 for (uint32_t i = 0; i < info.mCount; i++) {
2233 if (aName == info.mInfos[i].mName) {
2234 return info.mValues[i].ToSMILAttr(this);
2239 // Integers:
2241 IntegerAttributesInfo info = GetIntegerInfo();
2242 for (uint32_t i = 0; i < info.mCount; i++) {
2243 if (aName == info.mInfos[i].mName) {
2244 return info.mValues[i].ToSMILAttr(this);
2249 // Integer Pairs:
2251 IntegerPairAttributesInfo info = GetIntegerPairInfo();
2252 for (uint32_t i = 0; i < info.mCount; i++) {
2253 if (aName == info.mInfos[i].mName) {
2254 return info.mValues[i].ToSMILAttr(this);
2259 // Enumerations:
2261 EnumAttributesInfo info = GetEnumInfo();
2262 for (uint32_t i = 0; i < info.mCount; i++) {
2263 if (aName == info.mInfos[i].mName) {
2264 return info.mValues[i].ToSMILAttr(this);
2269 // Booleans:
2271 BooleanAttributesInfo info = GetBooleanInfo();
2272 for (uint32_t i = 0; i < info.mCount; i++) {
2273 if (aName == info.mInfos[i].mName) {
2274 return info.mValues[i].ToSMILAttr(this);
2279 // orient:
2280 if (aName == nsGkAtoms::orient) {
2281 SVGAnimatedOrient* orient = GetAnimatedOrient();
2282 return orient ? orient->ToSMILAttr(this) : nullptr;
2285 // viewBox:
2286 if (aName == nsGkAtoms::viewBox) {
2287 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
2288 return viewBox ? viewBox->ToSMILAttr(this) : nullptr;
2291 // preserveAspectRatio:
2292 if (aName == nsGkAtoms::preserveAspectRatio) {
2293 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
2294 GetAnimatedPreserveAspectRatio();
2295 return preserveAspectRatio ? preserveAspectRatio->ToSMILAttr(this)
2296 : nullptr;
2299 // NumberLists:
2301 NumberListAttributesInfo info = GetNumberListInfo();
2302 for (uint32_t i = 0; i < info.mCount; i++) {
2303 if (aName == info.mInfos[i].mName) {
2304 MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes");
2305 return info.mValues[i].ToSMILAttr(this, uint8_t(i));
2310 // LengthLists:
2312 LengthListAttributesInfo info = GetLengthListInfo();
2313 for (uint32_t i = 0; i < info.mCount; i++) {
2314 if (aName == info.mInfos[i].mName) {
2315 MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes");
2316 return info.mValues[i].ToSMILAttr(this, uint8_t(i),
2317 info.mInfos[i].mAxis,
2318 info.mInfos[i].mCouldZeroPadList);
2323 // PointLists:
2325 if (GetPointListAttrName() == aName) {
2326 SVGAnimatedPointList* pointList = GetAnimatedPointList();
2327 if (pointList) {
2328 return pointList->ToSMILAttr(this);
2333 // PathSegLists:
2335 if (GetPathDataAttrName() == aName) {
2336 SVGAnimatedPathSegList* segList = GetAnimPathSegList();
2337 if (segList) {
2338 return segList->ToSMILAttr(this);
2343 if (aName == nsGkAtoms::_class) {
2344 return mClassAttribute.ToSMILAttr(this);
2348 // Strings
2350 StringAttributesInfo info = GetStringInfo();
2351 for (uint32_t i = 0; i < info.mCount; i++) {
2352 if (aNamespaceID == info.mInfos[i].mNamespaceID &&
2353 aName == info.mInfos[i].mName) {
2354 return info.mValues[i].ToSMILAttr(this);
2359 return nullptr;
2362 void SVGElement::AnimationNeedsResample() {
2363 Document* doc = GetComposedDoc();
2364 if (doc && doc->HasAnimationController()) {
2365 doc->GetAnimationController()->SetResampleNeeded();
2369 void SVGElement::FlushAnimations() {
2370 Document* doc = GetComposedDoc();
2371 if (doc && doc->HasAnimationController()) {
2372 doc->GetAnimationController()->FlushResampleRequests();
2376 void SVGElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
2377 size_t* aNodeSize) const {
2378 Element::AddSizeOfExcludingThis(aSizes, aNodeSize);
2381 } // namespace mozilla::dom