Bug 792199. Only decode visible images immediately when switching to a tab. r=joe
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Thu, 04 Oct 2012 16:02:15 -0400
changeset 115576 68dd2bcc51ee55e1ce3dd5e77d655e6e0cc60756
parent 115575 a2630fb2dbfa056a44106b59c84bfc7f9351a943
child 115577 6a76b682c6288b63dab4d77d36d5d60c8f21972b
push id1
push usersledru@mozilla.com
push dateThu, 04 Dec 2014 17:57:20 +0000
reviewersjoe
bugs792199
milestone18.0a1
Bug 792199. Only decode visible images immediately when switching to a tab. r=joe This creates a new method 'StartDecoding' which does a RequestDecode and some decoding of the image.
content/base/src/nsDocument.cpp
content/base/src/nsImageLoadingContent.cpp
content/svg/content/src/nsSVGFilters.cpp
image/public/imgIContainer.idl
image/public/imgIRequest.idl
image/src/RasterImage.cpp
image/src/RasterImage.h
image/src/VectorImage.cpp
image/src/VectorImage.h
image/src/imgRequest.cpp
image/src/imgRequest.h
image/src/imgRequestProxy.cpp
layout/style/nsStyleStruct.cpp
layout/xul/base/src/nsImageBoxFrame.cpp
layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
widget/cocoa/nsMenuItemIconX.mm
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -8386,17 +8386,17 @@ nsDocument::AddImage(imgIRequest* aImage
 
   nsresult rv = NS_OK;
 
   // If this is the first insertion and we're locking images, lock this image
   // too.
   if (oldCount == 0 && mLockingImages) {
     rv = aImage->LockImage();
     if (NS_SUCCEEDED(rv))
-      rv = aImage->RequestDecode();
+      rv = aImage->StartDecoding();
   }
 
   // If this is the first insertion and we're animating images, request
   // that this image be animated too.
   if (oldCount == 0 && mAnimatingImages) {
     nsresult rv2 = aImage->IncrementAnimationConsumers();
     rv = NS_SUCCEEDED(rv) ? rv2 : rv;
   }
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -263,17 +263,17 @@ nsImageLoadingContent::OnStopDecode(imgI
 
   // XXXkhuey should this be GetOurCurrentDoc?  Decoding if we're not in
   // the document seems silly.
   nsIDocument* doc = GetOurOwnerDoc();
   nsIPresShell* shell = doc ? doc->GetShell() : nullptr;
   if (shell && shell->IsVisible() &&
       (!shell->DidInitialize() || shell->IsPaintingSuppressed())) {
 
-    mCurrentRequest->RequestDecode();
+    mCurrentRequest->StartDecoding();
   }
 
   // Fire the appropriate DOM event.
   if (NS_SUCCEEDED(aStatus)) {
     FireEvent(NS_LITERAL_STRING("load"));
   } else {
     FireEvent(NS_LITERAL_STRING("error"));
   }
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -5784,17 +5784,17 @@ NS_IMETHODIMP
 nsSVGFEImageElement::OnStartContainer(imgIRequest *aRequest,
                                       imgIContainer *aContainer)
 {
   nsresult rv =
     nsImageLoadingContent::OnStartContainer(aRequest, aContainer);
 
   // Request a decode
   NS_ABORT_IF_FALSE(aContainer, "who sent the notification then?");
-  aContainer->RequestDecode();
+  aContainer->StartDecoding();
 
   // We have a size - invalidate
   Invalidate();
   return rv;
 }
 
 //----------------------------------------------------------------------
 // helper methods
--- a/image/public/imgIContainer.idl
+++ b/image/public/imgIContainer.idl
@@ -52,17 +52,17 @@ native gfxGraphicsFilter(gfxPattern::Gra
 /**
  * imgIContainer is the interface that represents an image. It allows
  * access to frames as Thebes surfaces, and permits users to extract subregions
  * as other imgIContainers. It also allows drawing of images on to Thebes
  * contexts.
  *
  * Internally, imgIContainer also manages animation of images.
  */
-[scriptable, uuid(ead94080-cfd6-4a3e-8353-bd45333061d2)]
+[scriptable, uuid(d6c58749-ceb6-4afe-ab72-ff3086433e1f)]
 interface imgIContainer : nsISupports
 {
   /**
    * The width of the container rectangle.  In the case of any error,
    * zero is returned, and an exception will be thrown.
    */
   readonly attribute int32_t width;
 
@@ -228,16 +228,21 @@ interface imgIContainer : nsISupports
    * the image will at some point fire off decode notifications. Calling draw(),
    * getFrame(), copyFrame(), or extractCurrentFrame() triggers the same
    * mechanism internally. Thus, if you want to be sure that the image will be
    * decoded but don't want to access it until then, you must call
    * requestDecode().
    */
   void requestDecode();
 
+  /*
+   * This is equivalent to requestDecode() but it also decodes some of the image.
+   */
+  [noscript] void startDecoding();
+
   /**
     * Increments the lock count on the image. An image will not be discarded
     * as long as the lock count is nonzero. Note that it is still possible for
     * the image to be undecoded if decode-on-draw is enabled and the image
     * was never drawn.
     *
     * Upon instantiation images have a lock count of zero.
     */
--- a/image/public/imgIRequest.idl
+++ b/image/public/imgIRequest.idl
@@ -141,16 +141,17 @@ interface imgIRequest : nsIRequest
    *
    * imgIContainer has a requestDecode() method, but callers may want to request
    * a decode before the container has necessarily been instantiated. Calling
    * requestDecode() on the imgIRequest simply forwards along the request if the
    * container already exists, or calls it once it gets OnStartContainer if the
    * container does not yet exist.
    */
   void requestDecode();
+  void startDecoding();
 
   /**
    * Locks an image. If the image does not exist yet, locks it once it becomes
    * available. The lock persists for the lifetime of the imgIRequest (until
    * unlockImage is called) even if the underlying image changes.
    *
    * If you don't call unlockImage() by the time this imgIRequest goes away, it
    * will be called for you automatically.
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -2513,24 +2513,38 @@ RasterImage::WantDecodedFrames()
   if (CanDiscard()) {
     NS_ABORT_IF_FALSE(DiscardingActive(),
                       "Decoded and discardable but discarding not activated!");
     rv = DiscardTracker::Reset(&mDiscardTrackerNode);
     CONTAINER_ENSURE_SUCCESS(rv);
   }
 
   // Request a decode (no-op if we're decoded)
-  return RequestDecode();
+  return StartDecoding();
 }
 
 //******************************************************************************
 /* void requestDecode() */
 NS_IMETHODIMP
 RasterImage::RequestDecode()
 {
+  return RequestDecodeCore(ASYNCHRONOUS);
+}
+
+/* void startDecode() */
+NS_IMETHODIMP
+RasterImage::StartDecoding()
+{
+  return RequestDecodeCore(SOMEWHAT_SYNCHRONOUS);
+}
+
+
+NS_IMETHODIMP
+RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
+{
   nsresult rv;
 
   if (mError)
     return NS_ERROR_FAILURE;
 
   // If we're fully decoded, we have nothing to do
   if (mDecoded)
     return NS_OK;
@@ -2579,17 +2593,17 @@ RasterImage::RequestDecode()
 
   // If we've read all the data we have, we're done
   if (mBytesDecoded == mSourceData.Length())
     return NS_OK;
 
   // If we can do decoding now, do so.  Small images will decode completely,
   // large images will decode a bit and post themselves to the event loop
   // to finish decoding.
-  if (!mDecoded && !mInDecoder && mHasSourceData) {
+  if (!mDecoded && !mInDecoder && mHasSourceData && aDecodeType == SOMEWHAT_SYNCHRONOUS) {
     SAMPLE_LABEL_PRINTF("RasterImage", "DecodeABitOf", "%s", GetURIString());
     DecodeWorker::Singleton()->DecodeABitOf(this);
     return NS_OK;
   }
 
   // If we get this far, dispatch the worker. We do this instead of starting
   // any immediate decoding to guarantee that all our decode notifications are
   // dispatched asynchronously, and to ensure we stay responsive.
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -158,17 +158,18 @@ public:
   NS_IMETHOD GetAnimated(bool *aAnimated);
   NS_IMETHOD GetCurrentFrameIsOpaque(bool *aCurrentFrameIsOpaque);
   NS_IMETHOD GetFrame(uint32_t aWhichFrame, uint32_t aFlags, gfxASurface **_retval);
   NS_IMETHOD GetImageContainer(mozilla::layers::ImageContainer **_retval);
   NS_IMETHOD CopyFrame(uint32_t aWhichFrame, uint32_t aFlags, gfxImageSurface **_retval);
   NS_IMETHOD ExtractFrame(uint32_t aWhichFrame, const nsIntRect & aRect, uint32_t aFlags, imgIContainer **_retval);
   NS_IMETHOD Draw(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter, const gfxMatrix & aUserSpaceToImageSpace, const gfxRect & aFill, const nsIntRect & aSubimage, const nsIntSize & aViewportSize, uint32_t aFlags);
   NS_IMETHOD_(nsIFrame *) GetRootLayoutFrame(void);
-  NS_IMETHOD RequestDecode(void);
+  NS_IMETHOD RequestDecode();
+  NS_IMETHOD StartDecoding();
   NS_IMETHOD LockImage(void);
   NS_IMETHOD UnlockImage(void);
   NS_IMETHOD RequestDiscard(void);
   NS_IMETHOD ResetAnimation(void);
   NS_IMETHOD_(void) RequestRefresh(const mozilla::TimeStamp& aTime);
   // END NS_DECL_IMGICONTAINER
 
   RasterImage(imgStatusTracker* aStatusTracker = nullptr);
@@ -683,16 +684,21 @@ private:
 
   bool ApplyDecodeFlags(uint32_t aNewFlags);
 
   already_AddRefed<layers::Image> GetCurrentImage();
   void UpdateImageContainer();
 
   void SetInUpdateImageContainer(bool aInUpdate) { mInUpdateImageContainer = aInUpdate; }
   bool IsInUpdateImageContainer() { return mInUpdateImageContainer; }
+  enum RequestDecodeType {
+      ASYNCHRONOUS,
+      SOMEWHAT_SYNCHRONOUS
+  };
+  NS_IMETHOD RequestDecodeCore(RequestDecodeType aDecodeType);
 
 private: // data
 
   nsIntSize                  mSize;
 
   // Whether mFrames below were decoded using any special flags.
   // Some flags (e.g. unpremultiplied data) may not be compatible
   // with the browser's needs for displaying the image to the user.
@@ -812,17 +818,17 @@ protected:
 class imgDecodeRequestor : public nsRunnable
 {
   public:
     imgDecodeRequestor(RasterImage &aContainer) {
       mContainer = aContainer.asWeakPtr();
     }
     NS_IMETHOD Run() {
       if (mContainer)
-        mContainer->RequestDecode();
+        mContainer->StartDecoding();
       return NS_OK;
     }
 
   private:
     WeakPtr<RasterImage> mContainer;
 };
 
 } // namespace image
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -570,16 +570,24 @@ VectorImage::GetRootLayoutFrame()
 /* void requestDecode() */
 NS_IMETHODIMP
 VectorImage::RequestDecode()
 {
   // Nothing to do for SVG images
   return NS_OK;
 }
 
+NS_IMETHODIMP
+VectorImage::StartDecoding()
+{
+  // Nothing to do for SVG images
+  return NS_OK;
+}
+
+
 //******************************************************************************
 /* void lockImage() */
 NS_IMETHODIMP
 VectorImage::LockImage()
 {
   // This method is for image-discarding, which only applies to RasterImages.
   return NS_OK;
 }
--- a/image/src/VectorImage.h
+++ b/image/src/VectorImage.h
@@ -40,17 +40,18 @@ public:
   NS_IMETHOD GetAnimated(bool *aAnimated);
   NS_IMETHOD GetCurrentFrameIsOpaque(bool *aCurrentFrameIsOpaque);
   NS_IMETHOD GetFrame(uint32_t aWhichFrame, uint32_t aFlags, gfxASurface **_retval);
   NS_IMETHOD GetImageContainer(mozilla::layers::ImageContainer **_retval) { *_retval = NULL; return NS_OK; }
   NS_IMETHOD CopyFrame(uint32_t aWhichFrame, uint32_t aFlags, gfxImageSurface **_retval);
   NS_IMETHOD ExtractFrame(uint32_t aWhichFrame, const nsIntRect & aRect, uint32_t aFlags, imgIContainer **_retval);
   NS_IMETHOD Draw(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter, const gfxMatrix & aUserSpaceToImageSpace, const gfxRect & aFill, const nsIntRect & aSubimage, const nsIntSize & aViewportSize, uint32_t aFlags);
   NS_IMETHOD_(nsIFrame *) GetRootLayoutFrame(void);
-  NS_IMETHOD RequestDecode(void);
+  NS_IMETHOD RequestDecode();
+  NS_IMETHOD StartDecoding();
   NS_IMETHOD LockImage(void);
   NS_IMETHOD UnlockImage(void);
   NS_IMETHOD RequestDiscard(void);
   NS_IMETHOD ResetAnimation(void);
   NS_IMETHOD_(void) RequestRefresh(const mozilla::TimeStamp& aTime);
   // END NS_DECL_IMGICONTAINER
 
   VectorImage(imgStatusTracker* aStatusTracker = nullptr);
--- a/image/src/imgRequest.cpp
+++ b/image/src/imgRequest.cpp
@@ -491,16 +491,32 @@ imgRequest::RequestDecode()
   }
 
   // Otherwise, flag to do it when we get the image
   mDecodeRequested = true;
 
   return NS_OK;
 }
 
+nsresult
+imgRequest::StartDecoding()
+{
+  // If we've initialized our image, we can request a decode.
+  if (mImage) {
+    return mImage->StartDecoding();
+  }
+
+  // Otherwise, flag to do it when we get the image
+  mDecodeRequested = true;
+
+  return NS_OK;
+}
+
+
+
 /** imgIContainerObserver methods **/
 
 /* [noscript] void frameChanged (in imgIRequest request,
                                  in imgIContainer container,
                                  in nsIntRect dirtyRect); */
 NS_IMETHODIMP imgRequest::FrameChanged(imgIRequest *request,
                                        imgIContainer *container,
                                        const nsIntRect *dirtyRect)
@@ -1142,17 +1158,17 @@ imgRequest::OnDataAvailable(nsIRequest *
             }
           }
         }
       }
 
       if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
         // If we were waiting on the image to do something, now's our chance.
         if (mDecodeRequested) {
-          mImage->RequestDecode();
+          mImage->StartDecoding();
         }
       } else { // mImage->GetType() == imgIContainer::TYPE_VECTOR
         nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
         NS_ABORT_IF_FALSE(imageAsStream,
                           "SVG-typed Image failed QI to nsIStreamListener");
         imageAsStream->OnStartRequest(aRequest, nullptr);
       }
     }
