From 6a55a4774c4007ff9cc3372c040790b2ee289f44 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Wed, 18 Feb 2009 17:13:24 -0800 Subject: [PATCH] Construct all our image loaders when we create frames so that we can allow arbitrary numbers of image loaders per frame (for multiple background images). (Bug 322475) r+sr=bzbarsky --- content/events/src/nsDOMDataContainerEvent.h | 1 + layout/base/nsCSSRendering.cpp | 22 +++- layout/base/nsFrameManager.cpp | 46 -------- layout/base/nsImageLoader.cpp | 57 +++++----- layout/base/nsImageLoader.h | 30 +++-- layout/base/nsPresContext.cpp | 113 ++++++++----------- layout/base/nsPresContext.h | 65 ++++++----- layout/generic/nsFrame.cpp | 64 +++++++++-- 8 files changed, 205 insertions(+), 193 deletions(-) diff --git a/content/events/src/nsDOMDataContainerEvent.h b/content/events/src/nsDOMDataContainerEvent.h index 774e92d3539..3cef42c3222 100644 --- a/content/events/src/nsDOMDataContainerEvent.h +++ b/content/events/src/nsDOMDataContainerEvent.h @@ -41,6 +41,7 @@ #include "nsIDOMDataContainerEvent.h" #include "nsDOMEvent.h" +#include "nsInterfaceHashtable.h" class nsDOMDataContainerEvent : public nsDOMEvent, public nsIDOMDataContainerEvent diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index 288ced5663b..7723c307816 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -1518,9 +1518,12 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, return; } - // Lookup the image - imgIRequest *req = aPresContext->LoadImage(aColor.mBackgroundImage, - aForFrame); + // Ensure we get invalidated for loads of the image. We need to do + // this here because this might be the only code that knows about the + // association of the style data with the frame. + aPresContext->SetupBackgroundImageLoaders(aForFrame, &aColor); + + imgIRequest *req = aColor.mBackgroundImage; PRUint32 status = imgIRequest::STATUS_ERROR; if (req) @@ -1725,9 +1728,16 @@ DrawBorderImage(nsPresContext* aPresContext, if (aDirtyRect.IsEmpty()) return; - // Clone the image loader and set up animation notifications. - imgIRequest *req = - aPresContext->LoadBorderImage(aBorderStyle.GetBorderImage(), aForFrame); + // Ensure we get invalidated for loads and animations of the image. + // We need to do this here because this might be the only code that + // knows about the association of the style data with the frame. + // XXX We shouldn't really... since if anybody is passing in a + // different style, they'll potentially have the wrong size for the + // border too. + aPresContext->SetupBorderImageLoaders(aForFrame, &aBorderStyle); + + imgIRequest *req = aBorderStyle.GetBorderImage(); + #ifdef DEBUG { PRUint32 status = imgIRequest::STATUS_ERROR; diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp index 5a9650048c5..d274141b8d0 100644 --- a/layout/base/nsFrameManager.cpp +++ b/layout/base/nsFrameManager.cpp @@ -91,7 +91,6 @@ #include "nsLayoutUtils.h" #include "nsAutoPtr.h" #include "imgIRequest.h" -#include "nsStyleStructInlines.h" #include "nsFrameManager.h" #ifdef ACCESSIBILITY @@ -1085,24 +1084,6 @@ CaptureChange(nsStyleContext* aOldContext, nsStyleContext* aNewContext, return aMinChange; } -static PRBool -ShouldStopImage(imgIRequest *aOldImage, imgIRequest *aNewImage) -{ - if (!aOldImage) - return PR_FALSE; - - PRBool stopImages = !aNewImage; - if (!stopImages) { - nsCOMPtr oldURI, newURI; - aOldImage->GetURI(getter_AddRefs(oldURI)); - aNewImage->GetURI(getter_AddRefs(newURI)); - PRBool equal; - stopImages = - NS_FAILED(oldURI->Equals(newURI, &equal)) || !equal; - } - return stopImages; -} - /** * Recompute style for aFrame and accumulate changes into aChangeList * given that aMinChange is already accumulated for an ancestor. @@ -1264,33 +1245,6 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, // if frame gets regenerated, let it keep old context aFrame->SetStyleContext(newContext); } - // if old context had image and new context does not have the same image, - // stop the image load for the frame - if (ShouldStopImage( - oldContext->GetStyleBackground()->mBackgroundImage, - newContext->GetStyleBackground()->mBackgroundImage)) { - // stop the image loading for the frame, the image has changed - aPresContext->StopBackgroundImageFor(aFrame); - } - - imgIRequest *newBorderImage = - newContext->GetStyleBorder()->GetBorderImage(); - if (ShouldStopImage(oldContext->GetStyleBorder()->GetBorderImage(), - newBorderImage)) { - // stop the image loading for the frame, the image has changed - aPresContext->StopBorderImageFor(aFrame); - } - - // Since the CalcDifference call depended on the result of - // GetActualBorder() and that result depends on whether the - // image has loaded, start the image load now so that we'll get - // notified when it completes loading and can do a restyle. - // Otherwise, the image might finish loading from the network - // before we start listening to its notifications, and then - // we'll never know that it's finished loading. - if (newBorderImage) { - aPresContext->LoadBorderImage(newBorderImage, aFrame); - } } oldContext->Release(); } diff --git a/layout/base/nsImageLoader.cpp b/layout/base/nsImageLoader.cpp index d3db2c07c98..e69479d0d80 100644 --- a/layout/base/nsImageLoader.cpp +++ b/layout/base/nsImageLoader.cpp @@ -63,36 +63,49 @@ NS_IMPL_ISUPPORTS2(nsImageLoader, imgIDecoderObserver, imgIContainerObserver) -nsImageLoader::nsImageLoader() : - mFrame(nsnull), mPresContext(nsnull) +nsImageLoader::nsImageLoader(nsIFrame *aFrame, PRBool aReflowOnLoad, + nsImageLoader *aNextLoader) + : mFrame(aFrame), + mReflowOnLoad(aReflowOnLoad), + mNextLoader(aNextLoader) { } nsImageLoader::~nsImageLoader() { mFrame = nsnull; - mPresContext = nsnull; if (mRequest) { mRequest->CancelAndForgetObserver(NS_ERROR_FAILURE); } } - -void -nsImageLoader::Init(nsIFrame *aFrame, nsPresContext *aPresContext, - PRBool aReflowOnLoad) +/* static */ already_AddRefed +nsImageLoader::Create(nsIFrame *aFrame, imgIRequest *aRequest, + PRBool aReflowOnLoad, nsImageLoader *aNextLoader) { - mFrame = aFrame; - mPresContext = aPresContext; - mReflowOnLoad = aReflowOnLoad; + nsRefPtr loader = + new nsImageLoader(aFrame, aReflowOnLoad, aNextLoader); + + loader->Load(aRequest); + + return loader.forget(); } void nsImageLoader::Destroy() { + // Destroy the chain with only one level of recursion. + nsRefPtr list = mNextLoader; + mNextLoader = nsnull; + while (list) { + nsRefPtr todestroy = list; + list = todestroy->mNextLoader; + todestroy->mNextLoader = nsnull; + todestroy->Destroy(); + } + mFrame = nsnull; - mPresContext = nsnull; if (mRequest) { mRequest->CancelAndForgetObserver(NS_ERROR_FAILURE); @@ -104,28 +117,14 @@ nsImageLoader::Destroy() nsresult nsImageLoader::Load(imgIRequest *aImage) { + NS_ASSERTION(!mRequest, "can't reuse image loaders"); + if (!mFrame) return NS_ERROR_NOT_INITIALIZED; if (!aImage) return NS_ERROR_FAILURE; - if (mRequest) { - nsCOMPtr oldURI; - mRequest->GetURI(getter_AddRefs(oldURI)); - nsCOMPtr newURI; - aImage->GetURI(getter_AddRefs(newURI)); - PRBool eq = PR_FALSE; - nsresult rv = newURI->Equals(oldURI, &eq); - if (NS_SUCCEEDED(rv) && eq) { - return NS_OK; - } - - // Now cancel the old request so it won't hold a stale ref to us. - mRequest->CancelAndForgetObserver(NS_ERROR_FAILURE); - mRequest = nsnull; - } - // Make sure to clone into a temporary, then set mRequest, since // cloning may notify and we don't want to trigger paints from this // code. @@ -147,7 +146,7 @@ NS_IMETHODIMP nsImageLoader::OnStartContainer(imgIRequest *aRequest, * one frame = 1 * one loop = 2 */ - aImage->SetAnimationMode(mPresContext->ImageAnimationMode()); + aImage->SetAnimationMode(mFrame->PresContext()->ImageAnimationMode()); // Ensure the animation (if any) is started. aImage->StartAnimation(); } @@ -206,7 +205,7 @@ void nsImageLoader::RedrawDirtyFrame(const nsRect* aDamageRect) { if (mReflowOnLoad) { - nsIPresShell *shell = mPresContext->GetPresShell(); + nsIPresShell *shell = mFrame->PresContext()->GetPresShell(); #ifdef DEBUG nsresult rv = #endif diff --git a/layout/base/nsImageLoader.h b/layout/base/nsImageLoader.h index 9f8fc129b2a..61452e8b869 100644 --- a/layout/base/nsImageLoader.h +++ b/layout/base/nsImageLoader.h @@ -37,23 +37,35 @@ * * ***** END LICENSE BLOCK ***** */ -/* class to notify frames of background image loads */ +/* class to notify frames of background and border image loads */ #include "nsStubImageDecoderObserver.h" -class nsPresContext; class nsIFrame; class nsIURI; #include "imgIRequest.h" #include "nsCOMPtr.h" +#include "nsAutoPtr.h" +/** + * Image loaders pass notifications for background and border image + * loading and animation on to the frames. + * + * Each frame's image loaders form a linked list. + */ class nsImageLoader : public nsStubImageDecoderObserver { -public: - nsImageLoader(); +private: + nsImageLoader(nsIFrame *aFrame, PRBool aReflowOnLoad, + nsImageLoader *aNextLoader); virtual ~nsImageLoader(); +public: + static already_AddRefed + Create(nsIFrame *aFrame, imgIRequest *aRequest, + PRBool aReflowOnLoad, nsImageLoader *aNextLoader); + NS_DECL_ISUPPORTS // imgIDecoderObserver (override nsStubImageDecoderObserver) @@ -69,21 +81,17 @@ public: NS_IMETHOD FrameChanged(imgIContainer *aContainer, gfxIImageFrame *newframe, nsIntRect *dirtyRect); - void Init(nsIFrame *aFrame, nsPresContext *aPresContext, - PRBool aReflowOnLoad); - nsresult Load(imgIRequest *aImage); - void Destroy(); - nsIFrame *GetFrame() { return mFrame; } imgIRequest *GetRequest() { return mRequest; } + nsImageLoader *GetNextLoader() { return mNextLoader; } private: + nsresult Load(imgIRequest *aImage); void RedrawDirtyFrame(const nsRect* aDamageRect); -private: nsIFrame *mFrame; - nsPresContext *mPresContext; nsCOMPtr mRequest; PRBool mReflowOnLoad; + nsRefPtr mNextLoader; }; diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 8c7d57130a0..0cf10e60b04 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -89,6 +89,7 @@ #include "nsCSSRules.h" #include "nsFontFaceLoader.h" #include "nsIEventListenerManager.h" +#include "nsStyleStructInlines.h" #ifdef MOZ_SMIL #include "nsSMILAnimationController.h" @@ -159,7 +160,7 @@ IsVisualCharset(const nsCString& aCharset) static PLDHashOperator -destroy_loads(const void * aKey, nsCOMPtr& aData, void* closure) +destroy_loads(const void * aKey, nsRefPtr& aData, void* closure) { aData->Destroy(); return PL_DHASH_NEXT; @@ -246,8 +247,8 @@ nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType) nsPresContext::~nsPresContext() { - mImageLoaders.Enumerate(destroy_loads, nsnull); - mBorderImageLoaders.Enumerate(destroy_loads, nsnull); + for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i) + mImageLoaders[i].Enumerate(destroy_loads, nsnull); NS_PRECONDITION(!mShell, "Presshell forgot to clear our mShell pointer"); SetShell(nsnull); @@ -313,7 +314,7 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPresContext) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPresContext) static PLDHashOperator -TraverseImageLoader(const void * aKey, nsCOMPtr& aData, +TraverseImageLoader(const void * aKey, nsRefPtr& aData, void* aClosure) { nsCycleCollectionTraversalCallback *cb = @@ -331,8 +332,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext) // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLookAndFeel); // a service // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLangGroup); // an atom - tmp->mImageLoaders.Enumerate(TraverseImageLoader, &cb); - tmp->mBorderImageLoaders.Enumerate(TraverseImageLoader, &cb); + for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i) + tmp->mImageLoaders[i].Enumerate(TraverseImageLoader, &cb); // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTheme); // a service // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLangService); // a service @@ -354,10 +355,10 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext) // NS_RELEASE(tmp->mLookAndFeel); // a service // NS_RELEASE(tmp->mLangGroup); // an atom - tmp->mImageLoaders.Enumerate(destroy_loads, nsnull); - tmp->mImageLoaders.Clear(); - tmp->mBorderImageLoaders.Enumerate(destroy_loads, nsnull); - tmp->mBorderImageLoaders.Clear(); + for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i) { + tmp->mImageLoaders[i].Enumerate(destroy_loads, nsnull); + tmp->mImageLoaders[i].Clear(); + } // NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTheme); // a service // NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mLangService); // a service @@ -837,11 +838,9 @@ nsPresContext::Init(nsIDeviceContext* aDeviceContext) mDeviceContext->FlushFontCache(); mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel(); - if (!mImageLoaders.Init()) - return NS_ERROR_OUT_OF_MEMORY; - - if (!mBorderImageLoaders.Init()) - return NS_ERROR_OUT_OF_MEMORY; + for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i) + if (!mImageLoaders[i].Init()) + return NS_ERROR_OUT_OF_MEMORY; // Get the look and feel service here; default colors will be initialized // from calling GetUserPreferences() when we get a presshell. @@ -1058,10 +1057,13 @@ static void SetImgAnimModeOnImgReq(imgIRequest* aImgReq, PRUint16 aMode) // Enumeration call back for HashTable static PLDHashOperator -set_animation_mode(const void * aKey, nsCOMPtr& aData, void* closure) +set_animation_mode(const void * aKey, nsRefPtr& aData, void* closure) { - imgIRequest* imgReq = aData->GetRequest(); - SetImgAnimModeOnImgReq(imgReq, (PRUint16)NS_PTR_TO_INT32(closure)); + for (nsImageLoader *loader = aData; loader; + loader = loader->GetNextLoader()) { + imgIRequest* imgReq = loader->GetRequest(); + SetImgAnimModeOnImgReq(imgReq, (PRUint16)NS_PTR_TO_INT32(closure)); + } return PL_DHASH_NEXT; } @@ -1122,8 +1124,8 @@ nsPresContext::SetImageAnimationModeInternal(PRUint16 aMode) return; // Set the mode on the image loaders. - mImageLoaders.Enumerate(set_animation_mode, NS_INT32_TO_PTR(aMode)); - mBorderImageLoaders.Enumerate(set_animation_mode, NS_INT32_TO_PTR(aMode)); + for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i) + mImageLoaders[i].Enumerate(set_animation_mode, NS_INT32_TO_PTR(aMode)); // Now walk the content tree and set the animation mode // on all the images. @@ -1228,66 +1230,51 @@ nsPresContext::SetFullZoom(float aZoom) mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel(); } -imgIRequest* -nsPresContext::DoLoadImage(nsPresContext::ImageLoaderTable& aTable, - imgIRequest* aImage, - nsIFrame* aTargetFrame, - PRBool aReflowOnLoad) +void +nsPresContext::SetImageLoaders(nsIFrame* aTargetFrame, + ImageLoadType aType, + nsImageLoader* aImageLoaders) { - // look and see if we have a loader for the target frame. - nsCOMPtr loader; - aTable.Get(aTargetFrame, getter_AddRefs(loader)); + nsRefPtr oldLoaders; + mImageLoaders[aType].Get(aTargetFrame, getter_AddRefs(oldLoaders)); - if (!loader) { - loader = new nsImageLoader(); - if (!loader) - return nsnull; - - loader->Init(aTargetFrame, this, aReflowOnLoad); - aTable.Put(aTargetFrame, loader); + if (aImageLoaders) { + mImageLoaders[aType].Put(aTargetFrame, aImageLoaders); + } else if (oldLoaders) { + mImageLoaders[aType].Remove(aTargetFrame); } - loader->Load(aImage); - - imgIRequest *request = loader->GetRequest(); - - return request; + if (oldLoaders) + oldLoaders->Destroy(); } -imgIRequest* -nsPresContext::LoadImage(imgIRequest* aImage, nsIFrame* aTargetFrame) +void +nsPresContext::SetupBackgroundImageLoaders(nsIFrame* aFrame, + const nsStyleBackground* aStyleBackground) { - return DoLoadImage(mImageLoaders, aImage, aTargetFrame, PR_FALSE); + nsRefPtr loader = + nsImageLoader::Create(aFrame, aStyleBackground->mBackgroundImage, + PR_FALSE, nsnull); + SetImageLoaders(aFrame, BACKGROUND_IMAGE, loader); } -imgIRequest* -nsPresContext::LoadBorderImage(imgIRequest* aImage, nsIFrame* aTargetFrame) +void +nsPresContext::SetupBorderImageLoaders(nsIFrame* aFrame, + const nsStyleBorder* aStyleBorder) { - return DoLoadImage(mBorderImageLoaders, aImage, aTargetFrame, - aTargetFrame->GetStyleBorder()->ImageBorderDiffers()); + nsRefPtr loader = + nsImageLoader::Create(aFrame, aStyleBorder->GetBorderImage(), + aStyleBorder->ImageBorderDiffers(), nsnull); + SetImageLoaders(aFrame, BORDER_IMAGE, loader); } void nsPresContext::StopImagesFor(nsIFrame* aTargetFrame) { - StopBackgroundImageFor(aTargetFrame); - StopBorderImageFor(aTargetFrame); + for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i) + SetImageLoaders(aTargetFrame, ImageLoadType(i), nsnull); } -void -nsPresContext::DoStopImageFor(nsPresContext::ImageLoaderTable& aTable, - nsIFrame* aTargetFrame) -{ - nsCOMPtr loader; - aTable.Get(aTargetFrame, getter_AddRefs(loader)); - - if (loader) { - loader->Destroy(); - - aTable.Remove(aTargetFrame); - } -} - void nsPresContext::SetContainer(nsISupports* aHandler) { diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index f19ba5a3ac6..c5fb7d05c69 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -60,7 +60,7 @@ #include "nsPropertyTable.h" #include "nsGkAtoms.h" #include "nsIDocument.h" -#include "nsInterfaceHashtable.h" +#include "nsRefPtrHashtable.h" #include "nsCycleCollectionParticipant.h" #include "nsChangeHint.h" // This also pulls in gfxTypes.h, which we cannot include directly. @@ -93,6 +93,7 @@ class nsILookAndFeel; class nsICSSPseudoComparator; class nsIAtom; struct nsStyleBackground; +struct nsStyleBorder; class nsIRunnable; class gfxUserFontSet; class nsUserFontSet; @@ -378,41 +379,46 @@ public: PRBool GetFocusRingOnAnything() const { return mFocusRingOnAnything; } PRUint8 GetFocusRingStyle() const { return mFocusRingStyle; } + /** + * The types of image load types that the pres context needs image + * loaders to track invalidation for. + */ + enum ImageLoadType { + BACKGROUND_IMAGE, + BORDER_IMAGE, + IMAGE_LOAD_TYPE_COUNT + }; /** - * Set up observers so that aTargetFrame will be invalidated when - * aImage loads, where aImage is its background image. Only a single - * image will be tracked per frame. + * Set the list of image loaders that track invalidation for a + * specific frame and type of image. This list will replace any + * previous list for that frame and image type (and null will remove + * any previous list). */ - NS_HIDDEN_(imgIRequest*) LoadImage(imgIRequest* aImage, - nsIFrame* aTargetFrame); + NS_HIDDEN_(void) SetImageLoaders(nsIFrame* aTargetFrame, + ImageLoadType aType, + nsImageLoader* aImageLoaders); + /** - * Set up observers so that aTargetFrame will be invalidated or - * reflowed (as appropriate) when aImage loads, where aImage is its - * *border* image. Only a single image will be tracked per frame. + * Make an appropriate SetImageLoaders call (including potentially + * with null aImageLoaders) given that aFrame draws its background + * based on aStyleBackground. */ - NS_HIDDEN_(imgIRequest*) LoadBorderImage(imgIRequest* aImage, - nsIFrame* aTargetFrame); + NS_HIDDEN_(void) SetupBackgroundImageLoaders(nsIFrame* aFrame, + const nsStyleBackground* + aStyleBackground); -private: - typedef nsInterfaceHashtable ImageLoaderTable; + /** + * Make an appropriate SetImageLoaders call (including potentially + * with null aImageLoaders) given that aFrame draws its border + * based on aStyleBorder. + */ + NS_HIDDEN_(void) SetupBorderImageLoaders(nsIFrame* aFrame, + const nsStyleBorder* aStyleBorder); - NS_HIDDEN_(imgIRequest*) DoLoadImage(ImageLoaderTable& aTable, - imgIRequest* aImage, - nsIFrame* aTargetFrame, - PRBool aReflowOnLoad); - - NS_HIDDEN_(void) DoStopImageFor(ImageLoaderTable& aTable, - nsIFrame* aTargetFrame); -public: - - NS_HIDDEN_(void) StopBackgroundImageFor(nsIFrame* aTargetFrame) - { DoStopImageFor(mImageLoaders, aTargetFrame); } - NS_HIDDEN_(void) StopBorderImageFor(nsIFrame* aTargetFrame) - { DoStopImageFor(mBorderImageLoaders, aTargetFrame); } /** * This method is called when a frame is being destroyed to - * ensure that the image load gets disassociated from the prescontext + * ensure that the image loads get disassociated from the prescontext */ NS_HIDDEN_(void) StopImagesFor(nsIFrame* aTargetFrame); @@ -847,8 +853,9 @@ protected: nsILinkHandler* mLinkHandler; // [WEAK] nsIAtom* mLangGroup; // [STRONG] - ImageLoaderTable mImageLoaders; - ImageLoaderTable mBorderImageLoaders; + nsRefPtrHashtable + mImageLoaders[IMAGE_LOAD_TYPE_COUNT]; + nsWeakPtr mContainer; float mTextZoom; // Text zoom, defaults to 1.0 diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 6eecf65ef0c..438a04540f4 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -518,19 +518,65 @@ nsFrame::GetOffsets(PRInt32 &aStart, PRInt32 &aEnd) const return NS_OK; } +static PRBool +EqualImages(imgIRequest *aOldImage, imgIRequest *aNewImage) +{ + if (aOldImage == aNewImage) + return PR_TRUE; + + if (!aOldImage || !aNewImage) + return PR_FALSE; + + nsCOMPtr oldURI, newURI; + aOldImage->GetURI(getter_AddRefs(oldURI)); + aNewImage->GetURI(getter_AddRefs(newURI)); + PRBool equal; + return NS_SUCCEEDED(oldURI->Equals(newURI, &equal)) && equal; +} + // Subclass hook for style post processing /* virtual */ void nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) { - // We have to start loading the border image before or during reflow, - // because the border-image's width overrides only apply once the - // image is loaded. Starting the load of the image means we'll get a - // reflow when the image loads. (Otherwise, if the image loads - // between reflow and paint, we never get the notification and our - // size ends up wrong.) - imgIRequest *borderImage = GetStyleBorder()->GetBorderImage(); - if (borderImage) { - PresContext()->LoadBorderImage(borderImage, this); + if (aOldStyleContext) { + // If the old context had a background image image and new context + // does not have the same image, clear the image load notifier + // (which keeps the image loading, if it still is) for the frame. + // We want to do this conservatively because some frames paint their + // backgrounds from some other frame's style data, and we don't want + // to clear those notifiers unless we have to. (They'll be reset + // when we paint, although we could miss a notification in that + // interval.) + imgIRequest *oldBackgroundImage = + aOldStyleContext->GetStyleBackground()->mBackgroundImage; + if (oldBackgroundImage && + !EqualImages(oldBackgroundImage, + GetStyleBackground()->mBackgroundImage)) { + // stop the image loading for the frame, the image has changed + PresContext()->SetImageLoaders(this, + nsPresContext::BACKGROUND_IMAGE, nsnull); + } + } + + imgIRequest *oldBorderImage = aOldStyleContext + ? aOldStyleContext->GetStyleBorder()->GetBorderImage() + : nsnull; + // For border-images, we can't be as conservative (we need to set the + // new loaders if there has been any change) since the CalcDifference + // call depended on the result of GetActualBorder() and that result + // depends on whether the image has loaded, start the image load now + // so that we'll get notified when it completes loading and can do a + // restyle. Otherwise, the image might finish loading from the + // network before we start listening to its notifications, and then + // we'll never know that it's finished loading. Likewise, we want to + // do this for freshly-created frames to prevent a similar race if the + // image loads between reflow (which can depend on whether the image + // is loaded) and paint. We also don't really care about any callers + // who try to paint borders with a different style context, because + // they won't have the correct size for the border either. + if (!EqualImages(oldBorderImage, GetStyleBorder()->GetBorderImage())) { + // stop and restart the image loading/notification + PresContext()->SetupBorderImageLoaders(this, GetStyleBorder()); } }