Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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 = 0; // Don't 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 bool const kDefaultSmartCacheSizeEnabled = false;
57 bool CacheObserver::sSmartCacheSizeEnabled = kDefaultSmartCacheSizeEnabled;
59 static uint32_t const kDefaultMaxMemoryEntrySize = 4 * 1024; // 4 MB
60 uint32_t CacheObserver::sMaxMemoryEntrySize = kDefaultMaxMemoryEntrySize;
62 static uint32_t const kDefaultMaxDiskEntrySize = 50 * 1024; // 50 MB
63 uint32_t CacheObserver::sMaxDiskEntrySize = kDefaultMaxDiskEntrySize;
65 static uint32_t const kDefaultCompressionLevel = 1;
66 uint32_t CacheObserver::sCompressionLevel = kDefaultCompressionLevel;
68 static bool kDefaultSanitizeOnShutdown = false;
69 bool CacheObserver::sSanitizeOnShutdown = kDefaultSanitizeOnShutdown;
71 static bool kDefaultClearCacheOnShutdown = false;
72 bool CacheObserver::sClearCacheOnShutdown = kDefaultClearCacheOnShutdown;
74 NS_IMPL_ISUPPORTS(CacheObserver,
75 nsIObserver,
76 nsISupportsWeakReference)
78 // static
79 nsresult
80 CacheObserver::Init()
81 {
82 if (sSelf) {
83 return NS_OK;
84 }
86 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
87 if (!obs) {
88 return NS_ERROR_UNEXPECTED;
89 }
91 sSelf = new CacheObserver();
92 NS_ADDREF(sSelf);
94 obs->AddObserver(sSelf, "prefservice:after-app-defaults", true);
95 obs->AddObserver(sSelf, "profile-do-change", true);
96 obs->AddObserver(sSelf, "sessionstore-windows-restored", true);
97 obs->AddObserver(sSelf, "profile-before-change", true);
98 obs->AddObserver(sSelf, "xpcom-shutdown", true);
99 obs->AddObserver(sSelf, "last-pb-context-exited", true);
100 obs->AddObserver(sSelf, "webapps-clear-data", true);
101 obs->AddObserver(sSelf, "memory-pressure", true);
103 return NS_OK;
104 }
106 // static
107 nsresult
108 CacheObserver::Shutdown()
109 {
110 if (!sSelf) {
111 return NS_ERROR_NOT_INITIALIZED;
112 }
114 NS_RELEASE(sSelf);
115 return NS_OK;
116 }
118 void
119 CacheObserver::AttachToPreferences()
120 {
121 sAutoDeleteCacheVersion = mozilla::Preferences::GetInt(
122 "browser.cache.auto_delete_cache_version", kAutoDeleteCacheVersion);
124 mozilla::Preferences::AddUintVarCache(
125 &sUseNewCache, "browser.cache.use_new_backend", kDefaultUseNewCache);
126 mozilla::Preferences::AddBoolVarCache(
127 &sUseNewCacheTemp, "browser.cache.use_new_backend_temp", false);
129 mozilla::Preferences::AddBoolVarCache(
130 &sUseDiskCache, "browser.cache.disk.enable", kDefaultUseDiskCache);
131 mozilla::Preferences::AddBoolVarCache(
132 &sUseMemoryCache, "browser.cache.memory.enable", kDefaultUseMemoryCache);
134 mozilla::Preferences::AddUintVarCache(
135 &sMetadataMemoryLimit, "browser.cache.disk.metadata_memory_limit", kDefaultMetadataMemoryLimit);
137 mozilla::Preferences::AddUintVarCache(
138 &sDiskCacheCapacity, "browser.cache.disk.capacity", kDefaultDiskCacheCapacity);
139 mozilla::Preferences::AddBoolVarCache(
140 &sSmartCacheSizeEnabled, "browser.cache.disk.smart_size.enabled", kDefaultSmartCacheSizeEnabled);
141 mozilla::Preferences::AddIntVarCache(
142 &sMemoryCacheCapacity, "browser.cache.memory.capacity", kDefaultMemoryCacheCapacity);
144 mozilla::Preferences::AddUintVarCache(
145 &sMaxDiskEntrySize, "browser.cache.disk.max_entry_size", kDefaultMaxDiskEntrySize);
146 mozilla::Preferences::AddUintVarCache(
147 &sMaxMemoryEntrySize, "browser.cache.memory.max_entry_size", kDefaultMaxMemoryEntrySize);
149 // http://mxr.mozilla.org/mozilla-central/source/netwerk/cache/nsCacheEntryDescriptor.cpp#367
150 mozilla::Preferences::AddUintVarCache(
151 &sCompressionLevel, "browser.cache.compression_level", kDefaultCompressionLevel);
153 mozilla::Preferences::GetComplex(
154 "browser.cache.disk.parent_directory", NS_GET_IID(nsIFile),
155 getter_AddRefs(mCacheParentDirectoryOverride));
157 // First check the default value. If it is at -1, the experient
158 // is turned off. If it is at 0, then use the user pref value
159 // instead.
160 sHalfLifeExperiment = mozilla::Preferences::GetDefaultInt(
161 "browser.cache.frecency_experiment", kDefaultHalfLifeExperiment);
163 if (sHalfLifeExperiment == 0) {
164 // Default preferences indicate we want to run the experiment,
165 // hence read the user value.
166 sHalfLifeExperiment = mozilla::Preferences::GetInt(
167 "browser.cache.frecency_experiment", sHalfLifeExperiment);
168 }
170 if (sHalfLifeExperiment == 0) {
171 // The experiment has not yet been initialized but is engaged, do
172 // the initialization now.
173 srand(time(NULL));
174 sHalfLifeExperiment = (rand() % 4) + 1;
175 // Store the experiemnt value, since we need it not to change between
176 // browser sessions.
177 mozilla::Preferences::SetInt(
178 "browser.cache.frecency_experiment", sHalfLifeExperiment);
179 }
181 switch (sHalfLifeExperiment) {
182 case 1: // The experiment is engaged
183 sHalfLifeHours = 6;
184 break;
185 case 2:
186 sHalfLifeHours = 24;
187 break;
188 case 3:
189 sHalfLifeHours = 7 * 24;
190 break;
191 case 4:
192 sHalfLifeHours = 50 * 24;
193 break;
195 case -1:
196 default: // The experiment is off or broken
197 sHalfLifeExperiment = -1;
198 sHalfLifeHours = std::max(1U, std::min(1440U, mozilla::Preferences::GetUint(
199 "browser.cache.frecency_half_life_hours", kDefaultHalfLifeHours)));
200 break;
201 }
203 mozilla::Preferences::AddBoolVarCache(
204 &sSanitizeOnShutdown, "privacy.sanitize.sanitizeOnShutdown", kDefaultSanitizeOnShutdown);
205 mozilla::Preferences::AddBoolVarCache(
206 &sClearCacheOnShutdown, "privacy.clearOnShutdown.cache", kDefaultClearCacheOnShutdown);
207 }
209 // static
210 uint32_t const CacheObserver::MemoryCacheCapacity()
211 {
212 if (sMemoryCacheCapacity >= 0)
213 return sMemoryCacheCapacity << 10;
215 if (sAutoMemoryCacheCapacity != -1)
216 return sAutoMemoryCacheCapacity;
218 static uint64_t bytes = PR_GetPhysicalMemorySize();
219 // If getting the physical memory failed, arbitrarily assume
220 // 32 MB of RAM. We use a low default to have a reasonable
221 // size on all the devices we support.
222 if (bytes == 0)
223 bytes = 32 * 1024 * 1024;
225 // Conversion from unsigned int64_t to double doesn't work on all platforms.
226 // We need to truncate the value at INT64_MAX to make sure we don't
227 // overflow.
228 if (bytes > INT64_MAX)
229 bytes = INT64_MAX;
231 uint64_t kbytes = bytes >> 10;
232 double kBytesD = double(kbytes);
233 double x = log(kBytesD)/log(2.0) - 14;
235 int32_t capacity = 0;
236 if (x > 0) {
237 capacity = (int32_t)(x * x / 3.0 + x + 2.0 / 3 + 0.1); // 0.1 for rounding
238 if (capacity > 32)
239 capacity = 32;
240 capacity <<= 20;
241 }
243 // Result is in bytes.
244 return sAutoMemoryCacheCapacity = capacity;
245 }
247 void CacheObserver::SchduleAutoDelete()
248 {
249 // Auto-delete not set
250 if (sAutoDeleteCacheVersion == -1)
251 return;
253 // Don't autodelete the same version of the cache user has setup
254 // to use.
255 int32_t activeVersion = UseNewCache() ? 1 : 0;
256 if (sAutoDeleteCacheVersion == activeVersion)
257 return;
259 CacheStorageService::WipeCacheDirectory(sAutoDeleteCacheVersion);
260 }
262 // static
263 bool const CacheObserver::UseNewCache()
264 {
265 uint32_t useNewCache = sUseNewCache;
267 if (sUseNewCacheTemp)
268 useNewCache = 1;
270 switch (useNewCache) {
271 case 0: // use the old cache backend
272 return false;
274 case 1: // use the new cache backend
275 return true;
276 }
278 return true;
279 }
281 // static
282 void
283 CacheObserver::SetDiskCacheCapacity(uint32_t aCapacity)
284 {
285 sDiskCacheCapacity = aCapacity >> 10;
287 if (!sSelf) {
288 return;
289 }
291 if (NS_IsMainThread()) {
292 sSelf->StoreDiskCacheCapacity();
293 } else {
294 nsCOMPtr<nsIRunnable> event =
295 NS_NewRunnableMethod(sSelf, &CacheObserver::StoreDiskCacheCapacity);
296 NS_DispatchToMainThread(event);
297 }
298 }
300 void
301 CacheObserver::StoreDiskCacheCapacity()
302 {
303 mozilla::Preferences::SetInt("browser.cache.disk.capacity",
304 sDiskCacheCapacity);
305 }
307 // static
308 void CacheObserver::ParentDirOverride(nsIFile** aDir)
309 {
310 if (NS_WARN_IF(!aDir))
311 return;
313 *aDir = nullptr;
315 if (!sSelf)
316 return;
317 if (!sSelf->mCacheParentDirectoryOverride)
318 return;
320 sSelf->mCacheParentDirectoryOverride->Clone(aDir);
321 }
323 namespace { // anon
325 class CacheStorageEvictHelper
326 {
327 public:
328 nsresult Run(mozIApplicationClearPrivateDataParams* aParams);
330 private:
331 uint32_t mAppId;
332 nsresult ClearStorage(bool const aPrivate,
333 bool const aInBrowser,
334 bool const aAnonymous);
335 };
337 nsresult
338 CacheStorageEvictHelper::Run(mozIApplicationClearPrivateDataParams* aParams)
339 {
340 nsresult rv;
342 rv = aParams->GetAppId(&mAppId);
343 NS_ENSURE_SUCCESS(rv, rv);
345 bool aBrowserOnly;
346 rv = aParams->GetBrowserOnly(&aBrowserOnly);
347 NS_ENSURE_SUCCESS(rv, rv);
349 MOZ_ASSERT(mAppId != nsILoadContextInfo::UNKNOWN_APP_ID);
351 // Clear all [private X anonymous] combinations
352 rv = ClearStorage(false, aBrowserOnly, false);
353 NS_ENSURE_SUCCESS(rv, rv);
354 rv = ClearStorage(false, aBrowserOnly, true);
355 NS_ENSURE_SUCCESS(rv, rv);
356 rv = ClearStorage(true, aBrowserOnly, false);
357 NS_ENSURE_SUCCESS(rv, rv);
358 rv = ClearStorage(true, aBrowserOnly, true);
359 NS_ENSURE_SUCCESS(rv, rv);
361 return NS_OK;
362 }
364 nsresult
365 CacheStorageEvictHelper::ClearStorage(bool const aPrivate,
366 bool const aInBrowser,
367 bool const aAnonymous)
368 {
369 nsresult rv;
371 nsRefPtr<LoadContextInfo> info = GetLoadContextInfo(
372 aPrivate, mAppId, aInBrowser, aAnonymous);
374 nsCOMPtr<nsICacheStorage> storage;
375 nsRefPtr<CacheStorageService> service = CacheStorageService::Self();
376 NS_ENSURE_TRUE(service, NS_ERROR_FAILURE);
378 // Clear disk storage
379 rv = service->DiskCacheStorage(info, false, getter_AddRefs(storage));
380 NS_ENSURE_SUCCESS(rv, rv);
381 rv = storage->AsyncEvictStorage(nullptr);
382 NS_ENSURE_SUCCESS(rv, rv);
384 // Clear memory storage
385 rv = service->MemoryCacheStorage(info, getter_AddRefs(storage));
386 NS_ENSURE_SUCCESS(rv, rv);
387 rv = storage->AsyncEvictStorage(nullptr);
388 NS_ENSURE_SUCCESS(rv, rv);
390 if (!aInBrowser) {
391 rv = ClearStorage(aPrivate, true, aAnonymous);
392 NS_ENSURE_SUCCESS(rv, rv);
393 }
395 return NS_OK;
396 }
398 } // anon
400 // static
401 bool const CacheObserver::EntryIsTooBig(int64_t aSize, bool aUsingDisk)
402 {
403 // If custom limit is set, check it.
404 int64_t preferredLimit = aUsingDisk
405 ? static_cast<int64_t>(sMaxDiskEntrySize) << 10
406 : static_cast<int64_t>(sMaxMemoryEntrySize) << 10;
408 if (preferredLimit != -1 && aSize > preferredLimit)
409 return true;
411 // Otherwise (or when in the custom limit), check limit based on the global
412 // limit. It's 1/8 (>> 3) of the respective capacity.
413 int64_t derivedLimit = aUsingDisk
414 ? (static_cast<int64_t>(DiskCacheCapacity() >> 3))
415 : (static_cast<int64_t>(MemoryCacheCapacity() >> 3));
417 if (aSize > derivedLimit)
418 return true;
420 return false;
421 }
423 NS_IMETHODIMP
424 CacheObserver::Observe(nsISupports* aSubject,
425 const char* aTopic,
426 const char16_t* aData)
427 {
428 if (!strcmp(aTopic, "prefservice:after-app-defaults")) {
429 CacheFileIOManager::Init();
430 return NS_OK;
431 }
433 if (!strcmp(aTopic, "profile-do-change")) {
434 AttachToPreferences();
435 CacheFileIOManager::Init();
436 CacheFileIOManager::OnProfile();
437 return NS_OK;
438 }
440 if (!strcmp(aTopic, "sessionstore-windows-restored")) {
441 SchduleAutoDelete();
442 return NS_OK;
443 }
445 if (!strcmp(aTopic, "profile-before-change")) {
446 nsRefPtr<CacheStorageService> service = CacheStorageService::Self();
447 if (service)
448 service->Shutdown();
450 return NS_OK;
451 }
453 if (!strcmp(aTopic, "xpcom-shutdown")) {
454 nsRefPtr<CacheStorageService> service = CacheStorageService::Self();
455 if (service)
456 service->Shutdown();
458 CacheFileIOManager::Shutdown();
459 return NS_OK;
460 }
462 if (!strcmp(aTopic, "last-pb-context-exited")) {
463 nsRefPtr<CacheStorageService> service = CacheStorageService::Self();
464 if (service)
465 service->DropPrivateBrowsingEntries();
467 return NS_OK;
468 }
470 if (!strcmp(aTopic, "webapps-clear-data")) {
471 nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
472 do_QueryInterface(aSubject);
473 if (!params) {
474 NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams");
475 return NS_ERROR_UNEXPECTED;
476 }
478 CacheStorageEvictHelper helper;
479 nsresult rv = helper.Run(params);
480 NS_ENSURE_SUCCESS(rv, rv);
482 return NS_OK;
483 }
485 if (!strcmp(aTopic, "memory-pressure")) {
486 nsRefPtr<CacheStorageService> service = CacheStorageService::Self();
487 if (service)
488 service->PurgeFromMemory(nsICacheStorageService::PURGE_EVERYTHING);
490 return NS_OK;
491 }
493 MOZ_ASSERT(false, "Missing observer handler");
494 return NS_OK;
495 }
497 } // net
498 } // mozilla