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/. */
6
7 #include "mozilla/dom/SVGElement.h"
8
9 #include "mozilla/dom/MutationEventBinding.h"
10 #include "mozilla/dom/MutationObservers.h"
11 #include "mozilla/dom/CSSRuleBinding.h"
12 #include "mozilla/dom/SVGElementBinding.h"
13 #include "mozilla/dom/SVGGeometryElement.h"
14 #include "mozilla/dom/SVGLengthBinding.h"
15 #include "mozilla/dom/SVGSVGElement.h"
16 #include "mozilla/dom/SVGTests.h"
17 #include "mozilla/dom/SVGUnitTypesBinding.h"
18 #include "mozilla/dom/Element.h"
19
20 #include "mozilla/ArrayUtils.h"
21 #include "mozilla/DebugOnly.h"
22 #include "mozilla/DeclarationBlock.h"
23 #include "mozilla/EventListenerManager.h"
24 #include "mozilla/InternalMutationEvent.h"
25 #include "mozilla/PresShell.h"
26 #include "mozilla/RestyleManager.h"
27 #include "mozilla/SMILAnimationController.h"
28 #include "mozilla/StaticPrefs_layout.h"
29 #include "mozilla/SVGContentUtils.h"
30 #include "mozilla/Unused.h"
31
32 #include "mozAutoDocUpdate.h"
33 #include "nsAttrValueOrString.h"
34 #include "nsCSSProps.h"
35 #include "nsCSSValue.h"
36 #include "nsContentUtils.h"
37 #include "nsDOMCSSAttrDeclaration.h"
38 #include "nsICSSDeclaration.h"
39 #include "nsIContentInlines.h"
40 #include "mozilla/dom/Document.h"
41 #include "nsError.h"
42 #include "nsGkAtoms.h"
43 #include "nsIFrame.h"
44 #include "nsQueryObject.h"
45 #include "nsLayoutUtils.h"
46 #include "SVGAnimatedNumberList.h"
47 #include "SVGAnimatedLengthList.h"
48 #include "SVGAnimatedPointList.h"
49 #include "SVGAnimatedPathSegList.h"
50 #include "SVGAnimatedTransformList.h"
51 #include "SVGAnimatedBoolean.h"
52 #include "SVGAnimatedEnumeration.h"
53 #include "SVGAnimatedInteger.h"
54 #include "SVGAnimatedIntegerPair.h"
55 #include "SVGAnimatedLength.h"
56 #include "SVGAnimatedNumber.h"
57 #include "SVGAnimatedNumberPair.h"
58 #include "SVGAnimatedOrient.h"
59 #include "SVGAnimatedString.h"
60 #include "SVGAnimatedViewBox.h"
61 #include "SVGGeometryProperty.h"
62 #include "SVGMotionSMILAttr.h"
63 #include <stdarg.h>
64
65 // This is needed to ensure correct handling of calls to the
66 // vararg-list methods in this file:
67 // SVGElement::GetAnimated{Length,Number,Integer}Values
68 // See bug 547964 for details:
69 static_assert(sizeof(void*) == sizeof(nullptr),
70 "nullptr should be the correct size");
71
NS_NewSVGElement(mozilla::dom::Element ** aResult,already_AddRefed<mozilla::dom::NodeInfo> && aNodeInfo)72 nsresult NS_NewSVGElement(
73 mozilla::dom::Element** aResult,
74 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) {
75 RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
76 auto* nim = nodeInfo->NodeInfoManager();
77 RefPtr<mozilla::dom::SVGElement> it =
78 new (nim) mozilla::dom::SVGElement(nodeInfo.forget());
79 nsresult rv = it->Init();
80
81 if (NS_FAILED(rv)) {
82 return rv;
83 }
84
85 it.forget(aResult);
86 return rv;
87 }
88
89 namespace mozilla {
90 namespace dom {
91 using namespace SVGUnitTypes_Binding;
92
93 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGElement)
94
95 // Use the CC variant of this, even though this class does not define
96 // a new CC participant, to make QIing to the CC interfaces faster.
97 NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(SVGElement, SVGElementBase,
98 SVGElement)
99
100 SVGEnumMapping SVGElement::sSVGUnitTypesMap[] = {
101 {nsGkAtoms::userSpaceOnUse, SVG_UNIT_TYPE_USERSPACEONUSE},
102 {nsGkAtoms::objectBoundingBox, SVG_UNIT_TYPE_OBJECTBOUNDINGBOX},
103 {nullptr, 0}};
104
SVGElement(already_AddRefed<mozilla::dom::NodeInfo> && aNodeInfo)105 SVGElement::SVGElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
106 : SVGElementBase(std::move(aNodeInfo)) {}
107
~SVGElement()108 SVGElement::~SVGElement() {
109 OwnerDoc()->UnscheduleSVGForPresAttrEvaluation(this);
110 }
111
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)112 JSObject* SVGElement::WrapNode(JSContext* aCx,
113 JS::Handle<JSObject*> aGivenProto) {
114 return SVGElement_Binding::Wrap(aCx, this, aGivenProto);
115 }
116
117 template <typename Value, typename Info>
ResetAll()118 void SVGElement::AttributesInfo<Value, Info>::ResetAll() {
119 for (uint32_t i = 0; i < mCount; ++i) {
120 Reset(i);
121 }
122 }
123
124 template <typename Value, typename Info>
CopyAllFrom(const AttributesInfo & aOther)125 void SVGElement::AttributesInfo<Value, Info>::CopyAllFrom(
126 const AttributesInfo& aOther) {
127 MOZ_DIAGNOSTIC_ASSERT(mCount == aOther.mCount,
128 "Should only be called on clones");
129 for (uint32_t i = 0; i < mCount; ++i) {
130 mValues[i] = aOther.mValues[i];
131 }
132 }
133
134 template <>
Reset(uint8_t aAttrEnum)135 void SVGElement::LengthAttributesInfo::Reset(uint8_t aAttrEnum) {
136 mValues[aAttrEnum].Init(mInfos[aAttrEnum].mCtxType, aAttrEnum,
137 mInfos[aAttrEnum].mDefaultValue,
138 mInfos[aAttrEnum].mDefaultUnitType);
139 }
140
141 template <>
Reset(uint8_t aAttrEnum)142 void SVGElement::LengthListAttributesInfo::Reset(uint8_t aAttrEnum) {
143 mValues[aAttrEnum].ClearBaseValue(aAttrEnum);
144 // caller notifies
145 }
146
147 template <>
Reset(uint8_t aAttrEnum)148 void SVGElement::NumberListAttributesInfo::Reset(uint8_t aAttrEnum) {
149 MOZ_ASSERT(aAttrEnum < mCount, "Bad attr enum");
150 mValues[aAttrEnum].ClearBaseValue(aAttrEnum);
151 // caller notifies
152 }
153
154 template <>
Reset(uint8_t aAttrEnum)155 void SVGElement::NumberAttributesInfo::Reset(uint8_t aAttrEnum) {
156 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
157 }
158
159 template <>
Reset(uint8_t aAttrEnum)160 void SVGElement::NumberPairAttributesInfo::Reset(uint8_t aAttrEnum) {
161 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue1,
162 mInfos[aAttrEnum].mDefaultValue2);
163 }
164
165 template <>
Reset(uint8_t aAttrEnum)166 void SVGElement::IntegerAttributesInfo::Reset(uint8_t aAttrEnum) {
167 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
168 }
169
170 template <>
Reset(uint8_t aAttrEnum)171 void SVGElement::IntegerPairAttributesInfo::Reset(uint8_t aAttrEnum) {
172 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue1,
173 mInfos[aAttrEnum].mDefaultValue2);
174 }
175
176 template <>
Reset(uint8_t aAttrEnum)177 void SVGElement::BooleanAttributesInfo::Reset(uint8_t aAttrEnum) {
178 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
179 }
180
181 template <>
Reset(uint8_t aAttrEnum)182 void SVGElement::StringListAttributesInfo::Reset(uint8_t aAttrEnum) {
183 mValues[aAttrEnum].Clear();
184 // caller notifies
185 }
186
187 template <>
Reset(uint8_t aAttrEnum)188 void SVGElement::EnumAttributesInfo::Reset(uint8_t aAttrEnum) {
189 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
190 }
191
192 template <>
Reset(uint8_t aAttrEnum)193 void SVGElement::StringAttributesInfo::Reset(uint8_t aAttrEnum) {
194 mValues[aAttrEnum].Init(aAttrEnum);
195 }
196
CopyInnerTo(mozilla::dom::Element * aDest)197 nsresult SVGElement::CopyInnerTo(mozilla::dom::Element* aDest) {
198 nsresult rv = Element::CopyInnerTo(aDest);
199 NS_ENSURE_SUCCESS(rv, rv);
200
201 auto* dest = static_cast<SVGElement*>(aDest);
202
203 // cloning a node must retain its internal nonce slot
204 if (auto* nonce = static_cast<nsString*>(GetProperty(nsGkAtoms::nonce))) {
205 dest->SetNonce(*nonce);
206 }
207
208 // If our destination is a print document, copy all the relevant length values
209 // etc so that they match the state of the original node.
210 if (aDest->OwnerDoc()->IsStaticDocument()) {
211 dest->GetLengthInfo().CopyAllFrom(GetLengthInfo());
212 dest->GetNumberInfo().CopyAllFrom(GetNumberInfo());
213 dest->GetNumberPairInfo().CopyAllFrom(GetNumberPairInfo());
214 dest->GetIntegerInfo().CopyAllFrom(GetIntegerInfo());
215 dest->GetIntegerPairInfo().CopyAllFrom(GetIntegerPairInfo());
216 dest->GetEnumInfo().CopyAllFrom(GetEnumInfo());
217 dest->GetStringInfo().CopyAllFrom(GetStringInfo());
218 dest->GetLengthListInfo().CopyAllFrom(GetLengthListInfo());
219 dest->GetNumberListInfo().CopyAllFrom(GetNumberListInfo());
220 }
221
222 return NS_OK;
223 }
224
225 //----------------------------------------------------------------------
226 // SVGElement methods
227
DidAnimateClass()228 void SVGElement::DidAnimateClass() {
229 // For Servo, snapshot the element before we change it.
230 PresShell* presShell = OwnerDoc()->GetPresShell();
231 if (presShell) {
232 if (nsPresContext* presContext = presShell->GetPresContext()) {
233 presContext->RestyleManager()->ClassAttributeWillBeChangedBySMIL(this);
234 }
235 }
236
237 nsAutoString src;
238 mClassAttribute.GetAnimValue(src, this);
239 if (!mClassAnimAttr) {
240 mClassAnimAttr = MakeUnique<nsAttrValue>();
241 }
242 mClassAnimAttr->ParseAtomArray(src);
243
244 // FIXME(emilio): This re-selector-matches, but we do the snapshot stuff right
245 // above... Is this needed anymore?
246 if (presShell) {
247 presShell->RestyleForAnimation(this, RestyleHint::RESTYLE_SELF);
248 }
249 }
250
Init()251 nsresult SVGElement::Init() {
252 // Set up length attributes - can't do this in the constructor
253 // because we can't do a virtual call at that point
254
255 GetLengthInfo().ResetAll();
256 GetNumberInfo().ResetAll();
257 GetNumberPairInfo().ResetAll();
258 GetIntegerInfo().ResetAll();
259 GetIntegerPairInfo().ResetAll();
260 GetBooleanInfo().ResetAll();
261 GetEnumInfo().ResetAll();
262
263 if (SVGAnimatedOrient* orient = GetAnimatedOrient()) {
264 orient->Init();
265 }
266
267 if (SVGAnimatedViewBox* viewBox = GetAnimatedViewBox()) {
268 viewBox->Init();
269 }
270
271 if (SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
272 GetAnimatedPreserveAspectRatio()) {
273 preserveAspectRatio->Init();
274 }
275
276 GetLengthListInfo().ResetAll();
277 GetNumberListInfo().ResetAll();
278
279 // No need to reset SVGPointList since the default value is always the same
280 // (an empty list).
281
282 // No need to reset SVGPathData since the default value is always the same
283 // (an empty list).
284
285 GetStringInfo().ResetAll();
286 return NS_OK;
287 }
288
289 //----------------------------------------------------------------------
290 // Implementation
291
292 //----------------------------------------------------------------------
293 // nsIContent methods
294
BindToTree(BindContext & aContext,nsINode & aParent)295 nsresult SVGElement::BindToTree(BindContext& aContext, nsINode& aParent) {
296 nsresult rv = SVGElementBase::BindToTree(aContext, aParent);
297 NS_ENSURE_SUCCESS(rv, rv);
298
299 // Hide any nonce from the DOM, but keep the internal value of the
300 // nonce by copying and resetting the internal nonce value.
301 if (HasFlag(NODE_HAS_NONCE_AND_HEADER_CSP) && IsInComposedDoc() &&
302 OwnerDoc()->GetBrowsingContext()) {
303 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
304 "SVGElement::ResetNonce::Runnable",
305 [self = RefPtr<SVGElement>(this)]() {
306 nsAutoString nonce;
307 self->GetNonce(nonce);
308 self->SetAttr(kNameSpaceID_None, nsGkAtoms::nonce, u""_ns, true);
309 self->SetNonce(nonce);
310 }));
311 }
312
313 return NS_OK;
314 }
315
AfterSetAttr(int32_t aNamespaceID,nsAtom * aName,const nsAttrValue * aValue,const nsAttrValue * aOldValue,nsIPrincipal * aSubjectPrincipal,bool aNotify)316 nsresult SVGElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
317 const nsAttrValue* aValue,
318 const nsAttrValue* aOldValue,
319 nsIPrincipal* aSubjectPrincipal,
320 bool aNotify) {
321 // We don't currently use nsMappedAttributes within SVG. If this changes, we
322 // need to be very careful because some nsAttrValues used by SVG point to
323 // member data of SVG elements and if an nsAttrValue outlives the SVG element
324 // whose data it points to (by virtue of being stored in
325 // mAttrs->mMappedAttributes, meaning it's shared between
326 // elements), the pointer will dangle. See bug 724680.
327 MOZ_ASSERT(!mAttrs.HasMappedAttrs(),
328 "Unexpected use of nsMappedAttributes within SVG");
329
330 // If this is an svg presentation attribute we need to map it into
331 // the content declaration block.
332 // XXX For some reason incremental mapping doesn't work, so for now
333 // just delete the style rule and lazily reconstruct it as needed).
334 if (aNamespaceID == kNameSpaceID_None && IsAttributeMapped(aName)) {
335 mContentDeclarationBlock = nullptr;
336 OwnerDoc()->ScheduleSVGForPresAttrEvaluation(this);
337 }
338
339 if (IsEventAttributeName(aName) && aValue) {
340 MOZ_ASSERT(aValue->Type() == nsAttrValue::eString,
341 "Expected string value for script body");
342 SetEventHandler(GetEventNameForAttr(aName), aValue->GetStringValue());
343 }
344
345 // The nonce will be copied over to an internal slot and cleared from the
346 // Element within BindToTree to avoid CSS Selector nonce exfiltration if
347 // the CSP list contains a header-delivered CSP.
348 if (nsGkAtoms::nonce == aName && kNameSpaceID_None == aNamespaceID) {
349 if (aValue) {
350 SetNonce(aValue->GetStringValue());
351 if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) {
352 SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP);
353 }
354 } else {
355 RemoveNonce();
356 }
357 }
358
359 return SVGElementBase::AfterSetAttr(aNamespaceID, aName, aValue, aOldValue,
360 aSubjectPrincipal, aNotify);
361 }
362
ParseAttribute(int32_t aNamespaceID,nsAtom * aAttribute,const nsAString & aValue,nsIPrincipal * aMaybeScriptedPrincipal,nsAttrValue & aResult)363 bool SVGElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
364 const nsAString& aValue,
365 nsIPrincipal* aMaybeScriptedPrincipal,
366 nsAttrValue& aResult) {
367 nsresult rv = NS_OK;
368 bool foundMatch = false;
369 bool didSetResult = false;
370
371 if (aNamespaceID == kNameSpaceID_None) {
372 // Check for SVGAnimatedLength attribute
373 LengthAttributesInfo lengthInfo = GetLengthInfo();
374
375 uint32_t i;
376 for (i = 0; i < lengthInfo.mCount; i++) {
377 if (aAttribute == lengthInfo.mInfos[i].mName) {
378 rv = lengthInfo.mValues[i].SetBaseValueString(aValue, this, false);
379 if (NS_FAILED(rv)) {
380 lengthInfo.Reset(i);
381 } else {
382 aResult.SetTo(lengthInfo.mValues[i], &aValue);
383 didSetResult = true;
384 }
385 foundMatch = true;
386 break;
387 }
388 }
389
390 if (!foundMatch) {
391 // Check for SVGAnimatedLengthList attribute
392 LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
393 for (i = 0; i < lengthListInfo.mCount; i++) {
394 if (aAttribute == lengthListInfo.mInfos[i].mName) {
395 rv = lengthListInfo.mValues[i].SetBaseValueString(aValue);
396 if (NS_FAILED(rv)) {
397 lengthListInfo.Reset(i);
398 } else {
399 aResult.SetTo(lengthListInfo.mValues[i].GetBaseValue(), &aValue);
400 didSetResult = true;
401 }
402 foundMatch = true;
403 break;
404 }
405 }
406 }
407
408 if (!foundMatch) {
409 // Check for SVGAnimatedNumberList attribute
410 NumberListAttributesInfo numberListInfo = GetNumberListInfo();
411 for (i = 0; i < numberListInfo.mCount; i++) {
412 if (aAttribute == numberListInfo.mInfos[i].mName) {
413 rv = numberListInfo.mValues[i].SetBaseValueString(aValue);
414 if (NS_FAILED(rv)) {
415 numberListInfo.Reset(i);
416 } else {
417 aResult.SetTo(numberListInfo.mValues[i].GetBaseValue(), &aValue);
418 didSetResult = true;
419 }
420 foundMatch = true;
421 break;
422 }
423 }
424 }
425
426 if (!foundMatch) {
427 // Check for SVGAnimatedPointList attribute
428 if (GetPointListAttrName() == aAttribute) {
429 if (SVGAnimatedPointList* pointList = GetAnimatedPointList()) {
430 pointList->SetBaseValueString(aValue);
431 // The spec says we parse everything up to the failure, so we DON'T
432 // need to check the result of SetBaseValueString or call
433 // pointList->ClearBaseValue() if it fails
434 aResult.SetTo(pointList->GetBaseValue(), &aValue);
435 didSetResult = true;
436 foundMatch = true;
437 }
438 }
439 }
440
441 if (!foundMatch) {
442 // Check for SVGAnimatedPathSegList attribute
443 if (GetPathDataAttrName() == aAttribute) {
444 if (SVGAnimatedPathSegList* segList = GetAnimPathSegList()) {
445 segList->SetBaseValueString(aValue);
446 // The spec says we parse everything up to the failure, so we DON'T
447 // need to check the result of SetBaseValueString or call
448 // segList->ClearBaseValue() if it fails
449 aResult.SetTo(segList->GetBaseValue(), &aValue);
450 didSetResult = true;
451 foundMatch = true;
452 }
453 }
454 }
455
456 if (!foundMatch) {
457 // Check for SVGAnimatedNumber attribute
458 NumberAttributesInfo numberInfo = GetNumberInfo();
459 for (i = 0; i < numberInfo.mCount; i++) {
460 if (aAttribute == numberInfo.mInfos[i].mName) {
461 rv = numberInfo.mValues[i].SetBaseValueString(aValue, this);
462 if (NS_FAILED(rv)) {
463 numberInfo.Reset(i);
464 } else {
465 aResult.SetTo(numberInfo.mValues[i].GetBaseValue(), &aValue);
466 didSetResult = true;
467 }
468 foundMatch = true;
469 break;
470 }
471 }
472 }
473
474 if (!foundMatch) {
475 // Check for SVGAnimatedNumberPair attribute
476 NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo();
477 for (i = 0; i < numberPairInfo.mCount; i++) {
478 if (aAttribute == numberPairInfo.mInfos[i].mName) {
479 rv = numberPairInfo.mValues[i].SetBaseValueString(aValue, this);
480 if (NS_FAILED(rv)) {
481 numberPairInfo.Reset(i);
482 } else {
483 aResult.SetTo(numberPairInfo.mValues[i], &aValue);
484 didSetResult = true;
485 }
486 foundMatch = true;
487 break;
488 }
489 }
490 }
491
492 if (!foundMatch) {
493 // Check for SVGAnimatedInteger attribute
494 IntegerAttributesInfo integerInfo = GetIntegerInfo();
495 for (i = 0; i < integerInfo.mCount; i++) {
496 if (aAttribute == integerInfo.mInfos[i].mName) {
497 rv = integerInfo.mValues[i].SetBaseValueString(aValue, this);
498 if (NS_FAILED(rv)) {
499 integerInfo.Reset(i);
500 } else {
501 aResult.SetTo(integerInfo.mValues[i].GetBaseValue(), &aValue);
502 didSetResult = true;
503 }
504 foundMatch = true;
505 break;
506 }
507 }
508 }
509
510 if (!foundMatch) {
511 // Check for SVGAnimatedIntegerPair attribute
512 IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo();
513 for (i = 0; i < integerPairInfo.mCount; i++) {
514 if (aAttribute == integerPairInfo.mInfos[i].mName) {
515 rv = integerPairInfo.mValues[i].SetBaseValueString(aValue, this);
516 if (NS_FAILED(rv)) {
517 integerPairInfo.Reset(i);
518 } else {
519 aResult.SetTo(integerPairInfo.mValues[i], &aValue);
520 didSetResult = true;
521 }
522 foundMatch = true;
523 break;
524 }
525 }
526 }
527
528 if (!foundMatch) {
529 // Check for SVGAnimatedBoolean attribute
530 BooleanAttributesInfo booleanInfo = GetBooleanInfo();
531 for (i = 0; i < booleanInfo.mCount; i++) {
532 if (aAttribute == booleanInfo.mInfos[i].mName) {
533 nsAtom* valAtom = NS_GetStaticAtom(aValue);
534 rv = valAtom ? booleanInfo.mValues[i].SetBaseValueAtom(valAtom, this)
535 : NS_ERROR_DOM_SYNTAX_ERR;
536 if (NS_FAILED(rv)) {
537 booleanInfo.Reset(i);
538 } else {
539 aResult.SetTo(valAtom);
540 didSetResult = true;
541 }
542 foundMatch = true;
543 break;
544 }
545 }
546 }
547
548 if (!foundMatch) {
549 // Check for SVGAnimatedEnumeration attribute
550 EnumAttributesInfo enumInfo = GetEnumInfo();
551 for (i = 0; i < enumInfo.mCount; i++) {
552 if (aAttribute == enumInfo.mInfos[i].mName) {
553 RefPtr<nsAtom> valAtom = NS_Atomize(aValue);
554 if (!enumInfo.mValues[i].SetBaseValueAtom(valAtom, this)) {
555 // Exact error value does not matter; we just need to mark the
556 // parse as failed.
557 rv = NS_ERROR_FAILURE;
558 enumInfo.Reset(i);
559 } else {
560 aResult.SetTo(valAtom);
561 didSetResult = true;
562 }
563 foundMatch = true;
564 break;
565 }
566 }
567 }
568
569 if (!foundMatch) {
570 // Check for conditional processing attributes
571 nsCOMPtr<SVGTests> tests = do_QueryObject(this);
572 if (tests && tests->ParseConditionalProcessingAttribute(
573 aAttribute, aValue, aResult)) {
574 foundMatch = true;
575 }
576 }
577
578 if (!foundMatch) {
579 // Check for StringList attribute
580 StringListAttributesInfo stringListInfo = GetStringListInfo();
581 for (i = 0; i < stringListInfo.mCount; i++) {
582 if (aAttribute == stringListInfo.mInfos[i].mName) {
583 rv = stringListInfo.mValues[i].SetValue(aValue);
584 if (NS_FAILED(rv)) {
585 stringListInfo.Reset(i);
586 } else {
587 aResult.SetTo(stringListInfo.mValues[i], &aValue);
588 didSetResult = true;
589 }
590 foundMatch = true;
591 break;
592 }
593 }
594 }
595
596 if (!foundMatch) {
597 // Check for orient attribute
598 if (aAttribute == nsGkAtoms::orient) {
599 SVGAnimatedOrient* orient = GetAnimatedOrient();
600 if (orient) {
601 rv = orient->SetBaseValueString(aValue, this, false);
602 if (NS_FAILED(rv)) {
603 orient->Init();
604 } else {
605 aResult.SetTo(*orient, &aValue);
606 didSetResult = true;
607 }
608 foundMatch = true;
609 }
610 // Check for viewBox attribute
611 } else if (aAttribute == nsGkAtoms::viewBox) {
612 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
613 if (viewBox) {
614 rv = viewBox->SetBaseValueString(aValue, this, false);
615 if (NS_FAILED(rv)) {
616 viewBox->Init();
617 } else {
618 aResult.SetTo(*viewBox, &aValue);
619 didSetResult = true;
620 }
621 foundMatch = true;
622 }
623 // Check for preserveAspectRatio attribute
624 } else if (aAttribute == nsGkAtoms::preserveAspectRatio) {
625 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
626 GetAnimatedPreserveAspectRatio();
627 if (preserveAspectRatio) {
628 rv = preserveAspectRatio->SetBaseValueString(aValue, this, false);
629 if (NS_FAILED(rv)) {
630 preserveAspectRatio->Init();
631 } else {
632 aResult.SetTo(*preserveAspectRatio, &aValue);
633 didSetResult = true;
634 }
635 foundMatch = true;
636 }
637 // Check for SVGAnimatedTransformList attribute
638 } else if (GetTransformListAttrName() == aAttribute) {
639 // The transform attribute is being set, so we must ensure that the
640 // SVGAnimatedTransformList is/has been allocated:
641 SVGAnimatedTransformList* transformList =
642 GetAnimatedTransformList(DO_ALLOCATE);
643 rv = transformList->SetBaseValueString(aValue, this);
644 if (NS_FAILED(rv)) {
645 transformList->ClearBaseValue();
646 } else {
647 aResult.SetTo(transformList->GetBaseValue(), &aValue);
648 didSetResult = true;
649 }
650 foundMatch = true;
651 } else if (aAttribute == nsGkAtoms::tabindex) {
652 didSetResult = aResult.ParseIntValue(aValue);
653 foundMatch = true;
654 }
655 }
656
657 if (aAttribute == nsGkAtoms::_class) {
658 mClassAttribute.SetBaseValue(aValue, this, false);
659 aResult.ParseAtomArray(aValue);
660 return true;
661 }
662
663 if (aAttribute == nsGkAtoms::rel) {
664 aResult.ParseAtomArray(aValue);
665 return true;
666 }
667 }
668
669 if (!foundMatch) {
670 // Check for SVGAnimatedString attribute
671 StringAttributesInfo stringInfo = GetStringInfo();
672 for (uint32_t i = 0; i < stringInfo.mCount; i++) {
673 if (aNamespaceID == stringInfo.mInfos[i].mNamespaceID &&
674 aAttribute == stringInfo.mInfos[i].mName) {
675 stringInfo.mValues[i].SetBaseValue(aValue, this, false);
676 foundMatch = true;
677 break;
678 }
679 }
680 }
681
682 if (foundMatch) {
683 if (NS_FAILED(rv)) {
684 ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue);
685 return false;
686 }
687 if (!didSetResult) {
688 aResult.SetTo(aValue);
689 }
690 return true;
691 }
692
693 return SVGElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
694 aMaybeScriptedPrincipal, aResult);
695 }
696
UnsetAttrInternal(int32_t aNamespaceID,nsAtom * aName,bool aNotify)697 void SVGElement::UnsetAttrInternal(int32_t aNamespaceID, nsAtom* aName,
698 bool aNotify) {
699 // XXXbz there's a bunch of redundancy here with AfterSetAttr.
700 // Maybe consolidate?
701
702 if (aNamespaceID == kNameSpaceID_None) {
703 // If this is an svg presentation attribute, remove declaration block to
704 // force an update
705 if (IsAttributeMapped(aName)) {
706 mContentDeclarationBlock = nullptr;
707 }
708
709 if (IsEventAttributeName(aName)) {
710 EventListenerManager* manager = GetExistingListenerManager();
711 if (manager) {
712 nsAtom* eventName = GetEventNameForAttr(aName);
713 manager->RemoveEventHandler(eventName);
714 }
715 return;
716 }
717
718 // Check if this is a length attribute going away
719 LengthAttributesInfo lenInfo = GetLengthInfo();
720
721 for (uint32_t i = 0; i < lenInfo.mCount; i++) {
722 if (aName == lenInfo.mInfos[i].mName) {
723 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
724 lenInfo.Reset(i);
725 return;
726 }
727 }
728
729 // Check if this is a length list attribute going away
730 LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
731
732 for (uint32_t i = 0; i < lengthListInfo.mCount; i++) {
733 if (aName == lengthListInfo.mInfos[i].mName) {
734 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
735 lengthListInfo.Reset(i);
736 return;
737 }
738 }
739
740 // Check if this is a number list attribute going away
741 NumberListAttributesInfo numberListInfo = GetNumberListInfo();
742
743 for (uint32_t i = 0; i < numberListInfo.mCount; i++) {
744 if (aName == numberListInfo.mInfos[i].mName) {
745 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
746 numberListInfo.Reset(i);
747 return;
748 }
749 }
750
751 // Check if this is a point list attribute going away
752 if (GetPointListAttrName() == aName) {
753 SVGAnimatedPointList* pointList = GetAnimatedPointList();
754 if (pointList) {
755 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
756 pointList->ClearBaseValue();
757 return;
758 }
759 }
760
761 // Check if this is a path segment list attribute going away
762 if (GetPathDataAttrName() == aName) {
763 SVGAnimatedPathSegList* segList = GetAnimPathSegList();
764 if (segList) {
765 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
766 segList->ClearBaseValue();
767 return;
768 }
769 }
770
771 // Check if this is a number attribute going away
772 NumberAttributesInfo numInfo = GetNumberInfo();
773
774 for (uint32_t i = 0; i < numInfo.mCount; i++) {
775 if (aName == numInfo.mInfos[i].mName) {
776 numInfo.Reset(i);
777 return;
778 }
779 }
780
781 // Check if this is a number pair attribute going away
782 NumberPairAttributesInfo numPairInfo = GetNumberPairInfo();
783
784 for (uint32_t i = 0; i < numPairInfo.mCount; i++) {
785 if (aName == numPairInfo.mInfos[i].mName) {
786 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
787 numPairInfo.Reset(i);
788 return;
789 }
790 }
791
792 // Check if this is an integer attribute going away
793 IntegerAttributesInfo intInfo = GetIntegerInfo();
794
795 for (uint32_t i = 0; i < intInfo.mCount; i++) {
796 if (aName == intInfo.mInfos[i].mName) {
797 intInfo.Reset(i);
798 return;
799 }
800 }
801
802 // Check if this is an integer pair attribute going away
803 IntegerPairAttributesInfo intPairInfo = GetIntegerPairInfo();
804
805 for (uint32_t i = 0; i < intPairInfo.mCount; i++) {
806 if (aName == intPairInfo.mInfos[i].mName) {
807 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
808 intPairInfo.Reset(i);
809 return;
810 }
811 }
812
813 // Check if this is a boolean attribute going away
814 BooleanAttributesInfo boolInfo = GetBooleanInfo();
815
816 for (uint32_t i = 0; i < boolInfo.mCount; i++) {
817 if (aName == boolInfo.mInfos[i].mName) {
818 boolInfo.Reset(i);
819 return;
820 }
821 }
822
823 // Check if this is an enum attribute going away
824 EnumAttributesInfo enumInfo = GetEnumInfo();
825
826 for (uint32_t i = 0; i < enumInfo.mCount; i++) {
827 if (aName == enumInfo.mInfos[i].mName) {
828 enumInfo.Reset(i);
829 return;
830 }
831 }
832
833 // Check if this is an orient attribute going away
834 if (aName == nsGkAtoms::orient) {
835 SVGAnimatedOrient* orient = GetAnimatedOrient();
836 if (orient) {
837 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
838 orient->Init();
839 return;
840 }
841 }
842
843 // Check if this is a viewBox attribute going away
844 if (aName == nsGkAtoms::viewBox) {
845 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
846 if (viewBox) {
847 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
848 viewBox->Init();
849 return;
850 }
851 }
852
853 // Check if this is a preserveAspectRatio attribute going away
854 if (aName == nsGkAtoms::preserveAspectRatio) {
855 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
856 GetAnimatedPreserveAspectRatio();
857 if (preserveAspectRatio) {
858 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
859 preserveAspectRatio->Init();
860 return;
861 }
862 }
863
864 // Check if this is a transform list attribute going away
865 if (GetTransformListAttrName() == aName) {
866 SVGAnimatedTransformList* transformList = GetAnimatedTransformList();
867 if (transformList) {
868 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
869 transformList->ClearBaseValue();
870 return;
871 }
872 }
873
874 // Check for conditional processing attributes
875 nsCOMPtr<SVGTests> tests = do_QueryObject(this);
876 if (tests && tests->IsConditionalProcessingAttribute(aName)) {
877 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
878 tests->UnsetAttr(aName);
879 return;
880 }
881
882 // Check if this is a string list attribute going away
883 StringListAttributesInfo stringListInfo = GetStringListInfo();
884
885 for (uint32_t i = 0; i < stringListInfo.mCount; i++) {
886 if (aName == stringListInfo.mInfos[i].mName) {
887 MaybeSerializeAttrBeforeRemoval(aName, aNotify);
888 stringListInfo.Reset(i);
889 return;
890 }
891 }
892
893 if (aName == nsGkAtoms::_class) {
894 mClassAttribute.Init();
895 return;
896 }
897 }
898
899 // Check if this is a string attribute going away
900 StringAttributesInfo stringInfo = GetStringInfo();
901
902 for (uint32_t i = 0; i < stringInfo.mCount; i++) {
903 if (aNamespaceID == stringInfo.mInfos[i].mNamespaceID &&
904 aName == stringInfo.mInfos[i].mName) {
905 stringInfo.Reset(i);
906 return;
907 }
908 }
909 }
910
BeforeSetAttr(int32_t aNamespaceID,nsAtom * aName,const nsAttrValueOrString * aValue,bool aNotify)911 nsresult SVGElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
912 const nsAttrValueOrString* aValue,
913 bool aNotify) {
914 if (!aValue) {
915 UnsetAttrInternal(aNamespaceID, aName, aNotify);
916 }
917 return SVGElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify);
918 }
919
GetAttributeChangeHint(const nsAtom * aAttribute,int32_t aModType) const920 nsChangeHint SVGElement::GetAttributeChangeHint(const nsAtom* aAttribute,
921 int32_t aModType) const {
922 nsChangeHint retval =
923 SVGElementBase::GetAttributeChangeHint(aAttribute, aModType);
924
925 nsCOMPtr<SVGTests> tests = do_QueryObject(const_cast<SVGElement*>(this));
926 if (tests && tests->IsConditionalProcessingAttribute(aAttribute)) {
927 // It would be nice to only reconstruct the frame if the value returned by
928 // SVGTests::PassesConditionalProcessingTests has changed, but we don't
929 // know that
930 retval |= nsChangeHint_ReconstructFrame;
931 }
932 return retval;
933 }
934
IsNodeOfType(uint32_t aFlags) const935 bool SVGElement::IsNodeOfType(uint32_t aFlags) const { return false; }
936
NodeInfoChanged(Document * aOldDoc)937 void SVGElement::NodeInfoChanged(Document* aOldDoc) {
938 SVGElementBase::NodeInfoChanged(aOldDoc);
939 aOldDoc->UnscheduleSVGForPresAttrEvaluation(this);
940 mContentDeclarationBlock = nullptr;
941 OwnerDoc()->ScheduleSVGForPresAttrEvaluation(this);
942 }
943
NS_IMETHODIMP_(bool)944 NS_IMETHODIMP_(bool)
945 SVGElement::IsAttributeMapped(const nsAtom* name) const {
946 if (name == nsGkAtoms::lang) {
947 return true;
948 }
949 return SVGElementBase::IsAttributeMapped(name);
950 }
951
952 // PresentationAttributes-FillStroke
953 /* static */
954 const Element::MappedAttributeEntry SVGElement::sFillStrokeMap[] = {
955 {nsGkAtoms::fill},
956 {nsGkAtoms::fill_opacity},
957 {nsGkAtoms::fill_rule},
958 {nsGkAtoms::paint_order},
959 {nsGkAtoms::stroke},
960 {nsGkAtoms::stroke_dasharray},
961 {nsGkAtoms::stroke_dashoffset},
962 {nsGkAtoms::stroke_linecap},
963 {nsGkAtoms::stroke_linejoin},
964 {nsGkAtoms::stroke_miterlimit},
965 {nsGkAtoms::stroke_opacity},
966 {nsGkAtoms::stroke_width},
967 {nsGkAtoms::vector_effect},
968 {nullptr}};
969
970 // PresentationAttributes-Graphics
971 /* static */
972 const Element::MappedAttributeEntry SVGElement::sGraphicsMap[] = {
973 {nsGkAtoms::clip_path},
974 {nsGkAtoms::clip_rule},
975 {nsGkAtoms::colorInterpolation},
976 {nsGkAtoms::cursor},
977 {nsGkAtoms::display},
978 {nsGkAtoms::filter},
979 {nsGkAtoms::image_rendering},
980 {nsGkAtoms::mask},
981 {nsGkAtoms::opacity},
982 {nsGkAtoms::pointer_events},
983 {nsGkAtoms::shape_rendering},
984 {nsGkAtoms::text_rendering},
985 {nsGkAtoms::transform_origin},
986 {nsGkAtoms::visibility},
987 {nullptr}};
988
989 // PresentationAttributes-TextContentElements
990 /* static */
991 const Element::MappedAttributeEntry SVGElement::sTextContentElementsMap[] = {
992 // Properties that we don't support are commented out.
993 // { nsGkAtoms::alignment_baseline },
994 // { nsGkAtoms::baseline_shift },
995 {nsGkAtoms::direction},
996 {nsGkAtoms::dominant_baseline},
997 {nsGkAtoms::letter_spacing},
998 {nsGkAtoms::text_anchor},
999 {nsGkAtoms::text_decoration},
1000 {nsGkAtoms::unicode_bidi},
1001 {nsGkAtoms::word_spacing},
1002 {nsGkAtoms::writing_mode},
1003 {nullptr}};
1004
1005 // PresentationAttributes-FontSpecification
1006 /* static */
1007 const Element::MappedAttributeEntry SVGElement::sFontSpecificationMap[] = {
1008 {nsGkAtoms::font_family}, {nsGkAtoms::font_size},
1009 {nsGkAtoms::font_size_adjust}, {nsGkAtoms::font_stretch},
1010 {nsGkAtoms::font_style}, {nsGkAtoms::font_variant},
1011 {nsGkAtoms::fontWeight}, {nullptr}};
1012
1013 // PresentationAttributes-GradientStop
1014 /* static */
1015 const Element::MappedAttributeEntry SVGElement::sGradientStopMap[] = {
1016 {nsGkAtoms::stop_color}, {nsGkAtoms::stop_opacity}, {nullptr}};
1017
1018 // PresentationAttributes-Viewports
1019 /* static */
1020 const Element::MappedAttributeEntry SVGElement::sViewportsMap[] = {
1021 {nsGkAtoms::overflow}, {nsGkAtoms::clip}, {nullptr}};
1022
1023 // PresentationAttributes-Makers
1024 /* static */
1025 const Element::MappedAttributeEntry SVGElement::sMarkersMap[] = {
1026 {nsGkAtoms::marker_end},
1027 {nsGkAtoms::marker_mid},
1028 {nsGkAtoms::marker_start},
1029 {nullptr}};
1030
1031 // PresentationAttributes-Color
1032 /* static */
1033 const Element::MappedAttributeEntry SVGElement::sColorMap[] = {
1034 {nsGkAtoms::color}, {nullptr}};
1035
1036 // PresentationAttributes-Filters
1037 /* static */
1038 const Element::MappedAttributeEntry SVGElement::sFiltersMap[] = {
1039 {nsGkAtoms::colorInterpolationFilters}, {nullptr}};
1040
1041 // PresentationAttributes-feFlood
1042 /* static */
1043 const Element::MappedAttributeEntry SVGElement::sFEFloodMap[] = {
1044 {nsGkAtoms::flood_color}, {nsGkAtoms::flood_opacity}, {nullptr}};
1045
1046 // PresentationAttributes-LightingEffects
1047 /* static */
1048 const Element::MappedAttributeEntry SVGElement::sLightingEffectsMap[] = {
1049 {nsGkAtoms::lighting_color}, {nullptr}};
1050
1051 // PresentationAttributes-mask
1052 /* static */
1053 const Element::MappedAttributeEntry SVGElement::sMaskMap[] = {
1054 {nsGkAtoms::mask_type}, {nullptr}};
1055
1056 //----------------------------------------------------------------------
1057 // Element methods
1058
1059 // forwarded to Element implementations
1060
1061 //----------------------------------------------------------------------
1062
GetOwnerSVGElement()1063 SVGSVGElement* SVGElement::GetOwnerSVGElement() {
1064 nsIContent* ancestor = GetFlattenedTreeParent();
1065
1066 while (ancestor && ancestor->IsSVGElement()) {
1067 if (ancestor->IsSVGElement(nsGkAtoms::foreignObject)) {
1068 return nullptr;
1069 }
1070 if (ancestor->IsSVGElement(nsGkAtoms::svg)) {
1071 return static_cast<SVGSVGElement*>(ancestor);
1072 }
1073 ancestor = ancestor->GetFlattenedTreeParent();
1074 }
1075
1076 // we don't have an ancestor <svg> element...
1077 return nullptr;
1078 }
1079
GetViewportElement()1080 SVGElement* SVGElement::GetViewportElement() {
1081 return SVGContentUtils::GetNearestViewportElement(this);
1082 }
1083
ClassName()1084 already_AddRefed<DOMSVGAnimatedString> SVGElement::ClassName() {
1085 return mClassAttribute.ToDOMAnimatedString(this);
1086 }
1087
1088 /* static */
UpdateDeclarationBlockFromLength(DeclarationBlock & aBlock,nsCSSPropertyID aPropId,const SVGAnimatedLength & aLength,ValToUse aValToUse)1089 bool SVGElement::UpdateDeclarationBlockFromLength(
1090 DeclarationBlock& aBlock, nsCSSPropertyID aPropId,
1091 const SVGAnimatedLength& aLength, ValToUse aValToUse) {
1092 aBlock.AssertMutable();
1093
1094 float value;
1095 if (aValToUse == ValToUse::Anim) {
1096 value = aLength.GetAnimValInSpecifiedUnits();
1097 } else {
1098 MOZ_ASSERT(aValToUse == ValToUse::Base);
1099 value = aLength.GetBaseValInSpecifiedUnits();
1100 }
1101
1102 // SVG parser doesn't check non-negativity of some parsed value,
1103 // we should not pass those to CSS side.
1104 if (value < 0 &&
1105 SVGGeometryProperty::IsNonNegativeGeometryProperty(aPropId)) {
1106 return false;
1107 }
1108
1109 nsCSSUnit cssUnit = SVGGeometryProperty::SpecifiedUnitTypeToCSSUnit(
1110 aLength.GetSpecifiedUnitType());
1111
1112 if (cssUnit == eCSSUnit_Percent) {
1113 Servo_DeclarationBlock_SetPercentValue(aBlock.Raw(), aPropId,
1114 value / 100.f);
1115 } else {
1116 Servo_DeclarationBlock_SetLengthValue(aBlock.Raw(), aPropId, value,
1117 cssUnit);
1118 }
1119
1120 return true;
1121 }
1122
1123 /* static */
UpdateDeclarationBlockFromPath(DeclarationBlock & aBlock,const SVGAnimatedPathSegList & aPath,ValToUse aValToUse)1124 bool SVGElement::UpdateDeclarationBlockFromPath(
1125 DeclarationBlock& aBlock, const SVGAnimatedPathSegList& aPath,
1126 ValToUse aValToUse) {
1127 aBlock.AssertMutable();
1128
1129 const SVGPathData& pathData =
1130 aValToUse == ValToUse::Anim ? aPath.GetAnimValue() : aPath.GetBaseValue();
1131
1132 // SVGPathData::mData is fallible but rust binding accepts nsTArray only, so
1133 // we need to point to one or the other. Fortunately, fallible and infallible
1134 // array types can be implicitly converted provided they are const.
1135 //
1136 // FIXME: here we just convert the data structure from cpp verion into rust
1137 // version. We don't do any normalization for the path data from d attribute.
1138 // Based on the current discussion of https://github.com/w3c/svgwg/issues/321,
1139 // we may have to convert the relative commands into absolute commands.
1140 // The normalization should be fixed in Bug 1489392. Besides, Bug 1714238
1141 // will use the same data structure, so we may simplify this more.
1142 const nsTArray<float>& asInFallibleArray = pathData.RawData();
1143 Servo_DeclarationBlock_SetPathValue(aBlock.Raw(), eCSSProperty_d,
1144 &asInFallibleArray);
1145 return true;
1146 }
1147
1148 //------------------------------------------------------------------------
1149 // Helper class: MappedAttrParser, for parsing values of mapped attributes
1150
1151 namespace {
1152
1153 class MOZ_STACK_CLASS MappedAttrParser {
1154 public:
1155 MappedAttrParser(css::Loader* aLoader, nsIURI* aBaseURI,
1156 SVGElement* aElement);
1157 ~MappedAttrParser();
1158
1159 // Parses a mapped attribute value.
1160 void ParseMappedAttrValue(nsAtom* aMappedAttrName,
1161 const nsAString& aMappedAttrValue);
1162
1163 void TellStyleAlreadyParsedResult(nsAtom const* aAtom,
1164 SVGAnimatedLength const& aLength);
1165 void TellStyleAlreadyParsedResult(const SVGAnimatedPathSegList& aPath);
1166
1167 // If we've parsed any values for mapped attributes, this method returns the
1168 // already_AddRefed css::Declaration that incorporates the parsed
1169 // values. Otherwise, this method returns null.
1170 already_AddRefed<DeclarationBlock> GetDeclarationBlock();
1171
1172 private:
1173 // MEMBER DATA
1174 // -----------
1175 css::Loader* mLoader;
1176
1177 nsCOMPtr<nsIURI> mBaseURI;
1178
1179 // Declaration for storing parsed values (lazily initialized)
1180 RefPtr<DeclarationBlock> mDecl;
1181
1182 // For reporting use counters
1183 SVGElement* mElement;
1184 };
1185
MappedAttrParser(css::Loader * aLoader,nsIURI * aBaseURI,SVGElement * aElement)1186 MappedAttrParser::MappedAttrParser(css::Loader* aLoader, nsIURI* aBaseURI,
1187 SVGElement* aElement)
1188 : mLoader(aLoader), mBaseURI(aBaseURI), mElement(aElement) {}
1189
~MappedAttrParser()1190 MappedAttrParser::~MappedAttrParser() {
1191 MOZ_ASSERT(!mDecl,
1192 "If mDecl was initialized, it should have been returned via "
1193 "GetDeclarationBlock (and had its pointer cleared)");
1194 }
1195
ParseMappedAttrValue(nsAtom * aMappedAttrName,const nsAString & aMappedAttrValue)1196 void MappedAttrParser::ParseMappedAttrValue(nsAtom* aMappedAttrName,
1197 const nsAString& aMappedAttrValue) {
1198 if (!mDecl) {
1199 mDecl = new DeclarationBlock();
1200 }
1201
1202 // Get the nsCSSPropertyID ID for our mapped attribute.
1203 nsCSSPropertyID propertyID =
1204 nsCSSProps::LookupProperty(nsAtomCString(aMappedAttrName));
1205 if (propertyID != eCSSProperty_UNKNOWN) {
1206 bool changed = false; // outparam for ParseProperty.
1207 NS_ConvertUTF16toUTF8 value(aMappedAttrValue);
1208
1209 // FIXME (bug 1343964): Figure out a better solution for sending the base
1210 // uri to servo
1211 nsCOMPtr<nsIReferrerInfo> referrerInfo =
1212 ReferrerInfo::CreateForSVGResources(mElement->OwnerDoc());
1213
1214 auto data = MakeRefPtr<URLExtraData>(mBaseURI, referrerInfo,
1215 mElement->NodePrincipal());
1216 changed = Servo_DeclarationBlock_SetPropertyById(
1217 mDecl->Raw(), propertyID, &value, false, data,
1218 ParsingMode::AllowUnitlessLength,
1219 mElement->OwnerDoc()->GetCompatibilityMode(), mLoader,
1220 CSSRule_Binding::STYLE_RULE, {});
1221
1222 // TODO(emilio): If we want to record these from CSSOM more generally, we
1223 // can pass the document use counters down the FFI call. For now manually
1224 // count them.
1225 if (changed && StaticPrefs::layout_css_use_counters_enabled()) {
1226 UseCounter useCounter = nsCSSProps::UseCounterFor(propertyID);
1227 MOZ_ASSERT(useCounter != eUseCounter_UNKNOWN);
1228 mElement->OwnerDoc()->SetUseCounter(useCounter);
1229 }
1230 return;
1231 }
1232 MOZ_ASSERT(aMappedAttrName == nsGkAtoms::lang,
1233 "Only 'lang' should be unrecognized!");
1234 // CSS parser doesn't know about 'lang', so we need to handle it specially.
1235 if (aMappedAttrName == nsGkAtoms::lang) {
1236 propertyID = eCSSProperty__x_lang;
1237 RefPtr<nsAtom> atom = NS_Atomize(aMappedAttrValue);
1238 Servo_DeclarationBlock_SetIdentStringValue(mDecl->Raw(), propertyID, atom);
1239 }
1240 }
1241
TellStyleAlreadyParsedResult(nsAtom const * aAtom,SVGAnimatedLength const & aLength)1242 void MappedAttrParser::TellStyleAlreadyParsedResult(
1243 nsAtom const* aAtom, SVGAnimatedLength const& aLength) {
1244 if (!mDecl) {
1245 mDecl = new DeclarationBlock();
1246 }
1247 nsCSSPropertyID propertyID = nsCSSProps::LookupProperty(nsAtomCString(aAtom));
1248 SVGElement::UpdateDeclarationBlockFromLength(*mDecl, propertyID, aLength,
1249 SVGElement::ValToUse::Base);
1250 }
1251
TellStyleAlreadyParsedResult(const SVGAnimatedPathSegList & aPath)1252 void MappedAttrParser::TellStyleAlreadyParsedResult(
1253 const SVGAnimatedPathSegList& aPath) {
1254 if (!mDecl) {
1255 mDecl = new DeclarationBlock();
1256 }
1257 SVGElement::UpdateDeclarationBlockFromPath(*mDecl, aPath,
1258 SVGElement::ValToUse::Base);
1259 }
1260
GetDeclarationBlock()1261 already_AddRefed<DeclarationBlock> MappedAttrParser::GetDeclarationBlock() {
1262 return mDecl.forget();
1263 }
1264
1265 } // namespace
1266
1267 //----------------------------------------------------------------------
1268 // Implementation Helpers:
1269
UpdateContentDeclarationBlock()1270 void SVGElement::UpdateContentDeclarationBlock() {
1271 NS_ASSERTION(!mContentDeclarationBlock,
1272 "we already have a content declaration block");
1273
1274 uint32_t attrCount = mAttrs.AttrCount();
1275 if (!attrCount) {
1276 // nothing to do
1277 return;
1278 }
1279
1280 Document* doc = OwnerDoc();
1281 MappedAttrParser mappedAttrParser(doc->CSSLoader(), GetBaseURI(), this);
1282
1283 bool lengthAffectsStyle =
1284 SVGGeometryProperty::ElementMapsLengthsToStyle(this);
1285
1286 for (uint32_t i = 0; i < attrCount; ++i) {
1287 const nsAttrName* attrName = mAttrs.AttrNameAt(i);
1288 if (!attrName->IsAtom() || !IsAttributeMapped(attrName->Atom())) continue;
1289
1290 if (attrName->NamespaceID() != kNameSpaceID_None &&
1291 !attrName->Equals(nsGkAtoms::lang, kNameSpaceID_XML)) {
1292 continue;
1293 }
1294
1295 if (attrName->Equals(nsGkAtoms::lang, kNameSpaceID_None) &&
1296 HasAttr(kNameSpaceID_XML, nsGkAtoms::lang)) {
1297 continue; // xml:lang has precedence
1298 }
1299
1300 if (lengthAffectsStyle) {
1301 auto const* length = GetAnimatedLength(attrName->Atom());
1302
1303 if (length && length->HasBaseVal()) {
1304 // This is an element with geometry property set via SVG attribute,
1305 // and the attribute is already successfully parsed. We want to go
1306 // through the optimized path to tell the style system the result
1307 // directly, rather than let it parse the same thing again.
1308 mappedAttrParser.TellStyleAlreadyParsedResult(attrName->Atom(),
1309 *length);
1310 continue;
1311 }
1312 }
1313
1314 if (attrName->Equals(nsGkAtoms::d, kNameSpaceID_None)) {
1315 const auto* path = GetAnimPathSegList();
1316 // Note: Only SVGPathElement has d attribute.
1317 MOZ_ASSERT(
1318 path,
1319 "SVGPathElement should have the non-null SVGAnimatedPathSegList");
1320 // The attribute should have been already successfully parsed.
1321 // We want to go through the optimized path to tell the style system
1322 // the result directly, rather than let it parse the same thing again.
1323 mappedAttrParser.TellStyleAlreadyParsedResult(*path);
1324 // Some other notes:
1325 // The syntax of CSS d property is different from SVG d attribute.
1326 // 1. CSS d proeprty accepts: none | path(<quoted string>);
1327 // 2. SVG d attribtue accepts: none | <string>
1328 // So we cannot use css parser to parse the SVG d attribute directly.
1329 // Besides, |mAttrs.AttrAt(i)| removes the quotes already, so the svg path
1330 // in |mAttrs.AttrAt(i)| would be something like `M0,0L1,1z` without the
1331 // quotes. So css tokenizer cannot recognize this as a quoted string, and
1332 // so svg_path::SVGPathData::parse() doesn't work for this. Fortunately,
1333 // we still can rely on the parsed result from
1334 // SVGElement::ParseAttribute() for d attribute.
1335 continue;
1336 }
1337
1338 nsAutoString value;
1339 mAttrs.AttrAt(i)->ToString(value);
1340 mappedAttrParser.ParseMappedAttrValue(attrName->Atom(), value);
1341 }
1342 mContentDeclarationBlock = mappedAttrParser.GetDeclarationBlock();
1343 }
1344
GetContentDeclarationBlock() const1345 const DeclarationBlock* SVGElement::GetContentDeclarationBlock() const {
1346 return mContentDeclarationBlock;
1347 }
1348
1349 /**
1350 * Helper methods for the type-specific WillChangeXXX methods.
1351 *
1352 * This method sends out appropriate pre-change notifications so that selector
1353 * restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop
1354 * matching) work, and it returns an nsAttrValue that _may_ contain the
1355 * attribute's pre-change value.
1356 *
1357 * The nsAttrValue returned by this method depends on whether there are
1358 * mutation event listeners listening for changes to this element's attributes.
1359 * If not, then the object returned is empty. If there are, then the
1360 * nsAttrValue returned contains a serialized copy of the attribute's value
1361 * prior to the change, and this object should be passed to the corresponding
1362 * DidChangeXXX method call (assuming a WillChangeXXX call is required for the
1363 * SVG type - see comment below). This is necessary so that the 'prevValue'
1364 * property of the mutation event that is dispatched will correctly contain the
1365 * old value.
1366 *
1367 * The reason we need to serialize the old value if there are mutation
1368 * event listeners is because the underlying nsAttrValue for the attribute
1369 * points directly to a parsed representation of the attribute (e.g. an
1370 * SVGAnimatedLengthList*) that is a member of the SVG element. That object
1371 * will have changed by the time DidChangeXXX has been called, so without the
1372 * serialization of the old attribute value that we provide, DidChangeXXX
1373 * would have no way to get the old value to pass to SetAttrAndNotify.
1374 *
1375 * We only return the old value when there are mutation event listeners because
1376 * it's not needed otherwise, and because it's expensive to serialize the old
1377 * value. This is especially true for list type attributes, which may be built
1378 * up via the SVG DOM resulting in a large number of Will/DidModifyXXX calls
1379 * before the script finally finishes setting the attribute.
1380 *
1381 * Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check
1382 * and filter out redundant changes. Before calling WillChangeXXX, the caller
1383 * should check whether the new and old values are actually the same, and skip
1384 * calling Will/DidChangeXXX if they are.
1385 *
1386 * Also note that not all SVG types use this scheme. For types that can be
1387 * represented by an nsAttrValue without pointing back to an SVG object (e.g.
1388 * enums, booleans, integers) we can simply use SetParsedAttr which will do all
1389 * of the above for us. For such types there is no matching WillChangeXXX
1390 * method, only DidChangeXXX which calls SetParsedAttr.
1391 */
WillChangeValue(nsAtom * aName,const mozAutoDocUpdate & aProofOfUpdate)1392 nsAttrValue SVGElement::WillChangeValue(
1393 nsAtom* aName, const mozAutoDocUpdate& aProofOfUpdate) {
1394 // We need an empty attr value:
1395 // a) to pass to BeforeSetAttr when GetParsedAttr returns nullptr
1396 // b) to store the old value in the case we have mutation listeners
1397 //
1398 // We can use the same value for both purposes, because if GetParsedAttr
1399 // returns non-null its return value is what will get passed to BeforeSetAttr,
1400 // not matter what our mutation listener situation is.
1401 //
1402 // Also, we should be careful to always return this value to benefit from
1403 // return value optimization.
1404 nsAttrValue emptyOrOldAttrValue;
1405 const nsAttrValue* attrValue = GetParsedAttr(aName);
1406
1407 // We only need to set the old value if we have listeners since otherwise it
1408 // isn't used.
1409 if (attrValue && nsContentUtils::HasMutationListeners(
1410 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) {
1411 emptyOrOldAttrValue.SetToSerialized(*attrValue);
1412 }
1413
1414 uint8_t modType =
1415 attrValue ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
1416 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
1417 MutationObservers::NotifyAttributeWillChange(this, kNameSpaceID_None, aName,
1418 modType);
1419
1420 // This is not strictly correct--the attribute value parameter for
1421 // BeforeSetAttr should reflect the value that *will* be set but that implies
1422 // allocating, e.g. an extra SVGAnimatedLength, and isn't necessary at the
1423 // moment since no SVG elements overload BeforeSetAttr. For now we just pass
1424 // the current value.
1425 nsAttrValueOrString attrStringOrValue(attrValue ? *attrValue
1426 : emptyOrOldAttrValue);
1427 DebugOnly<nsresult> rv = BeforeSetAttr(
1428 kNameSpaceID_None, aName, &attrStringOrValue, kNotifyDocumentObservers);
1429 // SVG elements aren't expected to overload BeforeSetAttr in such a way that
1430 // it may fail. So long as this is the case we don't need to check and pass on
1431 // the return value which simplifies the calling code significantly.
1432 MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected failure from BeforeSetAttr");
1433
1434 return emptyOrOldAttrValue;
1435 }
1436
1437 /**
1438 * Helper methods for the type-specific DidChangeXXX methods.
1439 *
1440 * aEmptyOrOldValue will normally be the object returned from the corresponding
1441 * WillChangeXXX call. This is because:
1442 * a) WillChangeXXX will ensure the object is set when we have mutation
1443 * listeners, and
1444 * b) WillChangeXXX will ensure the object represents a serialized version of
1445 * the old attribute value so that the value doesn't change when the
1446 * underlying SVG type is updated.
1447 *
1448 * aNewValue is replaced with the old value.
1449 */
DidChangeValue(nsAtom * aName,const nsAttrValue & aEmptyOrOldValue,nsAttrValue & aNewValue,const mozAutoDocUpdate & aProofOfUpdate)1450 void SVGElement::DidChangeValue(nsAtom* aName,
1451 const nsAttrValue& aEmptyOrOldValue,
1452 nsAttrValue& aNewValue,
1453 const mozAutoDocUpdate& aProofOfUpdate) {
1454 bool hasListeners = nsContentUtils::HasMutationListeners(
1455 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this);
1456 uint8_t modType =
1457 HasAttr(kNameSpaceID_None, aName)
1458 ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
1459 : static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
1460
1461 // XXX Really, the fourth argument to SetAttrAndNotify should be null if
1462 // aEmptyOrOldValue does not represent the actual previous value of the
1463 // attribute, but currently SVG elements do not even use the old attribute
1464 // value in |AfterSetAttr|, so this should be ok.
1465 SetAttrAndNotify(kNameSpaceID_None, aName, nullptr, &aEmptyOrOldValue,
1466 aNewValue, nullptr, modType, hasListeners,
1467 kNotifyDocumentObservers, kCallAfterSetAttr,
1468 GetComposedDoc(), aProofOfUpdate);
1469 }
1470
MaybeSerializeAttrBeforeRemoval(nsAtom * aName,bool aNotify)1471 void SVGElement::MaybeSerializeAttrBeforeRemoval(nsAtom* aName, bool aNotify) {
1472 if (!aNotify || !nsContentUtils::HasMutationListeners(
1473 this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) {
1474 return;
1475 }
1476
1477 const nsAttrValue* attrValue = mAttrs.GetAttr(aName);
1478 if (!attrValue) return;
1479
1480 nsAutoString serializedValue;
1481 attrValue->ToString(serializedValue);
1482 nsAttrValue oldAttrValue(serializedValue);
1483 bool oldValueSet;
1484 mAttrs.SetAndSwapAttr(aName, oldAttrValue, &oldValueSet);
1485 }
1486
GetEventNameForAttr(nsAtom * aAttr)1487 nsAtom* SVGElement::GetEventNameForAttr(nsAtom* aAttr) {
1488 if (IsSVGElement(nsGkAtoms::svg)) {
1489 if (aAttr == nsGkAtoms::onload) return nsGkAtoms::onSVGLoad;
1490 if (aAttr == nsGkAtoms::onscroll) return nsGkAtoms::onSVGScroll;
1491 }
1492 if (aAttr == nsGkAtoms::onbegin) return nsGkAtoms::onbeginEvent;
1493 if (aAttr == nsGkAtoms::onrepeat) return nsGkAtoms::onrepeatEvent;
1494 if (aAttr == nsGkAtoms::onend) return nsGkAtoms::onendEvent;
1495
1496 return SVGElementBase::GetEventNameForAttr(aAttr);
1497 }
1498
GetCtx() const1499 SVGViewportElement* SVGElement::GetCtx() const {
1500 return SVGContentUtils::GetNearestViewportElement(this);
1501 }
1502
1503 /* virtual */
PrependLocalTransformsTo(const gfxMatrix & aMatrix,SVGTransformTypes aWhich) const1504 gfxMatrix SVGElement::PrependLocalTransformsTo(const gfxMatrix& aMatrix,
1505 SVGTransformTypes aWhich) const {
1506 return aMatrix;
1507 }
1508
GetLengthInfo()1509 SVGElement::LengthAttributesInfo SVGElement::GetLengthInfo() {
1510 return LengthAttributesInfo(nullptr, nullptr, 0);
1511 }
1512
SetLength(nsAtom * aName,const SVGAnimatedLength & aLength)1513 void SVGElement::SetLength(nsAtom* aName, const SVGAnimatedLength& aLength) {
1514 LengthAttributesInfo lengthInfo = GetLengthInfo();
1515
1516 for (uint32_t i = 0; i < lengthInfo.mCount; i++) {
1517 if (aName == lengthInfo.mInfos[i].mName) {
1518 lengthInfo.mValues[i] = aLength;
1519 DidAnimateLength(i);
1520 return;
1521 }
1522 }
1523 MOZ_ASSERT(false, "no length found to set");
1524 }
1525
WillChangeLength(uint8_t aAttrEnum,const mozAutoDocUpdate & aProofOfUpdate)1526 nsAttrValue SVGElement::WillChangeLength(
1527 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1528 return WillChangeValue(GetLengthInfo().mInfos[aAttrEnum].mName,
1529 aProofOfUpdate);
1530 }
1531
DidChangeLength(uint8_t aAttrEnum,const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)1532 void SVGElement::DidChangeLength(uint8_t aAttrEnum,
1533 const nsAttrValue& aEmptyOrOldValue,
1534 const mozAutoDocUpdate& aProofOfUpdate) {
1535 LengthAttributesInfo info = GetLengthInfo();
1536
1537 NS_ASSERTION(info.mCount > 0,
1538 "DidChangeLength on element with no length attribs");
1539 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1540
1541 nsAttrValue newValue;
1542 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
1543
1544 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1545 aProofOfUpdate);
1546 }
1547
DidAnimateLength(uint8_t aAttrEnum)1548 void SVGElement::DidAnimateLength(uint8_t aAttrEnum) {
1549 // We need to do this here. Normally the SMIL restyle would also cause us to
1550 // do this from DidSetComputedStyle, but we don't have that guarantee if our
1551 // frame gets reconstructed.
1552 ClearAnyCachedPath();
1553
1554 if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) {
1555 nsCSSPropertyID propId =
1556 SVGGeometryProperty::AttrEnumToCSSPropId(this, aAttrEnum);
1557
1558 // We don't map use element width/height currently. We can remove this
1559 // test when we do.
1560 if (propId != eCSSProperty_UNKNOWN) {
1561 SMILOverrideStyle()->SetSMILValue(propId,
1562 GetLengthInfo().mValues[aAttrEnum]);
1563 return;
1564 }
1565 }
1566
1567 nsIFrame* frame = GetPrimaryFrame();
1568
1569 if (frame) {
1570 LengthAttributesInfo info = GetLengthInfo();
1571 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1572 MutationEvent_Binding::SMIL);
1573 }
1574 }
1575
GetAnimatedLength(uint8_t aAttrEnum)1576 SVGAnimatedLength* SVGElement::GetAnimatedLength(uint8_t aAttrEnum) {
1577 LengthAttributesInfo info = GetLengthInfo();
1578 if (aAttrEnum < info.mCount) {
1579 return &info.mValues[aAttrEnum];
1580 }
1581 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1582 return nullptr;
1583 }
1584
GetAnimatedLength(const nsAtom * aAttrName)1585 SVGAnimatedLength* SVGElement::GetAnimatedLength(const nsAtom* aAttrName) {
1586 LengthAttributesInfo lengthInfo = GetLengthInfo();
1587
1588 for (uint32_t i = 0; i < lengthInfo.mCount; i++) {
1589 if (aAttrName == lengthInfo.mInfos[i].mName) {
1590 return &lengthInfo.mValues[i];
1591 }
1592 }
1593 return nullptr;
1594 }
1595
GetAnimatedLengthValues(float * aFirst,...)1596 void SVGElement::GetAnimatedLengthValues(float* aFirst, ...) {
1597 LengthAttributesInfo info = GetLengthInfo();
1598
1599 NS_ASSERTION(info.mCount > 0,
1600 "GetAnimatedLengthValues on element with no length attribs");
1601
1602 SVGViewportElement* ctx = nullptr;
1603
1604 float* f = aFirst;
1605 uint32_t i = 0;
1606
1607 va_list args;
1608 va_start(args, aFirst);
1609
1610 while (f && i < info.mCount) {
1611 uint8_t type = info.mValues[i].GetSpecifiedUnitType();
1612 if (!ctx) {
1613 if (type != SVGLength_Binding::SVG_LENGTHTYPE_NUMBER &&
1614 type != SVGLength_Binding::SVG_LENGTHTYPE_PX)
1615 ctx = GetCtx();
1616 }
1617 if (type == SVGLength_Binding::SVG_LENGTHTYPE_EMS ||
1618 type == SVGLength_Binding::SVG_LENGTHTYPE_EXS)
1619 *f = info.mValues[i++].GetAnimValue(this);
1620 else
1621 *f = info.mValues[i++].GetAnimValue(ctx);
1622 f = va_arg(args, float*);
1623 }
1624
1625 va_end(args);
1626 }
1627
GetLengthListInfo()1628 SVGElement::LengthListAttributesInfo SVGElement::GetLengthListInfo() {
1629 return LengthListAttributesInfo(nullptr, nullptr, 0);
1630 }
1631
WillChangeLengthList(uint8_t aAttrEnum,const mozAutoDocUpdate & aProofOfUpdate)1632 nsAttrValue SVGElement::WillChangeLengthList(
1633 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1634 return WillChangeValue(GetLengthListInfo().mInfos[aAttrEnum].mName,
1635 aProofOfUpdate);
1636 }
1637
DidChangeLengthList(uint8_t aAttrEnum,const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)1638 void SVGElement::DidChangeLengthList(uint8_t aAttrEnum,
1639 const nsAttrValue& aEmptyOrOldValue,
1640 const mozAutoDocUpdate& aProofOfUpdate) {
1641 LengthListAttributesInfo info = GetLengthListInfo();
1642
1643 NS_ASSERTION(info.mCount > 0,
1644 "DidChangeLengthList on element with no length list attribs");
1645 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1646
1647 nsAttrValue newValue;
1648 newValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1649
1650 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1651 aProofOfUpdate);
1652 }
1653
DidAnimateLengthList(uint8_t aAttrEnum)1654 void SVGElement::DidAnimateLengthList(uint8_t aAttrEnum) {
1655 nsIFrame* frame = GetPrimaryFrame();
1656
1657 if (frame) {
1658 LengthListAttributesInfo info = GetLengthListInfo();
1659 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1660 MutationEvent_Binding::SMIL);
1661 }
1662 }
1663
GetAnimatedLengthListValues(SVGUserUnitList * aFirst,...)1664 void SVGElement::GetAnimatedLengthListValues(SVGUserUnitList* aFirst, ...) {
1665 LengthListAttributesInfo info = GetLengthListInfo();
1666
1667 NS_ASSERTION(
1668 info.mCount > 0,
1669 "GetAnimatedLengthListValues on element with no length list attribs");
1670
1671 SVGUserUnitList* list = aFirst;
1672 uint32_t i = 0;
1673
1674 va_list args;
1675 va_start(args, aFirst);
1676
1677 while (list && i < info.mCount) {
1678 list->Init(&(info.mValues[i].GetAnimValue()), this, info.mInfos[i].mAxis);
1679 ++i;
1680 list = va_arg(args, SVGUserUnitList*);
1681 }
1682
1683 va_end(args);
1684 }
1685
GetAnimatedLengthList(uint8_t aAttrEnum)1686 SVGAnimatedLengthList* SVGElement::GetAnimatedLengthList(uint8_t aAttrEnum) {
1687 LengthListAttributesInfo info = GetLengthListInfo();
1688 if (aAttrEnum < info.mCount) {
1689 return &(info.mValues[aAttrEnum]);
1690 }
1691 MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
1692 return nullptr;
1693 }
1694
GetNumberListInfo()1695 SVGElement::NumberListAttributesInfo SVGElement::GetNumberListInfo() {
1696 return NumberListAttributesInfo(nullptr, nullptr, 0);
1697 }
1698
WillChangeNumberList(uint8_t aAttrEnum,const mozAutoDocUpdate & aProofOfUpdate)1699 nsAttrValue SVGElement::WillChangeNumberList(
1700 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1701 return WillChangeValue(GetNumberListInfo().mInfos[aAttrEnum].mName,
1702 aProofOfUpdate);
1703 }
1704
DidChangeNumberList(uint8_t aAttrEnum,const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)1705 void SVGElement::DidChangeNumberList(uint8_t aAttrEnum,
1706 const nsAttrValue& aEmptyOrOldValue,
1707 const mozAutoDocUpdate& aProofOfUpdate) {
1708 NumberListAttributesInfo info = GetNumberListInfo();
1709
1710 MOZ_ASSERT(info.mCount > 0,
1711 "DidChangeNumberList on element with no number list attribs");
1712 MOZ_ASSERT(aAttrEnum < info.mCount, "aAttrEnum out of range");
1713
1714 nsAttrValue newValue;
1715 newValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1716
1717 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1718 aProofOfUpdate);
1719 }
1720
DidAnimateNumberList(uint8_t aAttrEnum)1721 void SVGElement::DidAnimateNumberList(uint8_t aAttrEnum) {
1722 nsIFrame* frame = GetPrimaryFrame();
1723
1724 if (frame) {
1725 NumberListAttributesInfo info = GetNumberListInfo();
1726 MOZ_ASSERT(aAttrEnum < info.mCount, "aAttrEnum out of range");
1727
1728 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1729 MutationEvent_Binding::SMIL);
1730 }
1731 }
1732
GetAnimatedNumberList(uint8_t aAttrEnum)1733 SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(uint8_t aAttrEnum) {
1734 NumberListAttributesInfo info = GetNumberListInfo();
1735 if (aAttrEnum < info.mCount) {
1736 return &(info.mValues[aAttrEnum]);
1737 }
1738 MOZ_ASSERT(false, "Bad attrEnum");
1739 return nullptr;
1740 }
1741
GetAnimatedNumberList(nsAtom * aAttrName)1742 SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(nsAtom* aAttrName) {
1743 NumberListAttributesInfo info = GetNumberListInfo();
1744 for (uint32_t i = 0; i < info.mCount; i++) {
1745 if (aAttrName == info.mInfos[i].mName) {
1746 return &info.mValues[i];
1747 }
1748 }
1749 MOZ_ASSERT(false, "Bad caller");
1750 return nullptr;
1751 }
1752
WillChangePointList(const mozAutoDocUpdate & aProofOfUpdate)1753 nsAttrValue SVGElement::WillChangePointList(
1754 const mozAutoDocUpdate& aProofOfUpdate) {
1755 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1756 return WillChangeValue(GetPointListAttrName(), aProofOfUpdate);
1757 }
1758
DidChangePointList(const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)1759 void SVGElement::DidChangePointList(const nsAttrValue& aEmptyOrOldValue,
1760 const mozAutoDocUpdate& aProofOfUpdate) {
1761 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
1762
1763 nsAttrValue newValue;
1764 newValue.SetTo(GetAnimatedPointList()->GetBaseValue(), nullptr);
1765
1766 DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue, newValue,
1767 aProofOfUpdate);
1768 }
1769
DidAnimatePointList()1770 void SVGElement::DidAnimatePointList() {
1771 MOZ_ASSERT(GetPointListAttrName(), "Animating non-existent path data?");
1772
1773 ClearAnyCachedPath();
1774
1775 nsIFrame* frame = GetPrimaryFrame();
1776
1777 if (frame) {
1778 frame->AttributeChanged(kNameSpaceID_None, GetPointListAttrName(),
1779 MutationEvent_Binding::SMIL);
1780 }
1781 }
1782
WillChangePathSegList(const mozAutoDocUpdate & aProofOfUpdate)1783 nsAttrValue SVGElement::WillChangePathSegList(
1784 const mozAutoDocUpdate& aProofOfUpdate) {
1785 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1786 return WillChangeValue(GetPathDataAttrName(), aProofOfUpdate);
1787 }
1788
DidChangePathSegList(const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)1789 void SVGElement::DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue,
1790 const mozAutoDocUpdate& aProofOfUpdate) {
1791 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
1792
1793 nsAttrValue newValue;
1794 newValue.SetTo(GetAnimPathSegList()->GetBaseValue(), nullptr);
1795
1796 DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue, newValue,
1797 aProofOfUpdate);
1798 }
1799
DidAnimatePathSegList()1800 void SVGElement::DidAnimatePathSegList() {
1801 nsStaticAtom* name = GetPathDataAttrName();
1802 MOZ_ASSERT(name, "Animating non-existent path data?");
1803
1804 ClearAnyCachedPath();
1805
1806 // Notify style we have to update the d property because of SMIL animation.
1807 if (StaticPrefs::layout_css_d_property_enabled() && name == nsGkAtoms::d) {
1808 SMILOverrideStyle()->SetSMILValue(nsCSSPropertyID::eCSSProperty_d,
1809 *GetAnimPathSegList());
1810 return;
1811 }
1812
1813 if (nsIFrame* frame = GetPrimaryFrame()) {
1814 frame->AttributeChanged(kNameSpaceID_None, name,
1815 MutationEvent_Binding::SMIL);
1816 }
1817 }
1818
GetNumberInfo()1819 SVGElement::NumberAttributesInfo SVGElement::GetNumberInfo() {
1820 return NumberAttributesInfo(nullptr, nullptr, 0);
1821 }
1822
DidChangeNumber(uint8_t aAttrEnum)1823 void SVGElement::DidChangeNumber(uint8_t aAttrEnum) {
1824 NumberAttributesInfo info = GetNumberInfo();
1825
1826 NS_ASSERTION(info.mCount > 0,
1827 "DidChangeNumber on element with no number attribs");
1828 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1829
1830 nsAttrValue attrValue;
1831 attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1832
1833 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1834 attrValue, true);
1835 }
1836
DidAnimateNumber(uint8_t aAttrEnum)1837 void SVGElement::DidAnimateNumber(uint8_t aAttrEnum) {
1838 nsIFrame* frame = GetPrimaryFrame();
1839
1840 if (frame) {
1841 NumberAttributesInfo info = GetNumberInfo();
1842 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1843 MutationEvent_Binding::SMIL);
1844 }
1845 }
1846
GetAnimatedNumberValues(float * aFirst,...)1847 void SVGElement::GetAnimatedNumberValues(float* aFirst, ...) {
1848 NumberAttributesInfo info = GetNumberInfo();
1849
1850 NS_ASSERTION(info.mCount > 0,
1851 "GetAnimatedNumberValues on element with no number attribs");
1852
1853 float* f = aFirst;
1854 uint32_t i = 0;
1855
1856 va_list args;
1857 va_start(args, aFirst);
1858
1859 while (f && i < info.mCount) {
1860 *f = info.mValues[i++].GetAnimValue();
1861 f = va_arg(args, float*);
1862 }
1863 va_end(args);
1864 }
1865
GetNumberPairInfo()1866 SVGElement::NumberPairAttributesInfo SVGElement::GetNumberPairInfo() {
1867 return NumberPairAttributesInfo(nullptr, nullptr, 0);
1868 }
1869
WillChangeNumberPair(uint8_t aAttrEnum)1870 nsAttrValue SVGElement::WillChangeNumberPair(uint8_t aAttrEnum) {
1871 mozAutoDocUpdate updateBatch(GetComposedDoc(), kDontNotifyDocumentObservers);
1872 return WillChangeValue(GetNumberPairInfo().mInfos[aAttrEnum].mName,
1873 updateBatch);
1874 }
1875
DidChangeNumberPair(uint8_t aAttrEnum,const nsAttrValue & aEmptyOrOldValue)1876 void SVGElement::DidChangeNumberPair(uint8_t aAttrEnum,
1877 const nsAttrValue& aEmptyOrOldValue) {
1878 NumberPairAttributesInfo info = GetNumberPairInfo();
1879
1880 NS_ASSERTION(info.mCount > 0,
1881 "DidChangePairNumber on element with no number pair attribs");
1882 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1883
1884 nsAttrValue newValue;
1885 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
1886
1887 mozAutoDocUpdate updateBatch(GetComposedDoc(), kNotifyDocumentObservers);
1888 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1889 updateBatch);
1890 }
1891
DidAnimateNumberPair(uint8_t aAttrEnum)1892 void SVGElement::DidAnimateNumberPair(uint8_t aAttrEnum) {
1893 nsIFrame* frame = GetPrimaryFrame();
1894
1895 if (frame) {
1896 NumberPairAttributesInfo info = GetNumberPairInfo();
1897 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1898 MutationEvent_Binding::SMIL);
1899 }
1900 }
1901
GetIntegerInfo()1902 SVGElement::IntegerAttributesInfo SVGElement::GetIntegerInfo() {
1903 return IntegerAttributesInfo(nullptr, nullptr, 0);
1904 }
1905
DidChangeInteger(uint8_t aAttrEnum)1906 void SVGElement::DidChangeInteger(uint8_t aAttrEnum) {
1907 IntegerAttributesInfo info = GetIntegerInfo();
1908 NS_ASSERTION(info.mCount > 0,
1909 "DidChangeInteger on element with no integer attribs");
1910 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1911
1912 nsAttrValue attrValue;
1913 attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
1914
1915 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1916 attrValue, true);
1917 }
1918
DidAnimateInteger(uint8_t aAttrEnum)1919 void SVGElement::DidAnimateInteger(uint8_t aAttrEnum) {
1920 nsIFrame* frame = GetPrimaryFrame();
1921
1922 if (frame) {
1923 IntegerAttributesInfo info = GetIntegerInfo();
1924 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1925 MutationEvent_Binding::SMIL);
1926 }
1927 }
1928
GetAnimatedIntegerValues(int32_t * aFirst,...)1929 void SVGElement::GetAnimatedIntegerValues(int32_t* aFirst, ...) {
1930 IntegerAttributesInfo info = GetIntegerInfo();
1931
1932 NS_ASSERTION(info.mCount > 0,
1933 "GetAnimatedIntegerValues on element with no integer attribs");
1934
1935 int32_t* n = aFirst;
1936 uint32_t i = 0;
1937
1938 va_list args;
1939 va_start(args, aFirst);
1940
1941 while (n && i < info.mCount) {
1942 *n = info.mValues[i++].GetAnimValue();
1943 n = va_arg(args, int32_t*);
1944 }
1945 va_end(args);
1946 }
1947
GetIntegerPairInfo()1948 SVGElement::IntegerPairAttributesInfo SVGElement::GetIntegerPairInfo() {
1949 return IntegerPairAttributesInfo(nullptr, nullptr, 0);
1950 }
1951
WillChangeIntegerPair(uint8_t aAttrEnum,const mozAutoDocUpdate & aProofOfUpdate)1952 nsAttrValue SVGElement::WillChangeIntegerPair(
1953 uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
1954 return WillChangeValue(GetIntegerPairInfo().mInfos[aAttrEnum].mName,
1955 aProofOfUpdate);
1956 }
1957
DidChangeIntegerPair(uint8_t aAttrEnum,const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)1958 void SVGElement::DidChangeIntegerPair(uint8_t aAttrEnum,
1959 const nsAttrValue& aEmptyOrOldValue,
1960 const mozAutoDocUpdate& aProofOfUpdate) {
1961 IntegerPairAttributesInfo info = GetIntegerPairInfo();
1962
1963 NS_ASSERTION(info.mCount > 0,
1964 "DidChangeIntegerPair on element with no integer pair attribs");
1965 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1966
1967 nsAttrValue newValue;
1968 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
1969
1970 DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
1971 aProofOfUpdate);
1972 }
1973
DidAnimateIntegerPair(uint8_t aAttrEnum)1974 void SVGElement::DidAnimateIntegerPair(uint8_t aAttrEnum) {
1975 nsIFrame* frame = GetPrimaryFrame();
1976
1977 if (frame) {
1978 IntegerPairAttributesInfo info = GetIntegerPairInfo();
1979 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
1980 MutationEvent_Binding::SMIL);
1981 }
1982 }
1983
GetBooleanInfo()1984 SVGElement::BooleanAttributesInfo SVGElement::GetBooleanInfo() {
1985 return BooleanAttributesInfo(nullptr, nullptr, 0);
1986 }
1987
DidChangeBoolean(uint8_t aAttrEnum)1988 void SVGElement::DidChangeBoolean(uint8_t aAttrEnum) {
1989 BooleanAttributesInfo info = GetBooleanInfo();
1990
1991 NS_ASSERTION(info.mCount > 0,
1992 "DidChangeBoolean on element with no boolean attribs");
1993 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
1994
1995 nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom());
1996 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
1997 attrValue, true);
1998 }
1999
DidAnimateBoolean(uint8_t aAttrEnum)2000 void SVGElement::DidAnimateBoolean(uint8_t aAttrEnum) {
2001 nsIFrame* frame = GetPrimaryFrame();
2002
2003 if (frame) {
2004 BooleanAttributesInfo info = GetBooleanInfo();
2005 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
2006 MutationEvent_Binding::SMIL);
2007 }
2008 }
2009
GetEnumInfo()2010 SVGElement::EnumAttributesInfo SVGElement::GetEnumInfo() {
2011 return EnumAttributesInfo(nullptr, nullptr, 0);
2012 }
2013
DidChangeEnum(uint8_t aAttrEnum)2014 void SVGElement::DidChangeEnum(uint8_t aAttrEnum) {
2015 EnumAttributesInfo info = GetEnumInfo();
2016
2017 NS_ASSERTION(info.mCount > 0,
2018 "DidChangeEnum on element with no enum attribs");
2019 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2020
2021 nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom(this));
2022 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
2023 attrValue, true);
2024 }
2025
DidAnimateEnum(uint8_t aAttrEnum)2026 void SVGElement::DidAnimateEnum(uint8_t aAttrEnum) {
2027 nsIFrame* frame = GetPrimaryFrame();
2028
2029 if (frame) {
2030 EnumAttributesInfo info = GetEnumInfo();
2031 frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
2032 MutationEvent_Binding::SMIL);
2033 }
2034 }
2035
GetAnimatedOrient()2036 SVGAnimatedOrient* SVGElement::GetAnimatedOrient() { return nullptr; }
2037
WillChangeOrient(const mozAutoDocUpdate & aProofOfUpdate)2038 nsAttrValue SVGElement::WillChangeOrient(
2039 const mozAutoDocUpdate& aProofOfUpdate) {
2040 return WillChangeValue(nsGkAtoms::orient, aProofOfUpdate);
2041 }
2042
DidChangeOrient(const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)2043 void SVGElement::DidChangeOrient(const nsAttrValue& aEmptyOrOldValue,
2044 const mozAutoDocUpdate& aProofOfUpdate) {
2045 SVGAnimatedOrient* orient = GetAnimatedOrient();
2046
2047 NS_ASSERTION(orient, "DidChangeOrient on element with no orient attrib");
2048
2049 nsAttrValue newValue;
2050 newValue.SetTo(*orient, nullptr);
2051
2052 DidChangeValue(nsGkAtoms::orient, aEmptyOrOldValue, newValue, aProofOfUpdate);
2053 }
2054
DidAnimateOrient()2055 void SVGElement::DidAnimateOrient() {
2056 nsIFrame* frame = GetPrimaryFrame();
2057
2058 if (frame) {
2059 frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::orient,
2060 MutationEvent_Binding::SMIL);
2061 }
2062 }
2063
GetAnimatedViewBox()2064 SVGAnimatedViewBox* SVGElement::GetAnimatedViewBox() { return nullptr; }
2065
WillChangeViewBox(const mozAutoDocUpdate & aProofOfUpdate)2066 nsAttrValue SVGElement::WillChangeViewBox(
2067 const mozAutoDocUpdate& aProofOfUpdate) {
2068 return WillChangeValue(nsGkAtoms::viewBox, aProofOfUpdate);
2069 }
2070
DidChangeViewBox(const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)2071 void SVGElement::DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue,
2072 const mozAutoDocUpdate& aProofOfUpdate) {
2073 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
2074
2075 NS_ASSERTION(viewBox, "DidChangeViewBox on element with no viewBox attrib");
2076
2077 nsAttrValue newValue;
2078 newValue.SetTo(*viewBox, nullptr);
2079
2080 DidChangeValue(nsGkAtoms::viewBox, aEmptyOrOldValue, newValue,
2081 aProofOfUpdate);
2082 }
2083
DidAnimateViewBox()2084 void SVGElement::DidAnimateViewBox() {
2085 nsIFrame* frame = GetPrimaryFrame();
2086
2087 if (frame) {
2088 frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::viewBox,
2089 MutationEvent_Binding::SMIL);
2090 }
2091 }
2092
GetAnimatedPreserveAspectRatio()2093 SVGAnimatedPreserveAspectRatio* SVGElement::GetAnimatedPreserveAspectRatio() {
2094 return nullptr;
2095 }
2096
WillChangePreserveAspectRatio(const mozAutoDocUpdate & aProofOfUpdate)2097 nsAttrValue SVGElement::WillChangePreserveAspectRatio(
2098 const mozAutoDocUpdate& aProofOfUpdate) {
2099 return WillChangeValue(nsGkAtoms::preserveAspectRatio, aProofOfUpdate);
2100 }
2101
DidChangePreserveAspectRatio(const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)2102 void SVGElement::DidChangePreserveAspectRatio(
2103 const nsAttrValue& aEmptyOrOldValue,
2104 const mozAutoDocUpdate& aProofOfUpdate) {
2105 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
2106 GetAnimatedPreserveAspectRatio();
2107
2108 NS_ASSERTION(preserveAspectRatio,
2109 "DidChangePreserveAspectRatio on element with no "
2110 "preserveAspectRatio attrib");
2111
2112 nsAttrValue newValue;
2113 newValue.SetTo(*preserveAspectRatio, nullptr);
2114
2115 DidChangeValue(nsGkAtoms::preserveAspectRatio, aEmptyOrOldValue, newValue,
2116 aProofOfUpdate);
2117 }
2118
DidAnimatePreserveAspectRatio()2119 void SVGElement::DidAnimatePreserveAspectRatio() {
2120 nsIFrame* frame = GetPrimaryFrame();
2121
2122 if (frame) {
2123 frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio,
2124 MutationEvent_Binding::SMIL);
2125 }
2126 }
2127
WillChangeTransformList(const mozAutoDocUpdate & aProofOfUpdate)2128 nsAttrValue SVGElement::WillChangeTransformList(
2129 const mozAutoDocUpdate& aProofOfUpdate) {
2130 return WillChangeValue(GetTransformListAttrName(), aProofOfUpdate);
2131 }
2132
DidChangeTransformList(const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)2133 void SVGElement::DidChangeTransformList(
2134 const nsAttrValue& aEmptyOrOldValue,
2135 const mozAutoDocUpdate& aProofOfUpdate) {
2136 MOZ_ASSERT(GetTransformListAttrName(),
2137 "Changing non-existent transform list?");
2138
2139 // The transform attribute is being set, so we must ensure that the
2140 // SVGAnimatedTransformList is/has been allocated:
2141 nsAttrValue newValue;
2142 newValue.SetTo(GetAnimatedTransformList(DO_ALLOCATE)->GetBaseValue(),
2143 nullptr);
2144
2145 DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue, newValue,
2146 aProofOfUpdate);
2147 }
2148
DidAnimateTransformList(int32_t aModType)2149 void SVGElement::DidAnimateTransformList(int32_t aModType) {
2150 MOZ_ASSERT(GetTransformListAttrName(),
2151 "Animating non-existent transform data?");
2152
2153 nsIFrame* frame = GetPrimaryFrame();
2154
2155 if (frame) {
2156 nsAtom* transformAttr = GetTransformListAttrName();
2157 frame->AttributeChanged(kNameSpaceID_None, transformAttr, aModType);
2158 // When script changes the 'transform' attribute, Element::SetAttrAndNotify
2159 // will call MutationObservers::NotifyAttributeChanged, under which
2160 // SVGTransformableElement::GetAttributeChangeHint will be called and an
2161 // appropriate change event posted to update our frame's overflow rects.
2162 // The SetAttrAndNotify doesn't happen for transform changes caused by
2163 // 'animateTransform' though (and sending out the mutation events that
2164 // MutationObservers::NotifyAttributeChanged dispatches would be
2165 // inappropriate anyway), so we need to post the change event ourself.
2166 nsChangeHint changeHint = GetAttributeChangeHint(transformAttr, aModType);
2167 if (changeHint) {
2168 nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0}, changeHint);
2169 }
2170 }
2171 }
2172
GetStringInfo()2173 SVGElement::StringAttributesInfo SVGElement::GetStringInfo() {
2174 return StringAttributesInfo(nullptr, nullptr, 0);
2175 }
2176
GetStringBaseValue(uint8_t aAttrEnum,nsAString & aResult) const2177 void SVGElement::GetStringBaseValue(uint8_t aAttrEnum,
2178 nsAString& aResult) const {
2179 SVGElement::StringAttributesInfo info =
2180 const_cast<SVGElement*>(this)->GetStringInfo();
2181
2182 NS_ASSERTION(info.mCount > 0,
2183 "GetBaseValue on element with no string attribs");
2184
2185 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2186
2187 GetAttr(info.mInfos[aAttrEnum].mNamespaceID, info.mInfos[aAttrEnum].mName,
2188 aResult);
2189 }
2190
SetStringBaseValue(uint8_t aAttrEnum,const nsAString & aValue)2191 void SVGElement::SetStringBaseValue(uint8_t aAttrEnum,
2192 const nsAString& aValue) {
2193 SVGElement::StringAttributesInfo info = GetStringInfo();
2194
2195 NS_ASSERTION(info.mCount > 0,
2196 "SetBaseValue on element with no string attribs");
2197
2198 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2199
2200 SetAttr(info.mInfos[aAttrEnum].mNamespaceID, info.mInfos[aAttrEnum].mName,
2201 aValue, true);
2202 }
2203
DidAnimateString(uint8_t aAttrEnum)2204 void SVGElement::DidAnimateString(uint8_t aAttrEnum) {
2205 nsIFrame* frame = GetPrimaryFrame();
2206
2207 if (frame) {
2208 StringAttributesInfo info = GetStringInfo();
2209 frame->AttributeChanged(info.mInfos[aAttrEnum].mNamespaceID,
2210 info.mInfos[aAttrEnum].mName,
2211 MutationEvent_Binding::SMIL);
2212 }
2213 }
2214
GetStringListInfo()2215 SVGElement::StringListAttributesInfo SVGElement::GetStringListInfo() {
2216 return StringListAttributesInfo(nullptr, nullptr, 0);
2217 }
2218
WillChangeStringList(bool aIsConditionalProcessingAttribute,uint8_t aAttrEnum,const mozAutoDocUpdate & aProofOfUpdate)2219 nsAttrValue SVGElement::WillChangeStringList(
2220 bool aIsConditionalProcessingAttribute, uint8_t aAttrEnum,
2221 const mozAutoDocUpdate& aProofOfUpdate) {
2222 nsStaticAtom* name;
2223 if (aIsConditionalProcessingAttribute) {
2224 nsCOMPtr<SVGTests> tests(do_QueryInterface(this));
2225 name = tests->GetAttrName(aAttrEnum);
2226 } else {
2227 name = GetStringListInfo().mInfos[aAttrEnum].mName;
2228 }
2229 return WillChangeValue(name, aProofOfUpdate);
2230 }
2231
DidChangeStringList(bool aIsConditionalProcessingAttribute,uint8_t aAttrEnum,const nsAttrValue & aEmptyOrOldValue,const mozAutoDocUpdate & aProofOfUpdate)2232 void SVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute,
2233 uint8_t aAttrEnum,
2234 const nsAttrValue& aEmptyOrOldValue,
2235 const mozAutoDocUpdate& aProofOfUpdate) {
2236 nsStaticAtom* name;
2237 nsAttrValue newValue;
2238 nsCOMPtr<SVGTests> tests;
2239
2240 if (aIsConditionalProcessingAttribute) {
2241 tests = do_QueryObject(this);
2242 name = tests->GetAttrName(aAttrEnum);
2243 tests->GetAttrValue(aAttrEnum, newValue);
2244 } else {
2245 StringListAttributesInfo info = GetStringListInfo();
2246
2247 NS_ASSERTION(info.mCount > 0,
2248 "DidChangeStringList on element with no string list attribs");
2249 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
2250
2251 name = info.mInfos[aAttrEnum].mName;
2252 newValue.SetTo(info.mValues[aAttrEnum], nullptr);
2253 }
2254
2255 DidChangeValue(name, aEmptyOrOldValue, newValue, aProofOfUpdate);
2256
2257 if (aIsConditionalProcessingAttribute) {
2258 tests->MaybeInvalidate();
2259 }
2260 }
2261
ReportAttributeParseFailure(Document * aDocument,nsAtom * aAttribute,const nsAString & aValue)2262 nsresult SVGElement::ReportAttributeParseFailure(Document* aDocument,
2263 nsAtom* aAttribute,
2264 const nsAString& aValue) {
2265 AutoTArray<nsString, 2> strings;
2266 strings.AppendElement(nsDependentAtomString(aAttribute));
2267 strings.AppendElement(aValue);
2268 return SVGContentUtils::ReportToConsole(aDocument, "AttributeParseWarning",
2269 strings);
2270 }
2271
RecompileScriptEventListeners()2272 void SVGElement::RecompileScriptEventListeners() {
2273 int32_t i, count = mAttrs.AttrCount();
2274 for (i = 0; i < count; ++i) {
2275 const nsAttrName* name = mAttrs.AttrNameAt(i);
2276
2277 // Eventlistenener-attributes are always in the null namespace
2278 if (!name->IsAtom()) {
2279 continue;
2280 }
2281
2282 nsAtom* attr = name->Atom();
2283 if (!IsEventAttributeName(attr)) {
2284 continue;
2285 }
2286
2287 nsAutoString value;
2288 GetAttr(attr, value);
2289 SetEventHandler(GetEventNameForAttr(attr), value, true);
2290 }
2291 }
2292
GetAnimatedAttr(int32_t aNamespaceID,nsAtom * aName)2293 UniquePtr<SMILAttr> SVGElement::GetAnimatedAttr(int32_t aNamespaceID,
2294 nsAtom* aName) {
2295 if (aNamespaceID == kNameSpaceID_None) {
2296 // Transforms:
2297 if (GetTransformListAttrName() == aName) {
2298 // The transform attribute is being animated, so we must ensure that the
2299 // SVGAnimatedTransformList is/has been allocated:
2300 return GetAnimatedTransformList(DO_ALLOCATE)->ToSMILAttr(this);
2301 }
2302
2303 // Motion (fake 'attribute' for animateMotion)
2304 if (aName == nsGkAtoms::mozAnimateMotionDummyAttr) {
2305 return MakeUnique<SVGMotionSMILAttr>(this);
2306 }
2307
2308 // Lengths:
2309 LengthAttributesInfo info = GetLengthInfo();
2310 for (uint32_t i = 0; i < info.mCount; i++) {
2311 if (aName == info.mInfos[i].mName) {
2312 return info.mValues[i].ToSMILAttr(this);
2313 }
2314 }
2315
2316 // Numbers:
2317 {
2318 NumberAttributesInfo info = GetNumberInfo();
2319 for (uint32_t i = 0; i < info.mCount; i++) {
2320 if (aName == info.mInfos[i].mName) {
2321 return info.mValues[i].ToSMILAttr(this);
2322 }
2323 }
2324 }
2325
2326 // Number Pairs:
2327 {
2328 NumberPairAttributesInfo info = GetNumberPairInfo();
2329 for (uint32_t i = 0; i < info.mCount; i++) {
2330 if (aName == info.mInfos[i].mName) {
2331 return info.mValues[i].ToSMILAttr(this);
2332 }
2333 }
2334 }
2335
2336 // Integers:
2337 {
2338 IntegerAttributesInfo info = GetIntegerInfo();
2339 for (uint32_t i = 0; i < info.mCount; i++) {
2340 if (aName == info.mInfos[i].mName) {
2341 return info.mValues[i].ToSMILAttr(this);
2342 }
2343 }
2344 }
2345
2346 // Integer Pairs:
2347 {
2348 IntegerPairAttributesInfo info = GetIntegerPairInfo();
2349 for (uint32_t i = 0; i < info.mCount; i++) {
2350 if (aName == info.mInfos[i].mName) {
2351 return info.mValues[i].ToSMILAttr(this);
2352 }
2353 }
2354 }
2355
2356 // Enumerations:
2357 {
2358 EnumAttributesInfo info = GetEnumInfo();
2359 for (uint32_t i = 0; i < info.mCount; i++) {
2360 if (aName == info.mInfos[i].mName) {
2361 return info.mValues[i].ToSMILAttr(this);
2362 }
2363 }
2364 }
2365
2366 // Booleans:
2367 {
2368 BooleanAttributesInfo info = GetBooleanInfo();
2369 for (uint32_t i = 0; i < info.mCount; i++) {
2370 if (aName == info.mInfos[i].mName) {
2371 return info.mValues[i].ToSMILAttr(this);
2372 }
2373 }
2374 }
2375
2376 // orient:
2377 if (aName == nsGkAtoms::orient) {
2378 SVGAnimatedOrient* orient = GetAnimatedOrient();
2379 return orient ? orient->ToSMILAttr(this) : nullptr;
2380 }
2381
2382 // viewBox:
2383 if (aName == nsGkAtoms::viewBox) {
2384 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
2385 return viewBox ? viewBox->ToSMILAttr(this) : nullptr;
2386 }
2387
2388 // preserveAspectRatio:
2389 if (aName == nsGkAtoms::preserveAspectRatio) {
2390 SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
2391 GetAnimatedPreserveAspectRatio();
2392 return preserveAspectRatio ? preserveAspectRatio->ToSMILAttr(this)
2393 : nullptr;
2394 }
2395
2396 // NumberLists:
2397 {
2398 NumberListAttributesInfo info = GetNumberListInfo();
2399 for (uint32_t i = 0; i < info.mCount; i++) {
2400 if (aName == info.mInfos[i].mName) {
2401 MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes");
2402 return info.mValues[i].ToSMILAttr(this, uint8_t(i));
2403 }
2404 }
2405 }
2406
2407 // LengthLists:
2408 {
2409 LengthListAttributesInfo info = GetLengthListInfo();
2410 for (uint32_t i = 0; i < info.mCount; i++) {
2411 if (aName == info.mInfos[i].mName) {
2412 MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes");
2413 return info.mValues[i].ToSMILAttr(this, uint8_t(i),
2414 info.mInfos[i].mAxis,
2415 info.mInfos[i].mCouldZeroPadList);
2416 }
2417 }
2418 }
2419
2420 // PointLists:
2421 {
2422 if (GetPointListAttrName() == aName) {
2423 SVGAnimatedPointList* pointList = GetAnimatedPointList();
2424 if (pointList) {
2425 return pointList->ToSMILAttr(this);
2426 }
2427 }
2428 }
2429
2430 // PathSegLists:
2431 {
2432 if (GetPathDataAttrName() == aName) {
2433 SVGAnimatedPathSegList* segList = GetAnimPathSegList();
2434 if (segList) {
2435 return segList->ToSMILAttr(this);
2436 }
2437 }
2438 }
2439
2440 if (aName == nsGkAtoms::_class) {
2441 return mClassAttribute.ToSMILAttr(this);
2442 }
2443 }
2444
2445 // Strings
2446 {
2447 StringAttributesInfo info = GetStringInfo();
2448 for (uint32_t i = 0; i < info.mCount; i++) {
2449 if (aNamespaceID == info.mInfos[i].mNamespaceID &&
2450 aName == info.mInfos[i].mName) {
2451 return info.mValues[i].ToSMILAttr(this);
2452 }
2453 }
2454 }
2455
2456 return nullptr;
2457 }
2458
AnimationNeedsResample()2459 void SVGElement::AnimationNeedsResample() {
2460 Document* doc = GetComposedDoc();
2461 if (doc && doc->HasAnimationController()) {
2462 doc->GetAnimationController()->SetResampleNeeded();
2463 }
2464 }
2465
FlushAnimations()2466 void SVGElement::FlushAnimations() {
2467 Document* doc = GetComposedDoc();
2468 if (doc && doc->HasAnimationController()) {
2469 doc->GetAnimationController()->FlushResampleRequests();
2470 }
2471 }
2472
AddSizeOfExcludingThis(nsWindowSizes & aSizes,size_t * aNodeSize) const2473 void SVGElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
2474 size_t* aNodeSize) const {
2475 Element::AddSizeOfExcludingThis(aSizes, aNodeSize);
2476
2477 // These are owned by the element and not referenced from the stylesheets.
2478 // They're referenced from the rule tree, but the rule nodes don't measure
2479 // their style source (since they're non-owning), so unconditionally reporting
2480 // them even though it's a refcounted object is ok.
2481 if (mContentDeclarationBlock) {
2482 aSizes.mLayoutSvgMappedDeclarations +=
2483 mContentDeclarationBlock->SizeofIncludingThis(
2484 aSizes.mState.mMallocSizeOf);
2485 }
2486 }
2487
2488 } // namespace dom
2489 } // namespace mozilla
2490