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 "mozilla/Services.h"
13 #include "mozilla/Preferences.h"
14 #include "mozilla/TimeStamp.h"
15 #include "nsServiceManagerUtils.h"
16 #include "mozilla/net/NeckoCommon.h"
20 #include "nsIUserIdleService.h"
22 namespace mozilla::net
{
24 StaticRefPtr
<CacheObserver
> CacheObserver::sSelf
;
26 static float const kDefaultHalfLifeHours
= 24.0F
; // 24 hours
27 float CacheObserver::sHalfLifeHours
= kDefaultHalfLifeHours
;
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);
35 Atomic
<PRIntervalTime
> CacheObserver::sShutdownDemandedTime(
36 PR_INTERVAL_NO_TIMEOUT
);
38 NS_IMPL_ISUPPORTS(CacheObserver
, nsIObserver
, nsISupportsWeakReference
)
41 nsresult
CacheObserver::Init() {
50 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
52 return NS_ERROR_UNEXPECTED
;
55 sSelf
= new CacheObserver();
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);
70 nsresult
CacheObserver::Shutdown() {
72 return NS_ERROR_NOT_INITIALIZED
;
79 void CacheObserver::AttachToPreferences() {
80 mozilla::Preferences::GetComplex(
81 "browser.cache.disk.parent_directory", NS_GET_IID(nsIFile
),
82 getter_AddRefs(mCacheParentDirectoryOverride
));
84 sHalfLifeHours
= std::max(
85 0.01F
, std::min(1440.0F
, mozilla::Preferences::GetFloat(
86 "browser.cache.frecency_half_life_hours",
87 kDefaultHalfLifeHours
)));
91 uint32_t CacheObserver::MemoryCacheCapacity() {
92 if (StaticPrefs::browser_cache_memory_capacity() >= 0) {
93 return StaticPrefs::browser_cache_memory_capacity();
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
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.
105 bytes
= 32 * 1024 * 1024;
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
110 if (bytes
> INT64_MAX
) {
113 uint64_t kbytes
= bytes
>> 10;
114 double kBytesD
= double(kbytes
);
115 double x
= log(kBytesD
) / log(2.0) - 14;
117 int32_t capacity
= 0;
119 // 0.1 is added here for rounding
120 capacity
= (int32_t)(x
* x
/ 3.0 + x
+ 2.0 / 3 + 0.1);
129 return sAutoMemoryCacheCapacity
;
133 void CacheObserver::SetSmartDiskCacheCapacity(uint32_t aCapacity
) {
134 sSmartDiskCacheCapacity
= aCapacity
;
138 uint32_t CacheObserver::DiskCacheCapacity() {
139 return SmartCacheSizeEnabled() ? sSmartDiskCacheCapacity
140 : StaticPrefs::browser_cache_disk_capacity();
144 void CacheObserver::ParentDirOverride(nsIFile
** aDir
) {
145 if (NS_WARN_IF(!aDir
)) return;
150 if (!sSelf
->mCacheParentDirectoryOverride
) return;
152 sSelf
->mCacheParentDirectoryOverride
->Clone(aDir
);
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();
161 // do not convert to bytes when the limit is -1, which means no limit
162 if (preferredLimit
> 0) {
163 preferredLimit
<<= 10;
166 if (preferredLimit
!= -1 && aSize
> preferredLimit
) return true;
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);
174 return aSize
> derivedLimit
;
178 bool CacheObserver::IsPastShutdownIOLag() {
182 if (sShutdownDemandedTime
== PR_INTERVAL_NO_TIMEOUT
||
183 MaxShutdownIOLag() == UINT32_MAX
) {
187 static const PRIntervalTime kMaxShutdownIOLag
=
188 PR_SecondsToInterval(MaxShutdownIOLag());
190 if ((PR_IntervalNow() - sShutdownDemandedTime
) > kMaxShutdownIOLag
) {
199 CacheObserver::Observe(nsISupports
* aSubject
, const char* aTopic
,
200 const char16_t
* aData
) {
201 if (!strcmp(aTopic
, "prefservice:after-app-defaults")) {
202 CacheFileIOManager::Init();
206 if (!strcmp(aTopic
, "profile-do-change")) {
207 AttachToPreferences();
208 CacheFileIOManager::Init();
209 CacheFileIOManager::OnProfile();
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();
220 RefPtr
<CacheStorageService
> service
= CacheStorageService::Self();
225 CacheFileIOManager::Shutdown();
229 if (!strcmp(aTopic
, "last-pb-context-exited")) {
230 RefPtr
<CacheStorageService
> service
= CacheStorageService::Self();
232 service
->DropPrivateBrowsingEntries();
238 if (!strcmp(aTopic
, "memory-pressure")) {
239 RefPtr
<CacheStorageService
> service
= CacheStorageService::Self();
241 service
->PurgeFromMemory(nsICacheStorageService::PURGE_EVERYTHING
);
247 if (!strcmp(aTopic
, "browser-delayed-startup-finished")) {
248 CacheFileIOManager::OnDelayedStartupFinished();
252 if (!strcmp(aTopic
, OBSERVER_TOPIC_IDLE_DAILY
)) {
253 CacheFileIOManager::OnIdleDaily();
257 MOZ_ASSERT(false, "Missing observer handler");
261 } // namespace mozilla::net