Bumping manifests a=b2g-bump
[gecko.git] / netwerk / cache2 / CacheObserver.cpp
blob7a21777101a2ff777c11b9b44cf122c73994d549
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"
16 #include "prsystem.h"
17 #include <time.h>
18 #include <math.h>
20 namespace mozilla {
21 namespace net {
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,
90 nsIObserver,
91 nsISupportsWeakReference)
93 // static
94 nsresult
95 CacheObserver::Init()
97 if (sSelf) {
98 return NS_OK;
101 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
102 if (!obs) {
103 return NS_ERROR_UNEXPECTED;
106 sSelf = new CacheObserver();
107 NS_ADDREF(sSelf);
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);
118 return NS_OK;
121 // static
122 nsresult
123 CacheObserver::Shutdown()
125 if (!sSelf) {
126 return NS_ERROR_NOT_INITIALIZED;
129 NS_RELEASE(sSelf);
130 return NS_OK;
133 void
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
187 // instead.
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.
201 srand(time(NULL));
202 sHalfLifeExperiment = (rand() % 4) + 1;
203 // Store the experiemnt value, since we need it not to change between
204 // browser sessions.
205 mozilla::Preferences::SetInt(
206 "browser.cache.frecency_experiment", sHalfLifeExperiment);
209 switch (sHalfLifeExperiment) {
210 case 1: // The experiment is engaged
211 sHalfLifeHours = 6;
212 break;
213 case 2:
214 sHalfLifeHours = 24;
215 break;
216 case 3:
217 sHalfLifeHours = 7 * 24;
218 break;
219 case 4:
220 sHalfLifeHours = 50 * 24;
221 break;
223 case -1:
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)));
228 break;
231 mozilla::Preferences::AddBoolVarCache(
232 &sSanitizeOnShutdown, "privacy.sanitize.sanitizeOnShutdown", kDefaultSanitizeOnShutdown);
233 mozilla::Preferences::AddBoolVarCache(
234 &sClearCacheOnShutdown, "privacy.clearOnShutdown.cache", kDefaultClearCacheOnShutdown);
237 // static
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.
250 if (bytes == 0)
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
255 // overflow.
256 if (bytes > INT64_MAX)
257 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;
264 if (x > 0) {
265 capacity = (int32_t)(x * x / 3.0 + x + 2.0 / 3 + 0.1); // 0.1 for rounding
266 if (capacity > 32)
267 capacity = 32;
268 capacity <<= 20;
271 // Result is in bytes.
272 return sAutoMemoryCacheCapacity = capacity;
275 // static
276 bool const CacheObserver::UseNewCache()
278 uint32_t useNewCache = sUseNewCache;
280 if (sUseNewCacheTemp)
281 useNewCache = 1;
283 switch (useNewCache) {
284 case 0: // use the old cache backend
285 return false;
287 case 1: // use the new cache backend
288 return true;
291 return true;
294 // static
295 void
296 CacheObserver::SetDiskCacheCapacity(uint32_t aCapacity)
298 sDiskCacheCapacity = aCapacity >> 10;
300 if (!sSelf) {
301 return;
304 if (NS_IsMainThread()) {
305 sSelf->StoreDiskCacheCapacity();
306 } else {
307 nsCOMPtr<nsIRunnable> event =
308 NS_NewRunnableMethod(sSelf, &CacheObserver::StoreDiskCacheCapacity);
309 NS_DispatchToMainThread(event);
313 void
314 CacheObserver::StoreDiskCacheCapacity()
316 mozilla::Preferences::SetInt("browser.cache.disk.capacity",
317 sDiskCacheCapacity);
320 // static
321 void CacheObserver::ParentDirOverride(nsIFile** aDir)
323 if (NS_WARN_IF(!aDir))
324 return;
326 *aDir = nullptr;
328 if (!sSelf)
329 return;
330 if (!sSelf->mCacheParentDirectoryOverride)
331 return;
333 sSelf->mCacheParentDirectoryOverride->Clone(aDir);
336 namespace { // anon
338 class CacheStorageEvictHelper
340 public:
341 nsresult Run(mozIApplicationClearPrivateDataParams* aParams);
343 private:
344 uint32_t mAppId;
345 nsresult ClearStorage(bool const aPrivate,
346 bool const aInBrowser,
347 bool const aAnonymous);
350 nsresult
351 CacheStorageEvictHelper::Run(mozIApplicationClearPrivateDataParams* aParams)
353 nsresult rv;
355 rv = aParams->GetAppId(&mAppId);
356 NS_ENSURE_SUCCESS(rv, rv);
358 bool aBrowserOnly;
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);
374 return NS_OK;
377 nsresult
378 CacheStorageEvictHelper::ClearStorage(bool const aPrivate,
379 bool const aInBrowser,
380 bool const aAnonymous)
382 nsresult rv;
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);
403 if (!aInBrowser) {
404 rv = ClearStorage(aPrivate, true, aAnonymous);
405 NS_ENSURE_SUCCESS(rv, rv);
408 return NS_OK;
411 } // anon
413 // static
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)
422 return true;
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)
431 return true;
433 return false;
436 NS_IMETHODIMP
437 CacheObserver::Observe(nsISupports* aSubject,
438 const char* aTopic,
439 const char16_t* aData)
441 if (!strcmp(aTopic, "prefservice:after-app-defaults")) {
442 CacheFileIOManager::Init();
443 return NS_OK;
446 if (!strcmp(aTopic, "profile-do-change")) {
447 AttachToPreferences();
448 CacheFileIOManager::Init();
449 CacheFileIOManager::OnProfile();
450 return NS_OK;
453 if (!strcmp(aTopic, "browser-delayed-startup-finished")) {
454 uint32_t activeVersion = UseNewCache() ? 1 : 0;
455 CacheStorageService::CleaupCacheDirectories(sAutoDeleteCacheVersion, activeVersion);
456 return NS_OK;
459 if (!strcmp(aTopic, "profile-before-change")) {
460 nsRefPtr<CacheStorageService> service = CacheStorageService::Self();
461 if (service)
462 service->Shutdown();
464 return NS_OK;
467 if (!strcmp(aTopic, "xpcom-shutdown")) {
468 nsRefPtr<CacheStorageService> service = CacheStorageService::Self();
469 if (service)
470 service->Shutdown();
472 CacheFileIOManager::Shutdown();
473 return NS_OK;
476 if (!strcmp(aTopic, "last-pb-context-exited")) {
477 nsRefPtr<CacheStorageService> service = CacheStorageService::Self();
478 if (service)
479 service->DropPrivateBrowsingEntries();
481 return NS_OK;
484 if (!strcmp(aTopic, "webapps-clear-data")) {
485 nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
486 do_QueryInterface(aSubject);
487 if (!params) {
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);
496 return NS_OK;
499 if (!strcmp(aTopic, "memory-pressure")) {
500 nsRefPtr<CacheStorageService> service = CacheStorageService::Self();
501 if (service)
502 service->PurgeFromMemory(nsICacheStorageService::PURGE_EVERYTHING);
504 return NS_OK;
507 MOZ_ASSERT(false, "Missing observer handler");
508 return NS_OK;
511 } // net
512 } // mozilla