From 136ecf84de9ff9f42c0f7b38bf908d230d2af0bf Mon Sep 17 00:00:00 2001 From: "bjarne@runitsoft.com" Date: Tue, 7 Jul 2009 22:23:20 -0700 Subject: [PATCH] Bug 457809 - Speculatively load images from preloading. r=mrbkap, sr=bzbarsky --- content/base/public/nsContentUtils.h | 7 ++++++ content/base/public/nsIDocument.h | 12 ++++++++-- content/base/src/nsContentUtils.cpp | 20 ++++++++++++++++ content/base/src/nsDocument.cpp | 34 ++++++++++++++++++++++++++++ content/base/src/nsDocument.h | 6 +++++ parser/htmlparser/src/nsParser.cpp | 10 +++++--- 6 files changed, 84 insertions(+), 5 deletions(-) diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index 1b64ba4bb266..14f41a6df04c 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -81,6 +81,7 @@ class nsIURI; class imgIDecoderObserver; class imgIRequest; class imgILoader; +class imgICache; class nsIPrefBranch; class nsIImage; class nsIImageLoadingContent; @@ -647,6 +648,11 @@ public: PRInt32 aLoadFlags, imgIRequest** aRequest); + /** + * Returns whether the given URI is in the image cache. + */ + static PRBool IsImageInCache(nsIURI* aURI); + /** * Method to get an nsIImage from an image loading content * @@ -1504,6 +1510,7 @@ private: static nsIPref *sPref; static imgILoader* sImgLoader; + static imgICache* sImgCache; static nsIConsoleService* sConsoleService; diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index ef180e5c9717..37197b9228ec 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -105,8 +105,8 @@ class nsIBoxObject; // IID for the nsIDocument interface #define NS_IDOCUMENT_IID \ - {0x2c155ed0, 0x3302, 0x4cff, \ - {0x9d, 0xb3, 0xed, 0x0c, 0xcd, 0xfc, 0x50, 0x06 } } + { 0x46003091, 0x7f99, 0x420f, \ + { 0x95, 0xbc, 0x28, 0xd7, 0xd5, 0x01, 0x5a, 0x41 } } // Flag for AddStyleSheet(). #define NS_STYLESHEET_FROM_CATALOG (1 << 0) @@ -1148,6 +1148,14 @@ public: PRUint32 EventHandlingSuppressed() const { return mEventsSuppressed; } PRBool IsDNSPrefetchAllowed() const { return mAllowDNSPrefetch; } + + /** + * Called by nsParser to preload images. Can be removed and code moved + * to nsPreloadURIs::PreloadURIs() in file nsParser.cpp whenever the + * parser-module is linked with gklayout-module. + */ + virtual void MaybePreLoadImage(nsIURI* uri) = 0; + protected: ~nsIDocument() { diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index e0a32d48a204..64c77e483698 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -179,6 +179,7 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID); #include "nsIConsoleService.h" #include "mozAutoDocUpdate.h" +#include "imgICache.h" #include "jsinterp.h" const char kLoadAsData[] = "loadAsData"; @@ -200,6 +201,7 @@ nsIXTFService *nsContentUtils::sXTFService = nsnull; nsIPrefBranch *nsContentUtils::sPrefBranch = nsnull; nsIPref *nsContentUtils::sPref = nsnull; imgILoader *nsContentUtils::sImgLoader; +imgICache *nsContentUtils::sImgCache; nsIConsoleService *nsContentUtils::sConsoleService; nsDataHashtable* nsContentUtils::sEventTable = nsnull; nsIStringBundleService *nsContentUtils::sStringBundleService; @@ -330,6 +332,10 @@ nsContentUtils::Init() if (NS_FAILED(rv)) { // no image loading for us. Oh, well. sImgLoader = nsnull; + sImgCache = nsnull; + } else { + if (NS_FAILED(CallGetService("@mozilla.org/image/cache;1", &sImgCache ))) + sImgCache = nsnull; } sPtrsToPtrsToRelease = new nsTArray(); @@ -893,6 +899,7 @@ nsContentUtils::Shutdown() NS_IF_RELEASE(sXTFService); #endif NS_IF_RELEASE(sImgLoader); + NS_IF_RELEASE(sImgCache); NS_IF_RELEASE(sPrefBranch); NS_IF_RELEASE(sPref); #ifdef IBMBIDI @@ -2383,6 +2390,19 @@ nsContentUtils::CanLoadImage(nsIURI* aURI, nsISupports* aContext, return NS_FAILED(rv) ? PR_FALSE : NS_CP_ACCEPTED(decision); } +// static +PRBool +nsContentUtils::IsImageInCache(nsIURI* aURI) +{ + if (!sImgCache) return PR_FALSE; + + // If something unexpected happened we return false, otherwise if props + // is set, the image is cached and we return true + nsCOMPtr props; + nsresult rv = sImgCache->FindEntryProperties(aURI, getter_AddRefs(props)); + return (NS_SUCCEEDED(rv) && props); +} + // static nsresult nsContentUtils::LoadImage(nsIURI* aURI, nsIDocument* aLoadingDocument, diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 022b9c88013b..ca0bc37f6664 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -1780,6 +1780,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mStyleSheets) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCatalogSheets) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mVisitednessChangedURIs) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mPreloadingImages) #ifdef MOZ_SMIL // Traverse animation components @@ -1824,6 +1825,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument) tmp->mParentDocument = nsnull; + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mPreloadingImages) + // nsDocument has a pretty complex destructor, so we're going to // assume that *most* cycles you actually want to break somewhere // else, and not unlink an awful lot here. @@ -3863,6 +3866,9 @@ nsDocument::DispatchContentLoadedEvents() // If you add early returns from this method, make sure you're // calling UnblockOnload properly. + // Unpin references to preloaded images + mPreloadingImages.Clear(); + // Fire a DOM event notifying listeners that this document has been // loaded (excluding images and other loads initiated by this // document). @@ -7485,6 +7491,34 @@ FireOrClearDelayedEvents(nsTArray >& aDocuments, } } +void +nsDocument::MaybePreLoadImage(nsIURI* uri) +{ + // Early exit if the img is already present in the img-cache + // which indicates that the "real" load has already started and + // that we shouldn't preload it. + if (nsContentUtils::IsImageInCache(uri)) { + return; + } + + // Image not in cache - trigger preload + nsCOMPtr request; + nsresult rv = + nsContentUtils::LoadImage(uri, + this, + NodePrincipal(), + mDocumentURI, // uri of document used as referrer + nsnull, // no observer + nsIRequest::LOAD_NORMAL, + getter_AddRefs(request)); + + // Pin image-reference to avoid evicting it from the img-cache before + // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and + // unlink + if (NS_SUCCEEDED(rv)) { + mPreloadingImages.AppendObject(request); + } +} class nsDelayedEventDispatcher : public nsRunnable { public: diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index 7cb0380810f7..a85e577eaeda 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -116,6 +116,7 @@ #include "nsIProgressEventSink.h" #include "nsISecurityEventSink.h" #include "nsIChannelEventSink.h" +#include "imgIRequest.h" #define XML_DECLARATION_BITS_DECLARATION_EXISTS (1 << 0) #define XML_DECLARATION_BITS_ENCODING_EXISTS (1 << 1) @@ -1006,6 +1007,8 @@ public: void MaybeInitializeFinalizeFrameLoaders(); void MaybeEndOutermostXBLUpdate(); + + virtual void MaybePreLoadImage(nsIURI* uri); protected: void RegisterNamedItems(nsIContent *aContent); @@ -1264,6 +1267,9 @@ private: nsExternalResourceMap mExternalResourceMap; + // All images in process of being preloaded + nsCOMArray mPreloadingImages; + #ifdef MOZ_SMIL nsAutoPtr mAnimationController; #endif // MOZ_SMIL diff --git a/parser/htmlparser/src/nsParser.cpp b/parser/htmlparser/src/nsParser.cpp index b9449ba0d878..190fefbe66fc 100644 --- a/parser/htmlparser/src/nsParser.cpp +++ b/parser/htmlparser/src/nsParser.cpp @@ -382,6 +382,9 @@ nsPreloadURIs::PreloadURIs(const nsAutoTArrayScriptLoader()->PreloadURI(uri, pe.charset, pe.elementType); break; + case nsSpeculativeScriptThread::IMAGE: + doc->MaybePreLoadImage(uri); + break; case nsSpeculativeScriptThread::STYLESHEET: { nsCOMPtr obs = new nsDummyCSSLoaderObserver(); doc->CSSLoader()->LoadSheet(uri, doc->NodePrincipal(), @@ -389,9 +392,6 @@ nsPreloadURIs::PreloadURIs(const nsAutoTArray