Bug 272288 Patch 2: Store override preserveAspectRatio values in property table on image document's root node, when painting <svg> image element. r=roc a=roc
authorDaniel Holbert <dholbert@cs.stanford.edu>
Sun, 19 Dec 2010 16:45:29 -0800
changeset 59474 16f87faaacb4107d7ac8b1089b3a6a476dc06197
parent 59473 c347676c5cffa5e763bc9d9ea56e3d14c80500a9
child 59475 3a8cdfab4ee8d1922f63adbc55c503e890e8a4b4
push id17637
push userdholbert@mozilla.com
push dateMon, 20 Dec 2010 00:53:32 +0000
treeherdermozilla-central@150af817b65d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, roc
bugs272288
milestone2.0b9pre
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 272288 Patch 2: Store override preserveAspectRatio values in property table on image document's root node, when painting <svg> image element. r=roc a=roc
content/base/src/nsGkAtomList.h
content/svg/content/src/SVGAnimatedPreserveAspectRatio.h
content/svg/content/src/nsSVGSVGElement.cpp
content/svg/content/src/nsSVGSVGElement.h
layout/reftests/svg/image/blueRect10x10-viewBox.svg
layout/reftests/svg/image/image-preserveAspectRatio-01-raster.svg
layout/reftests/svg/image/image-preserveAspectRatio-01-svg.svg
layout/reftests/svg/image/image-preserveAspectRatio-01.svg
layout/reftests/svg/image/image-preserveAspectRatio-02-raster.svg
layout/reftests/svg/image/image-preserveAspectRatio-02-svg.svg
layout/reftests/svg/image/image-preserveAspectRatio-02.svg
layout/reftests/svg/image/reftest.list
layout/svg/base/src/nsSVGImageFrame.cpp
modules/libpr0n/src/SVGDocumentWrapper.cpp
modules/libpr0n/src/SVGDocumentWrapper.h
modules/libpr0n/src/VectorImage.cpp
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1240,16 +1240,17 @@ GK_ATOM(onSVGResize, "onSVGResize")
 GK_ATOM(onSVGScroll, "onSVGScroll")
 GK_ATOM(onSVGUnload, "onSVGUnload")
 GK_ATOM(onSVGZoom, "onSVGZoom")
 GK_ATOM(onzoom, "onzoom")
 GK_ATOM(opacity, "opacity")
 GK_ATOM(_operator, "operator")
 GK_ATOM(out, "out")
 GK_ATOM(over, "over")
+GK_ATOM(overridePreserveAspectRatio, "overridePreserveAspectRatio")
 GK_ATOM(pad, "pad")
 GK_ATOM(path, "path")
 GK_ATOM(pathLength, "pathLength")
 GK_ATOM(patternContentUnits, "patternContentUnits")
 GK_ATOM(patternTransform, "patternTransform")
 GK_ATOM(patternUnits, "patternUnits")
 GK_ATOM(pc, "pc")
 GK_ATOM(pointer_events, "pointer-events")
--- a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h
+++ b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h
@@ -110,16 +110,18 @@ public:
   nsresult SetBaseAlign(PRUint16 aAlign, nsSVGElement *aSVGElement);
   nsresult SetBaseMeetOrSlice(PRUint16 aMeetOrSlice, nsSVGElement *aSVGElement);
   void SetAnimValue(PRUint64 aPackedValue, nsSVGElement *aSVGElement);
 
   const SVGPreserveAspectRatio &GetBaseValue() const
     { return mBaseVal; }
   const SVGPreserveAspectRatio &GetAnimValue() const
     { return mAnimVal; }
+  PRBool IsAnimated() const
+    { return mIsAnimated; }
 
   nsresult ToDOMAnimatedPreserveAspectRatio(
     nsIDOMSVGAnimatedPreserveAspectRatio **aResult,
     nsSVGElement* aSVGElement);
 #ifdef MOZ_SMIL
   // Returns a new nsISMILAttr object that the caller must delete
   nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement);
 #endif // MOZ_SMIL
