Merge mozilla-central to autoland
authorDorel Luca <dluca@mozilla.com>
Mon, 03 Aug 2020 03:15:45 +0300
changeset 543046 351bc35e3cc8466b49c709a296a0b9f1787080c4
parent 543045 8ef1f1e3a525122669ca43726dec7dd3c4cff0fa (current diff)
parent 543037 13fe46663222c174ae60555240b4017e2466bb55 (diff)
child 543047 0da3036d08906476d4c0155a6a48979f95d7eb44
push id37662
push userdluca@mozilla.com
push dateMon, 03 Aug 2020 03:30:15 +0000
treeherdermozilla-central@f6a3b097f8af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone81.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland
--- a/dom/svg/DOMSVGLength.cpp
+++ b/dom/svg/DOMSVGLength.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMSVGLength.h"
 
 #include "DOMSVGLengthList.h"
 #include "DOMSVGAnimatedLengthList.h"
+#include "mozAutoDocUpdate.h"
 #include "nsError.h"
 #include "nsMathUtils.h"
 #include "SVGAnimatedLength.h"
 #include "SVGAnimatedLengthList.h"
 #include "SVGAttrTearoffTable.h"
 #include "SVGLength.h"
 #include "mozilla/dom/SVGElement.h"
 #include "mozilla/dom/SVGLengthBinding.h"
@@ -54,16 +55,47 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGLe
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGLength)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGLength)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(DOMSVGLength)  // pseudo-interface
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
+//----------------------------------------------------------------------
+// Helper class: AutoChangeLengthNotifier
+// Stack-based helper class to pair calls to WillChangeLengthList and
+// DidChangeLengthList.
+class MOZ_RAII AutoChangeLengthNotifier : public mozAutoDocUpdate {
+ public:
+  explicit AutoChangeLengthNotifier(DOMSVGLength* aLength)
+      : mozAutoDocUpdate(aLength->Element()->GetComposedDoc(), true),
+        mLength(aLength) {
+    MOZ_ASSERT(mLength, "Expecting non-null length");
+    MOZ_ASSERT(mLength->HasOwner(),
+               "Expecting list to have an owner for notification");
+    mEmptyOrOldValue =
+        mLength->Element()->WillChangeLengthList(mLength->mAttrEnum, *this);
+  }
+
+  ~AutoChangeLengthNotifier() {
+    mLength->Element()->DidChangeLengthList(mLength->mAttrEnum,
+                                            mEmptyOrOldValue, *this);
+    // Null check mLength->mList, since DidChangeLengthList can run script,
+    // potentially removing mLength from its list.
+    if (mLength->mList && mLength->mList->IsAnimating()) {
+      mLength->Element()->AnimationNeedsResample();
+    }
+  }
+
+ private:
+  DOMSVGLength* const mLength;
+  nsAttrValue mEmptyOrOldValue;
+};
+
 DOMSVGLength::DOMSVGLength(DOMSVGLengthList* aList, uint8_t aAttrEnum,
                            uint32_t aListIndex, bool aIsAnimValItem)
     : mList(aList),
       mListIndex(aListIndex),
       mAttrEnum(aAttrEnum),
       mIsAnimValItem(aIsAnimValItem),
       mUnit(SVGLength_Binding::SVG_LENGTHTYPE_NUMBER),
       mValue(0.0f),
