1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/cache2/CacheObserver.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,498 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "CacheObserver.h" 1.9 + 1.10 +#include "CacheStorageService.h" 1.11 +#include "CacheFileIOManager.h" 1.12 +#include "LoadContextInfo.h" 1.13 +#include "nsICacheStorage.h" 1.14 +#include "nsIObserverService.h" 1.15 +#include "mozIApplicationClearPrivateDataParams.h" 1.16 +#include "mozilla/Services.h" 1.17 +#include "mozilla/Preferences.h" 1.18 +#include "nsServiceManagerUtils.h" 1.19 +#include "prsystem.h" 1.20 +#include <time.h> 1.21 +#include <math.h> 1.22 + 1.23 +namespace mozilla { 1.24 +namespace net { 1.25 + 1.26 +CacheObserver* CacheObserver::sSelf = nullptr; 1.27 + 1.28 +static uint32_t const kDefaultUseNewCache = 0; // Don't use the new cache by default 1.29 +uint32_t CacheObserver::sUseNewCache = kDefaultUseNewCache; 1.30 + 1.31 +static bool sUseNewCacheTemp = false; // Temp trigger to not lose early adopters 1.32 + 1.33 +static int32_t const kAutoDeleteCacheVersion = -1; // Auto-delete off by default 1.34 +static int32_t sAutoDeleteCacheVersion = kAutoDeleteCacheVersion; 1.35 + 1.36 +static int32_t const kDefaultHalfLifeExperiment = -1; // Disabled 1.37 +int32_t CacheObserver::sHalfLifeExperiment = kDefaultHalfLifeExperiment; 1.38 + 1.39 +static uint32_t const kDefaultHalfLifeHours = 6; // 6 hours 1.40 +uint32_t CacheObserver::sHalfLifeHours = kDefaultHalfLifeHours; 1.41 + 1.42 +static bool const kDefaultUseDiskCache = true; 1.43 +bool CacheObserver::sUseDiskCache = kDefaultUseDiskCache; 1.44 + 1.45 +static bool const kDefaultUseMemoryCache = true; 1.46 +bool CacheObserver::sUseMemoryCache = kDefaultUseMemoryCache; 1.47 + 1.48 +static uint32_t const kDefaultMetadataMemoryLimit = 250; // 0.25 MB 1.49 +uint32_t CacheObserver::sMetadataMemoryLimit = kDefaultMetadataMemoryLimit; 1.50 + 1.51 +static int32_t const kDefaultMemoryCacheCapacity = -1; // autodetect 1.52 +int32_t CacheObserver::sMemoryCacheCapacity = kDefaultMemoryCacheCapacity; 1.53 +// Cache of the calculated memory capacity based on the system memory size 1.54 +int32_t CacheObserver::sAutoMemoryCacheCapacity = -1; 1.55 + 1.56 +static uint32_t const kDefaultDiskCacheCapacity = 250 * 1024; // 250 MB 1.57 +uint32_t CacheObserver::sDiskCacheCapacity = kDefaultDiskCacheCapacity; 1.58 + 1.59 +static bool const kDefaultSmartCacheSizeEnabled = false; 1.60 +bool CacheObserver::sSmartCacheSizeEnabled = kDefaultSmartCacheSizeEnabled; 1.61 + 1.62 +static uint32_t const kDefaultMaxMemoryEntrySize = 4 * 1024; // 4 MB 1.63 +uint32_t CacheObserver::sMaxMemoryEntrySize = kDefaultMaxMemoryEntrySize; 1.64 + 1.65 +static uint32_t const kDefaultMaxDiskEntrySize = 50 * 1024; // 50 MB 1.66 +uint32_t CacheObserver::sMaxDiskEntrySize = kDefaultMaxDiskEntrySize; 1.67 + 1.68 +static uint32_t const kDefaultCompressionLevel = 1; 1.69 +uint32_t CacheObserver::sCompressionLevel = kDefaultCompressionLevel; 1.70 + 1.71 +static bool kDefaultSanitizeOnShutdown = false; 1.72 +bool CacheObserver::sSanitizeOnShutdown = kDefaultSanitizeOnShutdown; 1.73 + 1.74 +static bool kDefaultClearCacheOnShutdown = false; 1.75 +bool CacheObserver::sClearCacheOnShutdown = kDefaultClearCacheOnShutdown; 1.76 + 1.77 +NS_IMPL_ISUPPORTS(CacheObserver, 1.78 + nsIObserver, 1.79 + nsISupportsWeakReference) 1.80 + 1.81 +// static 1.82 +nsresult 1.83 +CacheObserver::Init() 1.84 +{ 1.85 + if (sSelf) { 1.86 + return NS_OK; 1.87 + } 1.88 + 1.89 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.90 + if (!obs) { 1.91 + return NS_ERROR_UNEXPECTED; 1.92 + } 1.93 + 1.94 + sSelf = new CacheObserver(); 1.95 + NS_ADDREF(sSelf); 1.96 + 1.97 + obs->AddObserver(sSelf, "prefservice:after-app-defaults", true); 1.98 + obs->AddObserver(sSelf, "profile-do-change", true); 1.99 + obs->AddObserver(sSelf, "sessionstore-windows-restored", true); 1.100 + obs->AddObserver(sSelf, "profile-before-change", true); 1.101 + obs->AddObserver(sSelf, "xpcom-shutdown", true); 1.102 + obs->AddObserver(sSelf, "last-pb-context-exited", true); 1.103 + obs->AddObserver(sSelf, "webapps-clear-data", true); 1.104 + obs->AddObserver(sSelf, "memory-pressure", true); 1.105 + 1.106 + return NS_OK; 1.107 +} 1.108 + 1.109 +// static 1.110 +nsresult 1.111 +CacheObserver::Shutdown() 1.112 +{ 1.113 + if (!sSelf) { 1.114 + return NS_ERROR_NOT_INITIALIZED; 1.115 + } 1.116 + 1.117 + NS_RELEASE(sSelf); 1.118 + return NS_OK; 1.119 +} 1.120 + 1.121 +void 1.122 +CacheObserver::AttachToPreferences() 1.123 +{ 1.124 + sAutoDeleteCacheVersion = mozilla::Preferences::GetInt( 1.125 + "browser.cache.auto_delete_cache_version", kAutoDeleteCacheVersion); 1.126 + 1.127 + mozilla::Preferences::AddUintVarCache( 1.128 + &sUseNewCache, "browser.cache.use_new_backend", kDefaultUseNewCache); 1.129 + mozilla::Preferences::AddBoolVarCache( 1.130 + &sUseNewCacheTemp, "browser.cache.use_new_backend_temp", false); 1.131 + 1.132 + mozilla::Preferences::AddBoolVarCache( 1.133 + &sUseDiskCache, "browser.cache.disk.enable", kDefaultUseDiskCache); 1.134 + mozilla::Preferences::AddBoolVarCache( 1.135 + &sUseMemoryCache, "browser.cache.memory.enable", kDefaultUseMemoryCache); 1.136 + 1.137 + mozilla::Preferences::AddUintVarCache( 1.138 + &sMetadataMemoryLimit, "browser.cache.disk.metadata_memory_limit", kDefaultMetadataMemoryLimit); 1.139 + 1.140 + mozilla::Preferences::AddUintVarCache( 1.141 + &sDiskCacheCapacity, "browser.cache.disk.capacity", kDefaultDiskCacheCapacity); 1.142 + mozilla::Preferences::AddBoolVarCache( 1.143 + &sSmartCacheSizeEnabled, "browser.cache.disk.smart_size.enabled", kDefaultSmartCacheSizeEnabled); 1.144 + mozilla::Preferences::AddIntVarCache( 1.145 + &sMemoryCacheCapacity, "browser.cache.memory.capacity", kDefaultMemoryCacheCapacity); 1.146 + 1.147 + mozilla::Preferences::AddUintVarCache( 1.148 + &sMaxDiskEntrySize, "browser.cache.disk.max_entry_size", kDefaultMaxDiskEntrySize); 1.149 + mozilla::Preferences::AddUintVarCache( 1.150 + &sMaxMemoryEntrySize, "browser.cache.memory.max_entry_size", kDefaultMaxMemoryEntrySize); 1.151 + 1.152 + // http://mxr.mozilla.org/mozilla-central/source/netwerk/cache/nsCacheEntryDescriptor.cpp#367 1.153 + mozilla::Preferences::AddUintVarCache( 1.154 + &sCompressionLevel, "browser.cache.compression_level", kDefaultCompressionLevel); 1.155 + 1.156 + mozilla::Preferences::GetComplex( 1.157 + "browser.cache.disk.parent_directory", NS_GET_IID(nsIFile), 1.158 + getter_AddRefs(mCacheParentDirectoryOverride)); 1.159 + 1.160 + // First check the default value. If it is at -1, the experient 1.161 + // is turned off. If it is at 0, then use the user pref value 1.162 + // instead. 1.163 + sHalfLifeExperiment = mozilla::Preferences::GetDefaultInt( 1.164 + "browser.cache.frecency_experiment", kDefaultHalfLifeExperiment); 1.165 + 1.166 + if (sHalfLifeExperiment == 0) { 1.167 + // Default preferences indicate we want to run the experiment, 1.168 + // hence read the user value. 1.169 + sHalfLifeExperiment = mozilla::Preferences::GetInt( 1.170 + "browser.cache.frecency_experiment", sHalfLifeExperiment); 1.171 + } 1.172 + 1.173 + if (sHalfLifeExperiment == 0) { 1.174 + // The experiment has not yet been initialized but is engaged, do 1.175 + // the initialization now. 1.176 + srand(time(NULL)); 1.177 + sHalfLifeExperiment = (rand() % 4) + 1; 1.178 + // Store the experiemnt value, since we need it not to change between 1.179 + // browser sessions. 1.180 + mozilla::Preferences::SetInt( 1.181 + "browser.cache.frecency_experiment", sHalfLifeExperiment); 1.182 + } 1.183 + 1.184 + switch (sHalfLifeExperiment) { 1.185 + case 1: // The experiment is engaged 1.186 + sHalfLifeHours = 6; 1.187 + break; 1.188 + case 2: 1.189 + sHalfLifeHours = 24; 1.190 + break; 1.191 + case 3: 1.192 + sHalfLifeHours = 7 * 24; 1.193 + break; 1.194 + case 4: 1.195 + sHalfLifeHours = 50 * 24; 1.196 + break; 1.197 + 1.198 + case -1: 1.199 + default: // The experiment is off or broken 1.200 + sHalfLifeExperiment = -1; 1.201 + sHalfLifeHours = std::max(1U, std::min(1440U, mozilla::Preferences::GetUint( 1.202 + "browser.cache.frecency_half_life_hours", kDefaultHalfLifeHours))); 1.203 + break; 1.204 + } 1.205 + 1.206 + mozilla::Preferences::AddBoolVarCache( 1.207 + &sSanitizeOnShutdown, "privacy.sanitize.sanitizeOnShutdown", kDefaultSanitizeOnShutdown); 1.208 + mozilla::Preferences::AddBoolVarCache( 1.209 + &sClearCacheOnShutdown, "privacy.clearOnShutdown.cache", kDefaultClearCacheOnShutdown); 1.210 +} 1.211 + 1.212 +// static 1.213 +uint32_t const CacheObserver::MemoryCacheCapacity() 1.214 +{ 1.215 + if (sMemoryCacheCapacity >= 0) 1.216 + return sMemoryCacheCapacity << 10; 1.217 + 1.218 + if (sAutoMemoryCacheCapacity != -1) 1.219 + return sAutoMemoryCacheCapacity; 1.220 + 1.221 + static uint64_t bytes = PR_GetPhysicalMemorySize(); 1.222 + // If getting the physical memory failed, arbitrarily assume 1.223 + // 32 MB of RAM. We use a low default to have a reasonable 1.224 + // size on all the devices we support. 1.225 + if (bytes == 0) 1.226 + bytes = 32 * 1024 * 1024; 1.227 + 1.228 + // Conversion from unsigned int64_t to double doesn't work on all platforms. 1.229 + // We need to truncate the value at INT64_MAX to make sure we don't 1.230 + // overflow. 1.231 + if (bytes > INT64_MAX) 1.232 + bytes = INT64_MAX; 1.233 + 1.234 + uint64_t kbytes = bytes >> 10; 1.235 + double kBytesD = double(kbytes); 1.236 + double x = log(kBytesD)/log(2.0) - 14; 1.237 + 1.238 + int32_t capacity = 0; 1.239 + if (x > 0) { 1.240 + capacity = (int32_t)(x * x / 3.0 + x + 2.0 / 3 + 0.1); // 0.1 for rounding 1.241 + if (capacity > 32) 1.242 + capacity = 32; 1.243 + capacity <<= 20; 1.244 + } 1.245 + 1.246 + // Result is in bytes. 1.247 + return sAutoMemoryCacheCapacity = capacity; 1.248 +} 1.249 + 1.250 +void CacheObserver::SchduleAutoDelete() 1.251 +{ 1.252 + // Auto-delete not set 1.253 + if (sAutoDeleteCacheVersion == -1) 1.254 + return; 1.255 + 1.256 + // Don't autodelete the same version of the cache user has setup 1.257 + // to use. 1.258 + int32_t activeVersion = UseNewCache() ? 1 : 0; 1.259 + if (sAutoDeleteCacheVersion == activeVersion) 1.260 + return; 1.261 + 1.262 + CacheStorageService::WipeCacheDirectory(sAutoDeleteCacheVersion); 1.263 +} 1.264 + 1.265 +// static 1.266 +bool const CacheObserver::UseNewCache() 1.267 +{ 1.268 + uint32_t useNewCache = sUseNewCache; 1.269 + 1.270 + if (sUseNewCacheTemp) 1.271 + useNewCache = 1; 1.272 + 1.273 + switch (useNewCache) { 1.274 + case 0: // use the old cache backend 1.275 + return false; 1.276 + 1.277 + case 1: // use the new cache backend 1.278 + return true; 1.279 + } 1.280 + 1.281 + return true; 1.282 +} 1.283 + 1.284 +// static 1.285 +void 1.286 +CacheObserver::SetDiskCacheCapacity(uint32_t aCapacity) 1.287 +{ 1.288 + sDiskCacheCapacity = aCapacity >> 10; 1.289 + 1.290 + if (!sSelf) { 1.291 + return; 1.292 + } 1.293 + 1.294 + if (NS_IsMainThread()) { 1.295 + sSelf->StoreDiskCacheCapacity(); 1.296 + } else { 1.297 + nsCOMPtr<nsIRunnable> event = 1.298 + NS_NewRunnableMethod(sSelf, &CacheObserver::StoreDiskCacheCapacity); 1.299 + NS_DispatchToMainThread(event); 1.300 + } 1.301 +} 1.302 + 1.303 +void 1.304 +CacheObserver::StoreDiskCacheCapacity() 1.305 +{ 1.306 + mozilla::Preferences::SetInt("browser.cache.disk.capacity", 1.307 + sDiskCacheCapacity); 1.308 +} 1.309 + 1.310 +// static 1.311 +void CacheObserver::ParentDirOverride(nsIFile** aDir) 1.312 +{ 1.313 + if (NS_WARN_IF(!aDir)) 1.314 + return; 1.315 + 1.316 + *aDir = nullptr; 1.317 + 1.318 + if (!sSelf) 1.319 + return; 1.320 + if (!sSelf->mCacheParentDirectoryOverride) 1.321 + return; 1.322 + 1.323 + sSelf->mCacheParentDirectoryOverride->Clone(aDir); 1.324 +} 1.325 + 1.326 +namespace { // anon 1.327 + 1.328 +class CacheStorageEvictHelper 1.329 +{ 1.330 +public: 1.331 + nsresult Run(mozIApplicationClearPrivateDataParams* aParams); 1.332 + 1.333 +private: 1.334 + uint32_t mAppId; 1.335 + nsresult ClearStorage(bool const aPrivate, 1.336 + bool const aInBrowser, 1.337 + bool const aAnonymous); 1.338 +}; 1.339 + 1.340 +nsresult 1.341 +CacheStorageEvictHelper::Run(mozIApplicationClearPrivateDataParams* aParams) 1.342 +{ 1.343 + nsresult rv; 1.344 + 1.345 + rv = aParams->GetAppId(&mAppId); 1.346 + NS_ENSURE_SUCCESS(rv, rv); 1.347 + 1.348 + bool aBrowserOnly; 1.349 + rv = aParams->GetBrowserOnly(&aBrowserOnly); 1.350 + NS_ENSURE_SUCCESS(rv, rv); 1.351 + 1.352 + MOZ_ASSERT(mAppId != nsILoadContextInfo::UNKNOWN_APP_ID); 1.353 + 1.354 + // Clear all [private X anonymous] combinations 1.355 + rv = ClearStorage(false, aBrowserOnly, false); 1.356 + NS_ENSURE_SUCCESS(rv, rv); 1.357 + rv = ClearStorage(false, aBrowserOnly, true); 1.358 + NS_ENSURE_SUCCESS(rv, rv); 1.359 + rv = ClearStorage(true, aBrowserOnly, false); 1.360 + NS_ENSURE_SUCCESS(rv, rv); 1.361 + rv = ClearStorage(true, aBrowserOnly, true); 1.362 + NS_ENSURE_SUCCESS(rv, rv); 1.363 + 1.364 + return NS_OK; 1.365 +} 1.366 + 1.367 +nsresult 1.368 +CacheStorageEvictHelper::ClearStorage(bool const aPrivate, 1.369 + bool const aInBrowser, 1.370 + bool const aAnonymous) 1.371 +{ 1.372 + nsresult rv; 1.373 + 1.374 + nsRefPtr<LoadContextInfo> info = GetLoadContextInfo( 1.375 + aPrivate, mAppId, aInBrowser, aAnonymous); 1.376 + 1.377 + nsCOMPtr<nsICacheStorage> storage; 1.378 + nsRefPtr<CacheStorageService> service = CacheStorageService::Self(); 1.379 + NS_ENSURE_TRUE(service, NS_ERROR_FAILURE); 1.380 + 1.381 + // Clear disk storage 1.382 + rv = service->DiskCacheStorage(info, false, getter_AddRefs(storage)); 1.383 + NS_ENSURE_SUCCESS(rv, rv); 1.384 + rv = storage->AsyncEvictStorage(nullptr); 1.385 + NS_ENSURE_SUCCESS(rv, rv); 1.386 + 1.387 + // Clear memory storage 1.388 + rv = service->MemoryCacheStorage(info, getter_AddRefs(storage)); 1.389 + NS_ENSURE_SUCCESS(rv, rv); 1.390 + rv = storage->AsyncEvictStorage(nullptr); 1.391 + NS_ENSURE_SUCCESS(rv, rv); 1.392 + 1.393 + if (!aInBrowser) { 1.394 + rv = ClearStorage(aPrivate, true, aAnonymous); 1.395 + NS_ENSURE_SUCCESS(rv, rv); 1.396 + } 1.397 + 1.398 + return NS_OK; 1.399 +} 1.400 + 1.401 +} // anon 1.402 + 1.403 +// static 1.404 +bool const CacheObserver::EntryIsTooBig(int64_t aSize, bool aUsingDisk) 1.405 +{ 1.406 + // If custom limit is set, check it. 1.407 + int64_t preferredLimit = aUsingDisk 1.408 + ? static_cast<int64_t>(sMaxDiskEntrySize) << 10 1.409 + : static_cast<int64_t>(sMaxMemoryEntrySize) << 10; 1.410 + 1.411 + if (preferredLimit != -1 && aSize > preferredLimit) 1.412 + return true; 1.413 + 1.414 + // Otherwise (or when in the custom limit), check limit based on the global 1.415 + // limit. It's 1/8 (>> 3) of the respective capacity. 1.416 + int64_t derivedLimit = aUsingDisk 1.417 + ? (static_cast<int64_t>(DiskCacheCapacity() >> 3)) 1.418 + : (static_cast<int64_t>(MemoryCacheCapacity() >> 3)); 1.419 + 1.420 + if (aSize > derivedLimit) 1.421 + return true; 1.422 + 1.423 + return false; 1.424 +} 1.425 + 1.426 +NS_IMETHODIMP 1.427 +CacheObserver::Observe(nsISupports* aSubject, 1.428 + const char* aTopic, 1.429 + const char16_t* aData) 1.430 +{ 1.431 + if (!strcmp(aTopic, "prefservice:after-app-defaults")) { 1.432 + CacheFileIOManager::Init(); 1.433 + return NS_OK; 1.434 + } 1.435 + 1.436 + if (!strcmp(aTopic, "profile-do-change")) { 1.437 + AttachToPreferences(); 1.438 + CacheFileIOManager::Init(); 1.439 + CacheFileIOManager::OnProfile(); 1.440 + return NS_OK; 1.441 + } 1.442 + 1.443 + if (!strcmp(aTopic, "sessionstore-windows-restored")) { 1.444 + SchduleAutoDelete(); 1.445 + return NS_OK; 1.446 + } 1.447 + 1.448 + if (!strcmp(aTopic, "profile-before-change")) { 1.449 + nsRefPtr<CacheStorageService> service = CacheStorageService::Self(); 1.450 + if (service) 1.451 + service->Shutdown(); 1.452 + 1.453 + return NS_OK; 1.454 + } 1.455 + 1.456 + if (!strcmp(aTopic, "xpcom-shutdown")) { 1.457 + nsRefPtr<CacheStorageService> service = CacheStorageService::Self(); 1.458 + if (service) 1.459 + service->Shutdown(); 1.460 + 1.461 + CacheFileIOManager::Shutdown(); 1.462 + return NS_OK; 1.463 + } 1.464 + 1.465 + if (!strcmp(aTopic, "last-pb-context-exited")) { 1.466 + nsRefPtr<CacheStorageService> service = CacheStorageService::Self(); 1.467 + if (service) 1.468 + service->DropPrivateBrowsingEntries(); 1.469 + 1.470 + return NS_OK; 1.471 + } 1.472 + 1.473 + if (!strcmp(aTopic, "webapps-clear-data")) { 1.474 + nsCOMPtr<mozIApplicationClearPrivateDataParams> params = 1.475 + do_QueryInterface(aSubject); 1.476 + if (!params) { 1.477 + NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams"); 1.478 + return NS_ERROR_UNEXPECTED; 1.479 + } 1.480 + 1.481 + CacheStorageEvictHelper helper; 1.482 + nsresult rv = helper.Run(params); 1.483 + NS_ENSURE_SUCCESS(rv, rv); 1.484 + 1.485 + return NS_OK; 1.486 + } 1.487 + 1.488 + if (!strcmp(aTopic, "memory-pressure")) { 1.489 + nsRefPtr<CacheStorageService> service = CacheStorageService::Self(); 1.490 + if (service) 1.491 + service->PurgeFromMemory(nsICacheStorageService::PURGE_EVERYTHING); 1.492 + 1.493 + return NS_OK; 1.494 + } 1.495 + 1.496 + MOZ_ASSERT(false, "Missing observer handler"); 1.497 + return NS_OK; 1.498 +} 1.499 + 1.500 +} // net 1.501 +} // mozilla