diff --git a/image/src/imgRequest.cpp b/image/src/imgRequest.cpp index 3d45ae6e0af..95e91462104 100644 --- a/image/src/imgRequest.cpp +++ b/image/src/imgRequest.cpp @@ -71,10 +71,8 @@ InitPrefCaches() PRLogModuleInfo *gImgLog = PR_NewLogModule("imgRequest"); #endif -NS_IMPL_ISUPPORTS8(imgRequest, - imgIDecoderObserver, imgIContainerObserver, +NS_IMPL_ISUPPORTS5(imgRequest, nsIStreamListener, nsIRequestObserver, - nsISupportsWeakReference, nsIChannelEventSink, nsIInterfaceRequestor, nsIAsyncVerifyRedirectCallback) @@ -90,7 +88,6 @@ imgRequest::imgRequest(imgLoader* aLoader) , mIsMultiPartChannel(false) , mGotData(false) , mIsInCache(false) - , mBlockingOnload(false) , mResniffMimeType(false) { // Register our pref observers if we haven't yet. @@ -175,6 +172,13 @@ bool imgRequest::HasCacheEntry() const return mCacheEntry != nullptr; } +void imgRequest::ResetCacheEntry() +{ + if (HasCacheEntry()) { + mCacheEntry->SetDataSize(0); + } +} + void imgRequest::AddProxy(imgRequestProxy *proxy) { NS_PRECONDITION(proxy, "null imgRequestProxy passed in"); @@ -270,16 +274,7 @@ void imgRequest::Cancel(nsresult aStatus) imgStatusTracker& statusTracker = GetStatusTracker(); - if (mBlockingOnload) { - mBlockingOnload = false; - - statusTracker.RecordUnblockOnload(); - - nsTObserverArray::ForwardIterator iter(GetStatusTracker().GetConsumers()); - while (iter.HasMore()) { - statusTracker.SendUnblockOnload(iter.GetNext()); - } - } + statusTracker.MaybeUnblockOnload(); statusTracker.RecordCancel(); @@ -477,289 +472,6 @@ imgRequest::StartDecoding() 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) -{ - LOG_SCOPE(gImgLog, "imgRequest::FrameChanged"); - NS_ABORT_IF_FALSE(mImage, - "FrameChanged callback before we've created our image"); - - mImage->GetStatusTracker().RecordFrameChanged(container, dirtyRect); - - nsTObserverArray::ForwardIterator iter(GetStatusTracker().GetConsumers()); - while (iter.HasMore()) { - mImage->GetStatusTracker().SendFrameChanged(iter.GetNext(), container, dirtyRect); - } - - return NS_OK; -} - -/** imgIDecoderObserver methods **/ - -/* void onStartDecode (in imgIRequest request); */ -NS_IMETHODIMP imgRequest::OnStartDecode(imgIRequest *request) -{ - LOG_SCOPE(gImgLog, "imgRequest::OnStartDecode"); - NS_ABORT_IF_FALSE(mImage, - "OnStartDecode callback before we've created our image"); - - - imgStatusTracker& tracker = mImage->GetStatusTracker(); - tracker.RecordStartDecode(); - - nsTObserverArray::ForwardIterator iter(GetStatusTracker().GetConsumers()); - while (iter.HasMore()) { - tracker.SendStartDecode(iter.GetNext()); - } - - if (!mIsMultiPartChannel) { - MOZ_ASSERT(!mBlockingOnload); - mBlockingOnload = true; - - tracker.RecordBlockOnload(); - - nsTObserverArray::ForwardIterator iter(GetStatusTracker().GetConsumers()); - while (iter.HasMore()) { - tracker.SendBlockOnload(iter.GetNext()); - } - } - - /* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which - indicates the beginning of a new decode. - The cache entry's size therefore needs to be reset to 0 here. If we do not do this, - the code in imgRequest::OnStopFrame will continue to increase the data size cumulatively. - */ - if (mCacheEntry) - mCacheEntry->SetDataSize(0); - - return NS_OK; -} - -NS_IMETHODIMP imgRequest::OnStartRequest(imgIRequest *aRequest) -{ - NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStartRequest"); - return NS_OK; -} - -/* void onStartContainer (in imgIRequest request, in imgIContainer image); */ -NS_IMETHODIMP imgRequest::OnStartContainer(imgIRequest *request, imgIContainer *image) -{ - LOG_SCOPE(gImgLog, "imgRequest::OnStartContainer"); - - NS_ASSERTION(image, "imgRequest::OnStartContainer called with a null image!"); - if (!image) return NS_ERROR_UNEXPECTED; - - NS_ABORT_IF_FALSE(mImage, - "OnStartContainer callback before we've created our image"); - NS_ABORT_IF_FALSE(image == mImage, - "OnStartContainer callback from an image we don't own"); - mImage->GetStatusTracker().RecordStartContainer(image); - - nsTObserverArray::ForwardIterator iter(GetStatusTracker().GetConsumers()); - while (iter.HasMore()) { - mImage->GetStatusTracker().SendStartContainer(iter.GetNext(), image); - } - - return NS_OK; -} - -/* void onStartFrame (in imgIRequest request, in unsigned long frame); */ -NS_IMETHODIMP imgRequest::OnStartFrame(imgIRequest *request, - uint32_t frame) -{ - LOG_SCOPE(gImgLog, "imgRequest::OnStartFrame"); - NS_ABORT_IF_FALSE(mImage, - "OnStartFrame callback before we've created our image"); - - mImage->GetStatusTracker().RecordStartFrame(frame); - - nsTObserverArray::ForwardIterator iter(GetStatusTracker().GetConsumers()); - while (iter.HasMore()) { - mImage->GetStatusTracker().SendStartFrame(iter.GetNext(), frame); - } - - return NS_OK; -} - -/* [noscript] void onDataAvailable (in imgIRequest request, in boolean aCurrentFrame, [const] in nsIntRect rect); */ -NS_IMETHODIMP imgRequest::OnDataAvailable(imgIRequest *request, - bool aCurrentFrame, - const nsIntRect * rect) -{ - LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable"); - NS_ABORT_IF_FALSE(mImage, - "OnDataAvailable callback before we've created our image"); - - mImage->GetStatusTracker().RecordDataAvailable(aCurrentFrame, rect); - - nsTObserverArray::ForwardIterator iter(GetStatusTracker().GetConsumers()); - while (iter.HasMore()) { - mImage->GetStatusTracker().SendDataAvailable(iter.GetNext(), aCurrentFrame, rect); - } - - return NS_OK; -} - -/* void onStopFrame (in imgIRequest request, in unsigned long frame); */ -NS_IMETHODIMP imgRequest::OnStopFrame(imgIRequest *request, - uint32_t frame) -{ - LOG_SCOPE(gImgLog, "imgRequest::OnStopFrame"); - NS_ABORT_IF_FALSE(mImage, - "OnStopFrame callback before we've created our image"); - - imgStatusTracker& tracker = mImage->GetStatusTracker(); - tracker.RecordStopFrame(frame); - - nsTObserverArray::ForwardIterator iter(GetStatusTracker().GetConsumers()); - while (iter.HasMore()) { - tracker.SendStopFrame(iter.GetNext(), frame); - } - - if (mBlockingOnload) { - mBlockingOnload = false; - - tracker.RecordUnblockOnload(); - - nsTObserverArray::ForwardIterator iter(GetStatusTracker().GetConsumers()); - while (iter.HasMore()) { - tracker.SendUnblockOnload(iter.GetNext()); - } - } - - return NS_OK; -} - -/* void onStopContainer (in imgIRequest request, in imgIContainer image); */ -NS_IMETHODIMP imgRequest::OnStopContainer(imgIRequest *request, - imgIContainer *image) -{ - LOG_SCOPE(gImgLog, "imgRequest::OnStopContainer"); - NS_ABORT_IF_FALSE(mImage, - "OnDataContainer callback before we've created our image"); - - imgStatusTracker& tracker = mImage->GetStatusTracker(); - tracker.RecordStopContainer(image); - - nsTObserverArray::ForwardIterator iter(GetStatusTracker().GetConsumers()); - while (iter.HasMore()) { - tracker.SendStopContainer(iter.GetNext(), image); - } - - // This is really hacky. We need to handle the case where we start decoding, - // block onload, but then hit an error before we get to our first frame. In - // theory we would just hook in at OnStopDecode, but OnStopDecode is broken - // until we fix bug 505385. OnStopContainer is actually going away at that - // point. So for now we take advantage of the fact that OnStopContainer is - // always fired in the decoders at the same time as OnStopDecode. - if (mBlockingOnload) { - mBlockingOnload = false; - - tracker.RecordUnblockOnload(); - - nsTObserverArray::ForwardIterator iter(GetStatusTracker().GetConsumers()); - while (iter.HasMore()) { - tracker.SendUnblockOnload(iter.GetNext()); - } - } - - return NS_OK; -} - -/* void onStopDecode (in imgIRequest request, in nsresult status, in wstring statusArg); */ -NS_IMETHODIMP imgRequest::OnStopDecode(imgIRequest *aRequest, - nsresult aStatus, - const PRUnichar *aStatusArg) -{ - LOG_SCOPE(gImgLog, "imgRequest::OnStopDecode"); - NS_ABORT_IF_FALSE(mImage, - "OnDataDecode callback before we've created our image"); - - // We finished the decode, and thus have the decoded frames. Update the cache - // entry size to take this into account. - UpdateCacheEntrySize(); - - mImage->GetStatusTracker().RecordStopDecode(aStatus, aStatusArg); - - nsTObserverArray::ForwardIterator iter(GetStatusTracker().GetConsumers()); - while (iter.HasMore()) { - mImage->GetStatusTracker().SendStopDecode(iter.GetNext(), aStatus, - aStatusArg); - } - - if (NS_FAILED(aStatus)) { - // Some kind of problem has happened with image decoding. - // Report the URI to net:failed-to-process-uri-conent observers. - - nsCOMPtr os = mozilla::services::GetObserverService(); - if (os) - os->NotifyObservers(mURI, "net:failed-to-process-uri-content", nullptr); - } - - // RasterImage and everything below it is completely correct and - // bulletproof about its handling of decoder notifications. - // Unfortunately, here and above we have to make some gross and - // inappropriate use of things to get things to work without - // completely overhauling the decoder observer interface (this will, - // thankfully, happen in bug 505385). From imgRequest and above (for - // the time being), OnStopDecode is just a companion to OnStopRequest - // that signals success or failure of the _load_ (not the _decode_). - // Within imgStatusTracker, we ignore OnStopDecode notifications from the - // decoder and RasterImage and generate our own every time we send - // OnStopRequest. From within SendStopDecode, we actually send - // OnStopContainer. For more information, see bug 435296. - - return NS_OK; -} - -NS_IMETHODIMP imgRequest::OnStopRequest(imgIRequest *aRequest, - bool aLastPart) -{ - NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStopRequest"); - return NS_OK; -} - -/* void onDiscard (in imgIRequest request); */ -NS_IMETHODIMP imgRequest::OnDiscard(imgIRequest *aRequest) -{ - NS_ABORT_IF_FALSE(mImage, - "OnDiscard callback before we've created our image"); - - mImage->GetStatusTracker().RecordDiscard(); - - // Update the cache entry size, since we just got rid of frame data - UpdateCacheEntrySize(); - - nsTObserverArray::ForwardIterator iter(GetStatusTracker().GetConsumers()); - while (iter.HasMore()) { - mImage->GetStatusTracker().SendDiscard(iter.GetNext()); - } - - return NS_OK; -} - -NS_IMETHODIMP imgRequest::OnImageIsAnimated(imgIRequest *aRequest) -{ - NS_ABORT_IF_FALSE(mImage, - "OnImageIsAnimated callback before we've created our image"); - mImage->GetStatusTracker().RecordImageIsAnimated(); - - nsTObserverArray::ForwardIterator iter(GetStatusTracker().GetConsumers()); - while (iter.HasMore()) { - mImage->GetStatusTracker().SendImageIsAnimated(iter.GetNext()); - } - - return NS_OK; -} - /** nsIRequestObserver methods **/ /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */ @@ -1082,7 +794,8 @@ imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, // Initialize the image that we created above. For RasterImages, this // instantiates a decoder behind the scenes, so if we don't have a decoder // for this mimetype we'll find out about it here. - rv = mImage->Init(this, mContentType.get(), uriString.get(), imageFlags); + rv = mImage->Init(GetStatusTracker().GetDecoderObserver(), + mContentType.get(), uriString.get(), imageFlags); // We allow multipart images to fail to initialize without cancelling the // load because subsequent images might be fine. diff --git a/image/src/imgRequest.h b/image/src/imgRequest.h index 0f594d57779..f4fc28509aa 100644 --- a/image/src/imgRequest.h +++ b/image/src/imgRequest.h @@ -7,8 +7,6 @@ #ifndef imgRequest_h__ #define imgRequest_h__ -#include "imgIDecoderObserver.h" - #include "nsIChannelEventSink.h" #include "nsIContentSniffer.h" #include "nsIInterfaceRequestor.h" @@ -22,7 +20,6 @@ #include "nsCategoryCache.h" #include "nsCOMPtr.h" #include "nsStringGlue.h" -#include "nsWeakReference.h" #include "nsError.h" #include "imgIRequest.h" #include "nsIAsyncVerifyRedirectCallback.h" @@ -41,9 +38,7 @@ class Image; } // namespace image } // namespace mozilla -class imgRequest : public imgIDecoderObserver, - public nsIStreamListener, - public nsSupportsWeakReference, +class imgRequest : public nsIStreamListener, public nsIChannelEventSink, public nsIInterfaceRequestor, public nsIAsyncVerifyRedirectCallback @@ -117,6 +112,14 @@ public: // Get the current principal of the image. No AddRefing. inline nsIPrincipal* GetPrincipal() const { return mPrincipal.get(); }; + // Resize the cache entry to 0 if it exists + void ResetCacheEntry(); + + // Update the cache entry size based on the image container + void UpdateCacheEntrySize(); + + nsresult GetURI(nsIURI **aURI); + private: friend class imgCacheEntry; friend class imgRequestProxy; @@ -132,7 +135,6 @@ private: void Cancel(nsresult aStatus); void RemoveFromCache(); - nsresult GetURI(nsIURI **aURI); nsresult GetSecurityInfo(nsISupports **aSecurityInfo); inline const char *GetMimeType() const { @@ -166,12 +168,10 @@ private: // try to update or modify the image cache. void SetIsInCache(bool cacheable); - // Update the cache entry size based on the image container - void UpdateCacheEntrySize(); + bool IsBlockingOnload() const; + void SetBlockingOnload(bool block) const; public: - NS_DECL_IMGIDECODEROBSERVER - NS_DECL_IMGICONTAINEROBSERVER NS_DECL_NSISTREAMLISTENER NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSICHANNELEVENTSINK diff --git a/image/src/imgRequestProxy.cpp b/image/src/imgRequestProxy.cpp index 8aa3303f1a1..d07eaf93fd6 100644 --- a/image/src/imgRequestProxy.cpp +++ b/image/src/imgRequestProxy.cpp @@ -98,10 +98,8 @@ nsresult imgRequestProxy::Init(imgStatusTracker* aStatusTracker, NS_ABORT_IF_FALSE(mAnimationConsumers == 0, "Cannot have animation before Init"); - mStatusTracker = aStatusTracker; mOwner = aStatusTracker->GetRequest(); mOwnerHasImage = !!aStatusTracker->GetImage(); - MOZ_ASSERT_IF(mOwner, mStatusTracker == &mOwner->GetStatusTracker()); mListener = aObserver; // Make sure to addref mListener before the AddProxy call below, since // that call might well want to release it if the imgRequest has @@ -136,7 +134,6 @@ nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner) nsRefPtr oldOwner = mOwner; mOwner = aNewOwner; - mStatusTracker = &aNewOwner->GetStatusTracker(); // If we were locked, apply the locks here for (uint32_t i = 0; i < oldLockCount; i++) @@ -501,7 +498,7 @@ NS_IMETHODIMP imgRequestProxy::Clone(imgIDecoderObserver* aObserver, // XXXldb That's not true anymore. Stuff from imgLoader adds the // request to the loadgroup. clone->SetLoadFlags(mLoadFlags); - nsresult rv = clone->Init(mStatusTracker, mLoadGroup, mURI, aObserver); + nsresult rv = clone->Init(&mOwner->GetStatusTracker(), mLoadGroup, mURI, aObserver); if (NS_FAILED(rv)) return rv; @@ -910,7 +907,7 @@ imgRequestProxy::GetStatusTracker() const // That's why this method uses mOwner->GetStatusTracker() instead of just // mOwner->mStatusTracker -- we might have a null mImage and yet have an // mOwner with a non-null mImage (and a null mStatusTracker pointer). - return *mStatusTracker; + return mOwner->GetStatusTracker(); } mozilla::image::Image* @@ -938,3 +935,9 @@ imgRequestProxyStatic::GetImage() const { return mImage; } + +imgStatusTracker& +imgRequestProxyStatic::GetStatusTracker() const +{ + return mImage->GetStatusTracker(); +} diff --git a/image/src/imgRequestProxy.h b/image/src/imgRequestProxy.h index a809e90045a..363e1ee0df3 100644 --- a/image/src/imgRequestProxy.h +++ b/image/src/imgRequestProxy.h @@ -168,7 +168,7 @@ protected: // live either on mOwner or mImage, depending on whether // (a) we have an mOwner at all // (b) whether mOwner has instantiated its image yet - imgStatusTracker& GetStatusTracker() const; + virtual imgStatusTracker& GetStatusTracker() const; nsITimedChannel* TimedChannel() { @@ -193,9 +193,6 @@ private: // means that imgRequest::mObservers will not have any stale pointers in it. nsRefPtr mOwner; - // Weak pointer to the status tracker. - imgStatusTracker* mStatusTracker; - // The URI of our request. nsCOMPtr mURI; @@ -240,6 +237,7 @@ public: }; NS_IMETHOD GetImagePrincipal(nsIPrincipal** aPrincipal); + virtual imgStatusTracker& GetStatusTracker() const MOZ_OVERRIDE; protected: // Our image. We have to hold a strong reference here, because that's normally diff --git a/image/src/imgStatusTracker.cpp b/image/src/imgStatusTracker.cpp index f649e9f1a1d..3cba948591f 100644 --- a/image/src/imgStatusTracker.cpp +++ b/image/src/imgStatusTracker.cpp @@ -12,12 +12,282 @@ #include "Image.h" #include "ImageLogging.h" #include "RasterImage.h" +#include "nsIObserverService.h" #include "mozilla/Util.h" #include "mozilla/Assertions.h" +#include "mozilla/Services.h" using namespace mozilla::image; +NS_IMPL_ISUPPORTS3(imgStatusTrackerObserver, + imgIDecoderObserver, + imgIContainerObserver, + nsISupportsWeakReference) + +/** imgIContainerObserver methods **/ + +/* [noscript] void frameChanged (in imgIRequest request, + in imgIContainer container, + in nsIntRect dirtyRect); */ +NS_IMETHODIMP imgStatusTrackerObserver::FrameChanged(imgIRequest *request, + imgIContainer *container, + const nsIntRect *dirtyRect) +{ + LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::FrameChanged"); + NS_ABORT_IF_FALSE(mTracker->GetImage(), + "FrameChanged callback before we've created our image"); + + mTracker->RecordFrameChanged(container, dirtyRect); + + nsTObserverArray::ForwardIterator iter(mTracker->GetConsumers()); + while (iter.HasMore()) { + mTracker->SendFrameChanged(iter.GetNext(), container, dirtyRect); + } + + return NS_OK; +} + +/** imgIDecoderObserver methods **/ + +/* void onStartDecode (in imgIRequest request); */ +NS_IMETHODIMP imgStatusTrackerObserver::OnStartDecode(imgIRequest *request) +{ + LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnStartDecode"); + NS_ABORT_IF_FALSE(mTracker->GetImage(), + "OnStartDecode callback before we've created our image"); + + + mTracker->RecordStartDecode(); + + nsTObserverArray::ForwardIterator iter(mTracker->GetConsumers()); + while (iter.HasMore()) { + mTracker->SendStartDecode(iter.GetNext()); + } + + if (!mTracker->GetRequest()->GetMultipart()) { + MOZ_ASSERT(!mTracker->mBlockingOnload); + mTracker->mBlockingOnload = true; + + mTracker->RecordBlockOnload(); + + nsTObserverArray::ForwardIterator iter(mTracker->GetConsumers()); + while (iter.HasMore()) { + mTracker->SendBlockOnload(iter.GetNext()); + } + } + + /* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which + indicates the beginning of a new decode. + The cache entry's size therefore needs to be reset to 0 here. If we do not do this, + the code in imgStatusTrackerObserver::OnStopFrame will continue to increase the data size cumulatively. + */ + mTracker->GetRequest()->ResetCacheEntry(); + + return NS_OK; +} + +NS_IMETHODIMP imgStatusTrackerObserver::OnStartRequest(imgIRequest *aRequest) +{ + NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStartRequest"); + return NS_OK; +} + +/* void onStartContainer (in imgIRequest request, in imgIContainer image); */ +NS_IMETHODIMP imgStatusTrackerObserver::OnStartContainer(imgIRequest *request, imgIContainer *image) +{ + LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnStartContainer"); + + NS_ASSERTION(image, "imgStatusTrackerObserver::OnStartContainer called with a null image!"); + if (!image) return NS_ERROR_UNEXPECTED; + + NS_ABORT_IF_FALSE(mTracker->GetImage(), + "OnStartContainer callback before we've created our image"); + NS_ABORT_IF_FALSE(image == mTracker->GetImage(), + "OnStartContainer callback from an image we don't own"); + mTracker->RecordStartContainer(image); + + nsTObserverArray::ForwardIterator iter(mTracker->GetConsumers()); + while (iter.HasMore()) { + mTracker->SendStartContainer(iter.GetNext(), image); + } + + return NS_OK; +} + +/* void onStartFrame (in imgIRequest request, in unsigned long frame); */ +NS_IMETHODIMP imgStatusTrackerObserver::OnStartFrame(imgIRequest *request, + uint32_t frame) +{ + LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnStartFrame"); + NS_ABORT_IF_FALSE(mTracker->GetImage(), + "OnStartFrame callback before we've created our image"); + + mTracker->RecordStartFrame(frame); + + nsTObserverArray::ForwardIterator iter(mTracker->GetConsumers()); + while (iter.HasMore()) { + mTracker->SendStartFrame(iter.GetNext(), frame); + } + + return NS_OK; +} + +/* [noscript] void onDataAvailable (in imgIRequest request, in boolean aCurrentFrame, [const] in nsIntRect rect); */ +NS_IMETHODIMP imgStatusTrackerObserver::OnDataAvailable(imgIRequest *request, + bool aCurrentFrame, + const nsIntRect * rect) +{ + LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnDataAvailable"); + NS_ABORT_IF_FALSE(mTracker->GetImage(), + "OnDataAvailable callback before we've created our image"); + + mTracker->RecordDataAvailable(aCurrentFrame, rect); + + nsTObserverArray::ForwardIterator iter(mTracker->GetConsumers()); + while (iter.HasMore()) { + mTracker->SendDataAvailable(iter.GetNext(), aCurrentFrame, rect); + } + + return NS_OK; +} + +/* void onStopFrame (in imgIRequest request, in unsigned long frame); */ +NS_IMETHODIMP imgStatusTrackerObserver::OnStopFrame(imgIRequest *request, + uint32_t frame) +{ + LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnStopFrame"); + NS_ABORT_IF_FALSE(mTracker->GetImage(), + "OnStopFrame callback before we've created our image"); + + mTracker->RecordStopFrame(frame); + + nsTObserverArray::ForwardIterator iter(mTracker->GetConsumers()); + while (iter.HasMore()) { + mTracker->SendStopFrame(iter.GetNext(), frame); + } + + mTracker->MaybeUnblockOnload(); + + return NS_OK; +} + +/* void onStopContainer (in imgIRequest request, in imgIContainer image); */ +NS_IMETHODIMP imgStatusTrackerObserver::OnStopContainer(imgIRequest *request, + imgIContainer *image) +{ + LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnStopContainer"); + NS_ABORT_IF_FALSE(mTracker->GetImage(), + "OnDataContainer callback before we've created our image"); + + mTracker->RecordStopContainer(image); + + nsTObserverArray::ForwardIterator iter(mTracker->GetConsumers()); + while (iter.HasMore()) { + mTracker->SendStopContainer(iter.GetNext(), image); + } + + // This is really hacky. We need to handle the case where we start decoding, + // block onload, but then hit an error before we get to our first frame. In + // theory we would just hook in at OnStopDecode, but OnStopDecode is broken + // until we fix bug 505385. OnStopContainer is actually going away at that + // point. So for now we take advantage of the fact that OnStopContainer is + // always fired in the decoders at the same time as OnStopDecode. + mTracker->MaybeUnblockOnload(); + + return NS_OK; +} + +/* void onStopDecode (in imgIRequest request, in nsresult status, in wstring statusArg); */ +NS_IMETHODIMP imgStatusTrackerObserver::OnStopDecode(imgIRequest *aRequest, + nsresult aStatus, + const PRUnichar *aStatusArg) +{ + LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnStopDecode"); + NS_ABORT_IF_FALSE(mTracker->GetImage(), + "OnDataDecode callback before we've created our image"); + + // We finished the decode, and thus have the decoded frames. Update the cache + // entry size to take this into account. + mTracker->GetRequest()->UpdateCacheEntrySize(); + + mTracker->RecordStopDecode(aStatus, aStatusArg); + + nsTObserverArray::ForwardIterator iter(mTracker->GetConsumers()); + while (iter.HasMore()) { + mTracker->SendStopDecode(iter.GetNext(), aStatus, aStatusArg); + } + + if (NS_FAILED(aStatus)) { + // Some kind of problem has happened with image decoding. + // Report the URI to net:failed-to-process-uri-conent observers. + + nsCOMPtr os = mozilla::services::GetObserverService(); + if (os) { + nsCOMPtr uri; + mTracker->GetRequest()->GetURI(getter_AddRefs(uri)); + os->NotifyObservers(uri, "net:failed-to-process-uri-content", nullptr); + } + } + + // RasterImage and everything below it is completely correct and + // bulletproof about its handling of decoder notifications. + // Unfortunately, here and above we have to make some gross and + // inappropriate use of things to get things to work without + // completely overhauling the decoder observer interface (this will, + // thankfully, happen in bug 505385). From imgRequest and above (for + // the time being), OnStopDecode is just a companion to OnStopRequest + // that signals success or failure of the _load_ (not the _decode_). + // Within imgStatusTracker, we ignore OnStopDecode notifications from the + // decoder and RasterImage and generate our own every time we send + // OnStopRequest. From within SendStopDecode, we actually send + // OnStopContainer. For more information, see bug 435296. + + return NS_OK; +} + +NS_IMETHODIMP imgStatusTrackerObserver::OnStopRequest(imgIRequest *aRequest, + bool aLastPart) +{ + NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStopRequest"); + return NS_OK; +} + +/* void onDiscard (in imgIRequest request); */ +NS_IMETHODIMP imgStatusTrackerObserver::OnDiscard(imgIRequest *aRequest) +{ + NS_ABORT_IF_FALSE(mTracker->GetImage(), + "OnDiscard callback before we've created our image"); + + mTracker->RecordDiscard(); + + // Update the cache entry size, since we just got rid of frame data + mTracker->GetRequest()->UpdateCacheEntrySize(); + + nsTObserverArray::ForwardIterator iter(mTracker->GetConsumers()); + while (iter.HasMore()) { + mTracker->SendDiscard(iter.GetNext()); + } + + return NS_OK; +} + +NS_IMETHODIMP imgStatusTrackerObserver::OnImageIsAnimated(imgIRequest *aRequest) +{ + NS_ABORT_IF_FALSE(mTracker->GetImage(), + "OnImageIsAnimated callback before we've created our image"); + mTracker->RecordImageIsAnimated(); + + nsTObserverArray::ForwardIterator iter(mTracker->GetConsumers()); + while (iter.HasMore()) { + mTracker->SendImageIsAnimated(iter.GetNext()); + } + + return NS_OK; +} + +// imgStatusTracker methods + static nsresult GetResultFromImageStatus(uint32_t aStatus) { @@ -33,7 +303,9 @@ imgStatusTracker::imgStatusTracker(Image* aImage, imgRequest* aRequest) mRequest(aRequest), mState(0), mImageStatus(imgIRequest::STATUS_NONE), - mHadLastPart(false) + mHadLastPart(false), + mBlockingOnload(false), + mTrackerObserver(new imgStatusTrackerObserver(this)) {} imgStatusTracker::imgStatusTracker(const imgStatusTracker& aOther) @@ -41,7 +313,8 @@ imgStatusTracker::imgStatusTracker(const imgStatusTracker& aOther) mRequest(aOther.mRequest), mState(aOther.mState), mImageStatus(aOther.mImageStatus), - mHadLastPart(aOther.mHadLastPart) + mHadLastPart(aOther.mHadLastPart), + mBlockingOnload(aOther.mBlockingOnload) // Note: we explicitly don't copy mRequestRunnable, because it won't be // nulled out when the mRequestRunnable's Run function eventually gets // called. @@ -240,7 +513,7 @@ imgStatusTracker::SyncNotify(imgRequestProxy* proxy) } } - // See bug 505385 and imgRequest::OnStopDecode for more information on why we + // See bug 505385 and imgStatusTrackerObserver::OnStopDecode for more information on why we // call OnStopContainer based on stateDecodeStopped, and why OnStopDecode is // called with OnStopRequest. if (mState & stateDecodeStopped) { @@ -400,13 +673,13 @@ imgStatusTracker::RecordStopContainer(imgIContainer* aContainer) { NS_ABORT_IF_FALSE(mImage, "RecordStopContainer called before we have an Image"); - // No-op: see imgRequest::OnStopDecode for more information + // No-op: see imgStatusTrackerObserver::OnStopDecode for more information } void imgStatusTracker::SendStopContainer(imgRequestProxy* aProxy, imgIContainer* aContainer) { - // No-op: see imgRequest::OnStopDecode for more information + // No-op: see imgStatusTrackerObserver::OnStopDecode for more information } void @@ -427,7 +700,7 @@ void imgStatusTracker::SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus, const PRUnichar* statusArg) { - // See imgRequest::OnStopDecode for more information on why we call + // See imgStatusTrackerObserver::OnStopDecode for more information on why we call // OnStopContainer from here this, and why imgRequestProxy::OnStopDecode() is // called from OnStopRequest() and SyncNotify(). if (!aProxy->NotificationsDeferred()) @@ -532,7 +805,7 @@ imgStatusTracker::RecordStopRequest(bool aLastPart, nsresult aStatus) void imgStatusTracker::SendStopRequest(imgRequestProxy* aProxy, bool aLastPart, nsresult aStatus) { - // See bug 505385 and imgRequest::OnStopDecode for more information on why + // See bug 505385 and imgStatusTrackerObserver::OnStopDecode for more information on why // OnStopDecode is called with OnStopRequest. if (!aProxy->NotificationsDeferred()) { aProxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), nullptr); @@ -569,3 +842,20 @@ imgStatusTracker::SendUnblockOnload(imgRequestProxy* aProxy) aProxy->UnblockOnload(); } } + +void +imgStatusTracker::MaybeUnblockOnload() +{ + if (!mBlockingOnload) { + return; + } + + mBlockingOnload = false; + + RecordUnblockOnload(); + + nsTObserverArray::ForwardIterator iter(mConsumers); + while (iter.HasMore()) { + SendUnblockOnload(iter.GetNext()); + } +} diff --git a/image/src/imgStatusTracker.h b/image/src/imgStatusTracker.h index 311fadc2bbe..806933587af 100644 --- a/image/src/imgStatusTracker.h +++ b/image/src/imgStatusTracker.h @@ -12,6 +12,7 @@ class imgRequest; class imgRequestProxy; class imgStatusNotifyRunnable; class imgRequestNotifyRunnable; +class imgStatusTracker; struct nsIntRect; namespace mozilla { namespace image { @@ -21,9 +22,12 @@ class Image; #include "nsCOMPtr.h" +#include "nsAutoPtr.h" #include "nsTObserverArray.h" #include "nsIRunnable.h" #include "nscore.h" +#include "nsWeakReference.h" +#include "imgIDecoderObserver.h" enum { stateRequestStarted = PR_BIT(0), @@ -35,6 +39,27 @@ enum { stateBlockingOnload = PR_BIT(6) }; +class imgStatusTrackerObserver : public imgIDecoderObserver, + public nsSupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_IMGIDECODEROBSERVER + NS_DECL_IMGICONTAINEROBSERVER + + imgStatusTrackerObserver(imgStatusTracker* aTracker) + : mTracker(aTracker) {} + + virtual ~imgStatusTrackerObserver() {} + + void SetTracker(imgStatusTracker* aTracker) { + mTracker = aTracker; + } + +private: + imgStatusTracker* mTracker; +}; + /* * The image status tracker is a class that encapsulates all the loading and * decoding status about an Image, and makes it possible to send notifications @@ -167,13 +192,18 @@ public: void RecordUnblockOnload(); void SendUnblockOnload(imgRequestProxy* aProxy); + void MaybeUnblockOnload(); + // Weak pointer getters - no AddRefs. inline mozilla::image::Image* GetImage() const { return mImage; }; inline imgRequest* GetRequest() const { return mRequest; }; + inline imgIDecoderObserver* GetDecoderObserver() { return mTrackerObserver.get(); } + private: friend class imgStatusNotifyRunnable; friend class imgRequestNotifyRunnable; + friend class imgStatusTrackerObserver; nsCOMPtr mRequestRunnable; @@ -184,10 +214,13 @@ private: uint32_t mState; uint32_t mImageStatus; bool mHadLastPart; + bool mBlockingOnload; // List of proxies attached to the image. Each proxy represents a consumer // using the image. nsTObserverArray mConsumers; + + nsRefPtr mTrackerObserver; }; #endif