--- a/content/svg/content/src/nsSVGSVGElement.cpp
+++ b/content/svg/content/src/nsSVGSVGElement.cpp
@@ -56,16 +56,17 @@
 #include "nsSVGRect.h"
 #include "nsISVGValueUtils.h"
 #include "nsDOMError.h"
 #include "nsISVGChildFrame.h"
 #include "nsGUIEvent.h"
 #include "nsSVGUtils.h"
 #include "nsSVGSVGElement.h"
 #include "nsSVGEffects.h" // For nsSVGEffects::RemoveAllRenderingObservers
+#include "nsContentErrors.h" // For NS_PROPTABLE_PROP_OVERWRITTEN
 
 #ifdef MOZ_SMIL
 #include "nsEventDispatcher.h"
 #include "nsSMILTimeContainer.h"
 #include "nsSMILAnimationController.h"
 #include "nsSMILTypes.h"
 #include "nsIContentIterator.h"
 
@@ -207,16 +208,17 @@ nsSVGSVGElement::nsSVGSVGElement(already
     mCurrentTranslate(0.0f, 0.0f),
     mCurrentScale(1.0f),
     mPreviousTranslate(0.0f, 0.0f),
     mPreviousScale(1.0f),
     mRedrawSuspendCount(0)
 #ifdef MOZ_SMIL
   , mStartAnimationOnBindToTree(!aFromParser)
 #endif // MOZ_SMIL
+  , mNeedsPreserveAspectRatioFlush(PR_FALSE)
 {
 }
 
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 // From NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGSVGElement)
 nsresult
@@ -989,21 +991,26 @@ nsSVGSVGElement::GetViewBoxTransform()
     viewBox.width  = viewportWidth;
     viewBox.height = viewportHeight;
   }
 
   if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
     return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
   }
 
+  // Do we have an override preserveAspectRatio value?
+  const SVGPreserveAspectRatio* overridePARPtr =
+    GetImageOverridePreserveAspectRatio();
+
   return nsSVGUtils::GetViewBoxTransform(this,
                                          viewportWidth, viewportHeight,
                                          viewBox.x, viewBox.y,
                                          viewBox.width, viewBox.height,
-                                         mPreserveAspectRatio);
+                                         overridePARPtr ? *overridePARPtr :
+                                         mPreserveAspectRatio.GetAnimValue());
 }
 
 #ifdef MOZ_SMIL
 nsresult
 nsSVGSVGElement::BindToTree(nsIDocument* aDocument,
                             nsIContent* aParent,
                             nsIContent* aBindingParent,
                             PRBool aCompileEventHandlers)
@@ -1095,16 +1102,23 @@ nsSVGSVGElement::InvalidateTransformNoti
   else if (frame) {
     // Uh oh -- we have a primary frame, but it failed the do_QueryFrame to the
     // expected type!
     NS_WARNING("wrong frame type");
   }
 #endif
 }
 
