From 4f028f9c9802e9b43267896a7f3db363a08f8b41 Mon Sep 17 00:00:00 2001 From: "pavlov%netscape.com" Date: Sun, 15 Apr 2001 02:13:49 +0000 Subject: [PATCH] fixing bug 75185 r=brendan sr=attinasi --- layout/generic/nsImageFrame.cpp | 276 +++++++++++++++++++------- layout/generic/nsImageFrame.h | 10 +- layout/html/base/src/nsImageFrame.cpp | 276 +++++++++++++++++++------- layout/html/base/src/nsImageFrame.h | 10 +- 4 files changed, 418 insertions(+), 154 deletions(-) diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 2e4028cf8520..118392ca0c6f 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -69,6 +69,8 @@ static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID); #include "nsIDOMNode.h" +#include "nsContentPolicyUtils.h" + #ifdef DEBUG #undef NOISY_IMAGE_LOADING @@ -189,13 +191,19 @@ nsImageFrame::Destroy(nsIPresContext* aPresContext) } #ifdef USE_IMG2 - if (mImageRequest) - mImageRequest->Cancel(NS_ERROR_FAILURE); // NS_BINDING_ABORT ? - if (mLowImageRequest) - mLowImageRequest->Cancel(NS_ERROR_FAILURE); // NS_BINDING_ABORT ? + if (mImageRequest) + mImageRequest->Cancel(NS_ERROR_FAILURE); // NS_BINDING_ABORT ? + if (mLowImageRequest) + mLowImageRequest->Cancel(NS_ERROR_FAILURE); // NS_BINDING_ABORT ? + + // set the frame to null so we don't send messages to a dead object. + if (mListener) + NS_REINTERPRET_CAST(nsImageListener*, mListener.get())->SetFrame(nsnull); + + mImageRequest = nsnull; + mLowImageRequest = nsnull; + mListener = nsnull; - if (mListener) - NS_REINTERPRET_CAST(nsImageListener*, mListener.get())->SetFrame(nsnull); // set the frame to null so we don't send messages to a dead object. #endif #ifndef USE_IMG2 @@ -245,19 +253,21 @@ nsImageFrame::Init(nsIPresContext* aPresContext, nsresult lowSrcResult; lowSrcResult = mContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::lowsrc, lowSrc); - // Set the image loader's source URL and base URL +#ifndef USE_IMG2 nsCOMPtr baseURL; GetBaseURI(getter_AddRefs(baseURL)); +#endif + // Set the image loader's source URL and base URL #ifdef USE_IMG2 - nsImageListener *listener; - NS_NEWXPCOM(listener, nsImageListener); - NS_ADDREF(listener); - listener->SetFrame(this); - listener->QueryInterface(NS_GET_IID(imgIDecoderObserver), getter_AddRefs(mListener)); - NS_ASSERTION(mListener, "queryinterface for the listener failed"); - NS_RELEASE(listener); - + if (!mListener) { + nsImageListener *listener = new nsImageListener(this); + if (!listener) return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(listener); + listener->QueryInterface(NS_GET_IID(imgIDecoderObserver), getter_AddRefs(mListener)); + NS_ASSERTION(mListener, "queryinterface for the listener failed"); + NS_RELEASE(listener); + } nsCOMPtr il(do_GetService("@mozilla.org/image/loader;1", &rv)); if (NS_FAILED(rv)) @@ -270,9 +280,11 @@ nsImageFrame::Init(nsIPresContext* aPresContext, if (NS_CONTENT_ATTR_HAS_VALUE == lowSrcResult && lowSrc.Length() > 0) { #ifdef USE_IMG2 nsCOMPtr lowURI; - NS_NewURI(getter_AddRefs(lowURI), src, baseURL); + GetURI(lowSrc, getter_AddRefs(lowURI)); - il->LoadImage(lowURI, loadGroup, mListener, aPresContext, getter_AddRefs(mLowImageRequest)); + if (CanLoadImage(lowURI)) { + il->LoadImage(lowURI, loadGroup, mListener, aPresContext, getter_AddRefs(mLowImageRequest)); + } #else mLowSrcImageLoader = new nsHTMLImageLoader; if (mLowSrcImageLoader) { @@ -287,9 +299,11 @@ nsImageFrame::Init(nsIPresContext* aPresContext, mCanSendLoadEvent = PR_TRUE; nsCOMPtr srcURI; - NS_NewURI(getter_AddRefs(srcURI), src, baseURL); - il->LoadImage(srcURI, loadGroup, mListener, aPresContext, getter_AddRefs(mImageRequest)); - // if the image was found in the cache, it is possible that LoadImage will result in a call to OnStartContainer() + GetURI(src, getter_AddRefs(srcURI)); + if (CanLoadImage(srcURI)) { + il->LoadImage(srcURI, loadGroup, mListener, aPresContext, getter_AddRefs(mImageRequest)); + // if the image was found in the cache, it is possible that LoadImage will result in a call to OnStartContainer() + } #else mImageLoader.Init(this, UpdateImageFrame, (void*)&mImageLoader, baseURL, src); @@ -324,10 +338,6 @@ NS_IMETHODIMP nsImageFrame::OnStartDecode(imgIRequest *aRequest, nsIPresContext NS_IMETHODIMP nsImageFrame::OnStartContainer(imgIRequest *aRequest, nsIPresContext *aPresContext, imgIContainer *aImage) { - nsCOMPtr presShell; - nsresult rv = aPresContext->GetShell(getter_AddRefs(presShell)); - if (NS_FAILED(rv)) return rv; - mInitialLoadCompleted = PR_TRUE; nscoord w, h; @@ -350,26 +360,16 @@ NS_IMETHODIMP nsImageFrame::OnStartContainer(imgIRequest *aRequest, nsIPresConte if (mComputedSize.width != 0 && mComputedSize.height != 0) mTransform.SetToScale((float(mIntrinsicSize.width) / float(mComputedSize.width)), (float(mIntrinsicSize.height) / float(mComputedSize.height))); - if (mParent) { - if (mGotInitialReflow) { // don't reflow if we havn't gotten the inital reflow yet + if (!mSizeConstrained) { + nsCOMPtr presShell; + aPresContext->GetShell(getter_AddRefs(presShell)); + NS_ASSERTION(mParent, "No parent to pass the reflow request up to."); + NS_ASSERTION(presShell, "No PresShell."); + if (mParent && presShell && mGotInitialReflow) { // don't reflow if we havn't gotten the inital reflow yet mState |= NS_FRAME_IS_DIRTY; - mParent->ReflowDirtyChild(presShell, (nsIFrame*) this); + mParent->ReflowDirtyChild(presShell, NS_STATIC_CAST(nsIFrame*, this)); } } - else { - NS_ASSERTION(0, "No parent to pass the reflow request up to."); - } - } - - if (mCanSendLoadEvent && presShell) { - // Send load event - mCanSendLoadEvent = PR_FALSE; - - nsEventStatus status = nsEventStatus_eIgnore; - nsEvent event; - event.eventStructType = NS_EVENT; - event.message = NS_IMAGE_LOAD; - presShell->HandleEventWithTarget(&event,this,mContent,NS_EVENT_FLAG_INIT | NS_EVENT_FLAG_CANT_BUBBLE,&status); } return NS_OK; @@ -382,19 +382,18 @@ NS_IMETHODIMP nsImageFrame::OnStartFrame(imgIRequest *aRequest, nsIPresContext * NS_IMETHODIMP nsImageFrame::OnDataAvailable(imgIRequest *aRequest, nsIPresContext *aPresContext, gfxIImageFrame *aFrame, const nsRect *aRect) { - nsCOMPtr presShell; - nsresult rv = aPresContext->GetShell(getter_AddRefs(presShell)); - if (NS_FAILED(rv)) return rv; - - // XXX we need to make sure that the reflow from the OnContainerStart has been + // XXX do we need to make sure that the reflow from the OnStartContainer has been // processed before we start calling invalidate if (!aRect) return NS_ERROR_NULL_POINTER; - nsRect r(aRect->x, aRect->y, aRect->width, aRect->height); + /* XXX Why do we subtract 1 here? The rect is (for example): (0, 0, 600, 1).. + Why do we have to make y -1? + */ + // The y coordinate of aRect is passed as a scanline where the first scanline is given // a value of 1. We need to convert this to the nsFrames coordinate space by subtracting // 1. @@ -426,15 +425,76 @@ NS_IMETHODIMP nsImageFrame::OnStopContainer(imgIRequest *aRequest, nsIPresContex NS_IMETHODIMP nsImageFrame::OnStopDecode(imgIRequest *aRequest, nsIPresContext *aPresContext, nsresult aStatus, const PRUnichar *aStatusArg) { + nsCOMPtr presShell; + aPresContext->GetShell(getter_AddRefs(presShell)); + + // check to see if an image error occurred + PRBool imageFailedToLoad = PR_FALSE; + + if (NS_FAILED(aStatus)) { // We failed to load the image. Notify the pres shell + PRBool lowFailed = PR_FALSE; + PRBool imageFailed = PR_FALSE; + + // One of the two images didn't load, which one? + if (mLowImageRequest == aRequest || !mLowImageRequest) { + lowFailed = PR_TRUE; + } + if (mImageRequest == aRequest || !mImageRequest) { + imageFailed = PR_TRUE; + } + + if (imageFailed && lowFailed) + imageFailedToLoad = PR_TRUE; + } + + // if src failed and there is no lowsrc + // or both failed to load, then notify the PresShell + if (imageFailedToLoad) { + if (presShell) { + nsAutoString usemap; + mContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::usemap, usemap); + // We failed to load the image. Notify the pres shell if we aren't an image map + if (usemap.IsEmpty()) { + presShell->CantRenderReplacedElement(aPresContext, this); + } + } + } + + // After these DOM events are fired its possible that this frame may be deleted. As a result + // the code should not attempt to access any of the frames internal data after this point. + if (presShell) { + if (imageFailedToLoad) { + // Send error event + nsEventStatus status = nsEventStatus_eIgnore; + nsEvent event; + event.eventStructType = NS_EVENT; + event.message = NS_IMAGE_ERROR; + presShell->HandleEventWithTarget(&event,this,mContent,NS_EVENT_FLAG_INIT,&status); + } else if (mCanSendLoadEvent) { + // Send load event + mCanSendLoadEvent = PR_FALSE; + + nsEventStatus status = nsEventStatus_eIgnore; + nsEvent event; + event.eventStructType = NS_EVENT; + event.message = NS_IMAGE_LOAD; + presShell->HandleEventWithTarget(&event,this,mContent,NS_EVENT_FLAG_INIT | NS_EVENT_FLAG_CANT_BUBBLE,&status); + } + } + return NS_OK; } NS_IMETHODIMP nsImageFrame::FrameChanged(imgIContainer *aContainer, nsIPresContext *aPresContext, gfxIImageFrame *aNewFrame, nsRect *aDirtyRect) { + nsRect r(*aDirtyRect); + float p2t; aPresContext->GetPixelsToTwips(&p2t); - nsRect r(*aDirtyRect); - r *= p2t; // convert to twips + r.x = NSIntPixelsToTwips(r.x, p2t); + r.y = NSIntPixelsToTwips(r.y, p2t); + r.width = NSIntPixelsToTwips(r.width, p2t); + r.height = NSIntPixelsToTwips(r.height, p2t); mTransform.TransformCoord(&r.x, &r.y, &r.width, &r.height); @@ -487,7 +547,7 @@ nsImageFrame::UpdateImage(nsIPresContext* aPresContext, PRUint32 aStatus, void* nsAutoString usemap; mContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::usemap, usemap); // We failed to load the image. Notify the pres shell if we aren't an image map - if (usemap.Length() == 0) { + if (usemap.IsEmpty()) { presShell->CantRenderReplacedElement(aPresContext, this); } } @@ -616,7 +676,7 @@ nsImageFrame::GetDesiredSize(nsIPresContext* aPresContext, newHeight = (mIntrinsicSize.height * newWidth) / mIntrinsicSize.width; haveComputedSize = PR_TRUE; } else { - newHeight = NSIntPixelsToTwips(1, p2t); // XXX? + newHeight = 0; needIntrinsicImageSize = PR_TRUE; } } @@ -628,30 +688,31 @@ nsImageFrame::GetDesiredSize(nsIPresContext* aPresContext, newWidth = (mIntrinsicSize.width * newHeight) / mIntrinsicSize.height; haveComputedSize = PR_TRUE; } else { - newWidth = NSIntPixelsToTwips(1, p2t); + newWidth = 0; needIntrinsicImageSize = PR_TRUE; } } else { // auto size the image - if (mIntrinsicSize.width == 0 && mIntrinsicSize.height == 0) { - newWidth = NSIntPixelsToTwips(1, p2t); - newHeight = NSIntPixelsToTwips(1, p2t); + if (mIntrinsicSize.width == 0 && mIntrinsicSize.height == 0) needIntrinsicImageSize = PR_TRUE; - } else { - newWidth = mIntrinsicSize.width; - newHeight = mIntrinsicSize.height; + else haveComputedSize = PR_TRUE; - } + newWidth = mIntrinsicSize.width; + newHeight = mIntrinsicSize.height; } mComputedSize.width = newWidth; mComputedSize.height = newHeight; - if (mComputedSize == mIntrinsicSize) + if (mComputedSize == mIntrinsicSize) { mTransform.SetToIdentity(); - else - mTransform.SetToScale((float(mIntrinsicSize.width) / float(mComputedSize.width)), (float(mIntrinsicSize.height) / float(mComputedSize.height))); + } else { + if (mComputedSize.width != 0 && mComputedSize.height != 0) { + mTransform.SetToScale(float(mIntrinsicSize.width) / float(mComputedSize.width), + float(mIntrinsicSize.height) / float(mComputedSize.height)); + } + } aDesiredSize.width = mComputedSize.width; aDesiredSize.height = mComputedSize.height; @@ -977,12 +1038,12 @@ nsImageFrame::Paint(nsIPresContext* aPresContext, PRInt32 imgSrcLinesLoaded = -1; #ifdef USE_IMG2 - NS_ASSERTION(mImageRequest, "no image request! this is bad"); - nsCOMPtr imgCon; nsCOMPtr lowImgCon; - mImageRequest->GetImage(getter_AddRefs(imgCon)); + if (mImageRequest) { + mImageRequest->GetImage(getter_AddRefs(imgCon)); + } #else nsIImage * lowImage = nsnull; nsIImage * image = nsnull; @@ -1001,8 +1062,10 @@ nsImageFrame::Paint(nsIPresContext* aPresContext, #endif #ifdef USE_IMG2 - PRUint32 loadStatus; - mImageRequest->GetImageStatus(&loadStatus); + PRUint32 loadStatus = imgIRequest::STATUS_NONE; + if (mImageRequest) { + mImageRequest->GetImageStatus(&loadStatus); + } if (!(loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) || (!imgCon && !lowImgCon)) { #else image = mImageLoader.GetImage(); @@ -1143,7 +1206,7 @@ nsImageFrame::GetImageMap(nsIPresContext* aPresContext) if (nsnull == mImageMap) { nsAutoString usemap; mContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::usemap, usemap); - if (0 == usemap.Length()) { + if (usemap.IsEmpty()) { return nsnull; } @@ -1480,18 +1543,17 @@ nsImageFrame::AttributeChanged(nsIPresContext* aPresContext, PRUint32 loadStatus; #ifdef USE_IMG2 - nsCOMPtr baseURI; - GetBaseURI(getter_AddRefs(baseURI)); - nsCOMPtr loadGroup; GetLoadGroup(aPresContext, getter_AddRefs(loadGroup)); mImageRequest->GetImageStatus(&loadStatus); if (loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) { nsCOMPtr uri; - NS_NewURI(getter_AddRefs(uri), newSRC, baseURI); + GetURI(newSRC, getter_AddRefs(uri)); + nsCOMPtr il(do_GetService("@mozilla.org/image/loader;1")); NS_ASSERTION(il, "no image loader!"); + il->LoadImage(uri, loadGroup, mListener, aPresContext, getter_AddRefs(mImageRequest)); mImageRequest->GetImageStatus(&loadStatus); @@ -1516,9 +1578,11 @@ nsImageFrame::AttributeChanged(nsIPresContext* aPresContext, mCanSendLoadEvent = PR_TRUE; nsCOMPtr uri; - NS_NewURI(getter_AddRefs(uri), newSRC, baseURI); + GetURI(newSRC, getter_AddRefs(uri)); + nsCOMPtr il(do_GetService("@mozilla.org/image/loader;1")); NS_ASSERTION(il, "no image loader!"); + il->LoadImage(uri, loadGroup, mListener, aPresContext, getter_AddRefs(mImageRequest)); #else mImageLoader.StopLoadImage(aPresContext); @@ -1619,6 +1683,35 @@ void HaveFixedSize(const nsHTMLReflowState& aReflowState, PRPackedBool& aConstra heightUnit == eStyleUnit_Percent)); } +#define INTERNAL_GOPHER_STRING "internal-gopher-" +#define INTERNAL_GOPHER_LENGTH 17 + +void +nsImageFrame::GetURI(const nsAReadableString& aSpec, nsIURI **aURI) +{ + nsAutoString newURI; + + /* Note: navigator 4.* and earlier releases ignored the base tags + effect on the builtin images. So we do too. Use aSpec instead + of the absolute url... + */ + + /* The prefix for special "internal" images that are well known. + Look and see if this is an internal-gopher- url. + */ + if (NS_LITERAL_STRING(INTERNAL_GOPHER_STRING).Equals(Substring(aSpec, 0, INTERNAL_GOPHER_LENGTH))) { + newURI.Assign(NS_LITERAL_STRING("resource:/res/html/gopher-") + + Substring(aSpec, INTERNAL_GOPHER_LENGTH, aSpec.Length() - INTERNAL_GOPHER_LENGTH) + + NS_LITERAL_STRING(".gif")); + } else { + newURI.Assign(aSpec); + } + + nsCOMPtr baseURI; + GetBaseURI(getter_AddRefs(baseURI)); + NS_NewURI(aURI, newURI, baseURI); +} + void nsImageFrame::GetBaseURI(nsIURI **aURI) { @@ -1664,6 +1757,40 @@ nsImageFrame::GetLoadGroup(nsIPresContext *aPresContext, nsILoadGroup **aLoadGro } +// Check with the content-policy things to make sure this load is permitted. +PRBool +nsImageFrame::CanLoadImage(nsIURI *aURI) +{ + PRBool shouldLoad = PR_TRUE; // default permit + + // XXX leave this if 0'd until there is a good way to test it. +#if 0 + + nsresult rv; + nsCOMPtr element(do_QueryInterface(mContent)); + + if (!element) // this would seem bad(tm) + return PR_FALSE; + + nsXPIDLCString uric; + aURI->GetSpec(getter_Copies(uric)); + + nsString uri = NS_ConvertUTF8toUCS2(uric); + + rv = NS_CheckContentLoadPolicy(nsIContentPolicy::CONTENT_IMAGE, + uri, element, &shouldLoad); + if (NS_SUCCEEDED(rv) && !shouldLoad) + return PR_FALSE; + + + /* ... additional checks ? */ +#endif + + return shouldLoad; +} + + + #ifdef DEBUG NS_IMETHODIMP nsImageFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const @@ -1692,7 +1819,8 @@ nsImageFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const #ifdef USE_IMG2 NS_IMPL_ISUPPORTS2(nsImageListener, imgIDecoderObserver, imgIContainerObserver) -nsImageListener::nsImageListener() +nsImageListener::nsImageListener(nsImageFrame *aFrame) : + mFrame(aFrame) { NS_INIT_ISUPPORTS(); } diff --git a/layout/generic/nsImageFrame.h b/layout/generic/nsImageFrame.h index b2046bf5ea55..8e85fe59554b 100644 --- a/layout/generic/nsImageFrame.h +++ b/layout/generic/nsImageFrame.h @@ -24,6 +24,7 @@ #include "nsLeafFrame.h" #include "nsString.h" +#include "nsAReadableString.h" #include "nsIPresContext.h" #include "nsHTMLImageLoader.h" #include "nsIImageFrame.h" @@ -49,7 +50,7 @@ class nsImageFrame; class nsImageListener : imgIDecoderObserver { public: - nsImageListener(); + nsImageListener(nsImageFrame *aFrame); virtual ~nsImageListener(); NS_DECL_ISUPPORTS @@ -182,8 +183,11 @@ protected: PRUint32 aStatus); #endif - void GetBaseURI(nsIURI **uri); - void GetLoadGroup(nsIPresContext *aPresContext, nsILoadGroup **aLoadGroup); + PRBool CanLoadImage(nsIURI *aURI); + + inline void GetURI(const nsAReadableString& aSpec, nsIURI **aURI); + inline void GetBaseURI(nsIURI **uri); + inline void GetLoadGroup(nsIPresContext *aPresContext, nsILoadGroup **aLoadGroup); nsHTMLImageLoader mImageLoader; nsHTMLImageLoader * mLowSrcImageLoader; diff --git a/layout/html/base/src/nsImageFrame.cpp b/layout/html/base/src/nsImageFrame.cpp index 2e4028cf8520..118392ca0c6f 100644 --- a/layout/html/base/src/nsImageFrame.cpp +++ b/layout/html/base/src/nsImageFrame.cpp @@ -69,6 +69,8 @@ static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID); #include "nsIDOMNode.h" +#include "nsContentPolicyUtils.h" + #ifdef DEBUG #undef NOISY_IMAGE_LOADING @@ -189,13 +191,19 @@ nsImageFrame::Destroy(nsIPresContext* aPresContext) } #ifdef USE_IMG2 - if (mImageRequest) - mImageRequest->Cancel(NS_ERROR_FAILURE); // NS_BINDING_ABORT ? - if (mLowImageRequest) - mLowImageRequest->Cancel(NS_ERROR_FAILURE); // NS_BINDING_ABORT ? + if (mImageRequest) + mImageRequest->Cancel(NS_ERROR_FAILURE); // NS_BINDING_ABORT ? + if (mLowImageRequest) + mLowImageRequest->Cancel(NS_ERROR_FAILURE); // NS_BINDING_ABORT ? + + // set the frame to null so we don't send messages to a dead object. + if (mListener) + NS_REINTERPRET_CAST(nsImageListener*, mListener.get())->SetFrame(nsnull); + + mImageRequest = nsnull; + mLowImageRequest = nsnull; + mListener = nsnull; - if (mListener) - NS_REINTERPRET_CAST(nsImageListener*, mListener.get())->SetFrame(nsnull); // set the frame to null so we don't send messages to a dead object. #endif #ifndef USE_IMG2 @@ -245,19 +253,21 @@ nsImageFrame::Init(nsIPresContext* aPresContext, nsresult lowSrcResult; lowSrcResult = mContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::lowsrc, lowSrc); - // Set the image loader's source URL and base URL +#ifndef USE_IMG2 nsCOMPtr baseURL; GetBaseURI(getter_AddRefs(baseURL)); +#endif + // Set the image loader's source URL and base URL #ifdef USE_IMG2 - nsImageListener *listener; - NS_NEWXPCOM(listener, nsImageListener); - NS_ADDREF(listener); - listener->SetFrame(this); - listener->QueryInterface(NS_GET_IID(imgIDecoderObserver), getter_AddRefs(mListener)); - NS_ASSERTION(mListener, "queryinterface for the listener failed"); - NS_RELEASE(listener); - + if (!mListener) { + nsImageListener *listener = new nsImageListener(this); + if (!listener) return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(listener); + listener->QueryInterface(NS_GET_IID(imgIDecoderObserver), getter_AddRefs(mListener)); + NS_ASSERTION(mListener, "queryinterface for the listener failed"); + NS_RELEASE(listener); + } nsCOMPtr il(do_GetService("@mozilla.org/image/loader;1", &rv)); if (NS_FAILED(rv)) @@ -270,9 +280,11 @@ nsImageFrame::Init(nsIPresContext* aPresContext, if (NS_CONTENT_ATTR_HAS_VALUE == lowSrcResult && lowSrc.Length() > 0) { #ifdef USE_IMG2 nsCOMPtr lowURI; - NS_NewURI(getter_AddRefs(lowURI), src, baseURL); + GetURI(lowSrc, getter_AddRefs(lowURI)); - il->LoadImage(lowURI, loadGroup, mListener, aPresContext, getter_AddRefs(mLowImageRequest)); + if (CanLoadImage(lowURI)) { + il->LoadImage(lowURI, loadGroup, mListener, aPresContext, getter_AddRefs(mLowImageRequest)); + } #else mLowSrcImageLoader = new nsHTMLImageLoader; if (mLowSrcImageLoader) { @@ -287,9 +299,11 @@ nsImageFrame::Init(nsIPresContext* aPresContext, mCanSendLoadEvent = PR_TRUE; nsCOMPtr srcURI; - NS_NewURI(getter_AddRefs(srcURI), src, baseURL); - il->LoadImage(srcURI, loadGroup, mListener, aPresContext, getter_AddRefs(mImageRequest)); - // if the image was found in the cache, it is possible that LoadImage will result in a call to OnStartContainer() + GetURI(src, getter_AddRefs(srcURI)); + if (CanLoadImage(srcURI)) { + il->LoadImage(srcURI, loadGroup, mListener, aPresContext, getter_AddRefs(mImageRequest)); + // if the image was found in the cache, it is possible that LoadImage will result in a call to OnStartContainer() + } #else mImageLoader.Init(this, UpdateImageFrame, (void*)&mImageLoader, baseURL, src); @@ -324,10 +338,6 @@ NS_IMETHODIMP nsImageFrame::OnStartDecode(imgIRequest *aRequest, nsIPresContext NS_IMETHODIMP nsImageFrame::OnStartContainer(imgIRequest *aRequest, nsIPresContext *aPresContext, imgIContainer *aImage) { - nsCOMPtr presShell; - nsresult rv = aPresContext->GetShell(getter_AddRefs(presShell)); - if (NS_FAILED(rv)) return rv; - mInitialLoadCompleted = PR_TRUE; nscoord w, h; @@ -350,26 +360,16 @@ NS_IMETHODIMP nsImageFrame::OnStartContainer(imgIRequest *aRequest, nsIPresConte if (mComputedSize.width != 0 && mComputedSize.height != 0) mTransform.SetToScale((float(mIntrinsicSize.width) / float(mComputedSize.width)), (float(mIntrinsicSize.height) / float(mComputedSize.height))); - if (mParent) { - if (mGotInitialReflow) { // don't reflow if we havn't gotten the inital reflow yet + if (!mSizeConstrained) { + nsCOMPtr presShell; + aPresContext->GetShell(getter_AddRefs(presShell)); + NS_ASSERTION(mParent, "No parent to pass the reflow request up to."); + NS_ASSERTION(presShell, "No PresShell."); + if (mParent && presShell && mGotInitialReflow) { // don't reflow if we havn't gotten the inital reflow yet mState |= NS_FRAME_IS_DIRTY; - mParent->ReflowDirtyChild(presShell, (nsIFrame*) this); + mParent->ReflowDirtyChild(presShell, NS_STATIC_CAST(nsIFrame*, this)); } } - else { - NS_ASSERTION(0, "No parent to pass the reflow request up to."); - } - } - - if (mCanSendLoadEvent && presShell) { - // Send load event - mCanSendLoadEvent = PR_FALSE; - - nsEventStatus status = nsEventStatus_eIgnore; - nsEvent event; - event.eventStructType = NS_EVENT; - event.message = NS_IMAGE_LOAD; - presShell->HandleEventWithTarget(&event,this,mContent,NS_EVENT_FLAG_INIT | NS_EVENT_FLAG_CANT_BUBBLE,&status); } return NS_OK; @@ -382,19 +382,18 @@ NS_IMETHODIMP nsImageFrame::OnStartFrame(imgIRequest *aRequest, nsIPresContext * NS_IMETHODIMP nsImageFrame::OnDataAvailable(imgIRequest *aRequest, nsIPresContext *aPresContext, gfxIImageFrame *aFrame, const nsRect *aRect) { - nsCOMPtr presShell; - nsresult rv = aPresContext->GetShell(getter_AddRefs(presShell)); - if (NS_FAILED(rv)) return rv; - - // XXX we need to make sure that the reflow from the OnContainerStart has been + // XXX do we need to make sure that the reflow from the OnStartContainer has been // processed before we start calling invalidate if (!aRect) return NS_ERROR_NULL_POINTER; - nsRect r(aRect->x, aRect->y, aRect->width, aRect->height); + /* XXX Why do we subtract 1 here? The rect is (for example): (0, 0, 600, 1).. + Why do we have to make y -1? + */ + // The y coordinate of aRect is passed as a scanline where the first scanline is given // a value of 1. We need to convert this to the nsFrames coordinate space by subtracting // 1. @@ -426,15 +425,76 @@ NS_IMETHODIMP nsImageFrame::OnStopContainer(imgIRequest *aRequest, nsIPresContex NS_IMETHODIMP nsImageFrame::OnStopDecode(imgIRequest *aRequest, nsIPresContext *aPresContext, nsresult aStatus, const PRUnichar *aStatusArg) { + nsCOMPtr presShell; + aPresContext->GetShell(getter_AddRefs(presShell)); + + // check to see if an image error occurred + PRBool imageFailedToLoad = PR_FALSE; + + if (NS_FAILED(aStatus)) { // We failed to load the image. Notify the pres shell + PRBool lowFailed = PR_FALSE; + PRBool imageFailed = PR_FALSE; + + // One of the two images didn't load, which one? + if (mLowImageRequest == aRequest || !mLowImageRequest) { + lowFailed = PR_TRUE; + } + if (mImageRequest == aRequest || !mImageRequest) { + imageFailed = PR_TRUE; + } + + if (imageFailed && lowFailed) + imageFailedToLoad = PR_TRUE; + } + + // if src failed and there is no lowsrc + // or both failed to load, then notify the PresShell + if (imageFailedToLoad) { + if (presShell) { + nsAutoString usemap; + mContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::usemap, usemap); + // We failed to load the image. Notify the pres shell if we aren't an image map + if (usemap.IsEmpty()) { + presShell->CantRenderReplacedElement(aPresContext, this); + } + } + } + + // After these DOM events are fired its possible that this frame may be deleted. As a result + // the code should not attempt to access any of the frames internal data after this point. + if (presShell) { + if (imageFailedToLoad) { + // Send error event + nsEventStatus status = nsEventStatus_eIgnore; + nsEvent event; + event.eventStructType = NS_EVENT; + event.message = NS_IMAGE_ERROR; + presShell->HandleEventWithTarget(&event,this,mContent,NS_EVENT_FLAG_INIT,&status); + } else if (mCanSendLoadEvent) { + // Send load event + mCanSendLoadEvent = PR_FALSE; + + nsEventStatus status = nsEventStatus_eIgnore; + nsEvent event; + event.eventStructType = NS_EVENT; + event.message = NS_IMAGE_LOAD; + presShell->HandleEventWithTarget(&event,this,mContent,NS_EVENT_FLAG_INIT | NS_EVENT_FLAG_CANT_BUBBLE,&status); + } + } + return NS_OK; } NS_IMETHODIMP nsImageFrame::FrameChanged(imgIContainer *aContainer, nsIPresContext *aPresContext, gfxIImageFrame *aNewFrame, nsRect *aDirtyRect) { + nsRect r(*aDirtyRect); + float p2t; aPresContext->GetPixelsToTwips(&p2t); - nsRect r(*aDirtyRect); - r *= p2t; // convert to twips + r.x = NSIntPixelsToTwips(r.x, p2t); + r.y = NSIntPixelsToTwips(r.y, p2t); + r.width = NSIntPixelsToTwips(r.width, p2t); + r.height = NSIntPixelsToTwips(r.height, p2t); mTransform.TransformCoord(&r.x, &r.y, &r.width, &r.height); @@ -487,7 +547,7 @@ nsImageFrame::UpdateImage(nsIPresContext* aPresContext, PRUint32 aStatus, void* nsAutoString usemap; mContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::usemap, usemap); // We failed to load the image. Notify the pres shell if we aren't an image map - if (usemap.Length() == 0) { + if (usemap.IsEmpty()) { presShell->CantRenderReplacedElement(aPresContext, this); } } @@ -616,7 +676,7 @@ nsImageFrame::GetDesiredSize(nsIPresContext* aPresContext, newHeight = (mIntrinsicSize.height * newWidth) / mIntrinsicSize.width; haveComputedSize = PR_TRUE; } else { - newHeight = NSIntPixelsToTwips(1, p2t); // XXX? + newHeight = 0; needIntrinsicImageSize = PR_TRUE; } } @@ -628,30 +688,31 @@ nsImageFrame::GetDesiredSize(nsIPresContext* aPresContext, newWidth = (mIntrinsicSize.width * newHeight) / mIntrinsicSize.height; haveComputedSize = PR_TRUE; } else { - newWidth = NSIntPixelsToTwips(1, p2t); + newWidth = 0; needIntrinsicImageSize = PR_TRUE; } } else { // auto size the image - if (mIntrinsicSize.width == 0 && mIntrinsicSize.height == 0) { - newWidth = NSIntPixelsToTwips(1, p2t); - newHeight = NSIntPixelsToTwips(1, p2t); + if (mIntrinsicSize.width == 0 && mIntrinsicSize.height == 0) needIntrinsicImageSize = PR_TRUE; - } else { - newWidth = mIntrinsicSize.width; - newHeight = mIntrinsicSize.height; + else haveComputedSize = PR_TRUE; - } + newWidth = mIntrinsicSize.width; + newHeight = mIntrinsicSize.height; } mComputedSize.width = newWidth; mComputedSize.height = newHeight; - if (mComputedSize == mIntrinsicSize) + if (mComputedSize == mIntrinsicSize) { mTransform.SetToIdentity(); - else - mTransform.SetToScale((float(mIntrinsicSize.width) / float(mComputedSize.width)), (float(mIntrinsicSize.height) / float(mComputedSize.height))); + } else { + if (mComputedSize.width != 0 && mComputedSize.height != 0) { + mTransform.SetToScale(float(mIntrinsicSize.width) / float(mComputedSize.width), + float(mIntrinsicSize.height) / float(mComputedSize.height)); + } + } aDesiredSize.width = mComputedSize.width; aDesiredSize.height = mComputedSize.height; @@ -977,12 +1038,12 @@ nsImageFrame::Paint(nsIPresContext* aPresContext, PRInt32 imgSrcLinesLoaded = -1; #ifdef USE_IMG2 - NS_ASSERTION(mImageRequest, "no image request! this is bad"); - nsCOMPtr imgCon; nsCOMPtr lowImgCon; - mImageRequest->GetImage(getter_AddRefs(imgCon)); + if (mImageRequest) { + mImageRequest->GetImage(getter_AddRefs(imgCon)); + } #else nsIImage * lowImage = nsnull; nsIImage * image = nsnull; @@ -1001,8 +1062,10 @@ nsImageFrame::Paint(nsIPresContext* aPresContext, #endif #ifdef USE_IMG2 - PRUint32 loadStatus; - mImageRequest->GetImageStatus(&loadStatus); + PRUint32 loadStatus = imgIRequest::STATUS_NONE; + if (mImageRequest) { + mImageRequest->GetImageStatus(&loadStatus); + } if (!(loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) || (!imgCon && !lowImgCon)) { #else image = mImageLoader.GetImage(); @@ -1143,7 +1206,7 @@ nsImageFrame::GetImageMap(nsIPresContext* aPresContext) if (nsnull == mImageMap) { nsAutoString usemap; mContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::usemap, usemap); - if (0 == usemap.Length()) { + if (usemap.IsEmpty()) { return nsnull; } @@ -1480,18 +1543,17 @@ nsImageFrame::AttributeChanged(nsIPresContext* aPresContext, PRUint32 loadStatus; #ifdef USE_IMG2 - nsCOMPtr baseURI; - GetBaseURI(getter_AddRefs(baseURI)); - nsCOMPtr loadGroup; GetLoadGroup(aPresContext, getter_AddRefs(loadGroup)); mImageRequest->GetImageStatus(&loadStatus); if (loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) { nsCOMPtr uri; - NS_NewURI(getter_AddRefs(uri), newSRC, baseURI); + GetURI(newSRC, getter_AddRefs(uri)); + nsCOMPtr il(do_GetService("@mozilla.org/image/loader;1")); NS_ASSERTION(il, "no image loader!"); + il->LoadImage(uri, loadGroup, mListener, aPresContext, getter_AddRefs(mImageRequest)); mImageRequest->GetImageStatus(&loadStatus); @@ -1516,9 +1578,11 @@ nsImageFrame::AttributeChanged(nsIPresContext* aPresContext, mCanSendLoadEvent = PR_TRUE; nsCOMPtr uri; - NS_NewURI(getter_AddRefs(uri), newSRC, baseURI); + GetURI(newSRC, getter_AddRefs(uri)); + nsCOMPtr il(do_GetService("@mozilla.org/image/loader;1")); NS_ASSERTION(il, "no image loader!"); + il->LoadImage(uri, loadGroup, mListener, aPresContext, getter_AddRefs(mImageRequest)); #else mImageLoader.StopLoadImage(aPresContext); @@ -1619,6 +1683,35 @@ void HaveFixedSize(const nsHTMLReflowState& aReflowState, PRPackedBool& aConstra heightUnit == eStyleUnit_Percent)); } +#define INTERNAL_GOPHER_STRING "internal-gopher-" +#define INTERNAL_GOPHER_LENGTH 17 + +void +nsImageFrame::GetURI(const nsAReadableString& aSpec, nsIURI **aURI) +{ + nsAutoString newURI; + + /* Note: navigator 4.* and earlier releases ignored the base tags + effect on the builtin images. So we do too. Use aSpec instead + of the absolute url... + */ + + /* The prefix for special "internal" images that are well known. + Look and see if this is an internal-gopher- url. + */ + if (NS_LITERAL_STRING(INTERNAL_GOPHER_STRING).Equals(Substring(aSpec, 0, INTERNAL_GOPHER_LENGTH))) { + newURI.Assign(NS_LITERAL_STRING("resource:/res/html/gopher-") + + Substring(aSpec, INTERNAL_GOPHER_LENGTH, aSpec.Length() - INTERNAL_GOPHER_LENGTH) + + NS_LITERAL_STRING(".gif")); + } else { + newURI.Assign(aSpec); + } + + nsCOMPtr baseURI; + GetBaseURI(getter_AddRefs(baseURI)); + NS_NewURI(aURI, newURI, baseURI); +} + void nsImageFrame::GetBaseURI(nsIURI **aURI) { @@ -1664,6 +1757,40 @@ nsImageFrame::GetLoadGroup(nsIPresContext *aPresContext, nsILoadGroup **aLoadGro } +// Check with the content-policy things to make sure this load is permitted. +PRBool +nsImageFrame::CanLoadImage(nsIURI *aURI) +{ + PRBool shouldLoad = PR_TRUE; // default permit + + // XXX leave this if 0'd until there is a good way to test it. +#if 0 + + nsresult rv; + nsCOMPtr element(do_QueryInterface(mContent)); + + if (!element) // this would seem bad(tm) + return PR_FALSE; + + nsXPIDLCString uric; + aURI->GetSpec(getter_Copies(uric)); + + nsString uri = NS_ConvertUTF8toUCS2(uric); + + rv = NS_CheckContentLoadPolicy(nsIContentPolicy::CONTENT_IMAGE, + uri, element, &shouldLoad); + if (NS_SUCCEEDED(rv) && !shouldLoad) + return PR_FALSE; + + + /* ... additional checks ? */ +#endif + + return shouldLoad; +} + + + #ifdef DEBUG NS_IMETHODIMP nsImageFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const @@ -1692,7 +1819,8 @@ nsImageFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const #ifdef USE_IMG2 NS_IMPL_ISUPPORTS2(nsImageListener, imgIDecoderObserver, imgIContainerObserver) -nsImageListener::nsImageListener() +nsImageListener::nsImageListener(nsImageFrame *aFrame) : + mFrame(aFrame) { NS_INIT_ISUPPORTS(); } diff --git a/layout/html/base/src/nsImageFrame.h b/layout/html/base/src/nsImageFrame.h index b2046bf5ea55..8e85fe59554b 100644 --- a/layout/html/base/src/nsImageFrame.h +++ b/layout/html/base/src/nsImageFrame.h @@ -24,6 +24,7 @@ #include "nsLeafFrame.h" #include "nsString.h" +#include "nsAReadableString.h" #include "nsIPresContext.h" #include "nsHTMLImageLoader.h" #include "nsIImageFrame.h" @@ -49,7 +50,7 @@ class nsImageFrame; class nsImageListener : imgIDecoderObserver { public: - nsImageListener(); + nsImageListener(nsImageFrame *aFrame); virtual ~nsImageListener(); NS_DECL_ISUPPORTS @@ -182,8 +183,11 @@ protected: PRUint32 aStatus); #endif - void GetBaseURI(nsIURI **uri); - void GetLoadGroup(nsIPresContext *aPresContext, nsILoadGroup **aLoadGroup); + PRBool CanLoadImage(nsIURI *aURI); + + inline void GetURI(const nsAReadableString& aSpec, nsIURI **aURI); + inline void GetBaseURI(nsIURI **uri); + inline void GetLoadGroup(nsIPresContext *aPresContext, nsILoadGroup **aLoadGroup); nsHTMLImageLoader mImageLoader; nsHTMLImageLoader * mLowSrcImageLoader;