diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index 3beee8ed00e7..9a7e7c421909 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -482,13 +482,6 @@ public: return sIOService; } - static imgILoader* GetImgLoader() - { - if (!sImgLoaderInitialized) - InitImgLoader(); - return sImgLoader; - } - #ifdef MOZ_XTF static nsIXTFService* GetXTFService(); #endif @@ -663,10 +656,17 @@ public: int32_t aLoadFlags, imgIRequest** aRequest); + /** + * Obtain an image loader that respects the given document/channel's privacy status. + * Null document/channel arguments return the public image loader. + */ + static imgILoader* GetImgLoaderForDocument(nsIDocument* aDoc); + static imgILoader* GetImgLoaderForChannel(nsIChannel* aChannel); + /** * Returns whether the given URI is in the image cache. */ - static bool IsImageInCache(nsIURI* aURI); + static bool IsImageInCache(nsIURI* aURI, nsIDocument* aDocument); /** * Method to get an imgIContainer from an image loading content @@ -2160,9 +2160,11 @@ private: static bool sImgLoaderInitialized; static void InitImgLoader(); - // The following two members are initialized lazily + // The following four members are initialized lazily static imgILoader* sImgLoader; + static imgILoader* sPrivateImgLoader; static imgICache* sImgCache; + static imgICache* sPrivateImgCache; static nsIConsoleService* sConsoleService; diff --git a/content/base/src/Makefile.in b/content/base/src/Makefile.in index b424893a3e1a..76c7c7b3f8bd 100644 --- a/content/base/src/Makefile.in +++ b/content/base/src/Makefile.in @@ -50,6 +50,7 @@ EXPORTS_mozilla/dom = \ $(NULL) LOCAL_INCLUDES = \ + -I$(topsrcdir)/image/src \ $(NULL) CPPSRCS = \ diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index ff256552fb8a..e79bb765a2ce 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -119,6 +119,7 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID); #include "nsIDOMHTMLInputElement.h" #include "nsParserConstants.h" #include "nsIWebNavigation.h" +#include "nsILoadContext.h" #include "nsTextFragment.h" #include "mozilla/Selection.h" @@ -134,6 +135,7 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID); #include "mozAutoDocUpdate.h" #include "imgICache.h" +#include "imgLoader.h" #include "xpcprivate.h" // nsXPConnect #include "nsScriptSecurityManager.h" #include "nsIChannelPolicy.h" @@ -187,7 +189,9 @@ nsIIOService *nsContentUtils::sIOService; nsIXTFService *nsContentUtils::sXTFService = nullptr; #endif imgILoader *nsContentUtils::sImgLoader; +imgILoader *nsContentUtils::sPrivateImgLoader; imgICache *nsContentUtils::sImgCache; +imgICache *nsContentUtils::sPrivateImgCache; nsIConsoleService *nsContentUtils::sConsoleService; nsDataHashtable* nsContentUtils::sAtomEventTable = nullptr; nsDataHashtable* nsContentUtils::sStringEventTable = nullptr; @@ -508,15 +512,17 @@ nsContentUtils::InitImgLoader() sImgLoaderInitialized = true; // Ignore failure and just don't load images - nsresult rv = CallGetService("@mozilla.org/image/loader;1", &sImgLoader); - if (NS_FAILED(rv)) { - // no image loading for us. Oh, well. - sImgLoader = nullptr; - sImgCache = nullptr; - } else { - if (NS_FAILED(CallGetService("@mozilla.org/image/cache;1", &sImgCache ))) - sImgCache = nullptr; - } + nsresult rv = CallCreateInstance("@mozilla.org/image/loader;1", &sImgLoader); + NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "Creation should have succeeded"); + rv = CallCreateInstance("@mozilla.org/image/loader;1", &sPrivateImgLoader); + NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "Creation should have succeeded"); + + rv = CallQueryInterface(sImgLoader, &sImgCache); + NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "imgICache and imgILoader should be paired"); + rv = CallQueryInterface(sPrivateImgLoader, &sPrivateImgCache); + NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "imgICache and imgILoader should be paired"); + + sPrivateImgCache->RespectPrivacyNotifications(); } bool @@ -1493,7 +1499,9 @@ nsContentUtils::Shutdown() NS_IF_RELEASE(sXTFService); #endif NS_IF_RELEASE(sImgLoader); + NS_IF_RELEASE(sPrivateImgLoader); NS_IF_RELEASE(sImgCache); + NS_IF_RELEASE(sPrivateImgCache); #ifdef IBMBIDI NS_IF_RELEASE(sBidiKeyboard); #endif @@ -2692,19 +2700,60 @@ nsContentUtils::CanLoadImage(nsIURI* aURI, nsISupports* aContext, return NS_FAILED(rv) ? false : NS_CP_ACCEPTED(decision); } +imgILoader* +nsContentUtils::GetImgLoaderForDocument(nsIDocument* aDoc) +{ + if (!sImgLoaderInitialized) + InitImgLoader(); + if (!aDoc) + return sImgLoader; + bool isPrivate = false; + nsCOMPtr loadGroup = aDoc->GetDocumentLoadGroup(); + nsCOMPtr callbacks; + if (loadGroup) { + loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks)); + if (callbacks) { + nsCOMPtr loadContext = do_GetInterface(callbacks); + isPrivate = loadContext && loadContext->UsePrivateBrowsing(); + } + } else { + nsCOMPtr channel = aDoc->GetChannel(); + if (channel) { + nsCOMPtr context; + NS_QueryNotificationCallbacks(channel, context); + isPrivate = context && context->UsePrivateBrowsing(); + } + } + return isPrivate ? sPrivateImgLoader : sImgLoader; +} + +// static +imgILoader* +nsContentUtils::GetImgLoaderForChannel(nsIChannel* aChannel) +{ + if (!sImgLoaderInitialized) + InitImgLoader(); + if (!aChannel) + return sImgLoader; + nsCOMPtr context; + NS_QueryNotificationCallbacks(aChannel, context); + return context && context->UsePrivateBrowsing() ? sPrivateImgLoader : sImgLoader; +} + // static bool -nsContentUtils::IsImageInCache(nsIURI* aURI) +nsContentUtils::IsImageInCache(nsIURI* aURI, nsIDocument* aDocument) { if (!sImgLoaderInitialized) InitImgLoader(); - if (!sImgCache) return false; + imgILoader* loader = GetImgLoaderForDocument(aDocument); + nsCOMPtr cache = do_QueryInterface(loader); // 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)); + nsresult rv = cache->FindEntryProperties(aURI, getter_AddRefs(props)); return (NS_SUCCEEDED(rv) && props); } @@ -2720,7 +2769,7 @@ nsContentUtils::LoadImage(nsIURI* aURI, nsIDocument* aLoadingDocument, NS_PRECONDITION(aLoadingPrincipal, "Must have a principal"); NS_PRECONDITION(aRequest, "Null out param"); - imgILoader* imgLoader = GetImgLoader(); + imgILoader* imgLoader = GetImgLoaderForDocument(aLoadingDocument); if (!imgLoader) { // nothing we can do here return NS_OK; diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index fe1dbc2adf52..cf67e8abbccd 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -7681,7 +7681,7 @@ nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr) // which indicates that the "real" load has already started and // that we shouldn't preload it. int16_t blockingStatus; - if (nsContentUtils::IsImageInCache(uri) || + if (nsContentUtils::IsImageInCache(uri, static_cast(this)) || !nsContentUtils::CanLoadImage(uri, static_cast(this), this, NodePrincipal(), &blockingStatus)) { return; diff --git a/content/base/src/nsImageLoadingContent.cpp b/content/base/src/nsImageLoadingContent.cpp index 7e1d39a8a5c1..3ab8ce77bc73 100644 --- a/content/base/src/nsImageLoadingContent.cpp +++ b/content/base/src/nsImageLoadingContent.cpp @@ -88,7 +88,7 @@ nsImageLoadingContent::nsImageLoadingContent() mCurrentRequestRegistered(false), mPendingRequestRegistered(false) { - if (!nsContentUtils::GetImgLoader()) { + if (!nsContentUtils::GetImgLoaderForChannel(nullptr)) { mLoadingEnabled = false; } } @@ -334,7 +334,7 @@ nsImageLoadingContent::SetLoadingEnabled(bool aLoadingEnabled) { NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); - if (nsContentUtils::GetImgLoader()) { + if (nsContentUtils::GetImgLoaderForChannel(nullptr)) { mLoadingEnabled = aLoadingEnabled; } return NS_OK; @@ -518,7 +518,7 @@ nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel, { NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); - if (!nsContentUtils::GetImgLoader()) { + if (!nsContentUtils::GetImgLoaderForChannel(aChannel)) { return NS_ERROR_NULL_POINTER; } @@ -537,7 +537,7 @@ nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel, // Do the load. nsCOMPtr& req = PrepareNextRequest(); - nsresult rv = nsContentUtils::GetImgLoader()-> + nsresult rv = nsContentUtils::GetImgLoaderForChannel(aChannel)-> LoadImageWithChannel(aChannel, this, doc, aListener, getter_AddRefs(req)); if (NS_SUCCEEDED(rv)) { diff --git a/content/base/src/nsObjectLoadingContent.cpp b/content/base/src/nsObjectLoadingContent.cpp index 19c03ec86fba..4ccd8ed1c399 100644 --- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -477,7 +477,7 @@ URIEquals(nsIURI *a, nsIURI *b) static bool IsSupportedImage(const nsCString& aMimeType) { - imgILoader* loader = nsContentUtils::GetImgLoader(); + nsCOMPtr loader = nsContentUtils::GetImgLoaderForChannel(nullptr); if (!loader) { return false; } diff --git a/docshell/base/nsWebNavigationInfo.cpp b/docshell/base/nsWebNavigationInfo.cpp index 30b42e846008..1567bc873be0 100644 --- a/docshell/base/nsWebNavigationInfo.cpp +++ b/docshell/base/nsWebNavigationInfo.cpp @@ -24,9 +24,9 @@ nsWebNavigationInfo::Init() mCategoryManager = do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); - mImgLoader = do_GetService("@mozilla.org/image/loader;1", &rv); + mImgLoader = nsContentUtils::GetImgLoaderForChannel(nullptr); - return rv; + return NS_OK; } NS_IMETHODIMP diff --git a/embedding/browser/webBrowser/nsContextMenuInfo.cpp b/embedding/browser/webBrowser/nsContextMenuInfo.cpp index 0a994274af66..492faa474fc9 100644 --- a/embedding/browser/webBrowser/nsContextMenuInfo.cpp +++ b/embedding/browser/webBrowser/nsContextMenuInfo.cpp @@ -301,8 +301,7 @@ nsContextMenuInfo::GetBackgroundImageRequestInternal(nsIDOMNode *aDOMNode, imgIR NS_NewURI(getter_AddRefs(bgUri), bgStringValue); NS_ENSURE_TRUE(bgUri, NS_ERROR_FAILURE); - nsCOMPtr il(do_GetService( - "@mozilla.org/image/loader;1")); + nsCOMPtr il(do_CreateInstance("@mozilla.org/image/loader;1")); NS_ENSURE_TRUE(il, NS_ERROR_FAILURE); return il->LoadImage(bgUri, nullptr, nullptr, principal, nullptr, diff --git a/image/build/nsImageModule.cpp b/image/build/nsImageModule.cpp index 5eb42d753d3b..a78279e5155a 100644 --- a/image/build/nsImageModule.cpp +++ b/image/build/nsImageModule.cpp @@ -96,7 +96,7 @@ static nsresult imglib_Initialize() { mozilla::image::DiscardTracker::Initialize(); - imgLoader::InitCache(); + imgLoader::GlobalInit(); return NS_OK; } diff --git a/image/public/imgICache.idl b/image/public/imgICache.idl index 67e1166816ea..c923cadaf40f 100644 --- a/image/public/imgICache.idl +++ b/image/public/imgICache.idl @@ -17,7 +17,7 @@ interface nsIProperties; * @version 0.1 * @see imagelib2 */ -[scriptable, uuid(f1b74aae-5661-4753-a21c-66dd644afebc)] +[scriptable, uuid(b06e0fa5-d6e2-4fa3-8fc0-7775aed96522)] interface imgICache : nsISupports { /** @@ -49,4 +49,11 @@ interface imgICache : nsISupports * @returns NULL if the URL was not found in the cache */ nsIProperties findEntryProperties(in nsIURI uri); + + /** + * Make this cache instance respect private browsing notifications. This entails clearing + * the chrome and content caches whenever the last-pb-context-exited notification is + * observed. + */ + void respectPrivacyNotifications(); }; diff --git a/image/public/imgITools.idl b/image/public/imgITools.idl index 147aac073c29..fb474ec620e6 100644 --- a/image/public/imgITools.idl +++ b/image/public/imgITools.idl @@ -8,8 +8,11 @@ interface nsIInputStream; interface imgIContainer; +interface imgILoader; +interface imgICache; +interface nsIDOMDocument; -[scriptable, uuid(8e16f39e-7012-46bd-aa22-2a7a3265608f)] +[scriptable, uuid(53dd1cbe-cb9f-4d9e-8104-1ab72851c88e)] interface imgITools : nsISupports { /** @@ -71,6 +74,26 @@ interface imgITools : nsISupports in long aHeight, [optional] in AString outputOptions); + /** + * getImgLoaderForDocument + * Retrieve an image loader that reflects the privacy status of the given + * document. + * + * @param doc + * A document. Must not be null. + */ + imgILoader getImgLoaderForDocument(in nsIDOMDocument doc); + + /** + * getImgLoaderForDocument + * Retrieve an image cache that reflects the privacy status of the given + * document. + * + * @param doc + * A document. Must not be null. + */ + imgICache getImgCacheForDocument(in nsIDOMDocument doc); + /** * encodeCroppedImage * Caller provides an image container, and the mime type it should be diff --git a/image/src/imgLoader.cpp b/image/src/imgLoader.cpp index ea51e8def3c4..d14a40daf5f9 100644 --- a/image/src/imgLoader.cpp +++ b/image/src/imgLoader.cpp @@ -50,7 +50,6 @@ #include "nsIApplicationCacheContainer.h" #include "nsIMemoryReporter.h" -#include "nsIPrivateBrowsingService.h" // we want to explore making the document own the load group // so we can associate the document URI with the load group. @@ -58,6 +57,7 @@ #include "nsIHttpChannelInternal.h" #include "nsIContentSecurityPolicy.h" #include "nsIChannelPolicy.h" +#include "nsILoadContext.h" #include "nsContentUtils.h" @@ -87,8 +87,11 @@ public: { AllSizes chrome; AllSizes content; - imgLoader::sChromeCache.EnumerateRead(EntryAllSizes, &chrome); - imgLoader::sCache.EnumerateRead(EntryAllSizes, &content); + + for (PRUint32 i = 0; i < mKnownLoaders.Length(); i++) { + mKnownLoaders[i]->mChromeCache.EnumerateRead(EntryAllSizes, &chrome); + mKnownLoaders[i]->mCache.EnumerateRead(EntryAllSizes, &content); + } #define REPORT(_path, _kind, _amount, _desc) \ do { \ @@ -155,8 +158,10 @@ public: NS_IMETHOD GetExplicitNonHeap(int64_t *n) { size_t n2 = 0; - imgLoader::sChromeCache.EnumerateRead(EntryExplicitNonHeapSize, &n2); - imgLoader::sCache.EnumerateRead(EntryExplicitNonHeapSize, &n2); + for (PRUint32 i = 0; i < mKnownLoaders.Length(); i++) { + mKnownLoaders[i]->mChromeCache.EnumerateRead(EntryExplicitNonHeapSize, &n2); + mKnownLoaders[i]->mCache.EnumerateRead(EntryExplicitNonHeapSize, &n2); + } *n = n2; return NS_OK; } @@ -164,11 +169,25 @@ public: static int64_t GetImagesContentUsedUncompressed() { size_t n = 0; - imgLoader::sCache.EnumerateRead(EntryUsedUncompressedSize, &n); + for (PRUint32 i = 0; i < imgLoader::sMemReporter->mKnownLoaders.Length(); i++) { + imgLoader::sMemReporter->mKnownLoaders[i]->mCache.EnumerateRead(EntryUsedUncompressedSize, &n); + } return n; } + void RegisterLoader(imgLoader* aLoader) + { + mKnownLoaders.AppendElement(aLoader); + } + + void UnregisterLoader(imgLoader* aLoader) + { + mKnownLoaders.RemoveElement(aLoader); + } + private: + nsTArray mKnownLoaders; + struct AllSizes { size_t mUsedRaw; size_t mUsedUncompressedHeap; @@ -339,11 +358,11 @@ nsProgressNotificationProxy::GetInterface(const nsIID& iid, return NS_NOINTERFACE; } -static void NewRequestAndEntry(bool aForcePrincipalCheckForCacheEntry, +static void NewRequestAndEntry(bool aForcePrincipalCheckForCacheEntry, imgLoader* aLoader, imgRequest **aRequest, imgCacheEntry **aEntry) { - nsRefPtr request = new imgRequest(); - nsRefPtr entry = new imgCacheEntry(request, aForcePrincipalCheckForCacheEntry); + nsRefPtr request = new imgRequest(aLoader); + nsRefPtr entry = new imgCacheEntry(aLoader, request, aForcePrincipalCheckForCacheEntry); request.forget(aRequest); entry.forget(aEntry); } @@ -511,8 +530,9 @@ static uint32_t SecondsFromPRTime(PRTime prTime) return uint32_t(int64_t(prTime) / int64_t(PR_USEC_PER_SEC)); } -imgCacheEntry::imgCacheEntry(imgRequest *request, bool forcePrincipalCheck) - : mRequest(request), +imgCacheEntry::imgCacheEntry(imgLoader* loader, imgRequest *request, bool forcePrincipalCheck) + : mLoader(loader), + mRequest(request), mDataSize(0), mTouchedTime(SecondsFromPRTime(PR_Now())), mExpiryTime(0), @@ -546,7 +566,7 @@ void imgCacheEntry::UpdateCache(int32_t diff /* = 0 */) if (!Evicted() && HasNoProxies()) { nsCOMPtr uri; mRequest->GetURI(getter_AddRefs(uri)); - imgLoader::CacheEntriesChanged(uri, diff); + mLoader->CacheEntriesChanged(uri, diff); } } @@ -706,8 +726,6 @@ class imgCacheObserver MOZ_FINAL : public nsIObserver public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER -private: - imgLoader mLoader; }; NS_IMPL_ISUPPORTS1(imgCacheObserver, nsIObserver) @@ -717,10 +735,6 @@ imgCacheObserver::Observe(nsISupports* aSubject, const char* aTopic, const PRUni { if (strcmp(aTopic, "memory-pressure") == 0) { DiscardTracker::DiscardAll(); - mLoader.MinimizeCaches(); - } else if (strcmp(aTopic, "chrome-flush-skin-caches") == 0 || - strcmp(aTopic, "chrome-flush-caches") == 0) { - mLoader.ClearChromeImageCache(); } return NS_OK; } @@ -760,45 +774,44 @@ void imgCacheExpirationTracker::NotifyExpired(imgCacheEntry *entry) // We can be called multiple times on the same entry. Don't do work multiple // times. if (!entry->Evicted()) - imgLoader::RemoveFromCache(entry); + entry->Loader()->RemoveFromCache(entry); - imgLoader::VerifyCacheSizes(); + entry->Loader()->VerifyCacheSizes(); } imgCacheObserver *gCacheObserver; -imgCacheExpirationTracker *gCacheTracker; - -imgLoader::imgCacheTable imgLoader::sCache; -imgCacheQueue imgLoader::sCacheQueue; - -imgLoader::imgCacheTable imgLoader::sChromeCache; -imgCacheQueue imgLoader::sChromeCacheQueue; double imgLoader::sCacheTimeWeight; uint32_t imgLoader::sCacheMaxSize; +imgMemoryReporter* imgLoader::sMemReporter; NS_IMPL_ISUPPORTS5(imgLoader, imgILoader, nsIContentSniffer, imgICache, nsISupportsWeakReference, nsIObserver) imgLoader::imgLoader() +: mRespectPrivacy(false) { - /* member initializers and constructor code */ + sMemReporter->AddRef(); + sMemReporter->RegisterLoader(this); } imgLoader::~imgLoader() { - /* destructor code */ + ClearChromeImageCache(); + ClearImageCache(); + sMemReporter->UnregisterLoader(this); + sMemReporter->Release(); } void imgLoader::VerifyCacheSizes() { #ifdef DEBUG - if (!gCacheTracker) + if (!mCacheTracker) return; - uint32_t cachesize = sCache.Count() + sChromeCache.Count(); - uint32_t queuesize = sCacheQueue.GetNumElements() + sChromeCacheQueue.GetNumElements(); + uint32_t cachesize = mCache.Count() + mChromeCache.Count(); + uint32_t queuesize = mCacheQueue.GetNumElements() + mChromeCacheQueue.GetNumElements(); uint32_t trackersize = 0; - for (nsExpirationTracker::Iterator it(gCacheTracker); it.Next(); ) + for (nsExpirationTracker::Iterator it(mCacheTracker); it.Next(); ) trackersize++; NS_ABORT_IF_FALSE(queuesize == trackersize, "Queue and tracker sizes out of sync!"); NS_ABORT_IF_FALSE(queuesize <= cachesize, "Queue has more elements than cache!"); @@ -810,9 +823,9 @@ imgLoader::imgCacheTable & imgLoader::GetCache(nsIURI *aURI) bool chrome = false; aURI->SchemeIs("chrome", &chrome); if (chrome) - return sChromeCache; + return mChromeCache; else - return sCache; + return mCache; } imgCacheQueue & imgLoader::GetCacheQueue(nsIURI *aURI) @@ -820,34 +833,22 @@ imgCacheQueue & imgLoader::GetCacheQueue(nsIURI *aURI) bool chrome = false; aURI->SchemeIs("chrome", &chrome); if (chrome) - return sChromeCacheQueue; + return mChromeCacheQueue; else - return sCacheQueue; + return mCacheQueue; } -nsresult imgLoader::InitCache() +void imgLoader::GlobalInit() { - NS_TIME_FUNCTION; - - nsresult rv; - nsCOMPtr os = mozilla::services::GetObserverService(); - if (!os) - return NS_ERROR_FAILURE; - gCacheObserver = new imgCacheObserver(); NS_ADDREF(gCacheObserver); - os->AddObserver(gCacheObserver, "memory-pressure", false); - os->AddObserver(gCacheObserver, "chrome-flush-skin-caches", false); - os->AddObserver(gCacheObserver, "chrome-flush-caches", false); - - gCacheTracker = new imgCacheExpirationTracker(); - - sCache.Init(); - sChromeCache.Init(); + nsCOMPtr os = mozilla::services::GetObserverService(); + if (os) + os->AddObserver(gCacheObserver, "memory-pressure", false); int32_t timeweight; - rv = Preferences::GetInt("image.cache.timeweight", &timeweight); + nsresult rv = Preferences::GetInt("image.cache.timeweight", &timeweight); if (NS_SUCCEEDED(rv)) sCacheTimeWeight = timeweight / 1000.0; else @@ -860,23 +861,49 @@ nsresult imgLoader::InitCache() else sCacheMaxSize = 5 * 1024 * 1024; - NS_RegisterMemoryMultiReporter(new imgMemoryReporter()); + sMemReporter = new imgMemoryReporter(); + NS_RegisterMemoryMultiReporter(sMemReporter); NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(ImagesContentUsedUncompressed)); - - return NS_OK; +} + +nsresult imgLoader::InitCache() +{ + NS_TIME_FUNCTION; + + nsCOMPtr os = mozilla::services::GetObserverService(); + if (!os) + return NS_ERROR_FAILURE; + + os->AddObserver(this, "memory-pressure", false); + os->AddObserver(this, "chrome-flush-skin-caches", false); + os->AddObserver(this, "chrome-flush-caches", false); + os->AddObserver(this, "last-pb-context-exited", false); + os->AddObserver(this, "profile-before-change", false); + os->AddObserver(this, "xpcom-shutdown", false); + + mCacheTracker = new imgCacheExpirationTracker(); + + mCache.Init(); + mChromeCache.Init(); + + return NS_OK; } nsresult imgLoader::Init() { + InitCache(); + ReadAcceptHeaderPref(); Preferences::AddWeakObserver(this, "image.http.accept"); - // Listen for when we leave private browsing mode - nsCOMPtr obService = mozilla::services::GetObserverService(); - if (obService) - obService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, true); + return NS_OK; +} +NS_IMETHODIMP +imgLoader::RespectPrivacyNotifications() +{ + mRespectPrivacy = true; return NS_OK; } @@ -888,12 +915,21 @@ imgLoader::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* a if (!strcmp(NS_ConvertUTF16toUTF8(aData).get(), "image.http.accept")) { ReadAcceptHeaderPref(); } - } - // ...and exits from private browsing. - else if (!strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC)) { - if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData)) + } else if (strcmp(aTopic, "memory-pressure") == 0) { + MinimizeCaches(); + } else if (strcmp(aTopic, "chrome-flush-skin-caches") == 0 || + strcmp(aTopic, "chrome-flush-caches") == 0) { + MinimizeCaches(); + ClearChromeImageCache(); + } else if (strcmp(aTopic, "last-pb-context-exited") == 0) { + if (mRespectPrivacy) { ClearImageCache(); + ClearChromeImageCache(); + } + } else if (strcmp(aTopic, "profile-before-change") == 0 || + strcmp(aTopic, "xpcom-shutdown") == 0) { + mCacheTracker = nullptr; } // (Nothing else should bring us here) @@ -942,8 +978,8 @@ NS_IMETHODIMP imgLoader::FindEntryProperties(nsIURI *uri, nsIProperties **_retva *_retval = nullptr; if (cache.Get(spec, getter_AddRefs(entry)) && entry) { - if (gCacheTracker && entry->HasNoProxies()) - gCacheTracker->MarkUsed(entry); + if (mCacheTracker && entry->HasNoProxies()) + mCacheTracker->MarkUsed(entry); nsRefPtr request = getter_AddRefs(entry->GetRequest()); if (request) { @@ -957,27 +993,23 @@ NS_IMETHODIMP imgLoader::FindEntryProperties(nsIURI *uri, nsIProperties **_retva void imgLoader::Shutdown() { - ClearChromeImageCache(); - ClearImageCache(); - NS_IF_RELEASE(gCacheObserver); - delete gCacheTracker; - gCacheTracker = nullptr; + NS_RELEASE(gCacheObserver); } nsresult imgLoader::ClearChromeImageCache() { - return EvictEntries(sChromeCache); + return EvictEntries(mChromeCache); } nsresult imgLoader::ClearImageCache() { - return EvictEntries(sCache); + return EvictEntries(mCache); } void imgLoader::MinimizeCaches() { - EvictEntries(sCacheQueue); - EvictEntries(sChromeCacheQueue); + EvictEntries(mCacheQueue); + EvictEntries(mChromeCacheQueue); } bool imgLoader::PutIntoCache(nsIURI *key, imgCacheEntry *entry) @@ -1020,8 +1052,8 @@ bool imgLoader::PutIntoCache(nsIURI *key, imgCacheEntry *entry) if (entry->HasNoProxies()) { nsresult addrv = NS_OK; - if (gCacheTracker) - addrv = gCacheTracker->AddObject(entry); + if (mCacheTracker) + addrv = mCacheTracker->AddObject(entry); if (NS_SUCCEEDED(addrv)) { imgCacheQueue &queue = GetCacheQueue(key); @@ -1051,8 +1083,8 @@ bool imgLoader::SetHasNoProxies(nsIURI *key, imgCacheEntry *entry) nsresult addrv = NS_OK; - if (gCacheTracker) - addrv = gCacheTracker->AddObject(entry); + if (mCacheTracker) + addrv = mCacheTracker->AddObject(entry); if (NS_SUCCEEDED(addrv)) { queue.Push(entry); @@ -1081,8 +1113,8 @@ bool imgLoader::SetHasProxies(nsIURI *key) imgCacheQueue &queue = GetCacheQueue(key); queue.Remove(entry); - if (gCacheTracker) - gCacheTracker->RemoveObject(entry); + if (mCacheTracker) + mCacheTracker->RemoveObject(entry); entry->SetHasNoProxies(false); @@ -1207,7 +1239,7 @@ bool imgLoader::ValidateRequestWithNewChannel(imgRequest *request, return false; nsRefPtr hvc = - new imgCacheValidator(progressproxy, request, aCX, forcePrincipalCheck); + new imgCacheValidator(progressproxy, this, request, aCX, forcePrincipalCheck); nsCOMPtr listener = hvc.get(); @@ -1388,8 +1420,8 @@ bool imgLoader::RemoveFromCache(nsIURI *aKey) // Entries with no proxies are in the tracker. if (entry->HasNoProxies()) { - if (gCacheTracker) - gCacheTracker->RemoveObject(entry); + if (mCacheTracker) + mCacheTracker->RemoveObject(entry); queue.Remove(entry); } @@ -1423,8 +1455,8 @@ bool imgLoader::RemoveFromCache(imgCacheEntry *entry) if (entry->HasNoProxies()) { LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache removing from tracker"); - if (gCacheTracker) - gCacheTracker->RemoveObject(entry); + if (mCacheTracker) + mCacheTracker->RemoveObject(entry); queue.Remove(entry); } @@ -1524,6 +1556,25 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, nsresult rv; nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL; +#ifdef DEBUG + bool isPrivate = false; + + nsCOMPtr channel = do_QueryInterface(aRequest); + if (channel) { + nsCOMPtr loadContext; + NS_QueryNotificationCallbacks(channel, loadContext); + isPrivate = loadContext && loadContext->UsePrivateBrowsing(); + } else if (aLoadGroup) { + nsCOMPtr callbacks; + aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks)); + if (callbacks) { + nsCOMPtr loadContext = do_GetInterface(callbacks); + isPrivate = loadContext && loadContext->UsePrivateBrowsing(); + } + } + MOZ_ASSERT(isPrivate == mRespectPrivacy); +#endif + // Get the default load flags from the loadgroup (if possible)... if (aLoadGroup) { aLoadGroup->GetLoadFlags(&requestFlags); @@ -1577,8 +1628,8 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, NS_ABORT_IF_FALSE(!request->HasCacheEntry(), "Proxyless entry's request has cache entry!"); request->SetCacheEntry(entry); - if (gCacheTracker) - gCacheTracker->MarkUsed(entry); + if (mCacheTracker) + mCacheTracker->MarkUsed(entry); } entry->Touch(); @@ -1615,8 +1666,13 @@ NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, if (NS_FAILED(rv)) return NS_ERROR_FAILURE; - NewRequestAndEntry(forcePrincipalCheck, getter_AddRefs(request), - getter_AddRefs(entry)); +#ifdef DEBUG + nsCOMPtr loadContext; + NS_QueryNotificationCallbacks(newChannel, loadContext); + MOZ_ASSERT_IF(loadContext, loadContext->UsePrivateBrowsing() == mRespectPrivacy); +#endif + + NewRequestAndEntry(forcePrincipalCheck, this, getter_AddRefs(request), getter_AddRefs(entry)); PR_LOG(gImgLog, PR_LOG_DEBUG, ("[this=%p] imgLoader::LoadImage -- Created new imgRequest [request=%p]\n", this, request.get())); @@ -1741,6 +1797,12 @@ NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderOb { NS_ASSERTION(channel, "imgLoader::LoadImageWithChannel -- NULL channel pointer"); +#ifdef DEBUG + nsCOMPtr loadContext; + NS_QueryNotificationCallbacks(channel, loadContext); + MOZ_ASSERT_IF(loadContext, loadContext->UsePrivateBrowsing() == mRespectPrivacy); +#endif + nsRefPtr request; nsCOMPtr uri; @@ -1798,8 +1860,8 @@ NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderOb NS_ABORT_IF_FALSE(!request->HasCacheEntry(), "Proxyless entry's request has cache entry!"); request->SetCacheEntry(entry); - if (gCacheTracker) - gCacheTracker->MarkUsed(entry); + if (mCacheTracker) + mCacheTracker->MarkUsed(entry); } } } @@ -1826,7 +1888,7 @@ NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderOb // Default to doing a principal check because we don't know who // started that load and whether their principal ended up being // inherited on the channel. - NewRequestAndEntry(true, getter_AddRefs(request), getter_AddRefs(entry)); + NewRequestAndEntry(true, this, getter_AddRefs(request), getter_AddRefs(entry)); // We use originalURI here to fulfil the imgIRequest contract on GetURI. nsCOMPtr originalURI; @@ -2038,17 +2100,16 @@ NS_IMPL_ISUPPORTS5(imgCacheValidator, nsIStreamListener, nsIRequestObserver, nsIChannelEventSink, nsIInterfaceRequestor, nsIAsyncVerifyRedirectCallback) -imgLoader imgCacheValidator::sImgLoader; - imgCacheValidator::imgCacheValidator(nsProgressNotificationProxy* progress, - imgRequest *request, void *aContext, - bool forcePrincipalCheckForCacheEntry) + imgLoader* loader, imgRequest *request, + void *aContext, bool forcePrincipalCheckForCacheEntry) : mProgressProxy(progress), mRequest(request), - mContext(aContext) + mContext(aContext), + mImgLoader(loader) { - NewRequestAndEntry(forcePrincipalCheckForCacheEntry, - getter_AddRefs(mNewRequest), getter_AddRefs(mNewEntry)); + NewRequestAndEntry(forcePrincipalCheckForCacheEntry, loader, getter_AddRefs(mNewRequest), + getter_AddRefs(mNewEntry)); } imgCacheValidator::~imgCacheValidator() @@ -2151,7 +2212,7 @@ NS_IMETHODIMP imgCacheValidator::OnStartRequest(nsIRequest *aRequest, nsISupport // Try to add the new request into the cache. Note that the entry must be in // the cache before the proxies' ownership changes, because adding a proxy // changes the caching behaviour for imgRequests. - sImgLoader.PutIntoCache(originalURI, mNewEntry); + mImgLoader->PutIntoCache(originalURI, mNewEntry); uint32_t count = mProxies.Count(); for (int32_t i = count-1; i>=0; i--) { diff --git a/image/src/imgLoader.h b/image/src/imgLoader.h index 64d3563b93ff..85756884eed2 100644 --- a/image/src/imgLoader.h +++ b/image/src/imgLoader.h @@ -27,16 +27,19 @@ #include "prlock.h" #endif +class imgLoader; class imgRequest; class imgRequestProxy; class imgIRequest; class imgIDecoderObserver; class nsILoadGroup; +class imgCacheExpirationTracker; +class imgMemoryReporter; class imgCacheEntry { public: - imgCacheEntry(imgRequest *request, bool aForcePrincipalCheck); + imgCacheEntry(imgLoader* loader, imgRequest *request, bool aForcePrincipalCheck); ~imgCacheEntry(); nsrefcnt AddRef() @@ -130,6 +133,11 @@ public: return mForcePrincipalCheck; } + imgLoader* Loader() const + { + return mLoader; + } + private: // methods friend class imgLoader; friend class imgCacheQueue; @@ -148,6 +156,7 @@ private: // data nsAutoRefCnt mRefCnt; NS_DECL_OWNINGTHREAD + imgLoader* mLoader; nsRefPtr mRequest; uint32_t mDataSize; int32_t mTouchedTime; @@ -219,18 +228,19 @@ public: static nsresult GetMimeTypeFromContent(const char* aContents, uint32_t aLength, nsACString& aContentType); + static void GlobalInit(); // for use by the factory static void Shutdown(); // for use by the factory - static nsresult ClearChromeImageCache(); - static nsresult ClearImageCache(); - static void MinimizeCaches(); + nsresult ClearChromeImageCache(); + nsresult ClearImageCache(); + void MinimizeCaches(); - static nsresult InitCache(); + nsresult InitCache(); - static bool RemoveFromCache(nsIURI *aKey); - static bool RemoveFromCache(imgCacheEntry *entry); + bool RemoveFromCache(nsIURI *aKey); + bool RemoveFromCache(imgCacheEntry *entry); - static bool PutIntoCache(nsIURI *key, imgCacheEntry *entry); + bool PutIntoCache(nsIURI *key, imgCacheEntry *entry); // Returns true if we should prefer evicting cache entry |two| over cache // entry |one|. @@ -256,7 +266,7 @@ public: return oneweight < twoweight; } - static void VerifyCacheSizes(); + void VerifyCacheSizes(); // The image loader maintains a hash table of all imgCacheEntries. However, // only some of them will be evicted from the cache: those who have no @@ -269,8 +279,8 @@ public: // HasObservers(). The request's cache entry will be re-set before this // happens, by calling imgRequest::SetCacheEntry() when an entry with no // observers is re-requested. - static bool SetHasNoProxies(nsIURI *key, imgCacheEntry *entry); - static bool SetHasProxies(nsIURI *key); + bool SetHasNoProxies(nsIURI *key, imgCacheEntry *entry); + bool SetHasProxies(nsIURI *key); private: // methods @@ -307,27 +317,32 @@ private: // methods typedef nsRefPtrHashtable imgCacheTable; - static nsresult EvictEntries(imgCacheTable &aCacheToClear); - static nsresult EvictEntries(imgCacheQueue &aQueueToClear); + nsresult EvictEntries(imgCacheTable &aCacheToClear); + nsresult EvictEntries(imgCacheQueue &aQueueToClear); - static imgCacheTable &GetCache(nsIURI *aURI); - static imgCacheQueue &GetCacheQueue(nsIURI *aURI); - static void CacheEntriesChanged(nsIURI *aURI, int32_t sizediff = 0); - static void CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue); + imgCacheTable &GetCache(nsIURI *aURI); + imgCacheQueue &GetCacheQueue(nsIURI *aURI); + void CacheEntriesChanged(nsIURI *aURI, PRInt32 sizediff = 0); + void CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue); private: // data friend class imgCacheEntry; friend class imgMemoryReporter; - static imgCacheTable sCache; - static imgCacheQueue sCacheQueue; + imgCacheTable mCache; + imgCacheQueue mCacheQueue; + + imgCacheTable mChromeCache; + imgCacheQueue mChromeCacheQueue; - static imgCacheTable sChromeCache; - static imgCacheQueue sChromeCacheQueue; static double sCacheTimeWeight; static uint32_t sCacheMaxSize; + static imgMemoryReporter* sMemReporter; nsCString mAcceptHeader; + + nsAutoPtr mCacheTracker; + bool mRespectPrivacy; }; @@ -395,8 +410,8 @@ class imgCacheValidator : public nsIStreamListener, public nsIAsyncVerifyRedirectCallback { public: - imgCacheValidator(nsProgressNotificationProxy* progress, imgRequest *request, - void *aContext, bool forcePrincipalCheckForCacheEntry); + imgCacheValidator(nsProgressNotificationProxy* progress, imgLoader* loader, + imgRequest *request, void *aContext, bool forcePrincipalCheckForCacheEntry); virtual ~imgCacheValidator(); void AddProxy(imgRequestProxy *aProxy); @@ -422,7 +437,7 @@ private: void *mContext; - static imgLoader sImgLoader; + imgLoader* mImgLoader; }; #endif // imgLoader_h__ diff --git a/image/src/imgRequest.cpp b/image/src/imgRequest.cpp index e89e05a912f0..2fc58a8e8702 100644 --- a/image/src/imgRequest.cpp +++ b/image/src/imgRequest.cpp @@ -79,8 +79,8 @@ NS_IMPL_ISUPPORTS8(imgRequest, nsIInterfaceRequestor, nsIAsyncVerifyRedirectCallback) -imgRequest::imgRequest() : - mValidator(nullptr), mImageSniffers("image-sniffing-services"), +imgRequest::imgRequest(imgLoader* aLoader) : + mLoader(aLoader), mValidator(nullptr), mImageSniffers("image-sniffing-services"), mInnerWindowId(0), mCORSMode(imgIRequest::CORS_NONE), mDecodeRequested(false), mIsMultiPartChannel(false), mGotData(false), mIsInCache(false), mBlockingOnload(false) @@ -178,7 +178,7 @@ nsresult imgRequest::AddProxy(imgRequestProxy *proxy) // proxies. if (mObservers.IsEmpty()) { NS_ABORT_IF_FALSE(mURI, "Trying to SetHasProxies without key uri."); - imgLoader::SetHasProxies(mURI); + mLoader->SetHasProxies(mURI); } // If we don't have any current observers, we should restart any animation. @@ -223,7 +223,7 @@ nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, bool if (mCacheEntry) { NS_ABORT_IF_FALSE(mURI, "Removing last observer without key uri."); - imgLoader::SetHasNoProxies(mURI, mCacheEntry); + mLoader->SetHasNoProxies(mURI, mCacheEntry); } #if defined(PR_LOGGING) else { @@ -328,9 +328,9 @@ void imgRequest::RemoveFromCache() if (mIsInCache) { // mCacheEntry is nulled out when we have no more observers. if (mCacheEntry) - imgLoader::RemoveFromCache(mCacheEntry); + mLoader->RemoveFromCache(mCacheEntry); else - imgLoader::RemoveFromCache(mURI); + mLoader->RemoveFromCache(mURI); } mCacheEntry = nullptr; diff --git a/image/src/imgRequest.h b/image/src/imgRequest.h index 424631b5b8c1..71c7715831cc 100644 --- a/image/src/imgRequest.h +++ b/image/src/imgRequest.h @@ -31,6 +31,7 @@ class imgCacheValidator; +class imgLoader; class imgRequestProxy; class imgCacheEntry; class imgMemoryReporter; @@ -50,7 +51,7 @@ class imgRequest : public imgIDecoderObserver, public nsIAsyncVerifyRedirectCallback { public: - imgRequest(); + imgRequest(imgLoader* aLoader); virtual ~imgRequest(); NS_DECL_ISUPPORTS @@ -183,6 +184,8 @@ public: private: friend class imgMemoryReporter; + // Weak reference to parent loader; this request cannot outlive its owner. + imgLoader* mLoader; nsCOMPtr mRequest; // The original URI we were loaded with. This is the same as the URI we are // keyed on in the cache. diff --git a/image/src/imgTools.cpp b/image/src/imgTools.cpp index 827b46d0d64f..eca1d366848e 100644 --- a/image/src/imgTools.cpp +++ b/image/src/imgTools.cpp @@ -8,6 +8,8 @@ #include "nsCOMPtr.h" #include "nsString.h" #include "nsError.h" +#include "imgILoader.h" +#include "imgICache.h" #include "imgIContainer.h" #include "imgIEncoder.h" #include "imgIDecoderObserver.h" @@ -19,10 +21,14 @@ #include "nsIInterfaceRequestorUtils.h" #include "nsStreamUtils.h" #include "nsNetUtil.h" +#include "nsContentUtils.h" #include "RasterImage.h" using namespace mozilla::image; +class nsIDOMDocument; +class nsIDocument; + /* ========== imgITools implementation ========== */ @@ -269,3 +275,20 @@ NS_IMETHODIMP imgTools::GetFirstImageFrame(imgIContainer *aContainer, frame.forget(aSurface); return NS_OK; } + +NS_IMETHODIMP +imgTools::GetImgLoaderForDocument(nsIDOMDocument* aDoc, imgILoader** aLoader) +{ + nsCOMPtr doc = do_QueryInterface(aDoc); + NS_IF_ADDREF(*aLoader = nsContentUtils::GetImgLoaderForDocument(doc)); + return NS_OK; +} + +NS_IMETHODIMP +imgTools::GetImgCacheForDocument(nsIDOMDocument* aDoc, imgICache** aCache) +{ + nsCOMPtr loader; + nsresult rv = GetImgLoaderForDocument(aDoc, getter_AddRefs(loader)); + NS_ENSURE_SUCCESS(rv, rv); + return CallQueryInterface(loader, aCache); +} diff --git a/image/test/mochitest/imgutils.js b/image/test/mochitest/imgutils.js index 433518547296..bbdcb9c4a46b 100644 --- a/image/test/mochitest/imgutils.js +++ b/image/test/mochitest/imgutils.js @@ -7,9 +7,10 @@ // Helper function to clear the image cache of content images function clearImageCache() { - netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - var imageCache = Components.classes["@mozilla.org/image/cache;1"] - .getService(Components.interfaces.imgICache); + var tools = SpecialPowers.wrap(Components) + .classes["@mozilla.org/image/tools;1"] + .getService(Components.interfaces.imgITools); + var imageCache = tools.getImgCacheForDocument(window.document); imageCache.clearCache(false); // true=chrome, false=content } diff --git a/image/test/unit/async_load_tests.js b/image/test/unit/async_load_tests.js index 0124701a06b9..f2346f79fb0d 100644 --- a/image/test/unit/async_load_tests.js +++ b/image/test/unit/async_load_tests.js @@ -85,9 +85,8 @@ function checkSecondLoad() { do_test_pending(); - var loader = Cc["@mozilla.org/image/loader;1"].getService(Ci.imgILoader); var listener = new ImageListener(checkClone, secondLoadDone); - requests.push(loader.loadImage(uri, null, null, null, null, listener, null, 0, null, null, null)); + requests.push(gCurrentLoader.loadImage(uri, null, null, null, null, listener, null, 0, null, null, null)); listener.synchronous = false; } @@ -139,12 +138,11 @@ function checkSecondChannelLoad() var channellistener = new ChannelListener(); channel.asyncOpen(channellistener, null); - var loader = Cc["@mozilla.org/image/loader;1"].getService(Ci.imgILoader); var listener = new ImageListener(getChannelLoadImageStartCallback(channellistener), getChannelLoadImageStopCallback(channellistener, all_done_callback)); var outlistener = {}; - requests.push(loader.loadImageWithChannel(channel, listener, null, outlistener)); + requests.push(gCurrentLoader.loadImageWithChannel(channel, listener, null, outlistener)); channellistener.outputListener = outlistener.value; listener.synchronous = false; @@ -152,11 +150,8 @@ function checkSecondChannelLoad() function run_loadImageWithChannel_tests() { - // To ensure we're testing what we expect to, clear the content image cache - // between test runs. - var loader = Cc["@mozilla.org/image/loader;1"].getService(Ci.imgILoader); - loader.QueryInterface(Ci.imgICache); - loader.clearCache(false); + // To ensure we're testing what we expect to, create a new loader and cache. + gCurrentLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader); do_test_pending(); @@ -165,12 +160,11 @@ function run_loadImageWithChannel_tests() var channellistener = new ChannelListener(); channel.asyncOpen(channellistener, null); - var loader = Cc["@mozilla.org/image/loader;1"].getService(Ci.imgILoader); var listener = new ImageListener(getChannelLoadImageStartCallback(channellistener), getChannelLoadImageStopCallback(channellistener, checkSecondChannelLoad)); var outlistener = {}; - requests.push(loader.loadImageWithChannel(channel, listener, null, outlistener)); + requests.push(gCurrentLoader.loadImageWithChannel(channel, listener, null, outlistener)); channellistener.outputListener = outlistener.value; listener.synchronous = false; @@ -185,12 +179,10 @@ function startImageCallback(otherCb) { return function(listener, request) { - var loader = Cc["@mozilla.org/image/loader;1"].getService(Ci.imgILoader); - // Make sure we can load the same image immediately out of the cache. do_test_pending(); var listener2 = new ImageListener(null, function(foo, bar) { do_test_finished(); }); - requests.push(loader.loadImage(uri, null, null, null, null, listener2, null, 0, null, null, null)); + requests.push(gCurrentLoader.loadImage(uri, null, null, null, null, listener2, null, 0, null, null, null)); listener2.synchronous = false; // Now that we've started another load, chain to the callback. @@ -198,13 +190,15 @@ function startImageCallback(otherCb) } } +var gCurrentLoader; + function run_test() { - var loader = Cc["@mozilla.org/image/loader;1"].getService(Ci.imgILoader); + gCurrentLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader); do_test_pending(); var listener = new ImageListener(startImageCallback(checkClone), firstLoadDone); - var req = loader.loadImage(uri, null, null, null, null, listener, null, 0, null, null, null); + var req = gCurrentLoader.loadImage(uri, null, null, null, null, listener, null, 0, null, null, null); requests.push(req); // Ensure that we don't cause any mayhem when we lock an image. diff --git a/image/test/unit/test_private_channel.js b/image/test/unit/test_private_channel.js index 5fbb5624a601..d15eb4613c86 100644 --- a/image/test/unit/test_private_channel.js +++ b/image/test/unit/test_private_channel.js @@ -11,7 +11,9 @@ load('image_load_helpers.js'); var gHits = 0; var gIoService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); -var gLoader = Cc["@mozilla.org/image/loader;1"].getService(Ci.imgILoader); +var gPublicLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader); +var gPrivateLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader); +gPrivateLoader.QueryInterface(Ci.imgICache).respectPrivacyNotifications(); function imageHandler(metadata, response) { gHits++; @@ -55,7 +57,8 @@ function setup_chan(path, isPrivate, callback) { var listener = new ImageListener(null, callback); listeners.push(listener); var outlistener = {}; - requests.push(gLoader.loadImageWithChannel(chan, listener, null, outlistener)); + var loader = isPrivate ? gPrivateLoader : gPublicLoader; + requests.push(loader.loadImageWithChannel(chan, listener, null, outlistener)); channelListener.outputListener = outlistener.value; listener.synchronous = false; } @@ -65,12 +68,12 @@ function loadImage(isPrivate, callback) { var uri = gIoService.newURI(gImgPath, null, null); var loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(Ci.nsILoadGroup); loadGroup.notificationCallbacks = new NotificationCallbacks(isPrivate); - requests.push(gLoader.loadImage(uri, null, null, null, loadGroup, listener, null, 0, null, null, null)); + var loader = isPrivate ? gPrivateLoader : gPublicLoader; + requests.push(loader.loadImage(uri, null, null, null, loadGroup, listener, null, 0, null, null, null)); listener.synchronous = false; } function run_loadImage_tests() { - clearAllImageCaches(); let cs = Cc["@mozilla.org/network/cache-service;1"].getService(Ci.nsICacheService); cs.evictEntries(Ci.nsICache.STORE_ANYWHERE); @@ -80,21 +83,14 @@ function run_loadImage_tests() { loadImage(true, function() { loadImage(true, function() { do_check_eq(gHits, 2); - do_test_finished(); + server.stop(do_test_finished); }); }); }); }); } -function clearAllImageCaches() { - gLoader.QueryInterface(Ci.imgICache); - gLoader.clearCache(false, false); - gLoader.clearCache(false, true); -} - function run_test() { - clearAllImageCaches(); do_test_pending(); // We create a public channel that loads an image, then an identical diff --git a/layout/build/nsContentDLF.cpp b/layout/build/nsContentDLF.cpp index 011e29963351..84dd80f6fa36 100644 --- a/layout/build/nsContentDLF.cpp +++ b/layout/build/nsContentDLF.cpp @@ -481,7 +481,7 @@ nsContentDLF::CreateXULDocument(const char* aCommand, } bool nsContentDLF::IsImageContentType(const char* aContentType) { - nsCOMPtr loader(do_GetService("@mozilla.org/image/loader;1")); + nsCOMPtr loader(do_CreateInstance("@mozilla.org/image/loader;1")); bool isDecoderAvailable = false; loader->SupportImageWithMimeType(aContentType, &isDecoderAvailable); return isDecoderAvailable; diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 748107df0e45..6dfde2a2a9ee 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -1786,8 +1786,8 @@ nsImageFrame::LoadIcon(const nsAString& aSpec, nsCOMPtr realURI; SpecToURI(aSpec, sIOService, getter_AddRefs(realURI)); - nsCOMPtr il(do_GetService("@mozilla.org/image/loader;1", &rv)); - if (NS_FAILED(rv)) return rv; + nsCOMPtr il = + nsContentUtils::GetImgLoaderForDocument(aPresContext->Document()); nsCOMPtr loadGroup; GetLoadGroup(aPresContext, getter_AddRefs(loadGroup)); diff --git a/widget/cocoa/nsMenuItemIconX.mm b/widget/cocoa/nsMenuItemIconX.mm index e0c07c4a7c19..4252f18c0b56 100644 --- a/widget/cocoa/nsMenuItemIconX.mm +++ b/widget/cocoa/nsMenuItemIconX.mm @@ -29,6 +29,7 @@ #include "gfxImageSurface.h" #include "imgIContainer.h" #include "nsCocoaUtils.h" +#include "nsContentUtils.h" static const uint32_t kIconWidth = 16; static const uint32_t kIconHeight = 16; @@ -278,10 +279,8 @@ nsMenuItemIconX::LoadIcon(nsIURI* aIconURI) nsCOMPtr loadGroup = document->GetDocumentLoadGroup(); if (!loadGroup) return NS_ERROR_FAILURE; - nsresult rv = NS_ERROR_FAILURE; - nsCOMPtr loader = do_GetService("@mozilla.org/image/loader;1", - &rv); - if (NS_FAILED(rv)) return rv; + nsCOMPtr loader = nsContentUtils::GetImgLoaderForDocument(document); + if (!loader) return NS_ERROR_FAILURE; if (!mSetIcon) { // Set a completely transparent 16x16 image as the icon on this menu item @@ -306,9 +305,9 @@ nsMenuItemIconX::LoadIcon(nsIURI* aIconURI) // Passing in null for channelPolicy here since nsMenuItemIconX::LoadIcon is // not exposed to web content - rv = loader->LoadImage(aIconURI, nullptr, nullptr, nullptr, loadGroup, this, - nullptr, nsIRequest::LOAD_NORMAL, nullptr, nullptr, - nullptr, getter_AddRefs(mIconRequest)); + nsresult rv = loader->LoadImage(aIconURI, nullptr, nullptr, nullptr, loadGroup, this, + nullptr, nsIRequest::LOAD_NORMAL, nullptr, nullptr, + nullptr, getter_AddRefs(mIconRequest)); if (NS_FAILED(rv)) return rv; // We need to request the icon be decoded (bug 573583, bug 705516).