1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "ImageLogging.h"
- #include "imgLoader.h"
- #include "mozilla/Attributes.h"
- #include "mozilla/ClearOnShutdown.h"
- #include "mozilla/Move.h"
- #include "mozilla/Preferences.h"
- #include "mozilla/ChaosMode.h"
- #include "nsImageModule.h"
- #include "imgRequestProxy.h"
- #include "nsCOMPtr.h"
- #include "nsContentPolicyUtils.h"
- #include "nsContentUtils.h"
- #include "nsNetUtil.h"
- #include "nsNetCID.h"
- #include "nsIProtocolHandler.h"
- #include "nsMimeTypes.h"
- #include "nsStreamUtils.h"
- #include "nsIHttpChannel.h"
- #include "nsICacheInfoChannel.h"
- #include "nsIInterfaceRequestor.h"
- #include "nsIInterfaceRequestorUtils.h"
- #include "nsIProgressEventSink.h"
- #include "nsIChannelEventSink.h"
- #include "nsIAsyncVerifyRedirectCallback.h"
- #include "nsIFileURL.h"
- #include "nsIFile.h"
- #include "nsCRT.h"
- #include "nsINetworkPredictor.h"
- #include "mozilla/dom/ContentParent.h"
- #include "mozilla/dom/nsMixedContentBlocker.h"
- #include "nsIApplicationCache.h"
- #include "nsIApplicationCacheContainer.h"
- #include "nsIMemoryReporter.h"
- #include "DecoderFactory.h"
- #include "Image.h"
- #include "gfxPrefs.h"
- #include "prtime.h"
- // we want to explore making the document own the load group
- // so we can associate the document URI with the load group.
- // until this point, we have an evil hack:
- #include "nsIHttpChannelInternal.h"
- #include "nsILoadContext.h"
- #include "nsILoadGroupChild.h"
- #include "nsIDOMDocument.h"
- using namespace mozilla;
- using namespace mozilla::dom;
- using namespace mozilla::image;
- using namespace mozilla::net;
- MOZ_DEFINE_MALLOC_SIZE_OF(ImagesMallocSizeOf)
- class imgMemoryReporter final : public nsIMemoryReporter
- {
- ~imgMemoryReporter() { }
- public:
- NS_DECL_ISUPPORTS
- NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
- nsISupports* aData, bool aAnonymize) override
- {
- nsTArray<ImageMemoryCounter> chrome;
- nsTArray<ImageMemoryCounter> content;
- nsTArray<ImageMemoryCounter> uncached;
- for (uint32_t i = 0; i < mKnownLoaders.Length(); i++) {
- for (auto iter = mKnownLoaders[i]->mChromeCache.Iter(); !iter.Done(); iter.Next()) {
- imgCacheEntry* entry = iter.UserData();
- RefPtr<imgRequest> req = entry->GetRequest();
- RecordCounterForRequest(req, &chrome, !entry->HasNoProxies());
- }
- for (auto iter = mKnownLoaders[i]->mCache.Iter(); !iter.Done(); iter.Next()) {
- imgCacheEntry* entry = iter.UserData();
- RefPtr<imgRequest> req = entry->GetRequest();
- RecordCounterForRequest(req, &content, !entry->HasNoProxies());
- }
- MutexAutoLock lock(mKnownLoaders[i]->mUncachedImagesMutex);
- for (auto iter = mKnownLoaders[i]->mUncachedImages.Iter();
- !iter.Done();
- iter.Next()) {
- nsPtrHashKey<imgRequest>* entry = iter.Get();
- RefPtr<imgRequest> req = entry->GetKey();
- RecordCounterForRequest(req, &uncached, req->HasConsumers());
- }
- }
- // Note that we only need to anonymize content image URIs.
- ReportCounterArray(aHandleReport, aData, chrome, "images/chrome");
- ReportCounterArray(aHandleReport, aData, content, "images/content",
- aAnonymize);
- // Uncached images may be content or chrome, so anonymize them.
- ReportCounterArray(aHandleReport, aData, uncached, "images/uncached",
- aAnonymize);
- return NS_OK;
- }
- static int64_t ImagesContentUsedUncompressedDistinguishedAmount()
- {
- size_t n = 0;
- for (uint32_t i = 0; i < imgLoader::sMemReporter->mKnownLoaders.Length();
- i++) {
- for (auto iter = imgLoader::sMemReporter->mKnownLoaders[i]->mCache.Iter();
- !iter.Done();
- iter.Next()) {
- imgCacheEntry* entry = iter.UserData();
- if (entry->HasNoProxies()) {
- continue;
- }
- RefPtr<imgRequest> req = entry->GetRequest();
- RefPtr<Image> image = req->GetImage();
- if (!image) {
- continue;
- }
- // Both this and EntryImageSizes measure images/content/raster/used/decoded
- // memory. This function's measurement is secondary -- the result doesn't
- // go in the "explicit" tree -- so we use moz_malloc_size_of instead of
- // ImagesMallocSizeOf to prevent DMD from seeing it reported twice.
- ImageMemoryCounter counter(image, moz_malloc_size_of, /* aIsUsed = */ true);
- n += counter.Values().DecodedHeap();
- n += counter.Values().DecodedNonHeap();
- }
- }
- return n;
- }
- void RegisterLoader(imgLoader* aLoader)
- {
- mKnownLoaders.AppendElement(aLoader);
- }
- void UnregisterLoader(imgLoader* aLoader)
- {
- mKnownLoaders.RemoveElement(aLoader);
- }
- private:
- nsTArray<imgLoader*> mKnownLoaders;
- struct MemoryTotal
- {
- MemoryTotal& operator+=(const ImageMemoryCounter& aImageCounter)
- {
- if (aImageCounter.Type() == imgIContainer::TYPE_RASTER) {
- if (aImageCounter.IsUsed()) {
- mUsedRasterCounter += aImageCounter.Values();
- } else {
- mUnusedRasterCounter += aImageCounter.Values();
- }
- } else if (aImageCounter.Type() == imgIContainer::TYPE_VECTOR) {
- if (aImageCounter.IsUsed()) {
- mUsedVectorCounter += aImageCounter.Values();
- } else {
- mUnusedVectorCounter += aImageCounter.Values();
- }
- } else {
- MOZ_CRASH("Unexpected image type");
- }
- return *this;
- }
- const MemoryCounter& UsedRaster() const { return mUsedRasterCounter; }
- const MemoryCounter& UnusedRaster() const { return mUnusedRasterCounter; }
- const MemoryCounter& UsedVector() const { return mUsedVectorCounter; }
- const MemoryCounter& UnusedVector() const { return mUnusedVectorCounter; }
- private:
- MemoryCounter mUsedRasterCounter;
- MemoryCounter mUnusedRasterCounter;
- MemoryCounter mUsedVectorCounter;
- MemoryCounter mUnusedVectorCounter;
- };
- // Reports all images of a single kind, e.g. all used chrome images.
- void ReportCounterArray(nsIHandleReportCallback* aHandleReport,
- nsISupports* aData,
- nsTArray<ImageMemoryCounter>& aCounterArray,
- const char* aPathPrefix,
- bool aAnonymize = false)
- {
- MemoryTotal summaryTotal;
- MemoryTotal nonNotableTotal;
- // Report notable images, and compute total and non-notable aggregate sizes.
- for (uint32_t i = 0; i < aCounterArray.Length(); i++) {
- ImageMemoryCounter& counter = aCounterArray[i];
- if (aAnonymize) {
- counter.URI().Truncate();
- counter.URI().AppendPrintf("<anonymized-%u>", i);
- } else {
- // The URI could be an extremely long data: URI. Truncate if needed.
- static const size_t max = 256;
- if (counter.URI().Length() > max) {
- counter.URI().Truncate(max);
- counter.URI().AppendLiteral(" (truncated)");
- }
- counter.URI().ReplaceChar('/', '\\');
- }
- summaryTotal += counter;
- if (counter.IsNotable()) {
- ReportImage(aHandleReport, aData, aPathPrefix, counter);
- } else {
- nonNotableTotal += counter;
- }
- }
- // Report non-notable images in aggregate.
- ReportTotal(aHandleReport, aData, /* aExplicit = */ true,
- aPathPrefix, "<non-notable images>/", nonNotableTotal);
- // Report a summary in aggregate, outside of the explicit tree.
- ReportTotal(aHandleReport, aData, /* aExplicit = */ false,
- aPathPrefix, "", summaryTotal);
- }
- static void ReportImage(nsIHandleReportCallback* aHandleReport,
- nsISupports* aData,
- const char* aPathPrefix,
- const ImageMemoryCounter& aCounter)
- {
- nsAutoCString pathPrefix(NS_LITERAL_CSTRING("explicit/"));
- pathPrefix.Append(aPathPrefix);
- pathPrefix.Append(aCounter.Type() == imgIContainer::TYPE_RASTER
- ? "/raster/"
- : "/vector/");
- pathPrefix.Append(aCounter.IsUsed() ? "used/" : "unused/");
- pathPrefix.Append("image(");
- pathPrefix.AppendInt(aCounter.IntrinsicSize().width);
- pathPrefix.Append("x");
- pathPrefix.AppendInt(aCounter.IntrinsicSize().height);
- pathPrefix.Append(", ");
- if (aCounter.URI().IsEmpty()) {
- pathPrefix.Append("<unknown URI>");
- } else {
- pathPrefix.Append(aCounter.URI());
- }
- pathPrefix.Append(")/");
- ReportSurfaces(aHandleReport, aData, pathPrefix, aCounter);
- ReportSourceValue(aHandleReport, aData, pathPrefix, aCounter.Values());
- }
- static void ReportSurfaces(nsIHandleReportCallback* aHandleReport,
- nsISupports* aData,
- const nsACString& aPathPrefix,
- const ImageMemoryCounter& aCounter)
- {
- for (const SurfaceMemoryCounter& counter : aCounter.Surfaces()) {
- nsAutoCString surfacePathPrefix(aPathPrefix);
- surfacePathPrefix.Append(counter.IsLocked() ? "locked/" : "unlocked/");
- surfacePathPrefix.Append("surface(");
- surfacePathPrefix.AppendInt(counter.Key().Size().width);
- surfacePathPrefix.Append("x");
- surfacePathPrefix.AppendInt(counter.Key().Size().height);
- if (counter.Type() == SurfaceMemoryCounterType::NORMAL) {
- PlaybackType playback = counter.Key().Playback();
- surfacePathPrefix.Append(playback == PlaybackType::eAnimated
- ? " (animation)"
- : "");
- if (counter.Key().Flags() != DefaultSurfaceFlags()) {
- surfacePathPrefix.Append(", flags:");
- surfacePathPrefix.AppendInt(uint32_t(counter.Key().Flags()),
- /* aRadix = */ 16);
- }
- } else if (counter.Type() == SurfaceMemoryCounterType::COMPOSITING) {
- surfacePathPrefix.Append(", compositing frame");
- } else if (counter.Type() == SurfaceMemoryCounterType::COMPOSITING_PREV) {
- surfacePathPrefix.Append(", compositing prev frame");
- } else {
- MOZ_ASSERT_UNREACHABLE("Unknown counter type");
- }
- surfacePathPrefix.Append(")/");
- ReportValues(aHandleReport, aData, surfacePathPrefix, counter.Values());
- }
- }
- static void ReportTotal(nsIHandleReportCallback* aHandleReport,
- nsISupports* aData,
- bool aExplicit,
- const char* aPathPrefix,
- const char* aPathInfix,
- const MemoryTotal& aTotal)
- {
- nsAutoCString pathPrefix;
- if (aExplicit) {
- pathPrefix.Append("explicit/");
- }
- pathPrefix.Append(aPathPrefix);
- nsAutoCString rasterUsedPrefix(pathPrefix);
- rasterUsedPrefix.Append("/raster/used/");
- rasterUsedPrefix.Append(aPathInfix);
- ReportValues(aHandleReport, aData, rasterUsedPrefix, aTotal.UsedRaster());
- nsAutoCString rasterUnusedPrefix(pathPrefix);
- rasterUnusedPrefix.Append("/raster/unused/");
- rasterUnusedPrefix.Append(aPathInfix);
- ReportValues(aHandleReport, aData, rasterUnusedPrefix,
- aTotal.UnusedRaster());
- nsAutoCString vectorUsedPrefix(pathPrefix);
- vectorUsedPrefix.Append("/vector/used/");
- vectorUsedPrefix.Append(aPathInfix);
- ReportValues(aHandleReport, aData, vectorUsedPrefix, aTotal.UsedVector());
- nsAutoCString vectorUnusedPrefix(pathPrefix);
- vectorUnusedPrefix.Append("/vector/unused/");
- vectorUnusedPrefix.Append(aPathInfix);
- ReportValues(aHandleReport, aData, vectorUnusedPrefix,
- aTotal.UnusedVector());
- }
- static void ReportValues(nsIHandleReportCallback* aHandleReport,
- nsISupports* aData,
- const nsACString& aPathPrefix,
- const MemoryCounter& aCounter)
- {
- ReportSourceValue(aHandleReport, aData, aPathPrefix, aCounter);
- ReportValue(aHandleReport, aData, KIND_HEAP, aPathPrefix,
- "decoded-heap",
- "Decoded image data which is stored on the heap.",
- aCounter.DecodedHeap());
- ReportValue(aHandleReport, aData, KIND_NONHEAP, aPathPrefix,
- "decoded-nonheap",
- "Decoded image data which isn't stored on the heap.",
- aCounter.DecodedNonHeap());
- }
- static void ReportSourceValue(nsIHandleReportCallback* aHandleReport,
- nsISupports* aData,
- const nsACString& aPathPrefix,
- const MemoryCounter& aCounter)
- {
- ReportValue(aHandleReport, aData, KIND_HEAP, aPathPrefix,
- "source",
- "Raster image source data and vector image documents.",
- aCounter.Source());
- }
- static void ReportValue(nsIHandleReportCallback* aHandleReport,
- nsISupports* aData,
- int32_t aKind,
- const nsACString& aPathPrefix,
- const char* aPathSuffix,
- const char* aDescription,
- size_t aValue)
- {
- if (aValue == 0) {
- return;
- }
- nsAutoCString desc(aDescription);
- nsAutoCString path(aPathPrefix);
- path.Append(aPathSuffix);
- aHandleReport->Callback(EmptyCString(), path, aKind, UNITS_BYTES,
- aValue, desc, aData);
- }
- static void RecordCounterForRequest(imgRequest* aRequest,
- nsTArray<ImageMemoryCounter>* aArray,
- bool aIsUsed)
- {
- RefPtr<Image> image = aRequest->GetImage();
- if (!image) {
- return;
- }
- ImageMemoryCounter counter(image, ImagesMallocSizeOf, aIsUsed);
- aArray->AppendElement(Move(counter));
- }
- };
- NS_IMPL_ISUPPORTS(imgMemoryReporter, nsIMemoryReporter)
- NS_IMPL_ISUPPORTS(nsProgressNotificationProxy,
- nsIProgressEventSink,
- nsIChannelEventSink,
- nsIInterfaceRequestor)
- NS_IMETHODIMP
- nsProgressNotificationProxy::OnProgress(nsIRequest* request,
- nsISupports* ctxt,
- int64_t progress,
- int64_t progressMax)
- {
- nsCOMPtr<nsILoadGroup> loadGroup;
- request->GetLoadGroup(getter_AddRefs(loadGroup));
- nsCOMPtr<nsIProgressEventSink> target;
- NS_QueryNotificationCallbacks(mOriginalCallbacks,
- loadGroup,
- NS_GET_IID(nsIProgressEventSink),
- getter_AddRefs(target));
- if (!target) {
- return NS_OK;
- }
- return target->OnProgress(mImageRequest, ctxt, progress, progressMax);
- }
- NS_IMETHODIMP
- nsProgressNotificationProxy::OnStatus(nsIRequest* request,
- nsISupports* ctxt,
- nsresult status,
- const char16_t* statusArg)
- {
- nsCOMPtr<nsILoadGroup> loadGroup;
- request->GetLoadGroup(getter_AddRefs(loadGroup));
- nsCOMPtr<nsIProgressEventSink> target;
- NS_QueryNotificationCallbacks(mOriginalCallbacks,
- loadGroup,
- NS_GET_IID(nsIProgressEventSink),
- getter_AddRefs(target));
- if (!target) {
- return NS_OK;
- }
- return target->OnStatus(mImageRequest, ctxt, status, statusArg);
- }
- NS_IMETHODIMP
- nsProgressNotificationProxy::
- AsyncOnChannelRedirect(nsIChannel* oldChannel,
- nsIChannel* newChannel,
- uint32_t flags,
- nsIAsyncVerifyRedirectCallback* cb)
- {
- // Tell the original original callbacks about it too
- nsCOMPtr<nsILoadGroup> loadGroup;
- newChannel->GetLoadGroup(getter_AddRefs(loadGroup));
- nsCOMPtr<nsIChannelEventSink> target;
- NS_QueryNotificationCallbacks(mOriginalCallbacks,
- loadGroup,
- NS_GET_IID(nsIChannelEventSink),
- getter_AddRefs(target));
- if (!target) {
- cb->OnRedirectVerifyCallback(NS_OK);
- return NS_OK;
- }
- // Delegate to |target| if set, reusing |cb|
- return target->AsyncOnChannelRedirect(oldChannel, newChannel, flags, cb);
- }
- NS_IMETHODIMP
- nsProgressNotificationProxy::GetInterface(const nsIID& iid,
- void** result)
- {
- if (iid.Equals(NS_GET_IID(nsIProgressEventSink))) {
- *result = static_cast<nsIProgressEventSink*>(this);
- NS_ADDREF_THIS();
- return NS_OK;
- }
- if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) {
- *result = static_cast<nsIChannelEventSink*>(this);
- NS_ADDREF_THIS();
- return NS_OK;
- }
- if (mOriginalCallbacks) {
- return mOriginalCallbacks->GetInterface(iid, result);
- }
- return NS_NOINTERFACE;
- }
- static void
- NewRequestAndEntry(bool aForcePrincipalCheckForCacheEntry, imgLoader* aLoader,
- const ImageCacheKey& aKey,
- imgRequest** aRequest, imgCacheEntry** aEntry)
- {
- RefPtr<imgRequest> request = new imgRequest(aLoader, aKey);
- RefPtr<imgCacheEntry> entry =
- new imgCacheEntry(aLoader, request, aForcePrincipalCheckForCacheEntry);
- aLoader->AddToUncachedImages(request);
- request.forget(aRequest);
- entry.forget(aEntry);
- }
- static bool
- ShouldRevalidateEntry(imgCacheEntry* aEntry,
- nsLoadFlags aFlags,
- bool aHasExpired)
- {
- bool bValidateEntry = false;
- if (aFlags & nsIRequest::LOAD_BYPASS_CACHE) {
- return false;
- }
- if (aFlags & nsIRequest::VALIDATE_ALWAYS) {
- bValidateEntry = true;
- } else if (aEntry->GetMustValidate()) {
- bValidateEntry = true;
- } else if (aHasExpired) {
- // The cache entry has expired... Determine whether the stale cache
- // entry can be used without validation...
- if (aFlags & (nsIRequest::VALIDATE_NEVER |
- nsIRequest::VALIDATE_ONCE_PER_SESSION)) {
- // VALIDATE_NEVER and VALIDATE_ONCE_PER_SESSION allow stale cache
- // entries to be used unless they have been explicitly marked to
- // indicate that revalidation is necessary.
- bValidateEntry = false;
- } else if (!(aFlags & nsIRequest::LOAD_FROM_CACHE)) {
- // LOAD_FROM_CACHE allows a stale cache entry to be used... Otherwise,
- // the entry must be revalidated.
- bValidateEntry = true;
- }
- }
- return bValidateEntry;
- }
- /* Call content policies on cached images that went through a redirect */
- static bool
- ShouldLoadCachedImage(imgRequest* aImgRequest,
- nsISupports* aLoadingContext,
- nsIPrincipal* aLoadingPrincipal,
- nsContentPolicyType aPolicyType)
- {
- /* Call content policies on cached images - Bug 1082837
- * Cached images are keyed off of the first uri in a redirect chain.
- * Hence content policies don't get a chance to test the intermediate hops
- * or the final desitnation. Here we test the final destination using
- * mCurrentURI off of the imgRequest and passing it into content policies.
- * For Mixed Content Blocker, we do an additional check to determine if any
- * of the intermediary hops went through an insecure redirect with the
- * mHadInsecureRedirect flag
- */
- bool insecureRedirect = aImgRequest->HadInsecureRedirect();
- nsCOMPtr<nsIURI> contentLocation;
- aImgRequest->GetCurrentURI(getter_AddRefs(contentLocation));
- nsresult rv;
- int16_t decision = nsIContentPolicy::REJECT_REQUEST;
- rv = NS_CheckContentLoadPolicy(aPolicyType,
- contentLocation,
- aLoadingPrincipal,
- aLoadingContext,
- EmptyCString(), //mime guess
- nullptr, //aExtra
- &decision,
- nsContentUtils::GetContentPolicy(),
- nsContentUtils::GetSecurityManager());
- if (NS_FAILED(rv) || !NS_CP_ACCEPTED(decision)) {
- return false;
- }
- // We call all Content Policies above, but we also have to call mcb
- // individually to check the intermediary redirect hops are secure.
- if (insecureRedirect) {
- if (!nsContentUtils::IsSystemPrincipal(aLoadingPrincipal)) {
- // Set the requestingLocation from the aLoadingPrincipal.
- nsCOMPtr<nsIURI> requestingLocation;
- if (aLoadingPrincipal) {
- rv = aLoadingPrincipal->GetURI(getter_AddRefs(requestingLocation));
- NS_ENSURE_SUCCESS(rv, false);
- }
- // reset the decision for mixed content blocker check
- decision = nsIContentPolicy::REJECT_REQUEST;
- rv = nsMixedContentBlocker::ShouldLoad(insecureRedirect,
- aPolicyType,
- contentLocation,
- requestingLocation,
- aLoadingContext,
- EmptyCString(), //mime guess
- nullptr,
- aLoadingPrincipal,
- &decision);
- if (NS_FAILED(rv) || !NS_CP_ACCEPTED(decision)) {
- return false;
- }
- }
- }
- return true;
- }
- // Returns true if this request is compatible with the given CORS mode on the
- // given loading principal, and false if the request may not be reused due
- // to CORS. Also checks the Referrer Policy, since requests with different
- // referrers/policies may generate different responses.
- static bool
- ValidateSecurityInfo(imgRequest* request, bool forcePrincipalCheck,
- int32_t corsmode, nsIPrincipal* loadingPrincipal,
- nsISupports* aCX, nsContentPolicyType aPolicyType,
- ReferrerPolicy referrerPolicy)
- {
- // If the entry's Referrer Policy doesn't match, we can't use this request.
- // XXX: this will return false if an image has different referrer attributes,
- // i.e. we currently don't use the cached image but reload the image with
- // the new referrer policy bug 1174921
- if (referrerPolicy != request->GetReferrerPolicy()) {
- return false;
- }
- // If the entry's CORS mode doesn't match, or the CORS mode matches but the
- // document principal isn't the same, we can't use this request.
- if (request->GetCORSMode() != corsmode) {
- return false;
- } else if (request->GetCORSMode() != imgIRequest::CORS_NONE ||
- forcePrincipalCheck) {
- nsCOMPtr<nsIPrincipal> otherprincipal = request->GetLoadingPrincipal();
- // If we previously had a principal, but we don't now, we can't use this
- // request.
- if (otherprincipal && !loadingPrincipal) {
- return false;
- }
- if (otherprincipal && loadingPrincipal) {
- bool equals = false;
- otherprincipal->Equals(loadingPrincipal, &equals);
- if (!equals) {
- return false;
- }
- }
- }
- // Content Policy Check on Cached Images
- return ShouldLoadCachedImage(request, aCX, loadingPrincipal, aPolicyType);
- }
- static nsresult
- NewImageChannel(nsIChannel** aResult,
- // If aForcePrincipalCheckForCacheEntry is true, then we will
- // force a principal check even when not using CORS before
- // assuming we have a cache hit on a cache entry that we
- // create for this channel. This is an out param that should
- // be set to true if this channel ends up depending on
- // aLoadingPrincipal and false otherwise.
- bool* aForcePrincipalCheckForCacheEntry,
- nsIURI* aURI,
- nsIURI* aInitialDocumentURI,
- int32_t aCORSMode,
- nsIURI* aReferringURI,
- ReferrerPolicy aReferrerPolicy,
- nsILoadGroup* aLoadGroup,
- const nsCString& aAcceptHeader,
- nsLoadFlags aLoadFlags,
- nsContentPolicyType aPolicyType,
- nsIPrincipal* aLoadingPrincipal,
- nsISupports* aRequestingContext,
- bool aRespectPrivacy)
- {
- MOZ_ASSERT(aResult);
- nsresult rv;
- nsCOMPtr<nsIHttpChannel> newHttpChannel;
- nsCOMPtr<nsIInterfaceRequestor> callbacks;
- if (aLoadGroup) {
- // Get the notification callbacks from the load group for the new channel.
- //
- // XXX: This is not exactly correct, because the network request could be
- // referenced by multiple windows... However, the new channel needs
- // something. So, using the 'first' notification callbacks is better
- // than nothing...
- //
- aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
- }
- // Pass in a nullptr loadgroup because this is the underlying network
- // request. This request may be referenced by several proxy image requests
- // (possibly in different documents).
- // If all of the proxy requests are canceled then this request should be
- // canceled too.
- //
- aLoadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
- nsCOMPtr<nsINode> requestingNode = do_QueryInterface(aRequestingContext);
- nsSecurityFlags securityFlags =
- aCORSMode == imgIRequest::CORS_NONE
- ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS
- : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
- if (aCORSMode == imgIRequest::CORS_ANONYMOUS) {
- securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
- } else if (aCORSMode == imgIRequest::CORS_USE_CREDENTIALS) {
- securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
- }
- securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
- // Note we are calling NS_NewChannelWithTriggeringPrincipal() here with a
- // node and a principal. This is for things like background images that are
- // specified by user stylesheets, where the document is being styled, but
- // the principal is that of the user stylesheet.
- if (requestingNode && aLoadingPrincipal) {
- rv = NS_NewChannelWithTriggeringPrincipal(aResult,
- aURI,
- requestingNode,
- aLoadingPrincipal,
- securityFlags,
- aPolicyType,
- nullptr, // loadGroup
- callbacks,
- aLoadFlags);
- if (NS_FAILED(rv)) {
- return rv;
- }
- if (aPolicyType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON) {
- // If this is a favicon loading, we will use the originAttributes from the
- // loadingPrincipal as the channel's originAttributes. This allows the favicon
- // loading from XUL will use the correct originAttributes.
- NeckoOriginAttributes neckoAttrs;
- neckoAttrs.InheritFromDocToNecko(BasePrincipal::Cast(aLoadingPrincipal)->OriginAttributesRef());
- nsCOMPtr<nsILoadInfo> loadInfo = (*aResult)->GetLoadInfo();
- rv = loadInfo->SetOriginAttributes(neckoAttrs);
- }
- } else {
- // either we are loading something inside a document, in which case
- // we should always have a requestingNode, or we are loading something
- // outside a document, in which case the loadingPrincipal and
- // triggeringPrincipal should always be the systemPrincipal.
- // However, there are exceptions: one is Notifications which create a
- // channel in the parent prcoess in which case we can't get a requestingNode.
- rv = NS_NewChannel(aResult,
- aURI,
- nsContentUtils::GetSystemPrincipal(),
- securityFlags,
- aPolicyType,
- nullptr, // loadGroup
- callbacks,
- aLoadFlags);
- if (NS_FAILED(rv)) {
- return rv;
- }
- // Use the OriginAttributes from the loading principal, if one is available,
- // and adjust the private browsing ID based on what kind of load the caller
- // has asked us to perform.
- NeckoOriginAttributes neckoAttrs;
- if (aLoadingPrincipal) {
- neckoAttrs.InheritFromDocToNecko(BasePrincipal::Cast(aLoadingPrincipal)->OriginAttributesRef());
- }
- neckoAttrs.mPrivateBrowsingId = aRespectPrivacy ? 1 : 0;
- nsCOMPtr<nsILoadInfo> loadInfo = (*aResult)->GetLoadInfo();
- rv = loadInfo->SetOriginAttributes(neckoAttrs);
- }
- if (NS_FAILED(rv)) {
- return rv;
- }
- // only inherit if we have a principal
- *aForcePrincipalCheckForCacheEntry =
- aLoadingPrincipal &&
- nsContentUtils::ChannelShouldInheritPrincipal(
- aLoadingPrincipal,
- aURI,
- /* aInheritForAboutBlank */ false,
- /* aForceInherit */ false);
- // Initialize HTTP-specific attributes
- newHttpChannel = do_QueryInterface(*aResult);
- if (newHttpChannel) {
- newHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
- aAcceptHeader,
- false);
- nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
- do_QueryInterface(newHttpChannel);
- NS_ENSURE_TRUE(httpChannelInternal, NS_ERROR_UNEXPECTED);
- httpChannelInternal->SetDocumentURI(aInitialDocumentURI);
- newHttpChannel->SetReferrerWithPolicy(aReferringURI, aReferrerPolicy);
- }
- // Image channels are loaded by default with reduced priority.
- nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(*aResult);
- if (p) {
- uint32_t priority = nsISupportsPriority::PRIORITY_LOW;
- if (aLoadFlags & nsIRequest::LOAD_BACKGROUND) {
- ++priority; // further reduce priority for background loads
- }
- p->AdjustPriority(priority);
- }
- // Create a new loadgroup for this new channel, using the old group as
- // the parent. The indirection keeps the channel insulated from cancels,
- // but does allow a way for this revalidation to be associated with at
- // least one base load group for scheduling/caching purposes.
- nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
- nsCOMPtr<nsILoadGroupChild> childLoadGroup = do_QueryInterface(loadGroup);
- if (childLoadGroup) {
- childLoadGroup->SetParentLoadGroup(aLoadGroup);
- }
- (*aResult)->SetLoadGroup(loadGroup);
- return NS_OK;
- }
- static uint32_t
- SecondsFromPRTime(PRTime prTime)
- {
- return uint32_t(int64_t(prTime) / int64_t(PR_USEC_PER_SEC));
- }
- imgCacheEntry::imgCacheEntry(imgLoader* loader, imgRequest* request,
- bool forcePrincipalCheck)
- : mLoader(loader),
- mRequest(request),
- mDataSize(0),
- mTouchedTime(SecondsFromPRTime(PR_Now())),
- mLoadTime(SecondsFromPRTime(PR_Now())),
- mExpiryTime(0),
- mMustValidate(false),
- // We start off as evicted so we don't try to update the cache. PutIntoCache
- // will set this to false.
- mEvicted(true),
- mHasNoProxies(true),
- mForcePrincipalCheck(forcePrincipalCheck)
- { }
- imgCacheEntry::~imgCacheEntry()
- {
- LOG_FUNC(gImgLog, "imgCacheEntry::~imgCacheEntry()");
- }
- void
- imgCacheEntry::Touch(bool updateTime /* = true */)
- {
- LOG_SCOPE(gImgLog, "imgCacheEntry::Touch");
- if (updateTime) {
- mTouchedTime = SecondsFromPRTime(PR_Now());
- }
- UpdateCache();
- }
- void
- imgCacheEntry::UpdateCache(int32_t diff /* = 0 */)
- {
- // Don't update the cache if we've been removed from it or it doesn't care
- // about our size or usage.
- if (!Evicted() && HasNoProxies()) {
- mLoader->CacheEntriesChanged(mRequest->IsChrome(), diff);
- }
- }
- void imgCacheEntry::UpdateLoadTime()
- {
- mLoadTime = SecondsFromPRTime(PR_Now());
- }
- void
- imgCacheEntry::SetHasNoProxies(bool hasNoProxies)
- {
- if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
- if (hasNoProxies) {
- LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies true",
- "uri", mRequest->CacheKey().Spec());
- } else {
- LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies false",
- "uri", mRequest->CacheKey().Spec());
- }
- }
- mHasNoProxies = hasNoProxies;
- }
- imgCacheQueue::imgCacheQueue()
- : mDirty(false),
- mSize(0)
- { }
- void
- imgCacheQueue::UpdateSize(int32_t diff)
- {
- mSize += diff;
- }
- uint32_t
- imgCacheQueue::GetSize() const
- {
- return mSize;
- }
- #include <algorithm>
- using namespace std;
- void
- imgCacheQueue::Remove(imgCacheEntry* entry)
- {
- queueContainer::iterator it = find(mQueue.begin(), mQueue.end(), entry);
- if (it != mQueue.end()) {
- mSize -= (*it)->GetDataSize();
- mQueue.erase(it);
- MarkDirty();
- }
- }
- void
- imgCacheQueue::Push(imgCacheEntry* entry)
- {
- mSize += entry->GetDataSize();
- RefPtr<imgCacheEntry> refptr(entry);
- mQueue.push_back(refptr);
- MarkDirty();
- }
- already_AddRefed<imgCacheEntry>
- imgCacheQueue::Pop()
- {
- if (mQueue.empty()) {
- return nullptr;
- }
- if (IsDirty()) {
- Refresh();
- }
- RefPtr<imgCacheEntry> entry = mQueue[0];
- std::pop_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
- mQueue.pop_back();
- mSize -= entry->GetDataSize();
- return entry.forget();
- }
- void
- imgCacheQueue::Refresh()
- {
- std::make_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
- mDirty = false;
- }
- void
- imgCacheQueue::MarkDirty()
- {
- mDirty = true;
- }
- bool
- imgCacheQueue::IsDirty()
- {
- return mDirty;
- }
- uint32_t
- imgCacheQueue::GetNumElements() const
- {
- return mQueue.size();
- }
- imgCacheQueue::iterator
- imgCacheQueue::begin()
- {
- return mQueue.begin();
- }
- imgCacheQueue::const_iterator
- imgCacheQueue::begin() const
- {
- return mQueue.begin();
- }
- imgCacheQueue::iterator
- imgCacheQueue::end()
- {
- return mQueue.end();
- }
- imgCacheQueue::const_iterator
- imgCacheQueue::end() const
- {
- return mQueue.end();
- }
- nsresult
- imgLoader::CreateNewProxyForRequest(imgRequest* aRequest,
- nsILoadGroup* aLoadGroup,
- imgINotificationObserver* aObserver,
- nsLoadFlags aLoadFlags,
- imgRequestProxy** _retval)
- {
- LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::CreateNewProxyForRequest",
- "imgRequest", aRequest);
- /* XXX If we move decoding onto separate threads, we should save off the
- calling thread here and pass it off to |proxyRequest| so that it call
- proxy calls to |aObserver|.
- */
- RefPtr<imgRequestProxy> proxyRequest = new imgRequestProxy();
- /* It is important to call |SetLoadFlags()| before calling |Init()| because
- |Init()| adds the request to the loadgroup.
- */
- proxyRequest->SetLoadFlags(aLoadFlags);
- RefPtr<ImageURL> uri;
- aRequest->GetURI(getter_AddRefs(uri));
- // init adds itself to imgRequest's list of observers
- nsresult rv = proxyRequest->Init(aRequest, aLoadGroup, uri, aObserver);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- proxyRequest.forget(_retval);
- return NS_OK;
- }
- class imgCacheExpirationTracker final
- : public nsExpirationTracker<imgCacheEntry, 3>
- {
- enum { TIMEOUT_SECONDS = 10 };
- public:
- imgCacheExpirationTracker();
- protected:
- void NotifyExpired(imgCacheEntry* entry);
- };
- imgCacheExpirationTracker::imgCacheExpirationTracker()
- : nsExpirationTracker<imgCacheEntry, 3>(TIMEOUT_SECONDS * 1000,
- "imgCacheExpirationTracker")
- { }
- void
- imgCacheExpirationTracker::NotifyExpired(imgCacheEntry* entry)
- {
- // Hold on to a reference to this entry, because the expiration tracker
- // mechanism doesn't.
- RefPtr<imgCacheEntry> kungFuDeathGrip(entry);
- if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
- RefPtr<imgRequest> req = entry->GetRequest();
- if (req) {
- LOG_FUNC_WITH_PARAM(gImgLog,
- "imgCacheExpirationTracker::NotifyExpired",
- "entry", req->CacheKey().Spec());
- }
- }
- // We can be called multiple times on the same entry. Don't do work multiple
- // times.
- if (!entry->Evicted()) {
- entry->Loader()->RemoveFromCache(entry);
- }
- entry->Loader()->VerifyCacheSizes();
- }
- ///////////////////////////////////////////////////////////////////////////////
- // imgLoader
- ///////////////////////////////////////////////////////////////////////////////
- double imgLoader::sCacheTimeWeight;
- uint32_t imgLoader::sCacheMaxSize;
- imgMemoryReporter* imgLoader::sMemReporter;
- NS_IMPL_ISUPPORTS(imgLoader, imgILoader, nsIContentSniffer, imgICache,
- nsISupportsWeakReference, nsIObserver)
- static imgLoader* gNormalLoader = nullptr;
- static imgLoader* gPrivateBrowsingLoader = nullptr;
- /* static */ already_AddRefed<imgLoader>
- imgLoader::CreateImageLoader()
- {
- // In some cases, such as xpctests, XPCOM modules are not automatically
- // initialized. We need to make sure that our module is initialized before
- // we hand out imgLoader instances and code starts using them.
- mozilla::image::EnsureModuleInitialized();
- RefPtr<imgLoader> loader = new imgLoader();
- loader->Init();
- return loader.forget();
- }
- imgLoader*
- imgLoader::NormalLoader()
- {
- if (!gNormalLoader) {
- gNormalLoader = CreateImageLoader().take();
- }
- return gNormalLoader;
- }
- imgLoader*
- imgLoader::PrivateBrowsingLoader()
- {
- if (!gPrivateBrowsingLoader) {
- gPrivateBrowsingLoader = CreateImageLoader().take();
- gPrivateBrowsingLoader->RespectPrivacyNotifications();
- }
- return gPrivateBrowsingLoader;
- }
- imgLoader::imgLoader()
- : mUncachedImagesMutex("imgLoader::UncachedImages"), mRespectPrivacy(false)
- {
- sMemReporter->AddRef();
- sMemReporter->RegisterLoader(this);
- }
- imgLoader::~imgLoader()
- {
- ClearChromeImageCache();
- ClearImageCache();
- {
- // If there are any of our imgRequest's left they are in the uncached
- // images set, so clear their pointer to us.
- MutexAutoLock lock(mUncachedImagesMutex);
- for (auto iter = mUncachedImages.Iter(); !iter.Done(); iter.Next()) {
- nsPtrHashKey<imgRequest>* entry = iter.Get();
- RefPtr<imgRequest> req = entry->GetKey();
- req->ClearLoader();
- }
- }
- sMemReporter->UnregisterLoader(this);
- sMemReporter->Release();
- }
- void
- imgLoader::VerifyCacheSizes()
- {
- #ifdef DEBUG
- if (!mCacheTracker) {
- return;
- }
- uint32_t cachesize = mCache.Count() + mChromeCache.Count();
- uint32_t queuesize =
- mCacheQueue.GetNumElements() + mChromeCacheQueue.GetNumElements();
- uint32_t trackersize = 0;
- for (nsExpirationTracker<imgCacheEntry, 3>::Iterator it(mCacheTracker.get());
- it.Next(); ){
- trackersize++;
- }
- MOZ_ASSERT(queuesize == trackersize, "Queue and tracker sizes out of sync!");
- MOZ_ASSERT(queuesize <= cachesize, "Queue has more elements than cache!");
- #endif
- }
- imgLoader::imgCacheTable&
- imgLoader::GetCache(bool aForChrome)
- {
- return aForChrome ? mChromeCache : mCache;
- }
- imgLoader::imgCacheTable&
- imgLoader::GetCache(const ImageCacheKey& aKey)
- {
- return GetCache(aKey.IsChrome());
- }
- imgCacheQueue&
- imgLoader::GetCacheQueue(bool aForChrome)
- {
- return aForChrome ? mChromeCacheQueue : mCacheQueue;
- }
- imgCacheQueue&
- imgLoader::GetCacheQueue(const ImageCacheKey& aKey)
- {
- return GetCacheQueue(aKey.IsChrome());
- }
- void imgLoader::GlobalInit()
- {
- sCacheTimeWeight = gfxPrefs::ImageCacheTimeWeight() / 1000.0;
- int32_t cachesize = gfxPrefs::ImageCacheSize();
- sCacheMaxSize = cachesize > 0 ? cachesize : 0;
- sMemReporter = new imgMemoryReporter();
- RegisterStrongMemoryReporter(sMemReporter);
- RegisterImagesContentUsedUncompressedDistinguishedAmount(
- imgMemoryReporter::ImagesContentUsedUncompressedDistinguishedAmount);
- }
- void imgLoader::ShutdownMemoryReporter()
- {
- UnregisterImagesContentUsedUncompressedDistinguishedAmount();
- UnregisterStrongMemoryReporter(sMemReporter);
- }
- nsresult
- imgLoader::InitCache()
- {
- nsCOMPtr<nsIObserverService> 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 = MakeUnique<imgCacheExpirationTracker>();
- return NS_OK;
- }
- nsresult
- imgLoader::Init()
- {
- InitCache();
- ReadAcceptHeaderPref();
- Preferences::AddWeakObserver(this, "image.http.accept");
- return NS_OK;
- }
- NS_IMETHODIMP
- imgLoader::RespectPrivacyNotifications()
- {
- mRespectPrivacy = true;
- return NS_OK;
- }
- NS_IMETHODIMP
- imgLoader::Observe(nsISupports* aSubject, const char* aTopic,
- const char16_t* aData)
- {
- // We listen for pref change notifications...
- if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
- if (!NS_strcmp(aData, u"image.http.accept")) {
- ReadAcceptHeaderPref();
- }
- } 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) {
- mCacheTracker = nullptr;
- } else if (strcmp(aTopic, "xpcom-shutdown") == 0) {
- mCacheTracker = nullptr;
- ShutdownMemoryReporter();
- } else {
- // (Nothing else should bring us here)
- MOZ_ASSERT(0, "Invalid topic received");
- }
- return NS_OK;
- }
- void imgLoader::ReadAcceptHeaderPref()
- {
- nsAdoptingCString accept = Preferences::GetCString("image.http.accept");
- if (accept) {
- mAcceptHeader = accept;
- } else {
- mAcceptHeader =
- IMAGE_PNG "," IMAGE_WILDCARD ";q=0.8," ANY_WILDCARD ";q=0.5";
- }
- }
- NS_IMETHODIMP
- imgLoader::ClearCache(bool chrome)
- {
- if (XRE_IsParentProcess()) {
- bool privateLoader = this == gPrivateBrowsingLoader;
- for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
- Unused << cp->SendClearImageCache(privateLoader, chrome);
- }
- }
- if (chrome) {
- return ClearChromeImageCache();
- } else {
- return ClearImageCache();
- }
- }
- NS_IMETHODIMP
- imgLoader::FindEntryProperties(nsIURI* uri,
- nsIDOMDocument* aDOMDoc,
- nsIProperties** _retval)
- {
- *_retval = nullptr;
- nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDOMDoc);
- PrincipalOriginAttributes attrs;
- if (doc) {
- nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
- if (principal) {
- attrs = BasePrincipal::Cast(principal)->OriginAttributesRef();
- }
- }
- nsresult rv;
- ImageCacheKey key(uri, attrs, doc, rv);
- NS_ENSURE_SUCCESS(rv, rv);
- imgCacheTable& cache = GetCache(key);
- RefPtr<imgCacheEntry> entry;
- if (cache.Get(key, getter_AddRefs(entry)) && entry) {
- if (mCacheTracker && entry->HasNoProxies()) {
- mCacheTracker->MarkUsed(entry);
- }
- RefPtr<imgRequest> request = entry->GetRequest();
- if (request) {
- nsCOMPtr<nsIProperties> properties = request->Properties();
- properties.forget(_retval);
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP_(void)
- imgLoader::ClearCacheForControlledDocument(nsIDocument* aDoc)
- {
- MOZ_ASSERT(aDoc);
- AutoTArray<RefPtr<imgCacheEntry>, 128> entriesToBeRemoved;
- imgCacheTable& cache = GetCache(false);
- for (auto iter = cache.Iter(); !iter.Done(); iter.Next()) {
- auto& key = iter.Key();
- if (key.ControlledDocument() == aDoc) {
- entriesToBeRemoved.AppendElement(iter.Data());
- }
- }
- for (auto& entry : entriesToBeRemoved) {
- if (!RemoveFromCache(entry)) {
- NS_WARNING("Couldn't remove an entry from the cache in ClearCacheForControlledDocument()\n");
- }
- }
- }
- void
- imgLoader::Shutdown()
- {
- NS_IF_RELEASE(gNormalLoader);
- gNormalLoader = nullptr;
- NS_IF_RELEASE(gPrivateBrowsingLoader);
- gPrivateBrowsingLoader = nullptr;
- }
- nsresult
- imgLoader::ClearChromeImageCache()
- {
- return EvictEntries(mChromeCache);
- }
- nsresult
- imgLoader::ClearImageCache()
- {
- return EvictEntries(mCache);
- }
- void
- imgLoader::MinimizeCaches()
- {
- EvictEntries(mCacheQueue);
- EvictEntries(mChromeCacheQueue);
- }
- bool
- imgLoader::PutIntoCache(const ImageCacheKey& aKey, imgCacheEntry* entry)
- {
- imgCacheTable& cache = GetCache(aKey);
- LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
- "imgLoader::PutIntoCache", "uri", aKey.Spec());
- // Check to see if this request already exists in the cache. If so, we'll
- // replace the old version.
- RefPtr<imgCacheEntry> tmpCacheEntry;
- if (cache.Get(aKey, getter_AddRefs(tmpCacheEntry)) && tmpCacheEntry) {
- MOZ_LOG(gImgLog, LogLevel::Debug,
- ("[this=%p] imgLoader::PutIntoCache -- Element already in the cache",
- nullptr));
- RefPtr<imgRequest> tmpRequest = tmpCacheEntry->GetRequest();
- // If it already exists, and we're putting the same key into the cache, we
- // should remove the old version.
- MOZ_LOG(gImgLog, LogLevel::Debug,
- ("[this=%p] imgLoader::PutIntoCache -- Replacing cached element",
- nullptr));
- RemoveFromCache(aKey);
- } else {
- MOZ_LOG(gImgLog, LogLevel::Debug,
- ("[this=%p] imgLoader::PutIntoCache --"
- " Element NOT already in the cache", nullptr));
- }
- cache.Put(aKey, entry);
- // We can be called to resurrect an evicted entry.
- if (entry->Evicted()) {
- entry->SetEvicted(false);
- }
- // If we're resurrecting an entry with no proxies, put it back in the
- // tracker and queue.
- if (entry->HasNoProxies()) {
- nsresult addrv = NS_OK;
- if (mCacheTracker) {
- addrv = mCacheTracker->AddObject(entry);
- }
- if (NS_SUCCEEDED(addrv)) {
- imgCacheQueue& queue = GetCacheQueue(aKey);
- queue.Push(entry);
- }
- }
- RefPtr<imgRequest> request = entry->GetRequest();
- request->SetIsInCache(true);
- RemoveFromUncachedImages(request);
- return true;
- }
- bool
- imgLoader::SetHasNoProxies(imgRequest* aRequest, imgCacheEntry* aEntry)
- {
- LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
- "imgLoader::SetHasNoProxies", "uri",
- aRequest->CacheKey().Spec());
- aEntry->SetHasNoProxies(true);
- if (aEntry->Evicted()) {
- return false;
- }
- imgCacheQueue& queue = GetCacheQueue(aRequest->IsChrome());
- nsresult addrv = NS_OK;
- if (mCacheTracker) {
- addrv = mCacheTracker->AddObject(aEntry);
- }
- if (NS_SUCCEEDED(addrv)) {
- queue.Push(aEntry);
- }
- imgCacheTable& cache = GetCache(aRequest->IsChrome());
- CheckCacheLimits(cache, queue);
- return true;
- }
- bool
- imgLoader::SetHasProxies(imgRequest* aRequest)
- {
- VerifyCacheSizes();
- const ImageCacheKey& key = aRequest->CacheKey();
- imgCacheTable& cache = GetCache(key);
- LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
- "imgLoader::SetHasProxies", "uri", key.Spec());
- RefPtr<imgCacheEntry> entry;
- if (cache.Get(key, getter_AddRefs(entry)) && entry) {
- // Make sure the cache entry is for the right request
- RefPtr<imgRequest> entryRequest = entry->GetRequest();
- if (entryRequest == aRequest && entry->HasNoProxies()) {
- imgCacheQueue& queue = GetCacheQueue(key);
- queue.Remove(entry);
- if (mCacheTracker) {
- mCacheTracker->RemoveObject(entry);
- }
- entry->SetHasNoProxies(false);
- return true;
- }
- }
- return false;
- }
- void
- imgLoader::CacheEntriesChanged(bool aForChrome, int32_t aSizeDiff /* = 0 */)
- {
- imgCacheQueue& queue = GetCacheQueue(aForChrome);
- queue.MarkDirty();
- queue.UpdateSize(aSizeDiff);
- }
- void
- imgLoader::CheckCacheLimits(imgCacheTable& cache, imgCacheQueue& queue)
- {
- if (queue.GetNumElements() == 0) {
- NS_ASSERTION(queue.GetSize() == 0,
- "imgLoader::CheckCacheLimits -- incorrect cache size");
- }
- // Remove entries from the cache until we're back at our desired max size.
- while (queue.GetSize() > sCacheMaxSize) {
- // Remove the first entry in the queue.
- RefPtr<imgCacheEntry> entry(queue.Pop());
- NS_ASSERTION(entry, "imgLoader::CheckCacheLimits -- NULL entry pointer");
- if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
- RefPtr<imgRequest> req = entry->GetRequest();
- if (req) {
- LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
- "imgLoader::CheckCacheLimits",
- "entry", req->CacheKey().Spec());
- }
- }
- if (entry) {
- RemoveFromCache(entry);
- }
- }
- }
- bool
- imgLoader::ValidateRequestWithNewChannel(imgRequest* request,
- nsIURI* aURI,
- nsIURI* aInitialDocumentURI,
- nsIURI* aReferrerURI,
- ReferrerPolicy aReferrerPolicy,
- nsILoadGroup* aLoadGroup,
- imgINotificationObserver* aObserver,
- nsISupports* aCX,
- nsLoadFlags aLoadFlags,
- nsContentPolicyType aLoadPolicyType,
- imgRequestProxy** aProxyRequest,
- nsIPrincipal* aLoadingPrincipal,
- int32_t aCORSMode)
- {
- // now we need to insert a new channel request object inbetween the real
- // request and the proxy that basically delays loading the image until it
- // gets a 304 or figures out that this needs to be a new request
- nsresult rv;
- // If we're currently in the middle of validating this request, just hand
- // back a proxy to it; the required work will be done for us.
- if (request->GetValidator()) {
- rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
- aLoadFlags, aProxyRequest);
- if (NS_FAILED(rv)) {
- return false;
- }
- if (*aProxyRequest) {
- imgRequestProxy* proxy = static_cast<imgRequestProxy*>(*aProxyRequest);
- // We will send notifications from imgCacheValidator::OnStartRequest().
- // In the mean time, we must defer notifications because we are added to
- // the imgRequest's proxy list, and we can get extra notifications
- // resulting from methods such as StartDecoding(). See bug 579122.
- proxy->SetNotificationsDeferred(true);
- // Attach the proxy without notifying
- request->GetValidator()->AddProxy(proxy);
- }
- return NS_SUCCEEDED(rv);
- } else {
- // We will rely on Necko to cache this request when it's possible, and to
- // tell imgCacheValidator::OnStartRequest whether the request came from its
- // cache.
- nsCOMPtr<nsIChannel> newChannel;
- bool forcePrincipalCheck;
- rv = NewImageChannel(getter_AddRefs(newChannel),
- &forcePrincipalCheck,
- aURI,
- aInitialDocumentURI,
- aCORSMode,
- aReferrerURI,
- aReferrerPolicy,
- aLoadGroup,
- mAcceptHeader,
- aLoadFlags,
- aLoadPolicyType,
- aLoadingPrincipal,
- aCX,
- mRespectPrivacy);
- if (NS_FAILED(rv)) {
- return false;
- }
- RefPtr<imgRequestProxy> req;
- rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
- aLoadFlags, getter_AddRefs(req));
- if (NS_FAILED(rv)) {
- return false;
- }
- // Make sure that OnStatus/OnProgress calls have the right request set...
- RefPtr<nsProgressNotificationProxy> progressproxy =
- new nsProgressNotificationProxy(newChannel, req);
- if (!progressproxy) {
- return false;
- }
- RefPtr<imgCacheValidator> hvc =
- new imgCacheValidator(progressproxy, this, request, aCX,
- forcePrincipalCheck);
- // Casting needed here to get past multiple inheritance.
- nsCOMPtr<nsIStreamListener> listener =
- do_QueryInterface(static_cast<nsIThreadRetargetableStreamListener*>(hvc));
- NS_ENSURE_TRUE(listener, false);
- // We must set the notification callbacks before setting up the
- // CORS listener, because that's also interested inthe
- // notification callbacks.
- newChannel->SetNotificationCallbacks(hvc);
- request->SetValidator(hvc);
- // We will send notifications from imgCacheValidator::OnStartRequest().
- // In the mean time, we must defer notifications because we are added to
- // the imgRequest's proxy list, and we can get extra notifications
- // resulting from methods such as StartDecoding(). See bug 579122.
- req->SetNotificationsDeferred(true);
- // Add the proxy without notifying
- hvc->AddProxy(req);
- mozilla::net::PredictorLearn(aURI, aInitialDocumentURI,
- nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, aLoadGroup);
- rv = newChannel->AsyncOpen2(listener);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- req->CancelAndForgetObserver(rv);
- return false;
- }
- req.forget(aProxyRequest);
- return true;
- }
- }
- bool
- imgLoader::ValidateEntry(imgCacheEntry* aEntry,
- nsIURI* aURI,
- nsIURI* aInitialDocumentURI,
- nsIURI* aReferrerURI,
- ReferrerPolicy aReferrerPolicy,
- nsILoadGroup* aLoadGroup,
- imgINotificationObserver* aObserver,
- nsISupports* aCX,
- nsLoadFlags aLoadFlags,
- nsContentPolicyType aLoadPolicyType,
- bool aCanMakeNewChannel,
- imgRequestProxy** aProxyRequest,
- nsIPrincipal* aLoadingPrincipal,
- int32_t aCORSMode)
- {
- LOG_SCOPE(gImgLog, "imgLoader::ValidateEntry");
- bool hasExpired;
- uint32_t expirationTime = aEntry->GetExpiryTime();
- if (expirationTime <= SecondsFromPRTime(PR_Now())) {
- hasExpired = true;
- } else {
- hasExpired = false;
- }
- nsresult rv;
- // Special treatment for file URLs - aEntry has expired if file has changed
- nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(aURI));
- if (fileUrl) {
- uint32_t lastModTime = aEntry->GetLoadTime();
- nsCOMPtr<nsIFile> theFile;
- rv = fileUrl->GetFile(getter_AddRefs(theFile));
- if (NS_SUCCEEDED(rv)) {
- PRTime fileLastMod;
- rv = theFile->GetLastModifiedTime(&fileLastMod);
- if (NS_SUCCEEDED(rv)) {
- // nsIFile uses millisec, NSPR usec
- fileLastMod *= 1000;
- hasExpired = SecondsFromPRTime((PRTime)fileLastMod) > lastModTime;
- }
- }
- }
- RefPtr<imgRequest> request(aEntry->GetRequest());
- if (!request) {
- return false;
- }
- if (!ValidateSecurityInfo(request, aEntry->ForcePrincipalCheck(),
- aCORSMode, aLoadingPrincipal,
- aCX, aLoadPolicyType, aReferrerPolicy))
- return false;
- // data URIs are immutable and by their nature can't leak data, so we can
- // just return true in that case. Doing so would mean that shift-reload
- // doesn't reload data URI documents/images though (which is handy for
- // debugging during gecko development) so we make an exception in that case.
- nsAutoCString scheme;
- aURI->GetScheme(scheme);
- if (scheme.EqualsLiteral("data") &&
- !(aLoadFlags & nsIRequest::LOAD_BYPASS_CACHE)) {
- return true;
- }
- bool validateRequest = false;
- // If the request's loadId is the same as the aCX, then it is ok to use
- // this one because it has already been validated for this context.
- //
- // XXX: nullptr seems to be a 'special' key value that indicates that NO
- // validation is required.
- //
- void *key = (void*) aCX;
- if (request->LoadId() != key) {
- // If we would need to revalidate this entry, but we're being told to
- // bypass the cache, we don't allow this entry to be used.
- if (aLoadFlags & nsIRequest::LOAD_BYPASS_CACHE) {
- return false;
- }
- if (MOZ_UNLIKELY(ChaosMode::isActive(ChaosFeature::ImageCache))) {
- if (ChaosMode::randomUint32LessThan(4) < 1) {
- return false;
- }
- }
- // Determine whether the cache aEntry must be revalidated...
- validateRequest = ShouldRevalidateEntry(aEntry, aLoadFlags, hasExpired);
- MOZ_LOG(gImgLog, LogLevel::Debug,
- ("imgLoader::ValidateEntry validating cache entry. "
- "validateRequest = %d", validateRequest));
- } else if (!key && MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
- MOZ_LOG(gImgLog, LogLevel::Debug,
- ("imgLoader::ValidateEntry BYPASSING cache validation for %s "
- "because of NULL LoadID", aURI->GetSpecOrDefault().get()));
- }
- // We can't use a cached request if it comes from a different
- // application cache than this load is expecting.
- nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
- nsCOMPtr<nsIApplicationCache> requestAppCache;
- nsCOMPtr<nsIApplicationCache> groupAppCache;
- if ((appCacheContainer = do_GetInterface(request->GetRequest()))) {
- appCacheContainer->GetApplicationCache(getter_AddRefs(requestAppCache));
- }
- if ((appCacheContainer = do_QueryInterface(aLoadGroup))) {
- appCacheContainer->GetApplicationCache(getter_AddRefs(groupAppCache));
- }
- if (requestAppCache != groupAppCache) {
- MOZ_LOG(gImgLog, LogLevel::Debug,
- ("imgLoader::ValidateEntry - Unable to use cached imgRequest "
- "[request=%p] because of mismatched application caches\n",
- address_of(request)));
- return false;
- }
- if (validateRequest && aCanMakeNewChannel) {
- LOG_SCOPE(gImgLog,
- "imgLoader::ValidateRequest |cache hit| must validate");
- return ValidateRequestWithNewChannel(request, aURI, aInitialDocumentURI,
- aReferrerURI, aReferrerPolicy,
- aLoadGroup, aObserver,
- aCX, aLoadFlags, aLoadPolicyType,
- aProxyRequest, aLoadingPrincipal,
- aCORSMode);
- }
- return !validateRequest;
- }
- bool
- imgLoader::RemoveFromCache(const ImageCacheKey& aKey)
- {
- LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
- "imgLoader::RemoveFromCache", "uri", aKey.Spec());
- imgCacheTable& cache = GetCache(aKey);
- imgCacheQueue& queue = GetCacheQueue(aKey);
- RefPtr<imgCacheEntry> entry;
- if (cache.Get(aKey, getter_AddRefs(entry)) && entry) {
- cache.Remove(aKey);
- MOZ_ASSERT(!entry->Evicted(), "Evicting an already-evicted cache entry!");
- // Entries with no proxies are in the tracker.
- if (entry->HasNoProxies()) {
- if (mCacheTracker) {
- mCacheTracker->RemoveObject(entry);
- }
- queue.Remove(entry);
- }
- entry->SetEvicted(true);
- RefPtr<imgRequest> request = entry->GetRequest();
- request->SetIsInCache(false);
- AddToUncachedImages(request);
- return true;
- } else {
- return false;
- }
- }
- bool
- imgLoader::RemoveFromCache(imgCacheEntry* entry)
- {
- LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache entry");
- RefPtr<imgRequest> request = entry->GetRequest();
- if (request) {
- const ImageCacheKey& key = request->CacheKey();
- imgCacheTable& cache = GetCache(key);
- imgCacheQueue& queue = GetCacheQueue(key);
- LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
- "imgLoader::RemoveFromCache", "entry's uri",
- key.Spec());
- cache.Remove(key);
- if (entry->HasNoProxies()) {
- LOG_STATIC_FUNC(gImgLog,
- "imgLoader::RemoveFromCache removing from tracker");
- if (mCacheTracker) {
- mCacheTracker->RemoveObject(entry);
- }
- queue.Remove(entry);
- }
- entry->SetEvicted(true);
- request->SetIsInCache(false);
- AddToUncachedImages(request);
- return true;
- }
- return false;
- }
- nsresult
- imgLoader::EvictEntries(imgCacheTable& aCacheToClear)
- {
- LOG_STATIC_FUNC(gImgLog, "imgLoader::EvictEntries table");
- // We have to make a temporary, since RemoveFromCache removes the element
- // from the queue, invalidating iterators.
- nsTArray<RefPtr<imgCacheEntry> > entries;
- for (auto iter = aCacheToClear.Iter(); !iter.Done(); iter.Next()) {
- RefPtr<imgCacheEntry>& data = iter.Data();
- entries.AppendElement(data);
- }
- for (uint32_t i = 0; i < entries.Length(); ++i) {
- if (!RemoveFromCache(entries[i])) {
- return NS_ERROR_FAILURE;
- }
- }
- MOZ_ASSERT(aCacheToClear.Count() == 0);
- return NS_OK;
- }
- nsresult
- imgLoader::EvictEntries(imgCacheQueue& aQueueToClear)
- {
- LOG_STATIC_FUNC(gImgLog, "imgLoader::EvictEntries queue");
- // We have to make a temporary, since RemoveFromCache removes the element
- // from the queue, invalidating iterators.
- nsTArray<RefPtr<imgCacheEntry> > entries(aQueueToClear.GetNumElements());
- for (imgCacheQueue::const_iterator i = aQueueToClear.begin();
- i != aQueueToClear.end(); ++i) {
- entries.AppendElement(*i);
- }
- for (uint32_t i = 0; i < entries.Length(); ++i) {
- if (!RemoveFromCache(entries[i])) {
- return NS_ERROR_FAILURE;
- }
- }
- MOZ_ASSERT(aQueueToClear.GetNumElements() == 0);
- return NS_OK;
- }
- void
- imgLoader::AddToUncachedImages(imgRequest* aRequest)
- {
- MutexAutoLock lock(mUncachedImagesMutex);
- mUncachedImages.PutEntry(aRequest);
- }
- void
- imgLoader::RemoveFromUncachedImages(imgRequest* aRequest)
- {
- MutexAutoLock lock(mUncachedImagesMutex);
- mUncachedImages.RemoveEntry(aRequest);
- }
- #define LOAD_FLAGS_CACHE_MASK (nsIRequest::LOAD_BYPASS_CACHE | \
- nsIRequest::LOAD_FROM_CACHE)
- #define LOAD_FLAGS_VALIDATE_MASK (nsIRequest::VALIDATE_ALWAYS | \
- nsIRequest::VALIDATE_NEVER | \
- nsIRequest::VALIDATE_ONCE_PER_SESSION)
- NS_IMETHODIMP
- imgLoader::LoadImageXPCOM(nsIURI* aURI,
- nsIURI* aInitialDocumentURI,
- nsIURI* aReferrerURI,
- const nsAString& aReferrerPolicy,
- nsIPrincipal* aLoadingPrincipal,
- nsILoadGroup* aLoadGroup,
- imgINotificationObserver* aObserver,
- nsISupports* aCX,
- nsLoadFlags aLoadFlags,
- nsISupports* aCacheKey,
- nsContentPolicyType aContentPolicyType,
- imgIRequest** _retval)
- {
- // Optional parameter, so defaults to 0 (== TYPE_INVALID)
- if (!aContentPolicyType) {
- aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
- }
- imgRequestProxy* proxy;
- ReferrerPolicy refpol = ReferrerPolicyFromString(aReferrerPolicy);
- nsCOMPtr<nsINode> node = do_QueryInterface(aCX);
- nsCOMPtr<nsIDocument> doc = do_QueryInterface(aCX);
- nsresult rv = LoadImage(aURI,
- aInitialDocumentURI,
- aReferrerURI,
- refpol == mozilla::net::RP_Unset ?
- mozilla::net::RP_Default : refpol,
- aLoadingPrincipal,
- aLoadGroup,
- aObserver,
- node,
- doc,
- aLoadFlags,
- aCacheKey,
- aContentPolicyType,
- EmptyString(),
- &proxy);
- *_retval = proxy;
- return rv;
- }
- nsresult
- imgLoader::LoadImage(nsIURI* aURI,
- nsIURI* aInitialDocumentURI,
- nsIURI* aReferrerURI,
- ReferrerPolicy aReferrerPolicy,
- nsIPrincipal* aLoadingPrincipal,
- nsILoadGroup* aLoadGroup,
- imgINotificationObserver* aObserver,
- nsINode *aContext,
- nsIDocument* aLoadingDocument,
- nsLoadFlags aLoadFlags,
- nsISupports* aCacheKey,
- nsContentPolicyType aContentPolicyType,
- const nsAString& initiatorType,
- imgRequestProxy** _retval)
- {
- VerifyCacheSizes();
- NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer");
- if (!aURI) {
- return NS_ERROR_NULL_POINTER;
- }
- LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::LoadImage", "aURI",
- aURI->GetSpecOrDefault().get());
- *_retval = nullptr;
- RefPtr<imgRequest> request;
- nsresult rv;
- nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
- #ifdef DEBUG
- bool isPrivate = false;
- if (aLoadGroup) {
- nsCOMPtr<nsIInterfaceRequestor> callbacks;
- aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
- if (callbacks) {
- nsCOMPtr<nsILoadContext> 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);
- }
- //
- // Merge the default load flags with those passed in via aLoadFlags.
- // Currently, *only* the caching, validation and background load flags
- // are merged...
- //
- // The flags in aLoadFlags take precedence over the default flags!
- //
- if (aLoadFlags & LOAD_FLAGS_CACHE_MASK) {
- // Override the default caching flags...
- requestFlags = (requestFlags & ~LOAD_FLAGS_CACHE_MASK) |
- (aLoadFlags & LOAD_FLAGS_CACHE_MASK);
- }
- if (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK) {
- // Override the default validation flags...
- requestFlags = (requestFlags & ~LOAD_FLAGS_VALIDATE_MASK) |
- (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK);
- }
- if (aLoadFlags & nsIRequest::LOAD_BACKGROUND) {
- // Propagate background loading...
- requestFlags |= nsIRequest::LOAD_BACKGROUND;
- }
- int32_t corsmode = imgIRequest::CORS_NONE;
- if (aLoadFlags & imgILoader::LOAD_CORS_ANONYMOUS) {
- corsmode = imgIRequest::CORS_ANONYMOUS;
- } else if (aLoadFlags & imgILoader::LOAD_CORS_USE_CREDENTIALS) {
- corsmode = imgIRequest::CORS_USE_CREDENTIALS;
- }
- RefPtr<imgCacheEntry> entry;
- // Look in the cache for our URI, and then validate it.
- // XXX For now ignore aCacheKey. We will need it in the future
- // for correctly dealing with image load requests that are a result
- // of post data.
- PrincipalOriginAttributes attrs;
- if (aLoadingPrincipal) {
- attrs = BasePrincipal::Cast(aLoadingPrincipal)->OriginAttributesRef();
- }
- ImageCacheKey key(aURI, attrs, aLoadingDocument, rv);
- NS_ENSURE_SUCCESS(rv, rv);
- imgCacheTable& cache = GetCache(key);
- if (cache.Get(key, getter_AddRefs(entry)) && entry) {
- if (ValidateEntry(entry, aURI, aInitialDocumentURI, aReferrerURI,
- aReferrerPolicy, aLoadGroup, aObserver, aLoadingDocument,
- requestFlags, aContentPolicyType, true, _retval,
- aLoadingPrincipal, corsmode)) {
- request = entry->GetRequest();
- // If this entry has no proxies, its request has no reference to the
- // entry.
- if (entry->HasNoProxies()) {
- LOG_FUNC_WITH_PARAM(gImgLog,
- "imgLoader::LoadImage() adding proxyless entry", "uri", key.Spec());
- MOZ_ASSERT(!request->HasCacheEntry(),
- "Proxyless entry's request has cache entry!");
- request->SetCacheEntry(entry);
- if (mCacheTracker && entry->GetExpirationState()->IsTracked()) {
- mCacheTracker->MarkUsed(entry);
- }
- }
- entry->Touch();
- } else {
- // We can't use this entry. We'll try to load it off the network, and if
- // successful, overwrite the old entry in the cache with a new one.
- entry = nullptr;
- }
- }
- // Keep the channel in this scope, so we can adjust its notificationCallbacks
- // later when we create the proxy.
- nsCOMPtr<nsIChannel> newChannel;
- // If we didn't get a cache hit, we need to load from the network.
- if (!request) {
- LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache miss|");
- bool forcePrincipalCheck;
- rv = NewImageChannel(getter_AddRefs(newChannel),
- &forcePrincipalCheck,
- aURI,
- aInitialDocumentURI,
- corsmode,
- aReferrerURI,
- aReferrerPolicy,
- aLoadGroup,
- mAcceptHeader,
- requestFlags,
- aContentPolicyType,
- aLoadingPrincipal,
- aContext,
- mRespectPrivacy);
- if (NS_FAILED(rv)) {
- return NS_ERROR_FAILURE;
- }
- MOZ_ASSERT(NS_UsePrivateBrowsing(newChannel) == mRespectPrivacy);
- NewRequestAndEntry(forcePrincipalCheck, this, key,
- getter_AddRefs(request),
- getter_AddRefs(entry));
- MOZ_LOG(gImgLog, LogLevel::Debug,
- ("[this=%p] imgLoader::LoadImage -- Created new imgRequest"
- " [request=%p]\n", this, request.get()));
- nsCOMPtr<nsILoadGroup> channelLoadGroup;
- newChannel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
- rv = request->Init(aURI, aURI, /* aHadInsecureRedirect = */ false,
- channelLoadGroup, newChannel, entry, aLoadingDocument,
- aLoadingPrincipal, corsmode, aReferrerPolicy);
- if (NS_FAILED(rv)) {
- return NS_ERROR_FAILURE;
- }
- // Add the initiator type for this image load
- nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(newChannel);
- if (timedChannel) {
- timedChannel->SetInitiatorType(initiatorType);
- }
- // create the proxy listener
- nsCOMPtr<nsIStreamListener> listener = new ProxyListener(request.get());
- MOZ_LOG(gImgLog, LogLevel::Debug,
- ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen2()\n",
- this));
- mozilla::net::PredictorLearn(aURI, aInitialDocumentURI,
- nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, aLoadGroup);
- nsresult openRes = newChannel->AsyncOpen2(listener);
- if (NS_FAILED(openRes)) {
- MOZ_LOG(gImgLog, LogLevel::Debug,
- ("[this=%p] imgLoader::LoadImage -- AsyncOpen2() failed: 0x%x\n",
- this, openRes));
- request->CancelAndAbort(openRes);
- return openRes;
- }
- // Try to add the new request into the cache.
- PutIntoCache(key, entry);
- } else {
- LOG_MSG_WITH_PARAM(gImgLog,
- "imgLoader::LoadImage |cache hit|", "request", request);
- }
- // If we didn't get a proxy when validating the cache entry, we need to
- // create one.
- if (!*_retval) {
- // ValidateEntry() has three return values: "Is valid," "might be valid --
- // validating over network", and "not valid." If we don't have a _retval,
- // we know ValidateEntry is not validating over the network, so it's safe
- // to SetLoadId here because we know this request is valid for this context.
- //
- // Note, however, that this doesn't guarantee the behaviour we want (one
- // URL maps to the same image on a page) if we load the same image in a
- // different tab (see bug 528003), because its load id will get re-set, and
- // that'll cause us to validate over the network.
- request->SetLoadId(aLoadingDocument);
- LOG_MSG(gImgLog, "imgLoader::LoadImage", "creating proxy request.");
- rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
- requestFlags, _retval);
- if (NS_FAILED(rv)) {
- return rv;
- }
- imgRequestProxy* proxy = *_retval;
- // Make sure that OnStatus/OnProgress calls have the right request set, if
- // we did create a channel here.
- if (newChannel) {
- nsCOMPtr<nsIInterfaceRequestor> requestor(
- new nsProgressNotificationProxy(newChannel, proxy));
- if (!requestor) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- newChannel->SetNotificationCallbacks(requestor);
- }
- // Note that it's OK to add here even if the request is done. If it is,
- // it'll send a OnStopRequest() to the proxy in imgRequestProxy::Notify and
- // the proxy will be removed from the loadgroup.
- proxy->AddToLoadGroup();
- // If we're loading off the network, explicitly don't notify our proxy,
- // because necko (or things called from necko, such as imgCacheValidator)
- // are going to call our notifications asynchronously, and we can't make it
- // further asynchronous because observers might rely on imagelib completing
- // its work between the channel's OnStartRequest and OnStopRequest.
- if (!newChannel) {
- proxy->NotifyListener();
- }
- return rv;
- }
- NS_ASSERTION(*_retval, "imgLoader::LoadImage -- no return value");
- return NS_OK;
- }
- NS_IMETHODIMP
- imgLoader::LoadImageWithChannelXPCOM(nsIChannel* channel,
- imgINotificationObserver* aObserver,
- nsISupports* aCX,
- nsIStreamListener** listener,
- imgIRequest** _retval)
- {
- nsresult result;
- imgRequestProxy* proxy;
- result = LoadImageWithChannel(channel,
- aObserver,
- aCX,
- listener,
- &proxy);
- *_retval = proxy;
- return result;
- }
- nsresult
- imgLoader::LoadImageWithChannel(nsIChannel* channel,
- imgINotificationObserver* aObserver,
- nsISupports* aCX,
- nsIStreamListener** listener,
- imgRequestProxy** _retval)
- {
- NS_ASSERTION(channel,
- "imgLoader::LoadImageWithChannel -- NULL channel pointer");
- MOZ_ASSERT(NS_UsePrivateBrowsing(channel) == mRespectPrivacy);
- RefPtr<imgRequest> request;
- nsCOMPtr<nsIURI> uri;
- channel->GetURI(getter_AddRefs(uri));
- nsCOMPtr<nsIDocument> doc = do_QueryInterface(aCX);
- NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
- nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
- PrincipalOriginAttributes attrs;
- if (loadInfo) {
- attrs.InheritFromNecko(loadInfo->GetOriginAttributes());
- }
- nsresult rv;
- ImageCacheKey key(uri, attrs, doc, rv);
- NS_ENSURE_SUCCESS(rv, rv);
- nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
- channel->GetLoadFlags(&requestFlags);
- RefPtr<imgCacheEntry> entry;
- if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) {
- RemoveFromCache(key);
- } else {
- // Look in the cache for our URI, and then validate it.
- // XXX For now ignore aCacheKey. We will need it in the future
- // for correctly dealing with image load requests that are a result
- // of post data.
- imgCacheTable& cache = GetCache(key);
- if (cache.Get(key, getter_AddRefs(entry)) && entry) {
- // We don't want to kick off another network load. So we ask
- // ValidateEntry to only do validation without creating a new proxy. If
- // it says that the entry isn't valid any more, we'll only use the entry
- // we're getting if the channel is loading from the cache anyways.
- //
- // XXX -- should this be changed? it's pretty much verbatim from the old
- // code, but seems nonsensical.
- //
- // Since aCanMakeNewChannel == false, we don't need to pass content policy
- // type/principal/etc
- nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
- // if there is a loadInfo, use the right contentType, otherwise
- // default to the internal image type
- nsContentPolicyType policyType = loadInfo
- ? loadInfo->InternalContentPolicyType()
- : nsIContentPolicy::TYPE_INTERNAL_IMAGE;
- if (ValidateEntry(entry, uri, nullptr, nullptr, RP_Default,
- nullptr, aObserver, aCX, requestFlags,
- policyType, false, nullptr,
- nullptr, imgIRequest::CORS_NONE)) {
- request = entry->GetRequest();
- } else {
- nsCOMPtr<nsICacheInfoChannel> cacheChan(do_QueryInterface(channel));
- bool bUseCacheCopy;
- if (cacheChan) {
- cacheChan->IsFromCache(&bUseCacheCopy);
- } else {
- bUseCacheCopy = false;
- }
- if (!bUseCacheCopy) {
- entry = nullptr;
- } else {
- request = entry->GetRequest();
- }
- }
- if (request && entry) {
- // If this entry has no proxies, its request has no reference to
- // the entry.
- if (entry->HasNoProxies()) {
- LOG_FUNC_WITH_PARAM(gImgLog,
- "imgLoader::LoadImageWithChannel() adding proxyless entry",
- "uri", key.Spec());
- MOZ_ASSERT(!request->HasCacheEntry(),
- "Proxyless entry's request has cache entry!");
- request->SetCacheEntry(entry);
- if (mCacheTracker && entry->GetExpirationState()->IsTracked()) {
- mCacheTracker->MarkUsed(entry);
- }
- }
- }
- }
- }
- nsCOMPtr<nsILoadGroup> loadGroup;
- channel->GetLoadGroup(getter_AddRefs(loadGroup));
- // Filter out any load flags not from nsIRequest
- requestFlags &= nsIRequest::LOAD_REQUESTMASK;
- rv = NS_OK;
- if (request) {
- // we have this in our cache already.. cancel the current (document) load
- // this should fire an OnStopRequest
- channel->Cancel(NS_ERROR_PARSED_DATA_CACHED);
- *listener = nullptr; // give them back a null nsIStreamListener
- rv = CreateNewProxyForRequest(request, loadGroup, aObserver,
- requestFlags, _retval);
- static_cast<imgRequestProxy*>(*_retval)->NotifyListener();
- } else {
- // We use originalURI here to fulfil the imgIRequest contract on GetURI.
- nsCOMPtr<nsIURI> originalURI;
- channel->GetOriginalURI(getter_AddRefs(originalURI));
- // XXX(seth): We should be able to just use |key| here, except that |key| is
- // constructed above with the *current URI* and not the *original URI*. I'm
- // pretty sure this is a bug, and it's preventing us from ever getting a
- // cache hit in LoadImageWithChannel when redirects are involved.
- ImageCacheKey originalURIKey(originalURI, attrs, doc, rv);
- NS_ENSURE_SUCCESS(rv, rv);
- // 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(/* aForcePrincipalCheckForCacheEntry = */ true,
- this, originalURIKey,
- getter_AddRefs(request),
- getter_AddRefs(entry));
- // No principal specified here, because we're not passed one.
- // In LoadImageWithChannel, the redirects that may have been
- // assoicated with this load would have gone through necko.
- // We only have the final URI in ImageLib and hence don't know
- // if the request went through insecure redirects. But if it did,
- // the necko cache should have handled that (since all necko cache hits
- // including the redirects will go through content policy). Hence, we
- // can set aHadInsecureRedirect to false here.
- rv = request->Init(originalURI, uri, /* aHadInsecureRedirect = */ false,
- channel, channel, entry, aCX, nullptr,
- imgIRequest::CORS_NONE, RP_Default);
- NS_ENSURE_SUCCESS(rv, rv);
- RefPtr<ProxyListener> pl =
- new ProxyListener(static_cast<nsIStreamListener*>(request.get()));
- pl.forget(listener);
- // Try to add the new request into the cache.
- PutIntoCache(originalURIKey, entry);
- rv = CreateNewProxyForRequest(request, loadGroup, aObserver,
- requestFlags, _retval);
- // Explicitly don't notify our proxy, because we're loading off the
- // network, and necko (or things called from necko, such as
- // imgCacheValidator) are going to call our notifications asynchronously,
- // and we can't make it further asynchronous because observers might rely
- // on imagelib completing its work between the channel's OnStartRequest and
- // OnStopRequest.
- }
- return rv;
- }
- bool
- imgLoader::SupportImageWithMimeType(const char* aMimeType,
- AcceptedMimeTypes aAccept
- /* = AcceptedMimeTypes::IMAGES */)
- {
- nsAutoCString mimeType(aMimeType);
- ToLowerCase(mimeType);
- if (aAccept == AcceptedMimeTypes::IMAGES_AND_DOCUMENTS &&
- mimeType.EqualsLiteral("image/svg+xml")) {
- return true;
- }
- DecoderType type = DecoderFactory::GetDecoderType(mimeType.get());
- return type != DecoderType::UNKNOWN;
- }
- NS_IMETHODIMP
- imgLoader::GetMIMETypeFromContent(nsIRequest* aRequest,
- const uint8_t* aContents,
- uint32_t aLength,
- nsACString& aContentType)
- {
- return GetMimeTypeFromContent((const char*)aContents, aLength, aContentType);
- }
- /* static */
- nsresult
- imgLoader::GetMimeTypeFromContent(const char* aContents,
- uint32_t aLength,
- nsACString& aContentType)
- {
- /* Is it a GIF? */
- if (aLength >= 6 && (!nsCRT::strncmp(aContents, "GIF87a", 6) ||
- !nsCRT::strncmp(aContents, "GIF89a", 6))) {
- aContentType.AssignLiteral(IMAGE_GIF);
- /* or a PNG? */
- } else if (aLength >= 8 && ((unsigned char)aContents[0]==0x89 &&
- (unsigned char)aContents[1]==0x50 &&
- (unsigned char)aContents[2]==0x4E &&
- (unsigned char)aContents[3]==0x47 &&
- (unsigned char)aContents[4]==0x0D &&
- (unsigned char)aContents[5]==0x0A &&
- (unsigned char)aContents[6]==0x1A &&
- (unsigned char)aContents[7]==0x0A)) {
- aContentType.AssignLiteral(IMAGE_PNG);
- /* maybe a JPEG (JFIF)? */
- /* JFIF files start with SOI APP0 but older files can start with SOI DQT
- * so we test for SOI followed by any marker, i.e. FF D8 FF
- * this will also work for SPIFF JPEG files if they appear in the future.
- *
- * (JFIF is 0XFF 0XD8 0XFF 0XE0 <skip 2> 0X4A 0X46 0X49 0X46 0X00)
- */
- } else if (aLength >= 3 &&
- ((unsigned char)aContents[0])==0xFF &&
- ((unsigned char)aContents[1])==0xD8 &&
- ((unsigned char)aContents[2])==0xFF) {
- aContentType.AssignLiteral(IMAGE_JPEG);
- /* or how about ART? */
- /* ART begins with JG (4A 47). Major version offset 2.
- * Minor version offset 3. Offset 4 must be nullptr.
- */
- } else if (aLength >= 5 &&
- ((unsigned char) aContents[0])==0x4a &&
- ((unsigned char) aContents[1])==0x47 &&
- ((unsigned char) aContents[4])==0x00 ) {
- aContentType.AssignLiteral(IMAGE_ART);
- } else if (aLength >= 2 && !nsCRT::strncmp(aContents, "BM", 2)) {
- aContentType.AssignLiteral(IMAGE_BMP);
- // ICOs always begin with a 2-byte 0 followed by a 2-byte 1.
- // CURs begin with 2-byte 0 followed by 2-byte 2.
- } else if (aLength >= 4 && (!memcmp(aContents, "\000\000\001\000", 4) ||
- !memcmp(aContents, "\000\000\002\000", 4))) {
- aContentType.AssignLiteral(IMAGE_ICO);
- // WebPs always begin with RIFF, a 32-bit length, and WEBP.
- } else if (aLength >= 12 && !memcmp(aContents, "RIFF", 4) &&
- !memcmp(aContents + 8, "WEBP", 4)) {
- aContentType.AssignLiteral(IMAGE_WEBP);
- } else {
- /* none of the above? I give up */
- return NS_ERROR_NOT_AVAILABLE;
- }
- return NS_OK;
- }
- /**
- * proxy stream listener class used to handle multipart/x-mixed-replace
- */
- #include "nsIRequest.h"
- #include "nsIStreamConverterService.h"
- NS_IMPL_ISUPPORTS(ProxyListener,
- nsIStreamListener,
- nsIThreadRetargetableStreamListener,
- nsIRequestObserver)
- ProxyListener::ProxyListener(nsIStreamListener* dest) :
- mDestListener(dest)
- {
- /* member initializers and constructor code */
- }
- ProxyListener::~ProxyListener()
- {
- /* destructor code */
- }
- /** nsIRequestObserver methods **/
- NS_IMETHODIMP
- ProxyListener::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt)
- {
- if (!mDestListener) {
- return NS_ERROR_FAILURE;
- }
- nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
- if (channel) {
- // We need to set the initiator type for the image load
- nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(channel);
- if (timedChannel) {
- nsAutoString type;
- timedChannel->GetInitiatorType(type);
- if (type.IsEmpty()) {
- timedChannel->SetInitiatorType(NS_LITERAL_STRING("img"));
- }
- }
- nsAutoCString contentType;
- nsresult rv = channel->GetContentType(contentType);
- if (!contentType.IsEmpty()) {
- /* If multipart/x-mixed-replace content, we'll insert a MIME decoder
- in the pipeline to handle the content and pass it along to our
- original listener.
- */
- if (NS_LITERAL_CSTRING("multipart/x-mixed-replace").Equals(contentType)) {
- nsCOMPtr<nsIStreamConverterService> convServ(
- do_GetService("@mozilla.org/streamConverters;1", &rv));
- if (NS_SUCCEEDED(rv)) {
- nsCOMPtr<nsIStreamListener> toListener(mDestListener);
- nsCOMPtr<nsIStreamListener> fromListener;
- rv = convServ->AsyncConvertData("multipart/x-mixed-replace",
- "*/*",
- toListener,
- nullptr,
- getter_AddRefs(fromListener));
- if (NS_SUCCEEDED(rv)) {
- mDestListener = fromListener;
- }
- }
- }
- }
- }
- return mDestListener->OnStartRequest(aRequest, ctxt);
- }
- NS_IMETHODIMP
- ProxyListener::OnStopRequest(nsIRequest* aRequest,
- nsISupports* ctxt,
- nsresult status)
- {
- if (!mDestListener) {
- return NS_ERROR_FAILURE;
- }
- return mDestListener->OnStopRequest(aRequest, ctxt, status);
- }
- /** nsIStreamListener methods **/
- NS_IMETHODIMP
- ProxyListener::OnDataAvailable(nsIRequest* aRequest, nsISupports* ctxt,
- nsIInputStream* inStr, uint64_t sourceOffset,
- uint32_t count)
- {
- if (!mDestListener) {
- return NS_ERROR_FAILURE;
- }
- return mDestListener->OnDataAvailable(aRequest, ctxt, inStr,
- sourceOffset, count);
- }
- /** nsThreadRetargetableStreamListener methods **/
- NS_IMETHODIMP
- ProxyListener::CheckListenerChain()
- {
- NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
- nsresult rv = NS_OK;
- nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
- do_QueryInterface(mDestListener, &rv);
- if (retargetableListener) {
- rv = retargetableListener->CheckListenerChain();
- }
- MOZ_LOG(gImgLog, LogLevel::Debug,
- ("ProxyListener::CheckListenerChain %s [this=%p listener=%p rv=%x]",
- (NS_SUCCEEDED(rv) ? "success" : "failure"),
- this, (nsIStreamListener*)mDestListener, rv));
- return rv;
- }
- /**
- * http validate class. check a channel for a 304
- */
- NS_IMPL_ISUPPORTS(imgCacheValidator, nsIStreamListener, nsIRequestObserver,
- nsIThreadRetargetableStreamListener,
- nsIChannelEventSink, nsIInterfaceRequestor,
- nsIAsyncVerifyRedirectCallback)
- imgCacheValidator::imgCacheValidator(nsProgressNotificationProxy* progress,
- imgLoader* loader, imgRequest* request,
- nsISupports* aContext,
- bool forcePrincipalCheckForCacheEntry)
- : mProgressProxy(progress),
- mRequest(request),
- mContext(aContext),
- mImgLoader(loader),
- mHadInsecureRedirect(false)
- {
- NewRequestAndEntry(forcePrincipalCheckForCacheEntry, loader,
- mRequest->CacheKey(),
- getter_AddRefs(mNewRequest),
- getter_AddRefs(mNewEntry));
- }
- imgCacheValidator::~imgCacheValidator()
- {
- if (mRequest) {
- mRequest->SetValidator(nullptr);
- }
- }
- void
- imgCacheValidator::AddProxy(imgRequestProxy* aProxy)
- {
- // aProxy needs to be in the loadgroup since we're validating from
- // the network.
- aProxy->AddToLoadGroup();
- mProxies.AppendObject(aProxy);
- }
- /** nsIRequestObserver methods **/
- NS_IMETHODIMP
- imgCacheValidator::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt)
- {
- // We may be holding on to a document, so ensure that it's released.
- nsCOMPtr<nsISupports> context = mContext.forget();
- // If for some reason we don't still have an existing request (probably
- // because OnStartRequest got delivered more than once), just bail.
- if (!mRequest) {
- MOZ_ASSERT_UNREACHABLE("OnStartRequest delivered more than once?");
- aRequest->Cancel(NS_BINDING_ABORTED);
- return NS_ERROR_FAILURE;
- }
- // If this request is coming from cache and has the same URI as our
- // imgRequest, the request all our proxies are pointing at is valid, and all
- // we have to do is tell them to notify their listeners.
- nsCOMPtr<nsICacheInfoChannel> cacheChan(do_QueryInterface(aRequest));
- nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
- if (cacheChan && channel && !mRequest->CacheChanged(aRequest)) {
- bool isFromCache = false;
- cacheChan->IsFromCache(&isFromCache);
- nsCOMPtr<nsIURI> channelURI;
- channel->GetURI(getter_AddRefs(channelURI));
- nsCOMPtr<nsIURI> currentURI;
- mRequest->GetCurrentURI(getter_AddRefs(currentURI));
- bool sameURI = false;
- if (channelURI && currentURI) {
- channelURI->Equals(currentURI, &sameURI);
- }
- if (isFromCache && sameURI) {
- uint32_t count = mProxies.Count();
- for (int32_t i = count-1; i>=0; i--) {
- imgRequestProxy* proxy = static_cast<imgRequestProxy*>(mProxies[i]);
- // Proxies waiting on cache validation should be deferring
- // notifications. Undefer them.
- MOZ_ASSERT(proxy->NotificationsDeferred(),
- "Proxies waiting on cache validation should be "
- "deferring notifications!");
- proxy->SetNotificationsDeferred(false);
- // Notify synchronously, because we're already in OnStartRequest, an
- // asynchronously-called function.
- proxy->SyncNotifyListener();
- }
- // We don't need to load this any more.
- aRequest->Cancel(NS_BINDING_ABORTED);
- mRequest->SetLoadId(context);
- mRequest->SetValidator(nullptr);
- mRequest = nullptr;
- mNewRequest = nullptr;
- mNewEntry = nullptr;
- return NS_OK;
- }
- }
- // We can't load out of cache. We have to create a whole new request for the
- // data that's coming in off the channel.
- nsCOMPtr<nsIURI> uri;
- {
- RefPtr<ImageURL> imageURL;
- mRequest->GetURI(getter_AddRefs(imageURL));
- uri = imageURL->ToIURI();
- }
- if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
- LOG_MSG_WITH_PARAM(gImgLog,
- "imgCacheValidator::OnStartRequest creating new request",
- "uri", uri->GetSpecOrDefault().get());
- }
- int32_t corsmode = mRequest->GetCORSMode();
- ReferrerPolicy refpol = mRequest->GetReferrerPolicy();
- nsCOMPtr<nsIPrincipal> loadingPrincipal = mRequest->GetLoadingPrincipal();
- // Doom the old request's cache entry
- mRequest->RemoveFromCache();
- mRequest->SetValidator(nullptr);
- mRequest = nullptr;
- // We use originalURI here to fulfil the imgIRequest contract on GetURI.
- nsCOMPtr<nsIURI> originalURI;
- channel->GetOriginalURI(getter_AddRefs(originalURI));
- nsresult rv =
- mNewRequest->Init(originalURI, uri, mHadInsecureRedirect, aRequest, channel,
- mNewEntry, context, loadingPrincipal, corsmode, refpol);
- if (NS_FAILED(rv)) {
- return rv;
- }
- mDestListener = new ProxyListener(mNewRequest);
- // 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.
- mImgLoader->PutIntoCache(mNewRequest->CacheKey(), mNewEntry);
- uint32_t count = mProxies.Count();
- for (int32_t i = count-1; i>=0; i--) {
- imgRequestProxy* proxy = static_cast<imgRequestProxy*>(mProxies[i]);
- proxy->ChangeOwner(mNewRequest);
- // Notify synchronously, because we're already in OnStartRequest, an
- // asynchronously-called function.
- proxy->SetNotificationsDeferred(false);
- proxy->SyncNotifyListener();
- }
- mNewRequest = nullptr;
- mNewEntry = nullptr;
- return mDestListener->OnStartRequest(aRequest, ctxt);
- }
- NS_IMETHODIMP
- imgCacheValidator::OnStopRequest(nsIRequest* aRequest,
- nsISupports* ctxt,
- nsresult status)
- {
- // Be sure we've released the document that we may have been holding on to.
- mContext = nullptr;
- if (!mDestListener) {
- return NS_OK;
- }
- return mDestListener->OnStopRequest(aRequest, ctxt, status);
- }
- /** nsIStreamListener methods **/
- NS_IMETHODIMP
- imgCacheValidator::OnDataAvailable(nsIRequest* aRequest, nsISupports* ctxt,
- nsIInputStream* inStr,
- uint64_t sourceOffset, uint32_t count)
- {
- if (!mDestListener) {
- // XXX see bug 113959
- uint32_t _retval;
- inStr->ReadSegments(NS_DiscardSegment, nullptr, count, &_retval);
- return NS_OK;
- }
- return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset,
- count);
- }
- /** nsIThreadRetargetableStreamListener methods **/
- NS_IMETHODIMP
- imgCacheValidator::CheckListenerChain()
- {
- NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
- nsresult rv = NS_OK;
- nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
- do_QueryInterface(mDestListener, &rv);
- if (retargetableListener) {
- rv = retargetableListener->CheckListenerChain();
- }
- MOZ_LOG(gImgLog, LogLevel::Debug,
- ("[this=%p] imgCacheValidator::CheckListenerChain -- rv %d=%s",
- this, NS_SUCCEEDED(rv) ? "succeeded" : "failed", rv));
- return rv;
- }
- /** nsIInterfaceRequestor methods **/
- NS_IMETHODIMP
- imgCacheValidator::GetInterface(const nsIID& aIID, void** aResult)
- {
- if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
- return QueryInterface(aIID, aResult);
- }
- return mProgressProxy->GetInterface(aIID, aResult);
- }
- // These functions are materially the same as the same functions in imgRequest.
- // We duplicate them because we're verifying whether cache loads are necessary,
- // not unconditionally loading.
- /** nsIChannelEventSink methods **/
- NS_IMETHODIMP
- imgCacheValidator::
- AsyncOnChannelRedirect(nsIChannel* oldChannel,
- nsIChannel* newChannel,
- uint32_t flags,
- nsIAsyncVerifyRedirectCallback* callback)
- {
- // Note all cache information we get from the old channel.
- mNewRequest->SetCacheValidation(mNewEntry, oldChannel);
- // If the previous URI is a non-HTTPS URI, record that fact for later use by
- // security code, which needs to know whether there is an insecure load at any
- // point in the redirect chain.
- nsCOMPtr<nsIURI> oldURI;
- bool isHttps = false;
- bool isChrome = false;
- bool schemeLocal = false;
- if (NS_FAILED(oldChannel->GetURI(getter_AddRefs(oldURI))) ||
- NS_FAILED(oldURI->SchemeIs("https", &isHttps)) ||
- NS_FAILED(oldURI->SchemeIs("chrome", &isChrome)) ||
- NS_FAILED(NS_URIChainHasFlags(oldURI,
- nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
- &schemeLocal)) ||
- (!isHttps && !isChrome && !schemeLocal)) {
- mHadInsecureRedirect = true;
- }
- // Prepare for callback
- mRedirectCallback = callback;
- mRedirectChannel = newChannel;
- return mProgressProxy->AsyncOnChannelRedirect(oldChannel, newChannel, flags,
- this);
- }
- NS_IMETHODIMP
- imgCacheValidator::OnRedirectVerifyCallback(nsresult aResult)
- {
- // If we've already been told to abort, just do so.
- if (NS_FAILED(aResult)) {
- mRedirectCallback->OnRedirectVerifyCallback(aResult);
- mRedirectCallback = nullptr;
- mRedirectChannel = nullptr;
- return NS_OK;
- }
- // make sure we have a protocol that returns data rather than opens
- // an external application, e.g. mailto:
- nsCOMPtr<nsIURI> uri;
- mRedirectChannel->GetURI(getter_AddRefs(uri));
- bool doesNotReturnData = false;
- NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
- &doesNotReturnData);
- nsresult result = NS_OK;
- if (doesNotReturnData) {
- result = NS_ERROR_ABORT;
- }
- mRedirectCallback->OnRedirectVerifyCallback(result);
- mRedirectCallback = nullptr;
- mRedirectChannel = nullptr;
- return NS_OK;
- }
|