tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CacheObserver.cpp (7399B)


      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/. */
      4 
      5 #include "CacheObserver.h"
      6 
      7 #include "CacheStorageService.h"
      8 #include "CacheFileIOManager.h"
      9 #include "LoadContextInfo.h"
     10 #include "nsICacheStorage.h"
     11 #include "nsIObserverService.h"
     12 #include "mozilla/Services.h"
     13 #include "mozilla/Preferences.h"
     14 #include "mozilla/TimeStamp.h"
     15 #include "nsServiceManagerUtils.h"
     16 #include "mozilla/net/NeckoCommon.h"
     17 #include "prsystem.h"
     18 #include <time.h>
     19 #include <math.h>
     20 #include "nsIUserIdleService.h"
     21 
     22 namespace mozilla::net {
     23 
     24 StaticRefPtr<CacheObserver> CacheObserver::sSelf;
     25 
     26 static float const kDefaultHalfLifeHours = 24.0F;  // 24 hours
     27 float CacheObserver::sHalfLifeHours = kDefaultHalfLifeHours;
     28 
     29 // The default value will be overwritten as soon as the correct smart size is
     30 // calculated by CacheFileIOManager::UpdateSmartCacheSize(). It's limited to 1GB
     31 // just for case the size is never calculated which might in theory happen if
     32 // GetDiskSpaceAvailable() always fails.
     33 Atomic<uint32_t, Relaxed> CacheObserver::sSmartDiskCacheCapacity(1024 * 1024);
     34 
     35 Atomic<PRIntervalTime> CacheObserver::sShutdownDemandedTime(
     36    PR_INTERVAL_NO_TIMEOUT);
     37 
     38 NS_IMPL_ISUPPORTS(CacheObserver, nsIObserver, nsISupportsWeakReference)
     39 
     40 // static
     41 nsresult CacheObserver::Init() {
     42  if (IsNeckoChild()) {
     43    return NS_OK;
     44  }
     45 
     46  if (sSelf) {
     47    return NS_OK;
     48  }
     49 
     50  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     51  if (!obs) {
     52    return NS_ERROR_UNEXPECTED;
     53  }
     54 
     55  sSelf = new CacheObserver();
     56 
     57  obs->AddObserver(sSelf, "prefservice:after-app-defaults", true);
     58  obs->AddObserver(sSelf, "profile-do-change", true);
     59  obs->AddObserver(sSelf, "profile-before-change", true);
     60  obs->AddObserver(sSelf, "xpcom-shutdown", true);
     61  obs->AddObserver(sSelf, "last-pb-context-exited", true);
     62  obs->AddObserver(sSelf, "memory-pressure", true);
     63  obs->AddObserver(sSelf, "browser-delayed-startup-finished", true);
     64  obs->AddObserver(sSelf, OBSERVER_TOPIC_IDLE_DAILY, true);
     65 
     66  return NS_OK;
     67 }
     68 
     69 // static
     70 nsresult CacheObserver::Shutdown() {
     71  if (!sSelf) {
     72    return NS_ERROR_NOT_INITIALIZED;
     73  }
     74 
     75  sSelf = nullptr;
     76  return NS_OK;
     77 }
     78 
     79 void CacheObserver::AttachToPreferences() {
     80  mozilla::Preferences::GetComplex(
     81      "browser.cache.disk.parent_directory", NS_GET_IID(nsIFile),
     82      getter_AddRefs(mCacheParentDirectoryOverride));
     83 
     84  sHalfLifeHours = std::max(
     85      0.01F, std::min(1440.0F, mozilla::Preferences::GetFloat(
     86                                   "browser.cache.frecency_half_life_hours",
     87                                   kDefaultHalfLifeHours)));
     88 }
     89 
     90 // static
     91 uint32_t CacheObserver::MemoryCacheCapacity() {
     92  if (StaticPrefs::browser_cache_memory_capacity() >= 0) {
     93    return StaticPrefs::browser_cache_memory_capacity();
     94  }
     95 
     96  // Cache of the calculated memory capacity based on the system memory size in
     97  // KB (C++11 guarantees local statics will be initialized once and in a
     98  // thread-safe way.)
     99  static int32_t sAutoMemoryCacheCapacity = ([] {
    100    uint64_t bytes = PR_GetPhysicalMemorySize();
    101    // If getting the physical memory failed, arbitrarily assume
    102    // 32 MB of RAM. We use a low default to have a reasonable
    103    // size on all the devices we support.
    104    if (bytes == 0) {
    105      bytes = 32 * 1024 * 1024;
    106    }
    107    // Conversion from unsigned int64_t to double doesn't work on all platforms.
    108    // We need to truncate the value at INT64_MAX to make sure we don't
    109    // overflow.
    110    if (bytes > INT64_MAX) {
    111      bytes = INT64_MAX;
    112    }
    113    uint64_t kbytes = bytes >> 10;
    114    double kBytesD = double(kbytes);
    115    double x = log(kBytesD) / log(2.0) - 14;
    116 
    117    int32_t capacity = 0;
    118    if (x > 0) {
    119      // 0.1 is added here for rounding
    120      capacity = (int32_t)(x * x / 3.0 + x + 2.0 / 3 + 0.1);
    121      if (capacity > 32) {
    122        capacity = 32;
    123      }
    124      capacity <<= 10;
    125    }
    126    return capacity;
    127  })();
    128 
    129  return sAutoMemoryCacheCapacity;
    130 }
    131 
    132 // static
    133 void CacheObserver::SetSmartDiskCacheCapacity(uint32_t aCapacity) {
    134  sSmartDiskCacheCapacity = aCapacity;
    135 }
    136 
    137 // static
    138 uint32_t CacheObserver::DiskCacheCapacity() {
    139  return SmartCacheSizeEnabled() ? sSmartDiskCacheCapacity
    140                                 : StaticPrefs::browser_cache_disk_capacity();
    141 }
    142 
    143 // static
    144 void CacheObserver::ParentDirOverride(nsIFile** aDir) {
    145  if (NS_WARN_IF(!aDir)) return;
    146 
    147  *aDir = nullptr;
    148 
    149  if (!sSelf) return;
    150  if (!sSelf->mCacheParentDirectoryOverride) return;
    151 
    152  sSelf->mCacheParentDirectoryOverride->Clone(aDir);
    153 }
    154 
    155 // static
    156 bool CacheObserver::EntryIsTooBig(int64_t aSize, bool aUsingDisk) {
    157  // If custom limit is set, check it.
    158  int64_t preferredLimit =
    159      aUsingDisk ? MaxDiskEntrySize() : MaxMemoryEntrySize();
    160 
    161  // do not convert to bytes when the limit is -1, which means no limit
    162  if (preferredLimit > 0) {
    163    preferredLimit <<= 10;
    164  }
    165 
    166  if (preferredLimit != -1 && aSize > preferredLimit) return true;
    167 
    168  // Otherwise (or when in the custom limit), check limit based on the global
    169  // limit. It's 1/8 of the respective capacity.
    170  int64_t derivedLimit =
    171      aUsingDisk ? DiskCacheCapacity() : MemoryCacheCapacity();
    172  derivedLimit <<= (10 - 3);
    173 
    174  return aSize > derivedLimit;
    175 }
    176 
    177 // static
    178 bool CacheObserver::IsPastShutdownIOLag() {
    179 #ifdef DEBUG
    180  return false;
    181 #else
    182  if (sShutdownDemandedTime == PR_INTERVAL_NO_TIMEOUT ||
    183      MaxShutdownIOLag() == UINT32_MAX) {
    184    return false;
    185  }
    186 
    187  static const PRIntervalTime kMaxShutdownIOLag =
    188      PR_SecondsToInterval(MaxShutdownIOLag());
    189 
    190  if ((PR_IntervalNow() - sShutdownDemandedTime) > kMaxShutdownIOLag) {
    191    return true;
    192  }
    193 
    194  return false;
    195 #endif
    196 }
    197 
    198 NS_IMETHODIMP
    199 CacheObserver::Observe(nsISupports* aSubject, const char* aTopic,
    200                       const char16_t* aData) {
    201  if (!strcmp(aTopic, "prefservice:after-app-defaults")) {
    202    CacheFileIOManager::Init();
    203    return NS_OK;
    204  }
    205 
    206  if (!strcmp(aTopic, "profile-do-change")) {
    207    AttachToPreferences();
    208    CacheFileIOManager::Init();
    209    CacheFileIOManager::OnProfile();
    210    return NS_OK;
    211  }
    212 
    213  if (!strcmp(aTopic, "profile-change-net-teardown") ||
    214      !strcmp(aTopic, "profile-before-change") ||
    215      !strcmp(aTopic, "xpcom-shutdown")) {
    216    if (sShutdownDemandedTime == PR_INTERVAL_NO_TIMEOUT) {
    217      sShutdownDemandedTime = PR_IntervalNow();
    218    }
    219 
    220    RefPtr<CacheStorageService> service = CacheStorageService::Self();
    221    if (service) {
    222      service->Shutdown();
    223    }
    224 
    225    CacheFileIOManager::Shutdown();
    226    return NS_OK;
    227  }
    228 
    229  if (!strcmp(aTopic, "last-pb-context-exited")) {
    230    RefPtr<CacheStorageService> service = CacheStorageService::Self();
    231    if (service) {
    232      service->DropPrivateBrowsingEntries();
    233    }
    234 
    235    return NS_OK;
    236  }
    237 
    238  if (!strcmp(aTopic, "memory-pressure")) {
    239    RefPtr<CacheStorageService> service = CacheStorageService::Self();
    240    if (service) {
    241      service->PurgeFromMemory(nsICacheStorageService::PURGE_EVERYTHING);
    242    }
    243 
    244    return NS_OK;
    245  }
    246 
    247  if (!strcmp(aTopic, "browser-delayed-startup-finished")) {
    248    CacheFileIOManager::OnDelayedStartupFinished();
    249    return NS_OK;
    250  }
    251 
    252  if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE_DAILY)) {
    253    CacheFileIOManager::OnIdleDaily();
    254    return NS_OK;
    255  }
    256 
    257  MOZ_ASSERT(false, "Missing observer handler");
    258  return NS_OK;
    259 }
    260 
    261 }  // namespace mozilla::net