Bug 572520: step 6, centralize the tracking of load/decode status. Make a single class, imgStatusTracker, which keeps track of the state of an image, and also takes care of calling notifications on imgRequestProxys. r=jrmuizel,bholley

This commit is contained in:
Joe Drew 2010-05-14 16:47:59 -04:00
parent 92722f5a0a
commit dce4fe07b6
10 changed files with 699 additions and 424 deletions

View File

@ -58,6 +58,7 @@ CPPSRCS = \
imgRequestProxy.cpp \
imgTools.cpp \
imgDiscardTracker.cpp \
imgStatusTracker.cpp \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -144,6 +144,7 @@ imgContainer::imgContainer() :
mLoopCount(-1),
mObserver(nsnull),
mLockCount(0),
mStatusTracker(this),
mDecoder(nsnull),
mWorker(nsnull),
mBytesDecoded(0),
@ -337,6 +338,9 @@ NS_IMETHODIMP imgContainer::ExtractFrame(PRUint32 aWhichFrame,
img->mFrames.AppendElement(subframe.forget());
img->mStatusTracker.RecordLoaded();
img->mStatusTracker.RecordDecoded();
*_retval = img.forget().get();
return NS_OK;

View File

@ -64,6 +64,7 @@
#include "imgFrame.h"
#include "nsThreadUtils.h"
#include "imgDiscardTracker.h"
#include "imgStatusTracker.h"
#define NS_IMGCONTAINER_CID \
{ /* c76ff2c1-9bf6-418a-b143-3340c00112f7 */ \
@ -161,6 +162,9 @@ public:
/* Triggers discarding. */
void Discard();
imgStatusTracker& GetStatusTracker() { return mStatusTracker; }
PRBool IsInitialized() const { return mInitialized; }
private:
struct Anim
{
@ -331,6 +335,8 @@ private: // data
nsTArray<char> mSourceData;
nsCString mSourceDataMimeType;
imgStatusTracker mStatusTracker;
friend class imgDecodeWorker;
friend class imgDiscardTracker;

View File

@ -671,7 +671,7 @@ nsresult imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup
aRequest->GetURI(getter_AddRefs(uri));
// init adds itself to imgRequest's list of observers
nsresult rv = proxyRequest->Init(aRequest, aLoadGroup, uri, aObserver);
nsresult rv = proxyRequest->Init(aRequest, aLoadGroup, aRequest->mImage, uri, aObserver);
if (NS_FAILED(rv)) {
NS_RELEASE(proxyRequest);
return rv;
@ -1988,7 +1988,6 @@ imgCacheValidator::imgCacheValidator(imgRequest *request, void *aContext) :
imgCacheValidator::~imgCacheValidator()
{
/* destructor code */
if (mRequest) {
mRequest->mValidator = nsnull;
}
@ -2008,6 +2007,9 @@ void imgCacheValidator::AddProxy(imgRequestProxy *aProxy)
/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
{
// If this request is coming from cache, the request all our proxies are
// pointing at is valid, and all we have to do is tell them to notify their
// listeners.
nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(aRequest));
if (cacheChan) {
PRBool isFromCache;

View File

@ -166,14 +166,10 @@ NS_IMPL_ISUPPORTS7(imgRequest,
nsIInterfaceRequestor)
imgRequest::imgRequest() :
mImageStatus(imgIRequest::STATUS_NONE), mState(0), mCacheId(0),
mValidator(nsnull), mImageSniffers("image-sniffing-services"),
mDeferredLocks(0), mDecodeRequested(PR_FALSE),
mIsMultiPartChannel(PR_FALSE), mLoading(PR_FALSE),
mCacheId(0), mValidator(nsnull), mImageSniffers("image-sniffing-services"),
mDecodeRequested(PR_FALSE), mIsMultiPartChannel(PR_FALSE),
mGotData(PR_FALSE), mIsInCache(PR_FALSE)
{
/* member initializers and constructor code */
}
{}
imgRequest::~imgRequest()
{
@ -202,8 +198,8 @@ nsresult imgRequest::Init(nsIURI *aURI,
NS_ABORT_IF_FALSE(aChannel, "No channel");
mProperties = do_CreateInstance("@mozilla.org/properties;1");
if (!mProperties)
return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<imgIContainer> comImg = do_CreateInstance("@mozilla.org/image/container;3");
mImage = static_cast<imgContainer*>(comImg.get());
mURI = aURI;
mKeyURI = aKeyURI;
@ -216,13 +212,6 @@ nsresult imgRequest::Init(nsIURI *aURI,
mChannel->SetNotificationCallbacks(this);
/* set our loading flag to true here.
Setting it here lets checks to see if the load is in progress
before OnStartRequest gets called, letting 'this' properly get removed
from the cache in certain cases.
*/
mLoading = PR_TRUE;
mCacheEntry = aCacheEntry;
mCacheId = aCacheId;
@ -272,13 +261,12 @@ nsresult imgRequest::AddProxy(imgRequestProxy *proxy)
}
// If we don't have any current observers, we should restart any animation.
if (mImage && !HaveProxyWithObserver(proxy) && proxy->HasObserver()) {
if (!HaveProxyWithObserver(proxy) && proxy->HasObserver()) {
LOG_MSG(gImgLog, "imgRequest::AddProxy", "resetting animation");
mImage->ResetAnimation();
}
proxy->SetImage(mImage);
proxy->SetPrincipal(mPrincipal);
return mObservers.AppendElementUnlessExists(proxy) ?
@ -291,30 +279,14 @@ nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBoo
mObservers.RemoveElement(proxy);
/* Check mState below before we potentially call Cancel() below. Since
Cancel() may result in OnStopRequest being called back before Cancel()
returns, leaving mState in a different state then the one it was in at
this point.
*/
// Let the status tracker do its thing before we potentially call Cancel()
// below, because Cancel() may result in OnStopRequest being called back
// before Cancel() returns, leaving the image in a different state then the
// one it was in at this point.
if (aNotify) {
// The "real" OnStopDecode - fix this with bug 505385.
if (!(mState & stateDecodeStopped)) {
proxy->OnStopContainer(mImage);
}
mImage->GetStatusTracker().EmulateRequestFinished(proxy, aStatus, !aNotify);
// make sure that observer gets an OnStopDecode message sent to it
if (!(mState & stateRequestStopped)) {
proxy->OnStopDecode(aStatus, nsnull);
}
}
// make sure that observer gets an OnStopRequest message sent to it
if (!(mState & stateRequestStopped)) {
proxy->OnStopRequest(PR_TRUE);
}
if (mImage && !HaveProxyWithObserver(nsnull)) {
if (!HaveProxyWithObserver(nsnull)) {
LOG_MSG(gImgLog, "imgRequest::RemoveProxy", "stopping animation");
mImage->StopAnimation();
@ -342,11 +314,9 @@ nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBoo
This way, if a proxy is destroyed without calling cancel on it, it won't leak
and won't leave a bad pointer in mObservers.
*/
if (mRequest && mLoading && NS_FAILED(aStatus)) {
if (mImage->GetStatusTracker().IsLoading() && NS_FAILED(aStatus)) {
LOG_MSG(gImgLog, "imgRequest::RemoveProxy", "load in progress. canceling");
mImageStatus |= imgIRequest::STATUS_LOAD_PARTIAL;
this->Cancel(NS_BINDING_ABORTED);
}
@ -362,27 +332,6 @@ nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBoo
return NS_OK;
}
void imgRequest::Cancel(nsresult aStatus)
{
/* The Cancel() method here should only be called by this class. */
LOG_SCOPE(gImgLog, "imgRequest::Cancel");
if (mImage) {
LOG_MSG(gImgLog, "imgRequest::Cancel", "stopping animation");
mImage->StopAnimation();
}
if (!(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL))
mImageStatus |= imgIRequest::STATUS_ERROR;
RemoveFromCache();
if (mRequest && mLoading)
mRequest->Cancel(aStatus);
}
void imgRequest::CancelAndAbort(nsresult aStatus)
{
LOG_SCOPE(gImgLog, "imgRequest::CancelAndAbort");
@ -398,6 +347,23 @@ void imgRequest::CancelAndAbort(nsresult aStatus)
}
}
void imgRequest::Cancel(nsresult aStatus)
{
/* The Cancel() method here should only be called by this class. */
LOG_SCOPE(gImgLog, "imgRequest::Cancel");
LOG_MSG(gImgLog, "imgRequest::Cancel", "stopping animation");
mImage->StopAnimation();
mImage->GetStatusTracker().RecordCancel();
RemoveFromCache();
if (mRequest && mImage->GetStatusTracker().IsLoading())
mRequest->Cancel(aStatus);
}
nsresult imgRequest::GetURI(nsIURI **aURI)
{
LOG_FUNC(gImgLog, "imgRequest::GetURI");
@ -503,8 +469,7 @@ void imgRequest::UpdateCacheEntrySize()
{
if (mCacheEntry) {
PRUint32 imageSize = 0;
if (mImage)
mImage->GetDataSize(&imageSize);
mImage->GetDataSize(&imageSize);
mCacheEntry->SetDataSize(imageSize);
#ifdef DEBUG_joe
@ -513,46 +478,25 @@ void imgRequest::UpdateCacheEntrySize()
printf("CACHEPUT: %d %s %d\n", time(NULL), url.get(), imageSize);
#endif
}
}
nsresult
imgRequest::LockImage()
{
// If we have an image, apply the lock directly
if (mImage) {
NS_ABORT_IF_FALSE(mDeferredLocks == 0, "Have image, but deferred locks?");
return mImage->LockImage();
}
// Otherwise, queue it up for when we have an image
mDeferredLocks++;
return NS_OK;
return mImage->LockImage();
}
nsresult
imgRequest::UnlockImage()
{
// If we have an image, apply the unlock directly
if (mImage) {
NS_ABORT_IF_FALSE(mDeferredLocks == 0, "Have image, but deferred locks?");
return mImage->UnlockImage();
}
// If we don't have an image, get rid of one of our deferred locks (we should
// have one).
NS_ABORT_IF_FALSE(mDeferredLocks > 0, "lock/unlock calls must be matched!");
mDeferredLocks--;
return NS_OK;
return mImage->UnlockImage();
}
nsresult
imgRequest::RequestDecode()
{
// If we have an image, apply the request directly
if (mImage) {
// If we've initialized our image, we can request a decode.
if (mImage->IsInitialized()) {
return mImage->RequestDecode();
}
@ -570,9 +514,11 @@ NS_IMETHODIMP imgRequest::FrameChanged(imgIContainer *container,
{
LOG_SCOPE(gImgLog, "imgRequest::FrameChanged");
mImage->GetStatusTracker().RecordFrameChanged(container, dirtyRect);
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->FrameChanged(container, dirtyRect);
mImage->GetStatusTracker().SendFrameChanged(iter.GetNext(), container, dirtyRect);
}
return NS_OK;
@ -585,11 +531,11 @@ NS_IMETHODIMP imgRequest::OnStartDecode(imgIRequest *request)
{
LOG_SCOPE(gImgLog, "imgRequest::OnStartDecode");
mState |= stateDecodeStarted;
mImage->GetStatusTracker().RecordStartDecode();
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnStartDecode();
mImage->GetStatusTracker().SendStartDecode(iter.GetNext());
}
/* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which
@ -617,20 +563,18 @@ NS_IMETHODIMP imgRequest::OnStartContainer(imgIRequest *request, imgIContainer *
NS_ASSERTION(image, "imgRequest::OnStartContainer called with a null image!");
if (!image) return NS_ERROR_UNEXPECTED;
// We only want to send onStartContainer once
PRBool alreadySent = (mState & stateHasSize) != 0;
mState |= stateHasSize;
mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE;
if (!alreadySent) {
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnStartContainer(image);
}
// We only want to send onStartContainer once, but we might get multiple
// OnStartContainer calls (e.g. from multipart/x-mixed-replace). Therefore,
// we tell our status tracker about OnStartContainer *after* attempting to
// send the notifications. That way, if we have previously called
// OnStartContainer, the status tracker can notice.
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
mImage->GetStatusTracker().SendStartContainer(iter.GetNext(), image);
}
mImage->GetStatusTracker().RecordStartContainer(image);
return NS_OK;
}
@ -640,9 +584,11 @@ NS_IMETHODIMP imgRequest::OnStartFrame(imgIRequest *request,
{
LOG_SCOPE(gImgLog, "imgRequest::OnStartFrame");
mImage->GetStatusTracker().RecordStartFrame(frame);
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnStartFrame(frame);
mImage->GetStatusTracker().SendStartFrame(iter.GetNext(), frame);
}
return NS_OK;
@ -655,9 +601,11 @@ NS_IMETHODIMP imgRequest::OnDataAvailable(imgIRequest *request,
{
LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable");
mImage->GetStatusTracker().RecordDataAvailable(aCurrentFrame, rect);
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnDataAvailable(aCurrentFrame, rect);
mImage->GetStatusTracker().SendDataAvailable(iter.GetNext(), aCurrentFrame, rect);
}
return NS_OK;
@ -669,11 +617,11 @@ NS_IMETHODIMP imgRequest::OnStopFrame(imgIRequest *request,
{
LOG_SCOPE(gImgLog, "imgRequest::OnStopFrame");
mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE;
mImage->GetStatusTracker().RecordStopFrame(frame);
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnStopFrame(frame);
mImage->GetStatusTracker().SendStopFrame(iter.GetNext(), frame);
}
return NS_OK;
@ -685,13 +633,11 @@ NS_IMETHODIMP imgRequest::OnStopContainer(imgIRequest *request,
{
LOG_SCOPE(gImgLog, "imgRequest::OnStopContainer");
// XXXbholley - This should be moved into OnStopDecode when we fix bug
// 505385.
mState |= stateDecodeStopped;
mImage->GetStatusTracker().RecordStopContainer(image);
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnStopContainer(image);
mImage->GetStatusTracker().SendStopContainer(iter.GetNext(), image);
}
return NS_OK;
@ -708,12 +654,13 @@ NS_IMETHODIMP imgRequest::OnStopDecode(imgIRequest *aRequest,
// entry size to take this into account.
UpdateCacheEntrySize();
// If we were successful, set STATUS_DECODE_COMPLETE
if (NS_SUCCEEDED(aStatus))
mImageStatus |= imgIRequest::STATUS_DECODE_COMPLETE;
// If we weren't, clear all success status bits and set error.
else
mImageStatus = imgIRequest::STATUS_ERROR;
mImage->GetStatusTracker().RecordStopDecode(aStatus, aStatusArg);
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
mImage->GetStatusTracker().SendStopDecode(iter.GetNext(), aStatus,
aStatusArg);
}
// ImgContainer and everything below it is completely correct and
// bulletproof about its handling of decoder notifications.
@ -723,9 +670,10 @@ NS_IMETHODIMP imgRequest::OnStopDecode(imgIRequest *aRequest,
// 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_).
// As such, we ignore OnStopDecode notifications from the decoder and
// container and generate our own every time we send OnStopRequest.
// For more information, see bug 435296.
// Within imgStatusTracker, we ignore OnStopDecode notifications from the
// decoder and container 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;
}
@ -740,25 +688,17 @@ NS_IMETHODIMP imgRequest::OnStopRequest(imgIRequest *aRequest,
/* void onDiscard (in imgIRequest request); */
NS_IMETHODIMP imgRequest::OnDiscard(imgIRequest *aRequest)
{
// Clear the state bits we no longer deserve.
PRUint32 stateBitsToClear = stateDecodeStarted | stateDecodeStopped;
mState &= ~stateBitsToClear;
// Clear the status bits we no longer deserve.
PRUint32 statusBitsToClear = imgIRequest::STATUS_FRAME_COMPLETE
| imgIRequest::STATUS_DECODE_COMPLETE;
mImageStatus &= ~statusBitsToClear;
mImage->GetStatusTracker().RecordDiscard();
// Update the cache entry size, since we just got rid of frame data
UpdateCacheEntrySize();
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnDiscard();
mImage->GetStatusTracker().SendDiscard(iter.GetNext());
}
return NS_OK;
}
/** nsIRequestObserver methods **/
@ -776,23 +716,14 @@ NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt
mIsMultiPartChannel = PR_TRUE;
// If we're not multipart, we shouldn't have an image yet
NS_ABORT_IF_FALSE(mIsMultiPartChannel || !mImage,
NS_ABORT_IF_FALSE(mIsMultiPartChannel || !mImage->IsInitialized(),
"Already have an image for non-multipart request");
// If we're multipart and have an image, fix things up for another round
if (mIsMultiPartChannel && mImage) {
// If we're multipart, and our image is initialized, fix things up for another round
if (mIsMultiPartChannel && mImage->IsInitialized()) {
// Inform the container that we have new source data
mImage->NewSourceData();
// Clear any status and state bits indicating load/decode
mImageStatus &= ~imgIRequest::STATUS_LOAD_PARTIAL;
mImageStatus &= ~imgIRequest::STATUS_LOAD_COMPLETE;
mImageStatus &= ~imgIRequest::STATUS_FRAME_COMPLETE;
mState &= ~stateRequestStarted;
mState &= ~stateDecodeStarted;
mState &= ~stateDecodeStopped;
mState &= ~stateRequestStopped;
}
/*
@ -810,20 +741,15 @@ NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt
mRequest = chan;
}
// The request has started
mState |= stateRequestStarted;
mImage->GetStatusTracker().RecordStartRequest();
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
if (channel)
channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
/* set our loading flag to true */
mLoading = PR_TRUE;
/* notify our kids */
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->OnStartRequest();
mImage->GetStatusTracker().SendStartRequest(iter.GetNext());
}
/* Get our principal */
@ -909,11 +835,6 @@ NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt,
{
LOG_FUNC(gImgLog, "imgRequest::OnStopRequest");
mState |= stateRequestStopped;
/* set our loading flag to false */
mLoading = PR_FALSE;
PRBool lastPart = PR_TRUE;
nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
if (mpchan)
@ -937,7 +858,7 @@ NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt,
// Tell the image that it has all of the source data. Note that this can
// trigger a failure, since the image might be waiting for more non-optional
// data and this is the point where we break the news that it's not coming.
if (mImage) {
if (mImage->IsInitialized()) {
// Notify the image
nsresult rv = mImage->SourceDataComplete();
@ -950,30 +871,24 @@ NS_IMETHODIMP imgRequest::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt,
status = rv;
}
// If the request went through, say we loaded the image, and update the
// cache entry size. Otherwise, cancel the request, which adds an error
// flag to mImageStatus.
if (mImage && NS_SUCCEEDED(status)) {
// Flag that we loaded the image
mImageStatus |= imgIRequest::STATUS_LOAD_COMPLETE;
mImage->GetStatusTracker().RecordStopRequest(lastPart, status);
// If the request went through, update the cache entry size. Otherwise,
// cancel the request, which removes us from the cache.
if (mImage->IsInitialized() && NS_SUCCEEDED(status)) {
// We update the cache entry size here because this is where we finish
// loading compressed source data, which is part of our size calculus.
UpdateCacheEntrySize();
}
else
this->Cancel(status); // sets status, stops animations, removes from cache
/* notify the kids */
nsTObserverArray<imgRequestProxy*>::ForwardIterator sdIter(mObservers);
while (sdIter.HasMore()) {
sdIter.GetNext()->OnStopDecode(GetResultFromImageStatus(mImageStatus), nsnull);
else {
// stops animations, removes from cache
this->Cancel(status);
}
/* notify the kids */
nsTObserverArray<imgRequestProxy*>::ForwardIterator srIter(mObservers);
while (srIter.HasMore()) {
srIter.GetNext()->OnStopRequest(lastPart);
mImage->GetStatusTracker().SendStopRequest(srIter.GetNext(), lastPart, status);
}
return NS_OK;
@ -992,12 +907,13 @@ NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctx
NS_ASSERTION(aRequest, "imgRequest::OnDataAvailable -- no request!");
mGotData = PR_TRUE;
nsresult rv;
if (!mImage) {
if (!mGotData) {
LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |First time through... finding mimetype|");
mGotData = PR_TRUE;
/* look at the first few bytes and see if we can tell what the data is from that
* since servers tend to lie. :(
*/
@ -1095,25 +1011,12 @@ NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctx
if (mIsMultiPartChannel)
containerFlags |= imgIContainer::INIT_FLAG_MULTIPART;
// Create and initialize the imgContainer. 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.
mImage = do_CreateInstance("@mozilla.org/image/container;3");
if (!mImage) {
this->Cancel(NS_ERROR_OUT_OF_MEMORY);
return NS_ERROR_OUT_OF_MEMORY;
}
// Initialize the image that we created in OnStartRequest(). 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(), containerFlags);
if (NS_FAILED(rv)) { // Probably bad mimetype
// There's no reason to keep the image around. Save memory.
//
// XXXbholley - This is also here because I'm not sure we've found
// all the consumers who (incorrectly) check whether the container
// is null to determine things like size availability (they should
// be checking the image status instead).
mImage = nsnull;
this->Cancel(rv);
return NS_BINDING_ABORTED;
}
@ -1136,21 +1039,10 @@ NS_IMETHODIMP imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctx
}
}
// If we were waiting on the image to do something, now's our chance.
if (mDecodeRequested) {
mImage->RequestDecode();
}
while (mDeferredLocks) {
mImage->LockImage();
mDeferredLocks--;
}
// Tell all of our proxies that we have an image.
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
while (iter.HasMore()) {
iter.GetNext()->SetImage(mImage);
}
}
// WriteToContainer always consumes everything it gets

View File

@ -61,6 +61,7 @@
#include "nsWeakReference.h"
#include "ImageErrors.h"
#include "imgIRequest.h"
#include "imgContainer.h"
class imgCacheValidator;
@ -68,14 +69,6 @@ class imgRequestProxy;
class imgCacheEntry;
class imgMemoryReporter;
enum {
stateRequestStarted = PR_BIT(0),
stateHasSize = PR_BIT(1),
stateDecodeStarted = PR_BIT(2),
stateDecodeStopped = PR_BIT(3),
stateRequestStopped = PR_BIT(4)
};
class imgRequest : public imgIDecoderObserver,
public nsIStreamListener,
public nsSupportsWeakReference,
@ -107,7 +100,10 @@ public:
// a request is "reusable" if it has already been loaded, or it is
// currently being loaded on the same event queue as the new request
// being made...
PRBool IsReusable(void *aCacheId) { return !mLoading || (aCacheId == mCacheId); }
PRBool IsReusable(void *aCacheId) {
return (mImage && mImage->GetStatusTracker().IsLoading()) ||
(aCacheId == mCacheId);
}
// Cancel, but also ensure that all work done in Init() is undone. Call this
// only when the channel has failed to open, and so calling Cancel() on it
@ -119,14 +115,7 @@ public:
nsresult LockImage();
nsresult UnlockImage();
nsresult RequestDecode();
static nsresult GetResultFromImageStatus(PRUint32 aStatus)
{
if (aStatus & imgIRequest::STATUS_ERROR)
return NS_IMAGELIB_ERROR_FAILURE;
if (aStatus & imgIRequest::STATUS_LOAD_COMPLETE)
return NS_IMAGELIB_SUCCESS_LOAD_FINISHED;
return NS_OK;
}
private:
friend class imgCacheEntry;
friend class imgRequestProxy;
@ -138,13 +127,13 @@ private:
mLoadId = aLoadId;
mLoadTime = PR_Now();
}
inline PRUint32 GetImageStatus() const { return mImageStatus; }
inline PRUint32 GetState() const { return mState; }
void Cancel(nsresult aStatus);
void RemoveFromCache();
nsresult GetURI(nsIURI **aURI);
nsresult GetKeyURI(nsIURI **aURI);
nsresult GetSecurityInfo(nsISupports **aSecurityInfo);
void RemoveFromCache();
inline const char *GetMimeType() const {
return mContentType.get();
}
@ -200,7 +189,7 @@ private:
// The URI we are keyed on in the cache.
nsCOMPtr<nsIURI> mKeyURI;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<imgIContainer> mImage;
nsRefPtr<imgContainer> mImage;
nsCOMPtr<nsIProperties> mProperties;
nsCOMPtr<nsISupports> mSecurityInfo;
nsCOMPtr<nsIChannel> mChannel;
@ -208,8 +197,6 @@ private:
nsTObserverArray<imgRequestProxy*> mObservers;
PRUint32 mImageStatus;
PRUint32 mState;
nsCString mContentType;
nsRefPtr<imgCacheEntry> mCacheEntry; /* we hold on to this to this so long as we have observers */
@ -224,11 +211,9 @@ private:
// Sometimes consumers want to do things before the image is ready. Let them,
// and apply the action when the image becomes available.
PRUint32 mDeferredLocks;
PRPackedBool mDecodeRequested : 1;
PRPackedBool mIsMultiPartChannel : 1;
PRPackedBool mLoading : 1;
PRPackedBool mGotData : 1;
PRPackedBool mIsInCache : 1;
};

View File

@ -62,16 +62,13 @@ imgRequestProxy::imgRequestProxy() :
mURI(nsnull),
mImage(nsnull),
mPrincipal(nsnull),
mImageStatus(0),
mState(0),
mListener(nsnull),
mLoadFlags(nsIRequest::LOAD_NORMAL),
mLocksHeld(0),
mCanceled(PR_FALSE),
mIsInLoadGroup(PR_FALSE),
mListenerIsStrongRef(PR_FALSE),
mDecodeRequested(PR_FALSE),
mHadLastPart(PR_FALSE)
mDecodeRequested(PR_FALSE)
{
/* member initializers and constructor code */
@ -84,10 +81,8 @@ imgRequestProxy::~imgRequestProxy()
// Unlock the image the proper number of times if we're holding locks on it.
// Note that UnlockImage() decrements mLocksHeld each time it's called.
if (mOwner) {
while (mLocksHeld)
UnlockImage();
}
while (mLocksHeld)
UnlockImage();
// Explicitly set mListener to null to ensure that the RemoveProxy
// call below can't send |this| to an arbitrary listener while |this|
@ -112,13 +107,10 @@ imgRequestProxy::~imgRequestProxy()
}
}
nsresult imgRequestProxy::Init(imgRequest* request, nsILoadGroup* aLoadGroup, nsIURI* aURI,
imgIDecoderObserver* aObserver)
nsresult imgRequestProxy::Init(imgRequest* request, nsILoadGroup* aLoadGroup, imgContainer* aImage,
nsIURI* aURI, imgIDecoderObserver* aObserver)
{
NS_PRECONDITION(!mOwner && !mListener, "imgRequestProxy is already initialized");
NS_PRECONDITION(request, "no request");
if (!request)
return NS_ERROR_NULL_POINTER;
LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequestProxy::Init", "request", request);
@ -132,10 +124,12 @@ nsresult imgRequestProxy::Init(imgRequest* request, nsILoadGroup* aLoadGroup, ns
NS_ADDREF(mListener);
}
mLoadGroup = aLoadGroup;
mImage = aImage;
mURI = aURI;
// Note: AddProxy won't send all the On* notifications immediately
request->AddProxy(this);
if (mOwner)
mOwner->AddProxy(this);
return NS_OK;
}
@ -144,20 +138,28 @@ nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner)
{
NS_PRECONDITION(mOwner, "Cannot ChangeOwner on a proxy without an owner!");
if (mCanceled)
return NS_OK;
// Were we decoded before?
PRBool wasDecoded = PR_FALSE;
if (mOwner->GetImageStatus() & imgIRequest::STATUS_FRAME_COMPLETE)
wasDecoded = PR_TRUE;
// If we're holding locks, unlock the old image.
// Note that UnlockImage decrements mLocksHeld each time it's called.
PRUint32 oldLockCount = mLocksHeld;
while (mLocksHeld)
UnlockImage();
// Even if we are cancelled, we MUST change our image, because the image
// holds our status, and the status must always be correct.
mImage = aNewOwner->mImage;
// If we were locked, apply the locks here
for (PRUint32 i = 0; i < oldLockCount; i++)
LockImage();
if (mCanceled)
return NS_OK;
// Were we decoded before?
PRBool wasDecoded = PR_FALSE;
if (mImage->GetStatusTracker().GetImageStatus() & imgIRequest::STATUS_FRAME_COMPLETE)
wasDecoded = PR_TRUE;
// Passing false to aNotify means that mListener will still get
// OnStopRequest, if needed.
mOwner->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER, PR_FALSE);
@ -171,10 +173,6 @@ nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner)
if (wasDecoded || mDecodeRequested)
mOwner->RequestDecode();
// If we were locked, apply the locks here
for (PRUint32 i = 0; i < oldLockCount; i++)
LockImage();
return NS_OK;
}
@ -238,7 +236,7 @@ NS_IMETHODIMP imgRequestProxy::GetStatus(nsresult *aStatus)
/* void cancel (in nsresult status); */
NS_IMETHODIMP imgRequestProxy::Cancel(nsresult status)
{
if (mCanceled || !mOwner)
if (mCanceled)
return NS_ERROR_FAILURE;
LOG_SCOPE(gImgLog, "imgRequestProxy::Cancel");
@ -254,7 +252,8 @@ imgRequestProxy::DoCancel(nsresult status)
{
// Passing false to aNotify means that mListener will still get
// OnStopRequest, if needed.
mOwner->RemoveProxy(this, status, PR_FALSE);
if (mOwner)
mOwner->RemoveProxy(this, status, PR_FALSE);
NullOutListener();
}
@ -262,7 +261,7 @@ imgRequestProxy::DoCancel(nsresult status)
/* void cancelAndForgetObserver (in nsresult aStatus); */
NS_IMETHODIMP imgRequestProxy::CancelAndForgetObserver(nsresult aStatus)
{
if (mCanceled || !mOwner)
if (mCanceled)
return NS_ERROR_FAILURE;
LOG_SCOPE(gImgLog, "imgRequestProxy::CancelAndForgetObserver");
@ -272,10 +271,11 @@ NS_IMETHODIMP imgRequestProxy::CancelAndForgetObserver(nsresult aStatus)
// Now cheat and make sure our removal from loadgroup happens async
PRBool oldIsInLoadGroup = mIsInLoadGroup;
mIsInLoadGroup = PR_FALSE;
// Passing false to aNotify means that mListener will still get
// OnStopRequest, if needed.
mOwner->RemoveProxy(this, aStatus, PR_FALSE);
if (mOwner)
mOwner->RemoveProxy(this, aStatus, PR_FALSE);
mIsInLoadGroup = oldIsInLoadGroup;
@ -308,9 +308,6 @@ imgRequestProxy::RequestDecode()
NS_IMETHODIMP
imgRequestProxy::LockImage()
{
if (!mImage)
return NS_ERROR_FAILURE;
mLocksHeld++;
return mImage->LockImage();
@ -320,10 +317,8 @@ imgRequestProxy::LockImage()
NS_IMETHODIMP
imgRequestProxy::UnlockImage()
{
if (!mImage)
return NS_ERROR_FAILURE;
NS_ABORT_IF_FALSE(mLocksHeld > 0, "calling unlock but no locks!");
mLocksHeld--;
return mImage->UnlockImage();
@ -370,7 +365,7 @@ NS_IMETHODIMP imgRequestProxy::SetLoadFlags(nsLoadFlags flags)
/* attribute imgIContainer image; */
NS_IMETHODIMP imgRequestProxy::GetImage(imgIContainer * *aImage)
{
if (!mImage)
if (!mImage->IsInitialized())
return NS_ERROR_FAILURE;
NS_ADDREF(*aImage = mImage);
@ -381,19 +376,8 @@ NS_IMETHODIMP imgRequestProxy::GetImage(imgIContainer * *aImage)
/* readonly attribute unsigned long imageStatus; */
NS_IMETHODIMP imgRequestProxy::GetImageStatus(PRUint32 *aStatus)
{
if (!mOwner && !mImage) {
*aStatus = imgIRequest::STATUS_ERROR;
return NS_ERROR_FAILURE;
}
*aStatus = mImage->GetStatusTracker().GetImageStatus();
if (mOwner) {
*aStatus = mOwner->GetImageStatus();
return NS_OK;
}
// If we don't have an owner, we have an image, and have cached the image's
// status.
*aStatus = mImageStatus;
return NS_OK;
}
@ -435,6 +419,9 @@ NS_IMETHODIMP imgRequestProxy::Clone(imgIDecoderObserver* aObserver,
imgIRequest** aClone)
{
NS_PRECONDITION(aClone, "Null out param");
LOG_SCOPE(gImgLog, "imgRequestProxy::Clone");
*aClone = nsnull;
nsRefPtr<imgRequestProxy> clone = new imgRequestProxy();
@ -445,32 +432,18 @@ 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(mOwner, mLoadGroup, mURI, aObserver);
nsresult rv = clone->Init(mOwner, mLoadGroup, mImage, mURI, aObserver);
if (NS_FAILED(rv))
return rv;
clone->mHadLastPart = mHadLastPart;
clone->SetImage(mImage);
clone->SetPrincipal(mPrincipal);
// Temporary hack - must be removed in patch to centralize image state tracking.
PRUint32 imageStatus = 0;
GetImageStatus(&imageStatus);
PRUint32 state = 0;
GetState(&state);
clone->mImageStatus = imageStatus;
clone->mState = state;
if (NS_FAILED(rv))
return rv;
// Assign to *aClone before calling Notify so that if the caller expects to
// only be notified for requests it's already holding pointers to it won't be
// surprised.
*aClone = clone.forget().get();
NS_ADDREF(*aClone = clone);
// Send the notifications to the clone's observer
static_cast<imgRequestProxy*>(*aClone)->NotifyListener();
clone->NotifyListener();
return NS_OK;
}
@ -663,8 +636,6 @@ void imgRequestProxy::OnStopRequest(PRBool lastPart)
// listener, etc). Don't let them do it.
nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
mHadLastPart = lastPart;
if (mListener) {
// Hold a ref to the listener while we call it, just in case.
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
@ -708,66 +679,38 @@ void imgRequestProxy::NullOutListener()
}
}
nsresult imgRequestProxy::GetState(PRUint32 *aState)
{
if (!mOwner && !mImage) {
*aState = 0;
return NS_ERROR_FAILURE;
}
if (mOwner) {
*aState = mOwner->GetState();
return NS_OK;
}
// If we don't have an owner, we have an image, and have cached the image's
// state.
*aState = mState;
return NS_OK;
}
NS_IMETHODIMP
imgRequestProxy::GetStaticRequest(imgIRequest** aReturn)
{
*aReturn = nsnull;
nsCOMPtr<imgIContainer> img;
nsCOMPtr<imgIContainer> currentFrame;
GetImage(getter_AddRefs(img));
PRBool animated;
if (img && NS_SUCCEEDED(img->GetAnimated(&animated)) && !animated) {
if (NS_SUCCEEDED(mImage->GetAnimated(&animated)) && !animated) {
// Early exit - we're not animated, so we don't have to do anything.
NS_ADDREF(*aReturn = this);
return NS_OK;
} else if (img) {
// We are animated. We need to extract the current frame from this image.
PRInt32 w = 0;
PRInt32 h = 0;
img->GetWidth(&w);
img->GetHeight(&h);
nsIntRect rect(0, 0, w, h);
img->ExtractFrame(imgIContainer::FRAME_CURRENT, rect,
imgIContainer::FLAG_SYNC_DECODE,
getter_AddRefs(currentFrame));
}
// Now, we're creating a static imgRequestProxy, so we have to extract this
// image's status and other ancillary information.
PRUint32 imageStatus = 0;
GetImageStatus(&imageStatus);
PRUint32 state = 0;
GetState(&state);
// We are animated. We need to extract the current frame from this image.
PRInt32 w = 0;
PRInt32 h = 0;
mImage->GetWidth(&w);
mImage->GetHeight(&h);
nsIntRect rect(0, 0, w, h);
nsCOMPtr<imgIContainer> currentFrame;
nsresult rv = mImage->ExtractFrame(imgIContainer::FRAME_CURRENT, rect,
imgIContainer::FLAG_SYNC_DECODE,
getter_AddRefs(currentFrame));
if (NS_FAILED(rv))
return rv;
imgRequestProxy* req = new imgRequestProxy();
req->Init(nsnull, nsnull, mURI, nsnull);
req->SetImage(currentFrame);
nsRefPtr<imgContainer> frame = static_cast<imgContainer*>(currentFrame.get());
// Create a static imgRequestProxy with our new extracted frame.
nsRefPtr<imgRequestProxy> req = new imgRequestProxy();
req->Init(nsnull, nsnull, frame, mURI, nsnull);
req->SetPrincipal(mPrincipal);
// Temporary hack - must be removed in patch to centralize image state tracking.
req->mImageStatus = imageStatus;
req->mState = state;
NS_ADDREF(*aReturn = req);
return NS_OK;
@ -778,64 +721,13 @@ void imgRequestProxy::SetPrincipal(nsIPrincipal *aPrincipal)
mPrincipal = aPrincipal;
}
void imgRequestProxy::SetImage(imgIContainer *aImage)
void imgRequestProxy::NotifyListener()
{
mImage = aImage;
}
nsresult imgRequestProxy::NotifyListener()
{
nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
PRUint32 imageStatus = 0;
GetImageStatus(&imageStatus);
PRUint32 state = 0;
GetState(&state);
nsCOMPtr<imgIContainer> image;
GetImage(getter_AddRefs(image));
// OnStartRequest
if (state & stateRequestStarted)
OnStartRequest();
// OnStartContainer
if (state & stateHasSize)
OnStartContainer(image);
// OnStartDecode
if (state & stateDecodeStarted)
OnStartDecode();
// Send frame messages (OnStartFrame, OnDataAvailable, OnStopFrame)
PRUint32 nframes = 0;
if (image)
image->GetNumFrames(&nframes);
if (nframes > 0) {
PRUint32 frame;
image->GetCurrentFrameIndex(&frame);
OnStartFrame(frame);
// OnDataAvailable
// XXX - Should only send partial rects here, but that needs to
// wait until we fix up the observer interface
nsIntRect r;
image->GetCurrentFrameRect(r);
OnDataAvailable(frame, &r);
if (state & stateRequestStopped)
OnStopFrame(frame);
}
// The "real" OnStopDecode - Fix this with bug 505385.
if (state & stateDecodeStopped)
OnStopContainer(image);
if (state & stateRequestStopped) {
OnStopDecode(imgRequest::GetResultFromImageStatus(imageStatus), nsnull);
OnStopRequest(mHadLastPart);
}
return NS_OK;
// It would be nice to notify the observer directly instead of through the
// proxy, but there are several places we do extra processing when we receive
// notifications (like OnStopRequest()), and we need to check mCanceled
// everywhere too.
if (mListener)
mImage->GetStatusTracker().Notify(this);
}

View File

@ -37,6 +37,9 @@
*
* ***** END LICENSE BLOCK ***** */
#ifndef imgRequestProxy_h__
#define imgRequestProxy_h__
#include "imgIRequest.h"
#include "imgIDecoderObserver.h"
#include "nsISecurityInfoProvider.h"
@ -75,7 +78,8 @@ public:
// Callers to Init or ChangeOwner are required to call NotifyListener after
// (although not immediately after) doing so.
nsresult Init(imgRequest *request, nsILoadGroup *aLoadGroup, nsIURI* aURI, imgIDecoderObserver *aObserver);
nsresult Init(imgRequest *request, nsILoadGroup *aLoadGroup, imgContainer* aImage,
nsIURI* aURI, imgIDecoderObserver *aObserver);
nsresult ChangeOwner(imgRequest *aNewOwner); // this will change mOwner. Do not call this if the previous
// owner has already sent notifications out!
@ -83,14 +87,17 @@ public:
void AddToLoadGroup();
void RemoveFromLoadGroup(PRBool releaseLoadGroup);
// Notify this proxy's listener of the current state of the request.
nsresult NotifyListener();
protected:
friend class imgRequest;
inline PRBool HasObserver() const {
return mListener != nsnull;
}
void SetPrincipal(nsIPrincipal *aPrincipal);
void SetImage(imgIContainer *aImage);
// Notify this proxy's listener of the current state of the request.
void NotifyListener();
protected:
friend class imgStatusTracker;
class imgCancelRunnable;
friend class imgCancelRunnable;
@ -129,10 +136,6 @@ protected:
void OnStartRequest();
void OnStopRequest(PRBool aLastPart);
inline PRBool HasObserver() const {
return mListener != nsnull;
}
/* Finish up canceling ourselves */
void DoCancel(nsresult status);
@ -143,8 +146,6 @@ protected:
RemoveFromLoadGroup(PR_TRUE);
}
nsresult GetState(PRUint32 *aState);
private:
friend class imgCacheValidator;
@ -161,15 +162,12 @@ private:
// The image we represent. Is null until data has been received, and is then
// set by imgRequest.
nsCOMPtr<imgIContainer> mImage;
nsRefPtr<imgContainer> mImage;
// Our principal. Is null until data has been received from the channel, and
// is then set by imgRequest.
nsCOMPtr<nsIPrincipal> mPrincipal;
PRUint32 mImageStatus;
PRUint32 mState;
// mListener is only promised to be a weak ref (see imgILoader.idl),
// but we actually keep a strong ref to it until we've seen our
// first OnStopRequest.
@ -182,8 +180,6 @@ private:
PRPackedBool mIsInLoadGroup;
PRPackedBool mListenerIsStrongRef;
PRPackedBool mDecodeRequested;
// Whether we've seen the last part of the load. For normal,
// non-multipart/x-mixed-replace loads, this is true once OnStopRequest has
// been received.
PRPackedBool mHadLastPart;
};
#endif // imgRequestProxy_h__

View File

@ -0,0 +1,351 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
*
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Joe Drew <joe@drew.ca> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "imgStatusTracker.h"
#include "imgRequest.h"
#include "imgIContainer.h"
#include "imgRequestProxy.h"
static nsresult
GetResultFromImageStatus(PRUint32 aStatus)
{
if (aStatus & imgIRequest::STATUS_ERROR)
return NS_IMAGELIB_ERROR_FAILURE;
if (aStatus & imgIRequest::STATUS_LOAD_COMPLETE)
return NS_IMAGELIB_SUCCESS_LOAD_FINISHED;
return NS_OK;
}
imgStatusTracker::imgStatusTracker(imgIContainer* aImage)
: mImage(aImage),
mState(0),
mImageStatus(imgIRequest::STATUS_NONE),
mHadLastPart(PR_FALSE)
{}
PRBool
imgStatusTracker::IsLoading() const
{
// Checking for whether OnStopRequest has fired allows us to say we're
// loading before OnStartRequest gets called, letting the request properly
// get removed from the cache in certain cases.
return !(mState & stateRequestStopped);
}
PRUint32
imgStatusTracker::GetImageStatus() const
{
return mImageStatus;
}
void
imgStatusTracker::Notify(imgRequestProxy* proxy)
{
nsCOMPtr<imgIRequest> kungFuDeathGrip(proxy);
// OnStartRequest
if (mState & stateRequestStarted)
proxy->OnStartRequest();
// OnStartContainer
if (mState & stateHasSize)
proxy->OnStartContainer(mImage);
// OnStartDecode
if (mState & stateDecodeStarted)
proxy->OnStartDecode();
// Send frame messages (OnStartFrame, OnDataAvailable, OnStopFrame)
PRUint32 nframes = 0;
mImage->GetNumFrames(&nframes);
if (nframes > 0) {
PRUint32 frame;
mImage->GetCurrentFrameIndex(&frame);
proxy->OnStartFrame(frame);
// OnDataAvailable
// XXX - Should only send partial rects here, but that needs to
// wait until we fix up the observer interface
nsIntRect r;
mImage->GetCurrentFrameRect(r);
proxy->OnDataAvailable(frame, &r);
if (mState & stateFrameStopped)
proxy->OnStopFrame(frame);
}
// See bug 505385 and imgRequest::OnStopDecode for more information on why we
// call OnStopContainer based on stateDecodeStopped, and why OnStopDecode is
// called with OnStopRequest.
if (mState & stateDecodeStopped)
proxy->OnStopContainer(mImage);
if (mState & stateRequestStopped) {
proxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), nsnull);
proxy->OnStopRequest(mHadLastPart);
}
}
void
imgStatusTracker::EmulateRequestFinished(imgRequestProxy* aProxy, nsresult aStatus,
PRBool aOnlySendStopRequest)
{
nsCOMPtr<imgIRequest> kungFuDeathGrip(aProxy);
if (!aOnlySendStopRequest) {
// The "real" OnStopDecode - fix this with bug 505385.
if (!(mState & stateDecodeStopped)) {
aProxy->OnStopContainer(mImage);
}
if (!(mState & stateRequestStopped)) {
aProxy->OnStopDecode(aStatus, nsnull);
}
}
if (!(mState & stateRequestStopped)) {
aProxy->OnStopRequest(PR_TRUE);
}
}
void
imgStatusTracker::RecordCancel()
{
if (!(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL))
mImageStatus |= imgIRequest::STATUS_ERROR;
}
void
imgStatusTracker::RecordLoaded()
{
mState |= stateRequestStarted | stateHasSize | stateRequestStopped;
mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE | imgIRequest::STATUS_LOAD_COMPLETE;
mHadLastPart = PR_TRUE;
}
void
imgStatusTracker::RecordDecoded()
{
mState |= stateDecodeStarted | stateDecodeStopped | stateFrameStopped;
mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE | imgIRequest::STATUS_DECODE_COMPLETE;
}
/* non-virtual imgIDecoderObserver methods */
void
imgStatusTracker::RecordStartDecode()
{
mState |= stateDecodeStarted;
}
void
imgStatusTracker::SendStartDecode(imgRequestProxy* aProxy)
{
aProxy->OnStartDecode();
}
void
imgStatusTracker::RecordStartContainer(imgIContainer* aContainer)
{
mState |= stateHasSize;
mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE;
}
void
imgStatusTracker::SendStartContainer(imgRequestProxy* aProxy, imgIContainer* aContainer)
{
// We only want to send onStartContainer once, but we might get multiple
// OnStartContainer calls (e.g. from multipart/x-mixed-replace).
PRBool alreadySent = (mState & stateHasSize) != 0;
if (!alreadySent)
aProxy->OnStartContainer(aContainer);
}
void
imgStatusTracker::RecordStartFrame(PRUint32 aFrame)
{
// no bookkeeping necessary here - this is implied by imgIContainer's number
// of frames
}
void
imgStatusTracker::SendStartFrame(imgRequestProxy* aProxy, PRUint32 aFrame)
{
aProxy->OnStartFrame(aFrame);
}
void
imgStatusTracker::RecordDataAvailable(PRBool aCurrentFrame, const nsIntRect* aRect)
{
// no bookkeeping necessary here - this is implied by imgIContainer's
// number of frames and frame rect
}
void
imgStatusTracker::SendDataAvailable(imgRequestProxy* aProxy, PRBool aCurrentFrame,
const nsIntRect* aRect)
{
aProxy->OnDataAvailable(aCurrentFrame, aRect);
}
void
imgStatusTracker::RecordStopFrame(PRUint32 aFrame)
{
mState |= stateFrameStopped;
mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE;
}
void
imgStatusTracker::SendStopFrame(imgRequestProxy* aProxy, PRUint32 aFrame)
{
aProxy->OnStopFrame(aFrame);
}
void
imgStatusTracker::RecordStopContainer(imgIContainer* aContainer)
{
// No-op: see imgRequest::OnStopDecode for more information
}
void
imgStatusTracker::SendStopContainer(imgRequestProxy* aProxy, imgIContainer* aContainer)
{
// No-op: see imgRequest::OnStopDecode for more information
}
void
imgStatusTracker::RecordStopDecode(nsresult aStatus, const PRUnichar* statusArg)
{
mState |= stateDecodeStopped;
if (NS_SUCCEEDED(aStatus))
mImageStatus |= imgIRequest::STATUS_DECODE_COMPLETE;
// If we weren't successful, clear all success status bits and set error.
else
mImageStatus = imgIRequest::STATUS_ERROR;
}
void
imgStatusTracker::SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus,
const PRUnichar* statusArg)
{
// See imgRequest::OnStopDecode for more information on why we call
// OnStopContainer from here this, and why imgRequestProxy::OnStopDecode() is
// called from OnStopRequest().
aProxy->OnStopContainer(mImage);
}
void
imgStatusTracker::RecordDiscard()
{
// Clear the state bits we no longer deserve.
PRUint32 stateBitsToClear = stateDecodeStarted | stateDecodeStopped;
mState &= ~stateBitsToClear;
// Clear the status bits we no longer deserve.
PRUint32 statusBitsToClear = imgIRequest::STATUS_FRAME_COMPLETE
| imgIRequest::STATUS_DECODE_COMPLETE;
mImageStatus &= ~statusBitsToClear;
}
void
imgStatusTracker::SendDiscard(imgRequestProxy* aProxy)
{
aProxy->OnDiscard();
}
/* non-virtual imgIContainerObserver methods */
void
imgStatusTracker::RecordFrameChanged(imgIContainer* aContainer, nsIntRect* aDirtyRect)
{
// no bookkeeping necessary here - this is only for in-frame updates, which we
// don't fire while we're recording
}
void
imgStatusTracker::SendFrameChanged(imgRequestProxy* aProxy, imgIContainer* aContainer,
nsIntRect* aDirtyRect)
{
aProxy->FrameChanged(aContainer, aDirtyRect);
}
/* non-virtual sort-of-nsIRequestObserver methods */
void
imgStatusTracker::RecordStartRequest()
{
// We're starting a new load, so clear any status and state bits indicating
// load/decode
mImageStatus &= ~imgIRequest::STATUS_LOAD_PARTIAL;
mImageStatus &= ~imgIRequest::STATUS_LOAD_COMPLETE;
mImageStatus &= ~imgIRequest::STATUS_FRAME_COMPLETE;
mState &= ~stateRequestStarted;
mState &= ~stateDecodeStarted;
mState &= ~stateDecodeStopped;
mState &= ~stateRequestStopped;
mState |= stateRequestStarted;
}
void
imgStatusTracker::SendStartRequest(imgRequestProxy* aProxy)
{
aProxy->OnStartRequest();
}
void
imgStatusTracker::RecordStopRequest(PRBool aLastPart, nsresult aStatus)
{
mHadLastPart = aLastPart;
mState |= stateRequestStopped;
// If we were successful in loading, note that the image is complete.
if (NS_SUCCEEDED(aStatus))
mImageStatus |= imgIRequest::STATUS_LOAD_COMPLETE;
}
void
imgStatusTracker::SendStopRequest(imgRequestProxy* aProxy, PRBool aLastPart, nsresult aStatus)
{
// See bug 505385 and imgRequest::OnStopDecode for more information on why
// OnStopDecode is called with OnStopRequest.
aProxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), nsnull);
aProxy->OnStopRequest(aLastPart);
}

View File

@ -0,0 +1,146 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
*
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Joe Drew <joe@drew.ca> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef imgStatusTracker_h__
#define imgStatusTracker_h__
class nsIntRect;
class imgIContainer;
class imgRequest;
class imgRequestProxy;
#include "prtypes.h"
#include "nscore.h"
enum {
stateRequestStarted = PR_BIT(0),
stateHasSize = PR_BIT(1),
stateDecodeStarted = PR_BIT(2),
stateDecodeStopped = PR_BIT(3),
stateFrameStopped = PR_BIT(4),
stateRequestStopped = PR_BIT(5)
};
/*
* The image status tracker is a class that encapsulates all the loading and
* decoding status about an image (imgContainer), and makes it possible to send
* notifications to imgRequestProxys. When a new proxy needs to be notified of
* the current state of an image, simply call the Notify() method on this class
* with the relevant proxy as its argument.
*/
class imgStatusTracker
{
public:
// aImage is the image that this status tracker will pass to the
// imgRequestProxys in Notify() and EmulateRequestFinished(), and must be
// alive as long as this instance is, because we hold a weak reference to it.
imgStatusTracker(imgIContainer* aImage);
// "Replay" all of the notifications that would have to happen to put us in
// the state we're currently in.
void Notify(imgRequestProxy* proxy);
// Send all notifications that would be necessary to make |proxy| believe the
// request is finished downloading and decoding.
// If aOnlySendStopRequest is true, we will only send OnStopRequest, and then
// only if that is necessary.
void EmulateRequestFinished(imgRequestProxy* proxy, nsresult aStatus,
PRBool aOnlySendStopRequest);
// Returns whether we are in the process of loading; that is, whether we have
// not received OnStopRequest.
PRBool IsLoading() const;
// Get the current image status (as in imgIRequest).
PRUint32 GetImageStatus() const;
// Following are all the notification methods. You must call the Record
// variant on this status tracker, then call the Send variant for each proxy
// you want to notify.
// Call when the request is being cancelled.
void RecordCancel();
// Shorthand for recording all the load notifications: StartRequest,
// StartContainer, StopRequest.
void RecordLoaded();
// Shorthand for recording all the decode notifications: StartDecode,
// StartFrame, DataAvailable, StopFrame, StopContainer, StopDecode.
void RecordDecoded();
/* non-virtual imgIDecoderObserver methods */
void RecordStartDecode();
void SendStartDecode(imgRequestProxy* aProxy);
void RecordStartContainer(imgIContainer* aContainer);
void SendStartContainer(imgRequestProxy* aProxy, imgIContainer* aContainer);
void RecordStartFrame(PRUint32 aFrame);
void SendStartFrame(imgRequestProxy* aProxy, PRUint32 aFrame);
void RecordDataAvailable(PRBool aCurrentFrame, const nsIntRect* aRect);
void SendDataAvailable(imgRequestProxy* aProxy, PRBool aCurrentFrame, const nsIntRect* aRect);
void RecordStopFrame(PRUint32 aFrame);
void SendStopFrame(imgRequestProxy* aProxy, PRUint32 aFrame);
void RecordStopContainer(imgIContainer* aContainer);
void SendStopContainer(imgRequestProxy* aProxy, imgIContainer* aContainer);
void RecordStopDecode(nsresult status, const PRUnichar* statusArg);
void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus, const PRUnichar* statusArg);
void RecordDiscard();
void SendDiscard(imgRequestProxy* aProxy);
/* non-virtual imgIContainerObserver methods */
void RecordFrameChanged(imgIContainer* aContainer, nsIntRect* aDirtyRect);
void SendFrameChanged(imgRequestProxy* aProxy, imgIContainer* aContainer, nsIntRect* aDirtyRect);
/* non-virtual sort-of-nsIRequestObserver methods */
void RecordStartRequest();
void SendStartRequest(imgRequestProxy* aProxy);
void RecordStopRequest(PRBool aLastPart, nsresult aStatus);
void SendStopRequest(imgRequestProxy* aProxy, PRBool aLastPart, nsresult aStatus);
private:
// A weak pointer to the imgIContainer, because the container owns us, and we
// can't create a cycle.
imgIContainer* mImage;
PRUint32 mState;
nsresult mImageStatus;
PRPackedBool mHadLastPart;
};
#endif