Make imgRequestProxy hold a weak ref to its listener, to prevent reference

cycles.  Bug 196797, r=pavlov, sr=jst
This commit is contained in:
bzbarsky@mit.edu
2003-03-17 16:09:02 +00:00
parent 6983791836
commit ad7defd22e
11 changed files with 120 additions and 22 deletions

View File

@@ -200,6 +200,9 @@ nsHTMLImageElement::nsHTMLImageElement()
nsHTMLImageElement::~nsHTMLImageElement()
{
if (mRequest) {
mRequest->Cancel(NS_ERROR_FAILURE);
}
}
@@ -817,9 +820,9 @@ nsHTMLImageElement::SetSrcInner(nsIURI* aBaseURL,
// If we have a loader we're in the middle of loading a image,
// we'll cancel that load and start a new one.
// if (mRequest) {
// mRequest->Cancel() ?? cancel the load?
// }
if (mRequest) {
mRequest->Cancel(NS_ERROR_FAILURE);
}
nsCOMPtr<imgILoader> il(do_GetService("@mozilla.org/image/loader;1"));
if (!il) {

View File

@@ -158,6 +158,7 @@ NS_IMPL_THREADSAFE_ISUPPORTS2(ImageListener,
NS_IMETHODIMP
ImageListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
{
NS_PRECONDITION(!mDocument->mImageRequest, "OnStartRequest called twice!");
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
if (!channel) {
return NS_ERROR_FAILURE;
@@ -217,6 +218,9 @@ nsImageDocument::nsImageDocument()
nsImageDocument::~nsImageDocument()
{
if (mImageRequest) {
mImageRequest->Cancel(NS_ERROR_FAILURE);
}
}
NS_IMPL_ADDREF_INHERITED(nsImageDocument, nsHTMLDocument)
@@ -302,10 +306,14 @@ NS_IMETHODIMP
nsImageDocument::SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObject)
{
if (!aScriptGlobalObject) {
// If the global object is being set to null, then it means we are
// going away soon. Drop our ref to imgRequest so that we don't end
// up leaking due to cycles through imgLib
// If the global object is being set to null, then it means we are going
// away soon. Drop our ref to imgRequest so that we don't end up leaking
// due to cycles through imgLib. This should not be really necessary
// anymore, but it's a belt-and-suspenders thing.
if (mImageRequest) {
mImageRequest->Cancel(NS_ERROR_FAILURE);
mImageRequest = nsnull;
}
if (mImageResizingEnabled) {
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mImageElement);

View File

@@ -115,6 +115,9 @@ nsImageLoader::Load(nsIURI *aURI)
if (eq) {
return NS_OK;
}
// Now cancel the old request so it won't hold a stale ref to us.
mRequest->Cancel(NS_ERROR_FAILURE);
}
nsCOMPtr<imgILoader> il(do_GetService("@mozilla.org/image/loader;1", &rv));

View File

@@ -115,6 +115,9 @@ nsImageLoader::Load(nsIURI *aURI)
if (eq) {
return NS_OK;
}
// Now cancel the old request so it won't hold a stale ref to us.
mRequest->Cancel(NS_ERROR_FAILURE);
}
nsCOMPtr<imgILoader> il(do_GetService("@mozilla.org/image/loader;1", &rv));

View File

@@ -272,6 +272,15 @@ private:
// broken image and loading image icons
public:
IconLoad(nsIPresContext *aPresContext):mRefCount(0),mIconsLoaded(PR_FALSE) { GetPrefs(aPresContext); }
~IconLoad()
{
if (mIconLoads[0].mRequest) {
mIconLoads[0].mRequest->Cancel(NS_ERROR_FAILURE);
}
if (mIconLoads[1].mRequest) {
mIconLoads[1].mRequest->Cancel(NS_ERROR_FAILURE);
}
}
void AddRef(void) { ++mRefCount; }
PRBool Release(void) { return --mRefCount == 0; }
void GetPrefs(nsIPresContext *aPresContext);

View File

@@ -272,6 +272,15 @@ private:
// broken image and loading image icons
public:
IconLoad(nsIPresContext *aPresContext):mRefCount(0),mIconsLoaded(PR_FALSE) { GetPrefs(aPresContext); }
~IconLoad()
{
if (mIconLoads[0].mRequest) {
mIconLoads[0].mRequest->Cancel(NS_ERROR_FAILURE);
}
if (mIconLoads[1].mRequest) {
mIconLoads[1].mRequest->Cancel(NS_ERROR_FAILURE);
}
}
void AddRef(void) { ++mRefCount; }
PRBool Release(void) { return --mRefCount == 0; }
void GetPrefs(nsIPresContext *aPresContext);

View File

@@ -424,7 +424,10 @@ nsImageBoxFrame::UpdateImage(nsIPresContext* aPresContext, PRBool& aResize)
mHasImage = PR_FALSE;
aResize = PR_TRUE;
if (mImageRequest) {
mImageRequest->Cancel(NS_ERROR_FAILURE);
mImageRequest = nsnull;
}
return;
}

View File

@@ -101,6 +101,21 @@
#define ELLIPSIS "..."
// Enumeration function that cancels all the image requests in our cache
PR_STATIC_CALLBACK(PRBool)
CancelImageRequest(nsHashKey* aKey, void* aData, void* aClosure)
{
nsISupports* supports = NS_STATIC_CAST(nsISupports*, aData);
nsCOMPtr<imgIRequest> request = do_QueryInterface(supports);
nsCOMPtr<imgIDecoderObserver> observer;
request->GetDecoderObserver(getter_AddRefs(observer));
NS_ASSERTION(observer, "No observer? We're leaking!");
request->Cancel(NS_ERROR_FAILURE);
imgIDecoderObserver* observer2 = observer;
NS_RELEASE(observer2); // Balance out the addref from GetImage()
return PR_TRUE;
}
// The style context cache impl
nsStyleContext*
nsTreeStyleCache::GetStyleContext(nsICSSPseudoComparator* aComparator,
@@ -332,7 +347,10 @@ nsTreeBodyFrame::nsTreeBodyFrame(nsIPresShell* aPresShell)
// Destructor
nsTreeBodyFrame::~nsTreeBodyFrame()
{
if (mImageCache) {
mImageCache->Enumerate(CancelImageRequest);
delete mImageCache;
}
}
NS_IMETHODIMP_(nsrefcnt)
@@ -1877,6 +1895,7 @@ nsTreeBodyFrame::GetImage(PRInt32 aRowIndex, const PRUnichar* aColID, PRBool aUs
return NS_ERROR_OUT_OF_MEMORY;
listener->AddRow(aRowIndex);
nsCOMPtr<imgIDecoderObserver> imgDecoderObserver = listener;
nsCOMPtr<nsIURI> baseURI;
nsCOMPtr<nsIDocument> doc;
@@ -1903,8 +1922,8 @@ nsTreeBodyFrame::GetImage(PRInt32 aRowIndex, const PRUnichar* aColID, PRBool aUs
mImageGuard = PR_TRUE;
// XXX: initialDocumentURI is NULL!
rv = il->LoadImage(srcURI, nsnull, documentURI, nsnull, listener, doc,
nsIRequest::LOAD_NORMAL, nsnull, nsnull,
rv = il->LoadImage(srcURI, nsnull, documentURI, nsnull, imgDecoderObserver,
doc, nsIRequest::LOAD_NORMAL, nsnull, nsnull,
getter_AddRefs(imageRequest));
mImageGuard = PR_FALSE;
@@ -1920,6 +1939,8 @@ nsTreeBodyFrame::GetImage(PRInt32 aRowIndex, const PRUnichar* aColID, PRBool aUs
return NS_ERROR_OUT_OF_MEMORY;
}
mImageCache->Put(&key, imageRequest);
imgIDecoderObserver* decoderObserverPtr = imgDecoderObserver;
NS_ADDREF(decoderObserverPtr); // Will get released when we remove the cache entry.
}
return NS_OK;
}
@@ -3462,7 +3483,10 @@ NS_IMETHODIMP
nsTreeBodyFrame::ClearStyleAndImageCaches()
{
mStyleCache.Clear();
if (mImageCache) {
mImageCache->Enumerate(CancelImageRequest);
delete mImageCache;
}
mImageCache = nsnull;
mScrollbar = nsnull;
return NS_OK;

View File

@@ -58,6 +58,12 @@ interface imgILoader : nsISupports
* image came from a request that had post data
* @param aRequest A newly created, unused imgIRequest object or NULL for one to
be created for you.
* libpr0n does NOT keep a strong ref to the observer; this prevents
* reference cycles. This means that callers of loadImage should
* make sure to Cancel() the resulting request before the observer
* goes away.
*/
imgIRequest loadImage(in nsIURI aURI,
in nsIURI aInitialDocumentURL,
@@ -74,6 +80,10 @@ interface imgILoader : nsISupports
* @param uri the URI to load
* @param aObserver the observer
* @param cx some random data
*
* libpr0n does NOT keep a strong ref to the observer; this prevents
* reference cycles. This means that callers of loadImageWithChannel should
* make sure to Cancel() the resulting request before the observer goes away.
*/
imgIRequest loadImageWithChannel(in nsIChannel aChannel, in imgIDecoderObserver aObserver, in nsISupports cx, out nsIStreamListener aListener);

View File

@@ -42,6 +42,7 @@ NS_IMPL_THREADSAFE_ISUPPORTS2(imgRequestProxy, imgIRequest, nsIRequest)
imgRequestProxy::imgRequestProxy() :
mOwner(nsnull),
mListener(nsnull),
mLoadFlags(nsIRequest::LOAD_NORMAL),
mCanceled(PR_FALSE),
mIsInLoadGroup(PR_FALSE),
@@ -55,6 +56,7 @@ imgRequestProxy::imgRequestProxy() :
imgRequestProxy::~imgRequestProxy()
{
/* destructor code */
NS_PRECONDITION(!mListener, "Someone forgot to properly cancel this request!");
if (mOwner) {
if (!mCanceled) {
@@ -294,8 +296,11 @@ void imgRequestProxy::FrameChanged(imgIContainer *container, gfxIImageFrame *new
{
LOG_FUNC(gImgLog, "imgRequestProxy::FrameChanged");
if (mListener)
if (mListener) {
// Hold a ref to the listener while we call it, just in case.
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
mListener->FrameChanged(container, newframe, dirtyRect);
}
}
/** imgIDecoderObserver methods **/
@@ -304,56 +309,77 @@ void imgRequestProxy::OnStartDecode()
{
LOG_FUNC(gImgLog, "imgRequestProxy::OnStartDecode");
if (mListener)
if (mListener) {
// Hold a ref to the listener while we call it, just in case.
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
mListener->OnStartDecode(this);
}
}
void imgRequestProxy::OnStartContainer(imgIContainer *image)
{
LOG_FUNC(gImgLog, "imgRequestProxy::OnStartContainer");
if (mListener)
if (mListener) {
// Hold a ref to the listener while we call it, just in case.
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
mListener->OnStartContainer(this, image);
}
}
void imgRequestProxy::OnStartFrame(gfxIImageFrame *frame)
{
LOG_FUNC(gImgLog, "imgRequestProxy::OnStartFrame");
if (mListener)
if (mListener) {
// Hold a ref to the listener while we call it, just in case.
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
mListener->OnStartFrame(this, frame);
}
}
void imgRequestProxy::OnDataAvailable(gfxIImageFrame *frame, const nsRect * rect)
{
LOG_FUNC(gImgLog, "imgRequestProxy::OnDataAvailable");
if (mListener)
if (mListener) {
// Hold a ref to the listener while we call it, just in case.
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
mListener->OnDataAvailable(this, frame, rect);
}
}
void imgRequestProxy::OnStopFrame(gfxIImageFrame *frame)
{
LOG_FUNC(gImgLog, "imgRequestProxy::OnStopFrame");
if (mListener)
if (mListener) {
// Hold a ref to the listener while we call it, just in case.
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
mListener->OnStopFrame(this, frame);
}
}
void imgRequestProxy::OnStopContainer(imgIContainer *image)
{
LOG_FUNC(gImgLog, "imgRequestProxy::OnStopContainer");
if (mListener)
if (mListener) {
// Hold a ref to the listener while we call it, just in case.
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
mListener->OnStopContainer(this, image);
}
}
void imgRequestProxy::OnStopDecode(nsresult status, const PRUnichar *statusArg)
{
LOG_FUNC(gImgLog, "imgRequestProxy::OnStopDecode");
if (mListener)
if (mListener) {
// Hold a ref to the listener while we call it, just in case.
nsCOMPtr<imgIDecoderObserver> kungFuDeathGrip(mListener);
mListener->OnStopDecode(this, status, statusArg);
}
}

View File

@@ -82,7 +82,7 @@ private:
imgRequest *mOwner;
nsCOMPtr<imgIDecoderObserver> mListener;
imgIDecoderObserver* mListener; // Weak ref; see imgILoader.idl
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsLoadFlags mLoadFlags;