Bug 1253995 - Display stale image in nsImageFrame if we have a new src but haven't decoded it yet - r=seth
authorEdwin Flores <eflores@mozilla.com>
Fri, 22 Apr 2016 14:08:25 +0100
changeset 332417 3e12df7cefe4cf2e0b8dc11867e1008b21aa135a
parent 332416 b12d4b8634f3e1997dcaca3ac742c5ea3ea12df1
child 332418 82a1ca051000cdc8ae4457b8c5720e06fabd9bf1
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersseth
bugs1253995
milestone48.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 1253995 - Display stale image in nsImageFrame if we have a new src but haven't decoded it yet - r=seth
layout/generic/nsImageFrame.cpp
layout/generic/nsImageFrame.h
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -552,22 +552,22 @@ nsImageFrame::OnSizeAvailable(imgIReques
     return NS_OK;
   }
 
   bool intrinsicSizeChanged = false;
   if (SizeIsAvailable(aRequest)) {
     // This is valid and for the current request, so update our stored image
     // container, orienting according to our style.
     mImage = nsLayoutUtils::OrientImage(aImage, StyleVisibility()->mImageOrientation);
-    
+
     intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
     intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
   } else {
     // We no longer have a valid image, so release our stored image container.
-    mImage = nullptr;
+    mImage = mPrevImage = nullptr;
 
     // Have to size to 0,0 so that GetDesiredSize recalculates the size.
     mIntrinsicSize.width.SetCoordValue(0);
     mIntrinsicSize.height.SetCoordValue(0);
     mIntrinsicRatio.SizeTo(0, 0);
     intrinsicSizeChanged = true;
   }
 
@@ -581,16 +581,18 @@ nsImageFrame::OnSizeAvailable(imgIReques
         presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
                                     NS_FRAME_IS_DIRTY);
       }
     } else {
       // We've already gotten the initial reflow, and our size hasn't changed,
       // so we're ready to request a decode.
       MaybeDecodeForPredictedSize();
     }
+
+    mPrevImage = nullptr;
   }
 
   return NS_OK;
 }
 
 nsresult
 nsImageFrame::OnFrameUpdate(imgIRequest* aRequest, const nsIntRect* aRect)
 {
@@ -670,17 +672,17 @@ nsImageFrame::NotifyNewCurrentRequest(im
   if (NS_SUCCEEDED(aStatus) && image && SizeIsAvailable(aRequest)) {
     // Update our stored image container, orienting according to our style.
     mImage = nsLayoutUtils::OrientImage(image, StyleVisibility()->mImageOrientation);
 
     intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
     intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
   } else {
     // We no longer have a valid image, so release our stored image container.
-    mImage = nullptr;
+    mImage = mPrevImage = nullptr;
 
     // Have to size to 0,0 so that GetDesiredSize recalculates the size
     mIntrinsicSize.width.SetCoordValue(0);
     mIntrinsicSize.height.SetCoordValue(0);
     mIntrinsicRatio.SizeTo(0, 0);
   }
 
   if (mState & IMAGE_GOTINITIALREFLOW) { // do nothing if we haven't gotten the initial reflow yet
@@ -691,16 +693,18 @@ nsImageFrame::NotifyNewCurrentRequest(im
           presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
                                       NS_FRAME_IS_DIRTY);
         }
       } else {
         // We've already gotten the initial reflow, and our size hasn't changed,
         // so we're ready to request a decode.
         MaybeDecodeForPredictedSize();
       }
+
+      mPrevImage = nullptr;
     }
     // Update border+content to account for image change
     InvalidateFrame();
   }
 }
 
 void
 nsImageFrame::MaybeDecodeForPredictedSize()
@@ -1481,28 +1485,40 @@ static void PaintDebugImageMap(nsIFrame*
     aDrawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
   f->GetImageMap()->Draw(aFrame, *aDrawTarget,
                          ColorPattern(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f))));
 }
 #endif
 
 void
 nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
-                      nsRenderingContext* aCtx) {
+                      nsRenderingContext* aCtx)
+{
   uint32_t flags = imgIContainer::FLAG_NONE;
   if (aBuilder->ShouldSyncDecodeImages()) {
     flags |= imgIContainer::FLAG_SYNC_DECODE;
   }
   if (aBuilder->IsPaintingToWindow()) {
     flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
   }
 
   DrawResult result = static_cast<nsImageFrame*>(mFrame)->
     PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mImage, flags);
 
