Bug 610796: In SVG-as-an-image helper-documents, clear rendering observer lists at xpcom shutdown time. r=roc a=blocking-final+
authorDaniel Holbert <dholbert@cs.stanford.edu>
Tue, 16 Nov 2010 10:35:59 -0800
changeset 57609 1a816f05746db5c15869f80a041b2ed06f41c11f
parent 57608 a5401aa629bae5a3de08b409e83b22bac5a4ef20
child 57610 36905ac2cef2238597885079a25dbd5d05f810a8
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersroc, blocking-final
bugs610796
milestone2.0b8pre
Bug 610796: In SVG-as-an-image helper-documents, clear rendering observer lists at xpcom shutdown time. r=roc a=blocking-final+
layout/svg/base/src/nsSVGEffects.cpp
layout/svg/base/src/nsSVGEffects.h
modules/libpr0n/src/SVGDocumentWrapper.cpp
--- a/layout/svg/base/src/nsSVGEffects.cpp
+++ b/layout/svg/base/src/nsSVGEffects.cpp
@@ -188,16 +188,23 @@ nsSVGIDRenderingObserver::DoUpdate()
 void
 nsSVGRenderingObserver::InvalidateViaReferencedElement()
 {
   mInObserverList = PR_FALSE;
   DoUpdate();
 }
 
 void
+nsSVGRenderingObserver::NotifyEvictedFromRenderingObserverList()
+{
+  mInObserverList = PR_FALSE; // We've been removed from rendering-obs. list.
+  StopListening();            // Remove ourselves from mutation-obs. list.
+}
+
+void
 nsSVGRenderingObserver::AttributeChanged(nsIDocument* aDocument,
                                          dom::Element* aElement,
                                          PRInt32 aNameSpaceID,
                                          nsIAtom* aAttribute,
                                          PRInt32 aModType)
 {
   // An attribute belonging to the element that we are observing *or one of its
   // descendants* has changed.
@@ -537,16 +544,31 @@ nsSVGRenderingObserverList::InvalidateAl
   // The PL_DHASH_REMOVE in GatherEnumerator drops all our observers here:
   mObservers.EnumerateEntries(GatherEnumerator, &observers);
 
   for (PRUint32 i = 0; i < observers.Length(); ++i) {
     observers[i]->InvalidateViaReferencedElement();
   }
 }
 
+void
+nsSVGRenderingObserverList::RemoveAll()
+{
+  nsAutoTArray<nsSVGRenderingObserver*,10> observers;
+
+  // The PL_DHASH_REMOVE in GatherEnumerator drops all our observers here:
+  mObservers.EnumerateEntries(GatherEnumerator, &observers);
+
+  // 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 (PRUint32 i = 0; i < observers.Length(); ++i) {
+    observers[i]->NotifyEvictedFromRenderingObserverList();
+  }
+}
+
 static void
 DestroyObservers(void *aObject, nsIAtom *aPropertyName,
                  void *aPropertyValue, void *aData)
 {
   delete static_cast<nsSVGRenderingObserverList*>(aPropertyValue);
 }
 
 void
@@ -573,16 +595,26 @@ nsSVGEffects::RemoveRenderingObserver(El
     observerList->Remove(aObserver);
     if (observerList->IsEmpty()) {
       aElement->SetHasRenderingObservers(false);
     }
   }
 }
 
 void
+nsSVGEffects::RemoveAllRenderingObservers(Element *aElement)
+{
+  nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
+  if (observerList) {
+    observerList->RemoveAll();
+    aElement->SetHasRenderingObservers(false);
+  }
+}
+
+void
 nsSVGEffects::InvalidateRenderingObservers(nsIFrame *aFrame)
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation");
 
   if (!aFrame->GetContent()->IsElement())
     return;
 
   nsSVGRenderingObserverList *observerList =
--- a/layout/svg/base/src/nsSVGEffects.h
+++ b/layout/svg/base/src/nsSVGEffects.h
@@ -75,16 +75,22 @@ public:
 
   // nsIMutationObserver
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   void InvalidateViaReferencedElement();
+
+  // When a nsSVGRenderingObserver list gets forcibly cleared, it uses this
+  // callback to notify every observer that's cleared from it, so they can
+  // react.
+  void NotifyEvictedFromRenderingObserverList();
+
   nsIFrame* GetReferencedFrame();
   PRBool IsInObserverList() const { return mInObserverList; }
 
   /**
    * @param aOK this is only for the convenience of callers. We set *aOK to false
    * if this function returns null.
    */
   nsIFrame* GetReferencedFrame(nsIAtom* aFrameType, PRBool* aOK);
@@ -251,16 +257,22 @@ public:
   { 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 our observers, and notify them that we have dropped our reference
+   * to them.
+   */
+  void RemoveAll();
+
 private:
   nsTHashtable<nsVoidPtrHashKey> mObservers;
 };
 
 class nsSVGEffects {
 public:
   typedef mozilla::dom::Element Element;
   typedef mozilla::FramePropertyDescriptor FramePropertyDescriptor;
@@ -345,16 +357,22 @@ public:
   /**
    * @param aFrame must be a first-continuation.
    */
   static void AddRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver);
   /**
    * @param aFrame must be a first-continuation.
    */
   static void RemoveRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver);
+
+  /**
+   * Removes all rendering observers from aElement.
+   */
+  static void RemoveAllRenderingObservers(Element *aElement);
+
   /**
    * This can be called on any frame. We invalidate the observers of aFrame's
    * element, if any, or else walk up to the nearest observable SVG parent
    * frame with observers and invalidate them instead.
    *
    * Note that this method is very different to e.g.
    * nsNodeUtils::AttributeChanged which walks up the content node tree all the
    * way to the root node (not stopping if it encounters a non-container SVG
--- a/modules/libpr0n/src/SVGDocumentWrapper.cpp
+++ b/modules/libpr0n/src/SVGDocumentWrapper.cpp
@@ -55,16 +55,17 @@
 #include "nsIXMLContentSink.h"
 #include "nsNetCID.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsSize.h"
 #include "gfxRect.h"
 #include "nsSVGSVGElement.h"
 #include "nsSVGLength2.h"
+#include "nsSVGEffects.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 namespace imagelib {
 
 nsIAtom* SVGDocumentWrapper::kSVGAtom = nsnull; // lazily initialized
 
@@ -293,16 +294,22 @@ SVGDocumentWrapper::OnStopRequest(nsIReq
 
 /** nsIObserver Methods **/
 NS_IMETHODIMP
 SVGDocumentWrapper::Observe(nsISupports* aSubject,
                             const char* aTopic,
                             const PRUnichar *aData)
 {
   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+    // Sever ties from rendering observers to helper-doc's root SVG node
+    nsSVGSVGElement* svgElem = GetRootSVGElem();
+    if (svgElem) {
+      nsSVGEffects::RemoveAllRenderingObservers(svgElem);
+    }
+
     // Clean up at XPCOM shutdown time.
     DestroyViewer();
     if (mListener)
       mListener = nsnull;
     if (mLoadGroup)
       mLoadGroup = nsnull;
 
     // Turn off "registered" flag, or else we'll try to unregister when we die.