Bug 276431 Patch 14: Add SVGRootRenderingObserver helper-class, to catch invalidations in helper-document. r=roc a=blocking
authorDaniel Holbert <dholbert@cs.stanford.edu>
Wed, 08 Sep 2010 13:40:40 -0700
changeset 52222 77a3ee888b8fdec271060e4af7b996ceaa5708ed
parent 52221 c0270004efacf62e8004c2d0145b1af4b5b22f66
child 52223 ec53c1b41f3c42b769d7a2522909c6e8fc9cbb6a
push id15573
push userdholbert@mozilla.com
push dateWed, 08 Sep 2010 20:57:52 +0000
treeherdermozilla-central@ec53c1b41f3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, blocking
bugs276431
milestone2.0b6pre
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 276431 Patch 14: Add SVGRootRenderingObserver helper-class, to catch invalidations in helper-document. r=roc a=blocking
modules/libpr0n/src/SVGDocumentWrapper.cpp
modules/libpr0n/src/SVGDocumentWrapper.h
modules/libpr0n/src/VectorImage.cpp
modules/libpr0n/src/VectorImage.h
--- a/modules/libpr0n/src/SVGDocumentWrapper.cpp
+++ b/modules/libpr0n/src/SVGDocumentWrapper.cpp
@@ -69,16 +69,17 @@ namespace imagelib {
 nsIAtom* SVGDocumentWrapper::kSVGAtom = nsnull; // lazily initialized
 
 NS_IMPL_ISUPPORTS3(SVGDocumentWrapper,
                    nsIStreamListener,
                    nsIRequestObserver,
                    nsIObserver)
 
 SVGDocumentWrapper::SVGDocumentWrapper()
+ : mIgnoreInvalidation(PR_FALSE)
 {
   // Lazy-initialize our "svg" atom.  (It'd be nicer to just use nsGkAtoms::svg
   // directly, but we can't access it from here in non-libxul builds.)
   if (!SVGDocumentWrapper::kSVGAtom) {
     SVGDocumentWrapper::kSVGAtom =
       NS_NewPermanentAtom(NS_LITERAL_STRING("svg"));
   }
 }
@@ -147,18 +148,21 @@ SVGDocumentWrapper::GetRootLayoutFrame()
 {
   Element* rootElem = GetRootSVGElem();
   return rootElem ? rootElem->GetPrimaryFrame() : nsnull;
 }
 
 void
 SVGDocumentWrapper::UpdateViewportBounds(const nsIntSize& aViewportSize)
 {
+  NS_ABORT_IF_FALSE(!mIgnoreInvalidation, "shouldn't be reentrant");
+  mIgnoreInvalidation = PR_TRUE;
   mViewer->SetBounds(nsIntRect(nsIntPoint(0, 0), aViewportSize));
   FlushLayout();
+  mIgnoreInvalidation = PR_FALSE;
 }
 
 PRBool
 SVGDocumentWrapper::IsAnimated()
 {
   nsIDocument* doc = mViewer->GetDocument();
   return doc && doc->HasAnimationController() &&
     doc->GetAnimationController()->HasRegisteredAnimations();
@@ -391,16 +395,23 @@ SVGDocumentWrapper::FlushLayout()
   if (presShell) {
     presShell->FlushPendingNotifications(Flush_Layout);
   }
 }
 
 nsSVGSVGElement*
 SVGDocumentWrapper::GetRootSVGElem()
 {
+  if (!mViewer)
+    return nsnull; // Can happen during destruction
+
+  nsIDocument* doc = mViewer->GetDocument();
+  if (!doc)
+    return nsnull; // Can happen during destruction
+
   Element* rootElem = mViewer->GetDocument()->GetRootElement();
   if (!rootElem ||
       rootElem->GetNameSpaceID() != kNameSpaceID_SVG ||
       rootElem->Tag() != SVGDocumentWrapper::kSVGAtom) {
     return nsnull;
   }
 
   return static_cast<nsSVGSVGElement*>(rootElem);
--- a/modules/libpr0n/src/SVGDocumentWrapper.h
+++ b/modules/libpr0n/src/SVGDocumentWrapper.h
@@ -140,16 +140,24 @@ public:
   /**
    * Returns a PRBool indicating whether the document has any SMIL animations.
    *
    * @return PR_TRUE if the document has any SMIL animations. Else, PR_FALSE.
    */
   PRBool    IsAnimated();
 
   /**
+   * Indicates whether we should currently ignore rendering invalidations sent
+   * from the wrapped SVG doc.
+   *
+   * @return PR_TRUE if we should ignore invalidations sent from this SVG doc.
+   */
+  PRBool ShouldIgnoreInvalidation() { return mIgnoreInvalidation; }
+
+  /**
    * Methods to control animation.
    */
   void StartAnimation();
   void StopAnimation();
   void ResetAnimation();
 
 private:
   nsresult SetupViewer(nsIRequest *aRequest,
@@ -158,16 +166,17 @@ private:
   void     DestroyViewer();
   void     RegisterForXPCOMShutdown();
 
   void     FlushLayout();
 
   nsCOMPtr<nsIDocumentViewer> mViewer;
   nsCOMPtr<nsILoadGroup>      mLoadGroup;
   nsCOMPtr<nsIStreamListener> mListener;
+  PRPackedBool                mIgnoreInvalidation;
 
   // Lazily-initialized pointer to nsGkAtoms::svg, to make life easier in
   // non-libxul builds, which don't let us reference nsGkAtoms from imagelib.
   static nsIAtom* kSVGAtom;
 };
 
 } // namespace imagelib
 } // namespace mozilla
