netwerk/cache2/CacheObserver.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

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

mercurial