1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "CacheObserver.h"
7 #include "CacheStorageService.h"
8 #include "CacheFileIOManager.h"
9 #include "LoadContextInfo.h"
10 #include "nsICacheStorage.h"
11 #include "nsIObserverService.h"
12 #include "mozIApplicationClearPrivateDataParams.h"
13 #include "mozilla/Services.h"
14 #include "mozilla/Preferences.h"
15 #include "nsServiceManagerUtils.h"
23 CacheObserver
* CacheObserver::sSelf
= nullptr;
25 static uint32_t const kDefaultUseNewCache
= 1; // Use the new cache by default
26 uint32_t CacheObserver::sUseNewCache
= kDefaultUseNewCache
;
28 static bool sUseNewCacheTemp
= false; // Temp trigger to not lose early adopters
30 static int32_t const kAutoDeleteCacheVersion
= -1; // Auto-delete off by default
31 static int32_t sAutoDeleteCacheVersion
= kAutoDeleteCacheVersion
;
33 static int32_t const kDefaultHalfLifeExperiment
= -1; // Disabled
34 int32_t CacheObserver::sHalfLifeExperiment
= kDefaultHalfLifeExperiment
;
36 static uint32_t const kDefaultHalfLifeHours
= 6; // 6 hours
37 uint32_t CacheObserver::sHalfLifeHours
= kDefaultHalfLifeHours
;
39 static bool const kDefaultUseDiskCache
= true;
40 bool CacheObserver::sUseDiskCache
= kDefaultUseDiskCache
;
42 static bool const kDefaultUseMemoryCache
= true;
43 bool CacheObserver::sUseMemoryCache
= kDefaultUseMemoryCache
;
45 static uint32_t const kDefaultMetadataMemoryLimit
= 250; // 0.25 MB
46 uint32_t CacheObserver::sMetadataMemoryLimit
= kDefaultMetadataMemoryLimit
;
48 static int32_t const kDefaultMemoryCacheCapacity
= -1; // autodetect
49 int32_t CacheObserver::sMemoryCacheCapacity
= kDefaultMemoryCacheCapacity
;
50 // Cache of the calculated memory capacity based on the system memory size
51 int32_t CacheObserver::sAutoMemoryCacheCapacity
= -1;
53 static uint32_t const kDefaultDiskCacheCapacity
= 250 * 1024; // 250 MB
54 uint32_t CacheObserver::sDiskCacheCapacity
= kDefaultDiskCacheCapacity
;
56 static uint32_t const kDefaultDiskFreeSpaceSoftLimit
= 5 * 1024; // 5MB
57 uint32_t CacheObserver::sDiskFreeSpaceSoftLimit
= kDefaultDiskFreeSpaceSoftLimit
;
59 static uint32_t const kDefaultDiskFreeSpaceHardLimit
= 1024; // 1MB
60 uint32_t CacheObserver::sDiskFreeSpaceHardLimit
= kDefaultDiskFreeSpaceHardLimit
;
62 static bool const kDefaultSmartCacheSizeEnabled
= false;
63 bool CacheObserver::sSmartCacheSizeEnabled
= kDefaultSmartCacheSizeEnabled
;
65 static uint32_t const kDefaultPreloadChunkCount
= 4;
66 uint32_t CacheObserver::sPreloadChunkCount
= kDefaultPreloadChunkCount
;
68 static uint32_t const kDefaultMaxMemoryEntrySize
= 4 * 1024; // 4 MB
69 uint32_t CacheObserver::sMaxMemoryEntrySize
= kDefaultMaxMemoryEntrySize
;
71 static uint32_t const kDefaultMaxDiskEntrySize
= 50 * 1024; // 50 MB
72 uint32_t CacheObserver::sMaxDiskEntrySize
= kDefaultMaxDiskEntrySize
;
74 static uint32_t const kDefaultMaxDiskChunksMemoryUsage
= 10 * 1024; // 10MB
75 uint32_t CacheObserver::sMaxDiskChunksMemoryUsage
= kDefaultMaxDiskChunksMemoryUsage
;
77 static uint32_t const kDefaultMaxDiskPriorityChunksMemoryUsage
= 10 * 1024; // 10MB
78 uint32_t CacheObserver::sMaxDiskPriorityChunksMemoryUsage
= kDefaultMaxDiskPriorityChunksMemoryUsage
;
80 static uint32_t const kDefaultCompressionLevel
= 1;
81 uint32_t CacheObserver::sCompressionLevel
= kDefaultCompressionLevel
;
83 static bool kDefaultSanitizeOnShutdown
= false;
84 bool CacheObserver::sSanitizeOnShutdown
= kDefaultSanitizeOnShutdown
;
86 static bool kDefaultClearCacheOnShutdown
= false;
87 bool CacheObserver::sClearCacheOnShutdown
= kDefaultClearCacheOnShutdown
;
89 NS_IMPL_ISUPPORTS(CacheObserver
,
91 nsISupportsWeakReference
)
101 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
103 return NS_ERROR_UNEXPECTED
;
106 sSelf
= new CacheObserver();
109 obs
->AddObserver(sSelf
, "prefservice:after-app-defaults", true);
110 obs
->AddObserver(sSelf
, "profile-do-change", true);
111 obs
->AddObserver(sSelf
, "browser-delayed-startup-finished", true);
112 obs
->AddObserver(sSelf
, "profile-before-change", true);
113 obs
->AddObserver(sSelf
, "xpcom-shutdown", true);
114 obs
->AddObserver(sSelf
, "last-pb-context-exited", true);
115 obs
->AddObserver(sSelf
, "webapps-clear-data", true);
116 obs
->AddObserver(sSelf
, "memory-pressure", true);
123 CacheObserver::Shutdown()
126 return NS_ERROR_NOT_INITIALIZED
;
134 CacheObserver::AttachToPreferences()
136 sAutoDeleteCacheVersion
= mozilla::Preferences::GetInt(
137 "browser.cache.auto_delete_cache_version", kAutoDeleteCacheVersion
);
139 mozilla::Preferences::AddUintVarCache(
140 &sUseNewCache
, "browser.cache.use_new_backend", kDefaultUseNewCache
);
141 mozilla::Preferences::AddBoolVarCache(
142 &sUseNewCacheTemp
, "browser.cache.use_new_backend_temp", false);
144 mozilla::Preferences::AddBoolVarCache(
145 &sUseDiskCache
, "browser.cache.disk.enable", kDefaultUseDiskCache
);
146 mozilla::Preferences::AddBoolVarCache(
147 &sUseMemoryCache
, "browser.cache.memory.enable", kDefaultUseMemoryCache
);
149 mozilla::Preferences::AddUintVarCache(
150 &sMetadataMemoryLimit
, "browser.cache.disk.metadata_memory_limit", kDefaultMetadataMemoryLimit
);
152 mozilla::Preferences::AddUintVarCache(
153 &sDiskCacheCapacity
, "browser.cache.disk.capacity", kDefaultDiskCacheCapacity
);
154 mozilla::Preferences::AddBoolVarCache(
155 &sSmartCacheSizeEnabled
, "browser.cache.disk.smart_size.enabled", kDefaultSmartCacheSizeEnabled
);
156 mozilla::Preferences::AddIntVarCache(
157 &sMemoryCacheCapacity
, "browser.cache.memory.capacity", kDefaultMemoryCacheCapacity
);
159 mozilla::Preferences::AddUintVarCache(
160 &sDiskFreeSpaceSoftLimit
, "browser.cache.disk.free_space_soft_limit", kDefaultDiskFreeSpaceSoftLimit
);
161 mozilla::Preferences::AddUintVarCache(
162 &sDiskFreeSpaceHardLimit
, "browser.cache.disk.free_space_hard_limit", kDefaultDiskFreeSpaceHardLimit
);
164 mozilla::Preferences::AddUintVarCache(
165 &sPreloadChunkCount
, "browser.cache.disk.preload_chunk_count", kDefaultPreloadChunkCount
);
167 mozilla::Preferences::AddUintVarCache(
168 &sMaxDiskEntrySize
, "browser.cache.disk.max_entry_size", kDefaultMaxDiskEntrySize
);
169 mozilla::Preferences::AddUintVarCache(
170 &sMaxMemoryEntrySize
, "browser.cache.memory.max_entry_size", kDefaultMaxMemoryEntrySize
);
172 mozilla::Preferences::AddUintVarCache(
173 &sMaxDiskChunksMemoryUsage
, "browser.cache.disk.max_chunks_memory_usage", kDefaultMaxDiskChunksMemoryUsage
);
174 mozilla::Preferences::AddUintVarCache(
175 &sMaxDiskPriorityChunksMemoryUsage
, "browser.cache.disk.max_priority_chunks_memory_usage", kDefaultMaxDiskPriorityChunksMemoryUsage
);
177 // http://mxr.mozilla.org/mozilla-central/source/netwerk/cache/nsCacheEntryDescriptor.cpp#367
178 mozilla::Preferences::AddUintVarCache(
179 &sCompressionLevel
, "browser.cache.compression_level", kDefaultCompressionLevel
);
181 mozilla::Preferences::GetComplex(
182 "browser.cache.disk.parent_directory", NS_GET_IID(nsIFile
),
183 getter_AddRefs(mCacheParentDirectoryOverride
));
185 // First check the default value. If it is at -1, the experient
186 // is turned off. If it is at 0, then use the user pref value
188 sHalfLifeExperiment
= mozilla::Preferences::GetDefaultInt(
189 "browser.cache.frecency_experiment", kDefaultHalfLifeExperiment
);
191 if (sHalfLifeExperiment
== 0) {
192 // Default preferences indicate we want to run the experiment,
193 // hence read the user value.
194 sHalfLifeExperiment
= mozilla::Preferences::GetInt(
195 "browser.cache.frecency_experiment", sHalfLifeExperiment
);
198 if (sHalfLifeExperiment
== 0) {
199 // The experiment has not yet been initialized but is engaged, do
200 // the initialization now.
202 sHalfLifeExperiment
= (rand() % 4) + 1;
203 // Store the experiemnt value, since we need it not to change between
205 mozilla::Preferences::SetInt(
206 "browser.cache.frecency_experiment", sHalfLifeExperiment
);
209 switch (sHalfLifeExperiment
) {
210 case 1: // The experiment is engaged
217 sHalfLifeHours
= 7 * 24;
220 sHalfLifeHours
= 50 * 24;
224 default: // The experiment is off or broken
225 sHalfLifeExperiment
= -1;
226 sHalfLifeHours
= std::max(1U, std::min(1440U, mozilla::Preferences::GetUint(
227 "browser.cache.frecency_half_life_hours", kDefaultHalfLifeHours
)));
231 mozilla::Preferences::AddBoolVarCache(
232 &sSanitizeOnShutdown
, "privacy.sanitize.sanitizeOnShutdown", kDefaultSanitizeOnShutdown
);
233 mozilla::Preferences::AddBoolVarCache(
234 &sClearCacheOnShutdown
, "privacy.clearOnShutdown.cache", kDefaultClearCacheOnShutdown
);
238 uint32_t const CacheObserver::MemoryCacheCapacity()
240 if (sMemoryCacheCapacity
>= 0)
241 return sMemoryCacheCapacity
<< 10;
243 if (sAutoMemoryCacheCapacity
!= -1)
244 return sAutoMemoryCacheCapacity
;
246 static uint64_t bytes
= PR_GetPhysicalMemorySize();
247 // If getting the physical memory failed, arbitrarily assume
248 // 32 MB of RAM. We use a low default to have a reasonable
249 // size on all the devices we support.
251 bytes
= 32 * 1024 * 1024;
253 // Conversion from unsigned int64_t to double doesn't work on all platforms.
254 // We need to truncate the value at INT64_MAX to make sure we don't
256 if (bytes
> INT64_MAX
)
259 uint64_t kbytes
= bytes
>> 10;
260 double kBytesD
= double(kbytes
);
261 double x
= log(kBytesD
)/log(2.0) - 14;
263 int32_t capacity
= 0;
265 capacity
= (int32_t)(x
* x
/ 3.0 + x
+ 2.0 / 3 + 0.1); // 0.1 for rounding
271 // Result is in bytes.
272 return sAutoMemoryCacheCapacity
= capacity
;
276 bool const CacheObserver::UseNewCache()
278 uint32_t useNewCache
= sUseNewCache
;
280 if (sUseNewCacheTemp
)
283 switch (useNewCache
) {
284 case 0: // use the old cache backend
287 case 1: // use the new cache backend
296 CacheObserver::SetDiskCacheCapacity(uint32_t aCapacity
)
298 sDiskCacheCapacity
= aCapacity
>> 10;
304 if (NS_IsMainThread()) {
305 sSelf
->StoreDiskCacheCapacity();
307 nsCOMPtr
<nsIRunnable
> event
=
308 NS_NewRunnableMethod(sSelf
, &CacheObserver::StoreDiskCacheCapacity
);
309 NS_DispatchToMainThread(event
);
314 CacheObserver::StoreDiskCacheCapacity()
316 mozilla::Preferences::SetInt("browser.cache.disk.capacity",
321 void CacheObserver::ParentDirOverride(nsIFile
** aDir
)
323 if (NS_WARN_IF(!aDir
))
330 if (!sSelf
->mCacheParentDirectoryOverride
)
333 sSelf
->mCacheParentDirectoryOverride
->Clone(aDir
);
338 class CacheStorageEvictHelper
341 nsresult
Run(mozIApplicationClearPrivateDataParams
* aParams
);
345 nsresult
ClearStorage(bool const aPrivate
,
346 bool const aInBrowser
,
347 bool const aAnonymous
);
351 CacheStorageEvictHelper::Run(mozIApplicationClearPrivateDataParams
* aParams
)
355 rv
= aParams
->GetAppId(&mAppId
);
356 NS_ENSURE_SUCCESS(rv
, rv
);
359 rv
= aParams
->GetBrowserOnly(&aBrowserOnly
);
360 NS_ENSURE_SUCCESS(rv
, rv
);
362 MOZ_ASSERT(mAppId
!= nsILoadContextInfo::UNKNOWN_APP_ID
);
364 // Clear all [private X anonymous] combinations
365 rv
= ClearStorage(false, aBrowserOnly
, false);
366 NS_ENSURE_SUCCESS(rv
, rv
);
367 rv
= ClearStorage(false, aBrowserOnly
, true);
368 NS_ENSURE_SUCCESS(rv
, rv
);
369 rv
= ClearStorage(true, aBrowserOnly
, false);
370 NS_ENSURE_SUCCESS(rv
, rv
);
371 rv
= ClearStorage(true, aBrowserOnly
, true);
372 NS_ENSURE_SUCCESS(rv
, rv
);
378 CacheStorageEvictHelper::ClearStorage(bool const aPrivate
,
379 bool const aInBrowser
,
380 bool const aAnonymous
)
384 nsRefPtr
<LoadContextInfo
> info
= GetLoadContextInfo(
385 aPrivate
, mAppId
, aInBrowser
, aAnonymous
);
387 nsCOMPtr
<nsICacheStorage
> storage
;
388 nsRefPtr
<CacheStorageService
> service
= CacheStorageService::Self();
389 NS_ENSURE_TRUE(service
, NS_ERROR_FAILURE
);
391 // Clear disk storage
392 rv
= service
->DiskCacheStorage(info
, false, getter_AddRefs(storage
));
393 NS_ENSURE_SUCCESS(rv
, rv
);
394 rv
= storage
->AsyncEvictStorage(nullptr);
395 NS_ENSURE_SUCCESS(rv
, rv
);
397 // Clear memory storage
398 rv
= service
->MemoryCacheStorage(info
, getter_AddRefs(storage
));
399 NS_ENSURE_SUCCESS(rv
, rv
);
400 rv
= storage
->AsyncEvictStorage(nullptr);
401 NS_ENSURE_SUCCESS(rv
, rv
);
404 rv
= ClearStorage(aPrivate
, true, aAnonymous
);
405 NS_ENSURE_SUCCESS(rv
, rv
);
414 bool const CacheObserver::EntryIsTooBig(int64_t aSize
, bool aUsingDisk
)
416 // If custom limit is set, check it.
417 int64_t preferredLimit
= aUsingDisk
418 ? static_cast<int64_t>(sMaxDiskEntrySize
) << 10
419 : static_cast<int64_t>(sMaxMemoryEntrySize
) << 10;
421 if (preferredLimit
!= -1 && aSize
> preferredLimit
)
424 // Otherwise (or when in the custom limit), check limit based on the global
425 // limit. It's 1/8 (>> 3) of the respective capacity.
426 int64_t derivedLimit
= aUsingDisk
427 ? (static_cast<int64_t>(DiskCacheCapacity() >> 3))
428 : (static_cast<int64_t>(MemoryCacheCapacity() >> 3));
430 if (aSize
> derivedLimit
)
437 CacheObserver::Observe(nsISupports
* aSubject
,
439 const char16_t
* aData
)
441 if (!strcmp(aTopic
, "prefservice:after-app-defaults")) {
442 CacheFileIOManager::Init();
446 if (!strcmp(aTopic
, "profile-do-change")) {
447 AttachToPreferences();
448 CacheFileIOManager::Init();
449 CacheFileIOManager::OnProfile();
453 if (!strcmp(aTopic
, "browser-delayed-startup-finished")) {
454 uint32_t activeVersion
= UseNewCache() ? 1 : 0;
455 CacheStorageService::CleaupCacheDirectories(sAutoDeleteCacheVersion
, activeVersion
);
459 if (!strcmp(aTopic
, "profile-before-change")) {
460 nsRefPtr
<CacheStorageService
> service
= CacheStorageService::Self();
467 if (!strcmp(aTopic
, "xpcom-shutdown")) {
468 nsRefPtr
<CacheStorageService
> service
= CacheStorageService::Self();
472 CacheFileIOManager::Shutdown();
476 if (!strcmp(aTopic
, "last-pb-context-exited")) {
477 nsRefPtr
<CacheStorageService
> service
= CacheStorageService::Self();
479 service
->DropPrivateBrowsingEntries();
484 if (!strcmp(aTopic
, "webapps-clear-data")) {
485 nsCOMPtr
<mozIApplicationClearPrivateDataParams
> params
=
486 do_QueryInterface(aSubject
);
488 NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams");
489 return NS_ERROR_UNEXPECTED
;
492 CacheStorageEvictHelper helper
;
493 nsresult rv
= helper
.Run(params
);
494 NS_ENSURE_SUCCESS(rv
, rv
);
499 if (!strcmp(aTopic
, "memory-pressure")) {
500 nsRefPtr
<CacheStorageService
> service
= CacheStorageService::Self();
502 service
->PurgeFromMemory(nsICacheStorageService::PURGE_EVERYTHING
);
507 MOZ_ASSERT(false, "Missing observer handler");