--- a/image/src/imgRequest.h
+++ b/image/src/imgRequest.h
@@ -76,16 +76,17 @@ public:
   // only when the channel has failed to open, and so calling Cancel() on it
   // won't be sufficient.
   void CancelAndAbort(nsresult aStatus);
 
   // Methods that get forwarded to the Image, or deferred until it's
   // instantiated.
   nsresult LockImage();
   nsresult UnlockImage();
+  nsresult StartDecoding();
   nsresult RequestDecode();
 
   inline void SetInnerWindowID(uint64_t aInnerWindowId) {
     mInnerWindowId = aInnerWindowId;
   }
 
   inline uint64_t InnerWindowID() const {
     return mInnerWindowId;
--- a/image/src/imgRequestProxy.cpp
+++ b/image/src/imgRequestProxy.cpp
@@ -172,17 +172,17 @@ nsresult imgRequestProxy::ChangeOwner(im
 
   mOwner = aNewOwner;
 
   mOwner->AddProxy(this);
 
   // If we were decoded, or if we'd previously requested a decode, request a
   // decode on the new image
   if (wasDecoded || mDecodeRequested)
-    mOwner->RequestDecode();
+    mOwner->StartDecoding();
 
   return NS_OK;
 }
 
 void imgRequestProxy::AddToLoadGroup()
 {
   NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!");
 
@@ -297,30 +297,45 @@ NS_IMETHODIMP imgRequestProxy::CancelAnd
     NS_DispatchToCurrentThread(ev);
   }
 
   NullOutListener();
 
   return NS_OK;
 }
 
