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 109278 68dd2bcc51ee55e1ce3dd5e77d655e6e0cc60756
parent 109277 a2630fb2dbfa056a44106b59c84bfc7f9351a943
child 109279 6a76b682c6288b63dab4d77d36d5d60c8f21972b
push id23619
push useremorley@mozilla.com
push dateFri, 05 Oct 2012 10:54:02 +0000
treeherdermozilla-central@3b458f4e0f42 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjoe
bugs792199
milestone18.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 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