--- a/modules/libpr0n/src/VectorImage.cpp
+++ b/modules/libpr0n/src/VectorImage.cpp
@@ -48,20 +48,81 @@
 #include "nsIPresShell.h"
 #include "nsIStreamListener.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsSVGUtils.h"  // for nsSVGUtils::ConvertToSurfaceSize
 #include "nsSVGEffects.h" // for nsSVGRenderingObserver
 #include "gfxDrawable.h"
 #include "gfxUtils.h"
+#include "nsSVGSVGElement.h"
+
+using namespace mozilla::dom;
 
 namespace mozilla {
 namespace imagelib {
 
+#ifdef MOZ_ENABLE_LIBXUL
+// Helper-class: SVGRootRenderingObserver
+class SVGRootRenderingObserver : public nsSVGRenderingObserver {
+public:
+  SVGRootRenderingObserver(SVGDocumentWrapper* aDocWrapper,
+                           VectorImage*        aVectorImage)
+    : nsSVGRenderingObserver(),
+      mDocWrapper(aDocWrapper),
+      mVectorImage(aVectorImage)
+  {
+    StartListening();
+    Element* elem = GetTarget();
+    if (elem) {
+      nsSVGEffects::AddRenderingObserver(elem, this);
+    }
+  }
+
+  virtual ~SVGRootRenderingObserver()
+  {
+    StopListening();
+  }
+
+protected:
+  virtual Element* GetTarget()
+  {
+    return mDocWrapper->GetRootSVGElem();
+  }
+
+  virtual void DoUpdate()
+  {
+    Element* elem = GetTarget();
+    if (!elem)
+      return;
+
+    if (!mDocWrapper->ShouldIgnoreInvalidation()) {
+      nsIFrame* frame = elem->GetPrimaryFrame();
+      if (!frame || frame->PresContext()->PresShell()->IsDestroying()) {
+        // We're being destroyed. Bail out.
+        return;
+      }
+
+      mVectorImage->InvalidateObserver();
+    }
+
+    // Our caller might've removed us from rendering-observer list.
+    // Add ourselves back!
+    if (!mInObserverList) {
+      nsSVGEffects::AddRenderingObserver(elem, this);
+      mInObserverList = PR_TRUE;
+    }
+  }
+
+  // Private data
+  nsRefPtr<SVGDocumentWrapper> mDocWrapper;
+  VectorImage* mVectorImage;   // Raw pointer because it owns me.
+};
+#endif // MOZ_ENABLE_LIBXUL
+
 // Helper-class: SVGDrawingCallback
 class SVGDrawingCallback : public gfxDrawingCallback {
 public:
   SVGDrawingCallback(SVGDocumentWrapper* aSVGDocumentWrapper,
                      const nsIntRect& aViewport,
                      PRUint32 aImageFlags) :
     mSVGDocumentWrapper(aSVGDocumentWrapper),
     mViewport(aViewport),
@@ -569,16 +630,22 @@ VectorImage::OnStopRequest(nsIRequest* a
   mHaveAnimations = mSVGDocumentWrapper->IsAnimated();
 
   if (mHaveAnimations && mAnimationMode == kDontAnimMode) {
     // We're not supposed to be animating -- stop any animation before our
     // SVG document's timeline gets a chance to progress.
     mSVGDocumentWrapper->StopAnimation();
   }
 
+#ifdef MOZ_ENABLE_LIBXUL
+  // Start listening to our image for rendering updates
+  mRenderingObserver = new SVGRootRenderingObserver(mSVGDocumentWrapper, this);
+#endif // MOZ_ENABLE_LIBXUL
+
+  // Tell *our* observers that we're done loading
   nsCOMPtr<imgIDecoderObserver> observer = do_QueryReferent(mObserver);
   if (observer) {
     // NOTE: This signals that width/height are available.
     observer->OnStartContainer(nsnull, this);
 
     observer->FrameChanged(this, &kFullImageSpaceRect);
     observer->OnStopFrame(nsnull, 0);
     observer->OnStopDecode(nsnull, NS_OK, nsnull);
@@ -598,10 +665,22 @@ NS_IMETHODIMP
 VectorImage::OnDataAvailable(nsIRequest* aRequest, nsISupports* aCtxt,
                              nsIInputStream* aInStr, PRUint32 aSourceOffset,
                              PRUint32 aCount)
 {
   return mSVGDocumentWrapper->OnDataAvailable(aRequest, aCtxt, aInStr,
                                               aSourceOffset, aCount);
 }
 
+// --------------------------
+// Invalidation helper method
+
+void
+VectorImage::InvalidateObserver()
+{
+  nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
+  if (observer) {
+    observer->FrameChanged(this, &kFullImageSpaceRect);
+  }
+}
+
 } // namespace imagelib
 } // namespace mozilla
--- a/modules/libpr0n/src/VectorImage.h
+++ b/modules/libpr0n/src/VectorImage.h
@@ -44,16 +44,17 @@
 #include "nsWeakReference.h"
 
 class imgIDecoderObserver;
 
 namespace mozilla {
 namespace imagelib {
 
 class SVGDocumentWrapper;
+class SVGRootRenderingObserver;
 
 class VectorImage : public Image,
                     public nsIStreamListener
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_IMGICONTAINER
   NS_DECL_NSIREQUESTOBSERVER
@@ -67,23 +68,29 @@ public:
 
   // Methods inherited from Image
   nsresult Init(imgIDecoderObserver* aObserver,
                 const char* aMimeType,
                 PRUint32 aFlags);
   void GetCurrentFrameRect(nsIntRect& aRect);
   PRUint32 GetDataSize();
 
+  // Callback for SVGRootRenderingObserver
+  void InvalidateObserver();
+
 protected:
   virtual nsresult StartAnimation();
   virtual nsresult StopAnimation();
 
 private:
   nsWeakPtr                          mObserver;   //! imgIDecoderObserver
   nsRefPtr<SVGDocumentWrapper>       mSVGDocumentWrapper;
+#ifdef MOZ_ENABLE_LIBXUL
+  nsRefPtr<SVGRootRenderingObserver> mRenderingObserver;
+#endif // MOZ_ENABLE_LIBXUL
 
   nsIntRect      mRestrictedRegion;       // If we were created by
                                           // ExtractFrame, this is the region
                                           // that we're restricted to using.
                                           // Otherwise, this is ignored.
 
   nsIntSize      mLastRenderedSize;       // The viewport-size that we've
                                           // most recently passed to