+PRBool
+nsSVGSVGElement::HasPreserveAspectRatio()
+{
+  return HasAttr(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio) ||
+    mPreserveAspectRatio.IsAnimated();
+}
+
 //----------------------------------------------------------------------
 // nsSVGSVGElement
 
 float
 nsSVGSVGElement::GetLength(PRUint8 aCtxType)
 {
   float h, w;
 
@@ -1237,8 +1251,90 @@ nsSVGSVGElement::GetPreserveAspectRatio(
 #ifndef MOZ_ENABLE_LIBXUL
 // XXXdholbert HACK -- see comment w/ this method's declaration in header file.
 void
 nsSVGSVGElement::RemoveAllRenderingObservers()
 {
   nsSVGEffects::RemoveAllRenderingObservers(this);
 }
 #endif // !MOZ_LIBXUL
+
+// Callback function, for freeing PRUint64 values stored in property table
+static void
+ReleasePreserveAspectRatioPropertyValue(void*    aObject,       /* unused */
+                                        nsIAtom* aPropertyName, /* unused */
+                                        void*    aPropertyValue,
+                                        void*    aData          /* unused */)
+{
+  SVGPreserveAspectRatio* valPtr =
+    static_cast<SVGPreserveAspectRatio*>(aPropertyValue);
+  delete valPtr;
+}
+
+void
+nsSVGSVGElement::
+  SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR)
+{
+#ifdef DEBUG
+  NS_ABORT_IF_FALSE(GetCurrentDoc()->IsBeingUsedAsImage(),
+                    "should only override preserveAspectRatio in images");
+#endif
+
+  if (!mViewBox.IsValid()) {
+    return; // preserveAspectRatio irrelevant (only matters if we have viewBox)
+  }
+
+  if (aPAR.GetDefer() && HasPreserveAspectRatio()) {
+    return; // Referring element defers to my own preserveAspectRatio value.
+  }
+
+  SVGPreserveAspectRatio* pAROverridePtr = new SVGPreserveAspectRatio(aPAR);
+  nsresult rv = SetProperty(nsGkAtoms::overridePreserveAspectRatio,
+                            pAROverridePtr,
+                            ReleasePreserveAspectRatioPropertyValue);
+  NS_ABORT_IF_FALSE(rv != NS_PROPTABLE_PROP_OVERWRITTEN,
+                    "Setting override value when it's already set...?"); 
+
+  if (NS_LIKELY(NS_SUCCEEDED(rv))) {
+    mNeedsPreserveAspectRatioFlush = PR_TRUE;
+  } else {
+    // property-insertion failed (e.g. OOM in property-table code)
+    delete pAROverridePtr;
+  }
+}
+
+void
+nsSVGSVGElement::ClearImageOverridePreserveAspectRatio()
+{
+#ifdef DEBUG
+  NS_ABORT_IF_FALSE(GetCurrentDoc()->IsBeingUsedAsImage(),
+                    "should only override preserveAspectRatio in images");
+#endif
+
+  void* valPtr = UnsetProperty(nsGkAtoms::overridePreserveAspectRatio);
+  if (valPtr) {
+    mNeedsPreserveAspectRatioFlush = PR_TRUE;
+    delete static_cast<SVGPreserveAspectRatio*>(valPtr);
+  }
+}
+
+const SVGPreserveAspectRatio*
+nsSVGSVGElement::GetImageOverridePreserveAspectRatio()
+{
+  void* valPtr = GetProperty(nsGkAtoms::overridePreserveAspectRatio);
+#ifdef DEBUG
+  if (valPtr) {
+    NS_ABORT_IF_FALSE(GetCurrentDoc()->IsBeingUsedAsImage(),
+                      "should only override preserveAspectRatio in images");
+  }
+#endif
+
+  return static_cast<SVGPreserveAspectRatio*>(valPtr);
+}
+
+void
+nsSVGSVGElement::FlushPreserveAspectRatioOverride()
+{
+  if (mNeedsPreserveAspectRatioFlush) {
+    InvalidateTransformNotifyFrame();
+    mNeedsPreserveAspectRatioFlush = PR_FALSE;
+  }
+}
--- a/content/svg/content/src/nsSVGSVGElement.h
+++ b/content/svg/content/src/nsSVGSVGElement.h
@@ -125,26 +125,28 @@ public:
 class nsSVGSVGElement : public nsSVGSVGElementBase,
                         public nsIDOMSVGSVGElement,
                         public nsIDOMSVGFitToViewBox,
                         public nsIDOMSVGLocatable,
                         public nsIDOMSVGZoomAndPan
 {
   friend class nsSVGOuterSVGFrame;
   friend class nsSVGInnerSVGFrame;
+  friend class nsSVGImageFrame;
 
 protected:
   friend nsresult NS_NewSVGSVGElement(nsIContent **aResult,
                                       already_AddRefed<nsINodeInfo> aNodeInfo,
                                       mozilla::dom::FromParser aFromParser);
   nsSVGSVGElement(already_AddRefed<nsINodeInfo> aNodeInfo,
                   mozilla::dom::FromParser aFromParser);
   
 public:
   typedef mozilla::SVGAnimatedPreserveAspectRatio SVGAnimatedPreserveAspectRatio;
+  typedef mozilla::SVGPreserveAspectRatio SVGPreserveAspectRatio;
 
   // interfaces:
   NS_DECL_ISUPPORTS_INHERITED
 #ifdef MOZ_SMIL
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsSVGSVGElement, nsSVGSVGElementBase)
 #endif // MOZ_SMIL
   NS_DECL_NSIDOMSVGSVGELEMENT
   NS_DECL_NSIDOMSVGFITTOVIEWBOX
@@ -205,16 +207,20 @@ public:
   
   // nsSVGSVGElement methods:
   float GetLength(PRUint8 mCtxType);
 
   // public helpers:
   gfxMatrix GetViewBoxTransform();
   PRBool    HasValidViewbox() const { return mViewBox.IsValid(); }
 
+  // This flushes any pending notifications for a preserveAspectRatio override
+  // in this document.  (Only applicable in SVG-as-an-image documents.)
+  virtual void FlushPreserveAspectRatioOverride();
+
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   svgFloatSize GetViewportSize() const {
     return svgFloatSize(mViewportWidth, mViewportHeight);
   }
 
   void SetViewportSize(const svgFloatSize& aSize) {
     mViewportWidth  = aSize.width;
@@ -225,16 +231,24 @@ public:
 
 #ifndef MOZ_ENABLE_LIBXUL
   // XXXdholbert HACK to call static method
   // nsSVGEffects::RemoveAllRenderingObservers() on myself, on behalf
   // of imagelib in non-libxul builds.
   virtual void RemoveAllRenderingObservers();
 #endif // !MOZ_LIBXUL
 
+private:
+  // Methods for <image> elements to override my "PreserveAspectRatio" value.
+  // These are private so that only our friends (nsSVGImageFrame in
+  // particular) have access.
+  void SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR);
+  void ClearImageOverridePreserveAspectRatio();
+  const SVGPreserveAspectRatio* GetImageOverridePreserveAspectRatio();
+
 protected:
   // nsSVGElement overrides
   PRBool IsEventName(nsIAtom* aName);
 
 #ifdef MOZ_SMIL
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               PRBool aCompileEventHandlers);
@@ -262,16 +276,21 @@ protected:
    */
   PRBool WillBeOutermostSVG(nsIContent* aParent,
                             nsIContent* aBindingParent) const;
 #endif // MOZ_SMIL
 
   // invalidate viewbox -> viewport xform & inform frames
   void InvalidateTransformNotifyFrame();
 
