Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/cache2/CacheObserver.cpp
Line
Count
Source (jump to first uncovered line)
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
21
namespace mozilla {
22
namespace net {
23
24
CacheObserver* CacheObserver::sSelf = nullptr;
25
26
static float const kDefaultHalfLifeHours = 24.0F; // 24 hours
27
float CacheObserver::sHalfLifeHours = kDefaultHalfLifeHours;
28
29
static bool const kDefaultUseDiskCache = true;
30
bool CacheObserver::sUseDiskCache = kDefaultUseDiskCache;
31
32
static bool const kDefaultUseMemoryCache = true;
33
bool CacheObserver::sUseMemoryCache = kDefaultUseMemoryCache;
34
35
static uint32_t const kDefaultMetadataMemoryLimit = 250; // 0.25 MB
36
uint32_t CacheObserver::sMetadataMemoryLimit = kDefaultMetadataMemoryLimit;
37
38
static int32_t const kDefaultMemoryCacheCapacity = -1; // autodetect
39
int32_t CacheObserver::sMemoryCacheCapacity = kDefaultMemoryCacheCapacity;
40
// Cache of the calculated memory capacity based on the system memory size
41
int32_t CacheObserver::sAutoMemoryCacheCapacity = -1;
42
43
static uint32_t const kDefaultDiskCacheCapacity = 250 * 1024; // 250 MB
44
Atomic<uint32_t,Relaxed> CacheObserver::sDiskCacheCapacity
45
                                           (kDefaultDiskCacheCapacity);
46
47
static uint32_t const kDefaultDiskFreeSpaceSoftLimit = 5 * 1024; // 5MB
48
uint32_t CacheObserver::sDiskFreeSpaceSoftLimit = kDefaultDiskFreeSpaceSoftLimit;
49
50
static uint32_t const kDefaultDiskFreeSpaceHardLimit = 1024; // 1MB
51
uint32_t CacheObserver::sDiskFreeSpaceHardLimit = kDefaultDiskFreeSpaceHardLimit;
52
53
static bool const kDefaultSmartCacheSizeEnabled = false;
54
bool CacheObserver::sSmartCacheSizeEnabled = kDefaultSmartCacheSizeEnabled;
55
56
static uint32_t const kDefaultPreloadChunkCount = 4;
57
uint32_t CacheObserver::sPreloadChunkCount = kDefaultPreloadChunkCount;
58
59
static int32_t const kDefaultMaxMemoryEntrySize = 4 * 1024; // 4 MB
60
int32_t CacheObserver::sMaxMemoryEntrySize = kDefaultMaxMemoryEntrySize;
61
62
static int32_t const kDefaultMaxDiskEntrySize = 50 * 1024; // 50 MB
63
int32_t CacheObserver::sMaxDiskEntrySize = kDefaultMaxDiskEntrySize;
64
65
static uint32_t const kDefaultMaxDiskChunksMemoryUsage = 40 * 1024; // 40MB
66
uint32_t CacheObserver::sMaxDiskChunksMemoryUsage = kDefaultMaxDiskChunksMemoryUsage;
67
68
static uint32_t const kDefaultMaxDiskPriorityChunksMemoryUsage = 40 * 1024; // 40MB
69
uint32_t CacheObserver::sMaxDiskPriorityChunksMemoryUsage = kDefaultMaxDiskPriorityChunksMemoryUsage;
70
71
static uint32_t const kDefaultCompressionLevel = 1;
72
uint32_t CacheObserver::sCompressionLevel = kDefaultCompressionLevel;
73
74
static bool kDefaultSanitizeOnShutdown = false;
75
bool CacheObserver::sSanitizeOnShutdown = kDefaultSanitizeOnShutdown;
76
77
static bool kDefaultClearCacheOnShutdown = false;
78
bool CacheObserver::sClearCacheOnShutdown = kDefaultClearCacheOnShutdown;
79
80
static bool kDefaultCacheFSReported = false;
81
bool CacheObserver::sCacheFSReported = kDefaultCacheFSReported;
82
83
static bool kDefaultHashStatsReported = false;
84
bool CacheObserver::sHashStatsReported = kDefaultHashStatsReported;
85
86
static uint32_t const kDefaultMaxShutdownIOLag = 2; // seconds
87
Atomic<uint32_t, Relaxed> CacheObserver::sMaxShutdownIOLag(kDefaultMaxShutdownIOLag);
88
89
Atomic<PRIntervalTime> CacheObserver::sShutdownDemandedTime(PR_INTERVAL_NO_TIMEOUT);
90
91
NS_IMPL_ISUPPORTS(CacheObserver,
92
                  nsIObserver,
93
                  nsISupportsWeakReference)
94
95
// static
96
nsresult
97
CacheObserver::Init()
98
3
{
99
3
  if (IsNeckoChild()) {
100
0
    return NS_OK;
101
0
  }
102
3
103
3
  if (sSelf) {
104
0
    return NS_OK;
105
0
  }
106
3
107
3
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
108
3
  if (!obs) {
109
0
    return NS_ERROR_UNEXPECTED;
110
0
  }
111
3
112
3
  sSelf = new CacheObserver();
113
3
  NS_ADDREF(sSelf);
114
3
115
3
  obs->AddObserver(sSelf, "prefservice:after-app-defaults", true);
116
3
  obs->AddObserver(sSelf, "profile-do-change", true);
117
3
  obs->AddObserver(sSelf, "browser-delayed-startup-finished", true);
118
3
  obs->AddObserver(sSelf, "profile-before-change", true);
119
3
  obs->AddObserver(sSelf, "xpcom-shutdown", true);
120
3
  obs->AddObserver(sSelf, "last-pb-context-exited", true);
121
3
  obs->AddObserver(sSelf, "clear-origin-attributes-data", true);
122
3
  obs->AddObserver(sSelf, "memory-pressure", true);
123
3
124
3
  return NS_OK;
125
3
}
126
127
// static
128
nsresult
129
CacheObserver::Shutdown()
130
0
{
131
0
  if (!sSelf) {
132
0
    return NS_ERROR_NOT_INITIALIZED;
133
0
  }
134
0
135
0
  NS_RELEASE(sSelf);
136
0
  return NS_OK;
137
0
}
138
139
void
140
CacheObserver::AttachToPreferences()
141
0
{
142
0
  mozilla::Preferences::AddBoolVarCache(
143
0
    &sUseDiskCache, "browser.cache.disk.enable", kDefaultUseDiskCache);
144
0
  mozilla::Preferences::AddBoolVarCache(
145
0
    &sUseMemoryCache, "browser.cache.memory.enable", kDefaultUseMemoryCache);
146
0
147
0
  mozilla::Preferences::AddUintVarCache(
148
0
    &sMetadataMemoryLimit, "browser.cache.disk.metadata_memory_limit", kDefaultMetadataMemoryLimit);
149
0
150
0
  mozilla::Preferences::AddAtomicUintVarCache(
151
0
    &sDiskCacheCapacity, "browser.cache.disk.capacity", kDefaultDiskCacheCapacity);
152
0
  mozilla::Preferences::AddBoolVarCache(
153
0
    &sSmartCacheSizeEnabled, "browser.cache.disk.smart_size.enabled", kDefaultSmartCacheSizeEnabled);
154
0
  mozilla::Preferences::AddIntVarCache(
155
0
    &sMemoryCacheCapacity, "browser.cache.memory.capacity", kDefaultMemoryCacheCapacity);
156
0
157
0
  mozilla::Preferences::AddUintVarCache(
158
0
    &sDiskFreeSpaceSoftLimit, "browser.cache.disk.free_space_soft_limit", kDefaultDiskFreeSpaceSoftLimit);
159
0
  mozilla::Preferences::AddUintVarCache(
160
0
    &sDiskFreeSpaceHardLimit, "browser.cache.disk.free_space_hard_limit", kDefaultDiskFreeSpaceHardLimit);
161
0
162
0
  mozilla::Preferences::AddUintVarCache(
163
0
    &sPreloadChunkCount, "browser.cache.disk.preload_chunk_count", kDefaultPreloadChunkCount);
164
0
165
0
  mozilla::Preferences::AddIntVarCache(
166
0
    &sMaxDiskEntrySize, "browser.cache.disk.max_entry_size", kDefaultMaxDiskEntrySize);
167
0
  mozilla::Preferences::AddIntVarCache(
168
0
    &sMaxMemoryEntrySize, "browser.cache.memory.max_entry_size", kDefaultMaxMemoryEntrySize);
169
0
170
0
  mozilla::Preferences::AddUintVarCache(
171
0
    &sMaxDiskChunksMemoryUsage, "browser.cache.disk.max_chunks_memory_usage", kDefaultMaxDiskChunksMemoryUsage);
172
0
  mozilla::Preferences::AddUintVarCache(
173
0
    &sMaxDiskPriorityChunksMemoryUsage, "browser.cache.disk.max_priority_chunks_memory_usage", kDefaultMaxDiskPriorityChunksMemoryUsage);
174
0
175
0
  // http://mxr.mozilla.org/mozilla-central/source/netwerk/cache/nsCacheEntryDescriptor.cpp#367
176
0
  mozilla::Preferences::AddUintVarCache(
177
0
    &sCompressionLevel, "browser.cache.compression_level", kDefaultCompressionLevel);
178
0
179
0
  mozilla::Preferences::GetComplex(
180
0
    "browser.cache.disk.parent_directory", NS_GET_IID(nsIFile),
181
0
    getter_AddRefs(mCacheParentDirectoryOverride));
182
0
183
0
  sHalfLifeHours = std::max(0.01F, std::min(1440.0F, mozilla::Preferences::GetFloat(
184
0
    "browser.cache.frecency_half_life_hours", kDefaultHalfLifeHours)));
185
0
186
0
  mozilla::Preferences::AddBoolVarCache(
187
0
    &sSanitizeOnShutdown, "privacy.sanitize.sanitizeOnShutdown", kDefaultSanitizeOnShutdown);
188
0
  mozilla::Preferences::AddBoolVarCache(
189
0
    &sClearCacheOnShutdown, "privacy.clearOnShutdown.cache", kDefaultClearCacheOnShutdown);
190
0
191
0
  mozilla::Preferences::AddAtomicUintVarCache(
192
0
    &sMaxShutdownIOLag, "browser.cache.max_shutdown_io_lag", kDefaultMaxShutdownIOLag);
193
0
}
194
195
// static
196
uint32_t CacheObserver::MemoryCacheCapacity()
197
0
{
198
0
  if (sMemoryCacheCapacity >= 0)
199
0
    return sMemoryCacheCapacity << 10;
200
0
201
0
  if (sAutoMemoryCacheCapacity != -1)
202
0
    return sAutoMemoryCacheCapacity;
203
0
204
0
  static uint64_t bytes = PR_GetPhysicalMemorySize();
205
0
  // If getting the physical memory failed, arbitrarily assume
206
0
  // 32 MB of RAM. We use a low default to have a reasonable
207
0
  // size on all the devices we support.
208
0
  if (bytes == 0)
209
0
    bytes = 32 * 1024 * 1024;
210
0
211
0
  // Conversion from unsigned int64_t to double doesn't work on all platforms.
212
0
  // We need to truncate the value at INT64_MAX to make sure we don't
213
0
  // overflow.
214
0
  if (bytes > INT64_MAX)
215
0
    bytes = INT64_MAX;
216
0
217
0
  uint64_t kbytes = bytes >> 10;
218
0
  double kBytesD = double(kbytes);
219
0
  double x = log(kBytesD)/log(2.0) - 14;
220
0
221
0
  int32_t capacity = 0;
222
0
  if (x > 0) {
223
0
    capacity = (int32_t)(x * x / 3.0 + x + 2.0 / 3 + 0.1); // 0.1 for rounding
224
0
    if (capacity > 32)
225
0
      capacity = 32;
226
0
    capacity <<= 20;
227
0
  }
228
0
229
0
  // Result is in bytes.
230
0
  return sAutoMemoryCacheCapacity = capacity;
231
0
}
232
233
// static
234
void
235
CacheObserver::SetDiskCacheCapacity(uint32_t aCapacity)
236
0
{
237
0
  sDiskCacheCapacity = aCapacity >> 10;
238
0
239
0
  if (!sSelf) {
240
0
    return;
241
0
  }
242
0
243
0
  if (NS_IsMainThread()) {
244
0
    sSelf->StoreDiskCacheCapacity();
245
0
  } else {
246
0
    nsCOMPtr<nsIRunnable> event =
247
0
      NewRunnableMethod("net::CacheObserver::StoreDiskCacheCapacity",
248
0
                        sSelf,
249
0
                        &CacheObserver::StoreDiskCacheCapacity);
250
0
    NS_DispatchToMainThread(event);
251
0
  }
252
0
}
253
254
void
255
CacheObserver::StoreDiskCacheCapacity()
256
0
{
257
0
  mozilla::Preferences::SetInt("browser.cache.disk.capacity",
258
0
                               sDiskCacheCapacity);
259
0
}
260
261
// static
262
void
263
CacheObserver::SetCacheFSReported()
264
0
{
265
0
  sCacheFSReported = true;
266
0
267
0
  if (!sSelf) {
268
0
    return;
269
0
  }
270
0
271
0
  if (NS_IsMainThread()) {
272
0
    sSelf->StoreCacheFSReported();
273
0
  } else {
274
0
    nsCOMPtr<nsIRunnable> event =
275
0
      NewRunnableMethod("net::CacheObserver::StoreCacheFSReported",
276
0
                        sSelf,
277
0
                        &CacheObserver::StoreCacheFSReported);
278
0
    NS_DispatchToMainThread(event);
279
0
  }
280
0
}
281
282
void
283
CacheObserver::StoreCacheFSReported()
284
0
{
285
0
  mozilla::Preferences::SetInt("browser.cache.disk.filesystem_reported",
286
0
                               sCacheFSReported);
287
0
}
288
289
// static
290
void
291
CacheObserver::SetHashStatsReported()
292
0
{
293
0
  sHashStatsReported = true;
294
0
295
0
  if (!sSelf) {
296
0
    return;
297
0
  }
298
0
299
0
  if (NS_IsMainThread()) {
300
0
    sSelf->StoreHashStatsReported();
301
0
  } else {
302
0
    nsCOMPtr<nsIRunnable> event =
303
0
      NewRunnableMethod("net::CacheObserver::StoreHashStatsReported",
304
0
                        sSelf,
305
0
                        &CacheObserver::StoreHashStatsReported);
306
0
    NS_DispatchToMainThread(event);
307
0
  }
308
0
}
309
310
void
311
CacheObserver::StoreHashStatsReported()
312
0
{
313
0
  mozilla::Preferences::SetInt("browser.cache.disk.hashstats_reported",
314
0
                               sHashStatsReported);
315
0
}
316
317
// static
318
void CacheObserver::ParentDirOverride(nsIFile** aDir)
319
0
{
320
0
  if (NS_WARN_IF(!aDir))
321
0
    return;
322
0
323
0
  *aDir = nullptr;
324
0
325
0
  if (!sSelf)
326
0
    return;
327
0
  if (!sSelf->mCacheParentDirectoryOverride)
328
0
    return;
329
0
330
0
  sSelf->mCacheParentDirectoryOverride->Clone(aDir);
331
0
}
332
333
namespace {
334
namespace CacheStorageEvictHelper {
335
336
nsresult ClearStorage(bool const aPrivate,
337
                      bool const aAnonymous,
338
                      OriginAttributes &aOa)
339
0
{
340
0
  nsresult rv;
341
0
342
0
  aOa.SyncAttributesWithPrivateBrowsing(aPrivate);
343
0
  RefPtr<LoadContextInfo> info = GetLoadContextInfo(aAnonymous, aOa);
344
0
345
0
  nsCOMPtr<nsICacheStorage> storage;
346
0
  RefPtr<CacheStorageService> service = CacheStorageService::Self();
347
0
  NS_ENSURE_TRUE(service, NS_ERROR_FAILURE);
348
0
349
0
  // Clear disk storage
350
0
  rv = service->DiskCacheStorage(info, false, getter_AddRefs(storage));
351
0
  NS_ENSURE_SUCCESS(rv, rv);
352
0
  rv = storage->AsyncEvictStorage(nullptr);
353
0
  NS_ENSURE_SUCCESS(rv, rv);
354
0
355
0
  // Clear memory storage
356
0
  rv = service->MemoryCacheStorage(info, getter_AddRefs(storage));
357
0
  NS_ENSURE_SUCCESS(rv, rv);
358
0
  rv = storage->AsyncEvictStorage(nullptr);
359
0
  NS_ENSURE_SUCCESS(rv, rv);
360
0
361
0
  return NS_OK;
362
0
}
363
364
nsresult Run(OriginAttributes &aOa)
365
0
{
366
0
  nsresult rv;
367
0
368
0
  // Clear all [private X anonymous] combinations
369
0
  rv = ClearStorage(false, false, aOa);
370
0
  NS_ENSURE_SUCCESS(rv, rv);
371
0
  rv = ClearStorage(false, true, aOa);
372
0
  NS_ENSURE_SUCCESS(rv, rv);
373
0
  rv = ClearStorage(true, false, aOa);
374
0
  NS_ENSURE_SUCCESS(rv, rv);
375
0
  rv = ClearStorage(true, true, aOa);
376
0
  NS_ENSURE_SUCCESS(rv, rv);
377
0
378
0
  return NS_OK;
379
0
}
380
381
} // CacheStorageEvictHelper
382
} // anon
383
384
// static
385
bool CacheObserver::EntryIsTooBig(int64_t aSize, bool aUsingDisk)
386
0
{
387
0
  // If custom limit is set, check it.
388
0
  int64_t preferredLimit = aUsingDisk ? sMaxDiskEntrySize : sMaxMemoryEntrySize;
389
0
390
0
  // do not convert to bytes when the limit is -1, which means no limit
391
0
  if (preferredLimit > 0) {
392
0
    preferredLimit <<= 10;
393
0
  }
394
0
395
0
  if (preferredLimit != -1 && aSize > preferredLimit)
396
0
    return true;
397
0
398
0
  // Otherwise (or when in the custom limit), check limit based on the global
399
0
  // limit.  It's 1/8 (>> 3) of the respective capacity.
400
0
  int64_t derivedLimit = aUsingDisk
401
0
    ? (static_cast<int64_t>(DiskCacheCapacity() >> 3))
402
0
    : (static_cast<int64_t>(MemoryCacheCapacity() >> 3));
403
0
404
0
  if (aSize > derivedLimit)
405
0
    return true;
406
0
407
0
  return false;
408
0
}
409
410
// static
411
bool CacheObserver::IsPastShutdownIOLag()
412
0
{
413
#ifdef DEBUG
414
  return false;
415
#endif
416
417
0
  if (sShutdownDemandedTime == PR_INTERVAL_NO_TIMEOUT ||
418
0
      sMaxShutdownIOLag == UINT32_MAX) {
419
0
    return false;
420
0
  }
421
0
422
0
  static const PRIntervalTime kMaxShutdownIOLag =
423
0
    PR_SecondsToInterval(sMaxShutdownIOLag);
424
0
425
0
  if ((PR_IntervalNow() - sShutdownDemandedTime) > kMaxShutdownIOLag) {
426
0
    return true;
427
0
  }
428
0
429
0
  return false;
430
0
}
431
432
NS_IMETHODIMP
433
CacheObserver::Observe(nsISupports* aSubject,
434
                       const char* aTopic,
435
                       const char16_t* aData)
436
0
{
437
0
  if (!strcmp(aTopic, "prefservice:after-app-defaults")) {
438
0
    CacheFileIOManager::Init();
439
0
    return NS_OK;
440
0
  }
441
0
442
0
  if (!strcmp(aTopic, "profile-do-change")) {
443
0
    AttachToPreferences();
444
0
    CacheFileIOManager::Init();
445
0
    CacheFileIOManager::OnProfile();
446
0
    return NS_OK;
447
0
  }
448
0
449
0
  if (!strcmp(aTopic, "browser-delayed-startup-finished")) {
450
0
    CacheStorageService::CleaupCacheDirectories();
451
0
    return NS_OK;
452
0
  }
453
0
454
0
  if (!strcmp(aTopic, "profile-change-net-teardown") ||
455
0
      !strcmp(aTopic, "profile-before-change") ||
456
0
      !strcmp(aTopic, "xpcom-shutdown")) {
457
0
    if (sShutdownDemandedTime == PR_INTERVAL_NO_TIMEOUT) {
458
0
      sShutdownDemandedTime = PR_IntervalNow();
459
0
    }
460
0
461
0
    RefPtr<CacheStorageService> service = CacheStorageService::Self();
462
0
    if (service) {
463
0
      service->Shutdown();
464
0
    }
465
0
466
0
    CacheFileIOManager::Shutdown();
467
0
    return NS_OK;
468
0
  }
469
0
470
0
  if (!strcmp(aTopic, "last-pb-context-exited")) {
471
0
    RefPtr<CacheStorageService> service = CacheStorageService::Self();
472
0
    if (service) {
473
0
      service->DropPrivateBrowsingEntries();
474
0
    }
475
0
476
0
    return NS_OK;
477
0
  }
478
0
479
0
  if (!strcmp(aTopic, "clear-origin-attributes-data")) {
480
0
    OriginAttributes oa;
481
0
    if (!oa.Init(nsDependentString(aData))) {
482
0
      NS_ERROR("Could not parse OriginAttributes JSON in clear-origin-attributes-data notification");
483
0
      return NS_OK;
484
0
    }
485
0
486
0
    nsresult rv = CacheStorageEvictHelper::Run(oa);
487
0
    NS_ENSURE_SUCCESS(rv, rv);
488
0
489
0
    return NS_OK;
490
0
  }
491
0
492
0
  if (!strcmp(aTopic, "memory-pressure")) {
493
0
    RefPtr<CacheStorageService> service = CacheStorageService::Self();
494
0
    if (service)
495
0
      service->PurgeFromMemory(nsICacheStorageService::PURGE_EVERYTHING);
496
0
497
0
    return NS_OK;
498
0
  }
499
0
500
0
  MOZ_ASSERT(false, "Missing observer handler");
501
0
  return NS_OK;
502
0
}
503
504
} // namespace net
505
} // namespace mozilla