Bug 1495438. Move most code from SVGObserverUtils.h to its source file. r=longsonr
authorJonathan Watt <jwatt@jwatt.org>
Fri, 21 Sep 2018 19:09:41 +0100
changeset 494733 6d63dbe4ed3b874d2bab67871f47a1453c1a51d1
parent 494732 7112aa92f4420040221c14cf2e931b8ddae75a39
child 494734 2c68f94353082ab810ab4bb81f2d88273229fba2
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslongsonr
bugs1495438
milestone64.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
Bug 1495438. Move most code from SVGObserverUtils.h to its source file. r=longsonr Differential Revision: https://phabricator.services.mozilla.com/D7293
layout/svg/SVGObserverUtils.cpp
layout/svg/SVGObserverUtils.h
--- a/layout/svg/SVGObserverUtils.cpp
+++ b/layout/svg/SVGObserverUtils.cpp
@@ -49,16 +49,60 @@ ResolveURLUsingLocalRef(nsIFrame* aFrame
   return do_AddRef(new URLAndReferrerInfo(uri,
                                           aURL->mExtraData->GetReferrer(),
                                           aURL->mExtraData->GetReferrerPolicy()));
 }
 
 
 namespace mozilla {
 
+class SVGFilterObserverList;
+
+
+/**
+ * A class used as a member of the "observer" classes below to help them
+ * avoid dereferencing their frame during presshell teardown when their frame
+ * may have been destroyed (leaving their pointer to their frame dangling).
+ *
+ * When a presshell is torn down, the properties for each frame may not be
+ * deleted until after the frames are destroyed.  "Observer" objects (attached
+ * as frame properties) must therefore check whether the presshell is being
+ * torn down before using their pointer to their frame.
+ *
+ * mFramePresShell may be null, but when mFrame is non-null, mFramePresShell
+ * is guaranteed to be non-null, too.
+ */
+struct nsSVGFrameReferenceFromProperty
+{
+  explicit nsSVGFrameReferenceFromProperty(nsIFrame* aFrame)
+    : mFrame(aFrame)
+    , mFramePresShell(aFrame->PresShell())
+  {}
+
+  // Clear our reference to the frame.
+  void Detach() {
+    mFrame = nullptr;
+    mFramePresShell = nullptr;
+  }
+
+  // null if the frame has become invalid
+  nsIFrame* Get() {
+    if (mFramePresShell && mFramePresShell->IsDestroying()) {
+      Detach(); // mFrame is no longer valid.
+    }
+    return mFrame;
+  }
+
+private:
+  // The frame that our property is attached to (may be null).
+  nsIFrame* mFrame;
+  nsIPresShell* mFramePresShell;
+};
+
+
 void
 SVGRenderingObserver::StartObserving()
 {
   Element* target = GetReferencedElementWithoutObserving();
   if (target) {
     target->AddMutationObserver(this);
   }
 }
@@ -73,41 +117,28 @@ SVGRenderingObserver::StopObserving()
     if (mInObserverList) {
       SVGObserverUtils::RemoveRenderingObserver(target, this);
       mInObserverList = false;
     }
   }
   NS_ASSERTION(!mInObserverList, "still in an observer list?");
 }
 
-static SVGRenderingObserverList*
-GetObserverList(Element *aElement)
-{
-  return static_cast<SVGRenderingObserverList*>
-    (aElement->GetProperty(nsGkAtoms::renderingobserverlist));
-}
-
 Element*
 SVGRenderingObserver::GetAndObserveReferencedElement()
 {
-  Element* target = GetReferencedElementWithoutObserving();
 #ifdef DEBUG
-  if (target) {
-    SVGRenderingObserverList* observerList = GetObserverList(target);
-    bool inObserverList = observerList && observerList->Contains(this);
-    NS_ASSERTION(inObserverList == mInObserverList, "failed to track whether we're in our referenced element's observer list!");
-  } else {
-    NS_ASSERTION(!mInObserverList, "In whose observer list are we, then?");
-  }
+  DebugObserverSet();
 #endif
-  if (target && !mInObserverList) {
-    SVGObserverUtils::AddRenderingObserver(target, this);
+  Element* referencedElement = GetReferencedElementWithoutObserving();
+  if (referencedElement && !mInObserverList) {
+    SVGObserverUtils::AddRenderingObserver(referencedElement, this);
     mInObserverList = true;
   }
-  return target;
+  return referencedElement;
 }
 
 nsIFrame*
 SVGRenderingObserver::GetAndObserveReferencedFrame()
 {
   Element* referencedElement = GetAndObserveReferencedElement();
   return referencedElement ? referencedElement->GetPrimaryFrame() : nullptr;
 }
@@ -178,33 +209,89 @@ SVGRenderingObserver::ContentInserted(ns
 
 void
 SVGRenderingObserver::ContentRemoved(nsIContent* aChild,
                                      nsIContent* aPreviousSibling)
 {
   OnRenderingChange();
 }
 
+
+/**
+ * SVG elements reference supporting resources by element ID. We need to
+ * track when those resources change and when the document changes in ways
+ * that affect which element is referenced by a given ID (e.g., when
+ * element IDs change). The code here is responsible for that.
+ *
+ * When a frame references a supporting resource, we create a property
+ * object derived from SVGIDRenderingObserver to manage the relationship. The
+ * property object is attached to the referencing frame.
+ */
+class SVGIDRenderingObserver : public SVGRenderingObserver
+{
+public:
+  SVGIDRenderingObserver(URLAndReferrerInfo* aURI, nsIContent* aObservingContent,
+                         bool aReferenceImage);
+  virtual ~SVGIDRenderingObserver() {
+    StopObserving();
+  }
+
+protected:
+  Element* GetReferencedElementWithoutObserving() override {
+    return mObservedElementTracker.get();
+  }
+
+  void OnRenderingChange() override;
+
+  /**
+   * Helper that provides a reference to the element with the ID that our
+   * observer wants to observe, and that will invalidate our observer if the
+   * element that that ID identifies changes to a different element (or none).
+   */
+  class ElementTracker final : public IDTracker
+  {
+  public:
+    explicit ElementTracker(SVGIDRenderingObserver* aOwningObserver)
+      : mOwningObserver(aOwningObserver)
+    {}
+  protected:
+    void ElementChanged(Element* aFrom, Element* aTo) override {
+      mOwningObserver->StopObserving(); // stop observing the old element
+      IDTracker::ElementChanged(aFrom, aTo);
+      mOwningObserver->StartObserving(); // start observing the new element
+      mOwningObserver->OnRenderingChange();
+    }
+    /**
+     * Override IsPersistent because we want to keep tracking the element
+     * for the ID even when it changes.
+     */
+    bool IsPersistent() override { return true; }
+  private:
+    SVGIDRenderingObserver* mOwningObserver;
+  };
+
+  ElementTracker mObservedElementTracker;
+};
+
 /**
  * Note that in the current setup there are two separate observer lists.
  *
  * In SVGIDRenderingObserver's ctor, the new object adds itself to the
  * mutation observer list maintained by the referenced element. In this way the
  * SVGIDRenderingObserver is notified if there are any attribute or content
  * tree changes to the element or any of its *descendants*.
  *
  * In SVGIDRenderingObserver::GetAndObserveReferencedElement() the
  * SVGIDRenderingObserver object also adds itself to an
  * SVGRenderingObserverList object belonging to the referenced
  * element.
  *
  * XXX: it would be nice to have a clear and concise executive summary of the
  * benefits/necessity of maintaining a second observer list.
  */
-
 SVGIDRenderingObserver::SVGIDRenderingObserver(URLAndReferrerInfo* aURI,
                                                nsIContent* aObservingContent,
                                                bool aReferenceImage)
   : mObservedElementTracker(this)
 {
   // Start watching the target element
   nsCOMPtr<nsIURI> uri;
   nsCOMPtr<nsIURI> referrer;
@@ -215,66 +302,44 @@ SVGIDRenderingObserver::SVGIDRenderingOb
     referrerPolicy = aURI->GetReferrerPolicy();
   }
 
   mObservedElementTracker.Reset(aObservingContent, uri, referrer,
                                 referrerPolicy, true, aReferenceImage);
   StartObserving();
 }
 
-SVGIDRenderingObserver::~SVGIDRenderingObserver()
-{
-  StopObserving();
-}
-
 void
 SVGIDRenderingObserver::OnRenderingChange()
 {
   if (mObservedElementTracker.get() && mInObserverList) {
     SVGObserverUtils::RemoveRenderingObserver(mObservedElementTracker.get(), this);
     mInObserverList = false;
   }
 }
 
-void
-nsSVGFrameReferenceFromProperty::Detach()
+
+class nsSVGRenderingObserverProperty : public SVGIDRenderingObserver
 {
-  mFrame = nullptr;
-  mFramePresShell = nullptr;
-}
-
-nsIFrame*
-nsSVGFrameReferenceFromProperty::Get()
-{
-  if (mFramePresShell && mFramePresShell->IsDestroying()) {
-    // mFrame is no longer valid.
-    Detach();
-  }
-  return mFrame;
-}
-
+public:
+  NS_DECL_ISUPPORTS
 
-NS_IMPL_ISUPPORTS(SVGTemplateElementObserver, nsIMutationObserver)
-
-void
-SVGTemplateElementObserver::OnRenderingChange()
-{
-  SVGIDRenderingObserver::OnRenderingChange();
+  nsSVGRenderingObserverProperty(URLAndReferrerInfo* aURI, nsIFrame *aFrame,
+                                 bool aReferenceImage)
+    : SVGIDRenderingObserver(aURI, aFrame->GetContent(), aReferenceImage)
+    , mFrameReference(aFrame)
+  {}
 
-  if (nsIFrame* frame = mFrameReference.Get()) {
-    // We know that we don't need to walk the parent chain notifying rendering
-    // observers since changes to a gradient etc. do not affect ancestor
-    // elements.  So we only invalidate *direct* rendering observers here.
-    // Since we don't need to walk the parent chain, we don't need to worry
-    // about coalescing multiple invalidations by using a change hint as we do
-    // in nsSVGRenderingObserverProperty::OnRenderingChange.
-    SVGObserverUtils::InvalidateDirectRenderingObservers(frame);
-  }
-}
+protected:
+  virtual ~nsSVGRenderingObserverProperty() = default; // non-public
 
+  void OnRenderingChange() override;
+
+  nsSVGFrameReferenceFromProperty mFrameReference;
+};
 
 NS_IMPL_ISUPPORTS(nsSVGRenderingObserverProperty, nsIMutationObserver)
 
 void
 nsSVGRenderingObserverProperty::OnRenderingChange()
 {
   SVGIDRenderingObserver::OnRenderingChange();
 
@@ -288,16 +353,199 @@ nsSVGRenderingObserverProperty::OnRender
     // coalesced).
     nsLayoutUtils::PostRestyleEvent(
       frame->GetContent()->AsElement(), nsRestyleHint(0),
       nsChangeHint_InvalidateRenderingObservers);
   }
 }
 
 
