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