1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- /* 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 "mozilla/ArrayUtils.h"
- #include "mozilla/Attributes.h"
- #include "mozilla/Assertions.h"
- #include "mozilla/DebugOnly.h"
- #include "necko-config.h"
- #include "nsCache.h"
- #include "nsCacheService.h"
- #include "nsCacheRequest.h"
- #include "nsCacheEntry.h"
- #include "nsCacheEntryDescriptor.h"
- #include "nsCacheDevice.h"
- #include "nsMemoryCacheDevice.h"
- #include "nsICacheVisitor.h"
- #include "nsDiskCacheDevice.h"
- #include "nsDiskCacheDeviceSQL.h"
- #include "nsCacheUtils.h"
- #include "../cache2/CacheObserver.h"
- #include "nsIObserverService.h"
- #include "nsIPrefService.h"
- #include "nsIPrefBranch.h"
- #include "nsIFile.h"
- #include "nsIOService.h"
- #include "nsDirectoryServiceDefs.h"
- #include "nsAppDirectoryServiceDefs.h"
- #include "nsThreadUtils.h"
- #include "nsProxyRelease.h"
- #include "nsDeleteDir.h"
- #include "nsNetCID.h"
- #include <math.h> // for log()
- #include "mozilla/Services.h"
- #include "nsITimer.h"
- #include "mozIStorageService.h"
- #include "mozilla/net/NeckoCommon.h"
- #include <algorithm>
- using namespace mozilla;
- using namespace mozilla::net;
- /******************************************************************************
- * nsCacheProfilePrefObserver
- *****************************************************************************/
- #define DISK_CACHE_ENABLE_PREF "browser.cache.disk.enable"
- #define DISK_CACHE_DIR_PREF "browser.cache.disk.parent_directory"
- #define DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF\
- "browser.cache.disk.smart_size.first_run"
- #define DISK_CACHE_SMART_SIZE_ENABLED_PREF \
- "browser.cache.disk.smart_size.enabled"
- #define DISK_CACHE_SMART_SIZE_PREF "browser.cache.disk.smart_size_cached_value"
- #define DISK_CACHE_CAPACITY_PREF "browser.cache.disk.capacity"
- #define DISK_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.disk.max_entry_size"
- #define DISK_CACHE_CAPACITY 256000
- #define DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF \
- "browser.cache.disk.smart_size.use_old_max"
- #define OFFLINE_CACHE_ENABLE_PREF "browser.cache.offline.enable"
- #define OFFLINE_CACHE_DIR_PREF "browser.cache.offline.parent_directory"
- #define OFFLINE_CACHE_CAPACITY_PREF "browser.cache.offline.capacity"
- #define OFFLINE_CACHE_CAPACITY 512000
- #define MEMORY_CACHE_ENABLE_PREF "browser.cache.memory.enable"
- #define MEMORY_CACHE_CAPACITY_PREF "browser.cache.memory.capacity"
- #define MEMORY_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.memory.max_entry_size"
- #define CACHE_COMPRESSION_LEVEL_PREF "browser.cache.compression_level"
- #define CACHE_COMPRESSION_LEVEL 1
- #define SANITIZE_ON_SHUTDOWN_PREF "privacy.sanitize.sanitizeOnShutdown"
- #define CLEAR_ON_SHUTDOWN_PREF "privacy.clearOnShutdown.cache"
- static const char * observerList[] = {
- "profile-before-change",
- "profile-do-change",
- NS_XPCOM_SHUTDOWN_OBSERVER_ID,
- "last-pb-context-exited",
- "suspend_process_notification",
- "resume_process_notification"
- };
- static const char * prefList[] = {
- DISK_CACHE_ENABLE_PREF,
- DISK_CACHE_SMART_SIZE_ENABLED_PREF,
- DISK_CACHE_CAPACITY_PREF,
- DISK_CACHE_DIR_PREF,
- DISK_CACHE_MAX_ENTRY_SIZE_PREF,
- DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
- OFFLINE_CACHE_ENABLE_PREF,
- OFFLINE_CACHE_CAPACITY_PREF,
- OFFLINE_CACHE_DIR_PREF,
- MEMORY_CACHE_ENABLE_PREF,
- MEMORY_CACHE_CAPACITY_PREF,
- MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
- CACHE_COMPRESSION_LEVEL_PREF,
- SANITIZE_ON_SHUTDOWN_PREF,
- CLEAR_ON_SHUTDOWN_PREF
- };
- // Cache sizes, in KB
- const int32_t DEFAULT_CACHE_SIZE = 250 * 1024; // 250 MB
- const int32_t MAX_CACHE_SIZE = 350 * 1024; // 350 MB
- const int32_t OLD_MAX_CACHE_SIZE = 1024 * 1024; // 1 GB
- // Default cache size was 50 MB for many years until FF 4:
- const int32_t PRE_GECKO_2_0_DEFAULT_CACHE_SIZE = 50 * 1024;
- class nsCacheProfilePrefObserver : public nsIObserver
- {
- virtual ~nsCacheProfilePrefObserver() {}
- public:
- NS_DECL_THREADSAFE_ISUPPORTS
- NS_DECL_NSIOBSERVER
- nsCacheProfilePrefObserver()
- : mHaveProfile(false)
- , mDiskCacheEnabled(false)
- , mDiskCacheCapacity(0)
- , mDiskCacheMaxEntrySize(-1) // -1 means "no limit"
- , mSmartSizeEnabled(false)
- , mShouldUseOldMaxSmartSize(false)
- , mOfflineCacheEnabled(false)
- , mOfflineCacheCapacity(0)
- , mMemoryCacheEnabled(true)
- , mMemoryCacheCapacity(-1)
- , mMemoryCacheMaxEntrySize(-1) // -1 means "no limit"
- , mCacheCompressionLevel(CACHE_COMPRESSION_LEVEL)
- , mSanitizeOnShutdown(false)
- , mClearCacheOnShutdown(false)
- {
- }
-
- nsresult Install();
- void Remove();
- nsresult ReadPrefs(nsIPrefBranch* branch);
-
- bool DiskCacheEnabled();
- int32_t DiskCacheCapacity() { return mDiskCacheCapacity; }
- void SetDiskCacheCapacity(int32_t);
- int32_t DiskCacheMaxEntrySize() { return mDiskCacheMaxEntrySize; }
- nsIFile * DiskCacheParentDirectory() { return mDiskCacheParentDirectory; }
- bool SmartSizeEnabled() { return mSmartSizeEnabled; }
- bool ShouldUseOldMaxSmartSize() { return mShouldUseOldMaxSmartSize; }
- void SetUseNewMaxSmartSize(bool useNew) { mShouldUseOldMaxSmartSize = !useNew; }
- bool OfflineCacheEnabled();
- int32_t OfflineCacheCapacity() { return mOfflineCacheCapacity; }
- nsIFile * OfflineCacheParentDirectory() { return mOfflineCacheParentDirectory; }
-
- bool MemoryCacheEnabled();
- int32_t MemoryCacheCapacity();
- int32_t MemoryCacheMaxEntrySize() { return mMemoryCacheMaxEntrySize; }
- int32_t CacheCompressionLevel();
- bool SanitizeAtShutdown() { return mSanitizeOnShutdown && mClearCacheOnShutdown; }
- static uint32_t GetSmartCacheSize(const nsAString& cachePath,
- uint32_t currentSize,
- bool shouldUseOldMaxSmartSize);
- bool PermittedToSmartSize(nsIPrefBranch*, bool firstRun);
- private:
- bool mHaveProfile;
-
- bool mDiskCacheEnabled;
- int32_t mDiskCacheCapacity; // in kilobytes
- int32_t mDiskCacheMaxEntrySize; // in kilobytes
- nsCOMPtr<nsIFile> mDiskCacheParentDirectory;
- bool mSmartSizeEnabled;
- bool mShouldUseOldMaxSmartSize;
- bool mOfflineCacheEnabled;
- int32_t mOfflineCacheCapacity; // in kilobytes
- nsCOMPtr<nsIFile> mOfflineCacheParentDirectory;
-
- bool mMemoryCacheEnabled;
- int32_t mMemoryCacheCapacity; // in kilobytes
- int32_t mMemoryCacheMaxEntrySize; // in kilobytes
- int32_t mCacheCompressionLevel;
- bool mSanitizeOnShutdown;
- bool mClearCacheOnShutdown;
- };
- NS_IMPL_ISUPPORTS(nsCacheProfilePrefObserver, nsIObserver)
- class nsSetDiskSmartSizeCallback final : public nsITimerCallback
- {
- ~nsSetDiskSmartSizeCallback() {}
- public:
- NS_DECL_THREADSAFE_ISUPPORTS
- NS_IMETHOD Notify(nsITimer* aTimer) override {
- if (nsCacheService::gService) {
- nsCacheServiceAutoLock autoLock;
- nsCacheService::gService->SetDiskSmartSize_Locked();
- nsCacheService::gService->mSmartSizeTimer = nullptr;
- }
- return NS_OK;
- }
- };
- NS_IMPL_ISUPPORTS(nsSetDiskSmartSizeCallback, nsITimerCallback)
- // Runnable sent to main thread after the cache IO thread calculates available
- // disk space, so that there is no race in setting mDiskCacheCapacity.
- class nsSetSmartSizeEvent: public Runnable
- {
- public:
- explicit nsSetSmartSizeEvent(int32_t smartSize)
- : mSmartSize(smartSize) {}
- NS_IMETHOD Run()
- {
- NS_ASSERTION(NS_IsMainThread(),
- "Setting smart size data off the main thread");
- // Main thread may have already called nsCacheService::Shutdown
- if (!nsCacheService::IsInitialized())
- return NS_ERROR_NOT_AVAILABLE;
- // Ensure smart sizing wasn't switched off while event was pending.
- // It is safe to access the observer without the lock since we are
- // on the main thread and the value changes only on the main thread.
- if (!nsCacheService::gService->mObserver->SmartSizeEnabled())
- return NS_OK;
- nsCacheService::SetDiskCacheCapacity(mSmartSize);
- nsCOMPtr<nsIPrefBranch> ps = do_GetService(NS_PREFSERVICE_CONTRACTID);
- if (!ps ||
- NS_FAILED(ps->SetIntPref(DISK_CACHE_SMART_SIZE_PREF, mSmartSize)))
- NS_WARNING("Failed to set smart size pref");
- return NS_OK;
- }
- private:
- int32_t mSmartSize;
- };
- // Runnable sent from main thread to cacheIO thread
- class nsGetSmartSizeEvent: public Runnable
- {
- public:
- nsGetSmartSizeEvent(const nsAString& cachePath, uint32_t currentSize,
- bool shouldUseOldMaxSmartSize)
- : mCachePath(cachePath)
- , mCurrentSize(currentSize)
- , mShouldUseOldMaxSmartSize(shouldUseOldMaxSmartSize)
- {}
-
- // Calculates user's disk space available on a background thread and
- // dispatches this value back to the main thread.
- NS_IMETHOD Run() override
- {
- uint32_t size;
- size = nsCacheProfilePrefObserver::GetSmartCacheSize(mCachePath,
- mCurrentSize,
- mShouldUseOldMaxSmartSize);
- NS_DispatchToMainThread(new nsSetSmartSizeEvent(size));
- return NS_OK;
- }
- private:
- nsString mCachePath;
- uint32_t mCurrentSize;
- bool mShouldUseOldMaxSmartSize;
- };
- class nsBlockOnCacheThreadEvent : public Runnable {
- public:
- nsBlockOnCacheThreadEvent()
- {
- }
- NS_IMETHOD Run() override
- {
- nsCacheServiceAutoLock autoLock;
- CACHE_LOG_DEBUG(("nsBlockOnCacheThreadEvent [%p]\n", this));
- nsCacheService::gService->mNotified = true;
- nsCacheService::gService->mCondVar.Notify();
- return NS_OK;
- }
- };
- nsresult
- nsCacheProfilePrefObserver::Install()
- {
- // install profile-change observer
- nsCOMPtr<nsIObserverService> observerService =
- mozilla::services::GetObserverService();
- if (!observerService)
- return NS_ERROR_FAILURE;
-
- nsresult rv, rv2 = NS_OK;
- for (unsigned int i=0; i<ArrayLength(observerList); i++) {
- rv = observerService->AddObserver(this, observerList[i], false);
- if (NS_FAILED(rv))
- rv2 = rv;
- }
-
- // install preferences observer
- nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
- if (!branch) return NS_ERROR_FAILURE;
- for (unsigned int i=0; i<ArrayLength(prefList); i++) {
- rv = branch->AddObserver(prefList[i], this, false);
- if (NS_FAILED(rv))
- rv2 = rv;
- }
- // Determine if we have a profile already
- // Install() is called *after* the profile-after-change notification
- // when there is only a single profile, or it is specified on the
- // commandline at startup.
- // In that case, we detect the presence of a profile by the existence
- // of the NS_APP_USER_PROFILE_50_DIR directory.
- nsCOMPtr<nsIFile> directory;
- rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
- getter_AddRefs(directory));
- if (NS_SUCCEEDED(rv))
- mHaveProfile = true;
- rv = ReadPrefs(branch);
- NS_ENSURE_SUCCESS(rv, rv);
- return rv2;
- }
- void
- nsCacheProfilePrefObserver::Remove()
- {
- // remove Observer Service observers
- nsCOMPtr<nsIObserverService> obs =
- mozilla::services::GetObserverService();
- if (obs) {
- for (unsigned int i=0; i<ArrayLength(observerList); i++) {
- obs->RemoveObserver(this, observerList[i]);
- }
- }
- // remove Pref Service observers
- nsCOMPtr<nsIPrefBranch> prefs =
- do_GetService(NS_PREFSERVICE_CONTRACTID);
- if (!prefs)
- return;
- for (unsigned int i=0; i<ArrayLength(prefList); i++)
- prefs->RemoveObserver(prefList[i], this); // remove cache pref observers
- }
- void
- nsCacheProfilePrefObserver::SetDiskCacheCapacity(int32_t capacity)
- {
- mDiskCacheCapacity = std::max(0, capacity);
- }
- NS_IMETHODIMP
- nsCacheProfilePrefObserver::Observe(nsISupports * subject,
- const char * topic,
- const char16_t * data_unicode)
- {
- nsresult rv;
- NS_ConvertUTF16toUTF8 data(data_unicode);
- CACHE_LOG_INFO(("Observe [topic=%s data=%s]\n", topic, data.get()));
- if (!nsCacheService::IsInitialized()) {
- if (!strcmp("resume_process_notification", topic)) {
- // A suspended process has a closed cache, so re-open it here.
- nsCacheService::GlobalInstance()->Init();
- }
- return NS_OK;
- }
- if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic)) {
- // xpcom going away, shutdown cache service
- nsCacheService::GlobalInstance()->Shutdown();
- } else if (!strcmp("profile-before-change", topic)) {
- // profile before change
- mHaveProfile = false;
- // XXX shutdown devices
- nsCacheService::OnProfileShutdown();
- } else if (!strcmp("suspend_process_notification", topic)) {
- // A suspended process may never return, so shutdown the cache to reduce
- // cache corruption.
- nsCacheService::GlobalInstance()->Shutdown();
- } else if (!strcmp("profile-do-change", topic)) {
- // profile after change
- mHaveProfile = true;
- nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
- if (!branch) {
- return NS_ERROR_FAILURE;
- }
- (void)ReadPrefs(branch);
- nsCacheService::OnProfileChanged();
- } else if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, topic)) {
- // ignore pref changes until we're done switch profiles
- if (!mHaveProfile)
- return NS_OK;
- nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(subject, &rv);
- if (NS_FAILED(rv))
- return rv;
- // which preference changed?
- if (!strcmp(DISK_CACHE_ENABLE_PREF, data.get())) {
- rv = branch->GetBoolPref(DISK_CACHE_ENABLE_PREF,
- &mDiskCacheEnabled);
- if (NS_FAILED(rv))
- return rv;
- nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
- } else if (!strcmp(DISK_CACHE_CAPACITY_PREF, data.get())) {
- int32_t capacity = 0;
- rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &capacity);
- if (NS_FAILED(rv))
- return rv;
- mDiskCacheCapacity = std::max(0, capacity);
- nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
-
- // Update the cache capacity when smart sizing is turned on/off
- } else if (!strcmp(DISK_CACHE_SMART_SIZE_ENABLED_PREF, data.get())) {
- // Is the update because smartsizing was turned on, or off?
- rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
- &mSmartSizeEnabled);
- if (NS_FAILED(rv))
- return rv;
- int32_t newCapacity = 0;
- if (mSmartSizeEnabled) {
- nsCacheService::SetDiskSmartSize();
- } else {
- // Smart sizing switched off: use user specified size
- rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &newCapacity);
- if (NS_FAILED(rv))
- return rv;
- mDiskCacheCapacity = std::max(0, newCapacity);
- nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
- }
- } else if (!strcmp(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, data.get())) {
- rv = branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
- &mShouldUseOldMaxSmartSize);
- if (NS_FAILED(rv))
- return rv;
- } else if (!strcmp(DISK_CACHE_MAX_ENTRY_SIZE_PREF, data.get())) {
- int32_t newMaxSize;
- rv = branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF,
- &newMaxSize);
- if (NS_FAILED(rv))
- return rv;
- mDiskCacheMaxEntrySize = std::max(-1, newMaxSize);
- nsCacheService::SetDiskCacheMaxEntrySize(mDiskCacheMaxEntrySize);
-
- #if 0
- } else if (!strcmp(DISK_CACHE_DIR_PREF, data.get())) {
- // XXX We probaby don't want to respond to this pref except after
- // XXX profile changes. Ideally, there should be somekind of user
- // XXX notification that the pref change won't take effect until
- // XXX the next time the profile changes (browser launch)
- #endif
- } else
- // which preference changed?
- if (!strcmp(OFFLINE_CACHE_ENABLE_PREF, data.get())) {
- rv = branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
- &mOfflineCacheEnabled);
- if (NS_FAILED(rv)) return rv;
- nsCacheService::SetOfflineCacheEnabled(OfflineCacheEnabled());
- } else if (!strcmp(OFFLINE_CACHE_CAPACITY_PREF, data.get())) {
- int32_t capacity = 0;
- rv = branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF, &capacity);
- if (NS_FAILED(rv)) return rv;
- mOfflineCacheCapacity = std::max(0, capacity);
- nsCacheService::SetOfflineCacheCapacity(mOfflineCacheCapacity);
- #if 0
- } else if (!strcmp(OFFLINE_CACHE_DIR_PREF, data.get())) {
- // XXX We probaby don't want to respond to this pref except after
- // XXX profile changes. Ideally, there should be some kind of user
- // XXX notification that the pref change won't take effect until
- // XXX the next time the profile changes (browser launch)
- #endif
- } else
- if (!strcmp(MEMORY_CACHE_ENABLE_PREF, data.get())) {
- rv = branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF,
- &mMemoryCacheEnabled);
- if (NS_FAILED(rv))
- return rv;
- nsCacheService::SetMemoryCache();
-
- } else if (!strcmp(MEMORY_CACHE_CAPACITY_PREF, data.get())) {
- mMemoryCacheCapacity = -1;
- (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,
- &mMemoryCacheCapacity);
- nsCacheService::SetMemoryCache();
- } else if (!strcmp(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF, data.get())) {
- int32_t newMaxSize;
- rv = branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
- &newMaxSize);
- if (NS_FAILED(rv))
- return rv;
-
- mMemoryCacheMaxEntrySize = std::max(-1, newMaxSize);
- nsCacheService::SetMemoryCacheMaxEntrySize(mMemoryCacheMaxEntrySize);
- } else if (!strcmp(CACHE_COMPRESSION_LEVEL_PREF, data.get())) {
- mCacheCompressionLevel = CACHE_COMPRESSION_LEVEL;
- (void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF,
- &mCacheCompressionLevel);
- mCacheCompressionLevel = std::max(0, mCacheCompressionLevel);
- mCacheCompressionLevel = std::min(9, mCacheCompressionLevel);
- } else if (!strcmp(SANITIZE_ON_SHUTDOWN_PREF, data.get())) {
- rv = branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF,
- &mSanitizeOnShutdown);
- if (NS_FAILED(rv))
- return rv;
- nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
- } else if (!strcmp(CLEAR_ON_SHUTDOWN_PREF, data.get())) {
- rv = branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF,
- &mClearCacheOnShutdown);
- if (NS_FAILED(rv))
- return rv;
- nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
- }
- } else if (!strcmp("last-pb-context-exited", topic)) {
- nsCacheService::LeavePrivateBrowsing();
- }
- return NS_OK;
- }
- // Returns default ("smart") size (in KB) of cache, given available disk space
- // (also in KB)
- static uint32_t
- SmartCacheSize(const uint32_t availKB, bool shouldUseOldMaxSmartSize)
- {
- uint32_t maxSize = shouldUseOldMaxSmartSize ? OLD_MAX_CACHE_SIZE : MAX_CACHE_SIZE;
- if (availKB > 100 * 1024 * 1024)
- return maxSize; // skip computing if we're over 100 GB
- // Grow/shrink in 10 MB units, deliberately, so that in the common case we
- // don't shrink cache and evict items every time we startup (it's important
- // that we don't slow down startup benchmarks).
- uint32_t sz10MBs = 0;
- uint32_t avail10MBs = availKB / (1024*10);
- // .5% of space above 25 GB
- if (avail10MBs > 2500) {
- sz10MBs += static_cast<uint32_t>((avail10MBs - 2500)*.005);
- avail10MBs = 2500;
- }
- // 1% of space between 7GB -> 25 GB
- if (avail10MBs > 700) {
- sz10MBs += static_cast<uint32_t>((avail10MBs - 700)*.01);
- avail10MBs = 700;
- }
- // 5% of space between 500 MB -> 7 GB
- if (avail10MBs > 50) {
- sz10MBs += static_cast<uint32_t>((avail10MBs - 50)*.05);
- avail10MBs = 50;
- }
- // 40% of space up to 500 MB (50 MB min)
- sz10MBs += std::max<uint32_t>(5, static_cast<uint32_t>(avail10MBs * .4));
- return std::min<uint32_t>(maxSize, sz10MBs * 10 * 1024);
- }
- /* Computes our best guess for the default size of the user's disk cache,
- * based on the amount of space they have free on their hard drive.
- * We use a tiered scheme: the more space available,
- * the larger the disk cache will be. However, we do not want
- * to enable the disk cache to grow to an unbounded size, so the larger the
- * user's available space is, the smaller of a percentage we take. We set a
- * lower bound of 50MB and an upper bound of 1GB.
- *
- *@param: None.
- *@return: The size that the user's disk cache should default to, in kBytes.
- */
- uint32_t
- nsCacheProfilePrefObserver::GetSmartCacheSize(const nsAString& cachePath,
- uint32_t currentSize,
- bool shouldUseOldMaxSmartSize)
- {
- // Check for free space on device where cache directory lives
- nsresult rv;
- nsCOMPtr<nsIFile>
- cacheDirectory (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
- if (NS_FAILED(rv) || !cacheDirectory)
- return DEFAULT_CACHE_SIZE;
- rv = cacheDirectory->InitWithPath(cachePath);
- if (NS_FAILED(rv))
- return DEFAULT_CACHE_SIZE;
- int64_t bytesAvailable;
- rv = cacheDirectory->GetDiskSpaceAvailable(&bytesAvailable);
- if (NS_FAILED(rv))
- return DEFAULT_CACHE_SIZE;
- return SmartCacheSize(static_cast<uint32_t>((bytesAvailable / 1024) +
- currentSize),
- shouldUseOldMaxSmartSize);
- }
- /* Determine if we are permitted to dynamically size the user's disk cache based
- * on their disk space available. We may do this so long as the pref
- * smart_size.enabled is true.
- */
- bool
- nsCacheProfilePrefObserver::PermittedToSmartSize(nsIPrefBranch* branch, bool
- firstRun)
- {
- nsresult rv;
- if (firstRun) {
- // check if user has set cache size in the past
- bool userSet;
- rv = branch->PrefHasUserValue(DISK_CACHE_CAPACITY_PREF, &userSet);
- if (NS_FAILED(rv)) userSet = true;
- if (userSet) {
- int32_t oldCapacity;
- // If user explicitly set cache size to be smaller than old default
- // of 50 MB, then keep user's value. Otherwise use smart sizing.
- rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &oldCapacity);
- if (oldCapacity < PRE_GECKO_2_0_DEFAULT_CACHE_SIZE) {
- mSmartSizeEnabled = false;
- branch->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
- mSmartSizeEnabled);
- return mSmartSizeEnabled;
- }
- }
- // Set manual setting to MAX cache size as starting val for any
- // adjustment by user: (bug 559942 comment 65)
- int32_t maxSize = mShouldUseOldMaxSmartSize ? OLD_MAX_CACHE_SIZE : MAX_CACHE_SIZE;
- branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, maxSize);
- }
- rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
- &mSmartSizeEnabled);
- if (NS_FAILED(rv))
- mSmartSizeEnabled = false;
- return mSmartSizeEnabled;
- }
- nsresult
- nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch* branch)
- {
- nsresult rv = NS_OK;
- // read disk cache device prefs
- mDiskCacheEnabled = true; // presume disk cache is enabled
- (void) branch->GetBoolPref(DISK_CACHE_ENABLE_PREF, &mDiskCacheEnabled);
- mDiskCacheCapacity = DISK_CACHE_CAPACITY;
- (void)branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &mDiskCacheCapacity);
- mDiskCacheCapacity = std::max(0, mDiskCacheCapacity);
- (void) branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF,
- &mDiskCacheMaxEntrySize);
- mDiskCacheMaxEntrySize = std::max(-1, mDiskCacheMaxEntrySize);
-
- (void) branch->GetComplexValue(DISK_CACHE_DIR_PREF, // ignore error
- NS_GET_IID(nsIFile),
- getter_AddRefs(mDiskCacheParentDirectory));
- (void) branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
- &mShouldUseOldMaxSmartSize);
-
- if (!mDiskCacheParentDirectory) {
- nsCOMPtr<nsIFile> directory;
- // try to get the disk cache parent directory
- rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
- getter_AddRefs(directory));
- if (NS_FAILED(rv)) {
- // try to get the profile directory (there may not be a profile yet)
- nsCOMPtr<nsIFile> profDir;
- NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
- getter_AddRefs(profDir));
- NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
- getter_AddRefs(directory));
- if (!directory)
- directory = profDir;
- else if (profDir) {
- nsCacheService::MoveOrRemoveDiskCache(profDir, directory,
- "Cache");
- }
- }
- // use file cache in build tree only if asked, to avoid cache dir litter
- if (!directory && PR_GetEnv("NECKO_DEV_ENABLE_DISK_CACHE")) {
- rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
- getter_AddRefs(directory));
- }
- if (directory)
- mDiskCacheParentDirectory = do_QueryInterface(directory, &rv);
- }
- if (mDiskCacheParentDirectory) {
- bool firstSmartSizeRun;
- rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,
- &firstSmartSizeRun);
- if (NS_FAILED(rv))
- firstSmartSizeRun = false;
- if (PermittedToSmartSize(branch, firstSmartSizeRun)) {
- // Avoid evictions: use previous cache size until smart size event
- // updates mDiskCacheCapacity
- rv = branch->GetIntPref(firstSmartSizeRun ?
- DISK_CACHE_CAPACITY_PREF :
- DISK_CACHE_SMART_SIZE_PREF,
- &mDiskCacheCapacity);
- if (NS_FAILED(rv))
- mDiskCacheCapacity = DEFAULT_CACHE_SIZE;
- }
- if (firstSmartSizeRun) {
- // It is no longer our first run
- rv = branch->SetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,
- false);
- if (NS_FAILED(rv))
- NS_WARNING("Failed setting first_run pref in ReadPrefs.");
- }
- }
- // read offline cache device prefs
- mOfflineCacheEnabled = true; // presume offline cache is enabled
- (void) branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
- &mOfflineCacheEnabled);
- mOfflineCacheCapacity = OFFLINE_CACHE_CAPACITY;
- (void)branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF,
- &mOfflineCacheCapacity);
- mOfflineCacheCapacity = std::max(0, mOfflineCacheCapacity);
- (void) branch->GetComplexValue(OFFLINE_CACHE_DIR_PREF, // ignore error
- NS_GET_IID(nsIFile),
- getter_AddRefs(mOfflineCacheParentDirectory));
- if (!mOfflineCacheParentDirectory) {
- nsCOMPtr<nsIFile> directory;
- // try to get the offline cache parent directory
- rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
- getter_AddRefs(directory));
- if (NS_FAILED(rv)) {
- // try to get the profile directory (there may not be a profile yet)
- nsCOMPtr<nsIFile> profDir;
- NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
- getter_AddRefs(profDir));
- NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
- getter_AddRefs(directory));
- if (!directory)
- directory = profDir;
- else if (profDir) {
- nsCacheService::MoveOrRemoveDiskCache(profDir, directory,
- "OfflineCache");
- }
- }
- #if DEBUG
- if (!directory) {
- // use current process directory during development
- rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
- getter_AddRefs(directory));
- }
- #endif
- if (directory)
- mOfflineCacheParentDirectory = do_QueryInterface(directory, &rv);
- }
- // read memory cache device prefs
- (void) branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF, &mMemoryCacheEnabled);
- mMemoryCacheCapacity = -1;
- (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,
- &mMemoryCacheCapacity);
- (void) branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
- &mMemoryCacheMaxEntrySize);
- mMemoryCacheMaxEntrySize = std::max(-1, mMemoryCacheMaxEntrySize);
- // read cache compression level pref
- mCacheCompressionLevel = CACHE_COMPRESSION_LEVEL;
- (void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF,
- &mCacheCompressionLevel);
- mCacheCompressionLevel = std::max(0, mCacheCompressionLevel);
- mCacheCompressionLevel = std::min(9, mCacheCompressionLevel);
- // read cache shutdown sanitization prefs
- (void) branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF,
- &mSanitizeOnShutdown);
- (void) branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF,
- &mClearCacheOnShutdown);
- return rv;
- }
- nsresult
- nsCacheService::DispatchToCacheIOThread(nsIRunnable* event)
- {
- if (!gService || !gService->mCacheIOThread) return NS_ERROR_NOT_AVAILABLE;
- return gService->mCacheIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
- }
- nsresult
- nsCacheService::SyncWithCacheIOThread()
- {
- if (!gService || !gService->mCacheIOThread) return NS_ERROR_NOT_AVAILABLE;
- gService->mLock.AssertCurrentThreadOwns();
- nsCOMPtr<nsIRunnable> event = new nsBlockOnCacheThreadEvent();
- // dispatch event - it will notify the monitor when it's done
- nsresult rv =
- gService->mCacheIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
- if (NS_FAILED(rv)) {
- NS_WARNING("Failed dispatching block-event");
- return NS_ERROR_UNEXPECTED;
- }
- // wait until notified, then return
- gService->mNotified = false;
- while (!gService->mNotified) {
- gService->mCondVar.Wait();
- }
- return NS_OK;
- }
- bool
- nsCacheProfilePrefObserver::DiskCacheEnabled()
- {
- if ((mDiskCacheCapacity == 0) || (!mDiskCacheParentDirectory)) return false;
- return mDiskCacheEnabled && (!mSanitizeOnShutdown || !mClearCacheOnShutdown);
- }
- bool
- nsCacheProfilePrefObserver::OfflineCacheEnabled()
- {
- if ((mOfflineCacheCapacity == 0) || (!mOfflineCacheParentDirectory))
- return false;
- return mOfflineCacheEnabled;
- }
- bool
- nsCacheProfilePrefObserver::MemoryCacheEnabled()
- {
- if (mMemoryCacheCapacity == 0) return false;
- return mMemoryCacheEnabled;
- }
- /**
- * MemoryCacheCapacity
- *
- * If the browser.cache.memory.capacity preference is positive, we use that
- * value for the amount of memory available for the cache.
- *
- * If browser.cache.memory.capacity is zero, the memory cache is disabled.
- *
- * If browser.cache.memory.capacity is negative or not present, we use a
- * formula that grows less than linearly with the amount of system memory,
- * with an upper limit on the cache size. No matter how much physical RAM is
- * present, the default cache size would not exceed 32 MB. This maximum would
- * apply only to systems with more than 4 GB of RAM (e.g. terminal servers)
- *
- * RAM Cache
- * --- -----
- * 32 Mb 2 Mb
- * 64 Mb 4 Mb
- * 128 Mb 6 Mb
- * 256 Mb 10 Mb
- * 512 Mb 14 Mb
- * 1024 Mb 18 Mb
- * 2048 Mb 24 Mb
- * 4096 Mb 30 Mb
- *
- * The equation for this is (for cache size C and memory size K (kbytes)):
- * x = log2(K) - 14
- * C = x^2/3 + x + 2/3 + 0.1 (0.1 for rounding)
- * if (C > 32) C = 32
- */
- int32_t
- nsCacheProfilePrefObserver::MemoryCacheCapacity()
- {
- int32_t capacity = mMemoryCacheCapacity;
- if (capacity >= 0) {
- CACHE_LOG_DEBUG(("Memory cache capacity forced to %d\n", capacity));
- return capacity;
- }
- static uint64_t bytes = PR_GetPhysicalMemorySize();
- CACHE_LOG_DEBUG(("Physical Memory size is %llu\n", bytes));
- // If getting the physical memory failed, arbitrarily assume
- // 32 MB of RAM. We use a low default to have a reasonable
- // size on all the devices we support.
- if (bytes == 0)
- bytes = 32 * 1024 * 1024;
- // Conversion from unsigned int64_t to double doesn't work on all platforms.
- // We need to truncate the value at INT64_MAX to make sure we don't
- // overflow.
- if (bytes > INT64_MAX)
- bytes = INT64_MAX;
- uint64_t kbytes = bytes >> 10;
- double kBytesD = double(kbytes);
- double x = log(kBytesD)/log(2.0) - 14;
- if (x > 0) {
- capacity = (int32_t)(x * x / 3.0 + x + 2.0 / 3 + 0.1); // 0.1 for rounding
- if (capacity > 32)
- capacity = 32;
- capacity *= 1024;
- } else {
- capacity = 0;
- }
- return capacity;
- }
- int32_t
- nsCacheProfilePrefObserver::CacheCompressionLevel()
- {
- return mCacheCompressionLevel;
- }
- /******************************************************************************
- * nsProcessRequestEvent
- *****************************************************************************/
- class nsProcessRequestEvent : public Runnable {
- public:
- explicit nsProcessRequestEvent(nsCacheRequest *aRequest)
- {
- mRequest = aRequest;
- }
- NS_IMETHOD Run() override
- {
- nsresult rv;
- NS_ASSERTION(mRequest->mListener,
- "Sync OpenCacheEntry() posted to background thread!");
- nsCacheServiceAutoLock lock;
- rv = nsCacheService::gService->ProcessRequest(mRequest,
- false,
- nullptr);
- // Don't delete the request if it was queued
- if (!(mRequest->IsBlocking() &&
- rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION))
- delete mRequest;
- return NS_OK;
- }
- protected:
- virtual ~nsProcessRequestEvent() {}
- private:
- nsCacheRequest *mRequest;
- };
- /******************************************************************************
- * nsDoomEvent
- *****************************************************************************/
- class nsDoomEvent : public Runnable {
- public:
- nsDoomEvent(nsCacheSession *session,
- const nsACString &key,
- nsICacheListener *listener)
- {
- mKey = *session->ClientID();
- mKey.Append(':');
- mKey.Append(key);
- mStoragePolicy = session->StoragePolicy();
- mListener = listener;
- mThread = do_GetCurrentThread();
- // We addref the listener here and release it in nsNotifyDoomListener
- // on the callers thread. If posting of nsNotifyDoomListener event fails
- // we leak the listener which is better than releasing it on a wrong
- // thread.
- NS_IF_ADDREF(mListener);
- }
- NS_IMETHOD Run() override
- {
- nsCacheServiceAutoLock lock;
- bool foundActive = true;
- nsresult status = NS_ERROR_NOT_AVAILABLE;
- nsCacheEntry *entry;
- entry = nsCacheService::gService->mActiveEntries.GetEntry(&mKey);
- if (!entry) {
- bool collision = false;
- foundActive = false;
- entry = nsCacheService::gService->SearchCacheDevices(&mKey,
- mStoragePolicy,
- &collision);
- }
- if (entry) {
- status = NS_OK;
- nsCacheService::gService->DoomEntry_Internal(entry, foundActive);
- }
- if (mListener) {
- mThread->Dispatch(new nsNotifyDoomListener(mListener, status),
- NS_DISPATCH_NORMAL);
- // posted event will release the reference on the correct thread
- mListener = nullptr;
- }
- return NS_OK;
- }
- private:
- nsCString mKey;
- nsCacheStoragePolicy mStoragePolicy;
- nsICacheListener *mListener;
- nsCOMPtr<nsIThread> mThread;
- };
- /******************************************************************************
- * nsCacheService
- *****************************************************************************/
- nsCacheService * nsCacheService::gService = nullptr;
- NS_IMPL_ISUPPORTS(nsCacheService, nsICacheService, nsICacheServiceInternal,
- nsIMemoryReporter)
- nsCacheService::nsCacheService()
- : mObserver(nullptr),
- mLock("nsCacheService.mLock"),
- mCondVar(mLock, "nsCacheService.mCondVar"),
- mNotified(false),
- mTimeStampLock("nsCacheService.mTimeStampLock"),
- mInitialized(false),
- mClearingEntries(false),
- mEnableMemoryDevice(true),
- mEnableDiskDevice(true),
- mMemoryDevice(nullptr),
- mDiskDevice(nullptr),
- mOfflineDevice(nullptr),
- mTotalEntries(0),
- mCacheHits(0),
- mCacheMisses(0),
- mMaxKeyLength(0),
- mMaxDataSize(0),
- mMaxMetaSize(0),
- mDeactivateFailures(0),
- mDeactivatedUnboundEntries(0)
- {
- NS_ASSERTION(gService==nullptr, "multiple nsCacheService instances!");
- gService = this;
- // create list of cache devices
- PR_INIT_CLIST(&mDoomedEntries);
- }
- nsCacheService::~nsCacheService()
- {
- if (mInitialized) // Shutdown hasn't been called yet.
- (void) Shutdown();
- if (mObserver) {
- mObserver->Remove();
- NS_RELEASE(mObserver);
- }
- gService = nullptr;
- }
- nsresult
- nsCacheService::Init()
- {
- // Thie method must be called on the main thread because mCacheIOThread must
- // only be modified on the main thread.
- if (!NS_IsMainThread()) {
- NS_ERROR("nsCacheService::Init called off the main thread");
- return NS_ERROR_NOT_SAME_THREAD;
- }
- NS_ASSERTION(!mInitialized, "nsCacheService already initialized.");
- if (mInitialized)
- return NS_ERROR_ALREADY_INITIALIZED;
- if (mozilla::net::IsNeckoChild()) {
- return NS_ERROR_UNEXPECTED;
- }
- nsresult rv;
- mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = NS_NewNamedThread("Cache I/O",
- getter_AddRefs(mCacheIOThread));
- if (NS_FAILED(rv)) {
- NS_RUNTIMEABORT("Can't create cache IO thread");
- }
- rv = nsDeleteDir::Init();
- if (NS_FAILED(rv)) {
- NS_WARNING("Can't initialize nsDeleteDir");
- }
- // initialize hashtable for active cache entries
- mActiveEntries.Init();
- // create profile/preference observer
- if (!mObserver) {
- mObserver = new nsCacheProfilePrefObserver();
- NS_ADDREF(mObserver);
- mObserver->Install();
- }
- mEnableDiskDevice = mObserver->DiskCacheEnabled();
- mEnableOfflineDevice = mObserver->OfflineCacheEnabled();
- mEnableMemoryDevice = mObserver->MemoryCacheEnabled();
- RegisterWeakMemoryReporter(this);
- mInitialized = true;
- return NS_OK;
- }
- void
- nsCacheService::Shutdown()
- {
- // This method must be called on the main thread because mCacheIOThread must
- // only be modified on the main thread.
- if (!NS_IsMainThread()) {
- NS_RUNTIMEABORT("nsCacheService::Shutdown called off the main thread");
- }
- nsCOMPtr<nsIThread> cacheIOThread;
- bool shouldSanitize = false;
- nsCOMPtr<nsIFile> parentDir;
- {
- nsCacheServiceAutoLock lock;
- NS_ASSERTION(mInitialized,
- "can't shutdown nsCacheService unless it has been initialized.");
- if (!mInitialized)
- return;
- mClearingEntries = true;
- DoomActiveEntries(nullptr);
- }
- CloseAllStreams();
- UnregisterWeakMemoryReporter(this);
- {
- nsCacheServiceAutoLock lock;
- NS_ASSERTION(mInitialized, "Bad state");
- mInitialized = false;
- // Clear entries
- ClearDoomList();
- if (mSmartSizeTimer) {
- mSmartSizeTimer->Cancel();
- mSmartSizeTimer = nullptr;
- }
- // Make sure to wait for any pending cache-operations before
- // proceeding with destructive actions (bug #620660)
- (void) SyncWithCacheIOThread();
- mActiveEntries.Shutdown();
- // obtain the disk cache directory in case we need to sanitize it
- parentDir = mObserver->DiskCacheParentDirectory();
- shouldSanitize = mObserver->SanitizeAtShutdown();
- // deallocate memory and disk caches
- delete mMemoryDevice;
- mMemoryDevice = nullptr;
- delete mDiskDevice;
- mDiskDevice = nullptr;
- if (mOfflineDevice)
- mOfflineDevice->Shutdown();
- NS_IF_RELEASE(mOfflineDevice);
- for (auto iter = mCustomOfflineDevices.Iter();
- !iter.Done(); iter.Next()) {
- iter.Data()->Shutdown();
- iter.Remove();
- }
- LogCacheStatistics();
- mClearingEntries = false;
- mCacheIOThread.swap(cacheIOThread);
- }
- if (cacheIOThread)
- nsShutdownThread::BlockingShutdown(cacheIOThread);
- if (shouldSanitize) {
- nsresult rv = parentDir->AppendNative(NS_LITERAL_CSTRING("Cache"));
- if (NS_SUCCEEDED(rv)) {
- bool exists;
- if (NS_SUCCEEDED(parentDir->Exists(&exists)) && exists)
- nsDeleteDir::DeleteDir(parentDir, false);
- }
- nsDeleteDir::Shutdown(shouldSanitize);
- } else {
- nsDeleteDir::Shutdown(shouldSanitize);
- }
- }
- nsresult
- nsCacheService::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
- {
- nsresult rv;
- if (aOuter != nullptr)
- return NS_ERROR_NO_AGGREGATION;
- nsCacheService * cacheService = new nsCacheService();
- if (cacheService == nullptr)
- return NS_ERROR_OUT_OF_MEMORY;
- NS_ADDREF(cacheService);
- rv = cacheService->Init();
- if (NS_SUCCEEDED(rv)) {
- rv = cacheService->QueryInterface(aIID, aResult);
- }
- NS_RELEASE(cacheService);
- return rv;
- }
- NS_IMETHODIMP
- nsCacheService::CreateSession(const char * clientID,
- nsCacheStoragePolicy storagePolicy,
- bool streamBased,
- nsICacheSession **result)
- {
- *result = nullptr;
- if (net::CacheObserver::UseNewCache())
- return NS_ERROR_NOT_IMPLEMENTED;
- return CreateSessionInternal(clientID, storagePolicy, streamBased, result);
- }
- nsresult
- nsCacheService::CreateSessionInternal(const char * clientID,
- nsCacheStoragePolicy storagePolicy,
- bool streamBased,
- nsICacheSession **result)
- {
- RefPtr<nsCacheSession> session =
- new nsCacheSession(clientID, storagePolicy, streamBased);
- session.forget(result);
- return NS_OK;
- }
- nsresult
- nsCacheService::EvictEntriesForSession(nsCacheSession * session)
- {
- NS_ASSERTION(gService, "nsCacheService::gService is null.");
- return gService->EvictEntriesForClient(session->ClientID()->get(),
- session->StoragePolicy());
- }
- namespace {
- class EvictionNotifierRunnable : public Runnable
- {
- public:
- explicit EvictionNotifierRunnable(nsISupports* aSubject)
- : mSubject(aSubject)
- { }
- NS_DECL_NSIRUNNABLE
- private:
- nsCOMPtr<nsISupports> mSubject;
- };
- NS_IMETHODIMP
- EvictionNotifierRunnable::Run()
- {
- nsCOMPtr<nsIObserverService> obsSvc =
- mozilla::services::GetObserverService();
- if (obsSvc) {
- obsSvc->NotifyObservers(mSubject,
- NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID,
- nullptr);
- }
- return NS_OK;
- }
- } // namespace
- nsresult
- nsCacheService::EvictEntriesForClient(const char * clientID,
- nsCacheStoragePolicy storagePolicy)
- {
- RefPtr<EvictionNotifierRunnable> r =
- new EvictionNotifierRunnable(NS_ISUPPORTS_CAST(nsICacheService*, this));
- NS_DispatchToMainThread(r);
- nsCacheServiceAutoLock lock;
- nsresult res = NS_OK;
- if (storagePolicy == nsICache::STORE_ANYWHERE ||
- storagePolicy == nsICache::STORE_ON_DISK) {
- if (mEnableDiskDevice) {
- nsresult rv = NS_OK;
- if (!mDiskDevice)
- rv = CreateDiskDevice();
- if (mDiskDevice)
- rv = mDiskDevice->EvictEntries(clientID);
- if (NS_FAILED(rv))
- res = rv;
- }
- }
- // Only clear the offline cache if it has been specifically asked for.
- if (storagePolicy == nsICache::STORE_OFFLINE) {
- if (mEnableOfflineDevice) {
- nsresult rv = NS_OK;
- if (!mOfflineDevice)
- rv = CreateOfflineDevice();
- if (mOfflineDevice)
- rv = mOfflineDevice->EvictEntries(clientID);
- if (NS_FAILED(rv))
- res = rv;
- }
- }
- if (storagePolicy == nsICache::STORE_ANYWHERE ||
- storagePolicy == nsICache::STORE_IN_MEMORY) {
- // If there is no memory device, there is no need to evict it...
- if (mMemoryDevice) {
- nsresult rv = mMemoryDevice->EvictEntries(clientID);
- if (NS_FAILED(rv))
- res = rv;
- }
- }
- return res;
- }
- nsresult
- nsCacheService::IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy,
- bool * result)
- {
- if (gService == nullptr) return NS_ERROR_NOT_AVAILABLE;
- nsCacheServiceAutoLock lock;
- *result = gService->IsStorageEnabledForPolicy_Locked(storagePolicy);
- return NS_OK;
- }
- nsresult
- nsCacheService::DoomEntry(nsCacheSession *session,
- const nsACString &key,
- nsICacheListener *listener)
- {
- CACHE_LOG_DEBUG(("Dooming entry for session %p, key %s\n",
- session, PromiseFlatCString(key).get()));
- if (!gService || !gService->mInitialized)
- return NS_ERROR_NOT_INITIALIZED;
- return DispatchToCacheIOThread(new nsDoomEvent(session, key, listener));
- }
- bool
- nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy storagePolicy)
- {
- if (gService->mEnableMemoryDevice &&
- (storagePolicy == nsICache::STORE_ANYWHERE ||
- storagePolicy == nsICache::STORE_IN_MEMORY)) {
- return true;
- }
- if (gService->mEnableDiskDevice &&
- (storagePolicy == nsICache::STORE_ANYWHERE ||
- storagePolicy == nsICache::STORE_ON_DISK)) {
- return true;
- }
- if (gService->mEnableOfflineDevice &&
- storagePolicy == nsICache::STORE_OFFLINE) {
- return true;
- }
-
- return false;
- }
- NS_IMETHODIMP nsCacheService::VisitEntries(nsICacheVisitor *visitor)
- {
- if (net::CacheObserver::UseNewCache())
- return NS_ERROR_NOT_IMPLEMENTED;
- return VisitEntriesInternal(visitor);
- }
- nsresult nsCacheService::VisitEntriesInternal(nsICacheVisitor *visitor)
- {
- NS_ENSURE_ARG_POINTER(visitor);
- nsCacheServiceAutoLock lock;
- if (!(mEnableDiskDevice || mEnableMemoryDevice))
- return NS_ERROR_NOT_AVAILABLE;
- // XXX record the fact that a visitation is in progress,
- // XXX i.e. keep list of visitors in progress.
-
- nsresult rv = NS_OK;
- // If there is no memory device, there are then also no entries to visit...
- if (mMemoryDevice) {
- rv = mMemoryDevice->Visit(visitor);
- if (NS_FAILED(rv)) return rv;
- }
- if (mEnableDiskDevice) {
- if (!mDiskDevice) {
- rv = CreateDiskDevice();
- if (NS_FAILED(rv)) return rv;
- }
- rv = mDiskDevice->Visit(visitor);
- if (NS_FAILED(rv)) return rv;
- }
- if (mEnableOfflineDevice) {
- if (!mOfflineDevice) {
- rv = CreateOfflineDevice();
- if (NS_FAILED(rv)) return rv;
- }
- rv = mOfflineDevice->Visit(visitor);
- if (NS_FAILED(rv)) return rv;
- }
- // XXX notify any shutdown process that visitation is complete for THIS visitor.
- // XXX keep queue of visitors
- return NS_OK;
- }
- void nsCacheService::FireClearNetworkCacheStoredAnywhereNotification()
- {
- MOZ_ASSERT(NS_IsMainThread());
- nsCOMPtr<nsIObserverService> obsvc = mozilla::services::GetObserverService();
- if (obsvc) {
- obsvc->NotifyObservers(nullptr,
- "network-clear-cache-stored-anywhere",
- nullptr);
- }
- }
- NS_IMETHODIMP nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy)
- {
- if (net::CacheObserver::UseNewCache())
- return NS_ERROR_NOT_IMPLEMENTED;
- return EvictEntriesInternal(storagePolicy);
- }
- nsresult nsCacheService::EvictEntriesInternal(nsCacheStoragePolicy storagePolicy)
- {
- if (storagePolicy == nsICache::STORE_ANYWHERE) {
- // if not called on main thread, dispatch the notification to the main thread to notify observers
- if (!NS_IsMainThread()) {
- nsCOMPtr<nsIRunnable> event = NewRunnableMethod(this,
- &nsCacheService::FireClearNetworkCacheStoredAnywhereNotification);
- NS_DispatchToMainThread(event);
- } else {
- // else you're already on main thread - notify observers
- FireClearNetworkCacheStoredAnywhereNotification();
- }
- }
- return EvictEntriesForClient(nullptr, storagePolicy);
- }
- NS_IMETHODIMP nsCacheService::GetCacheIOTarget(nsIEventTarget * *aCacheIOTarget)
- {
- NS_ENSURE_ARG_POINTER(aCacheIOTarget);
- // Because mCacheIOThread can only be changed on the main thread, it can be
- // read from the main thread without the lock. This is useful to prevent
- // blocking the main thread on other cache operations.
- if (!NS_IsMainThread()) {
- Lock();
- }
- nsresult rv;
- if (mCacheIOThread) {
- NS_ADDREF(*aCacheIOTarget = mCacheIOThread);
- rv = NS_OK;
- } else {
- *aCacheIOTarget = nullptr;
- rv = NS_ERROR_NOT_AVAILABLE;
- }
- if (!NS_IsMainThread()) {
- Unlock();
- }
- return rv;
- }
- NS_IMETHODIMP nsCacheService::GetLockHeldTime(double *aLockHeldTime)
- {
- MutexAutoLock lock(mTimeStampLock);
- if (mLockAcquiredTimeStamp.IsNull()) {
- *aLockHeldTime = 0.0;
- }
- else {
- *aLockHeldTime =
- (TimeStamp::Now() - mLockAcquiredTimeStamp).ToMilliseconds();
- }
- return NS_OK;
- }
- /**
- * Internal Methods
- */
- nsresult
- nsCacheService::CreateDiskDevice()
- {
- if (!mInitialized) return NS_ERROR_NOT_AVAILABLE;
- if (!mEnableDiskDevice) return NS_ERROR_NOT_AVAILABLE;
- if (mDiskDevice) return NS_OK;
- mDiskDevice = new nsDiskCacheDevice;
- if (!mDiskDevice) return NS_ERROR_OUT_OF_MEMORY;
- // set the preferences
- mDiskDevice->SetCacheParentDirectory(mObserver->DiskCacheParentDirectory());
- mDiskDevice->SetCapacity(mObserver->DiskCacheCapacity());
- mDiskDevice->SetMaxEntrySize(mObserver->DiskCacheMaxEntrySize());
- nsresult rv = mDiskDevice->Init();
- if (NS_FAILED(rv)) {
- #if DEBUG
- printf("###\n");
- printf("### mDiskDevice->Init() failed (0x%.8x)\n",
- static_cast<uint32_t>(rv));
- printf("### - disabling disk cache for this session.\n");
- printf("###\n");
- #endif
- mEnableDiskDevice = false;
- delete mDiskDevice;
- mDiskDevice = nullptr;
- return rv;
- }
- NS_ASSERTION(!mSmartSizeTimer, "Smartsize timer was already fired!");
- // Disk device is usually created during the startup. Delay smart size
- // calculation to avoid possible massive IO caused by eviction of entries
- // in case the new smart size is smaller than current cache usage.
- mSmartSizeTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
- if (NS_SUCCEEDED(rv)) {
- rv = mSmartSizeTimer->InitWithCallback(new nsSetDiskSmartSizeCallback(),
- 1000*60*3,
- nsITimer::TYPE_ONE_SHOT);
- if (NS_FAILED(rv)) {
- NS_WARNING("Failed to post smart size timer");
- mSmartSizeTimer = nullptr;
- }
- } else {
- NS_WARNING("Can't create smart size timer");
- }
- // Ignore state of the timer and return success since the purpose of the
- // method (create the disk-device) has been fulfilled
- return NS_OK;
- }
- // Runnable sent from cache thread to main thread
- class nsDisableOldMaxSmartSizePrefEvent: public Runnable
- {
- public:
- nsDisableOldMaxSmartSizePrefEvent() {}
- NS_IMETHOD Run() override
- {
- // Main thread may have already called nsCacheService::Shutdown
- if (!nsCacheService::IsInitialized())
- return NS_ERROR_NOT_AVAILABLE;
- nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
- if (!branch) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- nsresult rv = branch->SetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, false);
- if (NS_FAILED(rv)) {
- NS_WARNING("Failed to disable old max smart size");
- return rv;
- }
- // It is safe to call SetDiskSmartSize_Locked() without holding the lock
- // when we are on main thread and nsCacheService is initialized.
- nsCacheService::gService->SetDiskSmartSize_Locked();
- if (nsCacheService::gService->mObserver->PermittedToSmartSize(branch, false)) {
- rv = branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, MAX_CACHE_SIZE);
- if (NS_FAILED(rv)) {
- NS_WARNING("Failed to set cache capacity pref");
- }
- }
- return NS_OK;
- }
- };
- void
- nsCacheService::MarkStartingFresh()
- {
- if (!gService || !gService->mObserver->ShouldUseOldMaxSmartSize()) {
- // Already using new max, nothing to do here
- return;
- }
- gService->mObserver->SetUseNewMaxSmartSize(true);
- // We always dispatch an event here because we don't want to deal with lock
- // reentrance issues.
- NS_DispatchToMainThread(new nsDisableOldMaxSmartSizePrefEvent());
- }
- nsresult
- nsCacheService::GetOfflineDevice(nsOfflineCacheDevice **aDevice)
- {
- if (!mOfflineDevice) {
- nsresult rv = CreateOfflineDevice();
- NS_ENSURE_SUCCESS(rv, rv);
- }
- NS_ADDREF(*aDevice = mOfflineDevice);
- return NS_OK;
- }
- nsresult
- nsCacheService::GetCustomOfflineDevice(nsIFile *aProfileDir,
- int32_t aQuota,
- nsOfflineCacheDevice **aDevice)
- {
- nsresult rv;
- nsAutoString profilePath;
- rv = aProfileDir->GetPath(profilePath);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!mCustomOfflineDevices.Get(profilePath, aDevice)) {
- rv = CreateCustomOfflineDevice(aProfileDir, aQuota, aDevice);
- NS_ENSURE_SUCCESS(rv, rv);
- (*aDevice)->SetAutoShutdown();
- mCustomOfflineDevices.Put(profilePath, *aDevice);
- }
- return NS_OK;
- }
- nsresult
- nsCacheService::CreateOfflineDevice()
- {
- CACHE_LOG_INFO(("Creating default offline device"));
- if (mOfflineDevice) return NS_OK;
- if (!nsCacheService::IsInitialized()) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- nsresult rv = CreateCustomOfflineDevice(
- mObserver->OfflineCacheParentDirectory(),
- mObserver->OfflineCacheCapacity(),
- &mOfflineDevice);
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- nsresult
- nsCacheService::CreateCustomOfflineDevice(nsIFile *aProfileDir,
- int32_t aQuota,
- nsOfflineCacheDevice **aDevice)
- {
- NS_ENSURE_ARG(aProfileDir);
- if (MOZ_LOG_TEST(gCacheLog, LogLevel::Info)) {
- nsAutoCString profilePath;
- aProfileDir->GetNativePath(profilePath);
- CACHE_LOG_INFO(("Creating custom offline device, %s, %d",
- profilePath.BeginReading(), aQuota));
- }
- if (!mInitialized) return NS_ERROR_NOT_AVAILABLE;
- if (!mEnableOfflineDevice) return NS_ERROR_NOT_AVAILABLE;
- *aDevice = new nsOfflineCacheDevice;
- NS_ADDREF(*aDevice);
- // set the preferences
- (*aDevice)->SetCacheParentDirectory(aProfileDir);
- (*aDevice)->SetCapacity(aQuota);
- nsresult rv = (*aDevice)->InitWithSqlite(mStorageService);
- if (NS_FAILED(rv)) {
- CACHE_LOG_DEBUG(("OfflineDevice->InitWithSqlite() failed (0x%.8x)\n", rv));
- CACHE_LOG_DEBUG((" - disabling offline cache for this session.\n"));
- NS_RELEASE(*aDevice);
- }
- return rv;
- }
- nsresult
- nsCacheService::CreateMemoryDevice()
- {
- if (!mInitialized) return NS_ERROR_NOT_AVAILABLE;
- if (!mEnableMemoryDevice) return NS_ERROR_NOT_AVAILABLE;
- if (mMemoryDevice) return NS_OK;
- mMemoryDevice = new nsMemoryCacheDevice;
- if (!mMemoryDevice) return NS_ERROR_OUT_OF_MEMORY;
-
- // set preference
- int32_t capacity = mObserver->MemoryCacheCapacity();
- CACHE_LOG_DEBUG(("Creating memory device with capacity %d\n", capacity));
- mMemoryDevice->SetCapacity(capacity);
- mMemoryDevice->SetMaxEntrySize(mObserver->MemoryCacheMaxEntrySize());
- nsresult rv = mMemoryDevice->Init();
- if (NS_FAILED(rv)) {
- NS_WARNING("Initialization of Memory Cache failed.");
- delete mMemoryDevice;
- mMemoryDevice = nullptr;
- }
- return rv;
- }
- nsresult
- nsCacheService::RemoveCustomOfflineDevice(nsOfflineCacheDevice *aDevice)
- {
- nsCOMPtr<nsIFile> profileDir = aDevice->BaseDirectory();
- if (!profileDir)
- return NS_ERROR_UNEXPECTED;
- nsAutoString profilePath;
- nsresult rv = profileDir->GetPath(profilePath);
- NS_ENSURE_SUCCESS(rv, rv);
- mCustomOfflineDevices.Remove(profilePath);
- return NS_OK;
- }
- nsresult
- nsCacheService::CreateRequest(nsCacheSession * session,
- const nsACString & clientKey,
- nsCacheAccessMode accessRequested,
- bool blockingMode,
- nsICacheListener * listener,
- nsCacheRequest ** request)
- {
- NS_ASSERTION(request, "CreateRequest: request is null");
-
- nsAutoCString key(*session->ClientID());
- key.Append(':');
- key.Append(clientKey);
- if (mMaxKeyLength < key.Length()) mMaxKeyLength = key.Length();
- // create request
- *request = new nsCacheRequest(key, listener, accessRequested,
- blockingMode, session);
- if (!listener) return NS_OK; // we're sync, we're done.
- // get the request's thread
- (*request)->mThread = do_GetCurrentThread();
-
- return NS_OK;
- }
- class nsCacheListenerEvent : public Runnable
- {
- public:
- nsCacheListenerEvent(nsICacheListener *listener,
- nsICacheEntryDescriptor *descriptor,
- nsCacheAccessMode accessGranted,
- nsresult status)
- : mListener(listener) // transfers reference
- , mDescriptor(descriptor) // transfers reference (may be null)
- , mAccessGranted(accessGranted)
- , mStatus(status)
- {}
- NS_IMETHOD Run() override
- {
- mListener->OnCacheEntryAvailable(mDescriptor, mAccessGranted, mStatus);
- NS_RELEASE(mListener);
- NS_IF_RELEASE(mDescriptor);
- return NS_OK;
- }
- private:
- // We explicitly leak mListener or mDescriptor if Run is not called
- // because otherwise we cannot guarantee that they are destroyed on
- // the right thread.
- nsICacheListener *mListener;
- nsICacheEntryDescriptor *mDescriptor;
- nsCacheAccessMode mAccessGranted;
- nsresult mStatus;
- };
- nsresult
- nsCacheService::NotifyListener(nsCacheRequest * request,
- nsICacheEntryDescriptor * descriptor,
- nsCacheAccessMode accessGranted,
- nsresult status)
- {
- NS_ASSERTION(request->mThread, "no thread set in async request!");
- // Swap ownership, and release listener on target thread...
- nsICacheListener *listener = request->mListener;
- request->mListener = nullptr;
- nsCOMPtr<nsIRunnable> ev =
- new nsCacheListenerEvent(listener, descriptor,
- accessGranted, status);
- if (!ev) {
- // Better to leak listener and descriptor if we fail because we don't
- // want to destroy them inside the cache service lock or on potentially
- // the wrong thread.
- return NS_ERROR_OUT_OF_MEMORY;
- }
- return request->mThread->Dispatch(ev, NS_DISPATCH_NORMAL);
- }
- nsresult
- nsCacheService::ProcessRequest(nsCacheRequest * request,
- bool calledFromOpenCacheEntry,
- nsICacheEntryDescriptor ** result)
- {
- // !!! must be called with mLock held !!!
- nsresult rv;
- nsCacheEntry * entry = nullptr;
- nsCacheEntry * doomedEntry = nullptr;
- nsCacheAccessMode accessGranted = nsICache::ACCESS_NONE;
- if (result) *result = nullptr;
- while(1) { // Activate entry loop
- rv = ActivateEntry(request, &entry, &doomedEntry); // get the entry for this request
- if (NS_FAILED(rv)) break;
- while(1) { // Request Access loop
- NS_ASSERTION(entry, "no entry in Request Access loop!");
- // entry->RequestAccess queues request on entry
- rv = entry->RequestAccess(request, &accessGranted);
- if (rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION) break;
- if (request->IsBlocking()) {
- if (request->mListener) {
- // async exits - validate, doom, or close will resume
- return rv;
- }
- // XXX this is probably wrong...
- Unlock();
- rv = request->WaitForValidation();
- Lock();
- }
- PR_REMOVE_AND_INIT_LINK(request);
- if (NS_FAILED(rv)) break; // non-blocking mode returns WAIT_FOR_VALIDATION error
- // okay, we're ready to process this request, request access again
- }
- if (rv != NS_ERROR_CACHE_ENTRY_DOOMED) break;
- if (entry->IsNotInUse()) {
- // this request was the last one keeping it around, so get rid of it
- DeactivateEntry(entry);
- }
- // loop back around to look for another entry
- }
- if (NS_SUCCEEDED(rv) && request->mProfileDir) {
- // Custom cache directory has been demanded. Preset the cache device.
- if (entry->StoragePolicy() != nsICache::STORE_OFFLINE) {
- // Failsafe check: this is implemented only for offline cache atm.
- rv = NS_ERROR_FAILURE;
- } else {
- RefPtr<nsOfflineCacheDevice> customCacheDevice;
- rv = GetCustomOfflineDevice(request->mProfileDir, -1,
- getter_AddRefs(customCacheDevice));
- if (NS_SUCCEEDED(rv))
- entry->SetCustomCacheDevice(customCacheDevice);
- }
- }
- nsICacheEntryDescriptor *descriptor = nullptr;
-
- if (NS_SUCCEEDED(rv))
- rv = entry->CreateDescriptor(request, accessGranted, &descriptor);
- // If doomedEntry is set, ActivatEntry() doomed an existing entry and
- // created a new one for that cache-key. However, any pending requests
- // on the doomed entry were not processed and we need to do that here.
- // This must be done after adding the created entry to list of active
- // entries (which is done in ActivateEntry()) otherwise the hashkeys crash
- // (see bug ##561313). It is also important to do this after creating a
- // descriptor for this request, or some other request may end up being
- // executed first for the newly created entry.
- // Finally, it is worth to emphasize that if doomedEntry is set,
- // ActivateEntry() created a new entry for the request, which will be
- // initialized by RequestAccess() and they both should have returned NS_OK.
- if (doomedEntry) {
- (void) ProcessPendingRequests(doomedEntry);
- if (doomedEntry->IsNotInUse())
- DeactivateEntry(doomedEntry);
- doomedEntry = nullptr;
- }
- if (request->mListener) { // Asynchronous
-
- if (NS_FAILED(rv) && calledFromOpenCacheEntry && request->IsBlocking())
- return rv; // skip notifying listener, just return rv to caller
-
- // call listener to report error or descriptor
- nsresult rv2 = NotifyListener(request, descriptor, accessGranted, rv);
- if (NS_FAILED(rv2) && NS_SUCCEEDED(rv)) {
- rv = rv2; // trigger delete request
- }
- } else { // Synchronous
- *result = descriptor;
- }
- return rv;
- }
- nsresult
- nsCacheService::OpenCacheEntry(nsCacheSession * session,
- const nsACString & key,
- nsCacheAccessMode accessRequested,
- bool blockingMode,
- nsICacheListener * listener,
- nsICacheEntryDescriptor ** result)
- {
- CACHE_LOG_DEBUG(("Opening entry for session %p, key %s, mode %d, blocking %d\n",
- session, PromiseFlatCString(key).get(), accessRequested,
- blockingMode));
- if (result)
- *result = nullptr;
- if (!gService || !gService->mInitialized)
- return NS_ERROR_NOT_INITIALIZED;
- nsCacheRequest * request = nullptr;
- nsresult rv = gService->CreateRequest(session,
- key,
- accessRequested,
- blockingMode,
- listener,
- &request);
- if (NS_FAILED(rv)) return rv;
- CACHE_LOG_DEBUG(("Created request %p\n", request));
- // Process the request on the background thread if we are on the main thread
- // and the the request is asynchronous
- if (NS_IsMainThread() && listener && gService->mCacheIOThread) {
- nsCOMPtr<nsIRunnable> ev =
- new nsProcessRequestEvent(request);
- rv = DispatchToCacheIOThread(ev);
- // delete request if we didn't post the event
- if (NS_FAILED(rv))
- delete request;
- }
- else {
- nsCacheServiceAutoLock lock;
- rv = gService->ProcessRequest(request, true, result);
- // delete requests that have completed
- if (!(listener && blockingMode &&
- (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)))
- delete request;
- }
- return rv;
- }
- nsresult
- nsCacheService::ActivateEntry(nsCacheRequest * request,
- nsCacheEntry ** result,
- nsCacheEntry ** doomedEntry)
- {
- CACHE_LOG_DEBUG(("Activate entry for request %p\n", request));
- if (!mInitialized || mClearingEntries)
- return NS_ERROR_NOT_AVAILABLE;
- nsresult rv = NS_OK;
- NS_ASSERTION(request != nullptr, "ActivateEntry called with no request");
- if (result) *result = nullptr;
- if (doomedEntry) *doomedEntry = nullptr;
- if ((!request) || (!result) || (!doomedEntry))
- return NS_ERROR_NULL_POINTER;
- // check if the request can be satisfied
- if (!mEnableMemoryDevice && !request->IsStreamBased())
- return NS_ERROR_FAILURE;
- if (!IsStorageEnabledForPolicy_Locked(request->StoragePolicy()))
- return NS_ERROR_FAILURE;
- // search active entries (including those not bound to device)
- nsCacheEntry *entry = mActiveEntries.GetEntry(&(request->mKey));
- CACHE_LOG_DEBUG(("Active entry for request %p is %p\n", request, entry));
- if (!entry) {
- // search cache devices for entry
- bool collision = false;
- entry = SearchCacheDevices(&(request->mKey), request->StoragePolicy(), &collision);
- CACHE_LOG_DEBUG(("Device search for request %p returned %p\n",
- request, entry));
- // When there is a hashkey collision just refuse to cache it...
- if (collision) return NS_ERROR_CACHE_IN_USE;
- if (entry) entry->MarkInitialized();
- } else {
- NS_ASSERTION(entry->IsActive(), "Inactive entry found in mActiveEntries!");
- }
- if (entry) {
- ++mCacheHits;
- entry->Fetched();
- } else {
- ++mCacheMisses;
- }
- if (entry &&
- ((request->AccessRequested() == nsICache::ACCESS_WRITE) ||
- ((request->StoragePolicy() != nsICache::STORE_OFFLINE) &&
- (entry->mExpirationTime <= SecondsFromPRTime(PR_Now()) &&
- request->WillDoomEntriesIfExpired()))))
- {
- // this is FORCE-WRITE request or the entry has expired
- // we doom entry without processing pending requests, but store it in
- // doomedEntry which causes pending requests to be processed below
- rv = DoomEntry_Internal(entry, false);
- *doomedEntry = entry;
- if (NS_FAILED(rv)) {
- // XXX what to do? Increment FailedDooms counter?
- }
- entry = nullptr;
- }
- if (!entry) {
- if (! (request->AccessRequested() & nsICache::ACCESS_WRITE)) {
- // this is a READ-ONLY request
- rv = NS_ERROR_CACHE_KEY_NOT_FOUND;
- goto error;
- }
- entry = new nsCacheEntry(request->mKey,
- request->IsStreamBased(),
- request->StoragePolicy());
- if (!entry)
- return NS_ERROR_OUT_OF_MEMORY;
- if (request->IsPrivate())
- entry->MarkPrivate();
-
- entry->Fetched();
- ++mTotalEntries;
- // XXX we could perform an early bind in some cases based on storage policy
- }
- if (!entry->IsActive()) {
- rv = mActiveEntries.AddEntry(entry);
- if (NS_FAILED(rv)) goto error;
- CACHE_LOG_DEBUG(("Added entry %p to mActiveEntries\n", entry));
- entry->MarkActive(); // mark entry active, because it's now in mActiveEntries
- }
- *result = entry;
- return NS_OK;
-
- error:
- *result = nullptr;
- delete entry;
- return rv;
- }
- nsCacheEntry *
- nsCacheService::SearchCacheDevices(nsCString * key, nsCacheStoragePolicy policy, bool *collision)
- {
- nsCacheEntry * entry = nullptr;
- CACHE_LOG_DEBUG(("mMemoryDevice: 0x%p\n", mMemoryDevice));
- *collision = false;
- if ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_IN_MEMORY)) {
- // If there is no memory device, then there is nothing to search...
- if (mMemoryDevice) {
- entry = mMemoryDevice->FindEntry(key, collision);
- CACHE_LOG_DEBUG(("Searching mMemoryDevice for key %s found: 0x%p, "
- "collision: %d\n", key->get(), entry, collision));
- }
- }
- if (!entry &&
- ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_ON_DISK))) {
- if (mEnableDiskDevice) {
- if (!mDiskDevice) {
- nsresult rv = CreateDiskDevice();
- if (NS_FAILED(rv))
- return nullptr;
- }
-
- entry = mDiskDevice->FindEntry(key, collision);
- }
- }
- if (!entry && (policy == nsICache::STORE_OFFLINE ||
- (policy == nsICache::STORE_ANYWHERE &&
- gIOService->IsOffline()))) {
- if (mEnableOfflineDevice) {
- if (!mOfflineDevice) {
- nsresult rv = CreateOfflineDevice();
- if (NS_FAILED(rv))
- return nullptr;
- }
- entry = mOfflineDevice->FindEntry(key, collision);
- }
- }
- return entry;
- }
- nsCacheDevice *
- nsCacheService::EnsureEntryHasDevice(nsCacheEntry * entry)
- {
- nsCacheDevice * device = entry->CacheDevice();
- // return device if found, possibly null if the entry is doomed i.e prevent
- // doomed entries to bind to a device (see e.g. bugs #548406 and #596443)
- if (device || entry->IsDoomed()) return device;
- int64_t predictedDataSize = entry->PredictedDataSize();
- if (entry->IsStreamData() && entry->IsAllowedOnDisk() && mEnableDiskDevice) {
- // this is the default
- if (!mDiskDevice) {
- (void)CreateDiskDevice(); // ignore the error (check for mDiskDevice instead)
- }
- if (mDiskDevice) {
- // Bypass the cache if Content-Length says the entry will be too big
- if (predictedDataSize != -1 &&
- mDiskDevice->EntryIsTooBig(predictedDataSize)) {
- DebugOnly<nsresult> rv = nsCacheService::DoomEntry(entry);
- NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
- return nullptr;
- }
- entry->MarkBinding(); // enter state of binding
- nsresult rv = mDiskDevice->BindEntry(entry);
- entry->ClearBinding(); // exit state of binding
- if (NS_SUCCEEDED(rv))
- device = mDiskDevice;
- }
- }
- // if we can't use mDiskDevice, try mMemoryDevice
- if (!device && mEnableMemoryDevice && entry->IsAllowedInMemory()) {
- if (!mMemoryDevice) {
- (void)CreateMemoryDevice(); // ignore the error (check for mMemoryDevice instead)
- }
- if (mMemoryDevice) {
- // Bypass the cache if Content-Length says entry will be too big
- if (predictedDataSize != -1 &&
- mMemoryDevice->EntryIsTooBig(predictedDataSize)) {
- DebugOnly<nsresult> rv = nsCacheService::DoomEntry(entry);
- NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
- return nullptr;
- }
- entry->MarkBinding(); // enter state of binding
- nsresult rv = mMemoryDevice->BindEntry(entry);
- entry->ClearBinding(); // exit state of binding
- if (NS_SUCCEEDED(rv))
- device = mMemoryDevice;
- }
- }
- if (!device && entry->IsStreamData() &&
- entry->IsAllowedOffline() && mEnableOfflineDevice) {
- if (!mOfflineDevice) {
- (void)CreateOfflineDevice(); // ignore the error (check for mOfflineDevice instead)
- }
- device = entry->CustomCacheDevice()
- ? entry->CustomCacheDevice()
- : mOfflineDevice;
- if (device) {
- entry->MarkBinding();
- nsresult rv = device->BindEntry(entry);
- entry->ClearBinding();
- if (NS_FAILED(rv))
- device = nullptr;
- }
- }
- if (device)
- entry->SetCacheDevice(device);
- return device;
- }
- nsresult
- nsCacheService::DoomEntry(nsCacheEntry * entry)
- {
- return gService->DoomEntry_Internal(entry, true);
- }
- nsresult
- nsCacheService::DoomEntry_Internal(nsCacheEntry * entry,
- bool doProcessPendingRequests)
- {
- if (entry->IsDoomed()) return NS_OK;
-
- CACHE_LOG_DEBUG(("Dooming entry %p\n", entry));
- nsresult rv = NS_OK;
- entry->MarkDoomed();
-
- NS_ASSERTION(!entry->IsBinding(), "Dooming entry while binding device.");
- nsCacheDevice * device = entry->CacheDevice();
- if (device) device->DoomEntry(entry);
- if (entry->IsActive()) {
- // remove from active entries
- mActiveEntries.RemoveEntry(entry);
- CACHE_LOG_DEBUG(("Removed entry %p from mActiveEntries\n", entry));
- entry->MarkInactive();
- }
- // put on doom list to wait for descriptors to close
- NS_ASSERTION(PR_CLIST_IS_EMPTY(entry), "doomed entry still on device list");
- PR_APPEND_LINK(entry, &mDoomedEntries);
- // handle pending requests only if we're supposed to
- if (doProcessPendingRequests) {
- // tell pending requests to get on with their lives...
- rv = ProcessPendingRequests(entry);
- // All requests have been removed, but there may still be open descriptors
- if (entry->IsNotInUse()) {
- DeactivateEntry(entry); // tell device to get rid of it
- }
- }
- return rv;
- }
- void
- nsCacheService::OnProfileShutdown()
- {
- if (!gService || !gService->mInitialized) {
- // The cache service has been shut down, but someone is still holding
- // a reference to it. Ignore this call.
- return;
- }
- {
- nsCacheServiceAutoLock lock;
- gService->mClearingEntries = true;
- gService->DoomActiveEntries(nullptr);
- }
- gService->CloseAllStreams();
- nsCacheServiceAutoLock lock;
- gService->ClearDoomList();
- // Make sure to wait for any pending cache-operations before
- // proceeding with destructive actions (bug #620660)
- (void) SyncWithCacheIOThread();
- if (gService->mDiskDevice && gService->mEnableDiskDevice) {
- gService->mDiskDevice->Shutdown();
- }
- gService->mEnableDiskDevice = false;
- if (gService->mOfflineDevice && gService->mEnableOfflineDevice) {
- gService->mOfflineDevice->Shutdown();
- }
- for (auto iter = gService->mCustomOfflineDevices.Iter();
- !iter.Done(); iter.Next()) {
- iter.Data()->Shutdown();
- iter.Remove();
- }
- gService->mEnableOfflineDevice = false;
- if (gService->mMemoryDevice) {
- // clear memory cache
- gService->mMemoryDevice->EvictEntries(nullptr);
- }
- gService->mClearingEntries = false;
- }
- void
- nsCacheService::OnProfileChanged()
- {
- if (!gService) return;
- CACHE_LOG_DEBUG(("nsCacheService::OnProfileChanged"));
-
- nsCacheServiceAutoLock lock;
-
- gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled();
- gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
- gService->mEnableMemoryDevice = gService->mObserver->MemoryCacheEnabled();
- if (gService->mDiskDevice) {
- gService->mDiskDevice->SetCacheParentDirectory(gService->mObserver->DiskCacheParentDirectory());
- gService->mDiskDevice->SetCapacity(gService->mObserver->DiskCacheCapacity());
- // XXX initialization of mDiskDevice could be made lazily, if mEnableDiskDevice is false
- nsresult rv = gService->mDiskDevice->Init();
- if (NS_FAILED(rv)) {
- NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing disk device failed");
- gService->mEnableDiskDevice = false;
- // XXX delete mDiskDevice?
- }
- }
- if (gService->mOfflineDevice) {
- gService->mOfflineDevice->SetCacheParentDirectory(gService->mObserver->OfflineCacheParentDirectory());
- gService->mOfflineDevice->SetCapacity(gService->mObserver->OfflineCacheCapacity());
- // XXX initialization of mOfflineDevice could be made lazily, if mEnableOfflineDevice is false
- nsresult rv = gService->mOfflineDevice->InitWithSqlite(gService->mStorageService);
- if (NS_FAILED(rv)) {
- NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing offline device failed");
- gService->mEnableOfflineDevice = false;
- // XXX delete mOfflineDevice?
- }
- }
- // If memoryDevice exists, reset its size to the new profile
- if (gService->mMemoryDevice) {
- if (gService->mEnableMemoryDevice) {
- // make sure that capacity is reset to the right value
- int32_t capacity = gService->mObserver->MemoryCacheCapacity();
- CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
- capacity));
- gService->mMemoryDevice->SetCapacity(capacity);
- } else {
- // tell memory device to evict everything
- CACHE_LOG_DEBUG(("memory device disabled\n"));
- gService->mMemoryDevice->SetCapacity(0);
- // Don't delete memory device, because some entries may be active still...
- }
- }
- }
- void
- nsCacheService::SetDiskCacheEnabled(bool enabled)
- {
- if (!gService) return;
- nsCacheServiceAutoLock lock;
- gService->mEnableDiskDevice = enabled;
- }
- void
- nsCacheService::SetDiskCacheCapacity(int32_t capacity)
- {
- if (!gService) return;
- nsCacheServiceAutoLock lock;
- if (gService->mDiskDevice) {
- gService->mDiskDevice->SetCapacity(capacity);
- }
- gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled();
- }
- void
- nsCacheService::SetDiskCacheMaxEntrySize(int32_t maxSize)
- {
- if (!gService) return;
- nsCacheServiceAutoLock lock;
- if (gService->mDiskDevice) {
- gService->mDiskDevice->SetMaxEntrySize(maxSize);
- }
- }
- void
- nsCacheService::SetMemoryCacheMaxEntrySize(int32_t maxSize)
- {
- if (!gService) return;
- nsCacheServiceAutoLock lock;
- if (gService->mMemoryDevice) {
- gService->mMemoryDevice->SetMaxEntrySize(maxSize);
- }
- }
- void
- nsCacheService::SetOfflineCacheEnabled(bool enabled)
- {
- if (!gService) return;
- nsCacheServiceAutoLock lock;
- gService->mEnableOfflineDevice = enabled;
- }
- void
- nsCacheService::SetOfflineCacheCapacity(int32_t capacity)
- {
- if (!gService) return;
- nsCacheServiceAutoLock lock;
- if (gService->mOfflineDevice) {
- gService->mOfflineDevice->SetCapacity(capacity);
- }
- gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
- }
- void
- nsCacheService::SetMemoryCache()
- {
- if (!gService) return;
- CACHE_LOG_DEBUG(("nsCacheService::SetMemoryCache"));
- nsCacheServiceAutoLock lock;
- gService->mEnableMemoryDevice = gService->mObserver->MemoryCacheEnabled();
- if (gService->mEnableMemoryDevice) {
- if (gService->mMemoryDevice) {
- int32_t capacity = gService->mObserver->MemoryCacheCapacity();
- // make sure that capacity is reset to the right value
- CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
- capacity));
- gService->mMemoryDevice->SetCapacity(capacity);
- }
- } else {
- if (gService->mMemoryDevice) {
- // tell memory device to evict everything
- CACHE_LOG_DEBUG(("memory device disabled\n"));
- gService->mMemoryDevice->SetCapacity(0);
- // Don't delete memory device, because some entries may be active still...
- }
- }
- }
- /******************************************************************************
- * static methods for nsCacheEntryDescriptor
- *****************************************************************************/
- void
- nsCacheService::CloseDescriptor(nsCacheEntryDescriptor * descriptor)
- {
- // ask entry to remove descriptor
- nsCacheEntry * entry = descriptor->CacheEntry();
- bool doomEntry;
- bool stillActive = entry->RemoveDescriptor(descriptor, &doomEntry);
- if (!entry->IsValid()) {
- gService->ProcessPendingRequests(entry);
- }
- if (doomEntry) {
- gService->DoomEntry_Internal(entry, true);
- return;
- }
- if (!stillActive) {
- gService->DeactivateEntry(entry);
- }
- }
- nsresult
- nsCacheService::GetFileForEntry(nsCacheEntry * entry,
- nsIFile ** result)
- {
- nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
- if (!device) return NS_ERROR_UNEXPECTED;
-
- return device->GetFileForEntry(entry, result);
- }
- nsresult
- nsCacheService::OpenInputStreamForEntry(nsCacheEntry * entry,
- nsCacheAccessMode mode,
- uint32_t offset,
- nsIInputStream ** result)
- {
- nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
- if (!device) return NS_ERROR_UNEXPECTED;
- return device->OpenInputStreamForEntry(entry, mode, offset, result);
- }
- nsresult
- nsCacheService::OpenOutputStreamForEntry(nsCacheEntry * entry,
- nsCacheAccessMode mode,
- uint32_t offset,
- nsIOutputStream ** result)
- {
- nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
- if (!device) return NS_ERROR_UNEXPECTED;
- return device->OpenOutputStreamForEntry(entry, mode, offset, result);
- }
- nsresult
- nsCacheService::OnDataSizeChange(nsCacheEntry * entry, int32_t deltaSize)
- {
- nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
- if (!device) return NS_ERROR_UNEXPECTED;
- return device->OnDataSizeChange(entry, deltaSize);
- }
- void
- nsCacheService::LockAcquired()
- {
- MutexAutoLock lock(mTimeStampLock);
- mLockAcquiredTimeStamp = TimeStamp::Now();
- }
- void
- nsCacheService::LockReleased()
- {
- MutexAutoLock lock(mTimeStampLock);
- mLockAcquiredTimeStamp = TimeStamp();
- }
- void
- nsCacheService::Lock()
- {
- gService->mLock.Lock();
- gService->LockAcquired();
- }
- void
- nsCacheService::Unlock()
- {
- gService->mLock.AssertCurrentThreadOwns();
- nsTArray<nsISupports*> doomed;
- doomed.SwapElements(gService->mDoomedObjects);
- gService->LockReleased();
- gService->mLock.Unlock();
- for (uint32_t i = 0; i < doomed.Length(); ++i)
- doomed[i]->Release();
- }
- void
- nsCacheService::ReleaseObject_Locked(nsISupports * obj,
- nsIEventTarget * target)
- {
- gService->mLock.AssertCurrentThreadOwns();
- bool isCur;
- if (!target || (NS_SUCCEEDED(target->IsOnCurrentThread(&isCur)) && isCur)) {
- gService->mDoomedObjects.AppendElement(obj);
- } else {
- NS_ProxyRelease(target, dont_AddRef(obj));
- }
- }
- nsresult
- nsCacheService::SetCacheElement(nsCacheEntry * entry, nsISupports * element)
- {
- entry->SetData(element);
- entry->TouchData();
- return NS_OK;
- }
- nsresult
- nsCacheService::ValidateEntry(nsCacheEntry * entry)
- {
- nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
- if (!device) return NS_ERROR_UNEXPECTED;
- entry->MarkValid();
- nsresult rv = gService->ProcessPendingRequests(entry);
- NS_ASSERTION(rv == NS_OK, "ProcessPendingRequests failed.");
- // XXX what else should be done?
- return rv;
- }
- int32_t
- nsCacheService::CacheCompressionLevel()
- {
- int32_t level = gService->mObserver->CacheCompressionLevel();
- return level;
- }
- void
- nsCacheService::DeactivateEntry(nsCacheEntry * entry)
- {
- CACHE_LOG_DEBUG(("Deactivating entry %p\n", entry));
- nsresult rv = NS_OK;
- NS_ASSERTION(entry->IsNotInUse(), "### deactivating an entry while in use!");
- nsCacheDevice * device = nullptr;
- if (mMaxDataSize < entry->DataSize() ) mMaxDataSize = entry->DataSize();
- if (mMaxMetaSize < entry->MetaDataSize() ) mMaxMetaSize = entry->MetaDataSize();
- if (entry->IsDoomed()) {
- // remove from Doomed list
- PR_REMOVE_AND_INIT_LINK(entry);
- } else if (entry->IsActive()) {
- // remove from active entries
- mActiveEntries.RemoveEntry(entry);
- CACHE_LOG_DEBUG(("Removed deactivated entry %p from mActiveEntries\n",
- entry));
- entry->MarkInactive();
- // bind entry if necessary to store meta-data
- device = EnsureEntryHasDevice(entry);
- if (!device) {
- CACHE_LOG_DEBUG(("DeactivateEntry: unable to bind active "
- "entry %p\n",
- entry));
- NS_WARNING("DeactivateEntry: unable to bind active entry\n");
- return;
- }
- } else {
- // if mInitialized == false,
- // then we're shutting down and this state is okay.
- NS_ASSERTION(!mInitialized, "DeactivateEntry: bad cache entry state.");
- }
- device = entry->CacheDevice();
- if (device) {
- rv = device->DeactivateEntry(entry);
- if (NS_FAILED(rv)) {
- // increment deactivate failure count
- ++mDeactivateFailures;
- }
- } else {
- // increment deactivating unbound entry statistic
- ++mDeactivatedUnboundEntries;
- delete entry; // because no one else will
- }
- }
- nsresult
- nsCacheService::ProcessPendingRequests(nsCacheEntry * entry)
- {
- nsresult rv = NS_OK;
- nsCacheRequest * request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
- nsCacheRequest * nextRequest;
- bool newWriter = false;
-
- CACHE_LOG_DEBUG(("ProcessPendingRequests for %sinitialized %s %salid entry %p\n",
- (entry->IsInitialized()?"" : "Un"),
- (entry->IsDoomed()?"DOOMED" : ""),
- (entry->IsValid()? "V":"Inv"), entry));
- if (request == &entry->mRequestQ) return NS_OK; // no queued requests
- if (!entry->IsDoomed() && entry->IsInvalid()) {
- // 1st descriptor closed w/o MarkValid()
- NS_ASSERTION(PR_CLIST_IS_EMPTY(&entry->mDescriptorQ), "shouldn't be here with open descriptors");
- #if DEBUG
- // verify no ACCESS_WRITE requests(shouldn't have any of these)
- while (request != &entry->mRequestQ) {
- NS_ASSERTION(request->AccessRequested() != nsICache::ACCESS_WRITE,
- "ACCESS_WRITE request should have been given a new entry");
- request = (nsCacheRequest *)PR_NEXT_LINK(request);
- }
- request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
- #endif
- // find first request with ACCESS_READ_WRITE (if any) and promote it to 1st writer
- while (request != &entry->mRequestQ) {
- if (request->AccessRequested() == nsICache::ACCESS_READ_WRITE) {
- newWriter = true;
- CACHE_LOG_DEBUG((" promoting request %p to 1st writer\n", request));
- break;
- }
- request = (nsCacheRequest *)PR_NEXT_LINK(request);
- }
-
- if (request == &entry->mRequestQ) // no requests asked for ACCESS_READ_WRITE, back to top
- request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
-
- // XXX what should we do if there are only READ requests in queue?
- // XXX serialize their accesses, give them only read access, but force them to check validate flag?
- // XXX or do readers simply presume the entry is valid
- // See fix for bug #467392 below
- }
- nsCacheAccessMode accessGranted = nsICache::ACCESS_NONE;
- while (request != &entry->mRequestQ) {
- nextRequest = (nsCacheRequest *)PR_NEXT_LINK(request);
- CACHE_LOG_DEBUG((" %sync request %p for %p\n",
- (request->mListener?"As":"S"), request, entry));
- if (request->mListener) {
- // Async request
- PR_REMOVE_AND_INIT_LINK(request);
- if (entry->IsDoomed()) {
- rv = ProcessRequest(request, false, nullptr);
- if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)
- rv = NS_OK;
- else
- delete request;
- if (NS_FAILED(rv)) {
- // XXX what to do?
- }
- } else if (entry->IsValid() || newWriter) {
- rv = entry->RequestAccess(request, &accessGranted);
- NS_ASSERTION(NS_SUCCEEDED(rv),
- "if entry is valid, RequestAccess must succeed.");
- // XXX if (newWriter) NS_ASSERTION( accessGranted == request->AccessRequested(), "why not?");
- // entry->CreateDescriptor dequeues request, and queues descriptor
- nsICacheEntryDescriptor *descriptor = nullptr;
- rv = entry->CreateDescriptor(request,
- accessGranted,
- &descriptor);
- // post call to listener to report error or descriptor
- rv = NotifyListener(request, descriptor, accessGranted, rv);
- delete request;
- if (NS_FAILED(rv)) {
- // XXX what to do?
- }
-
- } else {
- // read-only request to an invalid entry - need to wait for
- // the entry to become valid so we post an event to process
- // the request again later (bug #467392)
- nsCOMPtr<nsIRunnable> ev =
- new nsProcessRequestEvent(request);
- rv = DispatchToCacheIOThread(ev);
- if (NS_FAILED(rv)) {
- delete request; // avoid leak
- }
- }
- } else {
- // Synchronous request
- request->WakeUp();
- }
- if (newWriter) break; // process remaining requests after validation
- request = nextRequest;
- }
- return NS_OK;
- }
- bool
- nsCacheService::IsDoomListEmpty()
- {
- nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
- return &mDoomedEntries == entry;
- }
- void
- nsCacheService::ClearDoomList()
- {
- nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
- while (entry != &mDoomedEntries) {
- nsCacheEntry * next = (nsCacheEntry *)PR_NEXT_LINK(entry);
- entry->DetachDescriptors();
- DeactivateEntry(entry);
- entry = next;
- }
- }
- void
- nsCacheService::DoomActiveEntries(DoomCheckFn check)
- {
- AutoTArray<nsCacheEntry*, 8> array;
- for (auto iter = mActiveEntries.Iter(); !iter.Done(); iter.Next()) {
- nsCacheEntry* entry =
- static_cast<nsCacheEntryHashTableEntry*>(iter.Get())->cacheEntry;
- if (check && !check(entry)) {
- continue;
- }
- array.AppendElement(entry);
- // entry is being removed from the active entry list
- entry->MarkInactive();
- iter.Remove();
- }
- uint32_t count = array.Length();
- for (uint32_t i = 0; i < count; ++i) {
- DoomEntry_Internal(array[i], true);
- }
- }
- void
- nsCacheService::CloseAllStreams()
- {
- nsTArray<RefPtr<nsCacheEntryDescriptor::nsInputStreamWrapper> > inputs;
- nsTArray<RefPtr<nsCacheEntryDescriptor::nsOutputStreamWrapper> > outputs;
- {
- nsCacheServiceAutoLock lock;
- nsTArray<nsCacheEntry*> entries;
- #if DEBUG
- // make sure there is no active entry
- for (auto iter = mActiveEntries.Iter(); !iter.Done(); iter.Next()) {
- auto entry = static_cast<nsCacheEntryHashTableEntry*>(iter.Get());
- entries.AppendElement(entry->cacheEntry);
- }
- NS_ASSERTION(entries.IsEmpty(), "Bad state");
- #endif
- // Get doomed entries
- nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
- while (entry != &mDoomedEntries) {
- nsCacheEntry * next = (nsCacheEntry *)PR_NEXT_LINK(entry);
- entries.AppendElement(entry);
- entry = next;
- }
- // Iterate through all entries and collect input and output streams
- for (size_t i = 0; i < entries.Length(); i++) {
- entry = entries.ElementAt(i);
- nsTArray<RefPtr<nsCacheEntryDescriptor> > descs;
- entry->GetDescriptors(descs);
- for (uint32_t j = 0 ; j < descs.Length() ; j++) {
- if (descs[j]->mOutputWrapper)
- outputs.AppendElement(descs[j]->mOutputWrapper);
- for (size_t k = 0; k < descs[j]->mInputWrappers.Length(); k++)
- inputs.AppendElement(descs[j]->mInputWrappers[k]);
- }
- }
- }
- uint32_t i;
- for (i = 0 ; i < inputs.Length() ; i++)
- inputs[i]->Close();
- for (i = 0 ; i < outputs.Length() ; i++)
- outputs[i]->Close();
- }
- bool
- nsCacheService::GetClearingEntries()
- {
- AssertOwnsLock();
- return gService->mClearingEntries;
- }
- // static
- void nsCacheService::GetCacheBaseDirectoty(nsIFile ** result)
- {
- *result = nullptr;
- if (!gService || !gService->mObserver)
- return;
- nsCOMPtr<nsIFile> directory =
- gService->mObserver->DiskCacheParentDirectory();
- if (!directory)
- return;
- directory->Clone(result);
- }
- // static
- void nsCacheService::GetDiskCacheDirectory(nsIFile ** result)
- {
- nsCOMPtr<nsIFile> directory;
- GetCacheBaseDirectoty(getter_AddRefs(directory));
- if (!directory)
- return;
- nsresult rv = directory->AppendNative(NS_LITERAL_CSTRING("Cache"));
- if (NS_FAILED(rv))
- return;
- directory.forget(result);
- }
- // static
- void nsCacheService::GetAppCacheDirectory(nsIFile ** result)
- {
- nsCOMPtr<nsIFile> directory;
- GetCacheBaseDirectoty(getter_AddRefs(directory));
- if (!directory)
- return;
- nsresult rv = directory->AppendNative(NS_LITERAL_CSTRING("OfflineCache"));
- if (NS_FAILED(rv))
- return;
- directory.forget(result);
- }
- void
- nsCacheService::LogCacheStatistics()
- {
- uint32_t hitPercentage = (uint32_t)((((double)mCacheHits) /
- ((double)(mCacheHits + mCacheMisses))) * 100);
- CACHE_LOG_INFO(("\nCache Service Statistics:\n\n"));
- CACHE_LOG_INFO((" TotalEntries = %d\n", mTotalEntries));
- CACHE_LOG_INFO((" Cache Hits = %d\n", mCacheHits));
- CACHE_LOG_INFO((" Cache Misses = %d\n", mCacheMisses));
- CACHE_LOG_INFO((" Cache Hit %% = %d%%\n", hitPercentage));
- CACHE_LOG_INFO((" Max Key Length = %d\n", mMaxKeyLength));
- CACHE_LOG_INFO((" Max Meta Size = %d\n", mMaxMetaSize));
- CACHE_LOG_INFO((" Max Data Size = %d\n", mMaxDataSize));
- CACHE_LOG_INFO(("\n"));
- CACHE_LOG_INFO((" Deactivate Failures = %d\n",
- mDeactivateFailures));
- CACHE_LOG_INFO((" Deactivated Unbound Entries = %d\n",
- mDeactivatedUnboundEntries));
- }
- nsresult
- nsCacheService::SetDiskSmartSize()
- {
- nsCacheServiceAutoLock lock;
- if (!gService) return NS_ERROR_NOT_AVAILABLE;
- return gService->SetDiskSmartSize_Locked();
- }
- nsresult
- nsCacheService::SetDiskSmartSize_Locked()
- {
- nsresult rv;
- if (mozilla::net::CacheObserver::UseNewCache()) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- if (!mObserver->DiskCacheParentDirectory())
- return NS_ERROR_NOT_AVAILABLE;
- if (!mDiskDevice)
- return NS_ERROR_NOT_AVAILABLE;
- if (!mObserver->SmartSizeEnabled())
- return NS_ERROR_NOT_AVAILABLE;
- nsAutoString cachePath;
- rv = mObserver->DiskCacheParentDirectory()->GetPath(cachePath);
- if (NS_SUCCEEDED(rv)) {
- nsCOMPtr<nsIRunnable> event =
- new nsGetSmartSizeEvent(cachePath, mDiskDevice->getCacheSize(),
- mObserver->ShouldUseOldMaxSmartSize());
- DispatchToCacheIOThread(event);
- } else {
- return NS_ERROR_FAILURE;
- }
- return NS_OK;
- }
- void
- nsCacheService::MoveOrRemoveDiskCache(nsIFile *aOldCacheDir,
- nsIFile *aNewCacheDir,
- const char *aCacheSubdir)
- {
- bool same;
- if (NS_FAILED(aOldCacheDir->Equals(aNewCacheDir, &same)) || same)
- return;
- nsCOMPtr<nsIFile> aOldCacheSubdir;
- aOldCacheDir->Clone(getter_AddRefs(aOldCacheSubdir));
- nsresult rv = aOldCacheSubdir->AppendNative(
- nsDependentCString(aCacheSubdir));
- if (NS_FAILED(rv))
- return;
- bool exists;
- if (NS_FAILED(aOldCacheSubdir->Exists(&exists)) || !exists)
- return;
- nsCOMPtr<nsIFile> aNewCacheSubdir;
- aNewCacheDir->Clone(getter_AddRefs(aNewCacheSubdir));
- rv = aNewCacheSubdir->AppendNative(nsDependentCString(aCacheSubdir));
- if (NS_FAILED(rv))
- return;
-
- nsAutoCString newPath;
- rv = aNewCacheSubdir->GetNativePath(newPath);
- if (NS_FAILED(rv))
- return;
-
- if (NS_SUCCEEDED(aNewCacheSubdir->Exists(&exists)) && !exists) {
- // New cache directory does not exist, try to move the old one here
- // rename needs an empty target directory
- // Make sure the parent of the target sub-dir exists
- rv = aNewCacheDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
- if (NS_SUCCEEDED(rv) || NS_ERROR_FILE_ALREADY_EXISTS == rv) {
- nsAutoCString oldPath;
- rv = aOldCacheSubdir->GetNativePath(oldPath);
- if (NS_FAILED(rv))
- return;
- if (rename(oldPath.get(), newPath.get()) == 0)
- return;
- }
- }
-
- // Delay delete by 1 minute to avoid IO thrash on startup.
- nsDeleteDir::DeleteDir(aOldCacheSubdir, false, 60000);
- }
- static bool
- IsEntryPrivate(nsCacheEntry* entry)
- {
- return entry->IsPrivate();
- }
- void
- nsCacheService::LeavePrivateBrowsing()
- {
- nsCacheServiceAutoLock lock;
- gService->DoomActiveEntries(IsEntryPrivate);
- if (gService->mMemoryDevice) {
- // clear memory cache
- gService->mMemoryDevice->EvictPrivateEntries();
- }
- }
- MOZ_DEFINE_MALLOC_SIZE_OF(DiskCacheDeviceMallocSizeOf)
- NS_IMETHODIMP
- nsCacheService::CollectReports(nsIHandleReportCallback* aHandleReport,
- nsISupports* aData, bool aAnonymize)
- {
- size_t disk = 0;
- if (mDiskDevice) {
- nsCacheServiceAutoLock lock;
- disk = mDiskDevice->SizeOfIncludingThis(DiskCacheDeviceMallocSizeOf);
- }
- size_t memory = mMemoryDevice ? mMemoryDevice->TotalSize() : 0;
- MOZ_COLLECT_REPORT(
- "explicit/network/disk-cache", KIND_HEAP, UNITS_BYTES, disk,
- "Memory used by the network disk cache.");
- MOZ_COLLECT_REPORT(
- "explicit/network/memory-cache", KIND_HEAP, UNITS_BYTES, memory,
- "Memory used by the network memory cache.");
- return NS_OK;
- }
|