@@ -212,17 +244,17 @@ void DOMSVGLength::SetValue(float aUserU
     if (InternalItem().GetValueInUserUnits(Element(), Axis()) ==
         aUserUnitValue) {
       return;
     }
     float uuPerUnit = InternalItem().GetUserUnitsPerUnit(Element(), Axis());
     if (uuPerUnit > 0) {
       float newValue = aUserUnitValue / uuPerUnit;
       if (IsFinite(newValue)) {
-        AutoChangeLengthListNotifier notifier(this);
+        AutoChangeLengthNotifier notifier(this);
         InternalItem().SetValueAndUnit(newValue, InternalItem().GetUnit());
         return;
       }
     }
   } else if (mUnit == SVGLength_Binding::SVG_LENGTHTYPE_NUMBER ||
              mUnit == SVGLength_Binding::SVG_LENGTHTYPE_PX) {
     mValue = aUserUnitValue;
     return;
@@ -249,25 +281,27 @@ float DOMSVGLength::ValueInSpecifiedUnit
 
 void DOMSVGLength::SetValueInSpecifiedUnits(float aValue, ErrorResult& aRv) {
   if (mIsAnimValItem) {
     aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (mVal) {
-    mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement, true);
+    MOZ_ASSERT(mSVGElement);
+    mozAutoDocUpdate updateBatch(mSVGElement->GetComposedDoc(), true);
+    mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement, true, updateBatch);
     return;
   }
 
   if (HasOwner()) {
     if (InternalItem().GetValueInCurrentUnits() == aValue) {
       return;
     }
-    AutoChangeLengthListNotifier notifier(this);
+    AutoChangeLengthNotifier notifier(this);
     InternalItem().SetValueInCurrentUnits(aValue);
     return;
   }
   mValue = aValue;
 }
 
 void DOMSVGLength::SetValueAsString(const nsAString& aValue, ErrorResult& aRv) {
   if (mIsAnimValItem) {
@@ -284,17 +318,17 @@ void DOMSVGLength::SetValueAsString(cons
   if (!value.SetValueFromString(aValue)) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
   if (HasOwner()) {
     if (InternalItem() == value) {
       return;
     }
-    AutoChangeLengthListNotifier notifier(this);
+    AutoChangeLengthNotifier notifier(this);
     InternalItem() = value;
     return;
   }
   mValue = value.GetValueInCurrentUnits();
   mUnit = value.GetUnit();
 }
 
 void DOMSVGLength::GetValueAsString(nsAString& aValue) {
@@ -334,17 +368,17 @@ void DOMSVGLength::NewValueSpecifiedUnit
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
   }
   if (HasOwner()) {
     if (InternalItem().GetUnit() == aUnit &&
         InternalItem().GetValueInCurrentUnits() == aValue) {
       return;
     }
-    AutoChangeLengthListNotifier notifier(this);
+    AutoChangeLengthNotifier notifier(this);
     InternalItem().SetValueAndUnit(aValue, uint8_t(aUnit));
     return;
   }
   mUnit = uint8_t(aUnit);
   mValue = aValue;
 }
 
 void DOMSVGLength::ConvertToSpecifiedUnits(uint16_t aUnit, ErrorResult& aRv) {
@@ -364,17 +398,17 @@ void DOMSVGLength::ConvertToSpecifiedUni
   }
   if (HasOwner()) {
     if (InternalItem().GetUnit() == aUnit) {
       return;
     }
     float val =
         InternalItem().GetValueInSpecifiedUnit(aUnit, Element(), Axis());
     if (IsFinite(val)) {
-      AutoChangeLengthListNotifier notifier(this);
+      AutoChangeLengthNotifier notifier(this);
       InternalItem().SetValueAndUnit(val, aUnit);
       return;
     }
   } else {
     SVGLength len(mValue, mUnit);
     float val = len.GetValueInSpecifiedUnit(aUnit, nullptr, 0);
     if (IsFinite(val)) {
       mValue = val;
--- a/dom/svg/DOMSVGLength.h
+++ b/dom/svg/DOMSVGLength.h
@@ -74,20 +74,17 @@ class SVGElement;
  *
  * To use these classes for <length> attributes as well as <list-of-length>
  * attributes, we would need to take a bit from mListIndex and use that to
  * indicate whether the object belongs to a list or non-list attribute, then
  * if-else as appropriate. The bug for doing that work is:
  * https://bugzilla.mozilla.org/show_bug.cgi?id=571734
  */
 class DOMSVGLength final : public nsISupports, public nsWrapperCache {
-  template <class T>
-  friend class AutoChangeLengthListNotifier;
-  using AutoChangeLengthListNotifier =
-      AutoChangeLengthListNotifier<DOMSVGLength>;
+  friend class AutoChangeLengthNotifier;
 
   /**
    * Ctor for creating the object returned by
    * SVGAnimatedLength::ToDOMBaseVal/ToDOMAnimVal
    */
   DOMSVGLength(SVGAnimatedLength* aVal, dom::SVGElement* aSVGElement,
                bool aAnimVal);
 
@@ -118,21 +115,16 @@ class DOMSVGLength final : public nsISup
    * Create an unowned copy of a length that is owned or is reflecting a single
    * attribute. The caller is responsible for the first AddRef().
    */
   DOMSVGLength* Copy();
 
   bool IsInList() const { return !!mList; }
 
   /**
-   * Returns true if our attribute is animating.
-   */
-  bool IsAnimating() const { return mList && mList->IsAnimating(); }
-
-  /**
    * In future, if this class is used for non-list lengths, this will be
    * different to IsInList().
    */
   bool HasOwner() const { return !!mList; }
 
   /**
    * Returns whether this length object is reflecting a single SVG element
    * attribute.  This includes the baseVal or animVal of SVGRectElement.x, for
--- a/dom/svg/DOMSVGLengthList.cpp
+++ b/dom/svg/DOMSVGLengthList.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMSVGLengthList.h"
 
 #include "SVGElement.h"
 #include "DOMSVGLength.h"
+#include "mozAutoDocUpdate.h"
 #include "nsError.h"
 #include "SVGAnimatedLengthList.h"
 #include "nsCOMPtr.h"
 #include "mozilla/dom/SVGLengthListBinding.h"
 #include <algorithm>
 
 // See the comment in this file's header.
 
@@ -76,16 +77,43 @@ void DOMSVGLengthList::IndexedSetter(uin
   Unused << ignored;
 }
 
 JSObject* DOMSVGLengthList::WrapObject(JSContext* cx,
                                        JS::Handle<JSObject*> aGivenProto) {
   return mozilla::dom::SVGLengthList_Binding::Wrap(cx, this, aGivenProto);
 }
 
+//----------------------------------------------------------------------
+// Helper class: AutoChangeLengthListNotifier
+// Stack-based helper class to pair calls to WillChangeLengthList and
+// DidChangeLengthList.
+class MOZ_RAII AutoChangeLengthListNotifier : public mozAutoDocUpdate {
+ public:
+  explicit AutoChangeLengthListNotifier(DOMSVGLengthList* aLengthList)
+      : mozAutoDocUpdate(aLengthList->Element()->GetComposedDoc(), true),
+        mLengthList(aLengthList) {
+    MOZ_ASSERT(mLengthList, "Expecting non-null lengthList");
+    mEmptyOrOldValue = mLengthList->Element()->WillChangeLengthList(
+        mLengthList->AttrEnum(), *this);
+  }
+
+  ~AutoChangeLengthListNotifier() {
+    mLengthList->Element()->DidChangeLengthList(mLengthList->AttrEnum(),
+                                                mEmptyOrOldValue, *this);
+    if (mLengthList->IsAnimating()) {
+      mLengthList->Element()->AnimationNeedsResample();
+    }
+  }
+
+ private:
+  DOMSVGLengthList* const mLengthList;
+  nsAttrValue mEmptyOrOldValue;
+};
+
 void DOMSVGLengthList::InternalListLengthWillChange(uint32_t aNewLength) {
   uint32_t oldLength = mItems.Length();
 
   if (aNewLength > DOMSVGLength::MaxListIndex()) {
     // It's safe to get out of sync with our internal list as long as we have
     // FEWER items than it does.
     aNewLength = DOMSVGLength::MaxListIndex();
   }
--- a/dom/svg/DOMSVGLengthList.h
+++ b/dom/svg/DOMSVGLengthList.h
@@ -3,59 +3,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOM_SVG_DOMSVGLENGTHLIST_H_
 #define DOM_SVG_DOMSVGLENGTHLIST_H_
 
 #include "DOMSVGAnimatedLengthList.h"
-#include "mozAutoDocUpdate.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDebug.h"
 #include "nsTArray.h"
 #include "SVGLengthList.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Unused.h"
 
 namespace mozilla {
 
 namespace dom {
 class DOMSVGLength;
 class SVGElement;
 
-//----------------------------------------------------------------------
-// Helper class: AutoChangeLengthListNotifier
-// Stack-based helper class to pair calls to WillChangeLengthList and
-// DidChangeLengthList. Used by DOMSVGLength and DOMSVGLengthList.
-template <class T>
-class MOZ_RAII AutoChangeLengthListNotifier : public mozAutoDocUpdate {
- public:
-  explicit AutoChangeLengthListNotifier(T* aValue)
-      : mozAutoDocUpdate(aValue->Element()->GetComposedDoc(), true),
-        mValue(aValue) {
-    MOZ_ASSERT(aValue, "Expecting non-null value");
-    mEmptyOrOldValue =
-        mValue->Element()->WillChangeLengthList(mValue->AttrEnum(), *this);
-  }
-
-  ~AutoChangeLengthListNotifier() {
-    mValue->Element()->DidChangeLengthList(mValue->AttrEnum(), mEmptyOrOldValue,
-                                           *this);
-    if (mValue->IsAnimating()) {
-      mValue->Element()->AnimationNeedsResample();
-    }
-  }
-
- private:
-  T* const mValue;
-  nsAttrValue mEmptyOrOldValue;
-};
-
 /**
  * Class DOMSVGLengthList
  *
  * This class is used to create the DOM tearoff objects that wrap internal
  * SVGLengthList objects.
  *
  * See the architecture comment in DOMSVGAnimatedLengthList.h.
  *
@@ -64,21 +35,18 @@ class MOZ_RAII AutoChangeLengthListNotif
  * responsible for nulling out our DOMSVGAnimatedLengthList's pointer to us
  * when we die, essentially making its pointer to us a weak pointer. Similarly,
  * our DOMSVGLength items are friends of us and responsible for nulling out our
  * pointers to them.
  *
  * Our DOM items are created lazily on demand as and when script requests them.
  */
 class DOMSVGLengthList final : public nsISupports, public nsWrapperCache {
-  template <class T>
   friend class AutoChangeLengthListNotifier;
   friend class DOMSVGLength;
-  using AutoChangeLengthListNotifier =
-      AutoChangeLengthListNotifier<DOMSVGLengthList>;
 
   ~DOMSVGLengthList() {
     // Our mAList's weak ref to us must be nulled out when we die. If GC has
     // unlinked us using the cycle collector code, then that has already
     // happened, and mAList is null.
     if (mAList) {
       (IsAnimValList() ? mAList->mAnimVal : mAList->mBaseVal) = nullptr;
     }
--- a/dom/svg/DOMSVGNumber.cpp
+++ b/dom/svg/DOMSVGNumber.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMSVGNumber.h"
 #include "DOMSVGNumberList.h"
 #include "DOMSVGAnimatedNumberList.h"
 #include "SVGAnimatedNumberList.h"
 #include "SVGElement.h"
+#include "mozAutoDocUpdate.h"
 #include "nsError.h"
 #include "nsContentUtils.h"  // for NS_ENSURE_FINITE
 #include "mozilla/dom/SVGNumberBinding.h"
 #include "mozilla/dom/SVGSVGElement.h"
 
 // See the architecture comment in DOMSVGAnimatedNumberList.h.
 
 namespace mozilla {
@@ -45,16 +46,47 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGNumber)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGNumber)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGNumber)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
+//----------------------------------------------------------------------
+// Helper class: AutoChangeNumberNotifier
+// Stack-based helper class to pair calls to WillChangeNumberList and
+// DidChangeNumberList.
+class MOZ_RAII AutoChangeNumberNotifier : public mozAutoDocUpdate {
+ public:
+  explicit AutoChangeNumberNotifier(DOMSVGNumber* aNumber)
+      : mozAutoDocUpdate(aNumber->Element()->GetComposedDoc(), true),
+        mNumber(aNumber) {
+    MOZ_ASSERT(mNumber, "Expecting non-null number");
+    MOZ_ASSERT(mNumber->HasOwner(),
+               "Expecting list to have an owner for notification");
+    mEmptyOrOldValue =
+        mNumber->Element()->WillChangeNumberList(mNumber->mAttrEnum, *this);
+  }
+
+  ~AutoChangeNumberNotifier() {
+    mNumber->Element()->DidChangeNumberList(mNumber->mAttrEnum,
+                                            mEmptyOrOldValue, *this);
+    // Null check mNumber->mList, since DidChangeNumberList can run script,
+    // potentially removing mNumber from its list.
+    if (mNumber->mList && mNumber->mList->IsAnimating()) {
+      mNumber->Element()->AnimationNeedsResample();
+    }
+  }
+
+ private:
+  DOMSVGNumber* const mNumber;
+  nsAttrValue mEmptyOrOldValue;
+};
+
 DOMSVGNumber::DOMSVGNumber(DOMSVGNumberList* aList, uint8_t aAttrEnum,
                            uint32_t aListIndex, bool aIsAnimValItem)
     : mList(aList),
       mParent(aList),
       mListIndex(aListIndex),
       mAttrEnum(aAttrEnum),
       mIsAnimValItem(aIsAnimValItem),
       mValue(0.0f) {
@@ -93,17 +125,17 @@ void DOMSVGNumber::SetValue(float aValue
     aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (HasOwner()) {
     if (InternalItem() == aValue) {
       return;
     }
-    AutoChangeNumberListNotifier notifier(this);
+    AutoChangeNumberNotifier notifier(this);
     InternalItem() = aValue;
     return;
   }
 
   mValue = aValue;
 }
 
 void DOMSVGNumber::InsertingIntoList(DOMSVGNumberList* aList, uint8_t aAttrEnum,
--- a/dom/svg/DOMSVGNumber.h
+++ b/dom/svg/DOMSVGNumber.h
@@ -33,20 +33,17 @@ class SVGSVGElement;
  *
  * For the DOM wrapper classes for non-list SVGNumber, see SVGAnimatedNumber.h.
  *
  * See the architecture comment in DOMSVGAnimatedNumberList.h.
  *
  * See the comment in DOMSVGLength.h (yes, LENGTH), which applies here too.
  */
 class DOMSVGNumber final : public nsISupports, public nsWrapperCache {
-  template <class T>
-  friend class AutoChangeNumberListNotifier;
-  using AutoChangeNumberListNotifier =
-      AutoChangeNumberListNotifier<DOMSVGNumber>;
+  friend class AutoChangeNumberNotifier;
 
   ~DOMSVGNumber() {
     // Our mList's weak ref to us must be nulled out when we die. If GC has
     // unlinked us using the cycle collector code, then that has already
     // happened, and mList is null.
     if (mList) {
       mList->mItems[mListIndex] = nullptr;
     }
@@ -79,21 +76,16 @@ class DOMSVGNumber final : public nsISup
     DOMSVGNumber* clone = new DOMSVGNumber(mParent);
     clone->mValue = ToSVGNumber();
     return clone;
   }
 
   bool IsInList() const { return !!mList; }
 
   /**
-   * Returns true if our attribute is animating.
-   */
-  bool IsAnimating() const { return mList && mList->IsAnimating(); }
-
-  /**
    * In future, if this class is used for non-list numbers, this will be
    * different to IsInList().
    */
   bool HasOwner() const { return !!mList; }
 
   /**
    * This method is called to notify this DOM object that it is being inserted
    * into a list, and give it the information it needs as a result.
--- a/dom/svg/DOMSVGNumberList.cpp
+++ b/dom/svg/DOMSVGNumberList.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMSVGNumberList.h"
 
 #include "SVGElement.h"
 #include "DOMSVGNumber.h"
+#include "mozAutoDocUpdate.h"
 #include "nsError.h"
 #include "SVGAnimatedNumberList.h"
 #include "mozilla/dom/SVGNumberListBinding.h"
 #include "mozilla/RefPtr.h"
 #include <algorithm>
 
 // See the comment in this file's header.
 
@@ -69,16 +70,43 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 JSObject* DOMSVGNumberList::WrapObject(JSContext* cx,
                                        JS::Handle<JSObject*> aGivenProto) {
   return mozilla::dom::SVGNumberList_Binding::Wrap(cx, this, aGivenProto);
 }
 
+//----------------------------------------------------------------------
+// Helper class: AutoChangeNumberListNotifier
+// Stack-based helper class to pair calls to WillChangeNumberList and
+// DidChangeNumberList.
+class MOZ_RAII AutoChangeNumberListNotifier : public mozAutoDocUpdate {
+ public:
+  explicit AutoChangeNumberListNotifier(DOMSVGNumberList* aNumberList)
+      : mozAutoDocUpdate(aNumberList->Element()->GetComposedDoc(), true),
+        mNumberList(aNumberList) {
+    MOZ_ASSERT(mNumberList, "Expecting non-null numberList");
+    mEmptyOrOldValue = mNumberList->Element()->WillChangeNumberList(
+        mNumberList->AttrEnum(), *this);
+  }
+
+  ~AutoChangeNumberListNotifier() {
+    mNumberList->Element()->DidChangeNumberList(mNumberList->AttrEnum(),
+                                                mEmptyOrOldValue, *this);
+    if (mNumberList->IsAnimating()) {
+      mNumberList->Element()->AnimationNeedsResample();
+    }
+  }
+
+ private:
+  DOMSVGNumberList* const mNumberList;
+  nsAttrValue mEmptyOrOldValue;
+};
+
 void DOMSVGNumberList::InternalListLengthWillChange(uint32_t aNewLength) {
   uint32_t oldLength = mItems.Length();
 
   if (aNewLength > DOMSVGNumber::MaxListIndex()) {
     // It's safe to get out of sync with our internal list as long as we have
     // FEWER items than it does.
     aNewLength = DOMSVGNumber::MaxListIndex();
   }
--- a/dom/svg/DOMSVGNumberList.h
+++ b/dom/svg/DOMSVGNumberList.h
@@ -3,59 +3,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOM_SVG_DOMSVGNUMBERLIST_H_
 #define DOM_SVG_DOMSVGNUMBERLIST_H_
 
 #include "DOMSVGAnimatedNumberList.h"
-#include "mozAutoDocUpdate.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDebug.h"
 #include "nsTArray.h"
 #include "SVGNumberList.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/RefPtr.h"
 
 namespace mozilla {
 
 namespace dom {
 class DOMSVGNumber;
 class SVGElement;
 
-//----------------------------------------------------------------------
-// Helper class: AutoChangeNumberListNotifier
-// Stack-based helper class to pair calls to WillChangeNumberList and
-// DidChangeNumberList. Used by DOMSVGNumber and DOMSVGNumberList.
-template <class T>
-class MOZ_RAII AutoChangeNumberListNotifier : public mozAutoDocUpdate {
- public:
-  explicit AutoChangeNumberListNotifier(T* aValue)
-      : mozAutoDocUpdate(aValue->Element()->GetComposedDoc(), true),
-        mValue(aValue) {
-    MOZ_ASSERT(mValue, "Expecting non-null value");
-    mEmptyOrOldValue =
-        mValue->Element()->WillChangeNumberList(mValue->AttrEnum(), *this);
-  }
-
-  ~AutoChangeNumberListNotifier() {
-    mValue->Element()->DidChangeNumberList(mValue->AttrEnum(), mEmptyOrOldValue,
-                                           *this);
-    if (mValue->IsAnimating()) {
-      mValue->Element()->AnimationNeedsResample();
-    }
-  }
-
- private:
-  T* const mValue;
-  nsAttrValue mEmptyOrOldValue;
-};
-
 /**
  * Class DOMSVGNumberList
  *
  * This class is used to create the DOM tearoff objects that wrap internal
  * SVGNumberList objects.
  *
  * See the architecture comment in DOMSVGAnimatedNumberList.h.
  *
@@ -64,21 +35,18 @@ class MOZ_RAII AutoChangeNumberListNotif
  * responsible for nulling out our DOMSVGAnimatedNumberList's pointer to us
  * when we die, essentially making its pointer to us a weak pointer. Similarly,
  * our DOMSVGNumber items are friends of us and responsible for nulling out our
  * pointers to them.
  *
  * Our DOM items are created lazily on demand as and when script requests them.
  */
 class DOMSVGNumberList final : public nsISupports, public nsWrapperCache {
-  template <class T>
   friend class AutoChangeNumberListNotifier;
   friend class DOMSVGNumber;
-  using AutoChangeNumberListNotifier =
-      AutoChangeNumberListNotifier<DOMSVGNumberList>;
 
   ~DOMSVGNumberList() {
     // Our mAList's weak ref to us must be nulled out when we die. If GC has
     // unlinked us using the cycle collector code, then that has already
     // happened, and mAList is null.
     if (mAList) {
       (IsAnimValList() ? mAList->mAnimVal : mAList->mBaseVal) = nullptr;
     }
--- a/dom/svg/DOMSVGPathSeg.cpp
+++ b/dom/svg/DOMSVGPathSeg.cpp
@@ -39,16 +39,45 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGPathSeg)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMSVGPathSeg, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMSVGPathSeg, Release)
 
+//----------------------------------------------------------------------
+// Helper class: AutoChangePathSegNotifier
+// Stack-based helper class to pair calls to WillChangePathSegList
+// and DidChangePathSegList.
+class MOZ_RAII AutoChangePathSegNotifier : public mozAutoDocUpdate {
+ public:
+  explicit AutoChangePathSegNotifier(DOMSVGPathSeg* aPathSeg)
+      : mozAutoDocUpdate(aPathSeg->Element()->GetComposedDoc(), true),
+        mPathSeg(aPathSeg) {
+    MOZ_ASSERT(mPathSeg, "Expecting non-null pathSeg");
+    MOZ_ASSERT(mPathSeg->HasOwner(),
+               "Expecting list to have an owner for notification");
+    mEmptyOrOldValue = mPathSeg->Element()->WillChangePathSegList(*this);
+  }
+
+  ~AutoChangePathSegNotifier() {
+    mPathSeg->Element()->DidChangePathSegList(mEmptyOrOldValue, *this);
+    // Null check mPathSeg->mList, since DidChangePathSegList can run script,
+    // potentially removing mPathSeg from its list.
+    if (mPathSeg->mList && mPathSeg->mList->AttrIsAnimating()) {
+      mPathSeg->Element()->AnimationNeedsResample();
+    }
+  }
+
+ private:
+  DOMSVGPathSeg* const mPathSeg;
+  nsAttrValue mEmptyOrOldValue;
+};
+
 DOMSVGPathSeg::DOMSVGPathSeg(DOMSVGPathSegList* aList, uint32_t aListIndex,
                              bool aIsAnimValItem)
     : mList(aList), mListIndex(aListIndex), mIsAnimValItem(aIsAnimValItem) {
   // These shifts are in sync with the members in the header.
   MOZ_ASSERT(aList && aListIndex <= MaxListIndex(), "bad arg");
 
   MOZ_ASSERT(IndexIsValid(), "Bad index for DOMSVGPathSeg!");
 }
@@ -117,17 +146,17 @@ bool DOMSVGPathSeg::IndexIsValid() {
     if (mIsAnimValItem) {                                               \
       rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);               \
       return;                                                           \
     }                                                                   \
     if (HasOwner()) {                                                   \
       if (InternalItem()[1 + index] == float(a##propName)) {            \
         return;                                                         \
       }                                                                 \
-      AutoChangePathSegListNotifier notifier(this);                     \
+      AutoChangePathSegNotifier notifier(this);                         \
       InternalItem()[1 + index] = float(a##propName);                   \
     } else {                                                            \
       mArgs[index] = float(a##propName);                                \
     }                                                                   \
   }
 
 // For float, the normal type of arguments
 #define IMPL_FLOAT_PROP(segName, propName, index) \
--- a/dom/svg/DOMSVGPathSeg.h
+++ b/dom/svg/DOMSVGPathSeg.h
@@ -67,22 +67,17 @@ class SVGElement;
  * See the architecture comment in DOMSVGLength.h (yes, LENGTH) for an overview
  * of the important points regarding how this specific class works.
  *
  * The main differences between this class and DOMSVGLength is that we have
  * sub-classes (it does not), and the "internal counterpart" that we provide a
  * DOM wrapper for is a list of floats, not an instance of an internal class.
  */
 class DOMSVGPathSeg : public nsWrapperCache {
-  template <class T>
-  friend class AutoChangePathSegListNotifier;
-
- protected:
-  using AutoChangePathSegListNotifier =
-      AutoChangePathSegListNotifier<DOMSVGPathSeg>;
+  friend class AutoChangePathSegNotifier;
 
  public:
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMSVGPathSeg)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DOMSVGPathSeg)
 
   /**
    * Unlike the other list classes, we hide our ctor (because no one should be
    * creating instances of this class directly). This factory method in exposed
@@ -95,22 +90,16 @@ class DOMSVGPathSeg : public nsWrapperCa
    * Create an unowned copy of this object. The caller is responsible for the
    * first AddRef()!
    */
   virtual DOMSVGPathSeg* Clone() = 0;
 
   bool IsInList() const { return !!mList; }
 
   /**
-   * Returns true if our attribute is animating (in which case our animVal is
-   * not simply a mirror of our baseVal).
-   */
-  bool AttrIsAnimating() const { return mList && mList->AttrIsAnimating(); }
-
-  /**
    * In future, if this class is used for non-list segments, this will be
    * different to IsInList().
    */
   bool HasOwner() const { return !!mList; }
 
   /**
    * This method is called to notify this DOM object that it is being inserted
    * into a list, and give it the information it needs as a result.
--- a/dom/svg/DOMSVGPathSegList.cpp
+++ b/dom/svg/DOMSVGPathSegList.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMSVGPathSegList.h"
 
 #include "DOMSVGPathSeg.h"
+#include "mozAutoDocUpdate.h"
 #include "nsError.h"
 #include "SVGAnimatedPathSegList.h"
 #include "SVGAttrTearoffTable.h"
 #include "SVGPathSegUtils.h"
 #include "mozilla/dom/SVGElement.h"
 #include "mozilla/dom/SVGPathSegListBinding.h"
 #include "mozilla/RefPtr.h"
 
@@ -45,16 +46,41 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGPathSegList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGPathSegList)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGPathSegList)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
+//----------------------------------------------------------------------
+// Helper class: AutoChangePathSegListNotifier
+// Stack-based helper class to pair calls to WillChangePathSegList and
+// DidChangePathSegList.
+class MOZ_RAII AutoChangePathSegListNotifier : public mozAutoDocUpdate {
+ public:
+  explicit AutoChangePathSegListNotifier(DOMSVGPathSegList* aPathSegList)
+      : mozAutoDocUpdate(aPathSegList->Element()->GetComposedDoc(), true),
+        mPathSegList(aPathSegList) {
+    MOZ_ASSERT(mPathSegList, "Expecting non-null pathSegList");
+    mEmptyOrOldValue = mPathSegList->Element()->WillChangePathSegList(*this);
+  }
+
+  ~AutoChangePathSegListNotifier() {
+    mPathSegList->Element()->DidChangePathSegList(mEmptyOrOldValue, *this);
+    if (mPathSegList->AttrIsAnimating()) {
+      mPathSegList->Element()->AnimationNeedsResample();
+    }
+  }
+
+ private:
+  DOMSVGPathSegList* const mPathSegList;
+  nsAttrValue mEmptyOrOldValue;
+};
+
 /* static */
 already_AddRefed<DOMSVGPathSegList> DOMSVGPathSegList::GetDOMWrapper(
     void* aList, SVGElement* aElement, bool aIsAnimValList) {
   RefPtr<DOMSVGPathSegList> wrapper =
       SVGPathSegListTearoffTable().GetTearoff(aList);
   if (!wrapper) {
     wrapper = new DOMSVGPathSegList(aElement, aIsAnimValList);
     SVGPathSegListTearoffTable().AddTearoff(aList, wrapper);
--- a/dom/svg/DOMSVGPathSegList.h
+++ b/dom/svg/DOMSVGPathSegList.h
@@ -2,60 +2,33 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOM_SVG_DOMSVGPATHSEGLIST_H_
 #define DOM_SVG_DOMSVGPATHSEGLIST_H_
 
-#include "mozAutoDocUpdate.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDebug.h"
+#include "SVGElement.h"
 #include "nsTArray.h"
 #include "SVGPathData.h"  // IWYU pragma: keep
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/RefPtr.h"
-#include "mozilla/dom/SVGElement.h"
 
 namespace mozilla {
 
 class SVGAnimatedPathSegList;
 
 namespace dom {
 
 class DOMSVGPathSeg;
 
-//----------------------------------------------------------------------
-// Helper class: AutoChangePathSegListNotifier
-// Stack-based helper class to pair calls to WillChangePathSegList and
-// DidChangePathSegList. Used by DOMSVGPathSeg and DOMSVGPathSegList.
-template <class T>
-class MOZ_RAII AutoChangePathSegListNotifier : public mozAutoDocUpdate {
- public:
-  explicit AutoChangePathSegListNotifier(T* aValue)
-      : mozAutoDocUpdate(aValue->Element()->GetComposedDoc(), true),
-        mValue(aValue) {
-    MOZ_ASSERT(mValue, "Expecting non-null value");
-    mEmptyOrOldValue = mValue->Element()->WillChangePathSegList(*this);
-  }
-
-  ~AutoChangePathSegListNotifier() {
-    mValue->Element()->DidChangePathSegList(mEmptyOrOldValue, *this);
-    if (mValue->AttrIsAnimating()) {
-      mValue->Element()->AnimationNeedsResample();
-    }
-  }
-
- private:
-  T* const mValue;
-  nsAttrValue mEmptyOrOldValue;
-};
-
 /**
  * Class DOMSVGPathSegList
  *
  * This class is used to create the DOM tearoff objects that wrap internal
  * SVGPathData objects.
  *
  * See the architecture comment in DOMSVGAnimatedLengthList.h first (that's
  * LENGTH list), then continue reading the remainder of this comment.
@@ -72,21 +45,18 @@ class MOZ_RAII AutoChangePathSegListNoti
  *
  * This class is strongly intertwined with DOMSVGPathSeg. Our DOMSVGPathSeg
  * items are friends of us and responsible for nulling out our pointers to
  * them when they die.
  *
  * Our DOM items are created lazily on demand as and when script requests them.
  */
 class DOMSVGPathSegList final : public nsISupports, public nsWrapperCache {
-  template <class T>
   friend class AutoChangePathSegListNotifier;
   friend class DOMSVGPathSeg;
-  using AutoChangePathSegListNotifier =
-      AutoChangePathSegListNotifier<DOMSVGPathSegList>;
 
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGPathSegList)
 
   virtual JSObject* WrapObject(JSContext* cx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
--- a/dom/svg/DOMSVGPoint.cpp
+++ b/dom/svg/DOMSVGPoint.cpp
@@ -3,29 +3,59 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMSVGPoint.h"
 
 #include "DOMSVGPointList.h"
 #include "gfx2DGlue.h"
+#include "mozAutoDocUpdate.h"
 #include "nsCOMPtr.h"
 #include "nsError.h"
 #include "SVGPoint.h"
 #include "mozilla/dom/DOMMatrix.h"
 #include "mozilla/dom/SVGElement.h"
 
 // See the architecture comment in DOMSVGPointList.h.
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace dom {
 
+//----------------------------------------------------------------------
+// Helper class: AutoChangePointNotifier
+// Stack-based helper class to pair calls to WillChangePointList and
+// DidChangePointList.
+class MOZ_RAII AutoChangePointNotifier : public mozAutoDocUpdate {
+ public:
+  explicit AutoChangePointNotifier(DOMSVGPoint* aPoint)
+      : mozAutoDocUpdate(aPoint->Element()->GetComposedDoc(), true),
+        mPoint(aPoint) {
+    MOZ_ASSERT(mPoint, "Expecting non-null point");
+    MOZ_ASSERT(mPoint->HasOwner(),
+               "Expecting list to have an owner for notification");
+    mEmptyOrOldValue = mPoint->Element()->WillChangePointList(*this);
+  }
+
+  ~AutoChangePointNotifier() {
+    mPoint->Element()->DidChangePointList(mEmptyOrOldValue, *this);
+    // Null check mPoint->mList, since DidChangePointList can run script,
+    // potentially removing mPoint from its list.
+    if (mPoint->mList && mPoint->mList->AttrIsAnimating()) {
+      mPoint->Element()->AnimationNeedsResample();
+    }
+  }
+
+ private:
+  DOMSVGPoint* const mPoint;
+  nsAttrValue mEmptyOrOldValue;
+};
+
 float DOMSVGPoint::X() {
   if (mIsAnimValItem && HasOwner()) {
     Element()->FlushAnimations();  // May make HasOwner() == false
   }
   return HasOwner() ? InternalItem().mX : mPt.mX;
 }
 
 void DOMSVGPoint::SetX(float aX, ErrorResult& rv) {
@@ -33,17 +63,17 @@ void DOMSVGPoint::SetX(float aX, ErrorRe
     rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (HasOwner()) {
     if (InternalItem().mX == aX) {
       return;
     }
-    AutoChangePointListNotifier notifier(this);
+    AutoChangePointNotifier notifier(this);
     InternalItem().mX = aX;
     return;
   }
   mPt.mX = aX;
 }
 
 float DOMSVGPoint::Y() {
   if (mIsAnimValItem && HasOwner()) {
@@ -57,17 +87,17 @@ void DOMSVGPoint::SetY(float aY, ErrorRe
     rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (HasOwner()) {
     if (InternalItem().mY == aY) {
       return;
     }
-    AutoChangePointListNotifier notifier(this);
+    AutoChangePointNotifier notifier(this);
     InternalItem().mY = aY;
     return;
   }
   mPt.mY = aY;
 }
 
 already_AddRefed<nsISVGPoint> DOMSVGPoint::MatrixTransform(
     const DOMMatrix2DInit& aMatrix, ErrorResult& aRv) {
--- a/dom/svg/DOMSVGPoint.h
+++ b/dom/svg/DOMSVGPoint.h
@@ -31,20 +31,18 @@ class SVGElement;
  *
  * See the architecture comment in DOMSVGPointList.h for an overview of the
  * important points regarding these DOM wrapper structures.
  *
  * See the architecture comment in DOMSVGLength.h (yes, LENGTH) for an overview
  * of the important points regarding how this specific class works.
  */
 class DOMSVGPoint final : public nsISVGPoint {
-  template <class T>
-  friend class AutoChangePointListNotifier;
+  friend class AutoChangePointNotifier;
 
-  using AutoChangePointListNotifier = AutoChangePointListNotifier<DOMSVGPoint>;
   using Point = gfx::Point;
 
  public:
   /**
    * Generic ctor for DOMSVGPoint objects that are created for an attribute.
    */
   DOMSVGPoint(DOMSVGPointList* aList, uint32_t aListIndex, bool aIsAnimValItem)
       : nsISVGPoint() {
@@ -75,22 +73,16 @@ class DOMSVGPoint final : public nsISVGP
   virtual float X() override;
   virtual void SetX(float aX, ErrorResult& rv) override;
   virtual float Y() override;
   virtual void SetY(float aY, ErrorResult& rv) override;
   virtual already_AddRefed<nsISVGPoint> MatrixTransform(
       const DOMMatrix2DInit& aMatrix, ErrorResult& aRv) override;
   nsISupports* GetParentObject() override { return mList; }
 
-  /**
-   * Returns true if our attribute is animating (in which case our animVal is
-   * not simply a mirror of our baseVal).
-   */
-  bool AttrIsAnimating() const { return mList && mList->AttrIsAnimating(); }
-
   virtual DOMSVGPoint* Copy() override { return new DOMSVGPoint(this); }
 
  protected:
   SVGElement* Element() { return mList->Element(); }
 };
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/svg/DOMSVGPointList.cpp
+++ b/dom/svg/DOMSVGPointList.cpp
@@ -7,16 +7,17 @@
 #include "DOMSVGPointList.h"
 
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "DOMSVGPoint.h"
 #include "nsError.h"
 #include "SVGAnimatedPointList.h"
 #include "SVGAttrTearoffTable.h"
+#include "mozAutoDocUpdate.h"
 #include "mozilla/dom/SVGElement.h"
 #include "mozilla/dom/SVGPointListBinding.h"
 #include <algorithm>
 
 // See the comment in this file's header.
 
 // local helper functions
 namespace {
@@ -62,16 +63,41 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGPointList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGPointList)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGPointList)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
+//----------------------------------------------------------------------
+// Helper class: AutoChangePointListNotifier
+// Stack-based helper class to pair calls to WillChangePointList and
+// DidChangePointList.
+class MOZ_RAII AutoChangePointListNotifier : public mozAutoDocUpdate {
+ public:
+  explicit AutoChangePointListNotifier(DOMSVGPointList* aPointList)
+      : mozAutoDocUpdate(aPointList->Element()->GetComposedDoc(), true),
+        mPointList(aPointList) {
+    MOZ_ASSERT(mPointList, "Expecting non-null pointList");
+    mEmptyOrOldValue = mPointList->Element()->WillChangePointList(*this);
+  }
+
+  ~AutoChangePointListNotifier() {
+    mPointList->Element()->DidChangePointList(mEmptyOrOldValue, *this);
+    if (mPointList->AttrIsAnimating()) {
+      mPointList->Element()->AnimationNeedsResample();
+    }
+  }
+
+ private:
+  DOMSVGPointList* const mPointList;
+  nsAttrValue mEmptyOrOldValue;
+};
+
 /* static */
 already_AddRefed<DOMSVGPointList> DOMSVGPointList::GetDOMWrapper(
     void* aList, SVGElement* aElement, bool aIsAnimValList) {
   RefPtr<DOMSVGPointList> wrapper =
       SVGPointListTearoffTable().GetTearoff(aList);
   if (!wrapper) {
     wrapper = new DOMSVGPointList(aElement, aIsAnimValList);
     SVGPointListTearoffTable().AddTearoff(aList, wrapper);
--- a/dom/svg/DOMSVGPointList.h
+++ b/dom/svg/DOMSVGPointList.h
@@ -2,17 +2,16 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOM_SVG_DOMSVGPOINTLIST_H_
 #define DOM_SVG_DOMSVGPOINTLIST_H_
 
-#include "mozAutoDocUpdate.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDebug.h"
 #include "SVGElement.h"
 #include "nsTArray.h"
 #include "SVGPointList.h"  // IWYU pragma: keep
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/RefPtr.h"
@@ -21,42 +20,16 @@ namespace mozilla {
 
 class SVGAnimatedPointList;
 
 namespace dom {
 
 class DOMSVGPoint;
 class nsISVGPoint;
 
-//----------------------------------------------------------------------
-// Helper class: AutoChangePointListNotifier
-// Stack-based helper class to pair calls to WillChangePointList and
-// DidChangePointList. Used by DOMSVGPoint and DOMSVGPointList.
-template <class T>
-class MOZ_RAII AutoChangePointListNotifier : public mozAutoDocUpdate {
- public:
-  explicit AutoChangePointListNotifier(T* aValue)
-      : mozAutoDocUpdate(aValue->Element()->GetComposedDoc(), true),
-        mValue(aValue) {
-    MOZ_ASSERT(mValue, "Expecting non-null value");
-    mEmptyOrOldValue = mValue->Element()->WillChangePointList(*this);
-  }
-
-  ~AutoChangePointListNotifier() {
-    mValue->Element()->DidChangePointList(mEmptyOrOldValue, *this);
-    if (mValue->AttrIsAnimating()) {
-      mValue->Element()->AnimationNeedsResample();
-    }
-  }
-
- private:
-  T* const mValue;
-  nsAttrValue mEmptyOrOldValue;
-};
-
 /**
  * Class DOMSVGPointList
  *
  * This class is used to create the DOM tearoff objects that wrap internal
  * SVGPointList objects.
  *
  * See the architecture comment in DOMSVGAnimatedLengthList.h first (that's
  * LENGTH list), then continue reading the remainder of this comment.
@@ -73,22 +46,19 @@ class MOZ_RAII AutoChangePointListNotifi
  *
  * This class is strongly intertwined with DOMSVGPoint. Our DOMSVGPoint
  * items are friends of us and responsible for nulling out our pointers to
  * them when they die.
  *
  * Our DOM items are created lazily on demand as and when script requests them.
  */
 class DOMSVGPointList final : public nsISupports, public nsWrapperCache {
-  template <class T>
   friend class AutoChangePointListNotifier;
   friend class nsISVGPoint;
   friend class DOMSVGPoint;
-  using AutoChangePointListNotifier =
-      AutoChangePointListNotifier<DOMSVGPointList>;
 
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGPointList)
 
   virtual JSObject* WrapObject(JSContext* cx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
--- a/dom/svg/DOMSVGTransform.cpp
+++ b/dom/svg/DOMSVGTransform.cpp
@@ -64,16 +64,50 @@ NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOM
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMSVGTransform, Release)
 
 JSObject* DOMSVGTransform::WrapObject(JSContext* aCx,
                                       JS::Handle<JSObject*> aGivenProto) {
   return SVGTransform_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 //----------------------------------------------------------------------
+// Helper class: AutoChangeTransformNotifier
+// Stack-based helper class to pair calls to WillChangeTransformList
+// and DidChangeTransformList.
+class MOZ_RAII AutoChangeTransformNotifier {
+ public:
+  explicit AutoChangeTransformNotifier(DOMSVGTransform* aTransform)
+      : mTransform(aTransform) {
+    MOZ_ASSERT(mTransform, "Expecting non-null transform");
+    if (mTransform->HasOwner()) {
+      mUpdateBatch.emplace(mTransform->Element()->GetComposedDoc(), true);
+      mEmptyOrOldValue =
+          mTransform->Element()->WillChangeTransformList(mUpdateBatch.ref());
+    }
+  }
+
+  ~AutoChangeTransformNotifier() {
+    if (mTransform->HasOwner()) {
+      mTransform->Element()->DidChangeTransformList(mEmptyOrOldValue,
+                                                    mUpdateBatch.ref());
+      // Null check mTransform->mList, since DidChangeTransformList can run
+      // script, potentially removing mTransform from its list.
+      if (mTransform->mList && mTransform->mList->IsAnimating()) {
+        mTransform->Element()->AnimationNeedsResample();
+      }
+    }
+  }
+
+ private:
+  Maybe<mozAutoDocUpdate> mUpdateBatch;
+  DOMSVGTransform* const mTransform;
+  nsAttrValue mEmptyOrOldValue;
+};
+
+//----------------------------------------------------------------------
 // Ctors:
 
 DOMSVGTransform::DOMSVGTransform(DOMSVGTransformList* aList,
                                  uint32_t aListIndex, bool aIsAnimValItem)
     : mList(aList),
       mListIndex(aListIndex),
       mIsAnimValItem(aIsAnimValItem),
       mTransform(nullptr) {
@@ -165,31 +199,31 @@ void DOMSVGTransform::SetTranslate(float
     return;
   }
 
   if (Transform().Type() == SVG_TRANSFORM_TRANSLATE && Matrixgfx()._31 == tx &&
       Matrixgfx()._32 == ty) {
     return;
   }
 
-  AutoChangeTransformListNotifier notifier(this);
+  AutoChangeTransformNotifier notifier(this);
   Transform().SetTranslate(tx, ty);
 }
 
 void DOMSVGTransform::SetScale(float sx, float sy, ErrorResult& rv) {
   if (mIsAnimValItem) {
     rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (Transform().Type() == SVG_TRANSFORM_SCALE && Matrixgfx()._11 == sx &&
       Matrixgfx()._22 == sy) {
     return;
   }
-  AutoChangeTransformListNotifier notifier(this);
+  AutoChangeTransformNotifier notifier(this);
   Transform().SetScale(sx, sy);
 }
 
 void DOMSVGTransform::SetRotate(float angle, float cx, float cy,
                                 ErrorResult& rv) {
   if (mIsAnimValItem) {
     rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
@@ -198,17 +232,17 @@ void DOMSVGTransform::SetRotate(float an
   if (Transform().Type() == SVG_TRANSFORM_ROTATE) {
     float currentCx, currentCy;
     Transform().GetRotationOrigin(currentCx, currentCy);
     if (Transform().Angle() == angle && currentCx == cx && currentCy == cy) {
       return;
     }
   }
 
-  AutoChangeTransformListNotifier notifier(this);
+  AutoChangeTransformNotifier notifier(this);
   Transform().SetRotate(angle, cx, cy);
 }
 
 void DOMSVGTransform::SetSkewX(float angle, ErrorResult& rv) {
   if (mIsAnimValItem) {
     rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
@@ -218,17 +252,17 @@ void DOMSVGTransform::SetSkewX(float ang
     return;
   }
 
   if (!IsFinite(tan(angle * kRadPerDegree))) {
     rv.ThrowRangeError<MSG_INVALID_TRANSFORM_ANGLE_ERROR>();
     return;
   }
 
-  AutoChangeTransformListNotifier notifier(this);
+  AutoChangeTransformNotifier notifier(this);
   DebugOnly<nsresult> result = Transform().SetSkewX(angle);
   MOZ_ASSERT(NS_SUCCEEDED(result), "SetSkewX unexpectedly failed");
 }
 
 void DOMSVGTransform::SetSkewY(float angle, ErrorResult& rv) {
   if (mIsAnimValItem) {
     rv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
@@ -239,17 +273,17 @@ void DOMSVGTransform::SetSkewY(float ang
     return;
   }
 
   if (!IsFinite(tan(angle * kRadPerDegree))) {
     rv.ThrowRangeError<MSG_INVALID_TRANSFORM_ANGLE_ERROR>();
     return;
   }
 
-  AutoChangeTransformListNotifier notifier(this);
+  AutoChangeTransformNotifier notifier(this);
   DebugOnly<nsresult> result = Transform().SetSkewY(angle);
   MOZ_ASSERT(NS_SUCCEEDED(result), "SetSkewY unexpectedly failed");
 }
 
 //----------------------------------------------------------------------
 // List management methods:
 
 void DOMSVGTransform::InsertingIntoList(DOMSVGTransformList* aList,
@@ -298,14 +332,14 @@ bool DOMSVGTransform::IndexIsValid() {
 void DOMSVGTransform::SetMatrix(const gfxMatrix& aMatrix) {
   MOZ_ASSERT(!mIsAnimValItem, "Attempting to modify read-only transform");
 
   if (Transform().Type() == SVG_TRANSFORM_MATRIX &&
       SVGTransform::MatricesEqual(Matrixgfx(), aMatrix)) {
     return;
   }
 
-  AutoChangeTransformListNotifier notifier(this);
+  AutoChangeTransformNotifier notifier(this);
   Transform().SetMatrix(aMatrix);
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/svg/DOMSVGTransform.h
+++ b/dom/svg/DOMSVGTransform.h
@@ -25,20 +25,17 @@ namespace dom {
 struct DOMMatrix2DInit;
 class SVGElement;
 class SVGMatrix;
 
 /**
  * DOM wrapper for an SVG transform. See DOMSVGLength.h.
  */
 class DOMSVGTransform final : public nsWrapperCache {
-  template <class T>
-  friend class AutoChangeTransformListNotifier;
-  using AutoChangeTransformListNotifier =
-      AutoChangeTransformListNotifier<DOMSVGTransform>;
+  friend class AutoChangeTransformNotifier;
 
  public:
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMSVGTransform)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DOMSVGTransform)
 
   /**
    * Generic ctor for DOMSVGTransform objects that are created for an attribute.
    */
@@ -68,22 +65,16 @@ class DOMSVGTransform final : public nsW
   DOMSVGTransform* Clone() {
     NS_ASSERTION(mList, "unexpected caller");
     return new DOMSVGTransform(InternalItem());
   }
 
   bool IsInList() const { return !!mList; }
 
   /**
-   * Returns true if our attribute is animating (in which case our animVal is
-   * not simply a mirror of our baseVal).
-   */
-  bool IsAnimating() const { return mList && mList->IsAnimating(); }
-
-  /**
    * In future, if this class is used for non-list transforms, this will be
    * different to IsInList().
    */
   bool HasOwner() const { return !!mList; }
 
   /**
    * This method is called to notify this DOM object that it is being inserted
    * into a list, and give it the information it needs as a result.
--- a/dom/svg/DOMSVGTransformList.cpp
+++ b/dom/svg/DOMSVGTransformList.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMSVGTransformList.h"
 
+#include "mozAutoDocUpdate.h"
 #include "mozilla/dom/SVGElement.h"
 #include "mozilla/dom/SVGMatrix.h"
 #include "mozilla/dom/SVGTransformListBinding.h"
 #include "DOMSVGTransform.h"
 #include "SVGAnimatedTransformList.h"
 #include "nsError.h"
 #include <algorithm>
 
@@ -69,16 +70,42 @@ NS_INTERFACE_MAP_END
 //----------------------------------------------------------------------
 // DOMSVGTransformList methods:
 
 JSObject* DOMSVGTransformList::WrapObject(JSContext* cx,
                                           JS::Handle<JSObject*> aGivenProto) {
   return mozilla::dom::SVGTransformList_Binding::Wrap(cx, this, aGivenProto);
 }
 
+//----------------------------------------------------------------------
+// Helper class: AutoChangeTransformListNotifier
+// Stack-based helper class to pair calls to WillChangeTransformList and
+// DidChangeTransformList.
+class MOZ_RAII AutoChangeTransformListNotifier : public mozAutoDocUpdate {
+ public:
+  explicit AutoChangeTransformListNotifier(DOMSVGTransformList* aTransformList)
+      : mozAutoDocUpdate(aTransformList->Element()->GetComposedDoc(), true),
+        mTransformList(aTransformList) {
+    MOZ_ASSERT(mTransformList, "Expecting non-null transformList");
+    mEmptyOrOldValue =
+        mTransformList->Element()->WillChangeTransformList(*this);
+  }
+
+  ~AutoChangeTransformListNotifier() {
+    mTransformList->Element()->DidChangeTransformList(mEmptyOrOldValue, *this);
+    if (mTransformList->IsAnimating()) {
+      mTransformList->Element()->AnimationNeedsResample();
+    }
+  }
+
+ private:
+  DOMSVGTransformList* const mTransformList;
+  nsAttrValue mEmptyOrOldValue;
+};
+
 void DOMSVGTransformList::InternalListLengthWillChange(uint32_t aNewLength) {
   uint32_t oldLength = mItems.Length();
 
   if (aNewLength > DOMSVGTransform::MaxListIndex()) {
     // It's safe to get out of sync with our internal list as long as we have
     // FEWER items than it does.
     aNewLength = DOMSVGTransform::MaxListIndex();
   }
--- a/dom/svg/DOMSVGTransformList.h
+++ b/dom/svg/DOMSVGTransformList.h
@@ -3,79 +3,42 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOM_SVG_DOMSVGTRANSFORMLIST_H_
 #define DOM_SVG_DOMSVGTRANSFORMLIST_H_
 
 #include "DOMSVGAnimatedTransformList.h"
-#include "mozAutoDocUpdate.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDebug.h"
 #include "SVGTransformList.h"
 #include "nsTArray.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 
 namespace mozilla {
 
 namespace dom {
 struct DOMMatrix2DInit;
 class DOMSVGTransform;
 class SVGElement;
 class SVGMatrix;
 
-//----------------------------------------------------------------------
-// Helper class: AutoChangeTransformListNotifier
-// Stack-based helper class to pair calls to WillChangeTransformList and
-// DidChangeTransformList. Used by DOMSVGTransform and DOMSVGTransformList.
-template <class T>
-class MOZ_RAII AutoChangeTransformListNotifier {
- public:
-  explicit AutoChangeTransformListNotifier(T* aValue) : mValue(aValue) {
-    MOZ_ASSERT(mValue, "Expecting non-null value");
-    // If we don't have an owner then changes don't affect anything else.
-    if (mValue->HasOwner()) {
-      mUpdateBatch.emplace(mValue->Element()->GetComposedDoc(), true);
-      mEmptyOrOldValue =
-          mValue->Element()->WillChangeTransformList(mUpdateBatch.ref());
-    }
-  }
-
-  ~AutoChangeTransformListNotifier() {
-    if (mValue->HasOwner()) {
-      mValue->Element()->DidChangeTransformList(mEmptyOrOldValue,
-                                                mUpdateBatch.ref());
-      if (mValue->IsAnimating()) {
-        mValue->Element()->AnimationNeedsResample();
-      }
-    }
-  }
-
- private:
-  T* const mValue;
-  Maybe<mozAutoDocUpdate> mUpdateBatch;
-  nsAttrValue mEmptyOrOldValue;
-};
-
 /**
  * Class DOMSVGTransformList
  *
  * This class is used to create the DOM tearoff objects that wrap internal
  * SVGTransformList objects.
  *
  * See the architecture comment in DOMSVGAnimatedTransformList.h.
  */
 class DOMSVGTransformList final : public nsISupports, public nsWrapperCache {
-  template <class T>
   friend class AutoChangeTransformListNotifier;
   friend class dom::DOMSVGTransform;
-  using AutoChangeTransformListNotifier =
-      AutoChangeTransformListNotifier<DOMSVGTransformList>;
 
   ~DOMSVGTransformList() {
     // Our mAList's weak ref to us must be nulled out when we die. If GC has
     // unlinked us using the cycle collector code, then that has already
     // happened, and mAList is null.
     if (mAList) {
       (IsAnimValList() ? mAList->mAnimVal : mAList->mBaseVal) = nullptr;
     }
@@ -109,22 +72,16 @@ class DOMSVGTransformList final : public
     MOZ_ASSERT(mItems.IsEmpty() || mItems.Length() == InternalList().Length(),
                "DOM wrapper's list length is out of sync");
     return mItems.Length();
   }
 
   /// Called to notify us to synchronize our length and detach excess items.
   void InternalListLengthWillChange(uint32_t aNewLength);
 
-  /*
-   * List classes always have an owner. We need this so that templates that work
-   * on lists and elements can check ownership where elements may be unowned.
-   */
-  bool HasOwner() const { return true; }
-
   /**
    * Returns true if our attribute is animating (in which case our animVal is
    * not simply a mirror of our baseVal).
    */
   bool IsAnimating() const { return mAList->IsAnimating(); }
   /**
    * Returns true if there is an animated list mirroring the base list.
    */
--- a/dom/svg/SVGAnimatedBoolean.cpp
+++ b/dom/svg/SVGAnimatedBoolean.cpp
@@ -13,43 +13,16 @@
 #include "mozilla/SMILValue.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 /* Implementation */
 
-//----------------------------------------------------------------------
-// Helper class: AutoChangeBooleanNotifier
-// Stack-based helper class to ensure DidChangeBoolean is called.
-class MOZ_RAII AutoChangeBooleanNotifier {
- public:
-  AutoChangeBooleanNotifier(SVGAnimatedBoolean* aBoolean,
-                            SVGElement* aSVGElement, bool aDoSetAttr = true)
-      : mBoolean(aBoolean), mSVGElement(aSVGElement), mDoSetAttr(aDoSetAttr) {
-    MOZ_ASSERT(mBoolean, "Expecting non-null boolean");
-    MOZ_ASSERT(mSVGElement, "Expecting non-null element");
-  }
-
-  ~AutoChangeBooleanNotifier() {
-    if (mDoSetAttr) {
-      mSVGElement->DidChangeBoolean(mBoolean->mAttrEnum);
-    }
-    if (mBoolean->mIsAnimated) {
-      mSVGElement->AnimationNeedsResample();
-    }
-  }
-
- private:
-  SVGAnimatedBoolean* const mBoolean;
-  SVGElement* const mSVGElement;
-  bool mDoSetAttr;
-};
-
 static inline SVGAttrTearoffTable<SVGAnimatedBoolean, DOMSVGAnimatedBoolean>&
 SVGAnimatedBooleanTearoffTable() {
   static SVGAttrTearoffTable<SVGAnimatedBoolean, DOMSVGAnimatedBoolean>
       sSVGAnimatedBooleanTearoffTable;
   return sSVGAnimatedBooleanTearoffTable;
 }
 
 static bool GetValueFromString(const nsAString& aValueAsString, bool& aValue) {
@@ -80,44 +53,45 @@ nsresult SVGAnimatedBoolean::SetBaseValu
                                               SVGElement* aSVGElement) {
   bool val = false;
 
   nsresult rv = GetValueFromAtom(aValue, &val);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  mBaseVal = val;
+  if (!mIsAnimated) {
+    mAnimVal = mBaseVal;
+  } else {
+    aSVGElement->AnimationNeedsResample();
+  }
+
   // We don't need to call DidChange* here - we're only called by
   // SVGElement::ParseAttribute under Element::SetAttr,
   // which takes care of notifying.
-  AutoChangeBooleanNotifier notifier(this, aSVGElement, false);
-
-  mBaseVal = val;
-  if (!mIsAnimated) {
-    mAnimVal = mBaseVal;
-  }
-
   return NS_OK;
 }
 
 nsAtom* SVGAnimatedBoolean::GetBaseValueAtom() const {
   return mBaseVal ? nsGkAtoms::_true : nsGkAtoms::_false;
 }
 
 void SVGAnimatedBoolean::SetBaseValue(bool aValue, SVGElement* aSVGElement) {
   if (aValue == mBaseVal) {
     return;
   }
 
-  AutoChangeBooleanNotifier notifier(this, aSVGElement);
-
   mBaseVal = aValue;
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
+  } else {
+    aSVGElement->AnimationNeedsResample();
   }
+  aSVGElement->DidChangeBoolean(mAttrEnum);
 }
 
 void SVGAnimatedBoolean::SetAnimValue(bool aValue, SVGElement* aSVGElement) {
   if (mIsAnimated && mAnimVal == aValue) {
     return;
   }
   mAnimVal = aValue;
   mIsAnimated = true;
--- a/dom/svg/SVGAnimatedBoolean.h
+++ b/dom/svg/SVGAnimatedBoolean.h
@@ -22,17 +22,16 @@ class SMILValue;
 namespace dom {
 class DOMSVGAnimatedBoolean;
 class SVGAnimationElement;
 class SVGElement;
 }  // namespace dom
 
 class SVGAnimatedBoolean {
  public:
-  friend class AutoChangeBooleanNotifier;
   using SVGElement = dom::SVGElement;
 
   void Init(uint8_t aAttrEnum = 0xff, bool aValue = false) {
     mAnimVal = mBaseVal = aValue;
     mAttrEnum = aAttrEnum;
     mIsAnimated = false;
   }
 
--- a/dom/svg/SVGAnimatedEnumeration.cpp
+++ b/dom/svg/SVGAnimatedEnumeration.cpp
@@ -12,43 +12,16 @@
 #include "nsError.h"
 #include "SMILEnumType.h"
 #include "SVGAttrTearoffTable.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
-//----------------------------------------------------------------------
-// Helper class: AutoChangeEnumNotifier
-// Stack-based helper class to ensure DidChangeEnum is called.
-class MOZ_RAII AutoChangeEnumNotifier {
- public:
-  AutoChangeEnumNotifier(SVGAnimatedEnumeration* aEnum, SVGElement* aSVGElement,
-                         bool aDoSetAttr = true)
-      : mEnum(aEnum), mSVGElement(aSVGElement), mDoSetAttr(aDoSetAttr) {
-    MOZ_ASSERT(mEnum, "Expecting non-null enum");
-    MOZ_ASSERT(mSVGElement, "Expecting non-null element");
-  }
-
-  ~AutoChangeEnumNotifier() {
-    if (mDoSetAttr) {
-      mSVGElement->DidChangeEnum(mEnum->mAttrEnum);
-    }
-    if (mEnum->mIsAnimated) {
-      mSVGElement->AnimationNeedsResample();
-    }
-  }
-
- private:
-  SVGAnimatedEnumeration* const mEnum;
-  SVGElement* const mSVGElement;
-  bool mDoSetAttr;
-};
-
 static SVGAttrTearoffTable<SVGAnimatedEnumeration,
                            SVGAnimatedEnumeration::DOMAnimatedEnum>
     sSVGAnimatedEnumTearoffTable;
 
 const SVGEnumMapping* SVGAnimatedEnumeration::GetMapping(
     SVGElement* aSVGElement) {
   SVGElement::EnumAttributesInfo info = aSVGElement->GetEnumInfo();
 
@@ -61,25 +34,25 @@ const SVGEnumMapping* SVGAnimatedEnumera
 bool SVGAnimatedEnumeration::SetBaseValueAtom(const nsAtom* aValue,
                                               SVGElement* aSVGElement) {
   const SVGEnumMapping* mapping = GetMapping(aSVGElement);
 
   while (mapping && mapping->mKey) {
     if (aValue == mapping->mKey) {
       mIsBaseSet = true;
       if (mBaseVal != mapping->mVal) {
+        mBaseVal = mapping->mVal;
+        if (!mIsAnimated) {
+          mAnimVal = mBaseVal;
+        } else {
+          aSVGElement->AnimationNeedsResample();
+        }
         // We don't need to call DidChange* here - we're only called by
         // SVGElement::ParseAttribute under Element::SetAttr,
         // which takes care of notifying.
-        AutoChangeEnumNotifier notifier(this, aSVGElement, false);
-
-        mBaseVal = mapping->mVal;
-        if (!mIsAnimated) {
-          mAnimVal = mBaseVal;
-        }
       }
       return true;
     }
     mapping++;
   }
 
   return false;
 }
@@ -101,22 +74,23 @@ void SVGAnimatedEnumeration::SetBaseValu
                                           SVGElement* aSVGElement,
                                           ErrorResult& aRv) {
   const SVGEnumMapping* mapping = GetMapping(aSVGElement);
 
   while (mapping && mapping->mKey) {
     if (mapping->mVal == aValue) {
       mIsBaseSet = true;
       if (mBaseVal != uint8_t(aValue)) {
-        AutoChangeEnumNotifier notifier(this, aSVGElement);
-
         mBaseVal = uint8_t(aValue);
         if (!mIsAnimated) {
           mAnimVal = mBaseVal;
+        } else {
+          aSVGElement->AnimationNeedsResample();
         }
+        aSVGElement->DidChangeEnum(mAttrEnum);
       }
       return;
     }
     mapping++;
   }
   return aRv.ThrowTypeError("Invalid SVGAnimatedEnumeration base value");
 }
 
--- a/dom/svg/SVGAnimatedEnumeration.h
+++ b/dom/svg/SVGAnimatedEnumeration.h
@@ -29,17 +29,16 @@ typedef uint8_t SVGEnumValue;
 
 struct SVGEnumMapping {
   nsStaticAtom* const mKey;
   const SVGEnumValue mVal;
 };
 
 class SVGAnimatedEnumeration {
  public:
-  friend class AutoChangeEnumNotifier;
   using SVGElement = dom::SVGElement;
 
   void Init(uint8_t aAttrEnum, uint16_t aValue) {
     mAnimVal = mBaseVal = uint8_t(aValue);
     mAttrEnum = aAttrEnum;
     mIsAnimated = false;
     mIsBaseSet = false;
   }
--- a/dom/svg/SVGAnimatedInteger.cpp
+++ b/dom/svg/SVGAnimatedInteger.cpp
@@ -13,43 +13,16 @@
 #include "mozilla/SVGContentUtils.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 /* Implementation */
 
-//----------------------------------------------------------------------
-// Helper class: AutoChangeIntegerNotifier
-// Stack-based helper class ensure DidChangeInteger is called.
-class MOZ_RAII AutoChangeIntegerNotifier {
- public:
-  AutoChangeIntegerNotifier(SVGAnimatedInteger* aInteger,
-                            SVGElement* aSVGElement, bool aDoSetAttr = true)
-      : mInteger(aInteger), mSVGElement(aSVGElement), mDoSetAttr(aDoSetAttr) {
-    MOZ_ASSERT(mInteger, "Expecting non-null integer");
-    MOZ_ASSERT(mSVGElement, "Expecting non-null element");
-  }
-
-  ~AutoChangeIntegerNotifier() {
-    if (mDoSetAttr) {
-      mSVGElement->DidChangeInteger(mInteger->mAttrEnum);
-    }
-    if (mInteger->mIsAnimated) {
-      mSVGElement->AnimationNeedsResample();
-    }
-  }
-
- private:
-  SVGAnimatedInteger* const mInteger;
-  SVGElement* const mSVGElement;
-  bool mDoSetAttr;
-};
-
 static SVGAttrTearoffTable<SVGAnimatedInteger,
                            SVGAnimatedInteger::DOMAnimatedInteger>
     sSVGAnimatedIntegerTearoffTable;
 
 nsresult SVGAnimatedInteger::SetBaseValueString(const nsAString& aValueAsString,
                                                 SVGElement* aSVGElement) {
   bool success;
   auto token = SVGContentUtils::GetAndEnsureOneToken(aValueAsString, success);
@@ -59,22 +32,22 @@ nsresult SVGAnimatedInteger::SetBaseValu
   }
 
   int32_t value;
 
   if (!SVGContentUtils::ParseInteger(token, value)) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
-  AutoChangeIntegerNotifier notifier(this, aSVGElement, false);
-
   mIsBaseSet = true;
   mBaseVal = value;
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
+  } else {
+    aSVGElement->AnimationNeedsResample();
   }
   return NS_OK;
 }
 
 void SVGAnimatedInteger::GetBaseValueString(nsAString& aValueAsString) {
   aValueAsString.Truncate();
   aValueAsString.AppendInt(mBaseVal);
 }
@@ -83,23 +56,24 @@ void SVGAnimatedInteger::SetBaseValue(in
   // We can't just rely on SetParsedAttrValue (as called by DidChangeInteger)
   // detecting redundant changes since it will compare false if the existing
   // attribute value has an associated serialized version (a string value) even
   // if the integers match due to the way integers are stored in nsAttrValue.
   if (aValue == mBaseVal && mIsBaseSet) {
     return;
   }
 
-  AutoChangeIntegerNotifier notifier(this, aSVGElement);
-
   mBaseVal = aValue;
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
+  } else {
+    aSVGElement->AnimationNeedsResample();
   }
+  aSVGElement->DidChangeInteger(mAttrEnum);
 }
 
 void SVGAnimatedInteger::SetAnimValue(int aValue, SVGElement* aSVGElement) {
   if (mIsAnimated && aValue == mAnimVal) {
     return;
   }
   mAnimVal = aValue;
   mIsAnimated = true;
--- a/dom/svg/SVGAnimatedInteger.h
+++ b/dom/svg/SVGAnimatedInteger.h
@@ -20,17 +20,16 @@ namespace mozilla {
 class SMILValue;
 
 namespace dom {
 class SVGAnimationElement;
 }  // namespace dom
 
 class SVGAnimatedInteger {
  public:
-  friend class AutoChangeIntegerNotifier;
   using SVGElement = dom::SVGElement;
 
   void Init(uint8_t aAttrEnum = 0xff, int32_t aValue = 0) {
     mAnimVal = mBaseVal = aValue;
     mAttrEnum = aAttrEnum;
     mIsAnimated = false;
     mIsBaseSet = false;
   }
--- a/dom/svg/SVGAnimatedIntegerPair.cpp
+++ b/dom/svg/SVGAnimatedIntegerPair.cpp
@@ -13,55 +13,16 @@
 #include "SVGIntegerPairSMILType.h"
 #include "mozilla/SMILValue.h"
 #include "mozilla/SVGContentUtils.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
-//----------------------------------------------------------------------
-// Helper class: AutoChangeIntegerPairNotifier
-// Stack-based helper class to pair calls to WillChangeIntegerPair and
-// DidChangeIntegerPair.
-class MOZ_RAII AutoChangeIntegerPairNotifier {
- public:
-  AutoChangeIntegerPairNotifier(SVGAnimatedIntegerPair* aIntegerPair,
-                                SVGElement* aSVGElement, bool aDoSetAttr = true)
-      : mIntegerPair(aIntegerPair),
-        mSVGElement(aSVGElement),
-        mDoSetAttr(aDoSetAttr) {
-    MOZ_ASSERT(mIntegerPair, "Expecting non-null integerPair");
-    MOZ_ASSERT(mSVGElement, "Expecting non-null element");
-
-    if (mDoSetAttr) {
-      mUpdateBatch.emplace(aSVGElement->GetComposedDoc(), true);
-      mEmptyOrOldValue = mSVGElement->WillChangeIntegerPair(
-          mIntegerPair->mAttrEnum, mUpdateBatch.ref());
-    }
-  }
-
-  ~AutoChangeIntegerPairNotifier() {
-    if (mDoSetAttr) {
-      mSVGElement->DidChangeIntegerPair(mIntegerPair->mAttrEnum,
-                                        mEmptyOrOldValue, mUpdateBatch.ref());
-    }
-    if (mIntegerPair->mIsAnimated) {
-      mSVGElement->AnimationNeedsResample();
-    }
-  }
-
- private:
-  SVGAnimatedIntegerPair* const mIntegerPair;
-  SVGElement* const mSVGElement;
-  Maybe<mozAutoDocUpdate> mUpdateBatch;
-  nsAttrValue mEmptyOrOldValue;
-  bool mDoSetAttr;
-};
-
 static SVGAttrTearoffTable<SVGAnimatedIntegerPair,
                            SVGAnimatedIntegerPair::DOMAnimatedInteger>
     sSVGFirstAnimatedIntegerTearoffTable;
 static SVGAttrTearoffTable<SVGAnimatedIntegerPair,
                            SVGAnimatedIntegerPair::DOMAnimatedInteger>
     sSVGSecondAnimatedIntegerTearoffTable;
 
 /* Implementation */
@@ -94,28 +55,29 @@ nsresult SVGAnimatedIntegerPair::SetBase
   int32_t val[2];
 
   nsresult rv = ParseIntegerOptionalInteger(aValueAsString, val);
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  // We don't need to call DidChange* here - we're only called by
-  // SVGElement::ParseAttribute under Element::SetAttr,
-  // which takes care of notifying.
-  AutoChangeIntegerPairNotifier notifier(this, aSVGElement, false);
-
   mBaseVal[0] = val[0];
   mBaseVal[1] = val[1];
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal[0] = mBaseVal[0];
     mAnimVal[1] = mBaseVal[1];
+  } else {
+    aSVGElement->AnimationNeedsResample();
   }
+
+  // We don't need to call DidChange* here - we're only called by
+  // SVGElement::ParseAttribute under Element::SetAttr,
+  // which takes care of notifying.
   return NS_OK;
 }
 
 void SVGAnimatedIntegerPair::GetBaseValueString(
     nsAString& aValueAsString) const {
   aValueAsString.Truncate();
   aValueAsString.AppendInt(mBaseVal[0]);
   if (mBaseVal[0] != mBaseVal[1]) {
@@ -126,40 +88,48 @@ void SVGAnimatedIntegerPair::GetBaseValu
 
 void SVGAnimatedIntegerPair::SetBaseValue(int32_t aValue, PairIndex aPairIndex,
                                           SVGElement* aSVGElement) {
   uint32_t index = (aPairIndex == eFirst ? 0 : 1);
   if (mIsBaseSet && mBaseVal[index] == aValue) {
     return;
   }
 
-  AutoChangeIntegerPairNotifier notifier(this, aSVGElement);
-
+  mozAutoDocUpdate updateBatch(aSVGElement->GetComposedDoc(), true);
+  nsAttrValue emptyOrOldValue =
+      aSVGElement->WillChangeIntegerPair(mAttrEnum, updateBatch);
   mBaseVal[index] = aValue;
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal[index] = aValue;
+  } else {
+    aSVGElement->AnimationNeedsResample();
   }
+  aSVGElement->DidChangeIntegerPair(mAttrEnum, emptyOrOldValue, updateBatch);
 }
 
 void SVGAnimatedIntegerPair::SetBaseValues(int32_t aValue1, int32_t aValue2,
                                            SVGElement* aSVGElement) {
   if (mIsBaseSet && mBaseVal[0] == aValue1 && mBaseVal[1] == aValue2) {
     return;
   }
 
-  AutoChangeIntegerPairNotifier notifier(this, aSVGElement);
-
+  mozAutoDocUpdate updateBatch(aSVGElement->GetComposedDoc(), true);
+  nsAttrValue emptyOrOldValue =
+      aSVGElement->WillChangeIntegerPair(mAttrEnum, updateBatch);
   mBaseVal[0] = aValue1;
   mBaseVal[1] = aValue2;
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal[0] = aValue1;
     mAnimVal[1] = aValue2;
+  } else {
+    aSVGElement->AnimationNeedsResample();
   }
+  aSVGElement->DidChangeIntegerPair(mAttrEnum, emptyOrOldValue, updateBatch);
 }
 
 void SVGAnimatedIntegerPair::SetAnimValue(const int32_t aValue[2],
                                           SVGElement* aSVGElement) {
   if (mIsAnimated && mAnimVal[0] == aValue[0] && mAnimVal[1] == aValue[1]) {
     return;
   }
   mAnimVal[0] = aValue[0];
--- a/dom/svg/SVGAnimatedIntegerPair.h
+++ b/dom/svg/SVGAnimatedIntegerPair.h
@@ -20,17 +20,16 @@ class SMILValue;
 
 namespace dom {
 class SVGAnimationElement;
 class SVGElement;
 }  // namespace dom
 
 class SVGAnimatedIntegerPair {
  public:
-  friend class AutoChangeIntegerPairNotifier;
   using SVGElement = dom::SVGElement;
 
   enum PairIndex { eFirst, eSecond };
 
   void Init(uint8_t aAttrEnum = 0xff, int32_t aValue1 = 0,
             int32_t aValue2 = 0) {
     mAnimVal[0] = mBaseVal[0] = aValue1;
     mAnimVal[1] = mBaseVal[1] = aValue2;
--- a/dom/svg/SVGAnimatedLength.cpp
+++ b/dom/svg/SVGAnimatedLength.cpp
@@ -20,53 +20,16 @@
 #include "nsTextFormatter.h"
 #include "SMILFloatType.h"
 #include "SVGAttrTearoffTable.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
-//----------------------------------------------------------------------
-// Helper class: AutoChangeLengthNotifier
-// Stack-based helper class to pair calls to WillChangeLength and
-// DidChangeLength.
-class MOZ_RAII AutoChangeLengthNotifier {
- public:
-  AutoChangeLengthNotifier(SVGAnimatedLength* aLength, SVGElement* aSVGElement,
-                           bool aDoSetAttr = true)
-      : mLength(aLength), mSVGElement(aSVGElement), mDoSetAttr(aDoSetAttr) {
-    MOZ_ASSERT(mLength, "Expecting non-null length");
-    MOZ_ASSERT(mSVGElement, "Expecting non-null element");
-
-    if (mDoSetAttr) {
-      mUpdateBatch.emplace(aSVGElement->GetComposedDoc(), true);
-      mEmptyOrOldValue =
-          mSVGElement->WillChangeLength(mLength->mAttrEnum, mUpdateBatch.ref());
-    }
-  }
-
-  ~AutoChangeLengthNotifier() {
-    if (mDoSetAttr) {
-      mSVGElement->DidChangeLength(mLength->mAttrEnum, mEmptyOrOldValue,
-                                   mUpdateBatch.ref());
-    }
-    if (mLength->mIsAnimated) {
-      mSVGElement->AnimationNeedsResample();
-    }
-  }
-
- private:
-  SVGAnimatedLength* const mLength;
-  SVGElement* const mSVGElement;
-  Maybe<mozAutoDocUpdate> mUpdateBatch;
-  nsAttrValue mEmptyOrOldValue;
-  bool mDoSetAttr;
-};
-
 static const nsStaticAtom* const unitMap[] = {
     nullptr, /* SVG_LENGTHTYPE_UNKNOWN */
     nullptr, /* SVG_LENGTHTYPE_NUMBER */
     nsGkAtoms::percentage,
     nsGkAtoms::em,
     nsGkAtoms::ex,
     nsGkAtoms::px,
     nsGkAtoms::cm,
@@ -284,29 +247,36 @@ float SVGAnimatedLength::GetPixelsPerUni
     case SVGLength_Binding::SVG_LENGTHTYPE_EXS:
       return aMetrics.GetExLength();
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown unit type");
       return 0;
   }
 }
 
-void SVGAnimatedLength::SetBaseValueInSpecifiedUnits(float aValue,
-                                                     SVGElement* aSVGElement,
-                                                     bool aDoSetAttr) {
+void SVGAnimatedLength::SetBaseValueInSpecifiedUnits(
+    float aValue, SVGElement* aSVGElement, bool aDoSetAttr,
+    const mozAutoDocUpdate& aProofOfUpdate) {
   if (mIsBaseSet && mBaseVal == aValue) {
     return;
   }
 
-  AutoChangeLengthNotifier notifier(this, aSVGElement, aDoSetAttr);
-
+  nsAttrValue emptyOrOldValue;
+  if (aDoSetAttr) {
+    emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum, aProofOfUpdate);
+  }
   mBaseVal = aValue;
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
+  } else {
+    aSVGElement->AnimationNeedsResample();
+  }
+  if (aDoSetAttr) {
+    aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue, aProofOfUpdate);
   }
 }
 
 nsresult SVGAnimatedLength::ConvertToSpecifiedUnits(uint16_t unitType,
                                                     SVGElement* aSVGElement) {
   if (!IsValidUnitType(unitType)) return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 
   if (mIsBaseSet && mSpecifiedUnitType == uint8_t(unitType)) return NS_OK;
@@ -323,46 +293,55 @@ nsresult SVGAnimatedLength::ConvertToSpe
   if (!IsFinite(valueInSpecifiedUnits)) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   // Even though we're not changing the visual effect this length will have
   // on the document, we still need to send out notifications in case we have
   // mutation listeners, since the actual string value of the attribute will
   // change.
-  AutoChangeLengthNotifier notifier(this, aSVGElement);
+  mozAutoDocUpdate updateBatch(aSVGElement->GetComposedDoc(), true);
+  nsAttrValue emptyOrOldValue =
+      aSVGElement->WillChangeLength(mAttrEnum, updateBatch);
 
   mSpecifiedUnitType = uint8_t(unitType);
   // Setting aDoSetAttr to false here will ensure we don't call
   // Will/DidChangeAngle a second time (and dispatch duplicate notifications).
-  SetBaseValueInSpecifiedUnits(valueInSpecifiedUnits, aSVGElement, false);
+  SetBaseValueInSpecifiedUnits(valueInSpecifiedUnits, aSVGElement, false,
+                               updateBatch);
+
+  aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue, updateBatch);
 
   return NS_OK;
 }
 
 nsresult SVGAnimatedLength::NewValueSpecifiedUnits(uint16_t aUnitType,
                                                    float aValueInSpecifiedUnits,
                                                    SVGElement* aSVGElement) {
   NS_ENSURE_FINITE(aValueInSpecifiedUnits, NS_ERROR_ILLEGAL_VALUE);
 
   if (!IsValidUnitType(aUnitType)) return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 
   if (mIsBaseSet && mBaseVal == aValueInSpecifiedUnits &&
       mSpecifiedUnitType == uint8_t(aUnitType)) {
     return NS_OK;
   }
 
-  AutoChangeLengthNotifier notifier(this, aSVGElement);
-
+  mozAutoDocUpdate updateBatch(aSVGElement->GetComposedDoc(), true);
+  nsAttrValue emptyOrOldValue =
+      aSVGElement->WillChangeLength(mAttrEnum, updateBatch);
   mBaseVal = aValueInSpecifiedUnits;
   mIsBaseSet = true;
   mSpecifiedUnitType = uint8_t(aUnitType);
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
+  } else {
+    aSVGElement->AnimationNeedsResample();
   }
+  aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue, updateBatch);
   return NS_OK;
 }
 
 already_AddRefed<DOMSVGLength> SVGAnimatedLength::ToDOMBaseVal(
     SVGElement* aSVGElement) {
   return DOMSVGLength::GetTearOff(this, aSVGElement, false);
 }
 
@@ -383,25 +362,35 @@ nsresult SVGAnimatedLength::SetBaseValue
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   if (mIsBaseSet && mBaseVal == float(value) &&
       mSpecifiedUnitType == uint8_t(unitType)) {
     return NS_OK;
   }
 
-  AutoChangeLengthNotifier notifier(this, aSVGElement, aDoSetAttr);
-
+  Maybe<mozAutoDocUpdate> updateBatch;
+  nsAttrValue emptyOrOldValue;
+  if (aDoSetAttr) {
+    updateBatch.emplace(aSVGElement->GetComposedDoc(), true);
+    emptyOrOldValue =
+        aSVGElement->WillChangeLength(mAttrEnum, updateBatch.ref());
+  }
   mBaseVal = value;
   mIsBaseSet = true;
   mSpecifiedUnitType = uint8_t(unitType);
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
+  } else {
+    aSVGElement->AnimationNeedsResample();
   }
 
+  if (aDoSetAttr) {
+    aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue, updateBatch.ref());
+  }
   return NS_OK;
 }
 
 void SVGAnimatedLength::GetBaseValueString(nsAString& aValueAsString) const {
   GetValueString(aValueAsString, mBaseVal, mSpecifiedUnitType);
 }
 
 void SVGAnimatedLength::GetAnimValueString(nsAString& aValueAsString) const {
@@ -415,17 +404,19 @@ nsresult SVGAnimatedLength::SetBaseValue
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   float valueInSpecifiedUnits = aValue / pixelsPerUnit;
   if (!IsFinite(valueInSpecifiedUnits)) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
-  SetBaseValueInSpecifiedUnits(valueInSpecifiedUnits, aSVGElement, aDoSetAttr);
+  mozAutoDocUpdate updateBatch(aSVGElement->GetComposedDoc(), true);
+  SetBaseValueInSpecifiedUnits(valueInSpecifiedUnits, aSVGElement, aDoSetAttr,
+                               updateBatch);
   return NS_OK;
 }
 
 void SVGAnimatedLength::SetAnimValueInSpecifiedUnits(float aValue,
                                                      SVGElement* aSVGElement) {
   if (mAnimVal == aValue && mIsAnimated) {
     return;
   }
--- a/dom/svg/SVGAnimatedLength.h
+++ b/dom/svg/SVGAnimatedLength.h
@@ -19,17 +19,16 @@
 #include "nsError.h"
 #include "nsMathUtils.h"
 
 class mozAutoDocUpdate;
 class nsIFrame;
 
 namespace mozilla {
 
-class AutoChangeLengthNotifier;
 class SMILValue;
 
 namespace dom {
 class DOMSVGAnimatedLength;
 class DOMSVGLength;
 class SVGAnimationElement;
 class SVGViewportElement;
 
@@ -75,17 +74,16 @@ class NonSVGFrameUserSpaceMetrics : publ
 
  private:
   nsIFrame* mFrame;
 };
 
 }  // namespace dom
 
 class SVGAnimatedLength {
-  friend class AutoChangeLengthNotifier;
   friend class dom::DOMSVGAnimatedLength;
   friend class dom::DOMSVGLength;
   using DOMSVGLength = dom::DOMSVGLength;
   using SVGElement = dom::SVGElement;
   using SVGViewportElement = dom::SVGViewportElement;
   using UserSpaceMetrics = dom::UserSpaceMetrics;
 
  public:
@@ -176,17 +174,18 @@ class SVGAnimatedLength {
 
   // SetBaseValue and SetAnimValue set the value in user units. This may fail
   // if unit conversion fails e.g. conversion to ex or em units where the
   // font-size is 0.
   // SetBaseValueInSpecifiedUnits and SetAnimValueInSpecifiedUnits do not
   // perform unit conversion and are therefore infallible.
   nsresult SetBaseValue(float aValue, SVGElement* aSVGElement, bool aDoSetAttr);
   void SetBaseValueInSpecifiedUnits(float aValue, SVGElement* aSVGElement,
-                                    bool aDoSetAttr);
+                                    bool aDoSetAttr,
+                                    const mozAutoDocUpdate& aProofOfUpdate);
   nsresult SetAnimValue(float aValue, SVGElement* aSVGElement);
   void SetAnimValueInSpecifiedUnits(float aValue, SVGElement* aSVGElement);
   nsresult NewValueSpecifiedUnits(uint16_t aUnitType,
                                   float aValueInSpecifiedUnits,
                                   SVGElement* aSVGElement);
   nsresult ConvertToSpecifiedUnits(uint16_t aUnitType, SVGElement* aSVGElement);
   already_AddRefed<DOMSVGLength> ToDOMBaseVal(SVGElement* aSVGElement);
   already_AddRefed<DOMSVGLength> ToDOMAnimVal(SVGElement* aSVGElement);
--- a/dom/svg/SVGAnimatedNumber.cpp
+++ b/dom/svg/SVGAnimatedNumber.cpp
@@ -14,39 +14,16 @@
 #include "SVGAttrTearoffTable.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 /* Implementation */
 
-//----------------------------------------------------------------------
-// Helper class: AutoChangeNumberNotifier
-// Stack-based helper class to ensure DidChangeNumber is called.
-class MOZ_RAII AutoChangeNumberNotifier {
- public:
-  AutoChangeNumberNotifier(SVGAnimatedNumber* aNumber, SVGElement* aSVGElement)
-      : mNumber(aNumber), mSVGElement(aSVGElement) {
-    MOZ_ASSERT(mNumber, "Expecting non-null number");
-    MOZ_ASSERT(mSVGElement, "Expecting non-null element");
-  }
-
-  ~AutoChangeNumberNotifier() {
-    mSVGElement->DidChangeNumber(mNumber->mAttrEnum);
-    if (mNumber->mIsAnimated) {
-      mSVGElement->AnimationNeedsResample();
-    }
-  }
-
- private:
-  SVGAnimatedNumber* const mNumber;
-  SVGElement* const mSVGElement;
-};
-
 static SVGAttrTearoffTable<SVGAnimatedNumber,
                            SVGAnimatedNumber::DOMAnimatedNumber>
     sSVGAnimatedNumberTearoffTable;
 
 static bool GetValueFromString(const nsAString& aString,
                                bool aPercentagesAllowed, float& aValue) {
   bool success;
   auto token = SVGContentUtils::GetAndEnsureOneToken(aString, success);
@@ -102,23 +79,24 @@ void SVGAnimatedNumber::GetBaseValueStri
   aValueAsString.AppendFloat(mBaseVal);
 }
 
 void SVGAnimatedNumber::SetBaseValue(float aValue, SVGElement* aSVGElement) {
   if (mIsBaseSet && aValue == mBaseVal) {
     return;
   }
 
-  AutoChangeNumberNotifier notifier(this, aSVGElement);
-
   mBaseVal = aValue;
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
+  } else {
+    aSVGElement->AnimationNeedsResample();
   }
+  aSVGElement->DidChangeNumber(mAttrEnum);
 }
 
 void SVGAnimatedNumber::SetAnimValue(float aValue, SVGElement* aSVGElement) {
   if (mIsAnimated && aValue == mAnimVal) {
     return;
   }
   mAnimVal = aValue;
   mIsAnimated = true;
--- a/dom/svg/SVGAnimatedNumber.h
+++ b/dom/svg/SVGAnimatedNumber.h
@@ -22,17 +22,16 @@ namespace mozilla {
 class SMILValue;
 
 namespace dom {
 class SVGAnimationElement;
 }  // namespace dom
 
 class SVGAnimatedNumber {
  public:
-  friend class AutoChangeNumberNotifier;
   using SVGElement = dom::SVGElement;
 
   void Init(uint8_t aAttrEnum = 0xff, float aValue = 0) {
     mAnimVal = mBaseVal = aValue;
     mAttrEnum = aAttrEnum;
     mIsAnimated = false;
     mIsBaseSet = false;
   }
--- a/dom/svg/SVGAnimatedNumberPair.cpp
+++ b/dom/svg/SVGAnimatedNumberPair.cpp
@@ -11,53 +11,16 @@
 #include "SVGNumberPairSMILType.h"
 #include "mozilla/SMILValue.h"
 #include "mozilla/SVGContentUtils.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
-//----------------------------------------------------------------------
-// Helper class: AutoChangeNumberPairNotifier
-// Stack-based helper class to pair calls to WillChangeNumberPair and
-// DidChangeNumberPair.
-class MOZ_RAII AutoChangeNumberPairNotifier {
- public:
-  AutoChangeNumberPairNotifier(SVGAnimatedNumberPair* aNumberPair,
-                               SVGElement* aSVGElement, bool aDoSetAttr = true)
-      : mNumberPair(aNumberPair),
-        mSVGElement(aSVGElement),
-        mDoSetAttr(aDoSetAttr) {
-    MOZ_ASSERT(mNumberPair, "Expecting non-null numberPair");
-    MOZ_ASSERT(mSVGElement, "Expecting non-null element");
-
-    if (mDoSetAttr) {
-      mEmptyOrOldValue =
-          mSVGElement->WillChangeNumberPair(mNumberPair->mAttrEnum);
-    }
-  }
-
-  ~AutoChangeNumberPairNotifier() {
-    if (mDoSetAttr) {
-      mSVGElement->DidChangeNumberPair(mNumberPair->mAttrEnum,
-                                       mEmptyOrOldValue);
-    }
-    if (mNumberPair->mIsAnimated) {
-      mSVGElement->AnimationNeedsResample();
-    }
-  }
-
- private:
-  SVGAnimatedNumberPair* const mNumberPair;
-  SVGElement* const mSVGElement;
-  nsAttrValue mEmptyOrOldValue;
-  bool mDoSetAttr;
-};
-
 static SVGAttrTearoffTable<SVGAnimatedNumberPair,
                            SVGAnimatedNumberPair::DOMAnimatedNumber>
     sSVGFirstAnimatedNumberTearoffTable;
 static SVGAttrTearoffTable<SVGAnimatedNumberPair,
                            SVGAnimatedNumberPair::DOMAnimatedNumber>
     sSVGSecondAnimatedNumberTearoffTable;
 
 static nsresult ParseNumberOptionalNumber(const nsAString& aValue,
@@ -87,29 +50,29 @@ nsresult SVGAnimatedNumberPair::SetBaseV
     const nsAString& aValueAsString, SVGElement* aSVGElement) {
   float val[2];
 
   nsresult rv = ParseNumberOptionalNumber(aValueAsString, val);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  // We don't need to call Will/DidChange* here - we're only called by
-  // SVGElement::ParseAttribute under Element::SetAttr,
-  // which takes care of notifying.
-  AutoChangeNumberPairNotifier notifier(this, aSVGElement, false);
-
   mBaseVal[0] = val[0];
   mBaseVal[1] = val[1];
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal[0] = mBaseVal[0];
     mAnimVal[1] = mBaseVal[1];
+  } else {
+    aSVGElement->AnimationNeedsResample();
   }
 
+  // We don't need to call Will/DidChange* here - we're only called by
+  // SVGElement::ParseAttribute under Element::SetAttr,
+  // which takes care of notifying.
   return NS_OK;
 }
 
 void SVGAnimatedNumberPair::GetBaseValueString(
     nsAString& aValueAsString) const {
   aValueAsString.Truncate();
   aValueAsString.AppendFloat(mBaseVal[0]);
   if (mBaseVal[0] != mBaseVal[1]) {
@@ -119,41 +82,43 @@ void SVGAnimatedNumberPair::GetBaseValue
 }
 
 void SVGAnimatedNumberPair::SetBaseValue(float aValue, PairIndex aPairIndex,
                                          SVGElement* aSVGElement) {
   uint32_t index = (aPairIndex == eFirst ? 0 : 1);
   if (mIsBaseSet && mBaseVal[index] == aValue) {
     return;
   }
-
-  AutoChangeNumberPairNotifier notifier(this, aSVGElement);
-
+  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeNumberPair(mAttrEnum);
   mBaseVal[index] = aValue;
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal[index] = aValue;
+  } else {
+    aSVGElement->AnimationNeedsResample();
   }
+  aSVGElement->DidChangeNumberPair(mAttrEnum, emptyOrOldValue);
 }
 
 void SVGAnimatedNumberPair::SetBaseValues(float aValue1, float aValue2,
                                           SVGElement* aSVGElement) {
   if (mIsBaseSet && mBaseVal[0] == aValue1 && mBaseVal[1] == aValue2) {
     return;
   }
-
-  AutoChangeNumberPairNotifier notifier(this, aSVGElement);
-
+  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeNumberPair(mAttrEnum);
   mBaseVal[0] = aValue1;
   mBaseVal[1] = aValue2;
   mIsBaseSet = true;
   if (!mIsAnimated) {
     mAnimVal[0] = aValue1;
     mAnimVal[1] = aValue2;
+  } else {
+    aSVGElement->AnimationNeedsResample();
   }
+  aSVGElement->DidChangeNumberPair(mAttrEnum, emptyOrOldValue);
 }
 
 void SVGAnimatedNumberPair::SetAnimValue(const float aValue[2],
                                          SVGElement* aSVGElement) {
   if (mIsAnimated && mAnimVal[0] == aValue[0] && mAnimVal[1] == aValue[1]) {
     return;
   }
   mAnimVal[0] = aValue[0];
--- a/dom/svg/SVGAnimatedNumberPair.h
+++ b/dom/svg/SVGAnimatedNumberPair.h
@@ -22,17 +22,16 @@ class SMILValue;
 
 namespace dom {
 class SVGAnimationElement;
 class SVGElement;
 }  // namespace dom
 
 class SVGAnimatedNumberPair {
  public:
-  friend class AutoChangeNumberPairNotifier;
   using SVGElement = dom::SVGElement;
 
   enum PairIndex { eFirst, eSecond };
 
   void Init(uint8_t aAttrEnum = 0xff, float aValue1 = 0, float aValue2 = 0) {
     mAnimVal[0] = mBaseVal[0] = aValue1;
     mAnimVal[1] = mBaseVal[1] = aValue2;
     mAttrEnum = aAttrEnum;
--- a/dom/svg/SVGAnimatedOrient.cpp
+++ b/dom/svg/SVGAnimatedOrient.cpp
@@ -44,19 +44,19 @@ static SVGAttrTearoffTable<SVGAnimatedOr
 /* Helper functions */
 
 //----------------------------------------------------------------------
 // Helper class: AutoChangeOrientNotifier
 // Stack-based helper class to pair calls to WillChangeOrient and
 // DidChangeOrient with mozAutoDocUpdate.
 class MOZ_RAII AutoChangeOrientNotifier {
  public:
-  AutoChangeOrientNotifier(
-      SVGAnimatedOrient* aOrient, SVGElement* aSVGElement,
-      bool aDoSetAttr = true)
+  explicit AutoChangeOrientNotifier(SVGAnimatedOrient* aOrient,
+                                    SVGElement* aSVGElement,
+                                    bool aDoSetAttr = true)
       : mOrient(aOrient), mSVGElement(aSVGElement), mDoSetAttr(aDoSetAttr) {
     MOZ_ASSERT(mOrient, "Expecting non-null orient");
     if (mSVGElement && mDoSetAttr) {
       mUpdateBatch.emplace(mSVGElement->GetComposedDoc(), true);
       mEmptyOrOldValue = mSVGElement->WillChangeOrient(mUpdateBatch.ref());
     }
   }
 
--- a/dom/svg/SVGAnimatedPreserveAspectRatio.cpp
+++ b/dom/svg/SVGAnimatedPreserveAspectRatio.cpp
@@ -34,55 +34,16 @@ NS_INTERFACE_MAP_END
 
 JSObject* DOMSVGAnimatedPreserveAspectRatio::WrapObject(
     JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
   return SVGAnimatedPreserveAspectRatio_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 /* Implementation */
 
-//----------------------------------------------------------------------
-// Helper class: AutoChangePreserveAspectRatioNotifier
-// Stack-based helper class to pair calls to WillChangePreserveAspectRatio and
-// DidChangePreserveAspectRatio.
-class MOZ_RAII AutoChangePreserveAspectRatioNotifier {
- public:
-  AutoChangePreserveAspectRatioNotifier(
-      SVGAnimatedPreserveAspectRatio* aPreserveAspectRatio,
-      SVGElement* aSVGElement, bool aDoSetAttr = true)
-      : mPreserveAspectRatio(aPreserveAspectRatio),
-        mSVGElement(aSVGElement),
-        mDoSetAttr(aDoSetAttr) {
-    MOZ_ASSERT(mPreserveAspectRatio, "Expecting non-null preserveAspectRatio");
-    MOZ_ASSERT(mSVGElement, "Expecting non-null element");
-    if (mDoSetAttr) {
-      mUpdateBatch.emplace(aSVGElement->GetComposedDoc(), true);
-      mEmptyOrOldValue =
-          mSVGElement->WillChangePreserveAspectRatio(mUpdateBatch.ref());
-    }
-  }
-
-  ~AutoChangePreserveAspectRatioNotifier() {
-    if (mDoSetAttr) {
-      mSVGElement->DidChangePreserveAspectRatio(mEmptyOrOldValue,
-                                                mUpdateBatch.ref());
-    }
-    if (mPreserveAspectRatio->mIsAnimated) {
-      mSVGElement->AnimationNeedsResample();
-    }
-  }
-
- private:
-  SVGAnimatedPreserveAspectRatio* const mPreserveAspectRatio;
-  SVGElement* const mSVGElement;
-  Maybe<mozAutoDocUpdate> mUpdateBatch;
-  nsAttrValue mEmptyOrOldValue;
-  bool mDoSetAttr;
-};
-
 static SVGAttrTearoffTable<SVGAnimatedPreserveAspectRatio,
                            DOMSVGAnimatedPreserveAspectRatio>
     sSVGAnimatedPAspectRatioTearoffTable;
 static SVGAttrTearoffTable<SVGAnimatedPreserveAspectRatio,
                            DOMSVGPreserveAspectRatio>
     sBaseSVGPAspectRatioTearoffTable;
 static SVGAttrTearoffTable<SVGAnimatedPreserveAspectRatio,
                            DOMSVGPreserveAspectRatio>
@@ -123,45 +84,64 @@ DOMSVGAnimatedPreserveAspectRatio::AnimV
 nsresult SVGAnimatedPreserveAspectRatio::SetBaseValueString(
     const nsAString& aValueAsString, SVGElement* aSVGElement, bool aDoSetAttr) {
   SVGPreserveAspectRatio val;
   nsresult res = SVGPreserveAspectRatio::FromString(aValueAsString, &val);
   if (NS_FAILED(res)) {
     return res;
   }
 
-  AutoChangePreserveAspectRatioNotifier notifier(this, aSVGElement, aDoSetAttr);
+  Maybe<mozAutoDocUpdate> updateBatch;
+  nsAttrValue emptyOrOldValue;
+  if (aDoSetAttr) {
+    updateBatch.emplace(aSVGElement->GetComposedDoc(), true);
+    emptyOrOldValue =
+        aSVGElement->WillChangePreserveAspectRatio(updateBatch.ref());
+  }
 
   mBaseVal = val;
   mIsBaseSet = true;
+
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
   }
-
+  if (aDoSetAttr) {
+    aSVGElement->DidChangePreserveAspectRatio(emptyOrOldValue,
+                                              updateBatch.ref());
+  }
+  if (mIsAnimated) {
+    aSVGElement->AnimationNeedsResample();
+  }
   return NS_OK;
 }
 
 void SVGAnimatedPreserveAspectRatio::GetBaseValueString(
     nsAString& aValueAsString) const {
   mBaseVal.ToString(aValueAsString);
 }
 
 void SVGAnimatedPreserveAspectRatio::SetBaseValue(
     const SVGPreserveAspectRatio& aValue, SVGElement* aSVGElement) {
   if (mIsBaseSet && mBaseVal == aValue) {
     return;
   }
 
-  AutoChangePreserveAspectRatioNotifier notifier(this, aSVGElement);
-
+  mozAutoDocUpdate updateBatch(aSVGElement->GetComposedDoc(), true);
+  nsAttrValue emptyOrOldValue =
+      aSVGElement->WillChangePreserveAspectRatio(updateBatch);
   mBaseVal = aValue;
   mIsBaseSet = true;
+
   if (!mIsAnimated) {
     mAnimVal = mBaseVal;
   }
+  aSVGElement->DidChangePreserveAspectRatio(emptyOrOldValue, updateBatch);
+  if (mIsAnimated) {
+    aSVGElement->AnimationNeedsResample();
+  }
 }
 
 static uint64_t PackPreserveAspectRatio(const SVGPreserveAspectRatio& par) {
   // All preserveAspectRatio values are enum values (do not interpolate), so we
   // can safely collate them and treat them as a single enum as for SMIL.
   uint64_t packed = 0;
   packed |= uint64_t(par.GetAlign()) << 8;
   packed |= uint64_t(par.GetMeetOrSlice());
--- a/dom/svg/SVGAnimatedPreserveAspectRatio.h
+++ b/dom/svg/SVGAnimatedPreserveAspectRatio.h
@@ -20,18 +20,16 @@ namespace mozilla {
 class SMILValue;
 
 namespace dom {
 class DOMSVGAnimatedPreserveAspectRatio;
 class SVGAnimationElement;
 }  // namespace dom
 
 class SVGAnimatedPreserveAspectRatio final {
-  friend class AutoChangePreserveAspectRatioNotifier;
-
  public:
   void Init() {
     mBaseVal.mAlign =
         dom::SVGPreserveAspectRatio_Binding::SVG_PRESERVEASPECTRATIO_XMIDYMID;
     mBaseVal.mMeetOrSlice =
         dom::SVGPreserveAspectRatio_Binding::SVG_MEETORSLICE_MEET;
     mAnimVal = mBaseVal;
     mIsAnimated = false;
--- a/dom/svg/SVGAnimatedViewBox.cpp
+++ b/dom/svg/SVGAnimatedViewBox.cpp
@@ -67,51 +67,16 @@ nsresult SVGViewBox::FromString(const ns
 
 static SVGAttrTearoffTable<SVGAnimatedViewBox, SVGRect>
     sBaseSVGViewBoxTearoffTable;
 static SVGAttrTearoffTable<SVGAnimatedViewBox, SVGRect>
     sAnimSVGViewBoxTearoffTable;
 SVGAttrTearoffTable<SVGAnimatedViewBox, SVGAnimatedRect>
     SVGAnimatedViewBox::sSVGAnimatedRectTearoffTable;
 
-//----------------------------------------------------------------------
-// Helper class: AutoChangeViewBoxNotifier
-// Stack-based helper class to pair calls to WillChangeViewBox and
-// DidChangeViewBox.
-class MOZ_RAII AutoChangeViewBoxNotifier {
- public:
-  AutoChangeViewBoxNotifier(SVGAnimatedViewBox* aViewBox,
-                            SVGElement* aSVGElement, bool aDoSetAttr = true)
-      : mViewBox(aViewBox), mSVGElement(aSVGElement), mDoSetAttr(aDoSetAttr) {
-    MOZ_ASSERT(mViewBox, "Expecting non-null viewBox");
-    MOZ_ASSERT(mSVGElement, "Expecting non-null element");
-
-    if (mDoSetAttr) {
-      mUpdateBatch.emplace(aSVGElement->GetComposedDoc(), true);
-      mEmptyOrOldValue = mSVGElement->WillChangeViewBox(mUpdateBatch.ref());
-    }
-  }
-
-  ~AutoChangeViewBoxNotifier() {
-    if (mDoSetAttr) {
-      mSVGElement->DidChangeViewBox(mEmptyOrOldValue, mUpdateBatch.ref());
-    }
-    if (mViewBox->mAnimVal) {
-      mSVGElement->AnimationNeedsResample();
-    }
-  }
-
- private:
-  SVGAnimatedViewBox* const mViewBox;
-  SVGElement* const mSVGElement;
-  Maybe<mozAutoDocUpdate> mUpdateBatch;
-  nsAttrValue mEmptyOrOldValue;
-  bool mDoSetAttr;
-};
-
 /* Implementation of SVGAnimatedViewBox methods */
 
 void SVGAnimatedViewBox::Init() {
   mHasBaseVal = false;
   // We shouldn't use mBaseVal for rendering (its usages should be guarded with
   // "mHasBaseVal" checks), but just in case we do by accident, this will
   // ensure that we treat it as "none" and ignore its numeric values:
   mBaseVal.none = true;
@@ -154,40 +119,57 @@ void SVGAnimatedViewBox::SetBaseValue(co
     // This method is used to set a single x, y, width
     // or height value. It can't create a base value
     // as the other components may be undefined. We record
     // the new value though, so as not to lose data.
     mBaseVal = aRect;
     return;
   }
 
-  AutoChangeViewBoxNotifier notifier(this, aSVGElement);
+  mozAutoDocUpdate updateBatch(aSVGElement->GetComposedDoc(), true);
+  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeViewBox(updateBatch);
 
   mBaseVal = aRect;
   mHasBaseVal = true;
+
+  aSVGElement->DidChangeViewBox(emptyOrOldValue, updateBatch);
+  if (mAnimVal) {
+    aSVGElement->AnimationNeedsResample();
+  }
 }
 
 nsresult SVGAnimatedViewBox::SetBaseValueString(const nsAString& aValue,
                                                 SVGElement* aSVGElement,
                                                 bool aDoSetAttr) {
   SVGViewBox viewBox;
 
   nsresult rv = SVGViewBox::FromString(aValue, &viewBox);
   if (NS_FAILED(rv)) {
     return rv;
   }
   // Comparison against mBaseVal is only valid if we currently have a base val.
   if (mHasBaseVal && viewBox == mBaseVal) {
     return NS_OK;
   }
 
-  AutoChangeViewBoxNotifier notifier(this, aSVGElement, aDoSetAttr);
+  Maybe<mozAutoDocUpdate> updateBatch;
+  nsAttrValue emptyOrOldValue;
+  if (aDoSetAttr) {
+    updateBatch.emplace(aSVGElement->GetComposedDoc(), true);
+    emptyOrOldValue = aSVGElement->WillChangeViewBox(updateBatch.ref());
+  }
   mHasBaseVal = true;
   mBaseVal = viewBox;
 
+  if (aDoSetAttr) {
+    aSVGElement->DidChangeViewBox(emptyOrOldValue, updateBatch.ref());
+  }
+  if (mAnimVal) {
+    aSVGElement->AnimationNeedsResample();
+  }
   return NS_OK;
 }
 
 void SVGAnimatedViewBox::GetBaseValueString(nsAString& aValue) const {
   if (mBaseVal.none) {
     aValue.AssignLiteral("none");
     return;
   }
--- a/dom/svg/SVGAnimatedViewBox.h
+++ b/dom/svg/SVGAnimatedViewBox.h
@@ -35,17 +35,16 @@ struct SVGViewBox {
       : x(aX), y(aY), width(aWidth), height(aHeight), none(false) {}
   bool operator==(const SVGViewBox& aOther) const;
 
   static nsresult FromString(const nsAString& aStr, SVGViewBox* aViewBox);
 };
 
 class SVGAnimatedViewBox {
  public:
-  friend class AutoChangeViewBoxNotifier;
   using SVGElement = dom::SVGElement;
 
   void Init();
 
   /**
    * Returns true if the corresponding "viewBox" attribute defined a rectangle
    * with finite values and nonnegative width/height.
    * Returns false if the viewBox was set to an invalid