+  if (result == DrawResult::NOT_READY ||
+      result == DrawResult::INCOMPLETE ||
+      result == DrawResult::TEMPORARY_ERROR) {
+    // If the current image failed to paint because it's still loading or
+    // decoding, try painting the previous image.
+    if (mPrevImage) {
+      result = static_cast<nsImageFrame*>(mFrame)->
+        PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mPrevImage, flags);
+    }
+  }
+
   nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
 }
 
 nsDisplayItemGeometry*
 nsDisplayImage::AllocateGeometry(nsDisplayListBuilder* aBuilder)
 {
   return new nsDisplayItemGenericImageGeometry(this, aBuilder);
 }
@@ -1687,16 +1703,22 @@ nsImageFrame::PaintImage(nsRenderingCont
 
     // then dashed black stroke over the top:
     ColorPattern black(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f)));
     StrokeOptions strokeOptions;
     nsLayoutUtils::InitDashPattern(strokeOptions, NS_STYLE_BORDER_STYLE_DOTTED);
     map->Draw(this, *drawTarget, black, strokeOptions);
   }
 
+  if (result == DrawResult::SUCCESS) {
+    mPrevImage = aImage;
+  } else if (result == DrawResult::BAD_IMAGE) {
+    mPrevImage = nullptr;
+  }
+
   return result;
 }
 
 void
 nsImageFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                const nsRect&           aDirtyRect,
                                const nsDisplayListSet& aLists)
 {
@@ -1741,17 +1763,17 @@ nsImageFrame::BuildDisplayList(nsDisplay
         uint32_t status = 0;
         currentRequest->GetImageStatus(&status);
         if (!(status & imgIRequest::STATUS_DECODE_COMPLETE)) {
           MaybeDecodeForPredictedSize();
         }
       }
     } else {
       aLists.Content()->AppendNewToTop(new (aBuilder)
-        nsDisplayImage(aBuilder, this, mImage));
+        nsDisplayImage(aBuilder, this, mImage, mPrevImage));
 
       // If we were previously displaying an icon, we're not anymore
       if (mDisplayingIcon) {
         gIconLoad->RemoveIconObserver(this);
         mDisplayingIcon = false;
       }
 
 #ifdef DEBUG
--- a/layout/generic/nsImageFrame.h
+++ b/layout/generic/nsImageFrame.h
@@ -326,16 +326,17 @@ private:
   void InvalidateSelf(const nsIntRect* aLayerInvalidRect,
                       const nsRect* aFrameInvalidRect);
 
   RefPtr<nsImageMap> mImageMap;
 
   nsCOMPtr<imgINotificationObserver> mListener;
 
   nsCOMPtr<imgIContainer> mImage;
+  nsCOMPtr<imgIContainer> mPrevImage;
   nsSize mComputedSize;
   mozilla::IntrinsicSize mIntrinsicSize;
   nsSize mIntrinsicRatio;
 
   bool mDisplayingIcon;
   bool mFirstFrameComplete;
   bool mReflowCallbackPosted;
   bool mForceSyncDecoding;
@@ -406,18 +407,21 @@ public:
  * image itself, and hence receive events just as if the image itself
  * received events.
  */
 class nsDisplayImage : public nsDisplayImageContainer {
 public:
   typedef mozilla::layers::LayerManager LayerManager;
 
   nsDisplayImage(nsDisplayListBuilder* aBuilder, nsImageFrame* aFrame,
-                 imgIContainer* aImage)
-    : nsDisplayImageContainer(aBuilder, aFrame), mImage(aImage) {
+                 imgIContainer* aImage, imgIContainer* aPrevImage)
+    : nsDisplayImageContainer(aBuilder, aFrame)
+    , mImage(aImage)
+    , mPrevImage(aPrevImage)
+  {
     MOZ_COUNT_CTOR(nsDisplayImage);
   }
   virtual ~nsDisplayImage() {
     MOZ_COUNT_DTOR(nsDisplayImage);
   }
 
   virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
@@ -456,11 +460,12 @@ public:
 
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager,
                                              const ContainerLayerParameters& aContainerParameters) override;
 
   NS_DISPLAY_DECL_NAME("Image", TYPE_IMAGE)
 private:
   nsCOMPtr<imgIContainer> mImage;
+  nsCOMPtr<imgIContainer> mPrevImage;
 };
 
 #endif /* nsImageFrame_h___ */