Bug 505385 - Part 9: Hoist decoder and container observer out of imgRequest into imgStatusTracker. r=joe

This commit is contained in:
Josh Matthews 2012-10-12 12:11:21 -04:00
parent d14b24720d
commit 575c6923d4
6 changed files with 362 additions and 325 deletions

View File

@ -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<imgRequestProxy*>::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<imgRequestProxy*>::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<imgRequestProxy*>::ForwardIterator iter(GetStatusTracker().GetConsumers());
while (iter.HasMore()) {
tracker.SendStartDecode(iter.GetNext());
}
if (!mIsMultiPartChannel) {
MOZ_ASSERT(!mBlockingOnload);
mBlockingOnload = true;
tracker.RecordBlockOnload();
nsTObserverArray<imgRequestProxy*>::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<imgRequestProxy*>::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<imgRequestProxy*>::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<imgRequestProxy*>::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<imgRequestProxy*>::ForwardIterator iter(GetStatusTracker().GetConsumers());
while (iter.HasMore()) {
tracker.SendStopFrame(iter.GetNext(), frame);
}
if (mBlockingOnload) {
mBlockingOnload = false;
tracker.RecordUnblockOnload();
nsTObserverArray<imgRequestProxy*>::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<imgRequestProxy*>::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<imgRequestProxy*>::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<imgRequestProxy*>::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<nsIObserverService> 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<imgRequestProxy*>::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<imgRequestProxy*>::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.

View File

@ -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

View File

@ -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<imgRequest> 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();
}

View File

@ -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<imgRequest> mOwner;
// Weak pointer to the status tracker.
imgStatusTracker* mStatusTracker;
// The URI of our request.
nsCOMPtr<nsIURI> 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

View File

@ -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<imgRequestProxy*>::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<imgRequestProxy*>::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<imgRequestProxy*>::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<imgRequestProxy*>::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<imgRequestProxy*>::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<imgRequestProxy*>::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<imgRequestProxy*>::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<imgRequestProxy*>::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<imgRequestProxy*>::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<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
nsCOMPtr<nsIURI> 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<imgRequestProxy*>::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<imgRequestProxy*>::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<imgRequestProxy*>::ForwardIterator iter(mConsumers);
while (iter.HasMore()) {
SendUnblockOnload(iter.GetNext());
}
}

View File

@ -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<nsIRunnable> 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<imgRequestProxy*> mConsumers;
nsRefPtr<imgStatusTrackerObserver> mTrackerObserver;
};
#endif