+  // Returns PR_TRUE if we have at least one of the following:
+  // - a (valid or invalid) value for the preserveAspectRatio attribute
+  // - a SMIL-animated value for the preserveAspectRatio attribute
+  PRBool HasPreserveAspectRatio();
+
   virtual LengthAttributesInfo GetLengthInfo();
 
   enum { X, Y, WIDTH, HEIGHT };
   nsSVGLength2 mLengthAttributes[4];
   static LengthInfo sLengthInfo[4];
 
   virtual EnumAttributesInfo GetEnumInfo();
 
@@ -315,11 +334,12 @@ protected:
 
 #ifdef MOZ_SMIL
   // For outermost <svg> elements created from parsing, animation is started by
   // the onload event in accordance with the SVG spec, but for <svg> elements
   // created by script or promoted from inner <svg> to outermost <svg> we need
   // to manually kick off animation when they are bound to the tree.
   PRPackedBool                      mStartAnimationOnBindToTree;
 #endif // MOZ_SMIL
+  PRPackedBool                      mNeedsPreserveAspectRatioFlush;
 };
 
 #endif
copy from layout/reftests/svg/image/blueRect10x10.svg
copy to layout/reftests/svg/image/blueRect10x10-viewBox.svg
--- a/layout/reftests/svg/image/blueRect10x10.svg
+++ b/layout/reftests/svg/image/blueRect10x10-viewBox.svg
@@ -1,6 +1,7 @@
 <!-- Helper SVG file used by some reftests -->