+/* void startDecode (); */
+NS_IMETHODIMP
+imgRequestProxy::StartDecoding()
+{
+  if (!mOwner)
+    return NS_ERROR_FAILURE;
+
+  // Flag this, so we know to transfer the request if our owner changes
+  mDecodeRequested = true;
+
+  // Forward the request
+  return mOwner->StartDecoding();
+}
+
 /* void requestDecode (); */
 NS_IMETHODIMP
 imgRequestProxy::RequestDecode()
 {
   if (!mOwner)
     return NS_ERROR_FAILURE;
 
   // Flag this, so we know to transfer the request if our owner changes
   mDecodeRequested = true;
 
   // Forward the request
   return mOwner->RequestDecode();
 }
 
+
 /* void lockImage (); */
 NS_IMETHODIMP
 imgRequestProxy::LockImage()
 {
   mLockCount++;
   if (mImage)
     return mImage->LockImage();
   return NS_OK;
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1608,17 +1608,17 @@ nsStyleImage::ComputeActualCropRect(nsIn
     *aIsEntireImage = aActualCropRect.IsEqualInterior(imageRect);
   return true;
 }
 
 nsresult
 nsStyleImage::RequestDecode() const
 {
   if ((mType == eStyleImageType_Image) && mImage)
-    return mImage->RequestDecode();
+    return mImage->StartDecoding();
   return NS_OK;
 }
 
 bool
 nsStyleImage::IsOpaque() const
 {
   if (!IsComplete())
     return false;
--- a/layout/xul/base/src/nsImageBoxFrame.cpp
+++ b/layout/xul/base/src/nsImageBoxFrame.cpp
@@ -257,17 +257,17 @@ nsImageBoxFrame::UpdateImage()
     }
   }
 
   if (!mImageRequest) {
     // We have no image, so size to 0
     mIntrinsicSize.SizeTo(0, 0);
   } else {
     // We don't want discarding or decode-on-draw for xul images.
-    mImageRequest->RequestDecode();
+    mImageRequest->StartDecoding();
     mImageRequest->LockImage();
   }
 }
 
 void
 nsImageBoxFrame::UpdateLoadFlags()
 {
   static nsIContent::AttrValuesArray strings[] =
--- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
+++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
@@ -2177,17 +2177,17 @@ nsTreeBodyFrame::GetImage(int32_t aRowIn
       }
     }
     listener->UnsuppressInvalidation();
 
     if (!imageRequest)
       return NS_ERROR_FAILURE;
 
     // We don't want discarding/decode-on-draw for xul images
-    imageRequest->RequestDecode();
+    imageRequest->StartDecoding();
     imageRequest->LockImage();
 
     // In a case it was already cached.
     imageRequest->GetImage(aResult);
     nsTreeImageCacheEntry cacheEntry(imageRequest, imgDecoderObserver);
     mImageCache.Put(imageSrc, cacheEntry);
   }
   return NS_OK;
--- a/widget/cocoa/nsMenuItemIconX.mm
+++ b/widget/cocoa/nsMenuItemIconX.mm
@@ -306,17 +306,17 @@ nsMenuItemIconX::LoadIcon(nsIURI* aIconU
   // Passing in null for channelPolicy here since nsMenuItemIconX::LoadIcon is
   // not exposed to web content
   nsresult rv = loader->LoadImage(aIconURI, nullptr, nullptr, nullptr, loadGroup, this,
                                    nullptr, nsIRequest::LOAD_NORMAL, nullptr, nullptr,
                                    nullptr, getter_AddRefs(mIconRequest));
   if (NS_FAILED(rv)) return rv;
 
   // We need to request the icon be decoded (bug 573583, bug 705516).
-  mIconRequest->RequestDecode();
+  mIconRequest->StartDecoding();
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 //
 // imgIContainerObserver