tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SVGElement.cpp (77473B)


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