-<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="root">
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="root"
+     viewBox="0 0 10 10">
   <rect x="0"  y="0"  width="10" height="10" fill="blue"/>
   <rect x="1" y="1" width="8"  height="8"  fill="lightblue"/>
   <rect x="1" y="1" width="8"  height="4"  fill="teal"/>
 </svg>
rename from layout/reftests/svg/image/image-preserveAspectRatio-01.svg
rename to layout/reftests/svg/image/image-preserveAspectRatio-01-raster.svg
copy from layout/reftests/svg/image/image-preserveAspectRatio-01.svg
copy to layout/reftests/svg/image/image-preserveAspectRatio-01-svg.svg
--- a/layout/reftests/svg/image/image-preserveAspectRatio-01.svg
+++ b/layout/reftests/svg/image/image-preserveAspectRatio-01-svg.svg
@@ -1,13 +1,12 @@
 <!-- Test to make sure "preserveAspectRatio" value is applied correctly on
      <image> tag, when the width:height ratio is less than the image's
      nativeWidth:nativeHeight  --> 
 <svg xmlns="http://www.w3.org/2000/svg"
-     xmlns:xlink="http://www.w3.org/1999/xlink"
-     style="image-rendering: -moz-crisp-edges">
+     xmlns:xlink="http://www.w3.org/1999/xlink">
   <script xlink:href="util.js" type="text/javascript"/>
   <script>
-    var grid = generateImageGrid("blueRect10x10.png", 20, 40);
+    var grid = generateImageGrid("blueRect10x10-viewBox.svg", 20, 40);
     grid.setAttribute("transform", "translate(10, 10)");
     document.documentElement.appendChild(grid);
   </script>
 </svg>
rename from layout/reftests/svg/image/image-preserveAspectRatio-02.svg
rename to layout/reftests/svg/image/image-preserveAspectRatio-02-raster.svg
copy from layout/reftests/svg/image/image-preserveAspectRatio-02.svg
copy to layout/reftests/svg/image/image-preserveAspectRatio-02-svg.svg
--- a/layout/reftests/svg/image/image-preserveAspectRatio-02.svg
+++ b/layout/reftests/svg/image/image-preserveAspectRatio-02-svg.svg
@@ -1,13 +1,12 @@
 <!-- Test to make sure "preserveAspectRatio" value is applied correctly on
      <image> tag, when the width:height ratio is greater than the image's
      nativeWidth:nativeHeight  --> 
 <svg xmlns="http://www.w3.org/2000/svg"
-     xmlns:xlink="http://www.w3.org/1999/xlink"
-     style="image-rendering: -moz-crisp-edges">
+     xmlns:xlink="http://www.w3.org/1999/xlink">
   <script xlink:href="util.js" type="text/javascript"/>
   <script>
-    var grid = generateImageGrid("blueRect10x10.png", 40, 20);
+    var grid = generateImageGrid("blueRect10x10-viewBox.svg", 40, 20);
     grid.setAttribute("transform", "translate(10, 10)");
     document.documentElement.appendChild(grid);
   </script>
 </svg>
--- a/layout/reftests/svg/image/reftest.list
+++ b/layout/reftests/svg/image/reftest.list
@@ -16,10 +16,12 @@
 == image-y-01.svg             image-y-01-ref.svg
 == image-zoom-01a.svg         image-zoom-01-ref.svg
 == image-zoom-01b.svg         image-zoom-01-ref.svg
 == image-zoom-02.svg          image-zoom-02-ref.svg
 
 # Tests for <image> with preserveAspectRatio
 # NOTE: The reference cases in the following tests trigger 20 assertions each
 # (1 per <symbol> element), due to bug 563481.
