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