nsCacheService.cpp 102 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "mozilla/ArrayUtils.h"
  6. #include "mozilla/Attributes.h"
  7. #include "mozilla/Assertions.h"
  8. #include "mozilla/DebugOnly.h"
  9. #include "necko-config.h"
  10. #include "nsCache.h"
  11. #include "nsCacheService.h"
  12. #include "nsCacheRequest.h"
  13. #include "nsCacheEntry.h"
  14. #include "nsCacheEntryDescriptor.h"
  15. #include "nsCacheDevice.h"
  16. #include "nsMemoryCacheDevice.h"
  17. #include "nsICacheVisitor.h"
  18. #include "nsDiskCacheDevice.h"
  19. #include "nsDiskCacheDeviceSQL.h"
  20. #include "nsCacheUtils.h"
  21. #include "../cache2/CacheObserver.h"
  22. #include "nsIObserverService.h"
  23. #include "nsIPrefService.h"
  24. #include "nsIPrefBranch.h"
  25. #include "nsIFile.h"
  26. #include "nsIOService.h"
  27. #include "nsDirectoryServiceDefs.h"
  28. #include "nsAppDirectoryServiceDefs.h"
  29. #include "nsThreadUtils.h"
  30. #include "nsProxyRelease.h"
  31. #include "nsDeleteDir.h"
  32. #include "nsNetCID.h"
  33. #include <math.h> // for log()
  34. #include "mozilla/Services.h"
  35. #include "nsITimer.h"
  36. #include "mozIStorageService.h"
  37. #include "mozilla/net/NeckoCommon.h"
  38. #include <algorithm>
  39. using namespace mozilla;
  40. using namespace mozilla::net;
  41. /******************************************************************************
  42. * nsCacheProfilePrefObserver
  43. *****************************************************************************/
  44. #define DISK_CACHE_ENABLE_PREF "browser.cache.disk.enable"
  45. #define DISK_CACHE_DIR_PREF "browser.cache.disk.parent_directory"
  46. #define DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF\
  47. "browser.cache.disk.smart_size.first_run"
  48. #define DISK_CACHE_SMART_SIZE_ENABLED_PREF \
  49. "browser.cache.disk.smart_size.enabled"
  50. #define DISK_CACHE_SMART_SIZE_PREF "browser.cache.disk.smart_size_cached_value"
  51. #define DISK_CACHE_CAPACITY_PREF "browser.cache.disk.capacity"
  52. #define DISK_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.disk.max_entry_size"
  53. #define DISK_CACHE_CAPACITY 256000
  54. #define DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF \
  55. "browser.cache.disk.smart_size.use_old_max"
  56. #define OFFLINE_CACHE_ENABLE_PREF "browser.cache.offline.enable"
  57. #define OFFLINE_CACHE_DIR_PREF "browser.cache.offline.parent_directory"
  58. #define OFFLINE_CACHE_CAPACITY_PREF "browser.cache.offline.capacity"
  59. #define OFFLINE_CACHE_CAPACITY 512000
  60. #define MEMORY_CACHE_ENABLE_PREF "browser.cache.memory.enable"
  61. #define MEMORY_CACHE_CAPACITY_PREF "browser.cache.memory.capacity"
  62. #define MEMORY_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.memory.max_entry_size"
  63. #define CACHE_COMPRESSION_LEVEL_PREF "browser.cache.compression_level"
  64. #define CACHE_COMPRESSION_LEVEL 1
  65. #define SANITIZE_ON_SHUTDOWN_PREF "privacy.sanitize.sanitizeOnShutdown"
  66. #define CLEAR_ON_SHUTDOWN_PREF "privacy.clearOnShutdown.cache"
  67. static const char * observerList[] = {
  68. "profile-before-change",
  69. "profile-do-change",
  70. NS_XPCOM_SHUTDOWN_OBSERVER_ID,
  71. "last-pb-context-exited",
  72. "suspend_process_notification",
  73. "resume_process_notification"
  74. };
  75. static const char * prefList[] = {
  76. DISK_CACHE_ENABLE_PREF,
  77. DISK_CACHE_SMART_SIZE_ENABLED_PREF,
  78. DISK_CACHE_CAPACITY_PREF,
  79. DISK_CACHE_DIR_PREF,
  80. DISK_CACHE_MAX_ENTRY_SIZE_PREF,
  81. DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
  82. OFFLINE_CACHE_ENABLE_PREF,
  83. OFFLINE_CACHE_CAPACITY_PREF,
  84. OFFLINE_CACHE_DIR_PREF,
  85. MEMORY_CACHE_ENABLE_PREF,
  86. MEMORY_CACHE_CAPACITY_PREF,
  87. MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
  88. CACHE_COMPRESSION_LEVEL_PREF,
  89. SANITIZE_ON_SHUTDOWN_PREF,
  90. CLEAR_ON_SHUTDOWN_PREF
  91. };
  92. // Cache sizes, in KB
  93. const int32_t DEFAULT_CACHE_SIZE = 250 * 1024; // 250 MB
  94. const int32_t MAX_CACHE_SIZE = 350 * 1024; // 350 MB
  95. const int32_t OLD_MAX_CACHE_SIZE = 1024 * 1024; // 1 GB
  96. // Default cache size was 50 MB for many years until FF 4:
  97. const int32_t PRE_GECKO_2_0_DEFAULT_CACHE_SIZE = 50 * 1024;
  98. class nsCacheProfilePrefObserver : public nsIObserver
  99. {
  100. virtual ~nsCacheProfilePrefObserver() {}
  101. public:
  102. NS_DECL_THREADSAFE_ISUPPORTS
  103. NS_DECL_NSIOBSERVER
  104. nsCacheProfilePrefObserver()
  105. : mHaveProfile(false)
  106. , mDiskCacheEnabled(false)
  107. , mDiskCacheCapacity(0)
  108. , mDiskCacheMaxEntrySize(-1) // -1 means "no limit"
  109. , mSmartSizeEnabled(false)
  110. , mShouldUseOldMaxSmartSize(false)
  111. , mOfflineCacheEnabled(false)
  112. , mOfflineCacheCapacity(0)
  113. , mMemoryCacheEnabled(true)
  114. , mMemoryCacheCapacity(-1)
  115. , mMemoryCacheMaxEntrySize(-1) // -1 means "no limit"
  116. , mCacheCompressionLevel(CACHE_COMPRESSION_LEVEL)
  117. , mSanitizeOnShutdown(false)
  118. , mClearCacheOnShutdown(false)
  119. {
  120. }
  121. nsresult Install();
  122. void Remove();
  123. nsresult ReadPrefs(nsIPrefBranch* branch);
  124. bool DiskCacheEnabled();
  125. int32_t DiskCacheCapacity() { return mDiskCacheCapacity; }
  126. void SetDiskCacheCapacity(int32_t);
  127. int32_t DiskCacheMaxEntrySize() { return mDiskCacheMaxEntrySize; }
  128. nsIFile * DiskCacheParentDirectory() { return mDiskCacheParentDirectory; }
  129. bool SmartSizeEnabled() { return mSmartSizeEnabled; }
  130. bool ShouldUseOldMaxSmartSize() { return mShouldUseOldMaxSmartSize; }
  131. void SetUseNewMaxSmartSize(bool useNew) { mShouldUseOldMaxSmartSize = !useNew; }
  132. bool OfflineCacheEnabled();
  133. int32_t OfflineCacheCapacity() { return mOfflineCacheCapacity; }
  134. nsIFile * OfflineCacheParentDirectory() { return mOfflineCacheParentDirectory; }
  135. bool MemoryCacheEnabled();
  136. int32_t MemoryCacheCapacity();
  137. int32_t MemoryCacheMaxEntrySize() { return mMemoryCacheMaxEntrySize; }
  138. int32_t CacheCompressionLevel();
  139. bool SanitizeAtShutdown() { return mSanitizeOnShutdown && mClearCacheOnShutdown; }
  140. static uint32_t GetSmartCacheSize(const nsAString& cachePath,
  141. uint32_t currentSize,
  142. bool shouldUseOldMaxSmartSize);
  143. bool PermittedToSmartSize(nsIPrefBranch*, bool firstRun);
  144. private:
  145. bool mHaveProfile;
  146. bool mDiskCacheEnabled;
  147. int32_t mDiskCacheCapacity; // in kilobytes
  148. int32_t mDiskCacheMaxEntrySize; // in kilobytes
  149. nsCOMPtr<nsIFile> mDiskCacheParentDirectory;
  150. bool mSmartSizeEnabled;
  151. bool mShouldUseOldMaxSmartSize;
  152. bool mOfflineCacheEnabled;
  153. int32_t mOfflineCacheCapacity; // in kilobytes
  154. nsCOMPtr<nsIFile> mOfflineCacheParentDirectory;
  155. bool mMemoryCacheEnabled;
  156. int32_t mMemoryCacheCapacity; // in kilobytes
  157. int32_t mMemoryCacheMaxEntrySize; // in kilobytes
  158. int32_t mCacheCompressionLevel;
  159. bool mSanitizeOnShutdown;
  160. bool mClearCacheOnShutdown;
  161. };
  162. NS_IMPL_ISUPPORTS(nsCacheProfilePrefObserver, nsIObserver)
  163. class nsSetDiskSmartSizeCallback final : public nsITimerCallback
  164. {
  165. ~nsSetDiskSmartSizeCallback() {}
  166. public:
  167. NS_DECL_THREADSAFE_ISUPPORTS
  168. NS_IMETHOD Notify(nsITimer* aTimer) override {
  169. if (nsCacheService::gService) {
  170. nsCacheServiceAutoLock autoLock;
  171. nsCacheService::gService->SetDiskSmartSize_Locked();
  172. nsCacheService::gService->mSmartSizeTimer = nullptr;
  173. }
  174. return NS_OK;
  175. }
  176. };
  177. NS_IMPL_ISUPPORTS(nsSetDiskSmartSizeCallback, nsITimerCallback)
  178. // Runnable sent to main thread after the cache IO thread calculates available
  179. // disk space, so that there is no race in setting mDiskCacheCapacity.
  180. class nsSetSmartSizeEvent: public Runnable
  181. {
  182. public:
  183. explicit nsSetSmartSizeEvent(int32_t smartSize)
  184. : mSmartSize(smartSize) {}
  185. NS_IMETHOD Run()
  186. {
  187. NS_ASSERTION(NS_IsMainThread(),
  188. "Setting smart size data off the main thread");
  189. // Main thread may have already called nsCacheService::Shutdown
  190. if (!nsCacheService::IsInitialized())
  191. return NS_ERROR_NOT_AVAILABLE;
  192. // Ensure smart sizing wasn't switched off while event was pending.
  193. // It is safe to access the observer without the lock since we are
  194. // on the main thread and the value changes only on the main thread.
  195. if (!nsCacheService::gService->mObserver->SmartSizeEnabled())
  196. return NS_OK;
  197. nsCacheService::SetDiskCacheCapacity(mSmartSize);
  198. nsCOMPtr<nsIPrefBranch> ps = do_GetService(NS_PREFSERVICE_CONTRACTID);
  199. if (!ps ||
  200. NS_FAILED(ps->SetIntPref(DISK_CACHE_SMART_SIZE_PREF, mSmartSize)))
  201. NS_WARNING("Failed to set smart size pref");
  202. return NS_OK;
  203. }
  204. private:
  205. int32_t mSmartSize;
  206. };
  207. // Runnable sent from main thread to cacheIO thread
  208. class nsGetSmartSizeEvent: public Runnable
  209. {
  210. public:
  211. nsGetSmartSizeEvent(const nsAString& cachePath, uint32_t currentSize,
  212. bool shouldUseOldMaxSmartSize)
  213. : mCachePath(cachePath)
  214. , mCurrentSize(currentSize)
  215. , mShouldUseOldMaxSmartSize(shouldUseOldMaxSmartSize)
  216. {}
  217. // Calculates user's disk space available on a background thread and
  218. // dispatches this value back to the main thread.
  219. NS_IMETHOD Run() override
  220. {
  221. uint32_t size;
  222. size = nsCacheProfilePrefObserver::GetSmartCacheSize(mCachePath,
  223. mCurrentSize,
  224. mShouldUseOldMaxSmartSize);
  225. NS_DispatchToMainThread(new nsSetSmartSizeEvent(size));
  226. return NS_OK;
  227. }
  228. private:
  229. nsString mCachePath;
  230. uint32_t mCurrentSize;
  231. bool mShouldUseOldMaxSmartSize;
  232. };
  233. class nsBlockOnCacheThreadEvent : public Runnable {
  234. public:
  235. nsBlockOnCacheThreadEvent()
  236. {
  237. }
  238. NS_IMETHOD Run() override
  239. {
  240. nsCacheServiceAutoLock autoLock;
  241. CACHE_LOG_DEBUG(("nsBlockOnCacheThreadEvent [%p]\n", this));
  242. nsCacheService::gService->mNotified = true;
  243. nsCacheService::gService->mCondVar.Notify();
  244. return NS_OK;
  245. }
  246. };
  247. nsresult
  248. nsCacheProfilePrefObserver::Install()
  249. {
  250. // install profile-change observer
  251. nsCOMPtr<nsIObserverService> observerService =
  252. mozilla::services::GetObserverService();
  253. if (!observerService)
  254. return NS_ERROR_FAILURE;
  255. nsresult rv, rv2 = NS_OK;
  256. for (unsigned int i=0; i<ArrayLength(observerList); i++) {
  257. rv = observerService->AddObserver(this, observerList[i], false);
  258. if (NS_FAILED(rv))
  259. rv2 = rv;
  260. }
  261. // install preferences observer
  262. nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
  263. if (!branch) return NS_ERROR_FAILURE;
  264. for (unsigned int i=0; i<ArrayLength(prefList); i++) {
  265. rv = branch->AddObserver(prefList[i], this, false);
  266. if (NS_FAILED(rv))
  267. rv2 = rv;
  268. }
  269. // Determine if we have a profile already
  270. // Install() is called *after* the profile-after-change notification
  271. // when there is only a single profile, or it is specified on the
  272. // commandline at startup.
  273. // In that case, we detect the presence of a profile by the existence
  274. // of the NS_APP_USER_PROFILE_50_DIR directory.
  275. nsCOMPtr<nsIFile> directory;
  276. rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
  277. getter_AddRefs(directory));
  278. if (NS_SUCCEEDED(rv))
  279. mHaveProfile = true;
  280. rv = ReadPrefs(branch);
  281. NS_ENSURE_SUCCESS(rv, rv);
  282. return rv2;
  283. }
  284. void
  285. nsCacheProfilePrefObserver::Remove()
  286. {
  287. // remove Observer Service observers
  288. nsCOMPtr<nsIObserverService> obs =
  289. mozilla::services::GetObserverService();
  290. if (obs) {
  291. for (unsigned int i=0; i<ArrayLength(observerList); i++) {
  292. obs->RemoveObserver(this, observerList[i]);
  293. }
  294. }
  295. // remove Pref Service observers
  296. nsCOMPtr<nsIPrefBranch> prefs =
  297. do_GetService(NS_PREFSERVICE_CONTRACTID);
  298. if (!prefs)
  299. return;
  300. for (unsigned int i=0; i<ArrayLength(prefList); i++)
  301. prefs->RemoveObserver(prefList[i], this); // remove cache pref observers
  302. }
  303. void
  304. nsCacheProfilePrefObserver::SetDiskCacheCapacity(int32_t capacity)
  305. {
  306. mDiskCacheCapacity = std::max(0, capacity);
  307. }
  308. NS_IMETHODIMP
  309. nsCacheProfilePrefObserver::Observe(nsISupports * subject,
  310. const char * topic,
  311. const char16_t * data_unicode)
  312. {
  313. nsresult rv;
  314. NS_ConvertUTF16toUTF8 data(data_unicode);
  315. CACHE_LOG_INFO(("Observe [topic=%s data=%s]\n", topic, data.get()));
  316. if (!nsCacheService::IsInitialized()) {
  317. if (!strcmp("resume_process_notification", topic)) {
  318. // A suspended process has a closed cache, so re-open it here.
  319. nsCacheService::GlobalInstance()->Init();
  320. }
  321. return NS_OK;
  322. }
  323. if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic)) {
  324. // xpcom going away, shutdown cache service
  325. nsCacheService::GlobalInstance()->Shutdown();
  326. } else if (!strcmp("profile-before-change", topic)) {
  327. // profile before change
  328. mHaveProfile = false;
  329. // XXX shutdown devices
  330. nsCacheService::OnProfileShutdown();
  331. } else if (!strcmp("suspend_process_notification", topic)) {
  332. // A suspended process may never return, so shutdown the cache to reduce
  333. // cache corruption.
  334. nsCacheService::GlobalInstance()->Shutdown();
  335. } else if (!strcmp("profile-do-change", topic)) {
  336. // profile after change
  337. mHaveProfile = true;
  338. nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
  339. if (!branch) {
  340. return NS_ERROR_FAILURE;
  341. }
  342. (void)ReadPrefs(branch);
  343. nsCacheService::OnProfileChanged();
  344. } else if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, topic)) {
  345. // ignore pref changes until we're done switch profiles
  346. if (!mHaveProfile)
  347. return NS_OK;
  348. nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(subject, &rv);
  349. if (NS_FAILED(rv))
  350. return rv;
  351. // which preference changed?
  352. if (!strcmp(DISK_CACHE_ENABLE_PREF, data.get())) {
  353. rv = branch->GetBoolPref(DISK_CACHE_ENABLE_PREF,
  354. &mDiskCacheEnabled);
  355. if (NS_FAILED(rv))
  356. return rv;
  357. nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
  358. } else if (!strcmp(DISK_CACHE_CAPACITY_PREF, data.get())) {
  359. int32_t capacity = 0;
  360. rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &capacity);
  361. if (NS_FAILED(rv))
  362. return rv;
  363. mDiskCacheCapacity = std::max(0, capacity);
  364. nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
  365. // Update the cache capacity when smart sizing is turned on/off
  366. } else if (!strcmp(DISK_CACHE_SMART_SIZE_ENABLED_PREF, data.get())) {
  367. // Is the update because smartsizing was turned on, or off?
  368. rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
  369. &mSmartSizeEnabled);
  370. if (NS_FAILED(rv))
  371. return rv;
  372. int32_t newCapacity = 0;
  373. if (mSmartSizeEnabled) {
  374. nsCacheService::SetDiskSmartSize();
  375. } else {
  376. // Smart sizing switched off: use user specified size
  377. rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &newCapacity);
  378. if (NS_FAILED(rv))
  379. return rv;
  380. mDiskCacheCapacity = std::max(0, newCapacity);
  381. nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
  382. }
  383. } else if (!strcmp(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, data.get())) {
  384. rv = branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
  385. &mShouldUseOldMaxSmartSize);
  386. if (NS_FAILED(rv))
  387. return rv;
  388. } else if (!strcmp(DISK_CACHE_MAX_ENTRY_SIZE_PREF, data.get())) {
  389. int32_t newMaxSize;
  390. rv = branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF,
  391. &newMaxSize);
  392. if (NS_FAILED(rv))
  393. return rv;
  394. mDiskCacheMaxEntrySize = std::max(-1, newMaxSize);
  395. nsCacheService::SetDiskCacheMaxEntrySize(mDiskCacheMaxEntrySize);
  396. #if 0
  397. } else if (!strcmp(DISK_CACHE_DIR_PREF, data.get())) {
  398. // XXX We probaby don't want to respond to this pref except after
  399. // XXX profile changes. Ideally, there should be somekind of user
  400. // XXX notification that the pref change won't take effect until
  401. // XXX the next time the profile changes (browser launch)
  402. #endif
  403. } else
  404. // which preference changed?
  405. if (!strcmp(OFFLINE_CACHE_ENABLE_PREF, data.get())) {
  406. rv = branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
  407. &mOfflineCacheEnabled);
  408. if (NS_FAILED(rv)) return rv;
  409. nsCacheService::SetOfflineCacheEnabled(OfflineCacheEnabled());
  410. } else if (!strcmp(OFFLINE_CACHE_CAPACITY_PREF, data.get())) {
  411. int32_t capacity = 0;
  412. rv = branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF, &capacity);
  413. if (NS_FAILED(rv)) return rv;
  414. mOfflineCacheCapacity = std::max(0, capacity);
  415. nsCacheService::SetOfflineCacheCapacity(mOfflineCacheCapacity);
  416. #if 0
  417. } else if (!strcmp(OFFLINE_CACHE_DIR_PREF, data.get())) {
  418. // XXX We probaby don't want to respond to this pref except after
  419. // XXX profile changes. Ideally, there should be some kind of user
  420. // XXX notification that the pref change won't take effect until
  421. // XXX the next time the profile changes (browser launch)
  422. #endif
  423. } else
  424. if (!strcmp(MEMORY_CACHE_ENABLE_PREF, data.get())) {
  425. rv = branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF,
  426. &mMemoryCacheEnabled);
  427. if (NS_FAILED(rv))
  428. return rv;
  429. nsCacheService::SetMemoryCache();
  430. } else if (!strcmp(MEMORY_CACHE_CAPACITY_PREF, data.get())) {
  431. mMemoryCacheCapacity = -1;
  432. (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,
  433. &mMemoryCacheCapacity);
  434. nsCacheService::SetMemoryCache();
  435. } else if (!strcmp(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF, data.get())) {
  436. int32_t newMaxSize;
  437. rv = branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
  438. &newMaxSize);
  439. if (NS_FAILED(rv))
  440. return rv;
  441. mMemoryCacheMaxEntrySize = std::max(-1, newMaxSize);
  442. nsCacheService::SetMemoryCacheMaxEntrySize(mMemoryCacheMaxEntrySize);
  443. } else if (!strcmp(CACHE_COMPRESSION_LEVEL_PREF, data.get())) {
  444. mCacheCompressionLevel = CACHE_COMPRESSION_LEVEL;
  445. (void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF,
  446. &mCacheCompressionLevel);
  447. mCacheCompressionLevel = std::max(0, mCacheCompressionLevel);
  448. mCacheCompressionLevel = std::min(9, mCacheCompressionLevel);
  449. } else if (!strcmp(SANITIZE_ON_SHUTDOWN_PREF, data.get())) {
  450. rv = branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF,
  451. &mSanitizeOnShutdown);
  452. if (NS_FAILED(rv))
  453. return rv;
  454. nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
  455. } else if (!strcmp(CLEAR_ON_SHUTDOWN_PREF, data.get())) {
  456. rv = branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF,
  457. &mClearCacheOnShutdown);
  458. if (NS_FAILED(rv))
  459. return rv;
  460. nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
  461. }
  462. } else if (!strcmp("last-pb-context-exited", topic)) {
  463. nsCacheService::LeavePrivateBrowsing();
  464. }
  465. return NS_OK;
  466. }
  467. // Returns default ("smart") size (in KB) of cache, given available disk space
  468. // (also in KB)
  469. static uint32_t
  470. SmartCacheSize(const uint32_t availKB, bool shouldUseOldMaxSmartSize)
  471. {
  472. uint32_t maxSize = shouldUseOldMaxSmartSize ? OLD_MAX_CACHE_SIZE : MAX_CACHE_SIZE;
  473. if (availKB > 100 * 1024 * 1024)
  474. return maxSize; // skip computing if we're over 100 GB
  475. // Grow/shrink in 10 MB units, deliberately, so that in the common case we
  476. // don't shrink cache and evict items every time we startup (it's important
  477. // that we don't slow down startup benchmarks).
  478. uint32_t sz10MBs = 0;
  479. uint32_t avail10MBs = availKB / (1024*10);
  480. // .5% of space above 25 GB
  481. if (avail10MBs > 2500) {
  482. sz10MBs += static_cast<uint32_t>((avail10MBs - 2500)*.005);
  483. avail10MBs = 2500;
  484. }
  485. // 1% of space between 7GB -> 25 GB
  486. if (avail10MBs > 700) {
  487. sz10MBs += static_cast<uint32_t>((avail10MBs - 700)*.01);
  488. avail10MBs = 700;
  489. }
  490. // 5% of space between 500 MB -> 7 GB
  491. if (avail10MBs > 50) {
  492. sz10MBs += static_cast<uint32_t>((avail10MBs - 50)*.05);
  493. avail10MBs = 50;
  494. }
  495. // 40% of space up to 500 MB (50 MB min)
  496. sz10MBs += std::max<uint32_t>(5, static_cast<uint32_t>(avail10MBs * .4));
  497. return std::min<uint32_t>(maxSize, sz10MBs * 10 * 1024);
  498. }
  499. /* Computes our best guess for the default size of the user's disk cache,
  500. * based on the amount of space they have free on their hard drive.
  501. * We use a tiered scheme: the more space available,
  502. * the larger the disk cache will be. However, we do not want
  503. * to enable the disk cache to grow to an unbounded size, so the larger the
  504. * user's available space is, the smaller of a percentage we take. We set a
  505. * lower bound of 50MB and an upper bound of 1GB.
  506. *
  507. *@param: None.
  508. *@return: The size that the user's disk cache should default to, in kBytes.
  509. */
  510. uint32_t
  511. nsCacheProfilePrefObserver::GetSmartCacheSize(const nsAString& cachePath,
  512. uint32_t currentSize,
  513. bool shouldUseOldMaxSmartSize)
  514. {
  515. // Check for free space on device where cache directory lives
  516. nsresult rv;
  517. nsCOMPtr<nsIFile>
  518. cacheDirectory (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
  519. if (NS_FAILED(rv) || !cacheDirectory)
  520. return DEFAULT_CACHE_SIZE;
  521. rv = cacheDirectory->InitWithPath(cachePath);
  522. if (NS_FAILED(rv))
  523. return DEFAULT_CACHE_SIZE;
  524. int64_t bytesAvailable;
  525. rv = cacheDirectory->GetDiskSpaceAvailable(&bytesAvailable);
  526. if (NS_FAILED(rv))
  527. return DEFAULT_CACHE_SIZE;
  528. return SmartCacheSize(static_cast<uint32_t>((bytesAvailable / 1024) +
  529. currentSize),
  530. shouldUseOldMaxSmartSize);
  531. }
  532. /* Determine if we are permitted to dynamically size the user's disk cache based
  533. * on their disk space available. We may do this so long as the pref
  534. * smart_size.enabled is true.
  535. */
  536. bool
  537. nsCacheProfilePrefObserver::PermittedToSmartSize(nsIPrefBranch* branch, bool
  538. firstRun)
  539. {
  540. nsresult rv;
  541. if (firstRun) {
  542. // check if user has set cache size in the past
  543. bool userSet;
  544. rv = branch->PrefHasUserValue(DISK_CACHE_CAPACITY_PREF, &userSet);
  545. if (NS_FAILED(rv)) userSet = true;
  546. if (userSet) {
  547. int32_t oldCapacity;
  548. // If user explicitly set cache size to be smaller than old default
  549. // of 50 MB, then keep user's value. Otherwise use smart sizing.
  550. rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &oldCapacity);
  551. if (oldCapacity < PRE_GECKO_2_0_DEFAULT_CACHE_SIZE) {
  552. mSmartSizeEnabled = false;
  553. branch->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
  554. mSmartSizeEnabled);
  555. return mSmartSizeEnabled;
  556. }
  557. }
  558. // Set manual setting to MAX cache size as starting val for any
  559. // adjustment by user: (bug 559942 comment 65)
  560. int32_t maxSize = mShouldUseOldMaxSmartSize ? OLD_MAX_CACHE_SIZE : MAX_CACHE_SIZE;
  561. branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, maxSize);
  562. }
  563. rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
  564. &mSmartSizeEnabled);
  565. if (NS_FAILED(rv))
  566. mSmartSizeEnabled = false;
  567. return mSmartSizeEnabled;
  568. }
  569. nsresult
  570. nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch* branch)
  571. {
  572. nsresult rv = NS_OK;
  573. // read disk cache device prefs
  574. mDiskCacheEnabled = true; // presume disk cache is enabled
  575. (void) branch->GetBoolPref(DISK_CACHE_ENABLE_PREF, &mDiskCacheEnabled);
  576. mDiskCacheCapacity = DISK_CACHE_CAPACITY;
  577. (void)branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &mDiskCacheCapacity);
  578. mDiskCacheCapacity = std::max(0, mDiskCacheCapacity);
  579. (void) branch->GetIntPref(DISK_CACHE_MAX_ENTRY_SIZE_PREF,
  580. &mDiskCacheMaxEntrySize);
  581. mDiskCacheMaxEntrySize = std::max(-1, mDiskCacheMaxEntrySize);
  582. (void) branch->GetComplexValue(DISK_CACHE_DIR_PREF, // ignore error
  583. NS_GET_IID(nsIFile),
  584. getter_AddRefs(mDiskCacheParentDirectory));
  585. (void) branch->GetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF,
  586. &mShouldUseOldMaxSmartSize);
  587. if (!mDiskCacheParentDirectory) {
  588. nsCOMPtr<nsIFile> directory;
  589. // try to get the disk cache parent directory
  590. rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
  591. getter_AddRefs(directory));
  592. if (NS_FAILED(rv)) {
  593. // try to get the profile directory (there may not be a profile yet)
  594. nsCOMPtr<nsIFile> profDir;
  595. NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
  596. getter_AddRefs(profDir));
  597. NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
  598. getter_AddRefs(directory));
  599. if (!directory)
  600. directory = profDir;
  601. else if (profDir) {
  602. nsCacheService::MoveOrRemoveDiskCache(profDir, directory,
  603. "Cache");
  604. }
  605. }
  606. // use file cache in build tree only if asked, to avoid cache dir litter
  607. if (!directory && PR_GetEnv("NECKO_DEV_ENABLE_DISK_CACHE")) {
  608. rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
  609. getter_AddRefs(directory));
  610. }
  611. if (directory)
  612. mDiskCacheParentDirectory = do_QueryInterface(directory, &rv);
  613. }
  614. if (mDiskCacheParentDirectory) {
  615. bool firstSmartSizeRun;
  616. rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,
  617. &firstSmartSizeRun);
  618. if (NS_FAILED(rv))
  619. firstSmartSizeRun = false;
  620. if (PermittedToSmartSize(branch, firstSmartSizeRun)) {
  621. // Avoid evictions: use previous cache size until smart size event
  622. // updates mDiskCacheCapacity
  623. rv = branch->GetIntPref(firstSmartSizeRun ?
  624. DISK_CACHE_CAPACITY_PREF :
  625. DISK_CACHE_SMART_SIZE_PREF,
  626. &mDiskCacheCapacity);
  627. if (NS_FAILED(rv))
  628. mDiskCacheCapacity = DEFAULT_CACHE_SIZE;
  629. }
  630. if (firstSmartSizeRun) {
  631. // It is no longer our first run
  632. rv = branch->SetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,
  633. false);
  634. if (NS_FAILED(rv))
  635. NS_WARNING("Failed setting first_run pref in ReadPrefs.");
  636. }
  637. }
  638. // read offline cache device prefs
  639. mOfflineCacheEnabled = true; // presume offline cache is enabled
  640. (void) branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
  641. &mOfflineCacheEnabled);
  642. mOfflineCacheCapacity = OFFLINE_CACHE_CAPACITY;
  643. (void)branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF,
  644. &mOfflineCacheCapacity);
  645. mOfflineCacheCapacity = std::max(0, mOfflineCacheCapacity);
  646. (void) branch->GetComplexValue(OFFLINE_CACHE_DIR_PREF, // ignore error
  647. NS_GET_IID(nsIFile),
  648. getter_AddRefs(mOfflineCacheParentDirectory));
  649. if (!mOfflineCacheParentDirectory) {
  650. nsCOMPtr<nsIFile> directory;
  651. // try to get the offline cache parent directory
  652. rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
  653. getter_AddRefs(directory));
  654. if (NS_FAILED(rv)) {
  655. // try to get the profile directory (there may not be a profile yet)
  656. nsCOMPtr<nsIFile> profDir;
  657. NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
  658. getter_AddRefs(profDir));
  659. NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
  660. getter_AddRefs(directory));
  661. if (!directory)
  662. directory = profDir;
  663. else if (profDir) {
  664. nsCacheService::MoveOrRemoveDiskCache(profDir, directory,
  665. "OfflineCache");
  666. }
  667. }
  668. #if DEBUG
  669. if (!directory) {
  670. // use current process directory during development
  671. rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
  672. getter_AddRefs(directory));
  673. }
  674. #endif
  675. if (directory)
  676. mOfflineCacheParentDirectory = do_QueryInterface(directory, &rv);
  677. }
  678. // read memory cache device prefs
  679. (void) branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF, &mMemoryCacheEnabled);
  680. mMemoryCacheCapacity = -1;
  681. (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,
  682. &mMemoryCacheCapacity);
  683. (void) branch->GetIntPref(MEMORY_CACHE_MAX_ENTRY_SIZE_PREF,
  684. &mMemoryCacheMaxEntrySize);
  685. mMemoryCacheMaxEntrySize = std::max(-1, mMemoryCacheMaxEntrySize);
  686. // read cache compression level pref
  687. mCacheCompressionLevel = CACHE_COMPRESSION_LEVEL;
  688. (void)branch->GetIntPref(CACHE_COMPRESSION_LEVEL_PREF,
  689. &mCacheCompressionLevel);
  690. mCacheCompressionLevel = std::max(0, mCacheCompressionLevel);
  691. mCacheCompressionLevel = std::min(9, mCacheCompressionLevel);
  692. // read cache shutdown sanitization prefs
  693. (void) branch->GetBoolPref(SANITIZE_ON_SHUTDOWN_PREF,
  694. &mSanitizeOnShutdown);
  695. (void) branch->GetBoolPref(CLEAR_ON_SHUTDOWN_PREF,
  696. &mClearCacheOnShutdown);
  697. return rv;
  698. }
  699. nsresult
  700. nsCacheService::DispatchToCacheIOThread(nsIRunnable* event)
  701. {
  702. if (!gService || !gService->mCacheIOThread) return NS_ERROR_NOT_AVAILABLE;
  703. return gService->mCacheIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
  704. }
  705. nsresult
  706. nsCacheService::SyncWithCacheIOThread()
  707. {
  708. if (!gService || !gService->mCacheIOThread) return NS_ERROR_NOT_AVAILABLE;
  709. gService->mLock.AssertCurrentThreadOwns();
  710. nsCOMPtr<nsIRunnable> event = new nsBlockOnCacheThreadEvent();
  711. // dispatch event - it will notify the monitor when it's done
  712. nsresult rv =
  713. gService->mCacheIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
  714. if (NS_FAILED(rv)) {
  715. NS_WARNING("Failed dispatching block-event");
  716. return NS_ERROR_UNEXPECTED;
  717. }
  718. // wait until notified, then return
  719. gService->mNotified = false;
  720. while (!gService->mNotified) {
  721. gService->mCondVar.Wait();
  722. }
  723. return NS_OK;
  724. }
  725. bool
  726. nsCacheProfilePrefObserver::DiskCacheEnabled()
  727. {
  728. if ((mDiskCacheCapacity == 0) || (!mDiskCacheParentDirectory)) return false;
  729. return mDiskCacheEnabled && (!mSanitizeOnShutdown || !mClearCacheOnShutdown);
  730. }
  731. bool
  732. nsCacheProfilePrefObserver::OfflineCacheEnabled()
  733. {
  734. if ((mOfflineCacheCapacity == 0) || (!mOfflineCacheParentDirectory))
  735. return false;
  736. return mOfflineCacheEnabled;
  737. }
  738. bool
  739. nsCacheProfilePrefObserver::MemoryCacheEnabled()
  740. {
  741. if (mMemoryCacheCapacity == 0) return false;
  742. return mMemoryCacheEnabled;
  743. }
  744. /**
  745. * MemoryCacheCapacity
  746. *
  747. * If the browser.cache.memory.capacity preference is positive, we use that
  748. * value for the amount of memory available for the cache.
  749. *
  750. * If browser.cache.memory.capacity is zero, the memory cache is disabled.
  751. *
  752. * If browser.cache.memory.capacity is negative or not present, we use a
  753. * formula that grows less than linearly with the amount of system memory,
  754. * with an upper limit on the cache size. No matter how much physical RAM is
  755. * present, the default cache size would not exceed 32 MB. This maximum would
  756. * apply only to systems with more than 4 GB of RAM (e.g. terminal servers)
  757. *
  758. * RAM Cache
  759. * --- -----
  760. * 32 Mb 2 Mb
  761. * 64 Mb 4 Mb
  762. * 128 Mb 6 Mb
  763. * 256 Mb 10 Mb
  764. * 512 Mb 14 Mb
  765. * 1024 Mb 18 Mb
  766. * 2048 Mb 24 Mb
  767. * 4096 Mb 30 Mb
  768. *
  769. * The equation for this is (for cache size C and memory size K (kbytes)):
  770. * x = log2(K) - 14
  771. * C = x^2/3 + x + 2/3 + 0.1 (0.1 for rounding)
  772. * if (C > 32) C = 32
  773. */
  774. int32_t
  775. nsCacheProfilePrefObserver::MemoryCacheCapacity()
  776. {
  777. int32_t capacity = mMemoryCacheCapacity;
  778. if (capacity >= 0) {
  779. CACHE_LOG_DEBUG(("Memory cache capacity forced to %d\n", capacity));
  780. return capacity;
  781. }
  782. static uint64_t bytes = PR_GetPhysicalMemorySize();
  783. CACHE_LOG_DEBUG(("Physical Memory size is %llu\n", bytes));
  784. // If getting the physical memory failed, arbitrarily assume
  785. // 32 MB of RAM. We use a low default to have a reasonable
  786. // size on all the devices we support.
  787. if (bytes == 0)
  788. bytes = 32 * 1024 * 1024;
  789. // Conversion from unsigned int64_t to double doesn't work on all platforms.
  790. // We need to truncate the value at INT64_MAX to make sure we don't
  791. // overflow.
  792. if (bytes > INT64_MAX)
  793. bytes = INT64_MAX;
  794. uint64_t kbytes = bytes >> 10;
  795. double kBytesD = double(kbytes);
  796. double x = log(kBytesD)/log(2.0) - 14;
  797. if (x > 0) {
  798. capacity = (int32_t)(x * x / 3.0 + x + 2.0 / 3 + 0.1); // 0.1 for rounding
  799. if (capacity > 32)
  800. capacity = 32;
  801. capacity *= 1024;
  802. } else {
  803. capacity = 0;
  804. }
  805. return capacity;
  806. }
  807. int32_t
  808. nsCacheProfilePrefObserver::CacheCompressionLevel()
  809. {
  810. return mCacheCompressionLevel;
  811. }
  812. /******************************************************************************
  813. * nsProcessRequestEvent
  814. *****************************************************************************/
  815. class nsProcessRequestEvent : public Runnable {
  816. public:
  817. explicit nsProcessRequestEvent(nsCacheRequest *aRequest)
  818. {
  819. mRequest = aRequest;
  820. }
  821. NS_IMETHOD Run() override
  822. {
  823. nsresult rv;
  824. NS_ASSERTION(mRequest->mListener,
  825. "Sync OpenCacheEntry() posted to background thread!");
  826. nsCacheServiceAutoLock lock;
  827. rv = nsCacheService::gService->ProcessRequest(mRequest,
  828. false,
  829. nullptr);
  830. // Don't delete the request if it was queued
  831. if (!(mRequest->IsBlocking() &&
  832. rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION))
  833. delete mRequest;
  834. return NS_OK;
  835. }
  836. protected:
  837. virtual ~nsProcessRequestEvent() {}
  838. private:
  839. nsCacheRequest *mRequest;
  840. };
  841. /******************************************************************************
  842. * nsDoomEvent
  843. *****************************************************************************/
  844. class nsDoomEvent : public Runnable {
  845. public:
  846. nsDoomEvent(nsCacheSession *session,
  847. const nsACString &key,
  848. nsICacheListener *listener)
  849. {
  850. mKey = *session->ClientID();
  851. mKey.Append(':');
  852. mKey.Append(key);
  853. mStoragePolicy = session->StoragePolicy();
  854. mListener = listener;
  855. mThread = do_GetCurrentThread();
  856. // We addref the listener here and release it in nsNotifyDoomListener
  857. // on the callers thread. If posting of nsNotifyDoomListener event fails
  858. // we leak the listener which is better than releasing it on a wrong
  859. // thread.
  860. NS_IF_ADDREF(mListener);
  861. }
  862. NS_IMETHOD Run() override
  863. {
  864. nsCacheServiceAutoLock lock;
  865. bool foundActive = true;
  866. nsresult status = NS_ERROR_NOT_AVAILABLE;
  867. nsCacheEntry *entry;
  868. entry = nsCacheService::gService->mActiveEntries.GetEntry(&mKey);
  869. if (!entry) {
  870. bool collision = false;
  871. foundActive = false;
  872. entry = nsCacheService::gService->SearchCacheDevices(&mKey,
  873. mStoragePolicy,
  874. &collision);
  875. }
  876. if (entry) {
  877. status = NS_OK;
  878. nsCacheService::gService->DoomEntry_Internal(entry, foundActive);
  879. }
  880. if (mListener) {
  881. mThread->Dispatch(new nsNotifyDoomListener(mListener, status),
  882. NS_DISPATCH_NORMAL);
  883. // posted event will release the reference on the correct thread
  884. mListener = nullptr;
  885. }
  886. return NS_OK;
  887. }
  888. private:
  889. nsCString mKey;
  890. nsCacheStoragePolicy mStoragePolicy;
  891. nsICacheListener *mListener;
  892. nsCOMPtr<nsIThread> mThread;
  893. };
  894. /******************************************************************************
  895. * nsCacheService
  896. *****************************************************************************/
  897. nsCacheService * nsCacheService::gService = nullptr;
  898. NS_IMPL_ISUPPORTS(nsCacheService, nsICacheService, nsICacheServiceInternal,
  899. nsIMemoryReporter)
  900. nsCacheService::nsCacheService()
  901. : mObserver(nullptr),
  902. mLock("nsCacheService.mLock"),
  903. mCondVar(mLock, "nsCacheService.mCondVar"),
  904. mNotified(false),
  905. mTimeStampLock("nsCacheService.mTimeStampLock"),
  906. mInitialized(false),
  907. mClearingEntries(false),
  908. mEnableMemoryDevice(true),
  909. mEnableDiskDevice(true),
  910. mMemoryDevice(nullptr),
  911. mDiskDevice(nullptr),
  912. mOfflineDevice(nullptr),
  913. mTotalEntries(0),
  914. mCacheHits(0),
  915. mCacheMisses(0),
  916. mMaxKeyLength(0),
  917. mMaxDataSize(0),
  918. mMaxMetaSize(0),
  919. mDeactivateFailures(0),
  920. mDeactivatedUnboundEntries(0)
  921. {
  922. NS_ASSERTION(gService==nullptr, "multiple nsCacheService instances!");
  923. gService = this;
  924. // create list of cache devices
  925. PR_INIT_CLIST(&mDoomedEntries);
  926. }
  927. nsCacheService::~nsCacheService()
  928. {
  929. if (mInitialized) // Shutdown hasn't been called yet.
  930. (void) Shutdown();
  931. if (mObserver) {
  932. mObserver->Remove();
  933. NS_RELEASE(mObserver);
  934. }
  935. gService = nullptr;
  936. }
  937. nsresult
  938. nsCacheService::Init()
  939. {
  940. // Thie method must be called on the main thread because mCacheIOThread must
  941. // only be modified on the main thread.
  942. if (!NS_IsMainThread()) {
  943. NS_ERROR("nsCacheService::Init called off the main thread");
  944. return NS_ERROR_NOT_SAME_THREAD;
  945. }
  946. NS_ASSERTION(!mInitialized, "nsCacheService already initialized.");
  947. if (mInitialized)
  948. return NS_ERROR_ALREADY_INITIALIZED;
  949. if (mozilla::net::IsNeckoChild()) {
  950. return NS_ERROR_UNEXPECTED;
  951. }
  952. nsresult rv;
  953. mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv);
  954. NS_ENSURE_SUCCESS(rv, rv);
  955. rv = NS_NewNamedThread("Cache I/O",
  956. getter_AddRefs(mCacheIOThread));
  957. if (NS_FAILED(rv)) {
  958. NS_RUNTIMEABORT("Can't create cache IO thread");
  959. }
  960. rv = nsDeleteDir::Init();
  961. if (NS_FAILED(rv)) {
  962. NS_WARNING("Can't initialize nsDeleteDir");
  963. }
  964. // initialize hashtable for active cache entries
  965. mActiveEntries.Init();
  966. // create profile/preference observer
  967. if (!mObserver) {
  968. mObserver = new nsCacheProfilePrefObserver();
  969. NS_ADDREF(mObserver);
  970. mObserver->Install();
  971. }
  972. mEnableDiskDevice = mObserver->DiskCacheEnabled();
  973. mEnableOfflineDevice = mObserver->OfflineCacheEnabled();
  974. mEnableMemoryDevice = mObserver->MemoryCacheEnabled();
  975. RegisterWeakMemoryReporter(this);
  976. mInitialized = true;
  977. return NS_OK;
  978. }
  979. void
  980. nsCacheService::Shutdown()
  981. {
  982. // This method must be called on the main thread because mCacheIOThread must
  983. // only be modified on the main thread.
  984. if (!NS_IsMainThread()) {
  985. NS_RUNTIMEABORT("nsCacheService::Shutdown called off the main thread");
  986. }
  987. nsCOMPtr<nsIThread> cacheIOThread;
  988. bool shouldSanitize = false;
  989. nsCOMPtr<nsIFile> parentDir;
  990. {
  991. nsCacheServiceAutoLock lock;
  992. NS_ASSERTION(mInitialized,
  993. "can't shutdown nsCacheService unless it has been initialized.");
  994. if (!mInitialized)
  995. return;
  996. mClearingEntries = true;
  997. DoomActiveEntries(nullptr);
  998. }
  999. CloseAllStreams();
  1000. UnregisterWeakMemoryReporter(this);
  1001. {
  1002. nsCacheServiceAutoLock lock;
  1003. NS_ASSERTION(mInitialized, "Bad state");
  1004. mInitialized = false;
  1005. // Clear entries
  1006. ClearDoomList();
  1007. if (mSmartSizeTimer) {
  1008. mSmartSizeTimer->Cancel();
  1009. mSmartSizeTimer = nullptr;
  1010. }
  1011. // Make sure to wait for any pending cache-operations before
  1012. // proceeding with destructive actions (bug #620660)
  1013. (void) SyncWithCacheIOThread();
  1014. mActiveEntries.Shutdown();
  1015. // obtain the disk cache directory in case we need to sanitize it
  1016. parentDir = mObserver->DiskCacheParentDirectory();
  1017. shouldSanitize = mObserver->SanitizeAtShutdown();
  1018. // deallocate memory and disk caches
  1019. delete mMemoryDevice;
  1020. mMemoryDevice = nullptr;
  1021. delete mDiskDevice;
  1022. mDiskDevice = nullptr;
  1023. if (mOfflineDevice)
  1024. mOfflineDevice->Shutdown();
  1025. NS_IF_RELEASE(mOfflineDevice);
  1026. for (auto iter = mCustomOfflineDevices.Iter();
  1027. !iter.Done(); iter.Next()) {
  1028. iter.Data()->Shutdown();
  1029. iter.Remove();
  1030. }
  1031. LogCacheStatistics();
  1032. mClearingEntries = false;
  1033. mCacheIOThread.swap(cacheIOThread);
  1034. }
  1035. if (cacheIOThread)
  1036. nsShutdownThread::BlockingShutdown(cacheIOThread);
  1037. if (shouldSanitize) {
  1038. nsresult rv = parentDir->AppendNative(NS_LITERAL_CSTRING("Cache"));
  1039. if (NS_SUCCEEDED(rv)) {
  1040. bool exists;
  1041. if (NS_SUCCEEDED(parentDir->Exists(&exists)) && exists)
  1042. nsDeleteDir::DeleteDir(parentDir, false);
  1043. }
  1044. nsDeleteDir::Shutdown(shouldSanitize);
  1045. } else {
  1046. nsDeleteDir::Shutdown(shouldSanitize);
  1047. }
  1048. }
  1049. nsresult
  1050. nsCacheService::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
  1051. {
  1052. nsresult rv;
  1053. if (aOuter != nullptr)
  1054. return NS_ERROR_NO_AGGREGATION;
  1055. nsCacheService * cacheService = new nsCacheService();
  1056. if (cacheService == nullptr)
  1057. return NS_ERROR_OUT_OF_MEMORY;
  1058. NS_ADDREF(cacheService);
  1059. rv = cacheService->Init();
  1060. if (NS_SUCCEEDED(rv)) {
  1061. rv = cacheService->QueryInterface(aIID, aResult);
  1062. }
  1063. NS_RELEASE(cacheService);
  1064. return rv;
  1065. }
  1066. NS_IMETHODIMP
  1067. nsCacheService::CreateSession(const char * clientID,
  1068. nsCacheStoragePolicy storagePolicy,
  1069. bool streamBased,
  1070. nsICacheSession **result)
  1071. {
  1072. *result = nullptr;
  1073. if (net::CacheObserver::UseNewCache())
  1074. return NS_ERROR_NOT_IMPLEMENTED;
  1075. return CreateSessionInternal(clientID, storagePolicy, streamBased, result);
  1076. }
  1077. nsresult
  1078. nsCacheService::CreateSessionInternal(const char * clientID,
  1079. nsCacheStoragePolicy storagePolicy,
  1080. bool streamBased,
  1081. nsICacheSession **result)
  1082. {
  1083. RefPtr<nsCacheSession> session =
  1084. new nsCacheSession(clientID, storagePolicy, streamBased);
  1085. session.forget(result);
  1086. return NS_OK;
  1087. }
  1088. nsresult
  1089. nsCacheService::EvictEntriesForSession(nsCacheSession * session)
  1090. {
  1091. NS_ASSERTION(gService, "nsCacheService::gService is null.");
  1092. return gService->EvictEntriesForClient(session->ClientID()->get(),
  1093. session->StoragePolicy());
  1094. }
  1095. namespace {
  1096. class EvictionNotifierRunnable : public Runnable
  1097. {
  1098. public:
  1099. explicit EvictionNotifierRunnable(nsISupports* aSubject)
  1100. : mSubject(aSubject)
  1101. { }
  1102. NS_DECL_NSIRUNNABLE
  1103. private:
  1104. nsCOMPtr<nsISupports> mSubject;
  1105. };
  1106. NS_IMETHODIMP
  1107. EvictionNotifierRunnable::Run()
  1108. {
  1109. nsCOMPtr<nsIObserverService> obsSvc =
  1110. mozilla::services::GetObserverService();
  1111. if (obsSvc) {
  1112. obsSvc->NotifyObservers(mSubject,
  1113. NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID,
  1114. nullptr);
  1115. }
  1116. return NS_OK;
  1117. }
  1118. } // namespace
  1119. nsresult
  1120. nsCacheService::EvictEntriesForClient(const char * clientID,
  1121. nsCacheStoragePolicy storagePolicy)
  1122. {
  1123. RefPtr<EvictionNotifierRunnable> r =
  1124. new EvictionNotifierRunnable(NS_ISUPPORTS_CAST(nsICacheService*, this));
  1125. NS_DispatchToMainThread(r);
  1126. nsCacheServiceAutoLock lock;
  1127. nsresult res = NS_OK;
  1128. if (storagePolicy == nsICache::STORE_ANYWHERE ||
  1129. storagePolicy == nsICache::STORE_ON_DISK) {
  1130. if (mEnableDiskDevice) {
  1131. nsresult rv = NS_OK;
  1132. if (!mDiskDevice)
  1133. rv = CreateDiskDevice();
  1134. if (mDiskDevice)
  1135. rv = mDiskDevice->EvictEntries(clientID);
  1136. if (NS_FAILED(rv))
  1137. res = rv;
  1138. }
  1139. }
  1140. // Only clear the offline cache if it has been specifically asked for.
  1141. if (storagePolicy == nsICache::STORE_OFFLINE) {
  1142. if (mEnableOfflineDevice) {
  1143. nsresult rv = NS_OK;
  1144. if (!mOfflineDevice)
  1145. rv = CreateOfflineDevice();
  1146. if (mOfflineDevice)
  1147. rv = mOfflineDevice->EvictEntries(clientID);
  1148. if (NS_FAILED(rv))
  1149. res = rv;
  1150. }
  1151. }
  1152. if (storagePolicy == nsICache::STORE_ANYWHERE ||
  1153. storagePolicy == nsICache::STORE_IN_MEMORY) {
  1154. // If there is no memory device, there is no need to evict it...
  1155. if (mMemoryDevice) {
  1156. nsresult rv = mMemoryDevice->EvictEntries(clientID);
  1157. if (NS_FAILED(rv))
  1158. res = rv;
  1159. }
  1160. }
  1161. return res;
  1162. }
  1163. nsresult
  1164. nsCacheService::IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy,
  1165. bool * result)
  1166. {
  1167. if (gService == nullptr) return NS_ERROR_NOT_AVAILABLE;
  1168. nsCacheServiceAutoLock lock;
  1169. *result = gService->IsStorageEnabledForPolicy_Locked(storagePolicy);
  1170. return NS_OK;
  1171. }
  1172. nsresult
  1173. nsCacheService::DoomEntry(nsCacheSession *session,
  1174. const nsACString &key,
  1175. nsICacheListener *listener)
  1176. {
  1177. CACHE_LOG_DEBUG(("Dooming entry for session %p, key %s\n",
  1178. session, PromiseFlatCString(key).get()));
  1179. if (!gService || !gService->mInitialized)
  1180. return NS_ERROR_NOT_INITIALIZED;
  1181. return DispatchToCacheIOThread(new nsDoomEvent(session, key, listener));
  1182. }
  1183. bool
  1184. nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy storagePolicy)
  1185. {
  1186. if (gService->mEnableMemoryDevice &&
  1187. (storagePolicy == nsICache::STORE_ANYWHERE ||
  1188. storagePolicy == nsICache::STORE_IN_MEMORY)) {
  1189. return true;
  1190. }
  1191. if (gService->mEnableDiskDevice &&
  1192. (storagePolicy == nsICache::STORE_ANYWHERE ||
  1193. storagePolicy == nsICache::STORE_ON_DISK)) {
  1194. return true;
  1195. }
  1196. if (gService->mEnableOfflineDevice &&
  1197. storagePolicy == nsICache::STORE_OFFLINE) {
  1198. return true;
  1199. }
  1200. return false;
  1201. }
  1202. NS_IMETHODIMP nsCacheService::VisitEntries(nsICacheVisitor *visitor)
  1203. {
  1204. if (net::CacheObserver::UseNewCache())
  1205. return NS_ERROR_NOT_IMPLEMENTED;
  1206. return VisitEntriesInternal(visitor);
  1207. }
  1208. nsresult nsCacheService::VisitEntriesInternal(nsICacheVisitor *visitor)
  1209. {
  1210. NS_ENSURE_ARG_POINTER(visitor);
  1211. nsCacheServiceAutoLock lock;
  1212. if (!(mEnableDiskDevice || mEnableMemoryDevice))
  1213. return NS_ERROR_NOT_AVAILABLE;
  1214. // XXX record the fact that a visitation is in progress,
  1215. // XXX i.e. keep list of visitors in progress.
  1216. nsresult rv = NS_OK;
  1217. // If there is no memory device, there are then also no entries to visit...
  1218. if (mMemoryDevice) {
  1219. rv = mMemoryDevice->Visit(visitor);
  1220. if (NS_FAILED(rv)) return rv;
  1221. }
  1222. if (mEnableDiskDevice) {
  1223. if (!mDiskDevice) {
  1224. rv = CreateDiskDevice();
  1225. if (NS_FAILED(rv)) return rv;
  1226. }
  1227. rv = mDiskDevice->Visit(visitor);
  1228. if (NS_FAILED(rv)) return rv;
  1229. }
  1230. if (mEnableOfflineDevice) {
  1231. if (!mOfflineDevice) {
  1232. rv = CreateOfflineDevice();
  1233. if (NS_FAILED(rv)) return rv;
  1234. }
  1235. rv = mOfflineDevice->Visit(visitor);
  1236. if (NS_FAILED(rv)) return rv;
  1237. }
  1238. // XXX notify any shutdown process that visitation is complete for THIS visitor.
  1239. // XXX keep queue of visitors
  1240. return NS_OK;
  1241. }
  1242. void nsCacheService::FireClearNetworkCacheStoredAnywhereNotification()
  1243. {
  1244. MOZ_ASSERT(NS_IsMainThread());
  1245. nsCOMPtr<nsIObserverService> obsvc = mozilla::services::GetObserverService();
  1246. if (obsvc) {
  1247. obsvc->NotifyObservers(nullptr,
  1248. "network-clear-cache-stored-anywhere",
  1249. nullptr);
  1250. }
  1251. }
  1252. NS_IMETHODIMP nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy)
  1253. {
  1254. if (net::CacheObserver::UseNewCache())
  1255. return NS_ERROR_NOT_IMPLEMENTED;
  1256. return EvictEntriesInternal(storagePolicy);
  1257. }
  1258. nsresult nsCacheService::EvictEntriesInternal(nsCacheStoragePolicy storagePolicy)
  1259. {
  1260. if (storagePolicy == nsICache::STORE_ANYWHERE) {
  1261. // if not called on main thread, dispatch the notification to the main thread to notify observers
  1262. if (!NS_IsMainThread()) {
  1263. nsCOMPtr<nsIRunnable> event = NewRunnableMethod(this,
  1264. &nsCacheService::FireClearNetworkCacheStoredAnywhereNotification);
  1265. NS_DispatchToMainThread(event);
  1266. } else {
  1267. // else you're already on main thread - notify observers
  1268. FireClearNetworkCacheStoredAnywhereNotification();
  1269. }
  1270. }
  1271. return EvictEntriesForClient(nullptr, storagePolicy);
  1272. }
  1273. NS_IMETHODIMP nsCacheService::GetCacheIOTarget(nsIEventTarget * *aCacheIOTarget)
  1274. {
  1275. NS_ENSURE_ARG_POINTER(aCacheIOTarget);
  1276. // Because mCacheIOThread can only be changed on the main thread, it can be
  1277. // read from the main thread without the lock. This is useful to prevent
  1278. // blocking the main thread on other cache operations.
  1279. if (!NS_IsMainThread()) {
  1280. Lock();
  1281. }
  1282. nsresult rv;
  1283. if (mCacheIOThread) {
  1284. NS_ADDREF(*aCacheIOTarget = mCacheIOThread);
  1285. rv = NS_OK;
  1286. } else {
  1287. *aCacheIOTarget = nullptr;
  1288. rv = NS_ERROR_NOT_AVAILABLE;
  1289. }
  1290. if (!NS_IsMainThread()) {
  1291. Unlock();
  1292. }
  1293. return rv;
  1294. }
  1295. NS_IMETHODIMP nsCacheService::GetLockHeldTime(double *aLockHeldTime)
  1296. {
  1297. MutexAutoLock lock(mTimeStampLock);
  1298. if (mLockAcquiredTimeStamp.IsNull()) {
  1299. *aLockHeldTime = 0.0;
  1300. }
  1301. else {
  1302. *aLockHeldTime =
  1303. (TimeStamp::Now() - mLockAcquiredTimeStamp).ToMilliseconds();
  1304. }
  1305. return NS_OK;
  1306. }
  1307. /**
  1308. * Internal Methods
  1309. */
  1310. nsresult
  1311. nsCacheService::CreateDiskDevice()
  1312. {
  1313. if (!mInitialized) return NS_ERROR_NOT_AVAILABLE;
  1314. if (!mEnableDiskDevice) return NS_ERROR_NOT_AVAILABLE;
  1315. if (mDiskDevice) return NS_OK;
  1316. mDiskDevice = new nsDiskCacheDevice;
  1317. if (!mDiskDevice) return NS_ERROR_OUT_OF_MEMORY;
  1318. // set the preferences
  1319. mDiskDevice->SetCacheParentDirectory(mObserver->DiskCacheParentDirectory());
  1320. mDiskDevice->SetCapacity(mObserver->DiskCacheCapacity());
  1321. mDiskDevice->SetMaxEntrySize(mObserver->DiskCacheMaxEntrySize());
  1322. nsresult rv = mDiskDevice->Init();
  1323. if (NS_FAILED(rv)) {
  1324. #if DEBUG
  1325. printf("###\n");
  1326. printf("### mDiskDevice->Init() failed (0x%.8x)\n",
  1327. static_cast<uint32_t>(rv));
  1328. printf("### - disabling disk cache for this session.\n");
  1329. printf("###\n");
  1330. #endif
  1331. mEnableDiskDevice = false;
  1332. delete mDiskDevice;
  1333. mDiskDevice = nullptr;
  1334. return rv;
  1335. }
  1336. NS_ASSERTION(!mSmartSizeTimer, "Smartsize timer was already fired!");
  1337. // Disk device is usually created during the startup. Delay smart size
  1338. // calculation to avoid possible massive IO caused by eviction of entries
  1339. // in case the new smart size is smaller than current cache usage.
  1340. mSmartSizeTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
  1341. if (NS_SUCCEEDED(rv)) {
  1342. rv = mSmartSizeTimer->InitWithCallback(new nsSetDiskSmartSizeCallback(),
  1343. 1000*60*3,
  1344. nsITimer::TYPE_ONE_SHOT);
  1345. if (NS_FAILED(rv)) {
  1346. NS_WARNING("Failed to post smart size timer");
  1347. mSmartSizeTimer = nullptr;
  1348. }
  1349. } else {
  1350. NS_WARNING("Can't create smart size timer");
  1351. }
  1352. // Ignore state of the timer and return success since the purpose of the
  1353. // method (create the disk-device) has been fulfilled
  1354. return NS_OK;
  1355. }
  1356. // Runnable sent from cache thread to main thread
  1357. class nsDisableOldMaxSmartSizePrefEvent: public Runnable
  1358. {
  1359. public:
  1360. nsDisableOldMaxSmartSizePrefEvent() {}
  1361. NS_IMETHOD Run() override
  1362. {
  1363. // Main thread may have already called nsCacheService::Shutdown
  1364. if (!nsCacheService::IsInitialized())
  1365. return NS_ERROR_NOT_AVAILABLE;
  1366. nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
  1367. if (!branch) {
  1368. return NS_ERROR_NOT_AVAILABLE;
  1369. }
  1370. nsresult rv = branch->SetBoolPref(DISK_CACHE_USE_OLD_MAX_SMART_SIZE_PREF, false);
  1371. if (NS_FAILED(rv)) {
  1372. NS_WARNING("Failed to disable old max smart size");
  1373. return rv;
  1374. }
  1375. // It is safe to call SetDiskSmartSize_Locked() without holding the lock
  1376. // when we are on main thread and nsCacheService is initialized.
  1377. nsCacheService::gService->SetDiskSmartSize_Locked();
  1378. if (nsCacheService::gService->mObserver->PermittedToSmartSize(branch, false)) {
  1379. rv = branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, MAX_CACHE_SIZE);
  1380. if (NS_FAILED(rv)) {
  1381. NS_WARNING("Failed to set cache capacity pref");
  1382. }
  1383. }
  1384. return NS_OK;
  1385. }
  1386. };
  1387. void
  1388. nsCacheService::MarkStartingFresh()
  1389. {
  1390. if (!gService || !gService->mObserver->ShouldUseOldMaxSmartSize()) {
  1391. // Already using new max, nothing to do here
  1392. return;
  1393. }
  1394. gService->mObserver->SetUseNewMaxSmartSize(true);
  1395. // We always dispatch an event here because we don't want to deal with lock
  1396. // reentrance issues.
  1397. NS_DispatchToMainThread(new nsDisableOldMaxSmartSizePrefEvent());
  1398. }
  1399. nsresult
  1400. nsCacheService::GetOfflineDevice(nsOfflineCacheDevice **aDevice)
  1401. {
  1402. if (!mOfflineDevice) {
  1403. nsresult rv = CreateOfflineDevice();
  1404. NS_ENSURE_SUCCESS(rv, rv);
  1405. }
  1406. NS_ADDREF(*aDevice = mOfflineDevice);
  1407. return NS_OK;
  1408. }
  1409. nsresult
  1410. nsCacheService::GetCustomOfflineDevice(nsIFile *aProfileDir,
  1411. int32_t aQuota,
  1412. nsOfflineCacheDevice **aDevice)
  1413. {
  1414. nsresult rv;
  1415. nsAutoString profilePath;
  1416. rv = aProfileDir->GetPath(profilePath);
  1417. NS_ENSURE_SUCCESS(rv, rv);
  1418. if (!mCustomOfflineDevices.Get(profilePath, aDevice)) {
  1419. rv = CreateCustomOfflineDevice(aProfileDir, aQuota, aDevice);
  1420. NS_ENSURE_SUCCESS(rv, rv);
  1421. (*aDevice)->SetAutoShutdown();
  1422. mCustomOfflineDevices.Put(profilePath, *aDevice);
  1423. }
  1424. return NS_OK;
  1425. }
  1426. nsresult
  1427. nsCacheService::CreateOfflineDevice()
  1428. {
  1429. CACHE_LOG_INFO(("Creating default offline device"));
  1430. if (mOfflineDevice) return NS_OK;
  1431. if (!nsCacheService::IsInitialized()) {
  1432. return NS_ERROR_NOT_AVAILABLE;
  1433. }
  1434. nsresult rv = CreateCustomOfflineDevice(
  1435. mObserver->OfflineCacheParentDirectory(),
  1436. mObserver->OfflineCacheCapacity(),
  1437. &mOfflineDevice);
  1438. NS_ENSURE_SUCCESS(rv, rv);
  1439. return NS_OK;
  1440. }
  1441. nsresult
  1442. nsCacheService::CreateCustomOfflineDevice(nsIFile *aProfileDir,
  1443. int32_t aQuota,
  1444. nsOfflineCacheDevice **aDevice)
  1445. {
  1446. NS_ENSURE_ARG(aProfileDir);
  1447. if (MOZ_LOG_TEST(gCacheLog, LogLevel::Info)) {
  1448. nsAutoCString profilePath;
  1449. aProfileDir->GetNativePath(profilePath);
  1450. CACHE_LOG_INFO(("Creating custom offline device, %s, %d",
  1451. profilePath.BeginReading(), aQuota));
  1452. }
  1453. if (!mInitialized) return NS_ERROR_NOT_AVAILABLE;
  1454. if (!mEnableOfflineDevice) return NS_ERROR_NOT_AVAILABLE;
  1455. *aDevice = new nsOfflineCacheDevice;
  1456. NS_ADDREF(*aDevice);
  1457. // set the preferences
  1458. (*aDevice)->SetCacheParentDirectory(aProfileDir);
  1459. (*aDevice)->SetCapacity(aQuota);
  1460. nsresult rv = (*aDevice)->InitWithSqlite(mStorageService);
  1461. if (NS_FAILED(rv)) {
  1462. CACHE_LOG_DEBUG(("OfflineDevice->InitWithSqlite() failed (0x%.8x)\n", rv));
  1463. CACHE_LOG_DEBUG((" - disabling offline cache for this session.\n"));
  1464. NS_RELEASE(*aDevice);
  1465. }
  1466. return rv;
  1467. }
  1468. nsresult
  1469. nsCacheService::CreateMemoryDevice()
  1470. {
  1471. if (!mInitialized) return NS_ERROR_NOT_AVAILABLE;
  1472. if (!mEnableMemoryDevice) return NS_ERROR_NOT_AVAILABLE;
  1473. if (mMemoryDevice) return NS_OK;
  1474. mMemoryDevice = new nsMemoryCacheDevice;
  1475. if (!mMemoryDevice) return NS_ERROR_OUT_OF_MEMORY;
  1476. // set preference
  1477. int32_t capacity = mObserver->MemoryCacheCapacity();
  1478. CACHE_LOG_DEBUG(("Creating memory device with capacity %d\n", capacity));
  1479. mMemoryDevice->SetCapacity(capacity);
  1480. mMemoryDevice->SetMaxEntrySize(mObserver->MemoryCacheMaxEntrySize());
  1481. nsresult rv = mMemoryDevice->Init();
  1482. if (NS_FAILED(rv)) {
  1483. NS_WARNING("Initialization of Memory Cache failed.");
  1484. delete mMemoryDevice;
  1485. mMemoryDevice = nullptr;
  1486. }
  1487. return rv;
  1488. }
  1489. nsresult
  1490. nsCacheService::RemoveCustomOfflineDevice(nsOfflineCacheDevice *aDevice)
  1491. {
  1492. nsCOMPtr<nsIFile> profileDir = aDevice->BaseDirectory();
  1493. if (!profileDir)
  1494. return NS_ERROR_UNEXPECTED;
  1495. nsAutoString profilePath;
  1496. nsresult rv = profileDir->GetPath(profilePath);
  1497. NS_ENSURE_SUCCESS(rv, rv);
  1498. mCustomOfflineDevices.Remove(profilePath);
  1499. return NS_OK;
  1500. }
  1501. nsresult
  1502. nsCacheService::CreateRequest(nsCacheSession * session,
  1503. const nsACString & clientKey,
  1504. nsCacheAccessMode accessRequested,
  1505. bool blockingMode,
  1506. nsICacheListener * listener,
  1507. nsCacheRequest ** request)
  1508. {
  1509. NS_ASSERTION(request, "CreateRequest: request is null");
  1510. nsAutoCString key(*session->ClientID());
  1511. key.Append(':');
  1512. key.Append(clientKey);
  1513. if (mMaxKeyLength < key.Length()) mMaxKeyLength = key.Length();
  1514. // create request
  1515. *request = new nsCacheRequest(key, listener, accessRequested,
  1516. blockingMode, session);
  1517. if (!listener) return NS_OK; // we're sync, we're done.
  1518. // get the request's thread
  1519. (*request)->mThread = do_GetCurrentThread();
  1520. return NS_OK;
  1521. }
  1522. class nsCacheListenerEvent : public Runnable
  1523. {
  1524. public:
  1525. nsCacheListenerEvent(nsICacheListener *listener,
  1526. nsICacheEntryDescriptor *descriptor,
  1527. nsCacheAccessMode accessGranted,
  1528. nsresult status)
  1529. : mListener(listener) // transfers reference
  1530. , mDescriptor(descriptor) // transfers reference (may be null)
  1531. , mAccessGranted(accessGranted)
  1532. , mStatus(status)
  1533. {}
  1534. NS_IMETHOD Run() override
  1535. {
  1536. mListener->OnCacheEntryAvailable(mDescriptor, mAccessGranted, mStatus);
  1537. NS_RELEASE(mListener);
  1538. NS_IF_RELEASE(mDescriptor);
  1539. return NS_OK;
  1540. }
  1541. private:
  1542. // We explicitly leak mListener or mDescriptor if Run is not called
  1543. // because otherwise we cannot guarantee that they are destroyed on
  1544. // the right thread.
  1545. nsICacheListener *mListener;
  1546. nsICacheEntryDescriptor *mDescriptor;
  1547. nsCacheAccessMode mAccessGranted;
  1548. nsresult mStatus;
  1549. };
  1550. nsresult
  1551. nsCacheService::NotifyListener(nsCacheRequest * request,
  1552. nsICacheEntryDescriptor * descriptor,
  1553. nsCacheAccessMode accessGranted,
  1554. nsresult status)
  1555. {
  1556. NS_ASSERTION(request->mThread, "no thread set in async request!");
  1557. // Swap ownership, and release listener on target thread...
  1558. nsICacheListener *listener = request->mListener;
  1559. request->mListener = nullptr;
  1560. nsCOMPtr<nsIRunnable> ev =
  1561. new nsCacheListenerEvent(listener, descriptor,
  1562. accessGranted, status);
  1563. if (!ev) {
  1564. // Better to leak listener and descriptor if we fail because we don't
  1565. // want to destroy them inside the cache service lock or on potentially
  1566. // the wrong thread.
  1567. return NS_ERROR_OUT_OF_MEMORY;
  1568. }
  1569. return request->mThread->Dispatch(ev, NS_DISPATCH_NORMAL);
  1570. }
  1571. nsresult
  1572. nsCacheService::ProcessRequest(nsCacheRequest * request,
  1573. bool calledFromOpenCacheEntry,
  1574. nsICacheEntryDescriptor ** result)
  1575. {
  1576. // !!! must be called with mLock held !!!
  1577. nsresult rv;
  1578. nsCacheEntry * entry = nullptr;
  1579. nsCacheEntry * doomedEntry = nullptr;
  1580. nsCacheAccessMode accessGranted = nsICache::ACCESS_NONE;
  1581. if (result) *result = nullptr;
  1582. while(1) { // Activate entry loop
  1583. rv = ActivateEntry(request, &entry, &doomedEntry); // get the entry for this request
  1584. if (NS_FAILED(rv)) break;
  1585. while(1) { // Request Access loop
  1586. NS_ASSERTION(entry, "no entry in Request Access loop!");
  1587. // entry->RequestAccess queues request on entry
  1588. rv = entry->RequestAccess(request, &accessGranted);
  1589. if (rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION) break;
  1590. if (request->IsBlocking()) {
  1591. if (request->mListener) {
  1592. // async exits - validate, doom, or close will resume
  1593. return rv;
  1594. }
  1595. // XXX this is probably wrong...
  1596. Unlock();
  1597. rv = request->WaitForValidation();
  1598. Lock();
  1599. }
  1600. PR_REMOVE_AND_INIT_LINK(request);
  1601. if (NS_FAILED(rv)) break; // non-blocking mode returns WAIT_FOR_VALIDATION error
  1602. // okay, we're ready to process this request, request access again
  1603. }
  1604. if (rv != NS_ERROR_CACHE_ENTRY_DOOMED) break;
  1605. if (entry->IsNotInUse()) {
  1606. // this request was the last one keeping it around, so get rid of it
  1607. DeactivateEntry(entry);
  1608. }
  1609. // loop back around to look for another entry
  1610. }
  1611. if (NS_SUCCEEDED(rv) && request->mProfileDir) {
  1612. // Custom cache directory has been demanded. Preset the cache device.
  1613. if (entry->StoragePolicy() != nsICache::STORE_OFFLINE) {
  1614. // Failsafe check: this is implemented only for offline cache atm.
  1615. rv = NS_ERROR_FAILURE;
  1616. } else {
  1617. RefPtr<nsOfflineCacheDevice> customCacheDevice;
  1618. rv = GetCustomOfflineDevice(request->mProfileDir, -1,
  1619. getter_AddRefs(customCacheDevice));
  1620. if (NS_SUCCEEDED(rv))
  1621. entry->SetCustomCacheDevice(customCacheDevice);
  1622. }
  1623. }
  1624. nsICacheEntryDescriptor *descriptor = nullptr;
  1625. if (NS_SUCCEEDED(rv))
  1626. rv = entry->CreateDescriptor(request, accessGranted, &descriptor);
  1627. // If doomedEntry is set, ActivatEntry() doomed an existing entry and
  1628. // created a new one for that cache-key. However, any pending requests
  1629. // on the doomed entry were not processed and we need to do that here.
  1630. // This must be done after adding the created entry to list of active
  1631. // entries (which is done in ActivateEntry()) otherwise the hashkeys crash
  1632. // (see bug ##561313). It is also important to do this after creating a
  1633. // descriptor for this request, or some other request may end up being
  1634. // executed first for the newly created entry.
  1635. // Finally, it is worth to emphasize that if doomedEntry is set,
  1636. // ActivateEntry() created a new entry for the request, which will be
  1637. // initialized by RequestAccess() and they both should have returned NS_OK.
  1638. if (doomedEntry) {
  1639. (void) ProcessPendingRequests(doomedEntry);
  1640. if (doomedEntry->IsNotInUse())
  1641. DeactivateEntry(doomedEntry);
  1642. doomedEntry = nullptr;
  1643. }
  1644. if (request->mListener) { // Asynchronous
  1645. if (NS_FAILED(rv) && calledFromOpenCacheEntry && request->IsBlocking())
  1646. return rv; // skip notifying listener, just return rv to caller
  1647. // call listener to report error or descriptor
  1648. nsresult rv2 = NotifyListener(request, descriptor, accessGranted, rv);
  1649. if (NS_FAILED(rv2) && NS_SUCCEEDED(rv)) {
  1650. rv = rv2; // trigger delete request
  1651. }
  1652. } else { // Synchronous
  1653. *result = descriptor;
  1654. }
  1655. return rv;
  1656. }
  1657. nsresult
  1658. nsCacheService::OpenCacheEntry(nsCacheSession * session,
  1659. const nsACString & key,
  1660. nsCacheAccessMode accessRequested,
  1661. bool blockingMode,
  1662. nsICacheListener * listener,
  1663. nsICacheEntryDescriptor ** result)
  1664. {
  1665. CACHE_LOG_DEBUG(("Opening entry for session %p, key %s, mode %d, blocking %d\n",
  1666. session, PromiseFlatCString(key).get(), accessRequested,
  1667. blockingMode));
  1668. if (result)
  1669. *result = nullptr;
  1670. if (!gService || !gService->mInitialized)
  1671. return NS_ERROR_NOT_INITIALIZED;
  1672. nsCacheRequest * request = nullptr;
  1673. nsresult rv = gService->CreateRequest(session,
  1674. key,
  1675. accessRequested,
  1676. blockingMode,
  1677. listener,
  1678. &request);
  1679. if (NS_FAILED(rv)) return rv;
  1680. CACHE_LOG_DEBUG(("Created request %p\n", request));
  1681. // Process the request on the background thread if we are on the main thread
  1682. // and the the request is asynchronous
  1683. if (NS_IsMainThread() && listener && gService->mCacheIOThread) {
  1684. nsCOMPtr<nsIRunnable> ev =
  1685. new nsProcessRequestEvent(request);
  1686. rv = DispatchToCacheIOThread(ev);
  1687. // delete request if we didn't post the event
  1688. if (NS_FAILED(rv))
  1689. delete request;
  1690. }
  1691. else {
  1692. nsCacheServiceAutoLock lock;
  1693. rv = gService->ProcessRequest(request, true, result);
  1694. // delete requests that have completed
  1695. if (!(listener && blockingMode &&
  1696. (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)))
  1697. delete request;
  1698. }
  1699. return rv;
  1700. }
  1701. nsresult
  1702. nsCacheService::ActivateEntry(nsCacheRequest * request,
  1703. nsCacheEntry ** result,
  1704. nsCacheEntry ** doomedEntry)
  1705. {
  1706. CACHE_LOG_DEBUG(("Activate entry for request %p\n", request));
  1707. if (!mInitialized || mClearingEntries)
  1708. return NS_ERROR_NOT_AVAILABLE;
  1709. nsresult rv = NS_OK;
  1710. NS_ASSERTION(request != nullptr, "ActivateEntry called with no request");
  1711. if (result) *result = nullptr;
  1712. if (doomedEntry) *doomedEntry = nullptr;
  1713. if ((!request) || (!result) || (!doomedEntry))
  1714. return NS_ERROR_NULL_POINTER;
  1715. // check if the request can be satisfied
  1716. if (!mEnableMemoryDevice && !request->IsStreamBased())
  1717. return NS_ERROR_FAILURE;
  1718. if (!IsStorageEnabledForPolicy_Locked(request->StoragePolicy()))
  1719. return NS_ERROR_FAILURE;
  1720. // search active entries (including those not bound to device)
  1721. nsCacheEntry *entry = mActiveEntries.GetEntry(&(request->mKey));
  1722. CACHE_LOG_DEBUG(("Active entry for request %p is %p\n", request, entry));
  1723. if (!entry) {
  1724. // search cache devices for entry
  1725. bool collision = false;
  1726. entry = SearchCacheDevices(&(request->mKey), request->StoragePolicy(), &collision);
  1727. CACHE_LOG_DEBUG(("Device search for request %p returned %p\n",
  1728. request, entry));
  1729. // When there is a hashkey collision just refuse to cache it...
  1730. if (collision) return NS_ERROR_CACHE_IN_USE;
  1731. if (entry) entry->MarkInitialized();
  1732. } else {
  1733. NS_ASSERTION(entry->IsActive(), "Inactive entry found in mActiveEntries!");
  1734. }
  1735. if (entry) {
  1736. ++mCacheHits;
  1737. entry->Fetched();
  1738. } else {
  1739. ++mCacheMisses;
  1740. }
  1741. if (entry &&
  1742. ((request->AccessRequested() == nsICache::ACCESS_WRITE) ||
  1743. ((request->StoragePolicy() != nsICache::STORE_OFFLINE) &&
  1744. (entry->mExpirationTime <= SecondsFromPRTime(PR_Now()) &&
  1745. request->WillDoomEntriesIfExpired()))))
  1746. {
  1747. // this is FORCE-WRITE request or the entry has expired
  1748. // we doom entry without processing pending requests, but store it in
  1749. // doomedEntry which causes pending requests to be processed below
  1750. rv = DoomEntry_Internal(entry, false);
  1751. *doomedEntry = entry;
  1752. if (NS_FAILED(rv)) {
  1753. // XXX what to do? Increment FailedDooms counter?
  1754. }
  1755. entry = nullptr;
  1756. }
  1757. if (!entry) {
  1758. if (! (request->AccessRequested() & nsICache::ACCESS_WRITE)) {
  1759. // this is a READ-ONLY request
  1760. rv = NS_ERROR_CACHE_KEY_NOT_FOUND;
  1761. goto error;
  1762. }
  1763. entry = new nsCacheEntry(request->mKey,
  1764. request->IsStreamBased(),
  1765. request->StoragePolicy());
  1766. if (!entry)
  1767. return NS_ERROR_OUT_OF_MEMORY;
  1768. if (request->IsPrivate())
  1769. entry->MarkPrivate();
  1770. entry->Fetched();
  1771. ++mTotalEntries;
  1772. // XXX we could perform an early bind in some cases based on storage policy
  1773. }
  1774. if (!entry->IsActive()) {
  1775. rv = mActiveEntries.AddEntry(entry);
  1776. if (NS_FAILED(rv)) goto error;
  1777. CACHE_LOG_DEBUG(("Added entry %p to mActiveEntries\n", entry));
  1778. entry->MarkActive(); // mark entry active, because it's now in mActiveEntries
  1779. }
  1780. *result = entry;
  1781. return NS_OK;
  1782. error:
  1783. *result = nullptr;
  1784. delete entry;
  1785. return rv;
  1786. }
  1787. nsCacheEntry *
  1788. nsCacheService::SearchCacheDevices(nsCString * key, nsCacheStoragePolicy policy, bool *collision)
  1789. {
  1790. nsCacheEntry * entry = nullptr;
  1791. CACHE_LOG_DEBUG(("mMemoryDevice: 0x%p\n", mMemoryDevice));
  1792. *collision = false;
  1793. if ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_IN_MEMORY)) {
  1794. // If there is no memory device, then there is nothing to search...
  1795. if (mMemoryDevice) {
  1796. entry = mMemoryDevice->FindEntry(key, collision);
  1797. CACHE_LOG_DEBUG(("Searching mMemoryDevice for key %s found: 0x%p, "
  1798. "collision: %d\n", key->get(), entry, collision));
  1799. }
  1800. }
  1801. if (!entry &&
  1802. ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_ON_DISK))) {
  1803. if (mEnableDiskDevice) {
  1804. if (!mDiskDevice) {
  1805. nsresult rv = CreateDiskDevice();
  1806. if (NS_FAILED(rv))
  1807. return nullptr;
  1808. }
  1809. entry = mDiskDevice->FindEntry(key, collision);
  1810. }
  1811. }
  1812. if (!entry && (policy == nsICache::STORE_OFFLINE ||
  1813. (policy == nsICache::STORE_ANYWHERE &&
  1814. gIOService->IsOffline()))) {
  1815. if (mEnableOfflineDevice) {
  1816. if (!mOfflineDevice) {
  1817. nsresult rv = CreateOfflineDevice();
  1818. if (NS_FAILED(rv))
  1819. return nullptr;
  1820. }
  1821. entry = mOfflineDevice->FindEntry(key, collision);
  1822. }
  1823. }
  1824. return entry;
  1825. }
  1826. nsCacheDevice *
  1827. nsCacheService::EnsureEntryHasDevice(nsCacheEntry * entry)
  1828. {
  1829. nsCacheDevice * device = entry->CacheDevice();
  1830. // return device if found, possibly null if the entry is doomed i.e prevent
  1831. // doomed entries to bind to a device (see e.g. bugs #548406 and #596443)
  1832. if (device || entry->IsDoomed()) return device;
  1833. int64_t predictedDataSize = entry->PredictedDataSize();
  1834. if (entry->IsStreamData() && entry->IsAllowedOnDisk() && mEnableDiskDevice) {
  1835. // this is the default
  1836. if (!mDiskDevice) {
  1837. (void)CreateDiskDevice(); // ignore the error (check for mDiskDevice instead)
  1838. }
  1839. if (mDiskDevice) {
  1840. // Bypass the cache if Content-Length says the entry will be too big
  1841. if (predictedDataSize != -1 &&
  1842. mDiskDevice->EntryIsTooBig(predictedDataSize)) {
  1843. DebugOnly<nsresult> rv = nsCacheService::DoomEntry(entry);
  1844. NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
  1845. return nullptr;
  1846. }
  1847. entry->MarkBinding(); // enter state of binding
  1848. nsresult rv = mDiskDevice->BindEntry(entry);
  1849. entry->ClearBinding(); // exit state of binding
  1850. if (NS_SUCCEEDED(rv))
  1851. device = mDiskDevice;
  1852. }
  1853. }
  1854. // if we can't use mDiskDevice, try mMemoryDevice
  1855. if (!device && mEnableMemoryDevice && entry->IsAllowedInMemory()) {
  1856. if (!mMemoryDevice) {
  1857. (void)CreateMemoryDevice(); // ignore the error (check for mMemoryDevice instead)
  1858. }
  1859. if (mMemoryDevice) {
  1860. // Bypass the cache if Content-Length says entry will be too big
  1861. if (predictedDataSize != -1 &&
  1862. mMemoryDevice->EntryIsTooBig(predictedDataSize)) {
  1863. DebugOnly<nsresult> rv = nsCacheService::DoomEntry(entry);
  1864. NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed.");
  1865. return nullptr;
  1866. }
  1867. entry->MarkBinding(); // enter state of binding
  1868. nsresult rv = mMemoryDevice->BindEntry(entry);
  1869. entry->ClearBinding(); // exit state of binding
  1870. if (NS_SUCCEEDED(rv))
  1871. device = mMemoryDevice;
  1872. }
  1873. }
  1874. if (!device && entry->IsStreamData() &&
  1875. entry->IsAllowedOffline() && mEnableOfflineDevice) {
  1876. if (!mOfflineDevice) {
  1877. (void)CreateOfflineDevice(); // ignore the error (check for mOfflineDevice instead)
  1878. }
  1879. device = entry->CustomCacheDevice()
  1880. ? entry->CustomCacheDevice()
  1881. : mOfflineDevice;
  1882. if (device) {
  1883. entry->MarkBinding();
  1884. nsresult rv = device->BindEntry(entry);
  1885. entry->ClearBinding();
  1886. if (NS_FAILED(rv))
  1887. device = nullptr;
  1888. }
  1889. }
  1890. if (device)
  1891. entry->SetCacheDevice(device);
  1892. return device;
  1893. }
  1894. nsresult
  1895. nsCacheService::DoomEntry(nsCacheEntry * entry)
  1896. {
  1897. return gService->DoomEntry_Internal(entry, true);
  1898. }
  1899. nsresult
  1900. nsCacheService::DoomEntry_Internal(nsCacheEntry * entry,
  1901. bool doProcessPendingRequests)
  1902. {
  1903. if (entry->IsDoomed()) return NS_OK;
  1904. CACHE_LOG_DEBUG(("Dooming entry %p\n", entry));
  1905. nsresult rv = NS_OK;
  1906. entry->MarkDoomed();
  1907. NS_ASSERTION(!entry->IsBinding(), "Dooming entry while binding device.");
  1908. nsCacheDevice * device = entry->CacheDevice();
  1909. if (device) device->DoomEntry(entry);
  1910. if (entry->IsActive()) {
  1911. // remove from active entries
  1912. mActiveEntries.RemoveEntry(entry);
  1913. CACHE_LOG_DEBUG(("Removed entry %p from mActiveEntries\n", entry));
  1914. entry->MarkInactive();
  1915. }
  1916. // put on doom list to wait for descriptors to close
  1917. NS_ASSERTION(PR_CLIST_IS_EMPTY(entry), "doomed entry still on device list");
  1918. PR_APPEND_LINK(entry, &mDoomedEntries);
  1919. // handle pending requests only if we're supposed to
  1920. if (doProcessPendingRequests) {
  1921. // tell pending requests to get on with their lives...
  1922. rv = ProcessPendingRequests(entry);
  1923. // All requests have been removed, but there may still be open descriptors
  1924. if (entry->IsNotInUse()) {
  1925. DeactivateEntry(entry); // tell device to get rid of it
  1926. }
  1927. }
  1928. return rv;
  1929. }
  1930. void
  1931. nsCacheService::OnProfileShutdown()
  1932. {
  1933. if (!gService || !gService->mInitialized) {
  1934. // The cache service has been shut down, but someone is still holding
  1935. // a reference to it. Ignore this call.
  1936. return;
  1937. }
  1938. {
  1939. nsCacheServiceAutoLock lock;
  1940. gService->mClearingEntries = true;
  1941. gService->DoomActiveEntries(nullptr);
  1942. }
  1943. gService->CloseAllStreams();
  1944. nsCacheServiceAutoLock lock;
  1945. gService->ClearDoomList();
  1946. // Make sure to wait for any pending cache-operations before
  1947. // proceeding with destructive actions (bug #620660)
  1948. (void) SyncWithCacheIOThread();
  1949. if (gService->mDiskDevice && gService->mEnableDiskDevice) {
  1950. gService->mDiskDevice->Shutdown();
  1951. }
  1952. gService->mEnableDiskDevice = false;
  1953. if (gService->mOfflineDevice && gService->mEnableOfflineDevice) {
  1954. gService->mOfflineDevice->Shutdown();
  1955. }
  1956. for (auto iter = gService->mCustomOfflineDevices.Iter();
  1957. !iter.Done(); iter.Next()) {
  1958. iter.Data()->Shutdown();
  1959. iter.Remove();
  1960. }
  1961. gService->mEnableOfflineDevice = false;
  1962. if (gService->mMemoryDevice) {
  1963. // clear memory cache
  1964. gService->mMemoryDevice->EvictEntries(nullptr);
  1965. }
  1966. gService->mClearingEntries = false;
  1967. }
  1968. void
  1969. nsCacheService::OnProfileChanged()
  1970. {
  1971. if (!gService) return;
  1972. CACHE_LOG_DEBUG(("nsCacheService::OnProfileChanged"));
  1973. nsCacheServiceAutoLock lock;
  1974. gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled();
  1975. gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
  1976. gService->mEnableMemoryDevice = gService->mObserver->MemoryCacheEnabled();
  1977. if (gService->mDiskDevice) {
  1978. gService->mDiskDevice->SetCacheParentDirectory(gService->mObserver->DiskCacheParentDirectory());
  1979. gService->mDiskDevice->SetCapacity(gService->mObserver->DiskCacheCapacity());
  1980. // XXX initialization of mDiskDevice could be made lazily, if mEnableDiskDevice is false
  1981. nsresult rv = gService->mDiskDevice->Init();
  1982. if (NS_FAILED(rv)) {
  1983. NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing disk device failed");
  1984. gService->mEnableDiskDevice = false;
  1985. // XXX delete mDiskDevice?
  1986. }
  1987. }
  1988. if (gService->mOfflineDevice) {
  1989. gService->mOfflineDevice->SetCacheParentDirectory(gService->mObserver->OfflineCacheParentDirectory());
  1990. gService->mOfflineDevice->SetCapacity(gService->mObserver->OfflineCacheCapacity());
  1991. // XXX initialization of mOfflineDevice could be made lazily, if mEnableOfflineDevice is false
  1992. nsresult rv = gService->mOfflineDevice->InitWithSqlite(gService->mStorageService);
  1993. if (NS_FAILED(rv)) {
  1994. NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing offline device failed");
  1995. gService->mEnableOfflineDevice = false;
  1996. // XXX delete mOfflineDevice?
  1997. }
  1998. }
  1999. // If memoryDevice exists, reset its size to the new profile
  2000. if (gService->mMemoryDevice) {
  2001. if (gService->mEnableMemoryDevice) {
  2002. // make sure that capacity is reset to the right value
  2003. int32_t capacity = gService->mObserver->MemoryCacheCapacity();
  2004. CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
  2005. capacity));
  2006. gService->mMemoryDevice->SetCapacity(capacity);
  2007. } else {
  2008. // tell memory device to evict everything
  2009. CACHE_LOG_DEBUG(("memory device disabled\n"));
  2010. gService->mMemoryDevice->SetCapacity(0);
  2011. // Don't delete memory device, because some entries may be active still...
  2012. }
  2013. }
  2014. }
  2015. void
  2016. nsCacheService::SetDiskCacheEnabled(bool enabled)
  2017. {
  2018. if (!gService) return;
  2019. nsCacheServiceAutoLock lock;
  2020. gService->mEnableDiskDevice = enabled;
  2021. }
  2022. void
  2023. nsCacheService::SetDiskCacheCapacity(int32_t capacity)
  2024. {
  2025. if (!gService) return;
  2026. nsCacheServiceAutoLock lock;
  2027. if (gService->mDiskDevice) {
  2028. gService->mDiskDevice->SetCapacity(capacity);
  2029. }
  2030. gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled();
  2031. }
  2032. void
  2033. nsCacheService::SetDiskCacheMaxEntrySize(int32_t maxSize)
  2034. {
  2035. if (!gService) return;
  2036. nsCacheServiceAutoLock lock;
  2037. if (gService->mDiskDevice) {
  2038. gService->mDiskDevice->SetMaxEntrySize(maxSize);
  2039. }
  2040. }
  2041. void
  2042. nsCacheService::SetMemoryCacheMaxEntrySize(int32_t maxSize)
  2043. {
  2044. if (!gService) return;
  2045. nsCacheServiceAutoLock lock;
  2046. if (gService->mMemoryDevice) {
  2047. gService->mMemoryDevice->SetMaxEntrySize(maxSize);
  2048. }
  2049. }
  2050. void
  2051. nsCacheService::SetOfflineCacheEnabled(bool enabled)
  2052. {
  2053. if (!gService) return;
  2054. nsCacheServiceAutoLock lock;
  2055. gService->mEnableOfflineDevice = enabled;
  2056. }
  2057. void
  2058. nsCacheService::SetOfflineCacheCapacity(int32_t capacity)
  2059. {
  2060. if (!gService) return;
  2061. nsCacheServiceAutoLock lock;
  2062. if (gService->mOfflineDevice) {
  2063. gService->mOfflineDevice->SetCapacity(capacity);
  2064. }
  2065. gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
  2066. }
  2067. void
  2068. nsCacheService::SetMemoryCache()
  2069. {
  2070. if (!gService) return;
  2071. CACHE_LOG_DEBUG(("nsCacheService::SetMemoryCache"));
  2072. nsCacheServiceAutoLock lock;
  2073. gService->mEnableMemoryDevice = gService->mObserver->MemoryCacheEnabled();
  2074. if (gService->mEnableMemoryDevice) {
  2075. if (gService->mMemoryDevice) {
  2076. int32_t capacity = gService->mObserver->MemoryCacheCapacity();
  2077. // make sure that capacity is reset to the right value
  2078. CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
  2079. capacity));
  2080. gService->mMemoryDevice->SetCapacity(capacity);
  2081. }
  2082. } else {
  2083. if (gService->mMemoryDevice) {
  2084. // tell memory device to evict everything
  2085. CACHE_LOG_DEBUG(("memory device disabled\n"));
  2086. gService->mMemoryDevice->SetCapacity(0);
  2087. // Don't delete memory device, because some entries may be active still...
  2088. }
  2089. }
  2090. }
  2091. /******************************************************************************
  2092. * static methods for nsCacheEntryDescriptor
  2093. *****************************************************************************/
  2094. void
  2095. nsCacheService::CloseDescriptor(nsCacheEntryDescriptor * descriptor)
  2096. {
  2097. // ask entry to remove descriptor
  2098. nsCacheEntry * entry = descriptor->CacheEntry();
  2099. bool doomEntry;
  2100. bool stillActive = entry->RemoveDescriptor(descriptor, &doomEntry);
  2101. if (!entry->IsValid()) {
  2102. gService->ProcessPendingRequests(entry);
  2103. }
  2104. if (doomEntry) {
  2105. gService->DoomEntry_Internal(entry, true);
  2106. return;
  2107. }
  2108. if (!stillActive) {
  2109. gService->DeactivateEntry(entry);
  2110. }
  2111. }
  2112. nsresult
  2113. nsCacheService::GetFileForEntry(nsCacheEntry * entry,
  2114. nsIFile ** result)
  2115. {
  2116. nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
  2117. if (!device) return NS_ERROR_UNEXPECTED;
  2118. return device->GetFileForEntry(entry, result);
  2119. }
  2120. nsresult
  2121. nsCacheService::OpenInputStreamForEntry(nsCacheEntry * entry,
  2122. nsCacheAccessMode mode,
  2123. uint32_t offset,
  2124. nsIInputStream ** result)
  2125. {
  2126. nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
  2127. if (!device) return NS_ERROR_UNEXPECTED;
  2128. return device->OpenInputStreamForEntry(entry, mode, offset, result);
  2129. }
  2130. nsresult
  2131. nsCacheService::OpenOutputStreamForEntry(nsCacheEntry * entry,
  2132. nsCacheAccessMode mode,
  2133. uint32_t offset,
  2134. nsIOutputStream ** result)
  2135. {
  2136. nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
  2137. if (!device) return NS_ERROR_UNEXPECTED;
  2138. return device->OpenOutputStreamForEntry(entry, mode, offset, result);
  2139. }
  2140. nsresult
  2141. nsCacheService::OnDataSizeChange(nsCacheEntry * entry, int32_t deltaSize)
  2142. {
  2143. nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
  2144. if (!device) return NS_ERROR_UNEXPECTED;
  2145. return device->OnDataSizeChange(entry, deltaSize);
  2146. }
  2147. void
  2148. nsCacheService::LockAcquired()
  2149. {
  2150. MutexAutoLock lock(mTimeStampLock);
  2151. mLockAcquiredTimeStamp = TimeStamp::Now();
  2152. }
  2153. void
  2154. nsCacheService::LockReleased()
  2155. {
  2156. MutexAutoLock lock(mTimeStampLock);
  2157. mLockAcquiredTimeStamp = TimeStamp();
  2158. }
  2159. void
  2160. nsCacheService::Lock()
  2161. {
  2162. gService->mLock.Lock();
  2163. gService->LockAcquired();
  2164. }
  2165. void
  2166. nsCacheService::Unlock()
  2167. {
  2168. gService->mLock.AssertCurrentThreadOwns();
  2169. nsTArray<nsISupports*> doomed;
  2170. doomed.SwapElements(gService->mDoomedObjects);
  2171. gService->LockReleased();
  2172. gService->mLock.Unlock();
  2173. for (uint32_t i = 0; i < doomed.Length(); ++i)
  2174. doomed[i]->Release();
  2175. }
  2176. void
  2177. nsCacheService::ReleaseObject_Locked(nsISupports * obj,
  2178. nsIEventTarget * target)
  2179. {
  2180. gService->mLock.AssertCurrentThreadOwns();
  2181. bool isCur;
  2182. if (!target || (NS_SUCCEEDED(target->IsOnCurrentThread(&isCur)) && isCur)) {
  2183. gService->mDoomedObjects.AppendElement(obj);
  2184. } else {
  2185. NS_ProxyRelease(target, dont_AddRef(obj));
  2186. }
  2187. }
  2188. nsresult
  2189. nsCacheService::SetCacheElement(nsCacheEntry * entry, nsISupports * element)
  2190. {
  2191. entry->SetData(element);
  2192. entry->TouchData();
  2193. return NS_OK;
  2194. }
  2195. nsresult
  2196. nsCacheService::ValidateEntry(nsCacheEntry * entry)
  2197. {
  2198. nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
  2199. if (!device) return NS_ERROR_UNEXPECTED;
  2200. entry->MarkValid();
  2201. nsresult rv = gService->ProcessPendingRequests(entry);
  2202. NS_ASSERTION(rv == NS_OK, "ProcessPendingRequests failed.");
  2203. // XXX what else should be done?
  2204. return rv;
  2205. }
  2206. int32_t
  2207. nsCacheService::CacheCompressionLevel()
  2208. {
  2209. int32_t level = gService->mObserver->CacheCompressionLevel();
  2210. return level;
  2211. }
  2212. void
  2213. nsCacheService::DeactivateEntry(nsCacheEntry * entry)
  2214. {
  2215. CACHE_LOG_DEBUG(("Deactivating entry %p\n", entry));
  2216. nsresult rv = NS_OK;
  2217. NS_ASSERTION(entry->IsNotInUse(), "### deactivating an entry while in use!");
  2218. nsCacheDevice * device = nullptr;
  2219. if (mMaxDataSize < entry->DataSize() ) mMaxDataSize = entry->DataSize();
  2220. if (mMaxMetaSize < entry->MetaDataSize() ) mMaxMetaSize = entry->MetaDataSize();
  2221. if (entry->IsDoomed()) {
  2222. // remove from Doomed list
  2223. PR_REMOVE_AND_INIT_LINK(entry);
  2224. } else if (entry->IsActive()) {
  2225. // remove from active entries
  2226. mActiveEntries.RemoveEntry(entry);
  2227. CACHE_LOG_DEBUG(("Removed deactivated entry %p from mActiveEntries\n",
  2228. entry));
  2229. entry->MarkInactive();
  2230. // bind entry if necessary to store meta-data
  2231. device = EnsureEntryHasDevice(entry);
  2232. if (!device) {
  2233. CACHE_LOG_DEBUG(("DeactivateEntry: unable to bind active "
  2234. "entry %p\n",
  2235. entry));
  2236. NS_WARNING("DeactivateEntry: unable to bind active entry\n");
  2237. return;
  2238. }
  2239. } else {
  2240. // if mInitialized == false,
  2241. // then we're shutting down and this state is okay.
  2242. NS_ASSERTION(!mInitialized, "DeactivateEntry: bad cache entry state.");
  2243. }
  2244. device = entry->CacheDevice();
  2245. if (device) {
  2246. rv = device->DeactivateEntry(entry);
  2247. if (NS_FAILED(rv)) {
  2248. // increment deactivate failure count
  2249. ++mDeactivateFailures;
  2250. }
  2251. } else {
  2252. // increment deactivating unbound entry statistic
  2253. ++mDeactivatedUnboundEntries;
  2254. delete entry; // because no one else will
  2255. }
  2256. }
  2257. nsresult
  2258. nsCacheService::ProcessPendingRequests(nsCacheEntry * entry)
  2259. {
  2260. nsresult rv = NS_OK;
  2261. nsCacheRequest * request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
  2262. nsCacheRequest * nextRequest;
  2263. bool newWriter = false;
  2264. CACHE_LOG_DEBUG(("ProcessPendingRequests for %sinitialized %s %salid entry %p\n",
  2265. (entry->IsInitialized()?"" : "Un"),
  2266. (entry->IsDoomed()?"DOOMED" : ""),
  2267. (entry->IsValid()? "V":"Inv"), entry));
  2268. if (request == &entry->mRequestQ) return NS_OK; // no queued requests
  2269. if (!entry->IsDoomed() && entry->IsInvalid()) {
  2270. // 1st descriptor closed w/o MarkValid()
  2271. NS_ASSERTION(PR_CLIST_IS_EMPTY(&entry->mDescriptorQ), "shouldn't be here with open descriptors");
  2272. #if DEBUG
  2273. // verify no ACCESS_WRITE requests(shouldn't have any of these)
  2274. while (request != &entry->mRequestQ) {
  2275. NS_ASSERTION(request->AccessRequested() != nsICache::ACCESS_WRITE,
  2276. "ACCESS_WRITE request should have been given a new entry");
  2277. request = (nsCacheRequest *)PR_NEXT_LINK(request);
  2278. }
  2279. request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
  2280. #endif
  2281. // find first request with ACCESS_READ_WRITE (if any) and promote it to 1st writer
  2282. while (request != &entry->mRequestQ) {
  2283. if (request->AccessRequested() == nsICache::ACCESS_READ_WRITE) {
  2284. newWriter = true;
  2285. CACHE_LOG_DEBUG((" promoting request %p to 1st writer\n", request));
  2286. break;
  2287. }
  2288. request = (nsCacheRequest *)PR_NEXT_LINK(request);
  2289. }
  2290. if (request == &entry->mRequestQ) // no requests asked for ACCESS_READ_WRITE, back to top
  2291. request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
  2292. // XXX what should we do if there are only READ requests in queue?
  2293. // XXX serialize their accesses, give them only read access, but force them to check validate flag?
  2294. // XXX or do readers simply presume the entry is valid
  2295. // See fix for bug #467392 below
  2296. }
  2297. nsCacheAccessMode accessGranted = nsICache::ACCESS_NONE;
  2298. while (request != &entry->mRequestQ) {
  2299. nextRequest = (nsCacheRequest *)PR_NEXT_LINK(request);
  2300. CACHE_LOG_DEBUG((" %sync request %p for %p\n",
  2301. (request->mListener?"As":"S"), request, entry));
  2302. if (request->mListener) {
  2303. // Async request
  2304. PR_REMOVE_AND_INIT_LINK(request);
  2305. if (entry->IsDoomed()) {
  2306. rv = ProcessRequest(request, false, nullptr);
  2307. if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)
  2308. rv = NS_OK;
  2309. else
  2310. delete request;
  2311. if (NS_FAILED(rv)) {
  2312. // XXX what to do?
  2313. }
  2314. } else if (entry->IsValid() || newWriter) {
  2315. rv = entry->RequestAccess(request, &accessGranted);
  2316. NS_ASSERTION(NS_SUCCEEDED(rv),
  2317. "if entry is valid, RequestAccess must succeed.");
  2318. // XXX if (newWriter) NS_ASSERTION( accessGranted == request->AccessRequested(), "why not?");
  2319. // entry->CreateDescriptor dequeues request, and queues descriptor
  2320. nsICacheEntryDescriptor *descriptor = nullptr;
  2321. rv = entry->CreateDescriptor(request,
  2322. accessGranted,
  2323. &descriptor);
  2324. // post call to listener to report error or descriptor
  2325. rv = NotifyListener(request, descriptor, accessGranted, rv);
  2326. delete request;
  2327. if (NS_FAILED(rv)) {
  2328. // XXX what to do?
  2329. }
  2330. } else {
  2331. // read-only request to an invalid entry - need to wait for
  2332. // the entry to become valid so we post an event to process
  2333. // the request again later (bug #467392)
  2334. nsCOMPtr<nsIRunnable> ev =
  2335. new nsProcessRequestEvent(request);
  2336. rv = DispatchToCacheIOThread(ev);
  2337. if (NS_FAILED(rv)) {
  2338. delete request; // avoid leak
  2339. }
  2340. }
  2341. } else {
  2342. // Synchronous request
  2343. request->WakeUp();
  2344. }
  2345. if (newWriter) break; // process remaining requests after validation
  2346. request = nextRequest;
  2347. }
  2348. return NS_OK;
  2349. }
  2350. bool
  2351. nsCacheService::IsDoomListEmpty()
  2352. {
  2353. nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
  2354. return &mDoomedEntries == entry;
  2355. }
  2356. void
  2357. nsCacheService::ClearDoomList()
  2358. {
  2359. nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
  2360. while (entry != &mDoomedEntries) {
  2361. nsCacheEntry * next = (nsCacheEntry *)PR_NEXT_LINK(entry);
  2362. entry->DetachDescriptors();
  2363. DeactivateEntry(entry);
  2364. entry = next;
  2365. }
  2366. }
  2367. void
  2368. nsCacheService::DoomActiveEntries(DoomCheckFn check)
  2369. {
  2370. AutoTArray<nsCacheEntry*, 8> array;
  2371. for (auto iter = mActiveEntries.Iter(); !iter.Done(); iter.Next()) {
  2372. nsCacheEntry* entry =
  2373. static_cast<nsCacheEntryHashTableEntry*>(iter.Get())->cacheEntry;
  2374. if (check && !check(entry)) {
  2375. continue;
  2376. }
  2377. array.AppendElement(entry);
  2378. // entry is being removed from the active entry list
  2379. entry->MarkInactive();
  2380. iter.Remove();
  2381. }
  2382. uint32_t count = array.Length();
  2383. for (uint32_t i = 0; i < count; ++i) {
  2384. DoomEntry_Internal(array[i], true);
  2385. }
  2386. }
  2387. void
  2388. nsCacheService::CloseAllStreams()
  2389. {
  2390. nsTArray<RefPtr<nsCacheEntryDescriptor::nsInputStreamWrapper> > inputs;
  2391. nsTArray<RefPtr<nsCacheEntryDescriptor::nsOutputStreamWrapper> > outputs;
  2392. {
  2393. nsCacheServiceAutoLock lock;
  2394. nsTArray<nsCacheEntry*> entries;
  2395. #if DEBUG
  2396. // make sure there is no active entry
  2397. for (auto iter = mActiveEntries.Iter(); !iter.Done(); iter.Next()) {
  2398. auto entry = static_cast<nsCacheEntryHashTableEntry*>(iter.Get());
  2399. entries.AppendElement(entry->cacheEntry);
  2400. }
  2401. NS_ASSERTION(entries.IsEmpty(), "Bad state");
  2402. #endif
  2403. // Get doomed entries
  2404. nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
  2405. while (entry != &mDoomedEntries) {
  2406. nsCacheEntry * next = (nsCacheEntry *)PR_NEXT_LINK(entry);
  2407. entries.AppendElement(entry);
  2408. entry = next;
  2409. }
  2410. // Iterate through all entries and collect input and output streams
  2411. for (size_t i = 0; i < entries.Length(); i++) {
  2412. entry = entries.ElementAt(i);
  2413. nsTArray<RefPtr<nsCacheEntryDescriptor> > descs;
  2414. entry->GetDescriptors(descs);
  2415. for (uint32_t j = 0 ; j < descs.Length() ; j++) {
  2416. if (descs[j]->mOutputWrapper)
  2417. outputs.AppendElement(descs[j]->mOutputWrapper);
  2418. for (size_t k = 0; k < descs[j]->mInputWrappers.Length(); k++)
  2419. inputs.AppendElement(descs[j]->mInputWrappers[k]);
  2420. }
  2421. }
  2422. }
  2423. uint32_t i;
  2424. for (i = 0 ; i < inputs.Length() ; i++)
  2425. inputs[i]->Close();
  2426. for (i = 0 ; i < outputs.Length() ; i++)
  2427. outputs[i]->Close();
  2428. }
  2429. bool
  2430. nsCacheService::GetClearingEntries()
  2431. {
  2432. AssertOwnsLock();
  2433. return gService->mClearingEntries;
  2434. }
  2435. // static
  2436. void nsCacheService::GetCacheBaseDirectoty(nsIFile ** result)
  2437. {
  2438. *result = nullptr;
  2439. if (!gService || !gService->mObserver)
  2440. return;
  2441. nsCOMPtr<nsIFile> directory =
  2442. gService->mObserver->DiskCacheParentDirectory();
  2443. if (!directory)
  2444. return;
  2445. directory->Clone(result);
  2446. }
  2447. // static
  2448. void nsCacheService::GetDiskCacheDirectory(nsIFile ** result)
  2449. {
  2450. nsCOMPtr<nsIFile> directory;
  2451. GetCacheBaseDirectoty(getter_AddRefs(directory));
  2452. if (!directory)
  2453. return;
  2454. nsresult rv = directory->AppendNative(NS_LITERAL_CSTRING("Cache"));
  2455. if (NS_FAILED(rv))
  2456. return;
  2457. directory.forget(result);
  2458. }
  2459. // static
  2460. void nsCacheService::GetAppCacheDirectory(nsIFile ** result)
  2461. {
  2462. nsCOMPtr<nsIFile> directory;
  2463. GetCacheBaseDirectoty(getter_AddRefs(directory));
  2464. if (!directory)
  2465. return;
  2466. nsresult rv = directory->AppendNative(NS_LITERAL_CSTRING("OfflineCache"));
  2467. if (NS_FAILED(rv))
  2468. return;
  2469. directory.forget(result);
  2470. }
  2471. void
  2472. nsCacheService::LogCacheStatistics()
  2473. {
  2474. uint32_t hitPercentage = (uint32_t)((((double)mCacheHits) /
  2475. ((double)(mCacheHits + mCacheMisses))) * 100);
  2476. CACHE_LOG_INFO(("\nCache Service Statistics:\n\n"));
  2477. CACHE_LOG_INFO((" TotalEntries = %d\n", mTotalEntries));
  2478. CACHE_LOG_INFO((" Cache Hits = %d\n", mCacheHits));
  2479. CACHE_LOG_INFO((" Cache Misses = %d\n", mCacheMisses));
  2480. CACHE_LOG_INFO((" Cache Hit %% = %d%%\n", hitPercentage));
  2481. CACHE_LOG_INFO((" Max Key Length = %d\n", mMaxKeyLength));
  2482. CACHE_LOG_INFO((" Max Meta Size = %d\n", mMaxMetaSize));
  2483. CACHE_LOG_INFO((" Max Data Size = %d\n", mMaxDataSize));
  2484. CACHE_LOG_INFO(("\n"));
  2485. CACHE_LOG_INFO((" Deactivate Failures = %d\n",
  2486. mDeactivateFailures));
  2487. CACHE_LOG_INFO((" Deactivated Unbound Entries = %d\n",
  2488. mDeactivatedUnboundEntries));
  2489. }
  2490. nsresult
  2491. nsCacheService::SetDiskSmartSize()
  2492. {
  2493. nsCacheServiceAutoLock lock;
  2494. if (!gService) return NS_ERROR_NOT_AVAILABLE;
  2495. return gService->SetDiskSmartSize_Locked();
  2496. }
  2497. nsresult
  2498. nsCacheService::SetDiskSmartSize_Locked()
  2499. {
  2500. nsresult rv;
  2501. if (mozilla::net::CacheObserver::UseNewCache()) {
  2502. return NS_ERROR_NOT_AVAILABLE;
  2503. }
  2504. if (!mObserver->DiskCacheParentDirectory())
  2505. return NS_ERROR_NOT_AVAILABLE;
  2506. if (!mDiskDevice)
  2507. return NS_ERROR_NOT_AVAILABLE;
  2508. if (!mObserver->SmartSizeEnabled())
  2509. return NS_ERROR_NOT_AVAILABLE;
  2510. nsAutoString cachePath;
  2511. rv = mObserver->DiskCacheParentDirectory()->GetPath(cachePath);
  2512. if (NS_SUCCEEDED(rv)) {
  2513. nsCOMPtr<nsIRunnable> event =
  2514. new nsGetSmartSizeEvent(cachePath, mDiskDevice->getCacheSize(),
  2515. mObserver->ShouldUseOldMaxSmartSize());
  2516. DispatchToCacheIOThread(event);
  2517. } else {
  2518. return NS_ERROR_FAILURE;
  2519. }
  2520. return NS_OK;
  2521. }
  2522. void
  2523. nsCacheService::MoveOrRemoveDiskCache(nsIFile *aOldCacheDir,
  2524. nsIFile *aNewCacheDir,
  2525. const char *aCacheSubdir)
  2526. {
  2527. bool same;
  2528. if (NS_FAILED(aOldCacheDir->Equals(aNewCacheDir, &same)) || same)
  2529. return;
  2530. nsCOMPtr<nsIFile> aOldCacheSubdir;
  2531. aOldCacheDir->Clone(getter_AddRefs(aOldCacheSubdir));
  2532. nsresult rv = aOldCacheSubdir->AppendNative(
  2533. nsDependentCString(aCacheSubdir));
  2534. if (NS_FAILED(rv))
  2535. return;
  2536. bool exists;
  2537. if (NS_FAILED(aOldCacheSubdir->Exists(&exists)) || !exists)
  2538. return;
  2539. nsCOMPtr<nsIFile> aNewCacheSubdir;
  2540. aNewCacheDir->Clone(getter_AddRefs(aNewCacheSubdir));
  2541. rv = aNewCacheSubdir->AppendNative(nsDependentCString(aCacheSubdir));
  2542. if (NS_FAILED(rv))
  2543. return;
  2544. nsAutoCString newPath;
  2545. rv = aNewCacheSubdir->GetNativePath(newPath);
  2546. if (NS_FAILED(rv))
  2547. return;
  2548. if (NS_SUCCEEDED(aNewCacheSubdir->Exists(&exists)) && !exists) {
  2549. // New cache directory does not exist, try to move the old one here
  2550. // rename needs an empty target directory
  2551. // Make sure the parent of the target sub-dir exists
  2552. rv = aNewCacheDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
  2553. if (NS_SUCCEEDED(rv) || NS_ERROR_FILE_ALREADY_EXISTS == rv) {
  2554. nsAutoCString oldPath;
  2555. rv = aOldCacheSubdir->GetNativePath(oldPath);
  2556. if (NS_FAILED(rv))
  2557. return;
  2558. if (rename(oldPath.get(), newPath.get()) == 0)
  2559. return;
  2560. }
  2561. }
  2562. // Delay delete by 1 minute to avoid IO thrash on startup.
  2563. nsDeleteDir::DeleteDir(aOldCacheSubdir, false, 60000);
  2564. }
  2565. static bool
  2566. IsEntryPrivate(nsCacheEntry* entry)
  2567. {
  2568. return entry->IsPrivate();
  2569. }
  2570. void
  2571. nsCacheService::LeavePrivateBrowsing()
  2572. {
  2573. nsCacheServiceAutoLock lock;
  2574. gService->DoomActiveEntries(IsEntryPrivate);
  2575. if (gService->mMemoryDevice) {
  2576. // clear memory cache
  2577. gService->mMemoryDevice->EvictPrivateEntries();
  2578. }
  2579. }
  2580. MOZ_DEFINE_MALLOC_SIZE_OF(DiskCacheDeviceMallocSizeOf)
  2581. NS_IMETHODIMP
  2582. nsCacheService::CollectReports(nsIHandleReportCallback* aHandleReport,
  2583. nsISupports* aData, bool aAnonymize)
  2584. {
  2585. size_t disk = 0;
  2586. if (mDiskDevice) {
  2587. nsCacheServiceAutoLock lock;
  2588. disk = mDiskDevice->SizeOfIncludingThis(DiskCacheDeviceMallocSizeOf);
  2589. }
  2590. size_t memory = mMemoryDevice ? mMemoryDevice->TotalSize() : 0;
  2591. MOZ_COLLECT_REPORT(
  2592. "explicit/network/disk-cache", KIND_HEAP, UNITS_BYTES, disk,
  2593. "Memory used by the network disk cache.");
  2594. MOZ_COLLECT_REPORT(
  2595. "explicit/network/memory-cache", KIND_HEAP, UNITS_BYTES, memory,
  2596. "Memory used by the network memory cache.");
  2597. return NS_OK;
  2598. }