-asserts(20) == image-preserveAspectRatio-01.svg image-preserveAspectRatio-01-ref.svg
-asserts(20) == image-preserveAspectRatio-02.svg image-preserveAspectRatio-02-ref.svg
+asserts(20) == image-preserveAspectRatio-01-raster.svg image-preserveAspectRatio-01-ref.svg
+asserts(20) == image-preserveAspectRatio-01-svg.svg    image-preserveAspectRatio-01-ref.svg
+asserts(20) == image-preserveAspectRatio-02-raster.svg image-preserveAspectRatio-02-ref.svg
+asserts(20) == image-preserveAspectRatio-02-svg.svg    image-preserveAspectRatio-02-ref.svg
--- a/layout/svg/base/src/nsSVGImageFrame.cpp
+++ b/layout/svg/base/src/nsSVGImageFrame.cpp
@@ -41,16 +41,17 @@
 #include "nsIDOMSVGImageElement.h"
 #include "nsLayoutUtils.h"
 #include "nsSVGImageElement.h"
 #include "nsSVGUtils.h"
 #include "gfxContext.h"
 #include "gfxMatrix.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "gfxPlatform.h"
+#include "nsSVGSVGElement.h"
 
 class nsSVGImageFrame;
 
 class nsSVGImageListener : public nsStubImageDecoderObserver
 {
 public:
   nsSVGImageListener(nsSVGImageFrame *aFrame);
 
@@ -258,18 +259,18 @@ nsSVGImageFrame::PaintSVG(nsSVGRenderSta
                           const nsIntRect *aDirtyRect)
 {
   nsresult rv = NS_OK;
 
   if (!GetStyleVisibility()->IsVisible())
     return NS_OK;
 
   float x, y, width, height;
-  nsSVGElement *element = static_cast<nsSVGElement*>(mContent);
-  element->GetAnimatedLengthValues(&x, &y, &width, &height, nsnull);
+  nsSVGImageElement *imgElem = static_cast<nsSVGImageElement*>(mContent);
+  imgElem->GetAnimatedLengthValues(&x, &y, &width, &height, nsnull);
   if (width <= 0 || height <= 0)
     return NS_OK;
 
   if (!mImageContainer) {
     nsCOMPtr<imgIRequest> currentRequest;
     nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
     if (imageLoader)
       imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
@@ -315,30 +316,52 @@ nsSVGImageFrame::PaintSVG(nsSVGRenderSta
 
     // XXXbholley - I don't think huge images in SVGs are common enough to
     // warrant worrying about the responsiveness impact of doing synchronous
     // decodes. The extra code complexity of determinining when we want to
     // force sync probably just isn't worth it, so always pass FLAG_SYNC_DECODE
     PRUint32 drawFlags = imgIContainer::FLAG_SYNC_DECODE;
 
     if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
+      nsIFrame* imgRootFrame = mImageContainer->GetRootLayoutFrame();
+      if (!imgRootFrame) {
+        // bad image (e.g. XML parse error in image's SVG file)
+        return NS_OK;
+      }
+
+      // Grab root node (w/ sanity-check to make sure it exists & is <svg>)
+      nsSVGSVGElement* rootSVGElem =
+        static_cast<nsSVGSVGElement*>(imgRootFrame->GetContent());
+      if (!rootSVGElem || rootSVGElem->GetNameSpaceID() != kNameSpaceID_SVG ||
+          rootSVGElem->Tag() != nsGkAtoms::svg) {
+        NS_ABORT_IF_FALSE(PR_FALSE, "missing or non-<svg> root node!!");
+        return PR_FALSE;
+      }
+
+      // Override preserveAspectRatio in our helper document
+      // XXXdholbert We should technically be overriding the helper doc's clip
+      // and overflow properties here, too. See bug 272288 comment 36.
+      rootSVGElem->SetImageOverridePreserveAspectRatio(
+        imgElem->mPreserveAspectRatio.GetAnimValue());
       nsRect destRect(0, 0,
                       appUnitsPerDevPx * width,
                       appUnitsPerDevPx * height);
 
       // Note: Can't use DrawSingleUnscaledImage for the TYPE_VECTOR case.
       // That method needs our image to have a fixed native width & height,
       // and that's not always true for TYPE_VECTOR images.
       nsLayoutUtils::DrawSingleImage(
         aContext->GetRenderingContext(this),
         mImageContainer,
         nsLayoutUtils::GetGraphicsFilterForFrame(this),
         destRect,
         aDirtyRect ? dirtyRect : destRect,
         drawFlags);
+
+      rootSVGElem->ClearImageOverridePreserveAspectRatio();
     } else { // mImageContainer->GetType() == TYPE_RASTER
       nsLayoutUtils::DrawSingleUnscaledImage(
         aContext->GetRenderingContext(this),
         mImageContainer,
         nsLayoutUtils::GetGraphicsFilterForFrame(this),
         nsPoint(0, 0),
         aDirtyRect ? &dirtyRect : nsnull,
         drawFlags);
--- a/modules/libpr0n/src/SVGDocumentWrapper.cpp
+++ b/modules/libpr0n/src/SVGDocumentWrapper.cpp
@@ -161,16 +161,31 @@ SVGDocumentWrapper::UpdateViewportBounds
 {
   NS_ABORT_IF_FALSE(!mIgnoreInvalidation, "shouldn't be reentrant");
   mIgnoreInvalidation = PR_TRUE;
   mViewer->SetBounds(nsIntRect(nsIntPoint(0, 0), aViewportSize));
   FlushLayout();
   mIgnoreInvalidation = PR_FALSE;
 }
 
+void
+SVGDocumentWrapper::FlushPreserveAspectRatioOverride()
+{
+  NS_ABORT_IF_FALSE(!mIgnoreInvalidation, "shouldn't be reentrant");
+
+  nsSVGSVGElement* svgElem = GetRootSVGElem();
+  if (!svgElem)
+    return;
+
+  mIgnoreInvalidation = PR_TRUE;
+  svgElem->FlushPreserveAspectRatioOverride();
+  FlushLayout();
+  mIgnoreInvalidation = PR_FALSE;
+}
+
 PRBool
 SVGDocumentWrapper::IsAnimated()
 {
 #ifdef MOZ_SMIL
   nsIDocument* doc = mViewer->GetDocument();
   return doc && doc->HasAnimationController() &&
     doc->GetAnimationController()->HasRegisteredAnimations();
 #else
--- a/modules/libpr0n/src/SVGDocumentWrapper.h
+++ b/modules/libpr0n/src/SVGDocumentWrapper.h
@@ -133,16 +133,24 @@ public:
    * Modifier to update the viewport dimensions of the wrapped document. This
    * method performs a synchronous "Flush_Layout" on the wrapped document,
    * since a viewport-change affects layout.
    *
    * @param aViewportSize The new viewport dimensions.
    */
   void UpdateViewportBounds(const nsIntSize& aViewportSize);
 
+  /**
+   * If an SVG image's helper document has a pending notification for an
+   * override on the root node's "preserveAspectRatio" attribute, then this
+   * method will flush that notification so that the image can paint correctly.
+   * (First, though, it sets the mIgnoreInvalidation flag so that we won't
+   * notify the image's observers and trigger unwanted repaint-requests.)
+   */
+  void FlushPreserveAspectRatioOverride();
 
   /**
    * 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();
 
--- a/modules/libpr0n/src/VectorImage.cpp
+++ b/modules/libpr0n/src/VectorImage.cpp
@@ -534,16 +534,17 @@ VectorImage::Draw(gfxContext* aContext,
     return NS_ERROR_FAILURE;
   }
   mIsDrawing = PR_TRUE;
 
   if (aViewportSize != mLastRenderedSize) {
     mSVGDocumentWrapper->UpdateViewportBounds(aViewportSize);
     mLastRenderedSize = aViewportSize;
   }
+  mSVGDocumentWrapper->FlushPreserveAspectRatioOverride();
 
   nsIntSize imageSize = mHaveRestrictedRegion ?
     mRestrictedRegion.Size() : aViewportSize;
 
   // XXXdholbert Do we need to convert image size from
   // CSS pixels to dev pixels here? (is gfxCallbackDrawable's 2nd arg in dev
   // pixels?)
   gfxIntSize imageSizeGfx(imageSize.width, imageSize.height);