+class SVGTextPathObserver final : public nsSVGRenderingObserverProperty
+{
+public:
+  SVGTextPathObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame, bool aReferenceImage)
+    : nsSVGRenderingObserverProperty(aURI, aFrame, aReferenceImage)
+    , mValid(true)
+  {}
+
+  bool ObservesReflow() override {
+    return false;
+  }
+
+protected:
+  void OnRenderingChange() override;
+
+private:
+  /**
+   * Returns true if the target of the textPath is the frame of a 'path' element.
+   */
+  bool TargetIsValid() {
+    Element* target = GetReferencedElementWithoutObserving();
+    return target && target->IsSVGElement(nsGkAtoms::path);
+  }
+
+  bool mValid;
+};
+
+void
+SVGTextPathObserver::OnRenderingChange()
+{
+  nsSVGRenderingObserverProperty::OnRenderingChange();
+
+  nsIFrame* frame = mFrameReference.Get();
+  if (!frame) {
+    return;
+  }
+
+  MOZ_ASSERT(frame->IsFrameOfType(nsIFrame::eSVG) ||
+             nsSVGUtils::IsInSVGTextSubtree(frame),
+             "SVG frame expected");
+
+  // Avoid getting into an infinite loop of reflows if the <textPath> is
+  // pointing to one of its ancestors.  TargetIsValid returns true iff
+  // the target element is a <path> element, and we would not have this
+  // SVGTextPathObserver if this <textPath> were a descendant of the
+  // target <path>.
+  //
+  // Note that we still have to post the restyle event when we
+  // change from being valid to invalid, so that mPositions on the
+  // SVGTextFrame gets updated, skipping the <textPath>, ensuring
+  // that nothing gets painted for that element.
+  bool nowValid = TargetIsValid();
+  if (!mValid && !nowValid) {
+    // Just return if we were previously invalid, and are still invalid.
+    return;
+  }
+  mValid = nowValid;
+
+  // Repaint asynchronously in case the path frame is being torn down
+  nsChangeHint changeHint =
+    nsChangeHint(nsChangeHint_RepaintFrame | nsChangeHint_UpdateTextPath);
+  frame->PresContext()->RestyleManager()->PostRestyleEvent(
+    frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint);
+}
+
+
+class SVGMarkerObserver final: public nsSVGRenderingObserverProperty
+{
+public:
+  SVGMarkerObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame, bool aReferenceImage)
+    : nsSVGRenderingObserverProperty(aURI, aFrame, aReferenceImage)
+  {}
+
+protected:
+  void OnRenderingChange() override;
+};
+
+void
+SVGMarkerObserver::OnRenderingChange()
+{
+  nsSVGRenderingObserverProperty::OnRenderingChange();
+
+  nsIFrame* frame = mFrameReference.Get();
+  if (!frame) {
+    return;
+  }
+
+  MOZ_ASSERT(frame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
+
+  // Don't need to request ReflowFrame if we're being reflowed.
+  if (!(frame->GetStateBits() & NS_FRAME_IN_REFLOW)) {
+    // XXXjwatt: We need to unify SVG into standard reflow so we can just use
+    // nsChangeHint_NeedReflow | nsChangeHint_NeedDirtyReflow here.
+    // XXXSDL KILL THIS!!!
+    nsSVGUtils::ScheduleReflowSVG(frame);
+  }
+  frame->PresContext()->RestyleManager()->PostRestyleEvent(
+    frame->GetContent()->AsElement(), nsRestyleHint(0),
+    nsChangeHint_RepaintFrame);
+}
+
+
+class nsSVGPaintingProperty final : public nsSVGRenderingObserverProperty
+{
+public:
+  nsSVGPaintingProperty(URLAndReferrerInfo* aURI, nsIFrame* aFrame, bool aReferenceImage)
+    : nsSVGRenderingObserverProperty(aURI, aFrame, aReferenceImage)
+  {}
+
+protected:
+  void OnRenderingChange() override;
+};
+
+void
+nsSVGPaintingProperty::OnRenderingChange()
+{
+  nsSVGRenderingObserverProperty::OnRenderingChange();
+
+  nsIFrame* frame = mFrameReference.Get();
+  if (!frame) {
+    return;
+  }
+
+  if (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
+    frame->InvalidateFrameSubtree();
+  } else {
+    for (nsIFrame* f = frame; f;
+         f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
+      f->InvalidateFrame();
+    }
+  }
+}
+
+
+/**
+ * In a filter chain, there can be multiple SVG reference filters.
+ * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2);
+ *
+ * This class keeps track of one SVG reference filter in a filter chain.
+ * e.g. url(#svg-filter-1)
+ *
+ * It fires invalidations when the SVG filter element's id changes or when
+ * the SVG filter element's content changes.
+ *
+ * The SVGFilterObserverList class manages a list of SVGFilterObservers.
+ */
+class SVGFilterObserver final : public SVGIDRenderingObserver
+{
+public:
+  SVGFilterObserver(URLAndReferrerInfo* aURI,
+                    nsIContent* aObservingContent,
+                    SVGFilterObserverList* aFilterChainObserver)
+    : SVGIDRenderingObserver(aURI, aObservingContent, false)
+    , mFilterObserverList(aFilterChainObserver)
+  {}
+
+  // XXXjwatt: This will return false if the reference is to a filter in an
+  // external resource document that hasn't loaded yet!
+  bool ReferencesValidResource() { return GetAndObserveFilterFrame(); }
+
+  void DetachFromChainObserver() { mFilterObserverList = nullptr; }
+
+  /**
+   * @return the filter frame, or null if there is no filter frame
+   */
+  nsSVGFilterFrame* GetAndObserveFilterFrame();
+
+  // nsISupports
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(SVGFilterObserver)
+
+  void Invalidate() { OnRenderingChange(); };
+
+protected:
+  virtual ~SVGFilterObserver() = default; // non-public
+
+  // SVGIDRenderingObserver
+  void OnRenderingChange() override;
+
+private:
+  SVGFilterObserverList* mFilterObserverList;
+};
+
 NS_IMPL_CYCLE_COLLECTING_ADDREF(SVGFilterObserver)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(SVGFilterObserver)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGFilterObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
 NS_INTERFACE_MAP_END
 
@@ -314,16 +562,63 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 nsSVGFilterFrame*
 SVGFilterObserver::GetAndObserveFilterFrame()
 {
   return static_cast<nsSVGFilterFrame*>(
     GetAndObserveReferencedFrame(LayoutFrameType::SVGFilter, nullptr));
 }
 
+
+/**
+ * This class manages a list of SVGFilterObservers, which correspond to
+ * reference to SVG filters in a list of filters in a given 'filter' property.
+ * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2);
+ *
+ * In the above example, the SVGFilterObserverList will manage two
+ * SVGFilterObservers, one for each of the references to SVG filters.  CSS
+ * filters like "blur(10px)" don't reference filter elements, so they don't
+ * need an SVGFilterObserver.  The style system invalidates changes to CSS
+ * filters.
+ */
+class SVGFilterObserverList : public nsISupports
+{
+public:
+  SVGFilterObserverList(const nsTArray<nsStyleFilter>& aFilters,
+                        nsIContent* aFilteredElement,
+                        nsIFrame* aFiltedFrame = nullptr);
+
+  bool ReferencesValidResources();
+  void Invalidate() { OnRenderingChange(); }
+
+  const nsTArray<RefPtr<SVGFilterObserver>>& GetObservers() const {
+    return mObservers;
+  }
+
+  // nsISupports
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(SVGFilterObserverList)
+
+protected:
+  virtual ~SVGFilterObserverList();
+
+  virtual void OnRenderingChange() = 0;
+
+private:
+
+  void DetachObservers()
+  {
+    for (uint32_t i = 0; i < mObservers.Length(); i++) {
+      mObservers[i]->DetachFromChainObserver();
+    }
+  }
+
+  nsTArray<RefPtr<SVGFilterObserver>> mObservers;
+};
+
 void
 SVGFilterObserver::OnRenderingChange()
 {
   SVGIDRenderingObserver::OnRenderingChange();
 
   if (mFilterObserverList) {
     mFilterObserverList->Invalidate();
   }
@@ -347,24 +642,25 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 SVGFilterObserverList::SVGFilterObserverList(const nsTArray<nsStyleFilter>& aFilters,
                                              nsIContent* aFilteredElement,
                                              nsIFrame* aFilteredFrame)
 {
   for (uint32_t i = 0; i < aFilters.Length(); i++) {
-    if (aFilters[i].GetType() != NS_STYLE_FILTER_URL)
+    if (aFilters[i].GetType() != NS_STYLE_FILTER_URL) {
       continue;
+    }
 
     // aFilteredFrame can be null if this filter belongs to a
     // CanvasRenderingContext2D.
     RefPtr<URLAndReferrerInfo> filterURL;
     if (aFilteredFrame) {
-      filterURL = SVGObserverUtils::GetFilterURI(aFilteredFrame, i);
+      filterURL = ResolveURLUsingLocalRef(aFilteredFrame, aFilters[i].GetURL());
     } else {
       nsCOMPtr<nsIURI> resolvedURI =
         aFilters[i].GetURL()->ResolveLocalRef(aFilteredElement);
       if (resolvedURI) {
         filterURL = new URLAndReferrerInfo(
           resolvedURI,
           aFilters[i].GetURL()->mExtraData->GetReferrer(),
           aFilters[i].GetURL()->mExtraData->GetReferrerPolicy());
@@ -388,16 +684,35 @@ SVGFilterObserverList::ReferencesValidRe
   for (uint32_t i = 0; i < mObservers.Length(); i++) {
     if (!mObservers[i]->ReferencesValidResource()) {
       return false;
     }
   }
   return true;
 }
 
+
+class SVGFilterObserverListForCSSProp final : public SVGFilterObserverList
+{
+public:
+  SVGFilterObserverListForCSSProp(const nsTArray<nsStyleFilter>& aFilters,
+                                  nsIFrame* aFilteredFrame)
+    : SVGFilterObserverList(aFilters, aFilteredFrame->GetContent(),
+                            aFilteredFrame)
+    , mFrameReference(aFilteredFrame)
+  {}
+
+  void DetachFromFrame() { mFrameReference.Detach(); }
+
+protected:
+  void OnRenderingChange() override;
+
+  nsSVGFrameReferenceFromProperty mFrameReference;
+};
+
 void
 SVGFilterObserverListForCSSProp::OnRenderingChange()
 {
   nsIFrame* frame = mFrameReference.Get();
   if (!frame)
     return;
 
   // Repaint asynchronously in case the filter frame is being torn down
@@ -426,63 +741,63 @@ class SVGFilterObserverListForCanvasCont
 public:
   SVGFilterObserverListForCanvasContext(CanvasRenderingContext2D* aContext,
                                         Element* aCanvasElement,
                                         nsTArray<nsStyleFilter>& aFilters)
     : SVGFilterObserverList(aFilters, aCanvasElement)
     , mContext(aContext)
   {}
 
-  virtual void OnRenderingChange() override
-  {
-    if (!mContext) {
-      MOZ_CRASH("GFX: This should never be called without a context");
-    }
-    // Refresh the cached FilterDescription in mContext->CurrentState().filter.
-    // If this filter is not at the top of the state stack, we'll refresh the
-    // wrong filter, but that's ok, because we'll refresh the right filter
-    // when we pop the state stack in CanvasRenderingContext2D::Restore().
-    RefPtr<CanvasRenderingContext2D> kungFuDeathGrip(mContext);
-    kungFuDeathGrip->UpdateFilter();
-  }
-
+  void OnRenderingChange() override;
   void DetachFromContext() { mContext = nullptr; }
 
 private:
   CanvasRenderingContext2D* mContext;
 };
 
-
 void
-SVGMarkerObserver::OnRenderingChange()
+SVGFilterObserverListForCanvasContext::OnRenderingChange()
 {
-  nsSVGRenderingObserverProperty::OnRenderingChange();
+  if (!mContext) {
+    MOZ_CRASH("GFX: This should never be called without a context");
+  }
+  // Refresh the cached FilterDescription in mContext->CurrentState().filter.
+  // If this filter is not at the top of the state stack, we'll refresh the
+  // wrong filter, but that's ok, because we'll refresh the right filter
+  // when we pop the state stack in CanvasRenderingContext2D::Restore().
+  RefPtr<CanvasRenderingContext2D> kungFuDeathGrip(mContext);
+  kungFuDeathGrip->UpdateFilter();
+}
 
-  nsIFrame* frame = mFrameReference.Get();
-  if (!frame)
-    return;
-
-  NS_ASSERTION(frame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
 
-  // Don't need to request ReflowFrame if we're being reflowed.
-  if (!(frame->GetStateBits() & NS_FRAME_IN_REFLOW)) {
-    // XXXjwatt: We need to unify SVG into standard reflow so we can just use
-    // nsChangeHint_NeedReflow | nsChangeHint_NeedDirtyReflow here.
-    // XXXSDL KILL THIS!!!
-    nsSVGUtils::ScheduleReflowSVG(frame);
+class SVGMaskObserverList final : public nsISupports
+{
+public:
+  explicit SVGMaskObserverList(nsIFrame* aFrame);
+
+  // nsISupports
+  NS_DECL_ISUPPORTS
+
+  const nsTArray<RefPtr<nsSVGPaintingProperty>>& GetObservers() const
+  {
+    return mProperties;
   }
-  frame->PresContext()->RestyleManager()->PostRestyleEvent(
-    frame->GetContent()->AsElement(), nsRestyleHint(0),
-    nsChangeHint_RepaintFrame);
-}
+
+  void ResolveImage(uint32_t aIndex);
+
+private:
+  virtual ~SVGMaskObserverList() = default; // non-public
+  nsTArray<RefPtr<nsSVGPaintingProperty>> mProperties;
+  nsIFrame* mFrame;
+};
 
 NS_IMPL_ISUPPORTS(SVGMaskObserverList, nsISupports)
 
 SVGMaskObserverList::SVGMaskObserverList(nsIFrame* aFrame)
- : mFrame(aFrame)
+  : mFrame(aFrame)
 {
   const nsStyleSVGReset *svgReset = aFrame->StyleSVGReset();
 
   for (uint32_t i = 0; i < svgReset->mMask.mImageCount; i++) {
     css::URLValueData* data = svgReset->mMask.mLayers[i].mImage.GetURLValue();
     RefPtr<URLAndReferrerInfo> maskUri = ResolveURLUsingLocalRef(aFrame, data);
 
     bool hasRef = false;
@@ -519,79 +834,258 @@ SVGMaskObserverList::ResolveImage(uint32
     mozilla::css::ImageLoader* imageLoader =
       mFrame->PresContext()->Document()->StyleImageLoader();
     if (imgRequestProxy* req = image.GetImageData()) {
       imageLoader->AssociateRequestToFrame(req, mFrame, 0);
     }
   }
 }
 
-bool
-SVGTextPathObserver::TargetIsValid()
+
+/**
+ * Used for gradient-to-gradient, pattern-to-pattern and filter-to-filter
+ * references to "template" elements (specified via the 'href' attributes).
+ *
+ * This is a special class for the case where we know we only want to call
+ * InvalidateDirectRenderingObservers (as opposed to
+ * InvalidateRenderingObservers).
+ *
+ * TODO(jwatt): If we added a new NS_FRAME_RENDERING_OBSERVER_CONTAINER state
+ * bit to clipPath, filter, gradients, marker, mask, pattern and symbol, and
+ * could have InvalidateRenderingObservers stop on reaching such an element,
+ * then we would no longer need this class (not to mention improving perf by
+ * significantly cutting down on ancestor traversal).
+ */
+class SVGTemplateElementObserver : public SVGIDRenderingObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  SVGTemplateElementObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
+                             bool aReferenceImage)
+    : SVGIDRenderingObserver(aURI, aFrame->GetContent(), aReferenceImage)
+    , mFrameReference(aFrame)
+  {}
+
+protected:
+  virtual ~SVGTemplateElementObserver() = default; // non-public
+
+  void OnRenderingChange() override;
+
+  nsSVGFrameReferenceFromProperty mFrameReference;
+};
+
+NS_IMPL_ISUPPORTS(SVGTemplateElementObserver, nsIMutationObserver)
+
+void
+SVGTemplateElementObserver::OnRenderingChange()
 {
-  Element* target = GetReferencedElementWithoutObserving();
-  return target && target->IsSVGElement(nsGkAtoms::path);
+  SVGIDRenderingObserver::OnRenderingChange();
+
+  if (nsIFrame* frame = mFrameReference.Get()) {
+    // We know that we don't need to walk the parent chain notifying rendering
+    // observers since changes to a gradient etc. do not affect ancestor
+    // elements.  So we only invalidate *direct* rendering observers here.
+    // Since we don't need to walk the parent chain, we don't need to worry
+    // about coalescing multiple invalidations by using a change hint as we do
+    // in nsSVGRenderingObserverProperty::OnRenderingChange.
+    SVGObserverUtils::InvalidateDirectRenderingObservers(frame);
+  }
+}
+
+
+/**
+ * XXXjwatt: unlike this other *List classes above, this is stored on the
+ * referenced element, not the referencing element.  Unlike the other list
+ * classes it is also not an ordered list - it's an unordered set. We should
+ * rename this accordingly to SVGRenderingObserverSet to make things clearer.
+ *
+ * A manager for one-shot SVGRenderingObserver tracking.
+ * nsSVGRenderingObservers can be added or removed. They are not strongly
+ * referenced so an observer must be removed before it dies.
+ * When InvalidateAll is called, all outstanding references get
+ * OnNonDOMMutationRenderingChange()
+ * called on them and the list is cleared. The intent is that
+ * the observer will force repainting of whatever part of the document
+ * is needed, and then at paint time the observer will do a clean lookup
+ * of the referenced element and [re-]add itself to the element's observer list.
+ *
+ * InvalidateAll must be called before this object is destroyed, i.e.
+ * before the referenced frame is destroyed. This should normally happen
+ * via nsSVGContainerFrame::RemoveFrame, since only frames in the frame
+ * tree should be referenced.
+ */
+class SVGRenderingObserverList
+{
+public:
+  SVGRenderingObserverList()
+    : mObservers(4)
+  {
+    MOZ_COUNT_CTOR(SVGRenderingObserverList);
+  }
+
+  ~SVGRenderingObserverList() {
+    InvalidateAll();
+    MOZ_COUNT_DTOR(SVGRenderingObserverList);
+  }
+
+  void Add(SVGRenderingObserver* aObserver) {
+    mObservers.PutEntry(aObserver);
+  }
+  void Remove(SVGRenderingObserver* aObserver) {
+    mObservers.RemoveEntry(aObserver);
+  }
+#ifdef DEBUG
+  bool Contains(SVGRenderingObserver* aObserver) {
+    return (mObservers.GetEntry(aObserver) != nullptr);
+  }
+#endif
+  bool IsEmpty() {
+    return mObservers.IsEmpty();
+  }
+
+  /**
+   * Drop all our observers, and notify them that we have changed and dropped
+   * our reference to them.
+   */
+  void InvalidateAll();
+
+  /**
+   * Drop all observers that observe reflow, and notify them that we have changed and dropped
+   * our reference to them.
+   */
+  void InvalidateAllForReflow();
+
+  /**
+   * Drop all our observers, and notify them that we have dropped our reference
+   * to them.
+   */
+  void RemoveAll();
+
+private:
+  nsTHashtable<nsPtrHashKey<SVGRenderingObserver>> mObservers;
+};
+
+void
+SVGRenderingObserverList::InvalidateAll()
+{
+  if (mObservers.IsEmpty()) {
+    return;
+  }
+
+  AutoTArray<SVGRenderingObserver*,10> observers;
+
+  for (auto it = mObservers.Iter(); !it.Done(); it.Next()) {
+    observers.AppendElement(it.Get()->GetKey());
+  }
+  mObservers.Clear();
+
+  for (uint32_t i = 0; i < observers.Length(); ++i) {
+    observers[i]->OnNonDOMMutationRenderingChange();
+  }
 }
 
 void
-SVGTextPathObserver::OnRenderingChange()
+SVGRenderingObserverList::InvalidateAllForReflow()
 {
-  nsSVGRenderingObserverProperty::OnRenderingChange();
-
-  nsIFrame* frame = mFrameReference.Get();
-  if (!frame)
-    return;
-
-  NS_ASSERTION(frame->IsFrameOfType(nsIFrame::eSVG) ||
-               nsSVGUtils::IsInSVGTextSubtree(frame),
-               "SVG frame expected");
-
-  // Avoid getting into an infinite loop of reflows if the <textPath> is
-  // pointing to one of its ancestors.  TargetIsValid returns true iff
-  // the target element is a <path> element, and we would not have this
-  // SVGTextPathObserver if this <textPath> were a descendant of the
-  // target <path>.
-  //
-  // Note that we still have to post the restyle event when we
-  // change from being valid to invalid, so that mPositions on the
-  // SVGTextFrame gets updated, skipping the <textPath>, ensuring
-  // that nothing gets painted for that element.
-  bool nowValid = TargetIsValid();
-  if (!mValid && !nowValid) {
-    // Just return if we were previously invalid, and are still invalid.
+  if (mObservers.IsEmpty()) {
     return;
   }
-  mValid = nowValid;
+
+  AutoTArray<SVGRenderingObserver*,10> observers;
 
-  // Repaint asynchronously in case the path frame is being torn down
-  nsChangeHint changeHint =
-    nsChangeHint(nsChangeHint_RepaintFrame | nsChangeHint_UpdateTextPath);
-  frame->PresContext()->RestyleManager()->PostRestyleEvent(
-    frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint);
+  for (auto it = mObservers.Iter(); !it.Done(); it.Next()) {
+    SVGRenderingObserver* obs = it.Get()->GetKey();
+    if (obs->ObservesReflow()) {
+      observers.AppendElement(obs);
+      it.Remove();
+    }
+  }
+
+  for (uint32_t i = 0; i < observers.Length(); ++i) {
+    observers[i]->OnNonDOMMutationRenderingChange();
+  }
 }
 
 void
-nsSVGPaintingProperty::OnRenderingChange()
+SVGRenderingObserverList::RemoveAll()
 {
-  nsSVGRenderingObserverProperty::OnRenderingChange();
-
-  nsIFrame* frame = mFrameReference.Get();
-  if (!frame)
-    return;
+  AutoTArray<SVGRenderingObserver*,10> observers;
 
-  if (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
-    frame->InvalidateFrameSubtree();
-  } else {
-    for (nsIFrame* f = frame; f;
-         f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
-      f->InvalidateFrame();
-    }
+  for (auto it = mObservers.Iter(); !it.Done(); it.Next()) {
+    observers.AppendElement(it.Get()->GetKey());
+  }
+  mObservers.Clear();
+
+  // Our list is now cleared.  We need to notify the observers we've removed,
+  // so they can update their state & remove themselves as mutation-observers.
+  for (uint32_t i = 0; i < observers.Length(); ++i) {
+    observers[i]->NotifyEvictedFromRenderingObserverList();
   }
 }
 
+
+static SVGRenderingObserverList*
+GetObserverList(Element* aElement)
+{
+  return static_cast<SVGRenderingObserverList*>
+    (aElement->GetProperty(nsGkAtoms::renderingobserverlist));
+}
+
+#ifdef DEBUG
+// Defined down here because we need SVGRenderingObserverList's definition.
+void
+SVGRenderingObserver::DebugObserverSet()
+{
+  Element* referencedElement = GetReferencedElementWithoutObserving();
+  if (referencedElement) {
+    SVGRenderingObserverList* observerList = GetObserverList(referencedElement);
+    bool inObserverList = observerList && observerList->Contains(this);
+    MOZ_ASSERT(inObserverList == mInObserverList,
+      "failed to track whether we're in our referenced element's observer list!");
+  } else {
+    MOZ_ASSERT(!mInObserverList, "In whose observer list are we, then?");
+  }
+}
+#endif
+
+
+typedef nsInterfaceHashtable<nsRefPtrHashKey<URLAndReferrerInfo>,
+                             nsIMutationObserver> URIObserverHashtable;
+
+using PaintingPropertyDescriptor =
+  const mozilla::FramePropertyDescriptor<nsSVGPaintingProperty>*;
+
+void DestroyFilterProperty(SVGFilterObserverListForCSSProp* aProp)
+{
+  // SVGFilterObserverListForCSSProp is cycle-collected, so dropping the last
+  // reference doesn't necessarily destroy it. We need to tell it that the
+  // frame has now become invalid.
+  aProp->DetachFromFrame();
+
+  aProp->Release();
+}
+
+NS_DECLARE_FRAME_PROPERTY_RELEASABLE(HrefToTemplateProperty,
+                                     SVGTemplateElementObserver)
+NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(FilterProperty,
+                                    SVGFilterObserverListForCSSProp,
+                                    DestroyFilterProperty)
+NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MaskProperty, SVGMaskObserverList)
+NS_DECLARE_FRAME_PROPERTY_RELEASABLE(ClipPathProperty, nsSVGPaintingProperty)
+NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerStartProperty, SVGMarkerObserver)
+NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerMidProperty, SVGMarkerObserver)
+NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerEndProperty, SVGMarkerObserver)
+NS_DECLARE_FRAME_PROPERTY_RELEASABLE(FillProperty, nsSVGPaintingProperty)
+NS_DECLARE_FRAME_PROPERTY_RELEASABLE(StrokeProperty, nsSVGPaintingProperty)
+NS_DECLARE_FRAME_PROPERTY_RELEASABLE(HrefAsTextPathProperty,
+                                     SVGTextPathObserver)
+NS_DECLARE_FRAME_PROPERTY_DELETABLE(BackgroundImageProperty,
+                                    URIObserverHashtable)
+
 template<class T>
 static T*
 GetEffectProperty(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
   const mozilla::FramePropertyDescriptor<T>* aProperty)
 {
   if (!aURI)
     return nullptr;
 
@@ -606,16 +1100,23 @@ GetEffectProperty(URLAndReferrerInfo* aU
 
 static nsSVGPaintingProperty*
 GetPaintingProperty(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
   const mozilla::FramePropertyDescriptor<nsSVGPaintingProperty>* aProperty)
 {
   return GetEffectProperty(aURI, aFrame, aProperty);
 }
 
+
+static already_AddRefed<URLAndReferrerInfo>
+GetMarkerURI(nsIFrame* aFrame, RefPtr<css::URLValue> nsStyleSVG::* aMarker)
+{
+  return ResolveURLUsingLocalRef(aFrame, aFrame->StyleSVG()->*aMarker);
+}
+
 bool
 SVGObserverUtils::GetMarkerFrames(nsIFrame* aMarkedFrame,
                                   nsSVGMarkerFrame*(*aFrames)[3])
 {
   MOZ_ASSERT(!aMarkedFrame->GetPrevContinuation() &&
              aMarkedFrame->IsSVGGeometryFrame() &&
              static_cast<SVGGeometryElement*>(aMarkedFrame->GetContent())->IsMarkable(),
              "Bad frame");
@@ -623,17 +1124,17 @@ SVGObserverUtils::GetMarkerFrames(nsIFra
   bool foundMarker = false;
   RefPtr<URLAndReferrerInfo> markerURL;
   SVGMarkerObserver* observer;
   nsIFrame* marker;
 
 #define GET_MARKER(type)                                                      \
   markerURL = GetMarkerURI(aMarkedFrame, &nsStyleSVG::mMarker##type);         \
   observer = GetEffectProperty(markerURL, aMarkedFrame,                       \
-                               SVGObserverUtils::Marker##type##Property());   \
+                               Marker##type##Property());                     \
   marker = observer ?                                                         \
            observer->GetAndObserveReferencedFrame(LayoutFrameType::SVGMarker, \
                                                   nullptr) :                  \
            nullptr;                                                           \
   foundMarker = foundMarker || bool(marker);                                  \
   (*aFrames)[nsSVGMark::e##type] = static_cast<nsSVGMarkerFrame*>(marker);
 
   GET_MARKER(Start)
@@ -652,23 +1153,23 @@ GetOrCreateFilterObserverListForCSS(nsIF
 {
   MOZ_ASSERT(!aFrame->GetPrevContinuation(), "Require first continuation");
 
   const nsStyleEffects* effects = aFrame->StyleEffects();
   if (!effects->HasFilters()) {
     return nullptr;
   }
   SVGFilterObserverListForCSSProp* observers =
-    aFrame->GetProperty(SVGObserverUtils::FilterProperty());
+    aFrame->GetProperty(FilterProperty());
   if (observers) {
     return observers;
   }
   observers = new SVGFilterObserverListForCSSProp(effects->mFilters, aFrame);
   NS_ADDREF(observers);
-  aFrame->SetProperty(SVGObserverUtils::FilterProperty(), observers);
+  aFrame->SetProperty(FilterProperty(), observers);
   return observers;
 }
 
 static SVGObserverUtils::ReferenceState
 GetAndObserveFilters(SVGFilterObserverListForCSSProp* aObserverList,
                      nsTArray<nsSVGFilterFrame*>* aFilterFrames)
 {
   if (!aObserverList) {
@@ -706,17 +1207,17 @@ SVGObserverUtils::GetAndObserveFilters(n
   return ::GetAndObserveFilters(observerList, aFilterFrames);
 }
 
 SVGObserverUtils::ReferenceState
 SVGObserverUtils::GetFiltersIfObserving(nsIFrame* aFilteredFrame,
                                         nsTArray<nsSVGFilterFrame*>* aFilterFrames)
 {
   SVGFilterObserverListForCSSProp* observerList =
-    aFilteredFrame->GetProperty(SVGObserverUtils::FilterProperty());
+    aFilteredFrame->GetProperty(FilterProperty());
   return ::GetAndObserveFilters(observerList, aFilterFrames);
 }
 
 already_AddRefed<nsISupports>
 SVGObserverUtils::ObserveFiltersForCanvasContext(CanvasRenderingContext2D* aContext,
                                                  Element* aCanvasElement,
                                                  nsTArray<nsStyleFilter>& aFilters)
 {
@@ -739,18 +1240,17 @@ GetOrCreateClipPathObserver(nsIFrame* aC
   MOZ_ASSERT(!aClippedFrame->GetPrevContinuation(), "Require first continuation");
 
   const nsStyleSVGReset* svgStyleReset = aClippedFrame->StyleSVGReset();
   if (svgStyleReset->mClipPath.GetType() != StyleShapeSourceType::URL) {
     return nullptr;
   }
   css::URLValue* url = svgStyleReset->mClipPath.GetURL();
   RefPtr<URLAndReferrerInfo> pathURI = ResolveURLUsingLocalRef(aClippedFrame, url);
-  return GetPaintingProperty(pathURI, aClippedFrame,
-                             SVGObserverUtils::ClipPathProperty());
+  return GetPaintingProperty(pathURI, aClippedFrame, ClipPathProperty());
 }
 
 SVGObserverUtils::ReferenceState
 SVGObserverUtils::GetAndObserveClipPath(nsIFrame* aClippedFrame,
                                         nsSVGClipPathFrame** aClipPathFrame)
 {
   if (aClipPathFrame) {
     *aClipPathFrame = nullptr;
@@ -782,23 +1282,23 @@ GetOrCreateMaskObserverList(nsIFrame* aM
   const nsStyleSVGReset* style = aMaskedFrame->StyleSVGReset();
   if (!style->HasMask()) {
     return nullptr;
   }
 
   MOZ_ASSERT(style->mMask.mImageCount > 0);
 
   SVGMaskObserverList* prop =
-    aMaskedFrame->GetProperty(SVGObserverUtils::MaskProperty());
+    aMaskedFrame->GetProperty(MaskProperty());
   if (prop) {
     return prop;
   }
   prop = new SVGMaskObserverList(aMaskedFrame);
   NS_ADDREF(prop);
-  aMaskedFrame->SetProperty(SVGObserverUtils::MaskProperty(), prop);
+  aMaskedFrame->SetProperty(MaskProperty(), prop);
   return prop;
 }
 
 SVGObserverUtils::ReferenceState
 SVGObserverUtils::GetAndObserveMasks(nsIFrame* aMaskedFrame,
                                      nsTArray<nsSVGMaskFrame*>* aMaskFrames)
 {
   SVGMaskObserverList* observerList = GetOrCreateMaskObserverList(aMaskedFrame);
@@ -838,17 +1338,17 @@ SVGObserverUtils::GetAndObserveMasks(nsI
 
   return state;
 }
 
 SVGGeometryElement*
 SVGObserverUtils::GetTextPathsReferencedPath(nsIFrame* aTextPathFrame)
 {
   SVGTextPathObserver* property =
-    aTextPathFrame->GetProperty(SVGObserverUtils::HrefAsTextPathProperty());
+    aTextPathFrame->GetProperty(HrefAsTextPathProperty());
 
   if (!property) {
     nsIContent* content = aTextPathFrame->GetContent();
     nsAutoString href;
     static_cast<SVGTextPathElement*>(content)->HrefAsString(href);
     if (href.IsEmpty()) {
       return nullptr; // no URL
     }
@@ -893,17 +1393,17 @@ SVGObserverUtils::RemoveTextPathObserver
   aTextPathFrame->DeleteProperty(HrefAsTextPathProperty());
 }
 
 nsIFrame*
 SVGObserverUtils::GetTemplateFrame(nsIFrame* aFrame,
                                    HrefToTemplateCallback aGetHref)
 {
   SVGTemplateElementObserver* observer =
-    aFrame->GetProperty(SVGObserverUtils::HrefToTemplateProperty());
+    aFrame->GetProperty(HrefToTemplateProperty());
 
   if (!observer) {
     nsAutoString href;
     aGetHref(href);
     if (href.IsEmpty()) {
       return nullptr; // no URL
     }
 
@@ -916,37 +1416,36 @@ SVGObserverUtils::GetTemplateFrame(nsIFr
 
     // There's no clear refererer policy spec about non-CSS SVG resource
     // references.  Bug 1415044 to investigate which referrer we should use.
     RefPtr<URLAndReferrerInfo> target =
       new URLAndReferrerInfo(targetURI,
                              content->OwnerDoc()->GetDocumentURI(),
                              content->OwnerDoc()->GetReferrerPolicy());
 
-    observer = GetEffectProperty(target, aFrame,
-                                 SVGObserverUtils::HrefToTemplateProperty());
+    observer = GetEffectProperty(target, aFrame, HrefToTemplateProperty());
   }
 
   return observer ? observer->GetAndObserveReferencedFrame() : nullptr;
 }
 
 void
 SVGObserverUtils::RemoveTemplateObserver(nsIFrame* aFrame)
 {
-  aFrame->DeleteProperty(SVGObserverUtils::HrefToTemplateProperty());
+  aFrame->DeleteProperty(HrefToTemplateProperty());
 }
 
 Element*
 SVGObserverUtils::GetAndObserveBackgroundImage(nsIFrame* aFrame,
                                                const nsAtom* aHref)
 {
-  SVGObserverUtils::URIObserverHashtable *hashtable =
+  URIObserverHashtable *hashtable =
     aFrame->GetProperty(BackgroundImageProperty());
   if (!hashtable) {
-    hashtable = new SVGObserverUtils::URIObserverHashtable();
+    hashtable = new URIObserverHashtable();
     aFrame->SetProperty(BackgroundImageProperty(), hashtable);
   }
 
   nsAutoString elementId =
     NS_LITERAL_STRING("#") + nsDependentAtomString(aHref);
   nsCOMPtr<nsIURI> targetURI;
   nsCOMPtr<nsIURI> base = aFrame->GetContent()->GetBaseURI();
   nsContentUtils::NewURIWithDocumentCharset(
@@ -992,18 +1491,17 @@ SVGObserverUtils::GetPaintServer(nsIFram
     return nullptr;
 
   RefPtr<URLAndReferrerInfo> paintServerURL =
     ResolveURLUsingLocalRef(frame,
                             (svgStyle->*aPaint).GetPaintServer());
 
   MOZ_ASSERT(aPaint == &nsStyleSVG::mFill || aPaint == &nsStyleSVG::mStroke);
   PaintingPropertyDescriptor propDesc = (aPaint == &nsStyleSVG::mFill) ?
-                                        SVGObserverUtils::FillProperty() :
-                                        SVGObserverUtils::StrokeProperty();
+                                        FillProperty() : StrokeProperty();
   nsSVGPaintingProperty *property =
     GetPaintingProperty(paintServerURL, frame, propDesc);
   if (!property)
     return nullptr;
   nsIFrame* result = property->GetAndObserveReferencedFrame();
   if (!result)
     return nullptr;
 
@@ -1045,83 +1543,16 @@ SVGObserverUtils::UpdateEffects(nsIFrame
     GetEffectProperty(markerURL, aFrame, MarkerStartProperty());
     markerURL = GetMarkerURI(aFrame, &nsStyleSVG::mMarkerMid);
     GetEffectProperty(markerURL, aFrame, MarkerMidProperty());
     markerURL = GetMarkerURI(aFrame, &nsStyleSVG::mMarkerEnd);
     GetEffectProperty(markerURL, aFrame, MarkerEndProperty());
   }
 }
 
-SVGFilterObserverListForCSSProp*
-SVGObserverUtils::GetFilterObserverList(nsIFrame* aFrame)
-{
-  NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
-
-  if (!aFrame->StyleEffects()->HasFilters())
-    return nullptr;
-
-  return aFrame->GetProperty(FilterProperty());
-}
-
-void
-SVGRenderingObserverList::InvalidateAll()
-{
-  if (mObservers.Count() == 0)
-    return;
-
-  AutoTArray<SVGRenderingObserver*,10> observers;
-
-  for (auto it = mObservers.Iter(); !it.Done(); it.Next()) {
-    observers.AppendElement(it.Get()->GetKey());
-  }
-  mObservers.Clear();
-
-  for (uint32_t i = 0; i < observers.Length(); ++i) {
-    observers[i]->OnNonDOMMutationRenderingChange();
-  }
-}
-
-void
-SVGRenderingObserverList::InvalidateAllForReflow()
-{
-  if (mObservers.Count() == 0)
-    return;
-
-  AutoTArray<SVGRenderingObserver*,10> observers;
-
-  for (auto it = mObservers.Iter(); !it.Done(); it.Next()) {
-    SVGRenderingObserver* obs = it.Get()->GetKey();
-    if (obs->ObservesReflow()) {
-      observers.AppendElement(obs);
-      it.Remove();
-    }
-  }
-
-  for (uint32_t i = 0; i < observers.Length(); ++i) {
-    observers[i]->OnNonDOMMutationRenderingChange();
-  }
-}
-
-void
-SVGRenderingObserverList::RemoveAll()
-{
-  AutoTArray<SVGRenderingObserver*,10> observers;
-
-  for (auto it = mObservers.Iter(); !it.Done(); it.Next()) {
-    observers.AppendElement(it.Get()->GetKey());
-  }
-  mObservers.Clear();
-
-  // Our list is now cleared.  We need to notify the observers we've removed,
-  // so they can update their state & remove themselves as mutation-observers.
-  for (uint32_t i = 0; i < observers.Length(); ++i) {
-    observers[i]->NotifyEvictedFromRenderingObserverList();
-  }
-}
-
 void
 SVGObserverUtils::AddRenderingObserver(Element* aElement,
                                        SVGRenderingObserver* aObserver)
 {
   SVGRenderingObserverList* observerList = GetObserverList(aElement);
   if (!observerList) {
     observerList = new SVGRenderingObserverList();
     if (!observerList)
@@ -1263,33 +1694,16 @@ SVGObserverUtils::GetBaseURLForLocalRef(
       return originalURI.forget();
     }
   }
 
   return baseURI.forget();
 }
 
 already_AddRefed<URLAndReferrerInfo>
-SVGObserverUtils::GetMarkerURI(nsIFrame* aFrame,
-                               RefPtr<css::URLValue> nsStyleSVG::* aMarker)
-{
-  return ResolveURLUsingLocalRef(aFrame, aFrame->StyleSVG()->*aMarker);
-}
-
-already_AddRefed<URLAndReferrerInfo>
-SVGObserverUtils::GetFilterURI(nsIFrame* aFrame, uint32_t aIndex)
-{
-  const nsStyleEffects* effects = aFrame->StyleEffects();
-  MOZ_ASSERT(effects->mFilters.Length() > aIndex);
-  MOZ_ASSERT(effects->mFilters[aIndex].GetType() == NS_STYLE_FILTER_URL);
-
-  return ResolveURLUsingLocalRef(aFrame, effects->mFilters[aIndex].GetURL());
-}
-
-already_AddRefed<URLAndReferrerInfo>
 SVGObserverUtils::GetFilterURI(nsIFrame* aFrame, const nsStyleFilter& aFilter)
 {
   MOZ_ASSERT(aFrame->StyleEffects()->mFilters.Length());
   MOZ_ASSERT(aFilter.GetType() == NS_STYLE_FILTER_URL);
 
   return ResolveURLUsingLocalRef(aFrame, aFilter.GetURL());
 }
 
--- a/layout/svg/SVGObserverUtils.h
+++ b/layout/svg/SVGObserverUtils.h
@@ -21,25 +21,23 @@
 #include "nsStringFwd.h"
 #include "nsStubMutationObserver.h"
 #include "nsSVGUtils.h"
 #include "nsTHashtable.h"
 #include "nsURIHashKey.h"
 #include "nsCycleCollectionParticipant.h"
 
 class nsAtom;
-class nsIPresShell;
 class nsIURI;
 class nsSVGClipPathFrame;
 class nsSVGMarkerFrame;
 class nsSVGPaintServerFrame;
 class nsSVGFilterFrame;
 class nsSVGMaskFrame;
 namespace mozilla {
-class SVGFilterObserverList;
 namespace dom {
 class CanvasRenderingContext2D;
 class SVGGeometryElement;
 }
 }
 
 namespace mozilla {
 
@@ -156,435 +154,32 @@ protected:
    *
    * Subclasses should override this method to handle rendering changes
    * appropriately.
    */
   virtual void OnRenderingChange() = 0;
 
   virtual Element* GetReferencedElementWithoutObserving() = 0;
 
+#ifdef DEBUG
+  void DebugObserverSet();
+#endif
+
   // Whether we're in our referenced element's observer list at this time.
   bool mInObserverList;
 };
 
 
-/*
- * SVG elements reference supporting resources by element ID. We need to
- * track when those resources change and when the document changes in ways
- * that affect which element is referenced by a given ID (e.g., when
- * element IDs change). The code here is responsible for that.
- *
- * When a frame references a supporting resource, we create a property
- * object derived from SVGIDRenderingObserver to manage the relationship. The
- * property object is attached to the referencing frame.
- */
-class SVGIDRenderingObserver : public SVGRenderingObserver
-{
-public:
-  typedef mozilla::dom::Element Element;
-  typedef mozilla::dom::IDTracker IDTracker;
-
-  SVGIDRenderingObserver(URLAndReferrerInfo* aURI, nsIContent* aObservingContent,
-                         bool aReferenceImage);
-  virtual ~SVGIDRenderingObserver();
-
-protected:
-  Element* GetReferencedElementWithoutObserving() override {
-    return mObservedElementTracker.get();
-  }
-
-  void OnRenderingChange() override;
-
-  /**
-   * Helper that provides a reference to the element with the ID that our
-   * observer wants to observe, and that will invalidate our observer if the
-   * element that that ID identifies changes to a different element (or none).
-   */
-  class ElementTracker final : public IDTracker
-  {
-  public:
-    explicit ElementTracker(SVGIDRenderingObserver* aOwningObserver)
-      : mOwningObserver(aOwningObserver)
-    {}
-  protected:
-    virtual void ElementChanged(Element* aFrom, Element* aTo) override {
-      mOwningObserver->StopObserving(); // stop observing the old element
-      IDTracker::ElementChanged(aFrom, aTo);
-      mOwningObserver->StartObserving(); // start observing the new element
-      mOwningObserver->OnRenderingChange();
-    }
-    /**
-     * Override IsPersistent because we want to keep tracking the element
-     * for the ID even when it changes.
-     */
-    virtual bool IsPersistent() override { return true; }
-  private:
-    SVGIDRenderingObserver* mOwningObserver;
-  };
-
-  ElementTracker mObservedElementTracker;
-};
-
-struct nsSVGFrameReferenceFromProperty
-{
-  explicit nsSVGFrameReferenceFromProperty(nsIFrame* aFrame)
-    : mFrame(aFrame)
-    , mFramePresShell(aFrame->PresShell())
-  {}
-
-  // Clear our reference to the frame.
-  void Detach();
-
-  // null if the frame has become invalid
-  nsIFrame* Get();
-
-private:
-  // The frame that this property is attached to, may be null
-  nsIFrame *mFrame;
-  // When a presshell is torn down, we don't delete the properties for
-  // each frame until after the frames are destroyed. So here we remember
-  // the presshell for the frames we care about and, before we use the frame,
-  // we test the presshell to see if it's destroying itself. If it is,
-  // then the frame pointer is not valid and we know the frame has gone away.
-  // mFramePresShell may be null, but when mFrame is non-null, mFramePresShell
-  // is guaranteed to be non-null, too.
-  nsIPresShell *mFramePresShell;
-};
-
-/**
- * Used for gradient-to-gradient, pattern-to-pattern and filter-to-filter
- * references to "template" elements (specified via the 'href' attributes).
- *
- * This is a special class for the case where we know we only want to call
- * InvalidateDirectRenderingObservers (as opposed to
- * InvalidateRenderingObservers).
- *
- * TODO(jwatt): If we added a new NS_FRAME_RENDERING_OBSERVER_CONTAINER state
- * bit to clipPath, filter, gradients, marker, mask, pattern and symbol, and
- * could have InvalidateRenderingObservers stop on reaching such an element,
- * then we would no longer need this class (not to mention improving perf by
- * significantly cutting down on ancestor traversal).
- */
-class SVGTemplateElementObserver : public SVGIDRenderingObserver
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  SVGTemplateElementObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
-                             bool aReferenceImage)
-    : SVGIDRenderingObserver(aURI, aFrame->GetContent(), aReferenceImage)
-    , mFrameReference(aFrame)
-  {}
-
-protected:
-  virtual ~SVGTemplateElementObserver() = default; // non-public
-
-  virtual void OnRenderingChange() override;
-
-  nsSVGFrameReferenceFromProperty mFrameReference;
-};
-
-class nsSVGRenderingObserverProperty : public SVGIDRenderingObserver
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  nsSVGRenderingObserverProperty(URLAndReferrerInfo* aURI, nsIFrame *aFrame,
-                                 bool aReferenceImage)
-    : SVGIDRenderingObserver(aURI, aFrame->GetContent(), aReferenceImage)
-    , mFrameReference(aFrame)
-  {}
-
-protected:
-  virtual ~nsSVGRenderingObserverProperty() {}
-
-  virtual void OnRenderingChange() override;
-
-  nsSVGFrameReferenceFromProperty mFrameReference;
-};
-
-/**
- * In a filter chain, there can be multiple SVG reference filters.
- * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2);
- *
- * This class keeps track of one SVG reference filter in a filter chain.
- * e.g. url(#svg-filter-1)
- *
- * It fires invalidations when the SVG filter element's id changes or when
- * the SVG filter element's content changes.
- *
- * The SVGFilterObserverList class manages a list of SVGFilterObservers.
- */
-class SVGFilterObserver final : public SVGIDRenderingObserver
-{
-public:
-  SVGFilterObserver(URLAndReferrerInfo* aURI,
-                    nsIContent* aObservingContent,
-                    SVGFilterObserverList* aFilterChainObserver)
-    : SVGIDRenderingObserver(aURI, aObservingContent, false)
-    , mFilterObserverList(aFilterChainObserver)
-  {
-  }
-
-  // XXXjwatt: This will return false if the reference is to a filter in an
-  // external resource document that hasn't loaded yet!
-  bool ReferencesValidResource() { return GetAndObserveFilterFrame(); }
-
-  void DetachFromChainObserver() { mFilterObserverList = nullptr; }
-
-  /**
-   * @return the filter frame, or null if there is no filter frame
-   */
-  nsSVGFilterFrame* GetAndObserveFilterFrame();
-
-  // nsISupports
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(SVGFilterObserver)
-
-  void Invalidate() { OnRenderingChange(); };
-
-protected:
-  virtual ~SVGFilterObserver() {}
-
-  // SVGIDRenderingObserver
-  virtual void OnRenderingChange() override;
-
-private:
-  SVGFilterObserverList* mFilterObserverList;
-};
-
-/**
- * This class manages a list of SVGFilterObservers, which correspond to
- * reference to SVG filters in a list of filters in a given 'filter' property.
- * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2);
- *
- * In the above example, the SVGFilterObserverList will manage two
- * SVGFilterObservers, one for each of the references to SVG filters.  CSS
- * filters like "blur(10px)" don't reference filter elements, so they don't
- * need an SVGFilterObserver.  The style system invalidates changes to CSS
- * filters.
- */
-class SVGFilterObserverList : public nsISupports
-{
-public:
-  SVGFilterObserverList(const nsTArray<nsStyleFilter>& aFilters,
-                        nsIContent* aFilteredElement,
-                        nsIFrame* aFiltedFrame = nullptr);
-
-  bool ReferencesValidResources();
-  void Invalidate() { OnRenderingChange(); }
-
-  const nsTArray<RefPtr<SVGFilterObserver>>& GetObservers() const {
-    return mObservers;
-  }
-
-  // nsISupports
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(SVGFilterObserverList)
-
-protected:
-  virtual ~SVGFilterObserverList();
-
-  virtual void OnRenderingChange() = 0;
-
-private:
-
-  void DetachObservers()
-  {
-    for (uint32_t i = 0; i < mObservers.Length(); i++) {
-      mObservers[i]->DetachFromChainObserver();
-    }
-  }
-
-  nsTArray<RefPtr<SVGFilterObserver>> mObservers;
-};
-
-class SVGFilterObserverListForCSSProp final : public SVGFilterObserverList
-{
-public:
-  SVGFilterObserverListForCSSProp(const nsTArray<nsStyleFilter>& aFilters,
-                                  nsIFrame* aFilteredFrame)
-    : SVGFilterObserverList(aFilters, aFilteredFrame->GetContent(),
-                            aFilteredFrame)
-    , mFrameReference(aFilteredFrame)
-  {}
-
-  void DetachFromFrame() { mFrameReference.Detach(); }
-
-protected:
-  virtual void OnRenderingChange() override;
-
-  nsSVGFrameReferenceFromProperty mFrameReference;
-};
-
-class SVGMarkerObserver final: public nsSVGRenderingObserverProperty
-{
-public:
-  SVGMarkerObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame, bool aReferenceImage)
-    : nsSVGRenderingObserverProperty(aURI, aFrame, aReferenceImage) {}
-
-protected:
-  virtual void OnRenderingChange() override;
-};
-
-class SVGTextPathObserver final : public nsSVGRenderingObserverProperty
-{
-public:
-  SVGTextPathObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame, bool aReferenceImage)
-    : nsSVGRenderingObserverProperty(aURI, aFrame, aReferenceImage)
-    , mValid(true) {}
-
-  virtual bool ObservesReflow() override { return false; }
-
-protected:
-  virtual void OnRenderingChange() override;
-
-private:
-  /**
-   * Returns true if the target of the textPath is the frame of a 'path' element.
-   */
-  bool TargetIsValid();
-
-  bool mValid;
-};
-
-class nsSVGPaintingProperty final : public nsSVGRenderingObserverProperty
-{
-public:
-  nsSVGPaintingProperty(URLAndReferrerInfo* aURI, nsIFrame* aFrame, bool aReferenceImage)
-    : nsSVGRenderingObserverProperty(aURI, aFrame, aReferenceImage) {}
-
-protected:
-  virtual void OnRenderingChange() override;
-};
-
-class SVGMaskObserverList final : public nsISupports
-{
-public:
-  explicit SVGMaskObserverList(nsIFrame* aFrame);
-
-  // nsISupports
-  NS_DECL_ISUPPORTS
-
-  const nsTArray<RefPtr<nsSVGPaintingProperty>>& GetObservers() const
-  {
-    return mProperties;
-  }
-
-  void ResolveImage(uint32_t aIndex);
-
-private:
-  virtual ~SVGMaskObserverList() {}
-  nsTArray<RefPtr<nsSVGPaintingProperty>> mProperties;
-  nsIFrame* mFrame;
-};
-
-/**
- * A manager for one-shot SVGRenderingObserver tracking.
- * nsSVGRenderingObservers can be added or removed. They are not strongly
- * referenced so an observer must be removed before it dies.
- * When InvalidateAll is called, all outstanding references get
- * OnNonDOMMutationRenderingChange()
- * called on them and the list is cleared. The intent is that
- * the observer will force repainting of whatever part of the document
- * is needed, and then at paint time the observer will do a clean lookup
- * of the referenced element and [re-]add itself to the element's observer list.
- *
- * InvalidateAll must be called before this object is destroyed, i.e.
- * before the referenced frame is destroyed. This should normally happen
- * via nsSVGContainerFrame::RemoveFrame, since only frames in the frame
- * tree should be referenced.
- */
-class SVGRenderingObserverList
-{
-public:
-  SVGRenderingObserverList()
-    : mObservers(4)
-  {
-    MOZ_COUNT_CTOR(SVGRenderingObserverList);
-  }
-
-  ~SVGRenderingObserverList() {
-    InvalidateAll();
-    MOZ_COUNT_DTOR(SVGRenderingObserverList);
-  }
-
-  void Add(SVGRenderingObserver* aObserver)
-  { mObservers.PutEntry(aObserver); }
-  void Remove(SVGRenderingObserver* aObserver)
-  { mObservers.RemoveEntry(aObserver); }
-#ifdef DEBUG
-  bool Contains(SVGRenderingObserver* aObserver)
-  { return (mObservers.GetEntry(aObserver) != nullptr); }
-#endif
-  bool IsEmpty()
-  { return mObservers.Count() == 0; }
-
-  /**
-   * Drop all our observers, and notify them that we have changed and dropped
-   * our reference to them.
-   */
-  void InvalidateAll();
-
-  /**
-   * Drop all observers that observe reflow, and notify them that we have changed and dropped
-   * our reference to them.
-   */
-  void InvalidateAllForReflow();
-
-  /**
-   * Drop all our observers, and notify them that we have dropped our reference
-   * to them.
-   */
-  void RemoveAll();
-
-private:
-  nsTHashtable<nsPtrHashKey<SVGRenderingObserver>> mObservers;
-};
-
 class SVGObserverUtils
 {
 public:
   typedef mozilla::dom::CanvasRenderingContext2D CanvasRenderingContext2D;
   typedef mozilla::dom::Element Element;
   typedef dom::SVGGeometryElement SVGGeometryElement;
-  typedef nsInterfaceHashtable<nsRefPtrHashKey<URLAndReferrerInfo>,
-    nsIMutationObserver> URIObserverHashtable;
-
-  using PaintingPropertyDescriptor =
-    const mozilla::FramePropertyDescriptor<nsSVGPaintingProperty>*;
-  using URIObserverHashtablePropertyDescriptor =
-    const mozilla::FramePropertyDescriptor<URIObserverHashtable>*;
-
-  static void DestroyFilterProperty(SVGFilterObserverListForCSSProp* aProp)
-  {
-    // SVGFilterObserverListForCSSProp is cycle-collected, so dropping the last
-    // reference doesn't necessarily destroy it. We need to tell it that the
-    // frame has now become invalid.
-    aProp->DetachFromFrame();
-
-    aProp->Release();
-  }
-
-  NS_DECLARE_FRAME_PROPERTY_RELEASABLE(HrefToTemplateProperty,
-                                       SVGTemplateElementObserver)
-  NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(FilterProperty,
-                                      SVGFilterObserverListForCSSProp,
-                                      DestroyFilterProperty)
-  NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MaskProperty, SVGMaskObserverList)
-  NS_DECLARE_FRAME_PROPERTY_RELEASABLE(ClipPathProperty, nsSVGPaintingProperty)
-  NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerStartProperty, SVGMarkerObserver)
-  NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerMidProperty, SVGMarkerObserver)
-  NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerEndProperty, SVGMarkerObserver)
-  NS_DECLARE_FRAME_PROPERTY_RELEASABLE(FillProperty, nsSVGPaintingProperty)
-  NS_DECLARE_FRAME_PROPERTY_RELEASABLE(StrokeProperty, nsSVGPaintingProperty)
-  NS_DECLARE_FRAME_PROPERTY_RELEASABLE(HrefAsTextPathProperty,
-                                       SVGTextPathObserver)
-  NS_DECLARE_FRAME_PROPERTY_DELETABLE(BackgroundImageProperty,
-                                      URIObserverHashtable)
+  using HrefToTemplateCallback = const std::function<void(nsAString&)>&;
 
   /**
    * Ensures that that if the given frame requires any resources that are in
    * SVG resource documents that the loading of those documents is initiated.
    * This does not make aFrame start to observe any elements that it
    * references.
    */
   static void InitiateResourceDocLoads(nsIFrame* aFrame);
@@ -600,21 +195,16 @@ public:
    * frame properties (XXX why not the other properties?), which makes it
    * useful for initializing those properties during first reflow.
    *
    * XXX rename to something more meaningful like RefreshResourceReferences?
    */
   static void UpdateEffects(nsIFrame* aFrame);
 
   /**
-   * @param aFrame should be the first continuation
-   */
-  static SVGFilterObserverListForCSSProp* GetFilterObserverList(nsIFrame* aFrame);
-
-  /**
    * @param aFrame must be a first-continuation.
    */
   static void AddRenderingObserver(Element* aElement,
                                    SVGRenderingObserver *aObserver);
   /**
    * @param aFrame must be a first-continuation.
    */
   static void RemoveRenderingObserver(Element* aElement,
@@ -780,17 +370,16 @@ public:
 
   /**
    * Make aTextPathFrame stop observing rendering changes to the
    * SVGGeometryElement that it references, if any.
    */
   static void
   RemoveTextPathObserver(nsIFrame* aTextPathFrame);
 
-  using HrefToTemplateCallback = const std::function<void(nsAString&)>&;
   /**
    * Gets the nsIFrame of a referenced SVG "template" element, if any, and
    * makes aFrame start observing rendering changes to the template element.
    *
    * Template elements: some elements like gradients, pattern or filter can
    * reference another element of the same type using their 'href' attribute,
    * and use that element as a template that provides attributes or content
    * that is missing from the referring element.
@@ -805,29 +394,16 @@ public:
   static void
   RemoveTemplateObserver(nsIFrame* aFrame);
 
   static Element*
   GetAndObserveBackgroundImage(nsIFrame* aFrame,
                                const nsAtom* aHref);
 
   /**
-   * A helper function to resolve marker's URL.
-   */
-  static already_AddRefed<URLAndReferrerInfo>
-  GetMarkerURI(nsIFrame* aFrame,
-               RefPtr<mozilla::css::URLValue> nsStyleSVG::* aMarker);
-
-  /**
-   * A helper function to resolve filter URL.
-   */
-  static already_AddRefed<URLAndReferrerInfo>
-  GetFilterURI(nsIFrame* aFrame, uint32_t aIndex);
-
-  /**
    * A helper function to resolve filter URL.
    */
   static already_AddRefed<URLAndReferrerInfo>
   GetFilterURI(nsIFrame* aFrame, const nsStyleFilter& aFilter);
 
   /**
    * Return a baseURL for resolving a local-ref URL.
    *